Commit 84c78cc5 authored by mergify[bot]'s avatar mergify[bot] Committed by GitHub

Merge branch 'develop' into aj/challenger-fetch-inputs

parents 581df616 235cab90
# op-chain-ops
This package contains a number of state utilities.
This package contains utilities for working with chain state.
## check-l2
......@@ -23,25 +23,3 @@ that can be executed by providing the `--l1-rpc-url` and `--l2-rpc-url` flags.
--l2-rpc-url http://localhost:9545 \
--l1-rpc-url http://localhost:8545
```
## eof-crawler
Simple CLI tool to scan all accounts in a geth LevelDB
for contracts that begin with the EOF prefix.
#### Usage
It can be built and run using the [Makefile](./Makefile) `eof-crawler` target.
Run `make eof-crawler` to create a binary in [./bin/eof-crawler](./bin/eof-crawler)
that can be executed by providing the `--db-path` and optional `--out` flags.
1. Pass the directory of the Geth DB into the tool
```sh
./bin/eof-crawler/main.go \
--db-path <db_path> \
--out <out_file>
```
2. Once the indexing has completed, an array of all EOF-prefixed contracts
will be written to designated output file (`eof_contracts.json` by default).
package main
import (
"errors"
"os"
"github.com/mattn/go-isatty"
"github.com/urfave/cli/v2"
"github.com/ethereum-optimism/optimism/op-chain-ops/eof"
"github.com/ethereum/go-ethereum/log"
)
func main() {
log.Root().SetHandler(log.StreamHandler(os.Stderr, log.TerminalFormat(isatty.IsTerminal(os.Stderr.Fd()))))
app := &cli.App{
Name: "eof-crawler",
Usage: "Scan a Geth database for EOF-prefixed contracts",
Flags: []cli.Flag{
&cli.StringFlag{
Name: "db-path",
Usage: "Path to the geth LevelDB",
},
&cli.StringFlag{
Name: "out",
Value: "eof-contracts.json",
Usage: "Path to the output file",
},
},
Action: func(ctx *cli.Context) error {
dbPath := ctx.String("db-path")
if len(dbPath) == 0 {
return errors.New("Must specify a db-path")
}
out := ctx.String("out")
return eof.IndexEOFContracts(dbPath, out)
},
}
if err := app.Run(os.Args); err != nil {
log.Crit("error indexing state", "err", err)
}
}
package eof
import (
"bytes"
"encoding/json"
"errors"
"fmt"
"log"
"os"
"time"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/common/hexutil"
"github.com/ethereum/go-ethereum/core/rawdb"
"github.com/ethereum/go-ethereum/core/state"
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/crypto"
"github.com/ethereum/go-ethereum/rlp"
"github.com/ethereum/go-ethereum/trie"
)
// Account represents an account in the state.
type Account struct {
Balance string `json:"balance"`
Nonce uint64 `json:"nonce"`
Root hexutil.Bytes `json:"root"`
CodeHash hexutil.Bytes `json:"codeHash"`
Code hexutil.Bytes `json:"code,omitempty"`
Address common.Address `json:"address,omitempty"`
SecureKey hexutil.Bytes `json:"key,omitempty"`
}
// emptyCodeHash is the known hash of an account with no code.
var emptyCodeHash = crypto.Keccak256(nil)
// IndexEOFContracts indexes all the EOF contracts in the state trie of the head block
// for the given db and writes them to a JSON file.
func IndexEOFContracts(dbPath string, out string) error {
// Open an existing Ethereum database
db, err := rawdb.NewLevelDBDatabase(dbPath, 16, 16, "", true)
if err != nil {
return fmt.Errorf("Failed to open database: %w", err)
}
stateDB := state.NewDatabase(db)
// Retrieve the head block
hash := rawdb.ReadHeadBlockHash(db)
number := rawdb.ReadHeaderNumber(db, hash)
if number == nil {
return errors.New("Failed to retrieve head block number")
}
head := rawdb.ReadBlock(db, hash, *number)
if head == nil {
return errors.New("Failed to retrieve head block")
}
// Retrieve the state belonging to the head block
st, err := trie.New(trie.StateTrieID(head.Root()), trie.NewDatabase(db))
if err != nil {
return fmt.Errorf("Failed to retrieve state trie: %w", err)
}
log.Printf("Indexing state trie at head block #%d [0x%x]", *number, hash)
// Iterate over the entire account trie to search for EOF-prefixed contracts
start := time.Now()
missingPreimages := uint64(0)
eoas := uint64(0)
nonEofContracts := uint64(0)
eofContracts := make([]Account, 0)
it := trie.NewIterator(st.NodeIterator(nil))
for it.Next() {
// Decode the state account
var data types.StateAccount
err := rlp.DecodeBytes(it.Value, &data)
if err != nil {
return fmt.Errorf("Failed to decode state account: %w", err)
}
// Check to see if the account has any code associated with it before performing
// more reads from the trie & db.
if bytes.Equal(data.CodeHash, emptyCodeHash) {
eoas++
continue
}
// Create a serializable `Account` object
account := Account{
Balance: data.Balance.String(),
Nonce: data.Nonce,
Root: data.Root[:],
CodeHash: data.CodeHash,
SecureKey: it.Key,
}
// Attempt to get the address of the account from the trie
addrBytes, err := st.Get(it.Key)
if err != nil {
return fmt.Errorf("load address for account: %w", err)
}
if addrBytes == nil {
// Preimage missing! Cannot continue.
missingPreimages++
continue
}
addr := common.BytesToAddress(addrBytes)
// Attempt to get the code of the account from the trie
code, err := stateDB.ContractCode(crypto.Keccak256Hash(addrBytes), common.BytesToHash(data.CodeHash))
if err != nil {
return fmt.Errorf("Could not load code for account %x: %w", addr, err)
}
// Check if the contract's runtime bytecode starts with the EOF prefix.
if len(code) >= 1 && code[0] == 0xEF {
// Append the account to the list of EOF contracts
account.Address = addr
account.Code = code
eofContracts = append(eofContracts, account)
} else {
nonEofContracts++
}
}
// Print finishing status
log.Printf("Indexing done in %v, found %d EOF contracts", time.Since(start), len(eofContracts))
log.Printf("Num missing preimages: %d", missingPreimages)
log.Printf("Non-EOF-prefixed contracts: %d", nonEofContracts)
log.Printf("Accounts with no code (EOAs): %d", eoas)
// Write the EOF contracts to a file
file, err := json.MarshalIndent(eofContracts, "", " ")
if err != nil {
return fmt.Errorf("Cannot marshal EOF contracts: %w", err)
}
err = os.WriteFile(out, file, 0644)
if err != nil {
return fmt.Errorf("Failed to write EOF contracts array to file: %w", err)
}
log.Printf("Wrote list of EOF contracts to `%v`", out)
return nil
}
......@@ -27,8 +27,34 @@ contract SemverLock is Script {
commands[1] = _files[i];
string memory fileContents = string(vm.ffi(commands));
// Grab the contract name
commands = new string[](3);
commands[0] = "bash";
commands[1] = "-c";
commands[2] = string.concat(
"echo \"",
_files[i],
"\"| sed -E \'s|src/.*/(.+)\\.sol|\\1|\'"
);
string memory contractName = string(vm.ffi(commands));
commands[0] = "bash";
commands[1] = "-c";
commands[2] = "forge config --json | jq -r .out";
string memory artifactsDir = string(vm.ffi(commands));
// Parse the artifact to get the contract's initcode hash.
bytes memory initCode = vm.getCode(string.concat(
artifactsDir,
"/",
contractName,
".sol/",
contractName,
".json"
));
// Serialize the source hash in JSON.
string memory j = vm.serializeBytes32(out, _files[i], keccak256(abi.encodePacked(fileContents)));
string memory j = vm.serializeBytes32(out, _files[i], keccak256(abi.encodePacked(fileContents, initCode)));
// If this is the last file, set the output.
if (i == _files.length - 1) {
......
{
"src/L1/L1CrossDomainMessenger.sol": "0xa043f901e98a24be71a4ec79d5e730e8e56ec616bb79793fc191138924c5e4b5",
"src/L1/L1ERC721Bridge.sol": "0x4983a413d0c6d1d83cf6463cd44e1adeb3d1bd49b5de3bacfceba04fc717caa2",
"src/L1/L1StandardBridge.sol": "0x6e361f923509eb35a74391770dad9529db7deae751cfc36506e7ef40f39d5351",
"src/L1/L2OutputOracle.sol": "0x2b285a897d3285975bd47e89bd5ec7025369931384f9f02a20f48254dbfca181",
"src/L1/OptimismPortal.sol": "0xd5abaa3d1093c41f8e81b3cd298d4a35f90d103d9bca566a47ca562635f2f943",
"src/L1/SystemConfig.sol": "0xbd2be6c19e6e85eae73ddf3cd6304a395e2a41d86aee1c15b6b0f044bf54232e",
"src/L2/BaseFeeVault.sol": "0xaa2bf66e16ed9098af4251af94c30741f6ba094e321b786eff51b59574c6ad25",
"src/L2/GasPriceOracle.sol": "0x712134045fba966b0d0cc28019f5c2bd298d999649f387d989f744b274020a82",
"src/L2/L1Block.sol": "0x64d2517a595a5b5af7eef1070920eb90aa595871ec55ba8b6d6aa323043ac000",
"src/L2/L1FeeVault.sol": "0xc41752b9ce3d72b0f24c9baaa995e574e1a6b6166337485413a86c20f7063256",
"src/L2/L2CrossDomainMessenger.sol": "0xd1e057fe1889e0701f447af8016e4a201febdc28f640138878435746b3c6f647",
"src/L2/L2ERC721Bridge.sol": "0x2a0c241efb516161a12625e23d1e5aa32da815892e4fcc52f3b12d41cdff53b2",
"src/L2/L2StandardBridge.sol": "0x8ee5257e03ae4ba8555d9f7d13374c8a388315d62c16107bb4cadd450bfeb3d3",
"src/L2/L2ToL1MessagePasser.sol": "0x7e35c3c4f1dd3d131dd71db07676301f7c477f02b6d6bf0ec468ecf2bed8325b",
"src/L2/SequencerFeeVault.sol": "0xd7266bada6ee69aa484d5d3c0590441b4475a786ba0c7c4872f2114fca96d9ee",
"src/dispute/FaultDisputeGame.sol": "0x2a1e708bef8dda2760390cbabc1303dbe025752f40c32514bd8f61fdee84ffbe",
"src/legacy/DeployerWhitelist.sol": "0x47277d9c8409d517501d172db6697d55090d3d3a9e4bb2b1adea83471d793b6b",
"src/legacy/L1BlockNumber.sol": "0x1a1690b8b5ab53cf2b5c8e85fb86028b4078ae656286ae482cabe68374334f2a",
"src/legacy/LegacyMessagePasser.sol": "0xc7f42e6165507b4c50a5169a950f66602e6b4b8cff17f5d95994e121abb18390",
"src/periphery/op-nft/AttestationStation.sol": "0xe8a905953896b45bb5ece6598c12c8234ee78e57bdb6022ee0e6c4771fd36b7e",
"src/periphery/op-nft/Optimist.sol": "0x7fe55cdb30c6f00d8058abc949e441743ac41c8f345ad92711e5348c515790f0",
"src/periphery/op-nft/OptimistAllowlist.sol": "0x1c4d648ccee99e3d1849b362117de2b6ff215b685fd81e529641afbd0f7eda37",
"src/periphery/op-nft/OptimistInviter.sol": "0xf465cf89f1b71dad3698a3e7626bf6b7ee9172cfdadba0ca3542a5b0150d38d5",
"src/universal/OptimismMintableERC20.sol": "0xadcb9f22ae05f8dd05202de210faf56d44c5af4fd9ccdc049f116fc7d2499ff7",
"src/universal/OptimismMintableERC20Factory.sol": "0x1d48aaec29c6732e5d25652a568c4f58a9606656711a14ef98c7cdd5768d9677",
"src/universal/OptimismMintableERC721.sol": "0x72c9c204caddf5a48b6a704621363926714162c2453392b922091d80aa73924f",
"src/universal/OptimismMintableERC721Factory.sol": "0x138d1cecb3c0daa85c73617a1c5d0956544bf65153c13dc11529a71f4b70fb3b"
"src/L1/L1CrossDomainMessenger.sol": "0x76697013a600b9fe46f5606d445725a164a37cff7058d109dc2c1601a46a4d2a",
"src/L1/L1ERC721Bridge.sol": "0xac9d8e236a1b35c358f9800834f65375cf4270155e817cf3d0f2bac1968f9107",
"src/L1/L1StandardBridge.sol": "0x26fd79e041c403f4bc68758c410fcc801975e7648c0b51a2c4a6e8c44fabcbfd",
"src/L1/L2OutputOracle.sol": "0xd6c5eb38732077c4705f46a61be68a7beccc069a99ed1d07b8e1fc6e1de8ffa6",
"src/L1/OptimismPortal.sol": "0x06c324c474d251a1ef0a1a6a22013964c5b2b871e641bbedf447f231e3f44dcc",
"src/L1/SystemConfig.sol": "0x8e2b5103d2eb93b74af2e2f96a4505e637cdc3c44d80cf5ec2eca70060e1deff",
"src/L2/BaseFeeVault.sol": "0xa596e60762f16192cfa86459fcb9f4da9d8f756106eedac643a1ffeafbbfcc5f",
"src/L2/GasPriceOracle.sol": "0xc735a8bf01ad8bca194345748537bfd9924909c0342bc133c4a31e2fb8cb9882",
"src/L2/L1Block.sol": "0x7fbfc8b4da630386636c665570321fdec287b0867dbe0f91c2e7cd5b7697c220",
"src/L2/L1FeeVault.sol": "0x62bfe739ff939fc68f90916399ac4160936d31eb37749cb014dd9d0c5dd4183a",
"src/L2/L2CrossDomainMessenger.sol": "0x1ffe8f6bad8eaa4c7ecff77456d382bdc5b371ffd672af6b539cb1a97f23e265",
"src/L2/L2ERC721Bridge.sol": "0x2b30a48241787580918a6ce4263823c036a21bde9cd80cc38d9beb6626c4f93b",
"src/L2/L2StandardBridge.sol": "0x73a4fea3dca8ac7d7ba32e38aadeb69bd344042666a40a75e8c28849f01999e5",
"src/L2/L2ToL1MessagePasser.sol": "0xed800b600cb3f67e18a1ab10750e3934a8b3e42178f422bcacfde770a6e8e8bd",
"src/L2/SequencerFeeVault.sol": "0xd57c143b1f042400430b991b806bf971628e6980406c751e82d19ae80eeb4e8d",
"src/dispute/FaultDisputeGame.sol": "0x53ef150202f3e22dd0bd92a520c4eaa95752f5fd9d6b22f56d27958b71bb23ec",
"src/legacy/DeployerWhitelist.sol": "0x5e80f7b13ef73f06c63bd9b118a49da1ff06a5c0fcf8067b5a3365d731c23765",
"src/legacy/L1BlockNumber.sol": "0x84cc587148de5920dfcd19da44d28e769f0e4d08ca2bcc93f18aa78c6cc2ebe6",
"src/legacy/LegacyMessagePasser.sol": "0x2692b50b227e5f75a53439c0cf303498edfd4fc087555b3fc9bc4bceb518229b",
"src/periphery/op-nft/AttestationStation.sol": "0xc47d0edc7c97b9e57cc1348c9a10864dc0ee248a5634a38eb6865c7dd07db30b",
"src/periphery/op-nft/Optimist.sol": "0x697a51a2d501a965ab1b08c43ec63bc428dc1a1065aa6193a665c1e70840c628",
"src/periphery/op-nft/OptimistAllowlist.sol": "0xc416eca27d2132bd880ffca7f2b584e519b2cd3aba48418d3060b3dc376a95cb",
"src/periphery/op-nft/OptimistInviter.sol": "0x0813661f25e9b0bc7944591c1a4675940b8e610dfe90197a30ccc683922f2216",
"src/universal/OptimismMintableERC20.sol": "0xc36fa540d91199857e88d3740f97e19702443ba468731d18cb6f41ec67934a24",
"src/universal/OptimismMintableERC20Factory.sol": "0x9b0fc71d2080bee4b83da8d0ddc70e903239f12dcc6660fdc4dccf8d2a89b4d1",
"src/universal/OptimismMintableERC721.sol": "0x722060ab18481f862f4743a94717dade057a931b345ccadfdfb49374f5c8de31",
"src/universal/OptimismMintableERC721Factory.sol": "0x7b2b88226a95140ec6d45d742f772d5a1ff93e29a3f7a194c2b12e2bb9e08b02"
}
\ No newline at end of file
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment