diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index 26e5aa627c..b569180852 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -1,24 +1,36 @@ # Lines starting with '#' are comments. # Each line is a file pattern followed by one or more owners. -accounts/usbwallet @karalabe -accounts/scwallet @gballet -accounts/abi @gballet @MariusVanDerWijden -beacon/engine @lightclient -cmd/clef @holiman -cmd/evm @holiman @MariusVanDerWijden @lightclient -consensus @karalabe -core/ @karalabe @holiman @rjl493456442 -eth/ @karalabe @holiman @rjl493456442 -eth/catalyst/ @gballet @lightclient + +accounts/usbwallet/ @gballet +accounts/scwallet/ @gballet +accounts/abi/ @gballet @MariusVanDerWijden +beacon/engine/ @MariusVanDerWijden @lightclient @fjl +beacon/light/ @zsfelfoldi +beacon/merkle/ @zsfelfoldi +beacon/types/ @zsfelfoldi @fjl +beacon/params/ @zsfelfoldi @fjl +cmd/clef/ @holiman +cmd/evm/ @holiman @MariusVanDerWijden @lightclient +core/state/ @rjl493456442 @holiman +crypto/ @gballet @jwasinger @holiman @fjl +core/ @holiman @rjl493456442 +eth/ @holiman @rjl493456442 +eth/catalyst/ @MariusVanDerWijden @lightclient @fjl @jwasinger eth/tracers/ @s1na +ethclient/ @fjl +ethdb/ @rjl493456442 +event/ @fjl +trie/ @rjl493456442 +triedb/ @rjl493456442 core/tracing/ @s1na graphql/ @s1na -internal/ethapi @lightclient -internal/era @lightclient -les/ @zsfelfoldi @rjl493456442 -light/ @zsfelfoldi @rjl493456442 +internal/ethapi/ @fjl @s1na @lightclient +internal/era/ @lightclient +metrics/ @holiman +miner/ @MariusVanDerWijden @holiman @fjl @rjl493456442 node/ @fjl p2p/ @fjl @zsfelfoldi +rlp/ @fjl params/ @fjl @holiman @karalabe @gballet @rjl493456442 @zsfelfoldi rpc/ @fjl @holiman signer/ @holiman diff --git a/accounts/abi/abi.go b/accounts/abi/abi.go index c7bc2b4541..f75278c8b1 100644 --- a/accounts/abi/abi.go +++ b/accounts/abi/abi.go @@ -84,7 +84,7 @@ func (abi ABI) Pack(name string, args ...interface{}) ([]byte, error) { func (abi ABI) getArguments(name string, data []byte) (Arguments, error) { // since there can't be naming collisions with contracts and events, - // we need to decide whether we're calling a method or an event + // we need to decide whether we're calling a method, event or an error var args Arguments if method, ok := abi.Methods[name]; ok { if len(data)%32 != 0 { @@ -95,8 +95,11 @@ func (abi ABI) getArguments(name string, data []byte) (Arguments, error) { if event, ok := abi.Events[name]; ok { args = event.Inputs } + if err, ok := abi.Errors[name]; ok { + args = err.Inputs + } if args == nil { - return nil, fmt.Errorf("abi: could not locate named method or event: %s", name) + return nil, fmt.Errorf("abi: could not locate named method, event or error: %s", name) } return args, nil } diff --git a/accounts/abi/abi_test.go b/accounts/abi/abi_test.go index fc290cfe84..db9a4c55a5 100644 --- a/accounts/abi/abi_test.go +++ b/accounts/abi/abi_test.go @@ -29,6 +29,7 @@ import ( "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common/math" "github.com/ethereum/go-ethereum/crypto" + "github.com/ethereum/go-ethereum/internal/testrand" ) const jsondata = ` @@ -317,6 +318,38 @@ func TestCustomErrors(t *testing.T) { check("MyError", "MyError(uint256)") } +func TestCustomErrorUnpackIntoInterface(t *testing.T) { + t.Parallel() + errorName := "MyError" + json := fmt.Sprintf(`[{"inputs":[{"internalType":"address","name":"sender","type":"address"},{"internalType":"uint256","name":"balance","type":"uint256"}],"name":"%s","type":"error"}]`, errorName) + abi, err := JSON(strings.NewReader(json)) + if err != nil { + t.Fatal(err) + } + type MyError struct { + Sender common.Address + Balance *big.Int + } + + sender := testrand.Address() + balance := new(big.Int).SetBytes(testrand.Bytes(8)) + encoded, err := abi.Errors[errorName].Inputs.Pack(sender, balance) + if err != nil { + t.Fatal(err) + } + result := MyError{} + err = abi.UnpackIntoInterface(&result, errorName, encoded) + if err != nil { + t.Fatal(err) + } + if result.Sender != sender { + t.Errorf("expected %x got %x", sender, result.Sender) + } + if result.Balance.Cmp(balance) != 0 { + t.Errorf("expected %v got %v", balance, result.Balance) + } +} + func TestMultiPack(t *testing.T) { t.Parallel() abi, err := JSON(strings.NewReader(jsondata)) diff --git a/accounts/abi/topics.go b/accounts/abi/topics.go index 7ce9b7273c..4819334ae6 100644 --- a/accounts/abi/topics.go +++ b/accounts/abi/topics.go @@ -42,7 +42,7 @@ func MakeTopics(query ...[]interface{}) ([][]common.Hash, error) { case common.Address: copy(topic[common.HashLength-common.AddressLength:], rule[:]) case *big.Int: - copy(topic[:], math.U256Bytes(rule)) + copy(topic[:], math.U256Bytes(new(big.Int).Set(rule))) case bool: if rule { topic[common.HashLength-1] = 1 diff --git a/accounts/abi/topics_test.go b/accounts/abi/topics_test.go index 6a4c50078a..161867e2d9 100644 --- a/accounts/abi/topics_test.go +++ b/accounts/abi/topics_test.go @@ -149,6 +149,23 @@ func TestMakeTopics(t *testing.T) { } }) } + + t.Run("does not mutate big.Int", func(t *testing.T) { + t.Parallel() + want := [][]common.Hash{{common.HexToHash("ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff")}} + + in := big.NewInt(-1) + got, err := MakeTopics([]interface{}{in}) + if err != nil { + t.Fatalf("makeTopics() error = %v", err) + } + if !reflect.DeepEqual(got, want) { + t.Fatalf("makeTopics() = %v, want %v", got, want) + } + if orig := big.NewInt(-1); in.Cmp(orig) != 0 { + t.Fatalf("makeTopics() mutated an input parameter from %v to %v", orig, in) + } + }) } type args struct { diff --git a/beacon/engine/types.go b/beacon/engine/types.go index 9f41aa04ca..984090ef89 100644 --- a/beacon/engine/types.go +++ b/beacon/engine/types.go @@ -265,15 +265,7 @@ func ExecutableDataToBlockNoHash(data ExecutableData, versionedHashes []common.H var requestsHash *common.Hash if requests != nil { - // Put back request type byte. - typedRequests := make([][]byte, len(requests)) - for i, reqdata := range requests { - typedReqdata := make([]byte, len(reqdata)+1) - typedReqdata[0] = byte(i) - copy(typedReqdata[1:], reqdata) - typedRequests[i] = typedReqdata - } - h := types.CalcRequestsHash(typedRequests) + h := types.CalcRequestsHash(requests) requestsHash = &h } @@ -343,20 +335,11 @@ func BlockToExecutableData(block *types.Block, fees *big.Int, sidecars []*types. } } - // Remove type byte in requests. - var plainRequests [][]byte - if requests != nil { - plainRequests = make([][]byte, len(requests)) - for i, reqdata := range requests { - plainRequests[i] = reqdata[1:] - } - } - return &ExecutionPayloadEnvelope{ ExecutionPayload: data, BlockValue: fees, BlobsBundle: &bundle, - Requests: plainRequests, + Requests: requests, Override: false, } } diff --git a/build/checksums.txt b/build/checksums.txt index b835215850..b8061c9244 100644 --- a/build/checksums.txt +++ b/build/checksums.txt @@ -5,56 +5,56 @@ # https://github.com/ethereum/execution-spec-tests/releases/download/v2.1.0/ ca89c76851b0900bfcc3cbb9a26cbece1f3d7c64a3bed38723e914713290df6c fixtures_develop.tar.gz -# version:golang 1.23.3 +# version:golang 1.23.4 # https://go.dev/dl/ -8d6a77332487557c6afa2421131b50f83db4ae3c579c3bc72e670ee1f6968599 go1.23.3.src.tar.gz -bdbf2a243ed4a121c9988684e5b15989cb244c1ff9e41ca823d0187b5c859114 go1.23.3.aix-ppc64.tar.gz -b79c77bbdf61e6e486aa6bea9286f3f7969c28e2ff7686ce10c334f746bfb724 go1.23.3.darwin-amd64.pkg -c7e024d5c0bc81845070f23598caf02f05b8ae88fd4ad2cd3e236ddbea833ad2 go1.23.3.darwin-amd64.tar.gz -3e764df0db8f3c7470b9ff641954a380510a4822613c06bd5a195fd083f4731d go1.23.3.darwin-arm64.pkg -31e119fe9bde6e105407a32558d5b5fa6ca11e2bd17f8b7b2f8a06aba16a0632 go1.23.3.darwin-arm64.tar.gz -3872c9a98331050a242afe63fa6abc8fc313aca83dcaefda318e903309ac0c8d go1.23.3.dragonfly-amd64.tar.gz -69479fa016ec5b4605885643ce0c2dd5c583e02353978feb6de38c961863b9cc go1.23.3.freebsd-386.tar.gz -bf1de22a900646ef4f79480ed88337856d47089cc610f87e6fef46f6b8db0e1f go1.23.3.freebsd-amd64.tar.gz -e461f866479bc36bdd4cfec32bfecb1bb243152268a1b3223de109410dec3407 go1.23.3.freebsd-arm.tar.gz -24154b4018a45540aefeb6b5b9ffdcc8d9a8cdb78cd7fec262787b89fed19997 go1.23.3.freebsd-arm64.tar.gz -218f3f1532e61dd65c330c2a5fc85bec18cc3690489763e62ffa9bb9fc85a68e go1.23.3.freebsd-riscv64.tar.gz -24e3f34858b8687c31f5e5ab9e46d27fb613b0d50a94261c500cebb2d79c0672 go1.23.3.illumos-amd64.tar.gz -3d7b00191a43c50d28e0903a0c576104bc7e171a8670de419d41111c08dfa299 go1.23.3.linux-386.tar.gz -a0afb9744c00648bafb1b90b4aba5bdb86f424f02f9275399ce0c20b93a2c3a8 go1.23.3.linux-amd64.tar.gz -1f7cbd7f668ea32a107ecd41b6488aaee1f5d77a66efd885b175494439d4e1ce go1.23.3.linux-arm64.tar.gz -5f0332754beffc65af65a7b2da76e9dd997567d0d81b6f4f71d3588dc7b4cb00 go1.23.3.linux-armv6l.tar.gz -1d0161a8946c7d99f717bad23631738408511f9f87e78d852224a023d8882ad8 go1.23.3.linux-loong64.tar.gz -e924a7c9027f521f8a3563541ed0f89a4db3ef005b6b71263415b38e0b46e63a go1.23.3.linux-mips.tar.gz -4cdf8c38165627f032c2b17cdd95e4aafff40d75fc873824d4c94914284098ca go1.23.3.linux-mips64.tar.gz -5e49347e7325d2e268fb14040529b704e66eed77154cc73a919e9167d8527a2f go1.23.3.linux-mips64le.tar.gz -142eabc17cee99403e895383ed7a6b7b40e740e8c2f73b79352bb9d1242fbd98 go1.23.3.linux-mipsle.tar.gz -96ad61ba6b6cc0f5adfd75e65231c61e7db26d8236f01117023899528164d1b0 go1.23.3.linux-ppc64.tar.gz -e3b926c81e8099d3cee6e6e270b85b39c3bd44263f8d3df29aacb4d7e00507c8 go1.23.3.linux-ppc64le.tar.gz -324e03b6f59be841dfbaeabc466224b0f0905f5ad3a225b7c0703090e6c4b1a5 go1.23.3.linux-riscv64.tar.gz -6bd72fcef72b046b6282c2d1f2c38f31600e4fe9361fcd8341500c754fb09c38 go1.23.3.linux-s390x.tar.gz -5df382337fe2e4ea6adaafa823da5e083513a97534a38f89d691dd6f599084ca go1.23.3.netbsd-386.tar.gz -9ae7cb6095a3e91182ac03547167e230fddd4941ed02dbdb6af663b2a53d9db7 go1.23.3.netbsd-amd64.tar.gz -4a452c4134a9bea6213d8925d322f26b01c0eccda1330585bb2b241c76a0c3ea go1.23.3.netbsd-arm.tar.gz -8ff3b5184d840148dbca061c04dca35a7070dc894255d3b755066bd76a7094dc go1.23.3.netbsd-arm64.tar.gz -5b6940922e68ac1162a704a8b583fb4f039f955bfe97c35a56c40269cbcff9b1 go1.23.3.openbsd-386.tar.gz -6ae4aeb6a88f3754b10ecec90422a30fb8bf86c3187be2be9408d67a5a235ace go1.23.3.openbsd-amd64.tar.gz -e5eae226391b60c4d1ea1022663f55b225c6d7bab67f31fbafd5dd7a04684006 go1.23.3.openbsd-arm.tar.gz -e12b2c04535e0bf5561d54831122b410d708519c1ec2c56b0c2350b15243c331 go1.23.3.openbsd-arm64.tar.gz -599818e4062166d7a112f6f3fcca2dd4e2cdd3111fe48f9757bd8debf38c7f52 go1.23.3.openbsd-ppc64.tar.gz -9ca4db8cab2a07d561f5b2a9397793684ab3b22326add1fe8cda8a545a1693db go1.23.3.openbsd-riscv64.tar.gz -8fca1ec2aced936e0170605378ee7f0acb38f002490321f67fc83728ee281967 go1.23.3.plan9-386.tar.gz -22d663692224fc1933a97f61d9fe49815e3b9ef1c2be97046505683fdf2e23c7 go1.23.3.plan9-amd64.tar.gz -d0417a702d0e776d57e450fa2ce1ce7efa199a636644776862dbf946c409a462 go1.23.3.plan9-arm.tar.gz -b5d9db1c02e0ca266a142eb687bd7749890c30872b09a4a0ffcd491425039754 go1.23.3.solaris-amd64.tar.gz -14b7baf4af2046013b74dfac6e9a0a7403f15ee9940a16890bc028dfd32c49ac go1.23.3.windows-386.msi -23da9089ea6c5612d718f13c26e9bfc9aaaabe222838075346a8191d48f9dfe5 go1.23.3.windows-386.zip -614f0e3eed82245dfb4356d4e8d5b96abecca6a4c4f0168c0e389e4dd6284db8 go1.23.3.windows-amd64.msi -81968b563642096b8a7521171e2be6e77ff6f44032f7493b7bdec9d33f44f31d go1.23.3.windows-amd64.zip -c9951eecad732c59dfde6dc4803fa9253eb074663c61035c8d856f4d2eb146cb go1.23.3.windows-arm.msi -1a7db02be47deada42082d21d63eba0013f93375cfa0e7768962f1295a469022 go1.23.3.windows-arm.zip -a74e3e195219af4330b93c71cd4b736b709a5654a07cc37eebe181c4984afb82 go1.23.3.windows-arm64.msi -dbdfa868b1a3f8c62950373e4975d83f90dd8b869a3907319af8384919bcaffe go1.23.3.windows-arm64.zip +ad345ac421e90814293a9699cca19dd5238251c3f687980bbcae28495b263531 go1.23.4.src.tar.gz +459a09504f7ebf2cbcee6ac282c8f34f97651217b1feae64557dcdd392b9bb62 go1.23.4.aix-ppc64.tar.gz +0f4e569b2d38cb75cb2efcaf56beb42778ab5e46d89318fef39060fe36d7b9b7 go1.23.4.darwin-amd64.pkg +6700067389a53a1607d30aa8d6e01d198230397029faa0b109e89bc871ab5a0e go1.23.4.darwin-amd64.tar.gz +19c054eaf40c5fac65b027f7443c01382e493d3c8c42cf8b2504832ebddce037 go1.23.4.darwin-arm64.pkg +87d2bb0ad4fe24d2a0685a55df321e0efe4296419a9b3de03369dbe60b8acd3a go1.23.4.darwin-arm64.tar.gz +5e73dc89b44626677ec9d9aa4257d6d2ef1245502bc36a99385284910d0ade0a go1.23.4.dragonfly-amd64.tar.gz +8df26b1e71234756c1f0e82cfffba3f427c5a91a251844ada2c7694a6986c546 go1.23.4.freebsd-386.tar.gz +7de078d94d2af50ee9506ef7df85e4d12d4018b23e0b2cbcbc61d686f549b41a go1.23.4.freebsd-amd64.tar.gz +3f23e0a01cfe24e4160124cd7ab02bdd188264652074abdbce401c93f41e58a4 go1.23.4.freebsd-arm.tar.gz +986a20e7c94431f03b44b3c415abc698c7b4edc2ae8431f7ecae1c2429d4cfa2 go1.23.4.freebsd-arm64.tar.gz +25e39f005f977778ce963fc43089510fe7514f3cfc0358eab584de4ce9f181fb go1.23.4.freebsd-riscv64.tar.gz +7e1d52f93da68c3bab39e3d83f89944d7d151208e54fdc30b0eda2a3812661d7 go1.23.4.illumos-amd64.tar.gz +4a4a0e7587ef8c8a326439b957027f2791795e2d29d4ae3885b4091a48f843bc go1.23.4.linux-386.tar.gz +6924efde5de86fe277676e929dc9917d466efa02fb934197bc2eba35d5680971 go1.23.4.linux-amd64.tar.gz +16e5017863a7f6071363782b1b8042eb12c6ca4f4cd71528b2123f0a1275b13e go1.23.4.linux-arm64.tar.gz +1f1dda0dc7ce0b2295f57258ec5ef0803fd31b9ed0aa20e2e9222334e5755de1 go1.23.4.linux-armv6l.tar.gz +4f469179a335a1a7bb9f991ad0c567f3d3eeb9b412ecd192206ab5c3e1a52b5a go1.23.4.linux-loong64.tar.gz +86b68185bcc43ea07190e95137c3442f062acc7ae10c3f1cf900fbe23e07df24 go1.23.4.linux-mips.tar.gz +3a19245eec76533b3d01c90f3a40a38d63684028f0fd54d442dc9a9d03197891 go1.23.4.linux-mips64.tar.gz +b53a06fc8455f6a875329e8d2e24d39db298122c9cce6e948117022191f6c613 go1.23.4.linux-mips64le.tar.gz +66120a8105b8ba6559f4e6a13b1e39b433fb8032df9d1744e4486876fa1723ce go1.23.4.linux-mipsle.tar.gz +33be2bfb27f2821a65e9f6aba744c85ea7c5e233e16bac27bb3ec253bcd4e970 go1.23.4.linux-ppc64.tar.gz +65a303ef51e48ff77e004a6a5b4db6ce59495cd59c6af51b54bf4f786c01a1b9 go1.23.4.linux-ppc64le.tar.gz +7c40e9e0d722cef14ede765159ba297f4c6e3093bb106f10fbccf8564780049a go1.23.4.linux-riscv64.tar.gz +74aab82bf4eca7c26c830a5b0e2a31d193a4d5ba47045526b92473cc7188d7d7 go1.23.4.linux-s390x.tar.gz +dba009d8bf9928cb5a1e31fcbe0eb41335cce4fe63755d95cef6b5987df4ed5a go1.23.4.netbsd-386.tar.gz +54b081cc36355aa5ecb6db9544cf7e77366a7b08ce96cb98a45d043e393660c7 go1.23.4.netbsd-amd64.tar.gz +f05fec348c7c9f07e1ad4e436db4122e98de99ebcfbf6ac6176869785f334a02 go1.23.4.netbsd-arm.tar.gz +317878da2bface5a57a8eaf5c1fe2b40b1c82d8172a10453ad3eea36f6946bdb go1.23.4.netbsd-arm64.tar.gz +0d84350dfd72c505c6ad474e51676b04e95ffb748c614bd5bf8510026873059b go1.23.4.openbsd-386.tar.gz +cc62f5a14ea3d573d8edbce1833f70a8f99ca048a9db0fcc9e738fd48e950505 go1.23.4.openbsd-amd64.tar.gz +326aba6cf5bb9348fa3e41217abd2c84eac92608684e2fe8c5474fdab23a0db9 go1.23.4.openbsd-arm.tar.gz +51ea2a2588bf3da8e1476f3e2bd4d6724d74126e99f9c6ea9af4ebe389e64de6 go1.23.4.openbsd-arm64.tar.gz +44c5c82ab23e40225b2ba1e7d19150a5973ea58e93b4931e426e6e6f0d108872 go1.23.4.openbsd-ppc64.tar.gz +5fa31fc13d1e3c123a5e96ba38683fa2c947baed23ac9c7c341afcfe007c8993 go1.23.4.openbsd-riscv64.tar.gz +e5952fc93eeaa0094ef09a0e72a9f06f0621ce841a39f9637fb5b9062e77d67a go1.23.4.plan9-386.tar.gz +fb2a9ee3ae5a77e734862e257a9395b43e707ac45e060dfa84c5a40688e73170 go1.23.4.plan9-amd64.tar.gz +e1b95563b19fdebd6ea0d20c07641e69580976fa754e586c831ad7a3ae987140 go1.23.4.plan9-arm.tar.gz +088c282509fc9e1a8f29fc0dd16fe486854d05b8ceba08d077d17d11d6979a41 go1.23.4.solaris-amd64.tar.gz +e5865c1bfc3fee5d003819b2e2c800f598fe9994931bac63f573e8d05a10d91f go1.23.4.windows-386.msi +e544e0e356147ba998e267002bd0f2c4bf3370d495467a55baf2c63595a2026d go1.23.4.windows-386.zip +5f8cc5991eb8f4f96b6c611d839453cd11c9a2c3f23672a4188342c97ee159fa go1.23.4.windows-amd64.msi +16c59ac9196b63afb872ce9b47f945b9821a3e1542ec125f16f6085a1c0f3c39 go1.23.4.windows-amd64.zip +fc77c0531406d092c5356167e45c05a22d16bea84e3fa555e0f03af085c11763 go1.23.4.windows-arm.msi +1012cfd8ca7241c2beecb5c345dd61f01897c6f6baca80ea1bfed357035c868a go1.23.4.windows-arm.zip +8347c1aa4e1e67954d12830f88dbe44bd7ac0ec134bb472783dbfb5a3a8865d0 go1.23.4.windows-arm64.msi +db69cae5006753c785345c3215ad941f8b6224e2f81fec471c42d6857bee0e6f go1.23.4.windows-arm64.zip # version:golangci 1.61.0 # https://github.com/golangci/golangci-lint/releases/ diff --git a/build/ci.go b/build/ci.go index 8722098739..929a45ea4a 100644 --- a/build/ci.go +++ b/build/ci.go @@ -261,7 +261,7 @@ func buildFlags(env build.Environment, staticLinking bool, buildTags []string) ( // See https://sourceware.org/binutils/docs-2.23.1/ld/Options.html#Options // regarding the options --build-id=none and --strip-all. It is needed for // reproducible builds; removing references to temporary files in C-land, and - // making build-id reproducably absent. + // making build-id reproducibly absent. extld := []string{"-Wl,-z,stack-size=0x800000,--build-id=none,--strip-all"} if staticLinking { extld = append(extld, "-static") diff --git a/cmd/evm/blockrunner.go b/cmd/evm/blockrunner.go index d5cd8d8e3d..2cb0531e28 100644 --- a/cmd/evm/blockrunner.go +++ b/cmd/evm/blockrunner.go @@ -22,79 +22,84 @@ import ( "fmt" "os" "regexp" - "sort" + "slices" "github.com/ethereum/go-ethereum/core" "github.com/ethereum/go-ethereum/core/rawdb" - "github.com/ethereum/go-ethereum/core/tracing" - "github.com/ethereum/go-ethereum/eth/tracers/logger" "github.com/ethereum/go-ethereum/tests" "github.com/urfave/cli/v2" + "golang.org/x/exp/maps" ) -var RunFlag = &cli.StringFlag{ - Name: "run", - Value: ".*", - Usage: "Run only those tests matching the regular expression.", -} - var blockTestCommand = &cli.Command{ Action: blockTestCmd, Name: "blocktest", Usage: "Executes the given blockchain tests", - ArgsUsage: "", - Flags: []cli.Flag{RunFlag}, + ArgsUsage: "", + Flags: slices.Concat([]cli.Flag{ + DumpFlag, + HumanReadableFlag, + RunFlag, + WitnessCrossCheckFlag, + }, traceFlags), } func blockTestCmd(ctx *cli.Context) error { - if len(ctx.Args().First()) == 0 { - return errors.New("path-to-test argument required") + path := ctx.Args().First() + if len(path) == 0 { + return errors.New("path argument required") } - - var tracer *tracing.Hooks - // Configure the EVM logger - if ctx.Bool(MachineFlag.Name) { - tracer = logger.NewJSONLogger(&logger.Config{ - EnableMemory: !ctx.Bool(DisableMemoryFlag.Name), - DisableStack: ctx.Bool(DisableStackFlag.Name), - DisableStorage: ctx.Bool(DisableStorageFlag.Name), - EnableReturnData: !ctx.Bool(DisableReturnDataFlag.Name), - }, os.Stderr) + var ( + collected = collectJSONFiles(path) + results []testResult + ) + for _, fname := range collected { + r, err := runBlockTest(ctx, fname) + if err != nil { + return err + } + results = append(results, r...) } - // Load the test content from the input file - src, err := os.ReadFile(ctx.Args().First()) + report(ctx, results) + return nil +} + +func runBlockTest(ctx *cli.Context, fname string) ([]testResult, error) { + src, err := os.ReadFile(fname) if err != nil { - return err + return nil, err } - var tests map[string]tests.BlockTest + var tests map[string]*tests.BlockTest if err = json.Unmarshal(src, &tests); err != nil { - return err + return nil, err } re, err := regexp.Compile(ctx.String(RunFlag.Name)) if err != nil { - return fmt.Errorf("invalid regex -%s: %v", RunFlag.Name, err) + return nil, fmt.Errorf("invalid regex -%s: %v", RunFlag.Name, err) } + tracer := tracerFromFlags(ctx) - // Run them in order - var keys []string - for key := range tests { - keys = append(keys, key) - } - sort.Strings(keys) + // Pull out keys to sort and ensure tests are run in order. + keys := maps.Keys(tests) + slices.Sort(keys) + + // Run all the tests. + var results []testResult for _, name := range keys { if !re.MatchString(name) { continue } - test := tests[name] - if err := test.Run(false, rawdb.HashScheme, false, tracer, func(res error, chain *core.BlockChain) { + result := &testResult{Name: name, Pass: true} + if err := tests[name].Run(false, rawdb.HashScheme, ctx.Bool(WitnessCrossCheckFlag.Name), tracer, func(res error, chain *core.BlockChain) { if ctx.Bool(DumpFlag.Name) { - if state, _ := chain.State(); state != nil { - fmt.Println(string(state.Dump(nil))) + if s, _ := chain.State(); s != nil { + result.State = dump(s) } } }); err != nil { - return fmt.Errorf("test %v: %w", name, err) + result.Pass, result.Error = false, err.Error() } + results = append(results, *result) } - return nil + return results, nil } diff --git a/cmd/evm/compiler.go b/cmd/evm/compiler.go deleted file mode 100644 index c071834b59..0000000000 --- a/cmd/evm/compiler.go +++ /dev/null @@ -1,55 +0,0 @@ -// Copyright 2017 The go-ethereum Authors -// This file is part of go-ethereum. -// -// go-ethereum is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// go-ethereum is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with go-ethereum. If not, see . - -package main - -import ( - "errors" - "fmt" - "os" - - "github.com/ethereum/go-ethereum/cmd/evm/internal/compiler" - - "github.com/urfave/cli/v2" -) - -var compileCommand = &cli.Command{ - Action: compileCmd, - Name: "compile", - Usage: "Compiles easm source to evm binary", - ArgsUsage: "", -} - -func compileCmd(ctx *cli.Context) error { - debug := ctx.Bool(DebugFlag.Name) - - if len(ctx.Args().First()) == 0 { - return errors.New("filename required") - } - - fn := ctx.Args().First() - src, err := os.ReadFile(fn) - if err != nil { - return err - } - - bin, err := compiler.Compile(fn, src, debug) - if err != nil { - return err - } - fmt.Println(bin) - return nil -} diff --git a/cmd/evm/disasm.go b/cmd/evm/disasm.go deleted file mode 100644 index b1f35cbaf5..0000000000 --- a/cmd/evm/disasm.go +++ /dev/null @@ -1,55 +0,0 @@ -// Copyright 2017 The go-ethereum Authors -// This file is part of go-ethereum. -// -// go-ethereum is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// go-ethereum is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with go-ethereum. If not, see . - -package main - -import ( - "errors" - "fmt" - "os" - "strings" - - "github.com/ethereum/go-ethereum/core/asm" - "github.com/urfave/cli/v2" -) - -var disasmCommand = &cli.Command{ - Action: disasmCmd, - Name: "disasm", - Usage: "Disassembles evm binary", - ArgsUsage: "", -} - -func disasmCmd(ctx *cli.Context) error { - var in string - switch { - case len(ctx.Args().First()) > 0: - fn := ctx.Args().First() - input, err := os.ReadFile(fn) - if err != nil { - return err - } - in = string(input) - case ctx.IsSet(InputFlag.Name): - in = ctx.String(InputFlag.Name) - default: - return errors.New("missing filename or --input value") - } - - code := strings.TrimSpace(in) - fmt.Printf("%v\n", code) - return asm.PrintDisassembled(code) -} diff --git a/cmd/evm/eest.go b/cmd/evm/eest.go new file mode 100644 index 0000000000..43071a3e5d --- /dev/null +++ b/cmd/evm/eest.go @@ -0,0 +1,49 @@ +// Copyright 2024 The go-ethereum Authors +// This file is part of go-ethereum. +// +// go-ethereum is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// go-ethereum is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with go-ethereum. If not, see . + +package main + +import "regexp" + +// testMetadata provides more granular access to the test information encoded +// within its filename by the execution spec test (EEST). +type testMetadata struct { + fork string + module string // which python module gnerated the test, e.g. eip7702 + file string // exact file the test came from, e.g. test_gas.py + function string // func that created the test, e.g. test_valid_mcopy_operations + parameters string // the name of the parameters which were used to fill the test, e.g. zero_inputs +} + +// parseTestMetadata reads a test name and parses out more specific information +// about the test. +func parseTestMetadata(s string) *testMetadata { + var ( + pattern = `tests\/([^\/]+)\/([^\/]+)\/([^:]+)::([^[]+)\[fork_([^-\]]+)-[^-]+-(.+)\]` + re = regexp.MustCompile(pattern) + ) + match := re.FindStringSubmatch(s) + if len(match) == 0 { + return nil + } + return &testMetadata{ + fork: match[5], + module: match[2], + file: match[3], + function: match[4], + parameters: match[6], + } +} diff --git a/cmd/evm/eofparse.go b/cmd/evm/eofparse.go index 2122270942..df8581146a 100644 --- a/cmd/evm/eofparse.go +++ b/cmd/evm/eofparse.go @@ -31,13 +31,41 @@ import ( "github.com/urfave/cli/v2" ) +var jt vm.JumpTable + +const initcode = "INITCODE" + func init() { jt = vm.NewPragueEOFInstructionSetForTesting() } var ( - jt vm.JumpTable - initcode = "INITCODE" + hexFlag = &cli.StringFlag{ + Name: "hex", + Usage: "Single container data parse and validation", + } + refTestFlag = &cli.StringFlag{ + Name: "test", + Usage: "Path to EOF validation reference test.", + } + eofParseCommand = &cli.Command{ + Name: "eofparse", + Aliases: []string{"eof"}, + Usage: "Parses hex eof container and returns validation errors (if any)", + Action: eofParseAction, + Flags: []cli.Flag{ + hexFlag, + refTestFlag, + }, + } + eofDumpCommand = &cli.Command{ + Name: "eofdump", + Usage: "Parses hex eof container and prints out human-readable representation of the container.", + Action: eofDumpAction, + Flags: []cli.Flag{ + hexFlag, + }, + } ) func eofParseAction(ctx *cli.Context) error { diff --git a/cmd/evm/internal/compiler/compiler.go b/cmd/evm/internal/compiler/compiler.go deleted file mode 100644 index 54981b6697..0000000000 --- a/cmd/evm/internal/compiler/compiler.go +++ /dev/null @@ -1,39 +0,0 @@ -// Copyright 2017 The go-ethereum Authors -// This file is part of go-ethereum. -// -// go-ethereum is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// go-ethereum is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with go-ethereum. If not, see . - -package compiler - -import ( - "errors" - "fmt" - - "github.com/ethereum/go-ethereum/core/asm" -) - -func Compile(fn string, src []byte, debug bool) (string, error) { - compiler := asm.NewCompiler(debug) - compiler.Feed(asm.Lex(src, debug)) - - bin, compileErrors := compiler.Compile() - if len(compileErrors) > 0 { - // report errors - for _, err := range compileErrors { - fmt.Printf("%s:%v\n", fn, err) - } - return "", errors.New("compiling failed") - } - return bin, nil -} diff --git a/cmd/evm/internal/t8ntool/execution.go b/cmd/evm/internal/t8ntool/execution.go index 629ef9e250..62e3e19d2d 100644 --- a/cmd/evm/internal/t8ntool/execution.go +++ b/cmd/evm/internal/t8ntool/execution.go @@ -201,17 +201,16 @@ func (pre *Prestate) Apply(vmConfig vm.Config, chainConfig *params.ChainConfig, chainConfig.DAOForkBlock.Cmp(new(big.Int).SetUint64(pre.Env.Number)) == 0 { misc.ApplyDAOHardFork(statedb) } + evm := vm.NewEVM(vmContext, statedb, chainConfig, vmConfig) if beaconRoot := pre.Env.ParentBeaconBlockRoot; beaconRoot != nil { - evm := vm.NewEVM(vmContext, vm.TxContext{}, statedb, chainConfig, vmConfig) - core.ProcessBeaconBlockRoot(*beaconRoot, evm, statedb) + core.ProcessBeaconBlockRoot(*beaconRoot, evm) } if pre.Env.BlockHashes != nil && chainConfig.IsPrague(new(big.Int).SetUint64(pre.Env.Number), pre.Env.Timestamp) { var ( prevNumber = pre.Env.Number - 1 prevHash = pre.Env.BlockHashes[math.HexOrDecimal64(prevNumber)] - evm = vm.NewEVM(vmContext, vm.TxContext{}, statedb, chainConfig, vmConfig) ) - core.ProcessParentBlockHash(prevHash, evm, statedb) + core.ProcessParentBlockHash(prevHash, evm) } for i := 0; txIt.Next(); i++ { tx, err := txIt.Tx() @@ -246,18 +245,17 @@ func (pre *Prestate) Apply(vmConfig vm.Config, chainConfig *params.ChainConfig, if err != nil { return nil, nil, nil, err } + // TODO (rjl493456442) it's a bit weird to reset the tracer in the + // middle of block execution, please improve it somehow. if tracer != nil { - vmConfig.Tracer = tracer.Hooks + evm.SetTracer(tracer.Hooks) } statedb.SetTxContext(tx.Hash(), txIndex) var ( - txContext = core.NewEVMTxContext(msg) - snapshot = statedb.Snapshot() - prevGas = gaspool.Gas() + snapshot = statedb.Snapshot() + prevGas = gaspool.Gas() ) - evm := vm.NewEVM(vmContext, txContext, statedb, chainConfig, vmConfig) - if tracer != nil && tracer.OnTxStart != nil { tracer.OnTxStart(evm.GetVMContext(), tx, msg.From) } @@ -365,22 +363,19 @@ func (pre *Prestate) Apply(vmConfig vm.Config, chainConfig *params.ChainConfig, // Gather the execution-layer triggered requests. var requests [][]byte if chainConfig.IsPrague(vmContext.BlockNumber, vmContext.Time) { - // EIP-6110 deposits + requests = [][]byte{} + // EIP-6110 var allLogs []*types.Log for _, receipt := range receipts { allLogs = append(allLogs, receipt.Logs...) } - depositRequests, err := core.ParseDepositLogs(allLogs, chainConfig) - if err != nil { + if err := core.ParseDepositLogs(&requests, allLogs, chainConfig); err != nil { return nil, nil, nil, NewError(ErrorEVM, fmt.Errorf("could not parse requests logs: %v", err)) } - requests = append(requests, depositRequests) - // create EVM for system calls - vmenv := vm.NewEVM(vmContext, vm.TxContext{}, statedb, chainConfig, vm.Config{}) - // EIP-7002 withdrawals - requests = append(requests, core.ProcessWithdrawalQueue(vmenv, statedb)) - // EIP-7251 consolidations - requests = append(requests, core.ProcessConsolidationQueue(vmenv, statedb)) + // EIP-7002 + core.ProcessWithdrawalQueue(&requests, evm) + // EIP-7251 + core.ProcessConsolidationQueue(&requests, evm) } // Commit block diff --git a/cmd/evm/internal/t8ntool/transition.go b/cmd/evm/internal/t8ntool/transition.go index 1e2ab0004e..33e244f947 100644 --- a/cmd/evm/internal/t8ntool/transition.go +++ b/cmd/evm/internal/t8ntool/transition.go @@ -97,7 +97,6 @@ func Transition(ctx *cli.Context) error { DisableStack: ctx.Bool(TraceDisableStackFlag.Name), EnableMemory: ctx.Bool(TraceEnableMemoryFlag.Name), EnableReturnData: ctx.Bool(TraceEnableReturnDataFlag.Name), - Debug: true, } getTracer = func(txIndex int, txHash common.Hash, _ *params.ChainConfig) (*tracers.Tracer, io.WriteCloser, error) { traceFile, err := os.Create(filepath.Join(baseDir, fmt.Sprintf("trace-%d-%v.jsonl", txIndex, txHash.String()))) diff --git a/cmd/evm/main.go b/cmd/evm/main.go index 0d4471b8d5..0e6f6fa5be 100644 --- a/cmd/evm/main.go +++ b/cmd/evm/main.go @@ -19,11 +19,14 @@ package main import ( "fmt" - "math/big" + "io/fs" "os" - "slices" + "path/filepath" "github.com/ethereum/go-ethereum/cmd/evm/internal/t8ntool" + "github.com/ethereum/go-ethereum/core/state" + "github.com/ethereum/go-ethereum/core/tracing" + "github.com/ethereum/go-ethereum/eth/tracers/logger" "github.com/ethereum/go-ethereum/internal/debug" "github.com/ethereum/go-ethereum/internal/flags" "github.com/urfave/cli/v2" @@ -33,122 +36,100 @@ import ( _ "github.com/ethereum/go-ethereum/eth/tracers/native" ) +// Some other nice-to-haves: +// * accumulate traces into an object to bundle with test +// * write tx identifier for trace before hand (blocktest only) +// * combine blocktest and statetest runner logic using unified test interface + +const traceCategory = "TRACING" + var ( - DebugFlag = &cli.BoolFlag{ - Name: "debug", - Usage: "output full trace logs", - Category: flags.VMCategory, - } - StatDumpFlag = &cli.BoolFlag{ - Name: "statdump", - Usage: "displays stack and heap memory information", - Category: flags.VMCategory, - } - CodeFlag = &cli.StringFlag{ - Name: "code", - Usage: "EVM code", - Category: flags.VMCategory, - } - CodeFileFlag = &cli.StringFlag{ - Name: "codefile", - Usage: "File containing EVM code. If '-' is specified, code is read from stdin ", - Category: flags.VMCategory, - } - GasFlag = &cli.Uint64Flag{ - Name: "gas", - Usage: "gas limit for the evm", - Value: 10000000000, - Category: flags.VMCategory, - } - PriceFlag = &flags.BigFlag{ - Name: "price", - Usage: "price set for the evm", - Value: new(big.Int), - Category: flags.VMCategory, - } - ValueFlag = &flags.BigFlag{ - Name: "value", - Usage: "value set for the evm", - Value: new(big.Int), - Category: flags.VMCategory, - } - DumpFlag = &cli.BoolFlag{ - Name: "dump", - Usage: "dumps the state after the run", - Category: flags.VMCategory, - } - InputFlag = &cli.StringFlag{ - Name: "input", - Usage: "input for the EVM", - Category: flags.VMCategory, - } - InputFileFlag = &cli.StringFlag{ - Name: "inputfile", - Usage: "file containing input for the EVM", - Category: flags.VMCategory, + // Test running flags. + RunFlag = &cli.StringFlag{ + Name: "run", + Value: ".*", + Usage: "Run only those tests matching the regular expression.", } BenchFlag = &cli.BoolFlag{ Name: "bench", Usage: "benchmark the execution", Category: flags.VMCategory, } - CreateFlag = &cli.BoolFlag{ - Name: "create", - Usage: "indicates the action should be create rather than call", - Category: flags.VMCategory, + WitnessCrossCheckFlag = &cli.BoolFlag{ + Name: "cross-check", + Aliases: []string{"xc"}, + Usage: "Cross-check stateful execution against stateless, verifying the witness generation.", } - GenesisFlag = &cli.StringFlag{ - Name: "prestate", - Usage: "JSON file with prestate (genesis) config", - Category: flags.VMCategory, + + // Debugging flags. + DumpFlag = &cli.BoolFlag{ + Name: "dump", + Usage: "dumps the state after the run", } - MachineFlag = &cli.BoolFlag{ - Name: "json", - Usage: "output trace logs in machine readable format (json)", - Category: flags.VMCategory, + HumanReadableFlag = &cli.BoolFlag{ + Name: "human", + Usage: "\"Human-readable\" output", } - SenderFlag = &cli.StringFlag{ - Name: "sender", - Usage: "The transaction origin", - Category: flags.VMCategory, + StatDumpFlag = &cli.BoolFlag{ + Name: "statdump", + Usage: "displays stack and heap memory information", } - ReceiverFlag = &cli.StringFlag{ - Name: "receiver", - Usage: "The transaction receiver (execution context)", - Category: flags.VMCategory, + + // Tracing flags. + TraceFlag = &cli.BoolFlag{ + Name: "trace", + Usage: "Enable tracing and output trace log.", + Category: traceCategory, } - DisableMemoryFlag = &cli.BoolFlag{ - Name: "nomemory", + TraceFormatFlag = &cli.StringFlag{ + Name: "trace.format", + Usage: "Trace output format to use (struct|json)", + Value: "struct", + Category: traceCategory, + } + TraceDisableMemoryFlag = &cli.BoolFlag{ + Name: "trace.nomemory", + Aliases: []string{"nomemory"}, Value: true, Usage: "disable memory output", - Category: flags.VMCategory, + Category: traceCategory, } - DisableStackFlag = &cli.BoolFlag{ - Name: "nostack", + TraceDisableStackFlag = &cli.BoolFlag{ + Name: "trace.nostack", + Aliases: []string{"nostack"}, Usage: "disable stack output", - Category: flags.VMCategory, + Category: traceCategory, } - DisableStorageFlag = &cli.BoolFlag{ - Name: "nostorage", + TraceDisableStorageFlag = &cli.BoolFlag{ + Name: "trace.nostorage", + Aliases: []string{"nostorage"}, Usage: "disable storage output", - Category: flags.VMCategory, + Category: traceCategory, } - DisableReturnDataFlag = &cli.BoolFlag{ - Name: "noreturndata", + TraceDisableReturnDataFlag = &cli.BoolFlag{ + Name: "trace.noreturndata", + Aliases: []string{"noreturndata"}, Value: true, Usage: "enable return data output", - Category: flags.VMCategory, + Category: traceCategory, } - refTestFlag = &cli.StringFlag{ - Name: "test", - Usage: "Path to EOF validation reference test.", + + // Deprecated flags. + DebugFlag = &cli.BoolFlag{ + Name: "debug", + Usage: "output full trace logs (deprecated)", + Hidden: true, + Category: traceCategory, } - hexFlag = &cli.StringFlag{ - Name: "hex", - Usage: "single container data parse and validation", + MachineFlag = &cli.BoolFlag{ + Name: "json", + Usage: "output trace logs in machine readable format, json (deprecated)", + Hidden: true, + Category: traceCategory, } ) +// Command definitions. var ( stateTransitionCommand = &cli.Command{ Name: "transition", @@ -175,7 +156,6 @@ var ( t8ntool.RewardFlag, }, } - transactionCommand = &cli.Command{ Name: "transaction", Aliases: []string{"t9n"}, @@ -203,62 +183,27 @@ var ( t8ntool.SealCliqueFlag, }, } - eofParseCommand = &cli.Command{ - Name: "eofparse", - Aliases: []string{"eof"}, - Usage: "Parses hex eof container and returns validation errors (if any)", - Action: eofParseAction, - Flags: []cli.Flag{ - hexFlag, - refTestFlag, - }, - } - - eofDumpCommand = &cli.Command{ - Name: "eofdump", - Usage: "Parses hex eof container and prints out human-readable representation of the container.", - Action: eofDumpAction, - Flags: []cli.Flag{ - hexFlag, - }, - } ) -// vmFlags contains flags related to running the EVM. -var vmFlags = []cli.Flag{ - CodeFlag, - CodeFileFlag, - CreateFlag, - GasFlag, - PriceFlag, - ValueFlag, - InputFlag, - InputFileFlag, - GenesisFlag, - SenderFlag, - ReceiverFlag, -} - // traceFlags contains flags that configure tracing output. var traceFlags = []cli.Flag{ - BenchFlag, + TraceFlag, + TraceFormatFlag, + TraceDisableStackFlag, + TraceDisableMemoryFlag, + TraceDisableStorageFlag, + TraceDisableReturnDataFlag, + + // deprecated DebugFlag, - DumpFlag, MachineFlag, - StatDumpFlag, - DisableMemoryFlag, - DisableStackFlag, - DisableStorageFlag, - DisableReturnDataFlag, } var app = flags.NewApp("the evm command line interface") func init() { - app.Flags = slices.Concat(vmFlags, traceFlags, debug.Flags) + app.Flags = debug.Flags app.Commands = []*cli.Command{ - compileCommand, - disasmCommand, runCommand, blockTestCommand, stateTestCommand, @@ -280,11 +225,66 @@ func init() { func main() { if err := app.Run(os.Args); err != nil { - code := 1 - if ec, ok := err.(*t8ntool.NumberedError); ok { - code = ec.ExitCode() + fmt.Fprintln(os.Stderr, err) + os.Exit(1) + } +} + +// tracerFromFlags parses the cli flags and returns the specified tracer. +func tracerFromFlags(ctx *cli.Context) *tracing.Hooks { + config := &logger.Config{ + EnableMemory: !ctx.Bool(TraceDisableMemoryFlag.Name), + DisableStack: ctx.Bool(TraceDisableStackFlag.Name), + DisableStorage: ctx.Bool(TraceDisableStorageFlag.Name), + EnableReturnData: !ctx.Bool(TraceDisableReturnDataFlag.Name), + } + switch { + case ctx.Bool(TraceFlag.Name): + switch format := ctx.String(TraceFormatFlag.Name); format { + case "struct": + return logger.NewStreamingStructLogger(config, os.Stderr).Hooks() + case "json": + return logger.NewJSONLogger(config, os.Stderr) + case "md", "markdown": + return logger.NewMarkdownLogger(config, os.Stderr).Hooks() + default: + fmt.Fprintf(os.Stderr, "unknown trace format: %q\n", format) + os.Exit(1) + return nil } + // Deprecated ways of configuring tracing. + case ctx.Bool(MachineFlag.Name): + return logger.NewJSONLogger(config, os.Stderr) + case ctx.Bool(DebugFlag.Name): + return logger.NewStreamingStructLogger(config, os.Stderr).Hooks() + default: + return nil + } +} + +// collectJSONFiles walks the given path and accumulates all files with json +// extension. +func collectJSONFiles(path string) []string { + var out []string + err := filepath.Walk(path, func(path string, info fs.FileInfo, err error) error { + if err != nil { + return err + } + if !info.IsDir() && filepath.Ext(info.Name()) == ".json" { + out = append(out, path) + } + return nil + }) + if err != nil { fmt.Fprintln(os.Stderr, err) - os.Exit(code) } + return out +} + +// dump returns a state dump for the most current trie. +func dump(s *state.StateDB) *state.Dump { + root := s.IntermediateRoot(false) + cpy, _ := state.New(root, s.Database()) + dump := cpy.RawDump(nil) + return &dump } diff --git a/cmd/evm/reporter.go b/cmd/evm/reporter.go new file mode 100644 index 0000000000..f6249e1843 --- /dev/null +++ b/cmd/evm/reporter.go @@ -0,0 +1,87 @@ +// Copyright 2024 The go-ethereum Authors +// This file is part of go-ethereum. +// +// go-ethereum is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// go-ethereum is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with go-ethereum. If not, see . + +package main + +import ( + "encoding/json" + "fmt" + + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/core/state" + "github.com/urfave/cli/v2" +) + +const ( + PASS = "\033[32mPASS\033[0m" + FAIL = "\033[31mFAIL\033[0m" +) + +// testResult contains the execution status after running a state test, any +// error that might have occurred and a dump of the final state if requested. +type testResult struct { + Name string `json:"name"` + Pass bool `json:"pass"` + Root *common.Hash `json:"stateRoot,omitempty"` + Fork string `json:"fork"` + Error string `json:"error,omitempty"` + State *state.Dump `json:"state,omitempty"` + Stats *execStats `json:"benchStats,omitempty"` +} + +func (r testResult) String() string { + var status string + if r.Pass { + status = fmt.Sprintf("[%s]", PASS) + } else { + status = fmt.Sprintf("[%s]", FAIL) + } + info := r.Name + m := parseTestMetadata(r.Name) + if m != nil { + info = fmt.Sprintf("%s %s, param=%s", m.module, m.function, m.parameters) + } + var extra string + if !r.Pass { + extra = fmt.Sprintf(", err=%v, fork=%s", r.Error, r.Fork) + } + out := fmt.Sprintf("%s %s%s", status, info, extra) + if r.State != nil { + state, _ := json.MarshalIndent(r.State, "", " ") + out += "\n" + string(state) + } + return out +} + +// report prints the after-test summary. +func report(ctx *cli.Context, results []testResult) { + if ctx.Bool(HumanReadableFlag.Name) { + pass := 0 + for _, r := range results { + if r.Pass { + pass++ + } + } + for _, r := range results { + fmt.Println(r) + } + fmt.Println("--") + fmt.Printf("%d tests passed, %d tests failed.\n", pass, len(results)-pass) + return + } + out, _ := json.MarshalIndent(results, "", " ") + fmt.Println(string(out)) +} diff --git a/cmd/evm/runner.go b/cmd/evm/runner.go index 11acf209d1..ab1b140f9f 100644 --- a/cmd/evm/runner.go +++ b/cmd/evm/runner.go @@ -18,6 +18,7 @@ package main import ( "bytes" + "encoding/hex" "encoding/json" "fmt" "io" @@ -25,19 +26,19 @@ import ( "os" goruntime "runtime" "slices" + "strings" "testing" "time" - "github.com/ethereum/go-ethereum/cmd/evm/internal/compiler" "github.com/ethereum/go-ethereum/cmd/utils" "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/core" "github.com/ethereum/go-ethereum/core/rawdb" "github.com/ethereum/go-ethereum/core/state" "github.com/ethereum/go-ethereum/core/tracing" + "github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/core/vm" "github.com/ethereum/go-ethereum/core/vm/runtime" - "github.com/ethereum/go-ethereum/eth/tracers/logger" "github.com/ethereum/go-ethereum/internal/flags" "github.com/ethereum/go-ethereum/params" "github.com/ethereum/go-ethereum/triedb" @@ -51,14 +52,83 @@ var runCommand = &cli.Command{ Usage: "Run arbitrary evm binary", ArgsUsage: "", Description: `The run command runs arbitrary EVM code.`, - Flags: slices.Concat(vmFlags, traceFlags), + Flags: slices.Concat([]cli.Flag{ + BenchFlag, + CodeFileFlag, + CreateFlag, + GasFlag, + GenesisFlag, + InputFlag, + InputFileFlag, + PriceFlag, + ReceiverFlag, + SenderFlag, + ValueFlag, + StatDumpFlag, + DumpFlag, + }, traceFlags), } +var ( + CodeFileFlag = &cli.StringFlag{ + Name: "codefile", + Usage: "File containing EVM code. If '-' is specified, code is read from stdin ", + Category: flags.VMCategory, + } + CreateFlag = &cli.BoolFlag{ + Name: "create", + Usage: "Indicates the action should be create rather than call", + Category: flags.VMCategory, + } + GasFlag = &cli.Uint64Flag{ + Name: "gas", + Usage: "Gas limit for the evm", + Value: 10000000000, + Category: flags.VMCategory, + } + GenesisFlag = &cli.StringFlag{ + Name: "prestate", + Usage: "JSON file with prestate (genesis) config", + Category: flags.VMCategory, + } + InputFlag = &cli.StringFlag{ + Name: "input", + Usage: "Input for the EVM", + Category: flags.VMCategory, + } + InputFileFlag = &cli.StringFlag{ + Name: "inputfile", + Usage: "File containing input for the EVM", + Category: flags.VMCategory, + } + PriceFlag = &flags.BigFlag{ + Name: "price", + Usage: "Price set for the evm", + Value: new(big.Int), + Category: flags.VMCategory, + } + ReceiverFlag = &cli.StringFlag{ + Name: "receiver", + Usage: "The transaction receiver (execution context)", + Category: flags.VMCategory, + } + SenderFlag = &cli.StringFlag{ + Name: "sender", + Usage: "The transaction origin", + Category: flags.VMCategory, + } + ValueFlag = &flags.BigFlag{ + Name: "value", + Usage: "Value set for the evm", + Value: new(big.Int), + Category: flags.VMCategory, + } +) + // readGenesis will read the given JSON format genesis file and return // the initialized Genesis structure func readGenesis(genesisPath string) *core.Genesis { // Make sure we have a valid genesis JSON - //genesisPath := ctx.Args().First() if len(genesisPath) == 0 { utils.Fatalf("Must supply path to genesis JSON file") } @@ -84,19 +154,20 @@ type execStats struct { func timedExec(bench bool, execFunc func() ([]byte, uint64, error)) ([]byte, execStats, error) { if bench { + testing.Init() // Do one warm-up run output, gasUsed, err := execFunc() result := testing.Benchmark(func(b *testing.B) { for i := 0; i < b.N; i++ { haveOutput, haveGasUsed, haveErr := execFunc() if !bytes.Equal(haveOutput, output) { - b.Fatalf("output differs, have\n%x\nwant%x\n", haveOutput, output) + panic(fmt.Sprintf("output differs\nhave %x\nwant %x\n", haveOutput, output)) } if haveGasUsed != gasUsed { - b.Fatalf("gas differs, have %v want%v", haveGasUsed, gasUsed) + panic(fmt.Sprintf("gas differs, have %v want %v", haveGasUsed, gasUsed)) } if haveErr != err { - b.Fatalf("err differs, have %v want%v", haveErr, err) + panic(fmt.Sprintf("err differs, have %v want %v", haveErr, err)) } } }) @@ -126,18 +197,9 @@ func timedExec(bench bool, execFunc func() ([]byte, uint64, error)) ([]byte, exe } func runCmd(ctx *cli.Context) error { - logconfig := &logger.Config{ - EnableMemory: !ctx.Bool(DisableMemoryFlag.Name), - DisableStack: ctx.Bool(DisableStackFlag.Name), - DisableStorage: ctx.Bool(DisableStorageFlag.Name), - EnableReturnData: !ctx.Bool(DisableReturnDataFlag.Name), - Debug: ctx.Bool(DebugFlag.Name), - } - var ( tracer *tracing.Hooks - debugLogger *logger.StructLogger - statedb *state.StateDB + prestate *state.StateDB chainConfig *params.ChainConfig sender = common.BytesToAddress([]byte("sender")) receiver = common.BytesToAddress([]byte("receiver")) @@ -145,15 +207,7 @@ func runCmd(ctx *cli.Context) error { blobHashes []common.Hash // TODO (MariusVanDerWijden) implement blob hashes in state tests blobBaseFee = new(big.Int) // TODO (MariusVanDerWijden) implement blob fee in state tests ) - if ctx.Bool(MachineFlag.Name) { - tracer = logger.NewJSONLogger(logconfig, os.Stdout) - } else if ctx.Bool(DebugFlag.Name) { - debugLogger = logger.NewStructLogger(logconfig) - tracer = debugLogger.Hooks() - } else { - debugLogger = logger.NewStructLogger(logconfig) - } - + tracer = tracerFromFlags(ctx) initialGas := ctx.Uint64(GasFlag.Name) genesisConfig := new(core.Genesis) genesisConfig.GasLimit = initialGas @@ -174,7 +228,7 @@ func runCmd(ctx *cli.Context) error { defer triedb.Close() genesis := genesisConfig.MustCommit(db, triedb) sdb := state.NewDatabase(triedb, nil) - statedb, _ = state.New(genesis.Root(), sdb) + prestate, _ = state.New(genesis.Root(), sdb) chainConfig = genesisConfig.Config if ctx.String(SenderFlag.Name) != "" { @@ -187,51 +241,38 @@ func runCmd(ctx *cli.Context) error { var code []byte codeFileFlag := ctx.String(CodeFileFlag.Name) - codeFlag := ctx.String(CodeFlag.Name) + hexcode := ctx.Args().First() - // The '--code' or '--codefile' flag overrides code in state - if codeFileFlag != "" || codeFlag != "" { - var hexcode []byte - if codeFileFlag != "" { - var err error - // If - is specified, it means that code comes from stdin - if codeFileFlag == "-" { - //Try reading from stdin - if hexcode, err = io.ReadAll(os.Stdin); err != nil { - fmt.Printf("Could not load code from stdin: %v\n", err) - os.Exit(1) - } - } else { - // Codefile with hex assembly - if hexcode, err = os.ReadFile(codeFileFlag); err != nil { - fmt.Printf("Could not load code from file: %v\n", err) - os.Exit(1) - } - } - } else { - hexcode = []byte(codeFlag) - } - hexcode = bytes.TrimSpace(hexcode) - if len(hexcode)%2 != 0 { - fmt.Printf("Invalid input length for hex data (%d)\n", len(hexcode)) - os.Exit(1) - } - code = common.FromHex(string(hexcode)) - } else if fn := ctx.Args().First(); len(fn) > 0 { - // EASM-file to compile - src, err := os.ReadFile(fn) + // The '--codefile' flag overrides code in state + if codeFileFlag == "-" { + // If - is specified, it means that code comes from stdin + // Try reading from stdin + input, err := io.ReadAll(os.Stdin) if err != nil { - return err + fmt.Printf("Could not load code from stdin: %v\n", err) + os.Exit(1) } - bin, err := compiler.Compile(fn, src, false) + hexcode = string(input) + } else if codeFileFlag != "" { + // Codefile with hex assembly + input, err := os.ReadFile(codeFileFlag) if err != nil { - return err + fmt.Printf("Could not load code from file: %v\n", err) + os.Exit(1) } - code = common.Hex2Bytes(bin) + hexcode = string(input) } + + hexcode = strings.TrimSpace(hexcode) + if len(hexcode)%2 != 0 { + fmt.Printf("Invalid input length for hex data (%d)\n", len(hexcode)) + os.Exit(1) + } + code = common.FromHex(hexcode) + runtimeConfig := runtime.Config{ Origin: sender, - State: statedb, + State: prestate, GasLimit: initialGas, GasPrice: flags.GlobalBig(ctx, PriceFlag.Name), Value: flags.GlobalBig(ctx, ValueFlag.Name), @@ -274,14 +315,18 @@ func runCmd(ctx *cli.Context) error { if ctx.Bool(CreateFlag.Name) { input = append(code, input...) execFunc = func() ([]byte, uint64, error) { + // don't mutate the state! + runtimeConfig.State = prestate.Copy() output, _, gasLeft, err := runtime.Create(input, &runtimeConfig) return output, gasLeft, err } } else { if len(code) > 0 { - statedb.SetCode(receiver, code) + prestate.SetCode(receiver, code) } execFunc = func() ([]byte, uint64, error) { + // don't mutate the state! + runtimeConfig.State = prestate.Copy() output, gasLeft, err := runtime.Call(receiver, input, &runtimeConfig) return output, initialGas - gasLeft, err } @@ -291,7 +336,7 @@ func runCmd(ctx *cli.Context) error { output, stats, err := timedExec(bench, execFunc) if ctx.Bool(DumpFlag.Name) { - root, _, err := statedb.Commit(genesisConfig.Number, true) + root, _, err := runtimeConfig.State.Commit(genesisConfig.Number, true) if err != nil { fmt.Printf("Failed to commit changes %v\n", err) return err @@ -305,12 +350,10 @@ func runCmd(ctx *cli.Context) error { } if ctx.Bool(DebugFlag.Name) { - if debugLogger != nil { - fmt.Fprintln(os.Stderr, "#### TRACE ####") - logger.WriteTrace(os.Stderr, debugLogger.StructLogs()) + if logs := runtimeConfig.State.Logs(); len(logs) > 0 { + fmt.Fprintln(os.Stderr, "### LOGS") + writeLogs(os.Stderr, logs) } - fmt.Fprintln(os.Stderr, "#### LOGS ####") - logger.WriteLogs(os.Stderr, statedb.Logs()) } if bench || ctx.Bool(StatDumpFlag.Name) { @@ -329,3 +372,16 @@ allocated bytes: %d return nil } + +// writeLogs writes vm logs in a readable format to the given writer +func writeLogs(writer io.Writer, logs []*types.Log) { + for _, log := range logs { + fmt.Fprintf(writer, "LOG%d: %x bn=%d txi=%x\n", len(log.Topics), log.Address, log.BlockNumber, log.TxIndex) + + for i, topic := range log.Topics { + fmt.Fprintf(writer, "%08d %x\n", i, topic) + } + fmt.Fprint(writer, hex.Dump(log.Data)) + fmt.Fprintln(writer) + } +} diff --git a/cmd/evm/staterunner.go b/cmd/evm/staterunner.go index d0a0d3287c..323b7d60ab 100644 --- a/cmd/evm/staterunner.go +++ b/cmd/evm/staterunner.go @@ -21,12 +21,12 @@ import ( "encoding/json" "fmt" "os" + "regexp" + "slices" "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/core/rawdb" - "github.com/ethereum/go-ethereum/core/state" "github.com/ethereum/go-ethereum/core/vm" - "github.com/ethereum/go-ethereum/eth/tracers/logger" "github.com/ethereum/go-ethereum/internal/flags" "github.com/ethereum/go-ethereum/tests" "github.com/urfave/cli/v2" @@ -35,157 +35,124 @@ import ( var ( forkFlag = &cli.StringFlag{ Name: "statetest.fork", - Usage: "The hard-fork to run the test against", + Usage: "Only run tests for the specified fork.", Category: flags.VMCategory, } idxFlag = &cli.IntFlag{ Name: "statetest.index", - Usage: "The index of the subtest to run", + Usage: "The index of the subtest to run.", Category: flags.VMCategory, Value: -1, // default to select all subtest indices } - testNameFlag = &cli.StringFlag{ - Name: "statetest.name", - Usage: "The name of the state test to run", - Category: flags.VMCategory, - } ) var stateTestCommand = &cli.Command{ Action: stateTestCmd, Name: "statetest", Usage: "Executes the given state tests. Filenames can be fed via standard input (batch mode) or as an argument (one-off execution).", ArgsUsage: "", - Flags: []cli.Flag{ - forkFlag, - idxFlag, - testNameFlag, - }, -} - -// StatetestResult contains the execution status after running a state test, any -// error that might have occurred and a dump of the final state if requested. -type StatetestResult struct { - Name string `json:"name"` - Pass bool `json:"pass"` - Root *common.Hash `json:"stateRoot,omitempty"` - Fork string `json:"fork"` - Error string `json:"error,omitempty"` - State *state.Dump `json:"state,omitempty"` - BenchStats *execStats `json:"benchStats,omitempty"` + Flags: slices.Concat([]cli.Flag{ + DumpFlag, + HumanReadableFlag, + RunFlag, + }, traceFlags), } func stateTestCmd(ctx *cli.Context) error { - // Configure the EVM logger - config := &logger.Config{ - EnableMemory: !ctx.Bool(DisableMemoryFlag.Name), - DisableStack: ctx.Bool(DisableStackFlag.Name), - DisableStorage: ctx.Bool(DisableStorageFlag.Name), - EnableReturnData: !ctx.Bool(DisableReturnDataFlag.Name), - } - var cfg vm.Config - switch { - case ctx.Bool(MachineFlag.Name): - cfg.Tracer = logger.NewJSONLogger(config, os.Stderr) + path := ctx.Args().First() - case ctx.Bool(DebugFlag.Name): - cfg.Tracer = logger.NewStructLogger(config).Hooks() - } - // Load the test content from the input file - if len(ctx.Args().First()) != 0 { - return runStateTest(ctx, ctx.Args().First(), cfg, ctx.Bool(DumpFlag.Name), ctx.Bool(BenchFlag.Name)) + // If path is provided, run the tests at that path. + if len(path) != 0 { + var ( + collected = collectJSONFiles(path) + results []testResult + ) + for _, fname := range collected { + r, err := runStateTest(ctx, fname) + if err != nil { + return err + } + results = append(results, r...) + } + report(ctx, results) + return nil } - // Read filenames from stdin and execute back-to-back + // Otherwise, read filenames from stdin and execute back-to-back. scanner := bufio.NewScanner(os.Stdin) for scanner.Scan() { fname := scanner.Text() if len(fname) == 0 { return nil } - if err := runStateTest(ctx, fname, cfg, ctx.Bool(DumpFlag.Name), ctx.Bool(BenchFlag.Name)); err != nil { + results, err := runStateTest(ctx, fname) + if err != nil { return err } + report(ctx, results) } return nil } -type stateTestCase struct { - name string - test tests.StateTest - st tests.StateSubtest -} - -// collectMatchedSubtests returns test cases which match against provided filtering CLI parameters -func collectMatchedSubtests(ctx *cli.Context, testsByName map[string]tests.StateTest) []stateTestCase { - var res []stateTestCase - subtestName := ctx.String(testNameFlag.Name) - if subtestName != "" { - if subtest, ok := testsByName[subtestName]; ok { - testsByName := make(map[string]tests.StateTest) - testsByName[subtestName] = subtest - } - } - idx := ctx.Int(idxFlag.Name) - fork := ctx.String(forkFlag.Name) - - for key, test := range testsByName { - for _, st := range test.Subtests() { - if idx != -1 && st.Index != idx { - continue - } - if fork != "" && st.Fork != fork { - continue - } - res = append(res, stateTestCase{name: key, st: st, test: test}) - } - } - return res -} - // runStateTest loads the state-test given by fname, and executes the test. -func runStateTest(ctx *cli.Context, fname string, cfg vm.Config, dump bool, bench bool) error { +func runStateTest(ctx *cli.Context, fname string) ([]testResult, error) { src, err := os.ReadFile(fname) if err != nil { - return err + return nil, err } var testsByName map[string]tests.StateTest if err := json.Unmarshal(src, &testsByName); err != nil { - return err + return nil, fmt.Errorf("unable to read test file %s: %w", fname, err) } - matchingTests := collectMatchedSubtests(ctx, testsByName) + cfg := vm.Config{Tracer: tracerFromFlags(ctx)} + re, err := regexp.Compile(ctx.String(RunFlag.Name)) + if err != nil { + return nil, fmt.Errorf("invalid regex -%s: %v", RunFlag.Name, err) + } // Iterate over all the tests, run them and aggregate the results - var results []StatetestResult - for _, test := range matchingTests { - // Run the test and aggregate the result - result := &StatetestResult{Name: test.name, Fork: test.st.Fork, Pass: true} - test.test.Run(test.st, cfg, false, rawdb.HashScheme, func(err error, tstate *tests.StateTestState) { - var root common.Hash - if tstate.StateDB != nil { - root = tstate.StateDB.IntermediateRoot(false) - result.Root = &root - fmt.Fprintf(os.Stderr, "{\"stateRoot\": \"%#x\"}\n", root) - if dump { // Dump any state to aid debugging - cpy, _ := state.New(root, tstate.StateDB.Database()) - dump := cpy.RawDump(nil) - result.State = &dump - } + results := make([]testResult, 0, len(testsByName)) + for key, test := range testsByName { + if !re.MatchString(key) { + continue + } + for i, st := range test.Subtests() { + if idx := ctx.Int(idxFlag.Name); idx != -1 && idx != i { + // If specific index requested, skip all tests that do not match. + continue } - if err != nil { - // Test failed, mark as so - result.Pass, result.Error = false, err.Error() + if fork := ctx.String(forkFlag.Name); fork != "" && st.Fork != fork { + // If specific fork requested, skip all tests that do not match. + continue } - }) - if bench { - _, stats, _ := timedExec(true, func() ([]byte, uint64, error) { - _, _, gasUsed, _ := test.test.RunNoVerify(test.st, cfg, false, rawdb.HashScheme) - return nil, gasUsed, nil + // Run the test and aggregate the result + result := &testResult{Name: key, Fork: st.Fork, Pass: true} + test.Run(st, cfg, false, rawdb.HashScheme, func(err error, state *tests.StateTestState) { + var root common.Hash + if state.StateDB != nil { + root = state.StateDB.IntermediateRoot(false) + result.Root = &root + fmt.Fprintf(os.Stderr, "{\"stateRoot\": \"%#x\"}\n", root) + // Dump any state to aid debugging. + if ctx.Bool(DumpFlag.Name) { + result.State = dump(state.StateDB) + } + } + // Collect bench stats if requested. + if ctx.Bool(BenchFlag.Name) { + _, stats, _ := timedExec(true, func() ([]byte, uint64, error) { + _, _, gasUsed, _ := test.RunNoVerify(st, cfg, false, rawdb.HashScheme) + return nil, gasUsed, nil + }) + result.Stats = &stats + } + if err != nil { + // Test failed, mark as so. + result.Pass, result.Error = false, err.Error() + return + } }) - result.BenchStats = &stats + results = append(results, *result) } - results = append(results, *result) } - out, _ := json.MarshalIndent(results, "", " ") - fmt.Println(string(out)) - return nil + return results, nil } diff --git a/cmd/evm/t8n_test.go b/cmd/evm/t8n_test.go index c2ad5df973..d8a744c0c5 100644 --- a/cmd/evm/t8n_test.go +++ b/cmd/evm/t8n_test.go @@ -344,98 +344,6 @@ func lineIterator(path string) func() (string, error) { } } -// TestT8nTracing is a test that checks the tracing-output from t8n. -func TestT8nTracing(t *testing.T) { - t.Parallel() - tt := new(testT8n) - tt.TestCmd = cmdtest.NewTestCmd(t, tt) - for i, tc := range []struct { - base string - input t8nInput - expExitCode int - extraArgs []string - expectedTraces []string - }{ - { - base: "./testdata/31", - input: t8nInput{ - "alloc.json", "txs.json", "env.json", "Cancun", "", - }, - extraArgs: []string{"--trace"}, - expectedTraces: []string{"trace-0-0x88f5fbd1524731a81e49f637aa847543268a5aaf2a6b32a69d2c6d978c45dcfb.jsonl"}, - }, - { - base: "./testdata/31", - input: t8nInput{ - "alloc.json", "txs.json", "env.json", "Cancun", "", - }, - extraArgs: []string{"--trace.tracer", ` -{ - result: function(){ - return "hello world" - }, - fault: function(){} -}`}, - expectedTraces: []string{"trace-0-0x88f5fbd1524731a81e49f637aa847543268a5aaf2a6b32a69d2c6d978c45dcfb.json"}, - }, - { - base: "./testdata/32", - input: t8nInput{ - "alloc.json", "txs.json", "env.json", "Paris", "", - }, - extraArgs: []string{"--trace", "--trace.callframes"}, - expectedTraces: []string{"trace-0-0x47806361c0fa084be3caa18afe8c48156747c01dbdfc1ee11b5aecdbe4fcf23e.jsonl"}, - }, - } { - args := []string{"t8n"} - args = append(args, tc.input.get(tc.base)...) - // Place the output somewhere we can find it - outdir := t.TempDir() - args = append(args, "--output.basedir", outdir) - args = append(args, tc.extraArgs...) - - var qArgs []string // quoted args for debugging purposes - for _, arg := range args { - if len(arg) == 0 { - qArgs = append(qArgs, `""`) - } else { - qArgs = append(qArgs, arg) - } - } - tt.Logf("args: %v\n", strings.Join(qArgs, " ")) - tt.Run("evm-test", args...) - t.Log(string(tt.Output())) - - // Compare the expected traces - for _, traceFile := range tc.expectedTraces { - haveFn := lineIterator(filepath.Join(outdir, traceFile)) - wantFn := lineIterator(filepath.Join(tc.base, traceFile)) - - for line := 0; ; line++ { - want, wErr := wantFn() - have, hErr := haveFn() - if want != have { - t.Fatalf("test %d, trace %v, line %d\nwant: %v\nhave: %v\n", - i, traceFile, line, want, have) - } - if wErr != nil && hErr != nil { - break - } - if wErr != nil { - t.Fatal(wErr) - } - if hErr != nil { - t.Fatal(hErr) - } - t.Logf("%v\n", want) - } - } - if have, want := tt.ExitStatus(), tc.expExitCode; have != want { - t.Fatalf("test %d: wrong exit code, have %d, want %d", i, have, want) - } - } -} - type t9nInput struct { inTxs string stFork string @@ -675,6 +583,88 @@ func TestB11r(t *testing.T) { } } +func TestEvmRun(t *testing.T) { + t.Parallel() + tt := cmdtest.NewTestCmd(t, nil) + for i, tc := range []struct { + input []string + wantStdout string + wantStderr string + }{ + { // json tracing + input: []string{"run", "--trace", "--trace.format=json", "6040"}, + wantStdout: "./testdata/evmrun/1.out.1.txt", + wantStderr: "./testdata/evmrun/1.out.2.txt", + }, + { // Same as above, using the deprecated --json + input: []string{"run", "--json", "6040"}, + wantStdout: "./testdata/evmrun/1.out.1.txt", + wantStderr: "./testdata/evmrun/1.out.2.txt", + }, + { // default tracing (struct) + input: []string{"run", "--trace", "0x6040"}, + wantStdout: "./testdata/evmrun/2.out.1.txt", + wantStderr: "./testdata/evmrun/2.out.2.txt", + }, + { // default tracing (struct), plus alloc-dump + input: []string{"run", "--trace", "--dump", "0x6040"}, + wantStdout: "./testdata/evmrun/3.out.1.txt", + //wantStderr: "./testdata/evmrun/3.out.2.txt", + }, + { // json-tracing, plus alloc-dump + input: []string{"run", "--trace", "--trace.format=json", "--dump", "0x6040"}, + wantStdout: "./testdata/evmrun/4.out.1.txt", + //wantStderr: "./testdata/evmrun/4.out.2.txt", + }, + { // md-tracing + input: []string{"run", "--trace", "--trace.format=md", "0x6040"}, + wantStdout: "./testdata/evmrun/5.out.1.txt", + wantStderr: "./testdata/evmrun/5.out.2.txt", + }, + { // statetest subcommand + input: []string{"statetest", "./testdata/statetest.json"}, + wantStdout: "./testdata/evmrun/6.out.1.txt", + wantStderr: "./testdata/evmrun/6.out.2.txt", + }, + { // statetest subcommand with output + input: []string{"statetest", "--trace", "--trace.format=md", "./testdata/statetest.json"}, + wantStdout: "./testdata/evmrun/7.out.1.txt", + wantStderr: "./testdata/evmrun/7.out.2.txt", + }, + { // statetest subcommand with output + input: []string{"statetest", "--trace", "--trace.format=json", "./testdata/statetest.json"}, + wantStdout: "./testdata/evmrun/8.out.1.txt", + wantStderr: "./testdata/evmrun/8.out.2.txt", + }, + } { + tt.Logf("args: go run ./cmd/evm %v\n", strings.Join(tc.input, " ")) + tt.Run("evm-test", tc.input...) + + haveStdOut := tt.Output() + tt.WaitExit() + haveStdErr := tt.StderrText() + + if have, wantFile := haveStdOut, tc.wantStdout; wantFile != "" { + want, err := os.ReadFile(wantFile) + if err != nil { + t.Fatalf("test %d: could not read expected output: %v", i, err) + } + if string(haveStdOut) != string(want) { + t.Fatalf("test %d, output wrong, have \n%v\nwant\n%v\n", i, string(have), string(want)) + } + } + if have, wantFile := haveStdErr, tc.wantStderr; wantFile != "" { + want, err := os.ReadFile(wantFile) + if err != nil { + t.Fatalf("test %d: could not read expected output: %v", i, err) + } + if have != string(want) { + t.Fatalf("test %d, output wrong\nhave %q\nwant %q\n", i, have, string(want)) + } + } + } +} + // cmpJson compares the JSON in two byte slices. func cmpJson(a, b []byte) (bool, error) { var j, j2 interface{} @@ -686,3 +676,93 @@ func cmpJson(a, b []byte) (bool, error) { } return reflect.DeepEqual(j2, j), nil } + +// TestEVMTracing is a test that checks the tracing-output from evm. +func TestEVMTracing(t *testing.T) { + t.Parallel() + tt := cmdtest.NewTestCmd(t, nil) + for i, tc := range []struct { + base string + input []string + expectedTraces []string + }{ + { + base: "./testdata/31", + input: []string{"t8n", + "--input.alloc=./testdata/31/alloc.json", "--input.txs=./testdata/31/txs.json", + "--input.env=./testdata/31/env.json", "--state.fork=Cancun", + "--trace", + }, + expectedTraces: []string{"trace-0-0x88f5fbd1524731a81e49f637aa847543268a5aaf2a6b32a69d2c6d978c45dcfb.jsonl"}, + }, + { + base: "./testdata/31", + input: []string{"t8n", + "--input.alloc=./testdata/31/alloc.json", "--input.txs=./testdata/31/txs.json", + "--input.env=./testdata/31/env.json", "--state.fork=Cancun", + "--trace.tracer", ` +{ + result: function(){ + return "hello world" + }, + fault: function(){} +}`, + }, + expectedTraces: []string{"trace-0-0x88f5fbd1524731a81e49f637aa847543268a5aaf2a6b32a69d2c6d978c45dcfb.json"}, + }, + { + base: "./testdata/32", + input: []string{"t8n", + "--input.alloc=./testdata/32/alloc.json", "--input.txs=./testdata/32/txs.json", + "--input.env=./testdata/32/env.json", "--state.fork=Paris", + "--trace", "--trace.callframes", + }, + expectedTraces: []string{"trace-0-0x47806361c0fa084be3caa18afe8c48156747c01dbdfc1ee11b5aecdbe4fcf23e.jsonl"}, + }, + // TODO, make it possible to run tracers on statetests, e.g: + //{ + // base: "./testdata/31", + // input: []string{"statetest", "--trace", "--trace.tracer", `{ + // result: function(){ + // return "hello world" + // }, + // fault: function(){} + //}`, "./testdata/statetest.json"}, + // expectedTraces: []string{"trace-0-0x88f5fbd1524731a81e49f637aa847543268a5aaf2a6b32a69d2c6d978c45dcfb.json"}, + // }, + } { + // Place the output somewhere we can find it + outdir := t.TempDir() + args := append(tc.input, "--output.basedir", outdir) + + tt.Run("evm-test", args...) + tt.Logf("args: go run ./cmd/evm %v\n", args) + tt.WaitExit() + //t.Log(string(tt.Output())) + + // Compare the expected traces + for _, traceFile := range tc.expectedTraces { + haveFn := lineIterator(filepath.Join(outdir, traceFile)) + wantFn := lineIterator(filepath.Join(tc.base, traceFile)) + + for line := 0; ; line++ { + want, wErr := wantFn() + have, hErr := haveFn() + if want != have { + t.Fatalf("test %d, trace %v, line %d\nwant: %v\nhave: %v\n", + i, traceFile, line, want, have) + } + if wErr != nil && hErr != nil { + break + } + if wErr != nil { + t.Fatal(wErr) + } + if hErr != nil { + t.Fatal(hErr) + } + //t.Logf("%v\n", want) + } + } + } +} diff --git a/cmd/evm/testdata/evmrun/1.out.1.txt b/cmd/evm/testdata/evmrun/1.out.1.txt new file mode 100644 index 0000000000..e69de29bb2 diff --git a/cmd/evm/testdata/evmrun/1.out.2.txt b/cmd/evm/testdata/evmrun/1.out.2.txt new file mode 100644 index 0000000000..97d6c36c60 --- /dev/null +++ b/cmd/evm/testdata/evmrun/1.out.2.txt @@ -0,0 +1,3 @@ +{"pc":0,"op":96,"gas":"0x2540be400","gasCost":"0x3","memSize":0,"stack":[],"depth":1,"refund":0,"opName":"PUSH1"} +{"pc":2,"op":0,"gas":"0x2540be3fd","gasCost":"0x0","memSize":0,"stack":["0x40"],"depth":1,"refund":0,"opName":"STOP"} +{"output":"","gasUsed":"0x3"} diff --git a/cmd/evm/testdata/evmrun/2.out.1.txt b/cmd/evm/testdata/evmrun/2.out.1.txt new file mode 100644 index 0000000000..e69de29bb2 diff --git a/cmd/evm/testdata/evmrun/2.out.2.txt b/cmd/evm/testdata/evmrun/2.out.2.txt new file mode 100644 index 0000000000..4f31d87ccb --- /dev/null +++ b/cmd/evm/testdata/evmrun/2.out.2.txt @@ -0,0 +1,6 @@ +PUSH1 pc=00000000 gas=10000000000 cost=3 + +STOP pc=00000002 gas=9999999997 cost=0 +Stack: +00000000 0x40 + diff --git a/cmd/evm/testdata/evmrun/3.out.1.txt b/cmd/evm/testdata/evmrun/3.out.1.txt new file mode 100644 index 0000000000..44956f54f6 --- /dev/null +++ b/cmd/evm/testdata/evmrun/3.out.1.txt @@ -0,0 +1,13 @@ +{ + "root": "b444481d1367188172f8c6091e948aaa68bae763fd26d6b9e994306a66bf69f9", + "accounts": { + "pre(0x30d7a0694cb29af31b982480e11d7ebb003a3fca4026939149071f014689b142)": { + "balance": "0", + "nonce": 0, + "root": "0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421", + "codeHash": "0x3e48ef54b89079a075f3b8fc253c657a86b110a7aed3568c1517b10edf2c3eb6", + "code": "0x6040", + "key": "0x30d7a0694cb29af31b982480e11d7ebb003a3fca4026939149071f014689b142" + } + } +} diff --git a/cmd/evm/testdata/evmrun/3.out.2.txt b/cmd/evm/testdata/evmrun/3.out.2.txt new file mode 100644 index 0000000000..58e8cbfde7 --- /dev/null +++ b/cmd/evm/testdata/evmrun/3.out.2.txt @@ -0,0 +1,9 @@ +PUSH1 pc=00000000 gas=10000000000 cost=3 + +STOP pc=00000002 gas=9999999997 cost=0 +Stack: +00000000 0x40 + +INFO [12-03|10:37:15.827] Trie dumping started root=b44448..bf69f9 +WARN [12-03|10:37:15.827] Dump incomplete due to missing preimages missing=1 +INFO [12-03|10:37:15.827] Trie dumping complete accounts=1 elapsed="163.513µs" diff --git a/cmd/evm/testdata/evmrun/4.out.1.txt b/cmd/evm/testdata/evmrun/4.out.1.txt new file mode 100644 index 0000000000..44956f54f6 --- /dev/null +++ b/cmd/evm/testdata/evmrun/4.out.1.txt @@ -0,0 +1,13 @@ +{ + "root": "b444481d1367188172f8c6091e948aaa68bae763fd26d6b9e994306a66bf69f9", + "accounts": { + "pre(0x30d7a0694cb29af31b982480e11d7ebb003a3fca4026939149071f014689b142)": { + "balance": "0", + "nonce": 0, + "root": "0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421", + "codeHash": "0x3e48ef54b89079a075f3b8fc253c657a86b110a7aed3568c1517b10edf2c3eb6", + "code": "0x6040", + "key": "0x30d7a0694cb29af31b982480e11d7ebb003a3fca4026939149071f014689b142" + } + } +} diff --git a/cmd/evm/testdata/evmrun/4.out.2.txt b/cmd/evm/testdata/evmrun/4.out.2.txt new file mode 100644 index 0000000000..20441964e6 --- /dev/null +++ b/cmd/evm/testdata/evmrun/4.out.2.txt @@ -0,0 +1,6 @@ +{"pc":0,"op":96,"gas":"0x2540be400","gasCost":"0x3","memSize":0,"stack":[],"depth":1,"refund":0,"opName":"PUSH1"} +{"pc":2,"op":0,"gas":"0x2540be3fd","gasCost":"0x0","memSize":0,"stack":["0x40"],"depth":1,"refund":0,"opName":"STOP"} +{"output":"","gasUsed":"0x3"} +INFO [12-03|10:38:33.360] Trie dumping started root=b44448..bf69f9 +WARN [12-03|10:38:33.361] Dump incomplete due to missing preimages missing=1 +INFO [12-03|10:38:33.361] Trie dumping complete accounts=1 elapsed="240.811µs" diff --git a/cmd/evm/testdata/evmrun/5.out.1.txt b/cmd/evm/testdata/evmrun/5.out.1.txt new file mode 100644 index 0000000000..e69de29bb2 diff --git a/cmd/evm/testdata/evmrun/5.out.2.txt b/cmd/evm/testdata/evmrun/5.out.2.txt new file mode 100644 index 0000000000..356f731f07 --- /dev/null +++ b/cmd/evm/testdata/evmrun/5.out.2.txt @@ -0,0 +1,16 @@ +Pre-execution info: + - from: `0x000000000000000000000000000073656E646572` + - to: `0x0000000000000000000000007265636569766572` + - data: `` + - gas: `10000000000` + - value: `0` wei + +| Pc | Op | Cost | Refund | Stack | +|-------|-------------|------|-----------|-----------| +| 0 | PUSH1 | 3 | 0 | [] | +| 2 | STOP | 0 | 0 | [0x40] | + +Post-execution info: + - output: `` + - consumed gas: `3` + - error: `` diff --git a/cmd/evm/testdata/evmrun/6.out.1.txt b/cmd/evm/testdata/evmrun/6.out.1.txt new file mode 100644 index 0000000000..dc22c2ef3a --- /dev/null +++ b/cmd/evm/testdata/evmrun/6.out.1.txt @@ -0,0 +1,9 @@ +[ + { + "name": "00000006-naivefuzz-0", + "pass": false, + "stateRoot": "0xad1024c87b5548e77c937aa50f72b6cb620d278f4dd79bae7f78f71ff75af458", + "fork": "London", + "error": "post state root mismatch: got ad1024c87b5548e77c937aa50f72b6cb620d278f4dd79bae7f78f71ff75af458, want 0000000000000000000000000000000000000000000000000000000000000000" + } +] diff --git a/cmd/evm/testdata/evmrun/6.out.2.txt b/cmd/evm/testdata/evmrun/6.out.2.txt new file mode 100644 index 0000000000..2d841c4e34 --- /dev/null +++ b/cmd/evm/testdata/evmrun/6.out.2.txt @@ -0,0 +1 @@ +{"stateRoot": "0xad1024c87b5548e77c937aa50f72b6cb620d278f4dd79bae7f78f71ff75af458"} diff --git a/cmd/evm/testdata/evmrun/7.out.1.txt b/cmd/evm/testdata/evmrun/7.out.1.txt new file mode 100644 index 0000000000..dc22c2ef3a --- /dev/null +++ b/cmd/evm/testdata/evmrun/7.out.1.txt @@ -0,0 +1,9 @@ +[ + { + "name": "00000006-naivefuzz-0", + "pass": false, + "stateRoot": "0xad1024c87b5548e77c937aa50f72b6cb620d278f4dd79bae7f78f71ff75af458", + "fork": "London", + "error": "post state root mismatch: got ad1024c87b5548e77c937aa50f72b6cb620d278f4dd79bae7f78f71ff75af458, want 0000000000000000000000000000000000000000000000000000000000000000" + } +] diff --git a/cmd/evm/testdata/evmrun/7.out.2.txt b/cmd/evm/testdata/evmrun/7.out.2.txt new file mode 100644 index 0000000000..d81d96c6f6 --- /dev/null +++ b/cmd/evm/testdata/evmrun/7.out.2.txt @@ -0,0 +1,925 @@ +Pre-execution info: + - from: `0xa94f5374Fce5edBC8E2a8697C15331677e6EbF0B` + - to: `0x00000000000000000000000000000000000000f1` + - data: `0x81fbe24d1e33d7944b2e62ee0ff24811bbbcf8cb311e5617c80623dec4477cc14849fc042b9bbaebca9f03f66cca76c46353c5a68c2e134ef75f8c2425d9702f3a4bd3c5527e93d27579bdbd7d237eaa1c0278fce26479aaf11fb8d00e7478` + - gas: `737811` + - value: `1` wei + +| Pc | Op | Cost | Refund | Stack | +|-------|-------------|------|-----------|-----------| +| 0 | PUSH1 | 3 | 0 | [] | +| 2 | PUSH1 | 3 | 0 | [0x2] | +| 4 | SSTORE | 22100 | 0 | [0x2,0x3] | +| 5 | PUSH1 | 3 | 0 | [] | +| 7 | PUSH1 | 3 | 0 | [0x0] | +| 9 | PUSH1 | 3 | 0 | [0x0,0x0] | +| 11 | PUSH1 | 3 | 0 |[0x0,0x0,0x0] | +| 13 | PUSH1 | 3 | 0 |[0x0,0x0,0x0,0x0] | +| 15 | PUSH1 | 3 | 0 |[0x0,0x0,0x0,0x0,0x0] | +| 17 | GAS | 2 | 0 |[0x0,0x0,0x0,0x0,0x0,0x4] | +| 18 | CALLCODE | 704504 | 0 |[0x0,0x0,0x0,0x0,0x0,0x4,0xaeba5] | +| 19 | POP | 2 | 0 | [0x1] | +| 20 | PUSH32 | 3 | 0 | [] | +| 53 | PUSH1 | 3 | 0 |[0x600254506003545060016003557f7f6008545060006004557f60016004556000] | +| 55 | MSTORE | 6 | 0 |[0x600254506003545060016003557f7f6008545060006004557f60016004556000,0x0] | +| 56 | PUSH32 | 3 | 0 | [] | +| 89 | PUSH1 | 3 | 0 |[0x60045560006000600060006000606000527ff96000527f5af250600060006000] | +| 91 | MSTORE | 6 | 0 |[0x60045560006000600060006000606000527ff96000527f5af250600060006000,0x20] | +| 92 | PUSH32 | 3 | 0 | [] | +| 125 | PUSH1 | 3 | 0 |[0x606000527e60f45af45060006000600060006020527f600060f55a6020527ff1] | +| 127 | MSTORE | 6 | 0 |[0x606000527e60f45af45060006000600060006020527f600060f55a6020527ff1,0x40] | +| 128 | PUSH32 | 3 | 0 | [] | +| 161 | PUSH1 | 3 | 0 |[0x50f001075205846a44a283446020527f8ca2600060006040527f600060006004] | +| 163 | MSTORE | 6 | 0 |[0x50f001075205846a44a283446020527f8ca2600060006040527f600060006004,0x60] | +| 164 | PUSH32 | 3 | 0 | [] | +| 197 | PUSH1 | 3 | 0 |[0x5af4506040527f519930847f3b631c54a49b5f600354503260406060527f527f] | +| 199 | MSTORE | 6 | 0 |[0x5af4506040527f519930847f3b631c54a49b5f600354503260406060527f527f,0x80] | +| 200 | PUSH32 | 3 | 0 | [] | +| 233 | PUSH1 | 3 | 0 |[0x77306b60006000600060006060527f6000600c5af15060006000600060006080] | +| 235 | MSTORE | 6 | 0 |[0x77306b60006000600060006060527f6000600c5af15060006000600060006080,0xa0] | +| 236 | PUSH32 | 3 | 0 | [] | +| 269 | PUSH1 | 3 | 0 |[0x527f60f85af450506060527f066001600255606080527f035450600060005560] | +| 271 | MSTORE | 6 | 0 |[0x527f60f85af450506060527f066001600255606080527f035450600060005560,0xc0] | +| 272 | PUSH31 | 3 | 0 | [] | +| 304 | PUSH1 | 3 | 0 |[0x6060a0527f01556c3394fff4607f7f1684317b6080527f387b9f60a0527f19] | +| 306 | MSTORE | 6 | 0 |[0x6060a0527f01556c3394fff4607f7f1684317b6080527f387b9f60a0527f19,0xe0] | +| 307 | PUSH32 | 3 | 0 | [] | +| 340 | PUSH2 | 3 | 0 |[0x20700184809d60c0527f60015450011899016e6009ff60026001556000527f9f] | +| 343 | MSTORE | 6 | 0 |[0x20700184809d60c0527f60015450011899016e6009ff60026001556000527f9f,0x100] | +| 344 | PUSH32 | 3 | 0 | [] | +| 377 | PUSH2 | 3 | 0 |[0x60a05260c0527f7f600060e0527f527f9981600160045582600eff6000600060] | +| 380 | MSTORE | 6 | 0 |[0x60a05260c0527f7f600060e0527f527f9981600160045582600eff6000600060,0x120] | +| 381 | PUSH31 | 3 | 0 | [] | +| 413 | PUSH2 | 3 | 0 |[0x600060f65af45060006060e052610100527f7f6060c0527f20527e60006000] | +| 416 | MSTORE | 6 | 0 |[0x600060f65af45060006060e052610100527f7f6060c0527f20527e60006000,0x140] | +| 417 | PUSH32 | 3 | 0 | [] | +| 450 | PUSH2 | 3 | 0 |[0x6020527f60f75af4501d7f1903166660006000610120527f60610100527e6000] | +| 453 | MSTORE | 6 | 0 |[0x6020527f60f75af4501d7f1903166660006000610120527f60610100527e6000,0x160] | +| 454 | PUSH32 | 3 | 0 | [] | +| 487 | PUSH2 | 3 | 0 |[0x600060e0527f60046040527f5af150600060006000604052610140527f7f6000] | +| 490 | MSTORE | 6 | 0 |[0x600060e0527f60046040527f5af150600060006000604052610140527f7f6000,0x180] | +| 491 | PUSH32 | 3 | 0 | [] | +| 524 | PUSH2 | 3 | 0 |[0x6009610120527f5af4503c95138e5b8f610100527f7f605a6000536060610160] | +| 527 | MSTORE | 6 | 0 |[0x6009610120527f5af4503c95138e5b8f610100527f7f605a6000536060610160,0x1a0] | +| 528 | PUSH32 | 3 | 0 | [] | +| 561 | PUSH2 | 3 | 0 |[0x527f527f6031600153606b60610140527f02536010606060527f035360456061] | +| 564 | MSTORE | 6 | 0 |[0x527f527f6031600153606b60610140527f02536010606060527f035360456061,0x1c0] | +| 565 | PUSH32 | 3 | 0 | [] | +| 598 | PUSH2 | 3 | 0 |[0x120610180527f527f04536060600553600160608052610160527f7e527f6006] | +| 601 | MSTORE | 6 | 0 |[0x120610180527f527f04536060600553600160608052610160527f7e527f6006,0x1e0] | +| 602 | PUSH32 | 3 | 0 | [] | +| 635 | PUSH2 | 3 | 0 |[0x536060600753606101a0527f02600853606080610140527f527f556009536060] | +| 638 | MSTORE | 6 | 0 |[0x536060600753606101a0527f02600853606080610140527f527f556009536060,0x200] | +| 639 | PUSH32 | 3 | 0 | [] | +| 672 | PUSH2 | 3 | 0 |[0x610180527f600a53600160a06101c0527f527f600b536060600c6020527f5360] | +| 675 | MSTORE | 6 | 0 |[0x610180527f600a53600160a06101c0527f527f600b536060600c6020527f5360,0x220] | +| 676 | PUSH31 | 3 | 0 | [] | +| 708 | PUSH2 | 3 | 0 |[0x60610160527f0d53606101a0527f55606101e0527f0e60a0527f536060600f] | +| 711 | MSTORE | 6 | 0 |[0x60610160527f0d53606101a0527f55606101e0527f0e60a0527f536060600f,0x240] | +| 712 | PUSH32 | 3 | 0 | [] | +| 745 | PUSH2 | 3 | 0 |[0x536060c0527f01601053606060115360026101806101610200527fc0527f527f] | +| 748 | MSTORE | 6 | 0 |[0x536060c0527f01601053606060115360026101806101610200527fc0527f527f,0x260] | +| 749 | PUSH32 | 3 | 0 | [] | +| 782 | PUSH2 | 3 | 0 |[0x601253606040527f55601353606060c0527f60145360e0527f6000610220527f] | +| 785 | MSTORE | 6 | 0 |[0x601253606040527f55601353606060c0527f60145360e0527f6000610220527f,0x280] | +| 786 | PUSH32 | 3 | 0 | [] | +| 819 | PUSH2 | 3 | 0 |[0x6015536101e0527f60606101a0527f601653600060175360f360185360196060] | +| 822 | MSTORE | 6 | 0 |[0x6015536101e0527f60606101a0527f601653600060175360f360185360196060,0x2a0] | +| 823 | PUSH32 | 3 | 0 | [] | +| 856 | PUSH2 | 3 | 0 |[0x610240527f605260006060e052610200527f610100527f7f806101c0527f5360] | +| 859 | MSTORE | 7 | 0 |[0x610240527f605260006060e052610200527f610100527f7f806101c0527f5360,0x2c0] | +| 860 | PUSH32 | 3 | 0 | [] | +| 893 | PUSH2 | 3 | 0 |[0xf360815360610260527f8260006000f060006000600060610220527e845af450] | +| 896 | MSTORE | 6 | 0 |[0xf360815360610260527f8260006000f060006000600060610220527e845af450,0x2e0] | +| 897 | PUSH32 | 3 | 0 | [] | +| 930 | PUSH2 | 3 | 0 |[0x506000600061016101e0610280527f527f20527f60610100527e600060006003] | +| 933 | MSTORE | 6 | 0 |[0x506000600061016101e0610280527f527f20527f60610100527e600060006003,0x300] | +| 934 | PUSH32 | 3 | 0 | [] | +| 967 | PUSH2 | 3 | 0 |[0x610240527f5af15060005450c760006102a0527f6002551309f562610200527f] | +| 970 | MSTORE | 6 | 0 |[0x610240527f5af15060005450c760006102a0527f6002551309f562610200527f,0x320] | +| 971 | PUSH32 | 3 | 0 | [] | +| 1004 | PUSH2 | 3 | 0 |[0x66a486610140527f6b00610260527f1d457161016102c0527f20527f60005450] | +| 1007 | MSTORE | 6 | 0 |[0x66a486610140527f6b00610260527f1d457161016102c0527f20527f60005450,0x340] | +| 1008 | PUSH32 | 3 | 0 | [] | +| 1041 | PUSH2 | 3 | 0 |[0x1c641d373c7f60045450610220527f6000600155610280527f6102e0527f6005] | +| 1044 | MSTORE | 6 | 0 |[0x1c641d373c7f60045450610220527f6000600155610280527f6102e0527f6005,0x360] | +| 1045 | PUSH32 | 3 | 0 | [] | +| 1078 | PUSH2 | 3 | 0 |[0x54610160527f50600160025560085450610140527f60006002610240527f6103] | +| 1081 | MSTORE | 6 | 0 |[0x54610160527f50600160025560085450610140527f60006002610240527f6103,0x380] | +| 1082 | PUSH31 | 3 | 0 | [] | +| 1114 | PUSH2 | 3 | 0 |[0x527f6102a0527f557fd86000606000527e600060610180527e600060005af1] | +| 1117 | MSTORE | 6 | 0 |[0x527f6102a0527f557fd86000606000527e600060610180527e600060005af1,0x3a0] | +| 1118 | PUSH32 | 3 | 0 | [] | +| 1151 | PUSH2 | 3 | 0 |[0x508612610320527f17145147356102c0527f610260527f610160527f5198a37e] | +| 1154 | MSTORE | 6 | 0 |[0x508612610320527f17145147356102c0527f610260527f610160527f5198a37e,0x3c0] | +| 1155 | PUSH32 | 3 | 0 | [] | +| 1188 | PUSH2 | 3 | 0 |[0x127a7efa7c600052610340527f6101a0527f606020527f6102e0527f60602053] | +| 1191 | MSTORE | 7 | 0 |[0x127a7efa7c600052610340527f6101a0527f606020527f6102e0527f60602053,0x3e0] | +| 1192 | PUSH32 | 3 | 0 | [] | +| 1225 | PUSH2 | 3 | 0 |[0x60610280527ff760215360ff60610360527f225360610180527fdb6023536037] | +| 1228 | MSTORE | 6 | 0 |[0x60610280527ff760215360ff60610360527f225360610180527fdb6023536037,0x400] | +| 1229 | PUSH32 | 3 | 0 | [] | +| 1262 | PUSH2 | 3 | 0 |[0x60610300527f6101c0527f24536075606102610380527fa0527f2553609f6060] | +| 1265 | MSTORE | 6 | 0 |[0x60610300527f6101c0527f24536075606102610380527fa0527f2553609f6060,0x420] | +| 1266 | PUSH32 | 3 | 0 | [] | +| 1299 | PUSH2 | 3 | 0 |[0x40527f265360fe60275360610320527f8f60286101a0526103a0527f7f536061] | +| 1302 | MSTORE | 6 | 0 |[0x40527f265360fe60275360610320527f8f60286101a0526103a0527f7f536061,0x440] | +| 1303 | PUSH32 | 3 | 0 | [] | +| 1336 | PUSH2 | 3 | 0 |[0x1e0527f6102c0527f0b6029536060602a53600060610340527f2b536103c052] | +| 1339 | MSTORE | 6 | 0 |[0x1e0527f6102c0527f0b6029536060602a53600060610340527f2b536103c052,0x460] | +| 1340 | PUSH32 | 3 | 0 | [] | +| 1373 | PUSH2 | 3 | 0 |[0x7f6060602c53606052606060805360006061016102e0527f610200527fc05261] | +| 1376 | MSTORE | 6 | 0 |[0x7f6060602c53606052606060805360006061016102e0527f610200527fc05261,0x480] | +| 1377 | PUSH32 | 3 | 0 | [] | +| 1410 | PUSH2 | 3 | 0 |[0x36103e0527f60527f7f81536060608253602d60835360536084536060608553] | +| 1413 | MSTORE | 6 | 0 |[0x36103e0527f60527f7f81536060608253602d60835360536084536060608553,0x4a0] | +| 1414 | PUSH32 | 3 | 0 | [] | +| 1447 | PUSH2 | 3 | 0 |[0x60fd61030052610400527f7f6086610380527f536060610220527f6087536101] | +| 1450 | MSTORE | 6 | 0 |[0x60fd61030052610400527f7f6086610380527f536060610220527f6087536101,0x4c0] | +| 1451 | PUSH32 | 3 | 0 | [] | +| 1484 | PUSH2 | 3 | 0 |[0xe0527f602e608853605360610420527f89536060608a61036103a0527f20527f] | +| 1487 | MSTORE | 7 | 0 |[0xe0527f602e608853605360610420527f89536060608a61036103a0527f20527f,0x4e0] | +| 1488 | PUSH32 | 3 | 0 | [] | +| 1521 | PUSH2 | 3 | 0 |[0x53602f608b536060608c610240527f53610440527f6000608d5360f361020052] | +| 1524 | MSTORE | 6 | 0 |[0x53602f608b536060608c610240527f53610440527f6000608d5360f361020052,0x500] | +| 1525 | PUSH32 | 3 | 0 | [] | +| 1558 | PUSH2 | 3 | 0 |[0x60606103c0527f610220610340527f53608e610221610460527f536053610222] | +| 1561 | MSTORE | 6 | 0 |[0x60606103c0527f610220610340527f53608e610221610460527f536053610222,0x520] | +| 1562 | PUSH32 | 3 | 0 | [] | +| 1595 | PUSH2 | 3 | 0 |[0x536060610260527f610223536103e0527f600061022453606061610480527f03] | +| 1598 | MSTORE | 6 | 0 |[0x536060610260527f610223536103e0527f600061022453606061610480527f03,0x540] | +| 1599 | PUSH32 | 3 | 0 | [] | +| 1632 | PUSH2 | 3 | 0 |[0x60527f61022553608f61022653606061022753600061610400527f0261028061] | +| 1635 | MSTORE | 6 | 0 |[0x60527f61022553608f61022653606061022753600061610400527f0261028061,0x560] | +| 1636 | PUSH32 | 3 | 0 | [] | +| 1669 | PUSH2 | 3 | 0 |[0x4a0527f527f28536060610229610380527f53600061022a5360f561022b5360] | +| 1672 | MSTORE | 6 | 0 |[0x4a0527f527f28536060610229610380527f53600061022a5360f561022b5360,0x580] | +| 1673 | PUSH32 | 3 | 0 | [] | +| 1706 | PUSH2 | 3 | 0 |[0x610420526104c0527f7f6061022c53600061022d536102a0527f60606103a052] | +| 1709 | MSTORE | 7 | 0 |[0x610420526104c0527f7f6061022c53600061022d536102a0527f60606103a052,0x5a0] | +| 1710 | PUSH32 | 3 | 0 | [] | +| 1743 | PUSH2 | 3 | 0 |[0x7f61022e53600061026104e0527f2f610440527f536060610230536000610231] | +| 1746 | MSTORE | 6 | 0 |[0x7f61022e53600061026104e0527f2f610440527f536060610230536000610231,0x5c0] | +| 1747 | PUSH32 | 3 | 0 | [] | +| 1780 | PUSH2 | 3 | 0 |[0x53606061023253600061026103c0610500527f527fc0527f61610460527f0233] | +| 1783 | MSTORE | 6 | 0 |[0x53606061023253600061026103c0610500527f527fc0527f61610460527f0233,0x5e0] | +| 1784 | PUSH32 | 3 | 0 | [] | +| 1817 | PUSH2 | 3 | 0 |[0x53606061023453600061023553608561023653610520527f605a610237536061] | +| 1820 | MSTORE | 6 | 0 |[0x53606061023453600061023553608561023653610520527f605a610237536061,0x600] | +| 1821 | PUSH32 | 3 | 0 | [] | +| 1854 | PUSH2 | 3 | 0 |[0x3e052610480527f7ff261026102e0526038610300536053610540527f610301] | +| 1857 | MSTORE | 6 | 0 |[0x3e052610480527f7ff261026102e0526038610300536053610540527f610301,0x620] | +| 1858 | PUSH32 | 3 | 0 | [] | +| 1891 | PUSH2 | 3 | 0 |[0x536060610302536050610303536104a0527f60610400527f6161030453610560] | +| 1894 | MSTORE | 7 | 0 |[0x536060610302536050610303536104a0527f60610400527f6161030453610560,0x640] | +| 1895 | PUSH32 | 3 | 0 | [] | +| 1928 | PUSH2 | 3 | 0 |[0x527f6002610305536039610306536053610307536060616104c0527f03085360] | +| 1931 | MSTORE | 6 | 0 |[0x527f6002610305536039610306536053610307536060616104c0527f03085360,0x660] | +| 1932 | PUSH32 | 3 | 0 | [] | +| 1965 | PUSH2 | 3 | 0 |[0x5061610580527f610420527f030953606161030a53600261030b53603a61030c] | +| 1968 | MSTORE | 6 | 0 |[0x5061610580527f610420527f030953606161030a53600261030b53603a61030c,0x680] | +| 1969 | PUSH32 | 3 | 0 | [] | +| 2002 | PUSH2 | 3 | 0 |[0x536104e0527f606105a0527f5361030d53606161030e610440527f5360026103] | +| 2005 | MSTORE | 6 | 0 |[0x536104e0527f606105a0527f5361030d53606161030e610440527f5360026103,0x6a0] | +| 2006 | PUSH32 | 3 | 0 | [] | +| 2039 | PUSH2 | 3 | 0 |[0xf53603b61031053606061616105c0527f0500527f03115360006103125360f3] | +| 2042 | MSTORE | 6 | 0 |[0xf53603b61031053606061616105c0527f0500527f03115360006103125360f3,0x6c0] | +| 2043 | PUSH32 | 3 | 0 | [] | +| 2076 | PUSH2 | 3 | 0 |[0x61031353616104605260036104805360146105e0527f61048153610520527f60] | +| 2079 | MSTORE | 7 | 0 |[0x61031353616104605260036104805360146105e0527f61048153610520527f60,0x6e0] | +| 2080 | PUSH32 | 3 | 0 | [] | +| 2113 | PUSH2 | 3 | 0 |[0x60610482536000610483536060610484536000610485610600527f5360f06104] | +| 2116 | MSTORE | 6 | 0 |[0x60610482536000610483536060610484536000610485610600527f5360f06104,0x700] | +| 2117 | PUSH32 | 3 | 0 | [] | +| 2150 | PUSH2 | 3 | 0 |[0x86536060610540527f610487536000610488536060610489536000610620527f] | +| 2153 | MSTORE | 6 | 0 |[0x86536060610540527f610487536000610488536060610489536000610620527f,0x720] | +| 2154 | PUSH32 | 3 | 0 | [] | +| 2187 | PUSH2 | 3 | 0 |[0x61048a53606061048b5360006104610560527f8c53606061048d53600061048e] | +| 2190 | MSTORE | 6 | 0 |[0x61048a53606061048b5360006104610560527f8c53606061048d53600061048e,0x740] | +| 2191 | PUSH32 | 3 | 0 | [] | +| 2224 | PUSH2 | 3 | 0 |[0x610640527f53608461048f53605a6104905360f4610491536105805260606105] | +| 2227 | MSTORE | 7 | 0 |[0x610640527f53608461048f53605a6104905360f4610491536105805260606105,0x760] | +| 2228 | PUSH32 | 3 | 0 | [] | +| 2261 | PUSH2 | 3 | 0 |[0xa053605061610660527f05a15360616105a25360046105a35360926105a45360] | +| 2264 | MSTORE | 6 | 0 |[0xa053605061610660527f05a15360616105a25360046105a35360926105a45360,0x780] | +| 2265 | PUSH32 | 3 | 0 | [] | +| 2298 | PUSH2 | 3 | 0 |[0x536105a55360606105a6610680527f5360506105a75360616105a85360046105] | +| 2301 | MSTORE | 6 | 0 |[0x536105a55360606105a6610680527f5360506105a75360616105a85360046105,0x7a0] | +| 2302 | PUSH32 | 3 | 0 | [] | +| 2335 | PUSH2 | 3 | 0 |[0xa95360936105aa5360536105ab53606106a0527f616105ac5360046105ad5360] | +| 2338 | MSTORE | 6 | 0 |[0xa95360936105aa5360536105ab53606106a0527f616105ac5360046105ad5360,0x7c0] | +| 2339 | PUSH32 | 3 | 0 | [] | +| 2372 | PUSH2 | 3 | 0 |[0x946105ae5360606105af5360006105b05360f3616106c05260056106e05360b1] | +| 2375 | MSTORE | 7 | 0 |[0x946105ae5360606105af5360006105b05360f3616106c05260056106e05360b1,0x7e0] | +| 2376 | PUSH32 | 3 | 0 | [] | +| 2409 | PUSH2 | 3 | 0 |[0x6106e15360536106e25360606106e35360006106e45360616106e55360056106] | +| 2412 | MSTORE | 6 | 0 |[0x6106e15360536106e25360606106e35360006106e45360616106e55360056106,0x800] | +| 2413 | PUSH32 | 3 | 0 | [] | +| 2446 | PUSH2 | 3 | 0 |[0xe65360b26106e75360606106e85360006106e95360606106ea5360006106eb53] | +| 2449 | MSTORE | 6 | 0 |[0xe65360b26106e75360606106e85360006106e95360606106ea5360006106eb53,0x820] | +| 2450 | PUSH32 | 3 | 0 | [] | +| 2483 | PUSH2 | 3 | 0 |[0x60f56106ec5360606106ed5360006106ee5360606106ef5360006106f0536060] | +| 2486 | MSTORE | 6 | 0 |[0x60f56106ec5360606106ed5360006106ee5360606106ef5360006106f0536060,0x840] | +| 2487 | PUSH32 | 3 | 0 | [] | +| 2520 | PUSH2 | 3 | 0 |[0x6106f15360006106f25360606106f35360006106f45360606106f55360006106] | +| 2523 | MSTORE | 7 | 0 |[0x6106f15360006106f25360606106f35360006106f45360606106f55360006106,0x860] | +| 2524 | PUSH32 | 3 | 0 | [] | +| 2557 | PUSH2 | 3 | 0 |[0xf65360856106f753605a6106f85360f16106f95360506106fa5360506106fb53] | +| 2560 | MSTORE | 6 | 0 |[0xf65360856106f753605a6106f85360f16106f95360506106fa5360506106fb53,0x880] | +| 2561 | PUSH1 | 3 | 0 | [] | +| 2563 | PUSH2 | 3 | 0 | [0x61] | +| 2566 | MSTORE8 | 6 | 0 |[0x61,0x8a0] | +| 2567 | PUSH1 | 3 | 0 | [] | +| 2569 | PUSH2 | 3 | 0 | [0x6] | +| 2572 | MSTORE8 | 3 | 0 |[0x6,0x8a1] | +| 2573 | PUSH1 | 3 | 0 | [] | +| 2575 | PUSH2 | 3 | 0 | [0xfc] | +| 2578 | MSTORE8 | 3 | 0 |[0xfc,0x8a2] | +| 2579 | PUSH1 | 3 | 0 | [] | +| 2581 | PUSH2 | 3 | 0 | [0x60] | +| 2584 | MSTORE8 | 3 | 0 |[0x60,0x8a3] | +| 2585 | PUSH1 | 3 | 0 | [] | +| 2587 | PUSH2 | 3 | 0 | [0x0] | +| 2590 | MSTORE8 | 3 | 0 |[0x0,0x8a4] | +| 2591 | PUSH1 | 3 | 0 | [] | +| 2593 | PUSH2 | 3 | 0 | [0xf3] | +| 2596 | MSTORE8 | 3 | 0 |[0xf3,0x8a5] | +| 2597 | PUSH2 | 3 | 0 | [] | +| 2600 | PUSH1 | 3 | 0 | [0x8a6] | +| 2602 | PUSH1 | 3 | 0 |[0x8a6,0x0] | +| 2604 | CREATE | 32000 | 0 |[0x8a6,0x0,0x0] | +| 0 | PUSH1 | 3 | 0 | [] | +| 2 | SLOAD | 2100 | 0 | [0x2] | +| 3 | POP | 2 | 0 | [0x0] | +| 4 | PUSH1 | 3 | 0 | [] | +| 6 | SLOAD | 2100 | 0 | [0x3] | +| 7 | POP | 2 | 0 | [0x0] | +| 8 | PUSH1 | 3 | 0 | [] | +| 10 | PUSH1 | 3 | 0 | [0x1] | +| 12 | SSTORE | 20000 | 0 | [0x1,0x3] | +| 13 | PUSH32 | 3 | 0 | [] | +| 46 | PUSH1 | 3 | 0 |[0x7f6008545060006004557f600160045560006004556000600060006000600060] | +| 48 | MSTORE | 6 | 0 |[0x7f6008545060006004557f600160045560006004556000600060006000600060,0x0] | +| 49 | PUSH32 | 3 | 0 | [] | +| 82 | PUSH1 | 3 | 0 |[0xf96000527f5af250600060006000606000527e60f45af4506000600060006000] | +| 84 | MSTORE | 6 | 0 |[0xf96000527f5af250600060006000606000527e60f45af4506000600060006000,0x20] | +| 85 | PUSH32 | 3 | 0 | [] | +| 118 | PUSH1 | 3 | 0 |[0x600060f55a6020527ff150f001075205846a44a283446020527f8ca260006000] | +| 120 | MSTORE | 6 | 0 |[0x600060f55a6020527ff150f001075205846a44a283446020527f8ca260006000,0x40] | +| 121 | PUSH32 | 3 | 0 | [] | +| 154 | PUSH1 | 3 | 0 |[0x6000600060045af4506040527f519930847f3b631c54a49b5f60035450326040] | +| 156 | MSTORE | 6 | 0 |[0x6000600060045af4506040527f519930847f3b631c54a49b5f60035450326040,0x60] | +| 157 | PUSH32 | 3 | 0 | [] | +| 190 | PUSH1 | 3 | 0 |[0x527f77306b60006000600060006060527f6000600c5af1506000600060006000] | +| 192 | MSTORE | 6 | 0 |[0x527f77306b60006000600060006060527f6000600c5af1506000600060006000,0x80] | +| 193 | PUSH32 | 3 | 0 | [] | +| 226 | PUSH1 | 3 | 0 |[0x60f85af450506060527f066001600255606080527f0354506000600055600060] | +| 228 | MSTORE | 6 | 0 |[0x60f85af450506060527f066001600255606080527f0354506000600055600060,0xa0] | +| 229 | PUSH32 | 3 | 0 | [] | +| 262 | PUSH1 | 3 | 0 |[0x1556c3394fff4607f7f1684317b6080527f387b9f60a0527f1920700184809d] | +| 264 | MSTORE | 6 | 0 |[0x1556c3394fff4607f7f1684317b6080527f387b9f60a0527f1920700184809d,0xc0] | +| 265 | PUSH32 | 3 | 0 | [] | +| 298 | PUSH1 | 3 | 0 |[0x60015450011899016e6009ff60026001556000527f9f60a05260c0527f7f6000] | +| 300 | MSTORE | 6 | 0 |[0x60015450011899016e6009ff60026001556000527f9f60a05260c0527f7f6000,0xe0] | +| 301 | PUSH32 | 3 | 0 | [] | +| 334 | PUSH2 | 3 | 0 |[0x527f9981600160045582600eff600060006000600060f65af45060006060e052] | +| 337 | MSTORE | 6 | 0 |[0x527f9981600160045582600eff600060006000600060f65af45060006060e052,0x100] | +| 338 | PUSH32 | 3 | 0 | [] | +| 371 | PUSH2 | 3 | 0 |[0x7f6060c0527f20527e600060006020527f60f75af4501d7f1903166660006000] | +| 374 | MSTORE | 6 | 0 |[0x7f6060c0527f20527e600060006020527f60f75af4501d7f1903166660006000,0x120] | +| 375 | PUSH32 | 3 | 0 | [] | +| 408 | PUSH2 | 3 | 0 |[0x60610100527e6000600060e0527f60046040527f5af150600060006000604052] | +| 411 | MSTORE | 6 | 0 |[0x60610100527e6000600060e0527f60046040527f5af150600060006000604052,0x140] | +| 412 | PUSH32 | 3 | 0 | [] | +| 445 | PUSH2 | 3 | 0 |[0x7f60006009610120527f5af4503c95138e5b8f610100527f7f605a6000536060] | +| 448 | MSTORE | 6 | 0 |[0x7f60006009610120527f5af4503c95138e5b8f610100527f7f605a6000536060,0x160] | +| 449 | PUSH32 | 3 | 0 | [] | +| 482 | PUSH2 | 3 | 0 |[0x527f6031600153606b60610140527f02536010606060527f0353604560610120] | +| 485 | MSTORE | 6 | 0 |[0x527f6031600153606b60610140527f02536010606060527f0353604560610120,0x180] | +| 486 | PUSH32 | 3 | 0 | [] | +| 519 | PUSH2 | 3 | 0 |[0x527f04536060600553600160608052610160527f7e527f600653606060075360] | +| 522 | MSTORE | 6 | 0 |[0x527f04536060600553600160608052610160527f7e527f600653606060075360,0x1a0] | +| 523 | PUSH32 | 3 | 0 | [] | +| 556 | PUSH2 | 3 | 0 |[0x2600853606080610140527f527f556009536060610180527f600a53600160a0] | +| 559 | MSTORE | 6 | 0 |[0x2600853606080610140527f527f556009536060610180527f600a53600160a0,0x1c0] | +| 560 | PUSH32 | 3 | 0 | [] | +| 593 | PUSH2 | 3 | 0 |[0x527f600b536060600c6020527f53600060610160527f0d53606101a0527f5560] | +| 596 | MSTORE | 6 | 0 |[0x527f600b536060600c6020527f53600060610160527f0d53606101a0527f5560,0x1e0] | +| 597 | PUSH32 | 3 | 0 | [] | +| 630 | PUSH2 | 3 | 0 |[0xe60a0527f536060600f536060c0527f01601053606060115360026101806101] | +| 633 | MSTORE | 6 | 0 |[0xe60a0527f536060600f536060c0527f01601053606060115360026101806101,0x200] | +| 634 | PUSH32 | 3 | 0 | [] | +| 667 | PUSH2 | 3 | 0 |[0xc0527f527f601253606040527f55601353606060c0527f60145360e0527f6000] | +| 670 | MSTORE | 6 | 0 |[0xc0527f527f601253606040527f55601353606060c0527f60145360e0527f6000,0x220] | +| 671 | PUSH32 | 3 | 0 | [] | +| 704 | PUSH2 | 3 | 0 |[0x6015536101e0527f60606101a0527f601653600060175360f360185360196060] | +| 707 | MSTORE | 6 | 0 |[0x6015536101e0527f60606101a0527f601653600060175360f360185360196060,0x240] | +| 708 | PUSH32 | 3 | 0 | [] | +| 741 | PUSH2 | 3 | 0 |[0x605260006060e052610200527f610100527f7f806101c0527f5360f360815360] | +| 744 | MSTORE | 6 | 0 |[0x605260006060e052610200527f610100527f7f806101c0527f5360f360815360,0x260] | +| 745 | PUSH32 | 3 | 0 | [] | +| 778 | PUSH2 | 3 | 0 |[0x8260006000f060006000600060610220527e845af450506000600061016101e0] | +| 781 | MSTORE | 6 | 0 |[0x8260006000f060006000600060610220527e845af450506000600061016101e0,0x280] | +| 782 | PUSH32 | 3 | 0 | [] | +| 815 | PUSH2 | 3 | 0 |[0x527f20527f60610100527e600060006003610240527f5af15060005450c76000] | +| 818 | MSTORE | 6 | 0 |[0x527f20527f60610100527e600060006003610240527f5af15060005450c76000,0x2a0] | +| 819 | PUSH32 | 3 | 0 | [] | +| 852 | PUSH2 | 3 | 0 |[0x6002551309f562610200527f66a486610140527f6b00610260527f1d45716101] | +| 855 | MSTORE | 7 | 0 |[0x6002551309f562610200527f66a486610140527f6b00610260527f1d45716101,0x2c0] | +| 856 | PUSH32 | 3 | 0 | [] | +| 889 | PUSH2 | 3 | 0 |[0x20527f600054501c641d373c7f60045450610220527f6000600155610280527f] | +| 892 | MSTORE | 6 | 0 |[0x20527f600054501c641d373c7f60045450610220527f6000600155610280527f,0x2e0] | +| 893 | PUSH32 | 3 | 0 | [] | +| 926 | PUSH2 | 3 | 0 |[0x600554610160527f50600160025560085450610140527f60006002610240527f] | +| 929 | MSTORE | 6 | 0 |[0x600554610160527f50600160025560085450610140527f60006002610240527f,0x300] | +| 930 | PUSH32 | 3 | 0 | [] | +| 963 | PUSH2 | 3 | 0 |[0x6102a0527f557fd86000606000527e600060610180527e600060005af1508612] | +| 966 | MSTORE | 6 | 0 |[0x6102a0527f557fd86000606000527e600060610180527e600060005af1508612,0x320] | +| 967 | PUSH32 | 3 | 0 | [] | +| 1000 | PUSH2 | 3 | 0 |[0x17145147356102c0527f610260527f610160527f5198a37e127a7efa7c600052] | +| 1003 | MSTORE | 6 | 0 |[0x17145147356102c0527f610260527f610160527f5198a37e127a7efa7c600052,0x340] | +| 1004 | PUSH32 | 3 | 0 | [] | +| 1037 | PUSH2 | 3 | 0 |[0x6101a0527f606020527f6102e0527f6060205360610280527ff760215360ff60] | +| 1040 | MSTORE | 6 | 0 |[0x6101a0527f606020527f6102e0527f6060205360610280527ff760215360ff60,0x360] | +| 1041 | PUSH32 | 3 | 0 | [] | +| 1074 | PUSH2 | 3 | 0 |[0x225360610180527fdb602353603760610300527f6101c0527f24536075606102] | +| 1077 | MSTORE | 6 | 0 |[0x225360610180527fdb602353603760610300527f6101c0527f24536075606102,0x380] | +| 1078 | PUSH32 | 3 | 0 | [] | +| 1111 | PUSH2 | 3 | 0 |[0xa0527f2553609f606040527f265360fe60275360610320527f8f60286101a052] | +| 1114 | MSTORE | 6 | 0 |[0xa0527f2553609f606040527f265360fe60275360610320527f8f60286101a052,0x3a0] | +| 1115 | PUSH32 | 3 | 0 | [] | +| 1148 | PUSH2 | 3 | 0 |[0x7f53606101e0527f6102c0527f0b6029536060602a53600060610340527f2b53] | +| 1151 | MSTORE | 6 | 0 |[0x7f53606101e0527f6102c0527f0b6029536060602a53600060610340527f2b53,0x3c0] | +| 1152 | PUSH32 | 3 | 0 | [] | +| 1185 | PUSH2 | 3 | 0 |[0x6060602c53606052606060805360006061016102e0527f610200527fc0526103] | +| 1188 | MSTORE | 7 | 0 |[0x6060602c53606052606060805360006061016102e0527f610200527fc0526103,0x3e0] | +| 1189 | PUSH32 | 3 | 0 | [] | +| 1222 | PUSH2 | 3 | 0 |[0x60527f7f81536060608253602d6083536053608453606060855360fd61030052] | +| 1225 | MSTORE | 6 | 0 |[0x60527f7f81536060608253602d6083536053608453606060855360fd61030052,0x400] | +| 1226 | PUSH32 | 3 | 0 | [] | +| 1259 | PUSH2 | 3 | 0 |[0x7f6086610380527f536060610220527f6087536101e0527f602e608853605360] | +| 1262 | MSTORE | 6 | 0 |[0x7f6086610380527f536060610220527f6087536101e0527f602e608853605360,0x420] | +| 1263 | PUSH32 | 3 | 0 | [] | +| 1296 | PUSH2 | 3 | 0 |[0x89536060608a61036103a0527f20527f53602f608b536060608c610240527f53] | +| 1299 | MSTORE | 6 | 0 |[0x89536060608a61036103a0527f20527f53602f608b536060608c610240527f53,0x440] | +| 1300 | PUSH32 | 3 | 0 | [] | +| 1333 | PUSH2 | 3 | 0 |[0x6000608d5360f36102005260606103c0527f610220610340527f53608e610221] | +| 1336 | MSTORE | 6 | 0 |[0x6000608d5360f36102005260606103c0527f610220610340527f53608e610221,0x460] | +| 1337 | PUSH32 | 3 | 0 | [] | +| 1370 | PUSH2 | 3 | 0 |[0x536053610222536060610260527f610223536103e0527f600061022453606061] | +| 1373 | MSTORE | 6 | 0 |[0x536053610222536060610260527f610223536103e0527f600061022453606061,0x480] | +| 1374 | PUSH32 | 3 | 0 | [] | +| 1407 | PUSH2 | 3 | 0 |[0x360527f61022553608f61022653606061022753600061610400527f02610280] | +| 1410 | MSTORE | 6 | 0 |[0x360527f61022553608f61022653606061022753600061610400527f02610280,0x4a0] | +| 1411 | PUSH32 | 3 | 0 | [] | +| 1444 | PUSH2 | 3 | 0 |[0x527f28536060610229610380527f53600061022a5360f561022b536061042052] | +| 1447 | MSTORE | 6 | 0 |[0x527f28536060610229610380527f53600061022a5360f561022b536061042052,0x4c0] | +| 1448 | PUSH32 | 3 | 0 | [] | +| 1481 | PUSH2 | 3 | 0 |[0x7f6061022c53600061022d536102a0527f60606103a0527f61022e5360006102] | +| 1484 | MSTORE | 7 | 0 |[0x7f6061022c53600061022d536102a0527f60606103a0527f61022e5360006102,0x4e0] | +| 1485 | PUSH32 | 3 | 0 | [] | +| 1518 | PUSH2 | 3 | 0 |[0x2f610440527f53606061023053600061023153606061023253600061026103c0] | +| 1521 | MSTORE | 6 | 0 |[0x2f610440527f53606061023053600061023153606061023253600061026103c0,0x500] | +| 1522 | PUSH32 | 3 | 0 | [] | +| 1555 | PUSH2 | 3 | 0 |[0x527fc0527f61610460527f023353606061023453600061023553608561023653] | +| 1558 | MSTORE | 6 | 0 |[0x527fc0527f61610460527f023353606061023453600061023553608561023653,0x520] | +| 1559 | PUSH32 | 3 | 0 | [] | +| 1592 | PUSH2 | 3 | 0 |[0x605a61023753606103e052610480527f7ff261026102e0526038610300536053] | +| 1595 | MSTORE | 6 | 0 |[0x605a61023753606103e052610480527f7ff261026102e0526038610300536053,0x540] | +| 1596 | PUSH32 | 3 | 0 | [] | +| 1629 | PUSH2 | 3 | 0 |[0x610301536060610302536050610303536104a0527f60610400527f6161030453] | +| 1632 | MSTORE | 6 | 0 |[0x610301536060610302536050610303536104a0527f60610400527f6161030453,0x560] | +| 1633 | PUSH32 | 3 | 0 | [] | +| 1666 | PUSH2 | 3 | 0 |[0x6002610305536039610306536053610307536060616104c0527f030853605061] | +| 1669 | MSTORE | 6 | 0 |[0x6002610305536039610306536053610307536060616104c0527f030853605061,0x580] | +| 1670 | PUSH32 | 3 | 0 | [] | +| 1703 | PUSH2 | 3 | 0 |[0x610420527f030953606161030a53600261030b53603a61030c536104e0527f60] | +| 1706 | MSTORE | 7 | 0 |[0x610420527f030953606161030a53600261030b53603a61030c536104e0527f60,0x5a0] | +| 1707 | PUSH32 | 3 | 0 | [] | +| 1740 | PUSH2 | 3 | 0 |[0x5361030d53606161030e610440527f53600261030f53603b6103105360606161] | +| 1743 | MSTORE | 6 | 0 |[0x5361030d53606161030e610440527f53600261030f53603b6103105360606161,0x5c0] | +| 1744 | PUSH32 | 3 | 0 | [] | +| 1777 | PUSH2 | 3 | 0 |[0x500527f03115360006103125360f36103135361610460526003610480536014] | +| 1780 | MSTORE | 6 | 0 |[0x500527f03115360006103125360f36103135361610460526003610480536014,0x5e0] | +| 1781 | PUSH32 | 3 | 0 | [] | +| 1814 | PUSH2 | 3 | 0 |[0x61048153610520527f6060610482536000610483536060610484536000610485] | +| 1817 | MSTORE | 6 | 0 |[0x61048153610520527f6060610482536000610483536060610484536000610485,0x600] | +| 1818 | PUSH32 | 3 | 0 | [] | +| 1851 | PUSH2 | 3 | 0 |[0x5360f0610486536060610540527f610487536000610488536060610489536000] | +| 1854 | MSTORE | 6 | 0 |[0x5360f0610486536060610540527f610487536000610488536060610489536000,0x620] | +| 1855 | PUSH32 | 3 | 0 | [] | +| 1888 | PUSH2 | 3 | 0 |[0x61048a53606061048b5360006104610560527f8c53606061048d53600061048e] | +| 1891 | MSTORE | 7 | 0 |[0x61048a53606061048b5360006104610560527f8c53606061048d53600061048e,0x640] | +| 1892 | PUSH32 | 3 | 0 | [] | +| 1925 | PUSH2 | 3 | 0 |[0x53608461048f53605a6104905360f4610491536105805260606105a053605061] | +| 1928 | MSTORE | 6 | 0 |[0x53608461048f53605a6104905360f4610491536105805260606105a053605061,0x660] | +| 1929 | PUSH32 | 3 | 0 | [] | +| 1962 | PUSH2 | 3 | 0 |[0x5a15360616105a25360046105a35360926105a45360536105a55360606105a6] | +| 1965 | MSTORE | 6 | 0 |[0x5a15360616105a25360046105a35360926105a45360536105a55360606105a6,0x680] | +| 1966 | PUSH32 | 3 | 0 | [] | +| 1999 | PUSH2 | 3 | 0 |[0x5360506105a75360616105a85360046105a95360936105aa5360536105ab5360] | +| 2002 | MSTORE | 6 | 0 |[0x5360506105a75360616105a85360046105a95360936105aa5360536105ab5360,0x6a0] | +| 2003 | PUSH32 | 3 | 0 | [] | +| 2036 | PUSH2 | 3 | 0 |[0x616105ac5360046105ad5360946105ae5360606105af5360006105b05360f361] | +| 2039 | MSTORE | 6 | 0 |[0x616105ac5360046105ad5360946105ae5360606105af5360006105b05360f361,0x6c0] | +| 2040 | PUSH1 | 3 | 0 | [] | +| 2042 | PUSH2 | 3 | 0 | [0x5] | +| 2045 | MSTORE8 | 7 | 0 |[0x5,0x6e0] | +| 2046 | PUSH1 | 3 | 0 | [] | +| 2048 | PUSH2 | 3 | 0 | [0xb1] | +| 2051 | MSTORE8 | 3 | 0 |[0xb1,0x6e1] | +| 2052 | PUSH1 | 3 | 0 | [] | +| 2054 | PUSH2 | 3 | 0 | [0x53] | +| 2057 | MSTORE8 | 3 | 0 |[0x53,0x6e2] | +| 2058 | PUSH1 | 3 | 0 | [] | +| 2060 | PUSH2 | 3 | 0 | [0x60] | +| 2063 | MSTORE8 | 3 | 0 |[0x60,0x6e3] | +| 2064 | PUSH1 | 3 | 0 | [] | +| 2066 | PUSH2 | 3 | 0 | [0x0] | +| 2069 | MSTORE8 | 3 | 0 |[0x0,0x6e4] | +| 2070 | PUSH1 | 3 | 0 | [] | +| 2072 | PUSH2 | 3 | 0 | [0x61] | +| 2075 | MSTORE8 | 3 | 0 |[0x61,0x6e5] | +| 2076 | PUSH1 | 3 | 0 | [] | +| 2078 | PUSH2 | 3 | 0 | [0x5] | +| 2081 | MSTORE8 | 3 | 0 |[0x5,0x6e6] | +| 2082 | PUSH1 | 3 | 0 | [] | +| 2084 | PUSH2 | 3 | 0 | [0xb2] | +| 2087 | MSTORE8 | 3 | 0 |[0xb2,0x6e7] | +| 2088 | PUSH1 | 3 | 0 | [] | +| 2090 | PUSH2 | 3 | 0 | [0x60] | +| 2093 | MSTORE8 | 3 | 0 |[0x60,0x6e8] | +| 2094 | PUSH1 | 3 | 0 | [] | +| 2096 | PUSH2 | 3 | 0 | [0x0] | +| 2099 | MSTORE8 | 3 | 0 |[0x0,0x6e9] | +| 2100 | PUSH1 | 3 | 0 | [] | +| 2102 | PUSH2 | 3 | 0 | [0x60] | +| 2105 | MSTORE8 | 3 | 0 |[0x60,0x6ea] | +| 2106 | PUSH1 | 3 | 0 | [] | +| 2108 | PUSH2 | 3 | 0 | [0x0] | +| 2111 | MSTORE8 | 3 | 0 |[0x0,0x6eb] | +| 2112 | PUSH1 | 3 | 0 | [] | +| 2114 | PUSH2 | 3 | 0 | [0xf5] | +| 2117 | MSTORE8 | 3 | 0 |[0xf5,0x6ec] | +| 2118 | PUSH1 | 3 | 0 | [] | +| 2120 | PUSH2 | 3 | 0 | [0x60] | +| 2123 | MSTORE8 | 3 | 0 |[0x60,0x6ed] | +| 2124 | PUSH1 | 3 | 0 | [] | +| 2126 | PUSH2 | 3 | 0 | [0x0] | +| 2129 | MSTORE8 | 3 | 0 |[0x0,0x6ee] | +| 2130 | PUSH1 | 3 | 0 | [] | +| 2132 | PUSH2 | 3 | 0 | [0x60] | +| 2135 | MSTORE8 | 3 | 0 |[0x60,0x6ef] | +| 2136 | PUSH1 | 3 | 0 | [] | +| 2138 | PUSH2 | 3 | 0 | [0x0] | +| 2141 | MSTORE8 | 3 | 0 |[0x0,0x6f0] | +| 2142 | PUSH1 | 3 | 0 | [] | +| 2144 | PUSH2 | 3 | 0 | [0x60] | +| 2147 | MSTORE8 | 3 | 0 |[0x60,0x6f1] | +| 2148 | PUSH1 | 3 | 0 | [] | +| 2150 | PUSH2 | 3 | 0 | [0x0] | +| 2153 | MSTORE8 | 3 | 0 |[0x0,0x6f2] | +| 2154 | PUSH1 | 3 | 0 | [] | +| 2156 | PUSH2 | 3 | 0 | [0x60] | +| 2159 | MSTORE8 | 3 | 0 |[0x60,0x6f3] | +| 2160 | PUSH1 | 3 | 0 | [] | +| 2162 | PUSH2 | 3 | 0 | [0x0] | +| 2165 | MSTORE8 | 3 | 0 |[0x0,0x6f4] | +| 2166 | PUSH1 | 3 | 0 | [] | +| 2168 | PUSH2 | 3 | 0 | [0x60] | +| 2171 | MSTORE8 | 3 | 0 |[0x60,0x6f5] | +| 2172 | PUSH1 | 3 | 0 | [] | +| 2174 | PUSH2 | 3 | 0 | [0x0] | +| 2177 | MSTORE8 | 3 | 0 |[0x0,0x6f6] | +| 2178 | PUSH1 | 3 | 0 | [] | +| 2180 | PUSH2 | 3 | 0 | [0x85] | +| 2183 | MSTORE8 | 3 | 0 |[0x85,0x6f7] | +| 2184 | PUSH1 | 3 | 0 | [] | +| 2186 | PUSH2 | 3 | 0 | [0x5a] | +| 2189 | MSTORE8 | 3 | 0 |[0x5a,0x6f8] | +| 2190 | PUSH1 | 3 | 0 | [] | +| 2192 | PUSH2 | 3 | 0 | [0xf1] | +| 2195 | MSTORE8 | 3 | 0 |[0xf1,0x6f9] | +| 2196 | PUSH1 | 3 | 0 | [] | +| 2198 | PUSH2 | 3 | 0 | [0x50] | +| 2201 | MSTORE8 | 3 | 0 |[0x50,0x6fa] | +| 2202 | PUSH1 | 3 | 0 | [] | +| 2204 | PUSH2 | 3 | 0 | [0x50] | +| 2207 | MSTORE8 | 3 | 0 |[0x50,0x6fb] | +| 2208 | PUSH2 | 3 | 0 | [] | +| 2211 | PUSH1 | 3 | 0 | [0x6fc] | +| 2213 | RETURN | 0 | 0 |[0x6fc,0x0] | +| 2605 | PUSH1 | 3 | 0 |[0x7dce2faf43218578e3fcf2ad22df9918a89e2fba] | +| 2607 | PUSH1 | 3 | 0 |[0x7dce2faf43218578e3fcf2ad22df9918a89e2fba,0x0] | +| 2609 | PUSH1 | 3 | 0 |[0x7dce2faf43218578e3fcf2ad22df9918a89e2fba,0x0,0x0] | +| 2611 | PUSH1 | 3 | 0 |[0x7dce2faf43218578e3fcf2ad22df9918a89e2fba,0x0,0x0,0x0] | +| 2613 | PUSH1 | 3 | 0 |[0x7dce2faf43218578e3fcf2ad22df9918a89e2fba,0x0,0x0,0x0,0x0] | +| 2615 | DUP6 | 3 | 0 |[0x7dce2faf43218578e3fcf2ad22df9918a89e2fba,0x0,0x0,0x0,0x0,0x0] | +| 2616 | GAS | 2 | 0 |[0x7dce2faf43218578e3fcf2ad22df9918a89e2fba,0x0,0x0,0x0,0x0,0x0,0x7dce2faf43218578e3fcf2ad22df9918a89e2fba] | +| 2617 | CALL | 295218 | 0 |[0x7dce2faf43218578e3fcf2ad22df9918a89e2fba,0x0,0x0,0x0,0x0,0x0,0x7dce2faf43218578e3fcf2ad22df9918a89e2fba,0x4937e] | +| 0 | PUSH32 | 3 | 0 | [] | +| 33 | PUSH1 | 3 | 0 |[0x6008545060006004557f600160045560006004556000600060006000600060f9] | +| 35 | MSTORE | 6 | 0 |[0x6008545060006004557f600160045560006004556000600060006000600060f9,0x0] | +| 36 | PUSH32 | 3 | 0 | [] | +| 69 | PUSH1 | 3 | 0 |[0x5af250600060006000606000527e60f45af4506000600060006000600060f55a] | +| 71 | MSTORE | 6 | 0 |[0x5af250600060006000606000527e60f45af4506000600060006000600060f55a,0x20] | +| 72 | PUSH32 | 3 | 0 | [] | +| 105 | PUSH1 | 3 | 0 |[0xf150f001075205846a44a283446020527f8ca2600060006000600060045af450] | +| 107 | MSTORE | 6 | 0 |[0xf150f001075205846a44a283446020527f8ca2600060006000600060045af450,0x40] | +| 108 | PUSH32 | 3 | 0 | [] | +| 141 | PUSH1 | 3 | 0 |[0x519930847f3b631c54a49b5f60035450326040527f77306b6000600060006000] | +| 143 | MSTORE | 6 | 0 |[0x519930847f3b631c54a49b5f60035450326040527f77306b6000600060006000,0x60] | +| 144 | PUSH32 | 3 | 0 | [] | +| 177 | PUSH1 | 3 | 0 |[0x6000600c5af150600060006000600060f85af450506060527f06600160025560] | +| 179 | MSTORE | 6 | 0 |[0x6000600c5af150600060006000600060f85af450506060527f06600160025560,0x80] | +| 180 | PUSH32 | 3 | 0 | [] | +| 213 | PUSH1 | 3 | 0 |[0x35450600060005560006001556c3394fff4607f7f1684317b6080527f387b9f] | +| 215 | MSTORE | 6 | 0 |[0x35450600060005560006001556c3394fff4607f7f1684317b6080527f387b9f,0xa0] | +| 216 | PUSH32 | 3 | 0 | [] | +| 249 | PUSH1 | 3 | 0 |[0x1920700184809d60015450011899016e6009ff60026001556000527f9f60a052] | +| 251 | MSTORE | 6 | 0 |[0x1920700184809d60015450011899016e6009ff60026001556000527f9f60a052,0xc0] | +| 252 | PUSH32 | 3 | 0 | [] | +| 285 | PUSH1 | 3 | 0 |[0x7f6000527f9981600160045582600eff600060006000600060f65af450600060] | +| 287 | MSTORE | 6 | 0 |[0x7f6000527f9981600160045582600eff600060006000600060f65af450600060,0xe0] | +| 288 | PUSH32 | 3 | 0 | [] | +| 321 | PUSH2 | 3 | 0 |[0x6060c0527f20527e600060006020527f60f75af4501d7f190316666000600060] | +| 324 | MSTORE | 6 | 0 |[0x6060c0527f20527e600060006020527f60f75af4501d7f190316666000600060,0x100] | +| 325 | PUSH31 | 3 | 0 | [] | +| 357 | PUSH2 | 3 | 0 |[0x6000600060e0527f60046040527f5af1506000600060006040527f60006009] | +| 360 | MSTORE | 6 | 0 |[0x6000600060e0527f60046040527f5af1506000600060006040527f60006009,0x120] | +| 361 | PUSH32 | 3 | 0 | [] | +| 394 | PUSH2 | 3 | 0 |[0x5af4503c95138e5b8f610100527f7f605a6000536060527f6031600153606b60] | +| 397 | MSTORE | 6 | 0 |[0x5af4503c95138e5b8f610100527f7f605a6000536060527f6031600153606b60,0x140] | +| 398 | PUSH32 | 3 | 0 | [] | +| 431 | PUSH2 | 3 | 0 |[0x2536010606060527f0353604560610120527f04536060600553600160608052] | +| 434 | MSTORE | 6 | 0 |[0x2536010606060527f0353604560610120527f04536060600553600160608052,0x160] | +| 435 | PUSH32 | 3 | 0 | [] | +| 468 | PUSH2 | 3 | 0 |[0x7e527f60065360606007536002600853606080610140527f527f556009536060] | +| 471 | MSTORE | 6 | 0 |[0x7e527f60065360606007536002600853606080610140527f527f556009536060,0x180] | +| 472 | PUSH32 | 3 | 0 | [] | +| 505 | PUSH2 | 3 | 0 |[0x600a53600160a0527f600b536060600c6020527f53600060610160527f0d5360] | +| 508 | MSTORE | 6 | 0 |[0x600a53600160a0527f600b536060600c6020527f53600060610160527f0d5360,0x1a0] | +| 509 | PUSH32 | 3 | 0 | [] | +| 542 | PUSH2 | 3 | 0 |[0x55600e60a0527f536060600f536060c0527f0160105360606011536002610180] | +| 545 | MSTORE | 6 | 0 |[0x55600e60a0527f536060600f536060c0527f0160105360606011536002610180,0x1c0] | +| 546 | PUSH32 | 3 | 0 | [] | +| 579 | PUSH2 | 3 | 0 |[0x527f601253606040527f55601353606060c0527f60145360e0527f6000601553] | +| 582 | MSTORE | 6 | 0 |[0x527f601253606040527f55601353606060c0527f60145360e0527f6000601553,0x1e0] | +| 583 | PUSH32 | 3 | 0 | [] | +| 616 | PUSH2 | 3 | 0 |[0x60606101a0527f601653600060175360f360185360196060605260006060e052] | +| 619 | MSTORE | 6 | 0 |[0x60606101a0527f601653600060175360f360185360196060605260006060e052,0x200] | +| 620 | PUSH32 | 3 | 0 | [] | +| 653 | PUSH2 | 3 | 0 |[0x610100527f7f806101c0527f5360f3608153608260006000f060006000600060] | +| 656 | MSTORE | 6 | 0 |[0x610100527f7f806101c0527f5360f3608153608260006000f060006000600060,0x220] | +| 657 | PUSH31 | 3 | 0 | [] | +| 689 | PUSH2 | 3 | 0 |[0x845af450506000600061016101e0527f20527f60610100527e600060006003] | +| 692 | MSTORE | 6 | 0 |[0x845af450506000600061016101e0527f20527f60610100527e600060006003,0x240] | +| 693 | PUSH32 | 3 | 0 | [] | +| 726 | PUSH2 | 3 | 0 |[0x5af15060005450c760006002551309f562610200527f66a486610140527f6b00] | +| 729 | MSTORE | 6 | 0 |[0x5af15060005450c760006002551309f562610200527f66a486610140527f6b00,0x260] | +| 730 | PUSH32 | 3 | 0 | [] | +| 763 | PUSH2 | 3 | 0 |[0x1d4571610120527f600054501c641d373c7f60045450610220527f6000600155] | +| 766 | MSTORE | 6 | 0 |[0x1d4571610120527f600054501c641d373c7f60045450610220527f6000600155,0x280] | +| 767 | PUSH32 | 3 | 0 | [] | +| 800 | PUSH2 | 3 | 0 |[0x600554610160527f50600160025560085450610140527f60006002610240527f] | +| 803 | MSTORE | 6 | 0 |[0x600554610160527f50600160025560085450610140527f60006002610240527f,0x2a0] | +| 804 | PUSH32 | 3 | 0 | [] | +| 837 | PUSH2 | 3 | 0 |[0x557fd86000606000527e600060610180527e600060005af15086121714514735] | +| 840 | MSTORE | 7 | 0 |[0x557fd86000606000527e600060610180527e600060005af15086121714514735,0x2c0] | +| 841 | PUSH32 | 3 | 0 | [] | +| 874 | PUSH2 | 3 | 0 |[0x610260527f610160527f5198a37e127a7efa7c6000526101a0527f606020527f] | +| 877 | MSTORE | 6 | 0 |[0x610260527f610160527f5198a37e127a7efa7c6000526101a0527f606020527f,0x2e0] | +| 878 | PUSH32 | 3 | 0 | [] | +| 911 | PUSH2 | 3 | 0 |[0x6060205360610280527ff760215360ff60225360610180527fdb602353603760] | +| 914 | MSTORE | 6 | 0 |[0x6060205360610280527ff760215360ff60225360610180527fdb602353603760,0x300] | +| 915 | PUSH32 | 3 | 0 | [] | +| 948 | PUSH2 | 3 | 0 |[0x6101c0527f24536075606102a0527f2553609f606040527f265360fe60275360] | +| 951 | MSTORE | 6 | 0 |[0x6101c0527f24536075606102a0527f2553609f606040527f265360fe60275360,0x320] | +| 952 | PUSH32 | 3 | 0 | [] | +| 985 | PUSH2 | 3 | 0 |[0x8f60286101a0527f53606101e0527f6102c0527f0b6029536060602a53600060] | +| 988 | MSTORE | 6 | 0 |[0x8f60286101a0527f53606101e0527f6102c0527f0b6029536060602a53600060,0x340] | +| 989 | PUSH32 | 3 | 0 | [] | +| 1022 | PUSH2 | 3 | 0 |[0x2b536060602c53606052606060805360006061016102e0527f610200527fc052] | +| 1025 | MSTORE | 6 | 0 |[0x2b536060602c53606052606060805360006061016102e0527f610200527fc052,0x360] | +| 1026 | PUSH32 | 3 | 0 | [] | +| 1059 | PUSH2 | 3 | 0 |[0x7f81536060608253602d6083536053608453606060855360fd610300527f6086] | +| 1062 | MSTORE | 6 | 0 |[0x7f81536060608253602d6083536053608453606060855360fd610300527f6086,0x380] | +| 1063 | PUSH32 | 3 | 0 | [] | +| 1096 | PUSH2 | 3 | 0 |[0x536060610220527f6087536101e0527f602e60885360536089536060608a6103] | +| 1099 | MSTORE | 6 | 0 |[0x536060610220527f6087536101e0527f602e60885360536089536060608a6103,0x3a0] | +| 1100 | PUSH32 | 3 | 0 | [] | +| 1133 | PUSH2 | 3 | 0 |[0x20527f53602f608b536060608c610240527f536000608d5360f3610200526060] | +| 1136 | MSTORE | 6 | 0 |[0x20527f53602f608b536060608c610240527f536000608d5360f3610200526060,0x3c0] | +| 1137 | PUSH32 | 3 | 0 | [] | +| 1170 | PUSH2 | 3 | 0 |[0x610220610340527f53608e610221536053610222536060610260527f61022353] | +| 1173 | MSTORE | 7 | 0 |[0x610220610340527f53608e610221536053610222536060610260527f61022353,0x3e0] | +| 1174 | PUSH32 | 3 | 0 | [] | +| 1207 | PUSH2 | 3 | 0 |[0x6000610224536060610360527f61022553608f61022653606061022753600061] | +| 1210 | MSTORE | 6 | 0 |[0x6000610224536060610360527f61022553608f61022653606061022753600061,0x400] | +| 1211 | PUSH32 | 3 | 0 | [] | +| 1244 | PUSH2 | 3 | 0 |[0x2610280527f28536060610229610380527f53600061022a5360f561022b5360] | +| 1247 | MSTORE | 6 | 0 |[0x2610280527f28536060610229610380527f53600061022a5360f561022b5360,0x420] | +| 1248 | PUSH32 | 3 | 0 | [] | +| 1281 | PUSH2 | 3 | 0 |[0x6061022c53600061022d536102a0527f60606103a0527f61022e53600061022f] | +| 1284 | MSTORE | 6 | 0 |[0x6061022c53600061022d536102a0527f60606103a0527f61022e53600061022f,0x440] | +| 1285 | PUSH32 | 3 | 0 | [] | +| 1318 | PUSH2 | 3 | 0 |[0x53606061023053600061023153606061023253600061026103c0527fc0527f61] | +| 1321 | MSTORE | 6 | 0 |[0x53606061023053600061023153606061023253600061026103c0527fc0527f61,0x460] | +| 1322 | PUSH32 | 3 | 0 | [] | +| 1355 | PUSH2 | 3 | 0 |[0x23353606061023453600061023553608561023653605a61023753606103e052] | +| 1358 | MSTORE | 6 | 0 |[0x23353606061023453600061023553608561023653605a61023753606103e052,0x480] | +| 1359 | PUSH32 | 3 | 0 | [] | +| 1392 | PUSH2 | 3 | 0 |[0x7ff261026102e052603861030053605361030153606061030253605061030353] | +| 1395 | MSTORE | 6 | 0 |[0x7ff261026102e052603861030053605361030153606061030253605061030353,0x4a0] | +| 1396 | PUSH32 | 3 | 0 | [] | +| 1429 | PUSH2 | 3 | 0 |[0x60610400527f6161030453600261030553603961030653605361030753606061] | +| 1432 | MSTORE | 6 | 0 |[0x60610400527f6161030453600261030553603961030653605361030753606061,0x4c0] | +| 1433 | PUSH32 | 3 | 0 | [] | +| 1466 | PUSH2 | 3 | 0 |[0x30853605061610420527f030953606161030a53600261030b53603a61030c53] | +| 1469 | MSTORE | 7 | 0 |[0x30853605061610420527f030953606161030a53600261030b53603a61030c53,0x4e0] | +| 1470 | PUSH32 | 3 | 0 | [] | +| 1503 | PUSH2 | 3 | 0 |[0x605361030d53606161030e610440527f53600261030f53603b61031053606061] | +| 1506 | MSTORE | 6 | 0 |[0x605361030d53606161030e610440527f53600261030f53603b61031053606061,0x500] | +| 1507 | PUSH32 | 3 | 0 | [] | +| 1540 | PUSH2 | 3 | 0 |[0x3115360006103125360f3610313536161046052600361048053601461048153] | +| 1543 | MSTORE | 6 | 0 |[0x3115360006103125360f3610313536161046052600361048053601461048153,0x520] | +| 1544 | PUSH32 | 3 | 0 | [] | +| 1577 | PUSH2 | 3 | 0 |[0x60606104825360006104835360606104845360006104855360f0610486536060] | +| 1580 | MSTORE | 6 | 0 |[0x60606104825360006104835360606104845360006104855360f0610486536060,0x540] | +| 1581 | PUSH32 | 3 | 0 | [] | +| 1614 | PUSH2 | 3 | 0 |[0x61048753600061048853606061048953600061048a53606061048b5360006104] | +| 1617 | MSTORE | 6 | 0 |[0x61048753600061048853606061048953600061048a53606061048b5360006104,0x560] | +| 1618 | PUSH32 | 3 | 0 | [] | +| 1651 | PUSH2 | 3 | 0 |[0x8c53606061048d53600061048e53608461048f53605a6104905360f461049153] | +| 1654 | MSTORE | 6 | 0 |[0x8c53606061048d53600061048e53608461048f53605a6104905360f461049153,0x580] | +| 1655 | PUSH1 | 3 | 0 | [] | +| 1657 | PUSH2 | 3 | 0 | [0x60] | +| 1660 | MSTORE8 | 7 | 0 |[0x60,0x5a0] | +| 1661 | PUSH1 | 3 | 0 | [] | +| 1663 | PUSH2 | 3 | 0 | [0x50] | +| 1666 | MSTORE8 | 3 | 0 |[0x50,0x5a1] | +| 1667 | PUSH1 | 3 | 0 | [] | +| 1669 | PUSH2 | 3 | 0 | [0x61] | +| 1672 | MSTORE8 | 3 | 0 |[0x61,0x5a2] | +| 1673 | PUSH1 | 3 | 0 | [] | +| 1675 | PUSH2 | 3 | 0 | [0x4] | +| 1678 | MSTORE8 | 3 | 0 |[0x4,0x5a3] | +| 1679 | PUSH1 | 3 | 0 | [] | +| 1681 | PUSH2 | 3 | 0 | [0x92] | +| 1684 | MSTORE8 | 3 | 0 |[0x92,0x5a4] | +| 1685 | PUSH1 | 3 | 0 | [] | +| 1687 | PUSH2 | 3 | 0 | [0x53] | +| 1690 | MSTORE8 | 3 | 0 |[0x53,0x5a5] | +| 1691 | PUSH1 | 3 | 0 | [] | +| 1693 | PUSH2 | 3 | 0 | [0x60] | +| 1696 | MSTORE8 | 3 | 0 |[0x60,0x5a6] | +| 1697 | PUSH1 | 3 | 0 | [] | +| 1699 | PUSH2 | 3 | 0 | [0x50] | +| 1702 | MSTORE8 | 3 | 0 |[0x50,0x5a7] | +| 1703 | PUSH1 | 3 | 0 | [] | +| 1705 | PUSH2 | 3 | 0 | [0x61] | +| 1708 | MSTORE8 | 3 | 0 |[0x61,0x5a8] | +| 1709 | PUSH1 | 3 | 0 | [] | +| 1711 | PUSH2 | 3 | 0 | [0x4] | +| 1714 | MSTORE8 | 3 | 0 |[0x4,0x5a9] | +| 1715 | PUSH1 | 3 | 0 | [] | +| 1717 | PUSH2 | 3 | 0 | [0x93] | +| 1720 | MSTORE8 | 3 | 0 |[0x93,0x5aa] | +| 1721 | PUSH1 | 3 | 0 | [] | +| 1723 | PUSH2 | 3 | 0 | [0x53] | +| 1726 | MSTORE8 | 3 | 0 |[0x53,0x5ab] | +| 1727 | PUSH1 | 3 | 0 | [] | +| 1729 | PUSH2 | 3 | 0 | [0x61] | +| 1732 | MSTORE8 | 3 | 0 |[0x61,0x5ac] | +| 1733 | PUSH1 | 3 | 0 | [] | +| 1735 | PUSH2 | 3 | 0 | [0x4] | +| 1738 | MSTORE8 | 3 | 0 |[0x4,0x5ad] | +| 1739 | PUSH1 | 3 | 0 | [] | +| 1741 | PUSH2 | 3 | 0 | [0x94] | +| 1744 | MSTORE8 | 3 | 0 |[0x94,0x5ae] | +| 1745 | PUSH1 | 3 | 0 | [] | +| 1747 | PUSH2 | 3 | 0 | [0x60] | +| 1750 | MSTORE8 | 3 | 0 |[0x60,0x5af] | +| 1751 | PUSH1 | 3 | 0 | [] | +| 1753 | PUSH2 | 3 | 0 | [0x0] | +| 1756 | MSTORE8 | 3 | 0 |[0x0,0x5b0] | +| 1757 | PUSH1 | 3 | 0 | [] | +| 1759 | PUSH2 | 3 | 0 | [0xf3] | +| 1762 | MSTORE8 | 3 | 0 |[0xf3,0x5b1] | +| 1763 | PUSH1 | 3 | 0 | [] | +| 1765 | PUSH2 | 3 | 0 | [0x0] | +| 1768 | PUSH1 | 3 | 0 |[0x0,0x5b2] | +| 1770 | PUSH1 | 3 | 0 |[0x0,0x5b2,0x0] | +| 1772 | CREATE2 | 32276 | 0 |[0x0,0x5b2,0x0,0x0] | +| 0 | PUSH1 | 3 | 0 | [] | +| 2 | SLOAD | 2100 | 0 | [0x8] | +| 3 | POP | 2 | 0 | [0x0] | +| 4 | PUSH1 | 3 | 0 | [] | +| 6 | PUSH1 | 3 | 0 | [0x0] | +| 8 | SSTORE | 2200 | 0 | [0x0,0x4] | +| 9 | PUSH32 | 3 | 0 | [] | +| 42 | PUSH1 | 3 | 0 |[0x600160045560006004556000600060006000600060f95af25060006000600060] | +| 44 | MSTORE | 6 | 0 |[0x600160045560006004556000600060006000600060f95af25060006000600060,0x0] | +| 45 | PUSH31 | 3 | 0 | [] | +| 77 | PUSH1 | 3 | 0 |[0x60f45af4506000600060006000600060f55af150f001075205846a44a28344] | +| 79 | MSTORE | 6 | 0 |[0x60f45af4506000600060006000600060f55af150f001075205846a44a28344,0x20] | +| 80 | PUSH32 | 3 | 0 | [] | +| 113 | PUSH1 | 3 | 0 |[0x8ca2600060006000600060045af450519930847f3b631c54a49b5f6003545032] | +| 115 | MSTORE | 6 | 0 |[0x8ca2600060006000600060045af450519930847f3b631c54a49b5f6003545032,0x40] | +| 116 | PUSH32 | 3 | 0 | [] | +| 149 | PUSH1 | 3 | 0 |[0x77306b60006000600060006000600c5af150600060006000600060f85af45050] | +| 151 | MSTORE | 6 | 0 |[0x77306b60006000600060006000600c5af150600060006000600060f85af45050,0x60] | +| 152 | PUSH32 | 3 | 0 | [] | +| 185 | PUSH1 | 3 | 0 |[0x6600160025560035450600060005560006001556c3394fff4607f7f1684317b] | +| 187 | MSTORE | 6 | 0 |[0x6600160025560035450600060005560006001556c3394fff4607f7f1684317b,0x80] | +| 188 | PUSH32 | 3 | 0 | [] | +| 221 | PUSH1 | 3 | 0 |[0x387b9f1920700184809d60015450011899016e6009ff60026001556000527f9f] | +| 223 | MSTORE | 6 | 0 |[0x387b9f1920700184809d60015450011899016e6009ff60026001556000527f9f,0xa0] | +| 224 | PUSH32 | 3 | 0 | [] | +| 257 | PUSH1 | 3 | 0 |[0x6000527f9981600160045582600eff600060006000600060f65af45060006060] | +| 259 | MSTORE | 6 | 0 |[0x6000527f9981600160045582600eff600060006000600060f65af45060006060,0xc0] | +| 260 | PUSH32 | 3 | 0 | [] | +| 293 | PUSH1 | 3 | 0 |[0x20527e600060006020527f60f75af4501d7f1903166660006000600060006000] | +| 295 | MSTORE | 6 | 0 |[0x20527e600060006020527f60f75af4501d7f1903166660006000600060006000,0xe0] | +| 296 | PUSH32 | 3 | 0 | [] | +| 329 | PUSH2 | 3 | 0 |[0x60046040527f5af1506000600060006040527f600060095af4503c95138e5b8f] | +| 332 | MSTORE | 6 | 0 |[0x60046040527f5af1506000600060006040527f600060095af4503c95138e5b8f,0x100] | +| 333 | PUSH32 | 3 | 0 | [] | +| 366 | PUSH2 | 3 | 0 |[0x7f605a6000536060527f6031600153606b6002536010606060527f0353604560] | +| 369 | MSTORE | 6 | 0 |[0x7f605a6000536060527f6031600153606b6002536010606060527f0353604560,0x120] | +| 370 | PUSH32 | 3 | 0 | [] | +| 403 | PUSH2 | 3 | 0 |[0x45360606005536001606080527e527f60065360606007536002600853606080] | +| 406 | MSTORE | 6 | 0 |[0x45360606005536001606080527e527f60065360606007536002600853606080,0x140] | +| 407 | PUSH32 | 3 | 0 | [] | +| 440 | PUSH2 | 3 | 0 |[0x527f556009536060600a53600160a0527f600b536060600c6020527f53600060] | +| 443 | MSTORE | 6 | 0 |[0x527f556009536060600a53600160a0527f600b536060600c6020527f53600060,0x160] | +| 444 | PUSH32 | 3 | 0 | [] | +| 477 | PUSH2 | 3 | 0 |[0xd536055600e60a0527f536060600f536060c0527f0160105360606011536002] | +| 480 | MSTORE | 6 | 0 |[0xd536055600e60a0527f536060600f536060c0527f0160105360606011536002,0x180] | +| 481 | PUSH32 | 3 | 0 | [] | +| 514 | PUSH2 | 3 | 0 |[0x601253606040527f55601353606060c0527f60145360e0527f60006015536060] | +| 517 | MSTORE | 6 | 0 |[0x601253606040527f55601353606060c0527f60145360e0527f60006015536060,0x1a0] | +| 518 | PUSH32 | 3 | 0 | [] | +| 551 | PUSH2 | 3 | 0 |[0x601653600060175360f360185360196060605260006060e052610100527f7f80] | +| 554 | MSTORE | 6 | 0 |[0x601653600060175360f360185360196060605260006060e052610100527f7f80,0x1c0] | +| 555 | PUSH32 | 3 | 0 | [] | +| 588 | PUSH2 | 3 | 0 |[0x5360f3608153608260006000f06000600060006000845af45050600060006101] | +| 591 | MSTORE | 6 | 0 |[0x5360f3608153608260006000f06000600060006000845af45050600060006101,0x1e0] | +| 592 | PUSH32 | 3 | 0 | [] | +| 625 | PUSH2 | 3 | 0 |[0x20527f60610100527e6000600060035af15060005450c760006002551309f562] | +| 628 | MSTORE | 6 | 0 |[0x20527f60610100527e6000600060035af15060005450c760006002551309f562,0x200] | +| 629 | PUSH32 | 3 | 0 | [] | +| 662 | PUSH2 | 3 | 0 |[0x66a486610140527f6b001d4571610120527f600054501c641d373c7f60045450] | +| 665 | MSTORE | 6 | 0 |[0x66a486610140527f6b001d4571610120527f600054501c641d373c7f60045450,0x220] | +| 666 | PUSH32 | 3 | 0 | [] | +| 699 | PUSH2 | 3 | 0 |[0x6000600155600554610160527f50600160025560085450610140527f60006002] | +| 702 | MSTORE | 6 | 0 |[0x6000600155600554610160527f50600160025560085450610140527f60006002,0x240] | +| 703 | PUSH32 | 3 | 0 | [] | +| 736 | PUSH2 | 3 | 0 |[0x557fd86000606000527e600060610180527e600060005af15086121714514735] | +| 739 | MSTORE | 6 | 0 |[0x557fd86000606000527e600060610180527e600060005af15086121714514735,0x260] | +| 740 | PUSH32 | 3 | 0 | [] | +| 773 | PUSH2 | 3 | 0 |[0x610160527f5198a37e127a7efa7c6000526101a0527f606020527f6060205360] | +| 776 | MSTORE | 6 | 0 |[0x610160527f5198a37e127a7efa7c6000526101a0527f606020527f6060205360,0x280] | +| 777 | PUSH32 | 3 | 0 | [] | +| 810 | PUSH2 | 3 | 0 |[0xf760215360ff60225360610180527fdb6023536037606101c0527f2453607560] | +| 813 | MSTORE | 6 | 0 |[0xf760215360ff60225360610180527fdb6023536037606101c0527f2453607560,0x2a0] | +| 814 | PUSH32 | 3 | 0 | [] | +| 847 | PUSH2 | 3 | 0 |[0x2553609f606040527f265360fe602753608f60286101a0527f53606101e0527f] | +| 850 | MSTORE | 7 | 0 |[0x2553609f606040527f265360fe602753608f60286101a0527f53606101e0527f,0x2c0] | +| 851 | PUSH32 | 3 | 0 | [] | +| 884 | PUSH2 | 3 | 0 |[0xb6029536060602a536000602b536060602c5360605260606080536000606101] | +| 887 | MSTORE | 6 | 0 |[0xb6029536060602a536000602b536060602c5360605260606080536000606101,0x2e0] | +| 888 | PUSH32 | 3 | 0 | [] | +| 921 | PUSH2 | 3 | 0 |[0x610200527fc0527f81536060608253602d6083536053608453606060855360fd] | +| 924 | MSTORE | 6 | 0 |[0x610200527fc0527f81536060608253602d6083536053608453606060855360fd,0x300] | +| 925 | PUSH32 | 3 | 0 | [] | +| 958 | PUSH2 | 3 | 0 |[0x6086536060610220527f6087536101e0527f602e60885360536089536060608a] | +| 961 | MSTORE | 6 | 0 |[0x6086536060610220527f6087536101e0527f602e60885360536089536060608a,0x320] | +| 962 | PUSH32 | 3 | 0 | [] | +| 995 | PUSH2 | 3 | 0 |[0x53602f608b536060608c610240527f536000608d5360f3610200526060610220] | +| 998 | MSTORE | 6 | 0 |[0x53602f608b536060608c610240527f536000608d5360f3610200526060610220,0x340] | +| 999 | PUSH32 | 3 | 0 | [] | +| 1032 | PUSH2 | 3 | 0 |[0x53608e610221536053610222536060610260527f610223536000610224536060] | +| 1035 | MSTORE | 6 | 0 |[0x53608e610221536053610222536060610260527f610223536000610224536060,0x360] | +| 1036 | PUSH32 | 3 | 0 | [] | +| 1069 | PUSH2 | 3 | 0 |[0x61022553608f6102265360606102275360006102610280527f28536060610229] | +| 1072 | MSTORE | 6 | 0 |[0x61022553608f6102265360606102275360006102610280527f28536060610229,0x380] | +| 1073 | PUSH32 | 3 | 0 | [] | +| 1106 | PUSH2 | 3 | 0 |[0x53600061022a5360f561022b53606061022c53600061022d536102a0527f6060] | +| 1109 | MSTORE | 6 | 0 |[0x53600061022a5360f561022b53606061022c53600061022d536102a0527f6060,0x3a0] | +| 1110 | PUSH32 | 3 | 0 | [] | +| 1143 | PUSH2 | 3 | 0 |[0x61022e53600061022f5360606102305360006102315360606102325360006102] | +| 1146 | MSTORE | 6 | 0 |[0x61022e53600061022f5360606102305360006102315360606102325360006102,0x3c0] | +| 1147 | PUSH32 | 3 | 0 | [] | +| 1180 | PUSH2 | 3 | 0 |[0xc0527f61023353606061023453600061023553608561023653605a6102375360] | +| 1183 | MSTORE | 7 | 0 |[0xc0527f61023353606061023453600061023553608561023653605a6102375360,0x3e0] | +| 1184 | PUSH32 | 3 | 0 | [] | +| 1217 | PUSH2 | 3 | 0 |[0xf261026102e05260386103005360536103015360606103025360506103035360] | +| 1220 | MSTORE | 6 | 0 |[0xf261026102e05260386103005360536103015360606103025360506103035360,0x400] | +| 1221 | PUSH32 | 3 | 0 | [] | +| 1254 | PUSH2 | 3 | 0 |[0x6161030453600261030553603961030653605361030753606061030853605061] | +| 1257 | MSTORE | 6 | 0 |[0x6161030453600261030553603961030653605361030753606061030853605061,0x420] | +| 1258 | PUSH32 | 3 | 0 | [] | +| 1291 | PUSH2 | 3 | 0 |[0x30953606161030a53600261030b53603a61030c53605361030d53606161030e] | +| 1294 | MSTORE | 6 | 0 |[0x30953606161030a53600261030b53603a61030c53605361030d53606161030e,0x440] | +| 1295 | PUSH32 | 3 | 0 | [] | +| 1328 | PUSH2 | 3 | 0 |[0x53600261030f53603b6103105360606103115360006103125360f36103135361] | +| 1331 | MSTORE | 6 | 0 |[0x53600261030f53603b6103105360606103115360006103125360f36103135361,0x460] | +| 1332 | PUSH1 | 3 | 0 | [] | +| 1334 | PUSH2 | 3 | 0 | [0x3] | +| 1337 | MSTORE8 | 6 | 0 |[0x3,0x480] | +| 1338 | PUSH1 | 3 | 0 | [] | +| 1340 | PUSH2 | 3 | 0 | [0x14] | +| 1343 | MSTORE8 | 3 | 0 |[0x14,0x481] | +| 1344 | PUSH1 | 3 | 0 | [] | +| 1346 | PUSH2 | 3 | 0 | [0x60] | +| 1349 | MSTORE8 | 3 | 0 |[0x60,0x482] | +| 1350 | PUSH1 | 3 | 0 | [] | +| 1352 | PUSH2 | 3 | 0 | [0x0] | +| 1355 | MSTORE8 | 3 | 0 |[0x0,0x483] | +| 1356 | PUSH1 | 3 | 0 | [] | +| 1358 | PUSH2 | 3 | 0 | [0x60] | +| 1361 | MSTORE8 | 3 | 0 |[0x60,0x484] | +| 1362 | PUSH1 | 3 | 0 | [] | +| 1364 | PUSH2 | 3 | 0 | [0x0] | +| 1367 | MSTORE8 | 3 | 0 |[0x0,0x485] | +| 1368 | PUSH1 | 3 | 0 | [] | +| 1370 | PUSH2 | 3 | 0 | [0xf0] | +| 1373 | MSTORE8 | 3 | 0 |[0xf0,0x486] | +| 1374 | PUSH1 | 3 | 0 | [] | +| 1376 | PUSH2 | 3 | 0 | [0x60] | +| 1379 | MSTORE8 | 3 | 0 |[0x60,0x487] | +| 1380 | PUSH1 | 3 | 0 | [] | +| 1382 | PUSH2 | 3 | 0 | [0x0] | +| 1385 | MSTORE8 | 3 | 0 |[0x0,0x488] | +| 1386 | PUSH1 | 3 | 0 | [] | +| 1388 | PUSH2 | 3 | 0 | [0x60] | +| 1391 | MSTORE8 | 3 | 0 |[0x60,0x489] | +| 1392 | PUSH1 | 3 | 0 | [] | +| 1394 | PUSH2 | 3 | 0 | [0x0] | +| 1397 | MSTORE8 | 3 | 0 |[0x0,0x48a] | +| 1398 | PUSH1 | 3 | 0 | [] | +| 1400 | PUSH2 | 3 | 0 | [0x60] | +| 1403 | MSTORE8 | 3 | 0 |[0x60,0x48b] | +| 1404 | PUSH1 | 3 | 0 | [] | +| 1406 | PUSH2 | 3 | 0 | [0x0] | +| 1409 | MSTORE8 | 3 | 0 |[0x0,0x48c] | +| 1410 | PUSH1 | 3 | 0 | [] | +| 1412 | PUSH2 | 3 | 0 | [0x60] | +| 1415 | MSTORE8 | 3 | 0 |[0x60,0x48d] | +| 1416 | PUSH1 | 3 | 0 | [] | +| 1418 | PUSH2 | 3 | 0 | [0x0] | +| 1421 | MSTORE8 | 3 | 0 |[0x0,0x48e] | +| 1422 | PUSH1 | 3 | 0 | [] | +| 1424 | PUSH2 | 3 | 0 | [0x84] | +| 1427 | MSTORE8 | 3 | 0 |[0x84,0x48f] | +| 1428 | PUSH1 | 3 | 0 | [] | +| 1430 | PUSH2 | 3 | 0 | [0x5a] | +| 1433 | MSTORE8 | 3 | 0 |[0x5a,0x490] | +| 1434 | PUSH1 | 3 | 0 | [] | +| 1436 | PUSH2 | 3 | 0 | [0xf4] | +| 1439 | MSTORE8 | 3 | 0 |[0xf4,0x491] | +| 1440 | PUSH1 | 3 | 0 | [] | +| 1442 | PUSH2 | 3 | 0 | [0x50] | +| 1445 | MSTORE8 | 3 | 0 |[0x50,0x492] | +| 1446 | PUSH1 | 3 | 0 | [] | +| 1448 | PUSH2 | 3 | 0 | [0x50] | +| 1451 | MSTORE8 | 3 | 0 |[0x50,0x493] | +| 1452 | PUSH2 | 3 | 0 | [] | +| 1455 | PUSH1 | 3 | 0 | [0x494] | +| 1457 | RETURN | 0 | 0 |[0x494,0x0] | +| 1773 | PUSH1 | 3 | 0 |[0x94a843a7335fc63be036fbdecc40b1365f3c5f26] | +| 1775 | PUSH1 | 3 | 0 |[0x94a843a7335fc63be036fbdecc40b1365f3c5f26,0x0] | +| 1777 | PUSH1 | 3 | 0 |[0x94a843a7335fc63be036fbdecc40b1365f3c5f26,0x0,0x0] | +| 1779 | PUSH1 | 3 | 0 |[0x94a843a7335fc63be036fbdecc40b1365f3c5f26,0x0,0x0,0x0] | +| 1781 | PUSH1 | 3 | 0 |[0x94a843a7335fc63be036fbdecc40b1365f3c5f26,0x0,0x0,0x0,0x0] | +| 1783 | DUP6 | 3 | 0 |[0x94a843a7335fc63be036fbdecc40b1365f3c5f26,0x0,0x0,0x0,0x0,0x0] | +| 1784 | GAS | 2 | 0 |[0x94a843a7335fc63be036fbdecc40b1365f3c5f26,0x0,0x0,0x0,0x0,0x0,0x94a843a7335fc63be036fbdecc40b1365f3c5f26] | +| 1785 | CALL | 22413 | 0 |[0x94a843a7335fc63be036fbdecc40b1365f3c5f26,0x0,0x0,0x0,0x0,0x0,0x94a843a7335fc63be036fbdecc40b1365f3c5f26,0x58ef] | +| 0 | PUSH1 | 3 | 0 | [] | +| 2 | PUSH1 | 3 | 0 | [0x1] | +| 4 | SSTORE | 20000 | 0 | [0x1,0x4] | +| 5 | PUSH1 | 3 | 0 | [] | +| 7 | PUSH1 | 3 | 0 | [0x0] | +| 9 | SSTORE | 100 | 19900 | [0x0,0x4] | +| 10 | PUSH1 | 3 | 19900 | [] | +| 12 | PUSH1 | 3 | 19900 | [0x0] | +| 14 | PUSH1 | 3 | 19900 | [0x0,0x0] | +| 16 | PUSH1 | 3 | 19900 |[0x0,0x0,0x0] | +| 18 | PUSH1 | 3 | 19900 |[0x0,0x0,0x0,0x0] | +| 20 | PUSH1 | 3 | 19900 |[0x0,0x0,0x0,0x0,0x0] | +| 22 | GAS | 2 | 19900 |[0x0,0x0,0x0,0x0,0x0,0xf9] | +| 23 | CALLCODE | 100 | 19900 |[0x0,0x0,0x0,0x0,0x0,0xf9,0x885] | +Error: out of gas: out of gas +| 1786 | POP | 2 | 0 |[0x94a843a7335fc63be036fbdecc40b1365f3c5f26,0x0] | +| 1787 | POP | 2 | 0 |[0x94a843a7335fc63be036fbdecc40b1365f3c5f26] | +| 1788 | STOP | 0 | 0 | [] | +| 2618 | POP | 2 | 0 |[0x7dce2faf43218578e3fcf2ad22df9918a89e2fba,0x1] | +| 2619 | POP | 2 | 0 |[0x7dce2faf43218578e3fcf2ad22df9918a89e2fba] | +| 2620 | STOP | 0 | 0 | [] | + +Post-execution info: + - output: `` + - consumed gas: `732781` + - error: `` +{"stateRoot": "0xad1024c87b5548e77c937aa50f72b6cb620d278f4dd79bae7f78f71ff75af458"} diff --git a/cmd/evm/testdata/evmrun/8.out.1.txt b/cmd/evm/testdata/evmrun/8.out.1.txt new file mode 100644 index 0000000000..dc22c2ef3a --- /dev/null +++ b/cmd/evm/testdata/evmrun/8.out.1.txt @@ -0,0 +1,9 @@ +[ + { + "name": "00000006-naivefuzz-0", + "pass": false, + "stateRoot": "0xad1024c87b5548e77c937aa50f72b6cb620d278f4dd79bae7f78f71ff75af458", + "fork": "London", + "error": "post state root mismatch: got ad1024c87b5548e77c937aa50f72b6cb620d278f4dd79bae7f78f71ff75af458, want 0000000000000000000000000000000000000000000000000000000000000000" + } +] diff --git a/cmd/evm/testdata/evmrun/8.out.2.txt b/cmd/evm/testdata/evmrun/8.out.2.txt new file mode 100644 index 0000000000..3e6e162e29 --- /dev/null +++ b/cmd/evm/testdata/evmrun/8.out.2.txt @@ -0,0 +1,916 @@ +{"pc":0,"op":96,"gas":"0xb4213","gasCost":"0x3","memSize":0,"stack":[],"depth":1,"refund":0,"opName":"PUSH1"} +{"pc":2,"op":96,"gas":"0xb4210","gasCost":"0x3","memSize":0,"stack":["0x2"],"depth":1,"refund":0,"opName":"PUSH1"} +{"pc":4,"op":85,"gas":"0xb420d","gasCost":"0x5654","memSize":0,"stack":["0x2","0x3"],"depth":1,"refund":0,"opName":"SSTORE"} +{"pc":5,"op":96,"gas":"0xaebb9","gasCost":"0x3","memSize":0,"stack":[],"depth":1,"refund":0,"opName":"PUSH1"} +{"pc":7,"op":96,"gas":"0xaebb6","gasCost":"0x3","memSize":0,"stack":["0x0"],"depth":1,"refund":0,"opName":"PUSH1"} +{"pc":9,"op":96,"gas":"0xaebb3","gasCost":"0x3","memSize":0,"stack":["0x0","0x0"],"depth":1,"refund":0,"opName":"PUSH1"} +{"pc":11,"op":96,"gas":"0xaebb0","gasCost":"0x3","memSize":0,"stack":["0x0","0x0","0x0"],"depth":1,"refund":0,"opName":"PUSH1"} +{"pc":13,"op":96,"gas":"0xaebad","gasCost":"0x3","memSize":0,"stack":["0x0","0x0","0x0","0x0"],"depth":1,"refund":0,"opName":"PUSH1"} +{"pc":15,"op":96,"gas":"0xaebaa","gasCost":"0x3","memSize":0,"stack":["0x0","0x0","0x0","0x0","0x0"],"depth":1,"refund":0,"opName":"PUSH1"} +{"pc":17,"op":90,"gas":"0xaeba7","gasCost":"0x2","memSize":0,"stack":["0x0","0x0","0x0","0x0","0x0","0x4"],"depth":1,"refund":0,"opName":"GAS"} +{"pc":18,"op":242,"gas":"0xaeba5","gasCost":"0xabff8","memSize":0,"stack":["0x0","0x0","0x0","0x0","0x0","0x4","0xaeba5"],"depth":1,"refund":0,"opName":"CALLCODE"} +{"output":"","gasUsed":"0xf"} +{"pc":19,"op":80,"gas":"0xaeb32","gasCost":"0x2","memSize":0,"stack":["0x1"],"depth":1,"refund":0,"opName":"POP"} +{"pc":20,"op":127,"gas":"0xaeb30","gasCost":"0x3","memSize":0,"stack":[],"depth":1,"refund":0,"opName":"PUSH32"} +{"pc":53,"op":96,"gas":"0xaeb2d","gasCost":"0x3","memSize":0,"stack":["0x600254506003545060016003557f7f6008545060006004557f60016004556000"],"depth":1,"refund":0,"opName":"PUSH1"} +{"pc":55,"op":82,"gas":"0xaeb2a","gasCost":"0x6","memSize":0,"stack":["0x600254506003545060016003557f7f6008545060006004557f60016004556000","0x0"],"depth":1,"refund":0,"opName":"MSTORE"} +{"pc":56,"op":127,"gas":"0xaeb24","gasCost":"0x3","memSize":32,"stack":[],"depth":1,"refund":0,"opName":"PUSH32"} +{"pc":89,"op":96,"gas":"0xaeb21","gasCost":"0x3","memSize":32,"stack":["0x60045560006000600060006000606000527ff96000527f5af250600060006000"],"depth":1,"refund":0,"opName":"PUSH1"} +{"pc":91,"op":82,"gas":"0xaeb1e","gasCost":"0x6","memSize":32,"stack":["0x60045560006000600060006000606000527ff96000527f5af250600060006000","0x20"],"depth":1,"refund":0,"opName":"MSTORE"} +{"pc":92,"op":127,"gas":"0xaeb18","gasCost":"0x3","memSize":64,"stack":[],"depth":1,"refund":0,"opName":"PUSH32"} +{"pc":125,"op":96,"gas":"0xaeb15","gasCost":"0x3","memSize":64,"stack":["0x606000527e60f45af45060006000600060006020527f600060f55a6020527ff1"],"depth":1,"refund":0,"opName":"PUSH1"} +{"pc":127,"op":82,"gas":"0xaeb12","gasCost":"0x6","memSize":64,"stack":["0x606000527e60f45af45060006000600060006020527f600060f55a6020527ff1","0x40"],"depth":1,"refund":0,"opName":"MSTORE"} +{"pc":128,"op":127,"gas":"0xaeb0c","gasCost":"0x3","memSize":96,"stack":[],"depth":1,"refund":0,"opName":"PUSH32"} +{"pc":161,"op":96,"gas":"0xaeb09","gasCost":"0x3","memSize":96,"stack":["0x50f001075205846a44a283446020527f8ca2600060006040527f600060006004"],"depth":1,"refund":0,"opName":"PUSH1"} +{"pc":163,"op":82,"gas":"0xaeb06","gasCost":"0x6","memSize":96,"stack":["0x50f001075205846a44a283446020527f8ca2600060006040527f600060006004","0x60"],"depth":1,"refund":0,"opName":"MSTORE"} +{"pc":164,"op":127,"gas":"0xaeb00","gasCost":"0x3","memSize":128,"stack":[],"depth":1,"refund":0,"opName":"PUSH32"} +{"pc":197,"op":96,"gas":"0xaeafd","gasCost":"0x3","memSize":128,"stack":["0x5af4506040527f519930847f3b631c54a49b5f600354503260406060527f527f"],"depth":1,"refund":0,"opName":"PUSH1"} +{"pc":199,"op":82,"gas":"0xaeafa","gasCost":"0x6","memSize":128,"stack":["0x5af4506040527f519930847f3b631c54a49b5f600354503260406060527f527f","0x80"],"depth":1,"refund":0,"opName":"MSTORE"} +{"pc":200,"op":127,"gas":"0xaeaf4","gasCost":"0x3","memSize":160,"stack":[],"depth":1,"refund":0,"opName":"PUSH32"} +{"pc":233,"op":96,"gas":"0xaeaf1","gasCost":"0x3","memSize":160,"stack":["0x77306b60006000600060006060527f6000600c5af15060006000600060006080"],"depth":1,"refund":0,"opName":"PUSH1"} +{"pc":235,"op":82,"gas":"0xaeaee","gasCost":"0x6","memSize":160,"stack":["0x77306b60006000600060006060527f6000600c5af15060006000600060006080","0xa0"],"depth":1,"refund":0,"opName":"MSTORE"} +{"pc":236,"op":127,"gas":"0xaeae8","gasCost":"0x3","memSize":192,"stack":[],"depth":1,"refund":0,"opName":"PUSH32"} +{"pc":269,"op":96,"gas":"0xaeae5","gasCost":"0x3","memSize":192,"stack":["0x527f60f85af450506060527f066001600255606080527f035450600060005560"],"depth":1,"refund":0,"opName":"PUSH1"} +{"pc":271,"op":82,"gas":"0xaeae2","gasCost":"0x6","memSize":192,"stack":["0x527f60f85af450506060527f066001600255606080527f035450600060005560","0xc0"],"depth":1,"refund":0,"opName":"MSTORE"} +{"pc":272,"op":126,"gas":"0xaeadc","gasCost":"0x3","memSize":224,"stack":[],"depth":1,"refund":0,"opName":"PUSH31"} +{"pc":304,"op":96,"gas":"0xaead9","gasCost":"0x3","memSize":224,"stack":["0x6060a0527f01556c3394fff4607f7f1684317b6080527f387b9f60a0527f19"],"depth":1,"refund":0,"opName":"PUSH1"} +{"pc":306,"op":82,"gas":"0xaead6","gasCost":"0x6","memSize":224,"stack":["0x6060a0527f01556c3394fff4607f7f1684317b6080527f387b9f60a0527f19","0xe0"],"depth":1,"refund":0,"opName":"MSTORE"} +{"pc":307,"op":127,"gas":"0xaead0","gasCost":"0x3","memSize":256,"stack":[],"depth":1,"refund":0,"opName":"PUSH32"} +{"pc":340,"op":97,"gas":"0xaeacd","gasCost":"0x3","memSize":256,"stack":["0x20700184809d60c0527f60015450011899016e6009ff60026001556000527f9f"],"depth":1,"refund":0,"opName":"PUSH2"} +{"pc":343,"op":82,"gas":"0xaeaca","gasCost":"0x6","memSize":256,"stack":["0x20700184809d60c0527f60015450011899016e6009ff60026001556000527f9f","0x100"],"depth":1,"refund":0,"opName":"MSTORE"} +{"pc":344,"op":127,"gas":"0xaeac4","gasCost":"0x3","memSize":288,"stack":[],"depth":1,"refund":0,"opName":"PUSH32"} +{"pc":377,"op":97,"gas":"0xaeac1","gasCost":"0x3","memSize":288,"stack":["0x60a05260c0527f7f600060e0527f527f9981600160045582600eff6000600060"],"depth":1,"refund":0,"opName":"PUSH2"} +{"pc":380,"op":82,"gas":"0xaeabe","gasCost":"0x6","memSize":288,"stack":["0x60a05260c0527f7f600060e0527f527f9981600160045582600eff6000600060","0x120"],"depth":1,"refund":0,"opName":"MSTORE"} +{"pc":381,"op":126,"gas":"0xaeab8","gasCost":"0x3","memSize":320,"stack":[],"depth":1,"refund":0,"opName":"PUSH31"} +{"pc":413,"op":97,"gas":"0xaeab5","gasCost":"0x3","memSize":320,"stack":["0x600060f65af45060006060e052610100527f7f6060c0527f20527e60006000"],"depth":1,"refund":0,"opName":"PUSH2"} +{"pc":416,"op":82,"gas":"0xaeab2","gasCost":"0x6","memSize":320,"stack":["0x600060f65af45060006060e052610100527f7f6060c0527f20527e60006000","0x140"],"depth":1,"refund":0,"opName":"MSTORE"} +{"pc":417,"op":127,"gas":"0xaeaac","gasCost":"0x3","memSize":352,"stack":[],"depth":1,"refund":0,"opName":"PUSH32"} +{"pc":450,"op":97,"gas":"0xaeaa9","gasCost":"0x3","memSize":352,"stack":["0x6020527f60f75af4501d7f1903166660006000610120527f60610100527e6000"],"depth":1,"refund":0,"opName":"PUSH2"} +{"pc":453,"op":82,"gas":"0xaeaa6","gasCost":"0x6","memSize":352,"stack":["0x6020527f60f75af4501d7f1903166660006000610120527f60610100527e6000","0x160"],"depth":1,"refund":0,"opName":"MSTORE"} +{"pc":454,"op":127,"gas":"0xaeaa0","gasCost":"0x3","memSize":384,"stack":[],"depth":1,"refund":0,"opName":"PUSH32"} +{"pc":487,"op":97,"gas":"0xaea9d","gasCost":"0x3","memSize":384,"stack":["0x600060e0527f60046040527f5af150600060006000604052610140527f7f6000"],"depth":1,"refund":0,"opName":"PUSH2"} +{"pc":490,"op":82,"gas":"0xaea9a","gasCost":"0x6","memSize":384,"stack":["0x600060e0527f60046040527f5af150600060006000604052610140527f7f6000","0x180"],"depth":1,"refund":0,"opName":"MSTORE"} +{"pc":491,"op":127,"gas":"0xaea94","gasCost":"0x3","memSize":416,"stack":[],"depth":1,"refund":0,"opName":"PUSH32"} +{"pc":524,"op":97,"gas":"0xaea91","gasCost":"0x3","memSize":416,"stack":["0x6009610120527f5af4503c95138e5b8f610100527f7f605a6000536060610160"],"depth":1,"refund":0,"opName":"PUSH2"} +{"pc":527,"op":82,"gas":"0xaea8e","gasCost":"0x6","memSize":416,"stack":["0x6009610120527f5af4503c95138e5b8f610100527f7f605a6000536060610160","0x1a0"],"depth":1,"refund":0,"opName":"MSTORE"} +{"pc":528,"op":127,"gas":"0xaea88","gasCost":"0x3","memSize":448,"stack":[],"depth":1,"refund":0,"opName":"PUSH32"} +{"pc":561,"op":97,"gas":"0xaea85","gasCost":"0x3","memSize":448,"stack":["0x527f527f6031600153606b60610140527f02536010606060527f035360456061"],"depth":1,"refund":0,"opName":"PUSH2"} +{"pc":564,"op":82,"gas":"0xaea82","gasCost":"0x6","memSize":448,"stack":["0x527f527f6031600153606b60610140527f02536010606060527f035360456061","0x1c0"],"depth":1,"refund":0,"opName":"MSTORE"} +{"pc":565,"op":127,"gas":"0xaea7c","gasCost":"0x3","memSize":480,"stack":[],"depth":1,"refund":0,"opName":"PUSH32"} +{"pc":598,"op":97,"gas":"0xaea79","gasCost":"0x3","memSize":480,"stack":["0x120610180527f527f04536060600553600160608052610160527f7e527f6006"],"depth":1,"refund":0,"opName":"PUSH2"} +{"pc":601,"op":82,"gas":"0xaea76","gasCost":"0x6","memSize":480,"stack":["0x120610180527f527f04536060600553600160608052610160527f7e527f6006","0x1e0"],"depth":1,"refund":0,"opName":"MSTORE"} +{"pc":602,"op":127,"gas":"0xaea70","gasCost":"0x3","memSize":512,"stack":[],"depth":1,"refund":0,"opName":"PUSH32"} +{"pc":635,"op":97,"gas":"0xaea6d","gasCost":"0x3","memSize":512,"stack":["0x536060600753606101a0527f02600853606080610140527f527f556009536060"],"depth":1,"refund":0,"opName":"PUSH2"} +{"pc":638,"op":82,"gas":"0xaea6a","gasCost":"0x6","memSize":512,"stack":["0x536060600753606101a0527f02600853606080610140527f527f556009536060","0x200"],"depth":1,"refund":0,"opName":"MSTORE"} +{"pc":639,"op":127,"gas":"0xaea64","gasCost":"0x3","memSize":544,"stack":[],"depth":1,"refund":0,"opName":"PUSH32"} +{"pc":672,"op":97,"gas":"0xaea61","gasCost":"0x3","memSize":544,"stack":["0x610180527f600a53600160a06101c0527f527f600b536060600c6020527f5360"],"depth":1,"refund":0,"opName":"PUSH2"} +{"pc":675,"op":82,"gas":"0xaea5e","gasCost":"0x6","memSize":544,"stack":["0x610180527f600a53600160a06101c0527f527f600b536060600c6020527f5360","0x220"],"depth":1,"refund":0,"opName":"MSTORE"} +{"pc":676,"op":126,"gas":"0xaea58","gasCost":"0x3","memSize":576,"stack":[],"depth":1,"refund":0,"opName":"PUSH31"} +{"pc":708,"op":97,"gas":"0xaea55","gasCost":"0x3","memSize":576,"stack":["0x60610160527f0d53606101a0527f55606101e0527f0e60a0527f536060600f"],"depth":1,"refund":0,"opName":"PUSH2"} +{"pc":711,"op":82,"gas":"0xaea52","gasCost":"0x6","memSize":576,"stack":["0x60610160527f0d53606101a0527f55606101e0527f0e60a0527f536060600f","0x240"],"depth":1,"refund":0,"opName":"MSTORE"} +{"pc":712,"op":127,"gas":"0xaea4c","gasCost":"0x3","memSize":608,"stack":[],"depth":1,"refund":0,"opName":"PUSH32"} +{"pc":745,"op":97,"gas":"0xaea49","gasCost":"0x3","memSize":608,"stack":["0x536060c0527f01601053606060115360026101806101610200527fc0527f527f"],"depth":1,"refund":0,"opName":"PUSH2"} +{"pc":748,"op":82,"gas":"0xaea46","gasCost":"0x6","memSize":608,"stack":["0x536060c0527f01601053606060115360026101806101610200527fc0527f527f","0x260"],"depth":1,"refund":0,"opName":"MSTORE"} +{"pc":749,"op":127,"gas":"0xaea40","gasCost":"0x3","memSize":640,"stack":[],"depth":1,"refund":0,"opName":"PUSH32"} +{"pc":782,"op":97,"gas":"0xaea3d","gasCost":"0x3","memSize":640,"stack":["0x601253606040527f55601353606060c0527f60145360e0527f6000610220527f"],"depth":1,"refund":0,"opName":"PUSH2"} +{"pc":785,"op":82,"gas":"0xaea3a","gasCost":"0x6","memSize":640,"stack":["0x601253606040527f55601353606060c0527f60145360e0527f6000610220527f","0x280"],"depth":1,"refund":0,"opName":"MSTORE"} +{"pc":786,"op":127,"gas":"0xaea34","gasCost":"0x3","memSize":672,"stack":[],"depth":1,"refund":0,"opName":"PUSH32"} +{"pc":819,"op":97,"gas":"0xaea31","gasCost":"0x3","memSize":672,"stack":["0x6015536101e0527f60606101a0527f601653600060175360f360185360196060"],"depth":1,"refund":0,"opName":"PUSH2"} +{"pc":822,"op":82,"gas":"0xaea2e","gasCost":"0x6","memSize":672,"stack":["0x6015536101e0527f60606101a0527f601653600060175360f360185360196060","0x2a0"],"depth":1,"refund":0,"opName":"MSTORE"} +{"pc":823,"op":127,"gas":"0xaea28","gasCost":"0x3","memSize":704,"stack":[],"depth":1,"refund":0,"opName":"PUSH32"} +{"pc":856,"op":97,"gas":"0xaea25","gasCost":"0x3","memSize":704,"stack":["0x610240527f605260006060e052610200527f610100527f7f806101c0527f5360"],"depth":1,"refund":0,"opName":"PUSH2"} +{"pc":859,"op":82,"gas":"0xaea22","gasCost":"0x7","memSize":704,"stack":["0x610240527f605260006060e052610200527f610100527f7f806101c0527f5360","0x2c0"],"depth":1,"refund":0,"opName":"MSTORE"} +{"pc":860,"op":127,"gas":"0xaea1b","gasCost":"0x3","memSize":736,"stack":[],"depth":1,"refund":0,"opName":"PUSH32"} +{"pc":893,"op":97,"gas":"0xaea18","gasCost":"0x3","memSize":736,"stack":["0xf360815360610260527f8260006000f060006000600060610220527e845af450"],"depth":1,"refund":0,"opName":"PUSH2"} +{"pc":896,"op":82,"gas":"0xaea15","gasCost":"0x6","memSize":736,"stack":["0xf360815360610260527f8260006000f060006000600060610220527e845af450","0x2e0"],"depth":1,"refund":0,"opName":"MSTORE"} +{"pc":897,"op":127,"gas":"0xaea0f","gasCost":"0x3","memSize":768,"stack":[],"depth":1,"refund":0,"opName":"PUSH32"} +{"pc":930,"op":97,"gas":"0xaea0c","gasCost":"0x3","memSize":768,"stack":["0x506000600061016101e0610280527f527f20527f60610100527e600060006003"],"depth":1,"refund":0,"opName":"PUSH2"} +{"pc":933,"op":82,"gas":"0xaea09","gasCost":"0x6","memSize":768,"stack":["0x506000600061016101e0610280527f527f20527f60610100527e600060006003","0x300"],"depth":1,"refund":0,"opName":"MSTORE"} +{"pc":934,"op":127,"gas":"0xaea03","gasCost":"0x3","memSize":800,"stack":[],"depth":1,"refund":0,"opName":"PUSH32"} +{"pc":967,"op":97,"gas":"0xaea00","gasCost":"0x3","memSize":800,"stack":["0x610240527f5af15060005450c760006102a0527f6002551309f562610200527f"],"depth":1,"refund":0,"opName":"PUSH2"} +{"pc":970,"op":82,"gas":"0xae9fd","gasCost":"0x6","memSize":800,"stack":["0x610240527f5af15060005450c760006102a0527f6002551309f562610200527f","0x320"],"depth":1,"refund":0,"opName":"MSTORE"} +{"pc":971,"op":127,"gas":"0xae9f7","gasCost":"0x3","memSize":832,"stack":[],"depth":1,"refund":0,"opName":"PUSH32"} +{"pc":1004,"op":97,"gas":"0xae9f4","gasCost":"0x3","memSize":832,"stack":["0x66a486610140527f6b00610260527f1d457161016102c0527f20527f60005450"],"depth":1,"refund":0,"opName":"PUSH2"} +{"pc":1007,"op":82,"gas":"0xae9f1","gasCost":"0x6","memSize":832,"stack":["0x66a486610140527f6b00610260527f1d457161016102c0527f20527f60005450","0x340"],"depth":1,"refund":0,"opName":"MSTORE"} +{"pc":1008,"op":127,"gas":"0xae9eb","gasCost":"0x3","memSize":864,"stack":[],"depth":1,"refund":0,"opName":"PUSH32"} +{"pc":1041,"op":97,"gas":"0xae9e8","gasCost":"0x3","memSize":864,"stack":["0x1c641d373c7f60045450610220527f6000600155610280527f6102e0527f6005"],"depth":1,"refund":0,"opName":"PUSH2"} +{"pc":1044,"op":82,"gas":"0xae9e5","gasCost":"0x6","memSize":864,"stack":["0x1c641d373c7f60045450610220527f6000600155610280527f6102e0527f6005","0x360"],"depth":1,"refund":0,"opName":"MSTORE"} +{"pc":1045,"op":127,"gas":"0xae9df","gasCost":"0x3","memSize":896,"stack":[],"depth":1,"refund":0,"opName":"PUSH32"} +{"pc":1078,"op":97,"gas":"0xae9dc","gasCost":"0x3","memSize":896,"stack":["0x54610160527f50600160025560085450610140527f60006002610240527f6103"],"depth":1,"refund":0,"opName":"PUSH2"} +{"pc":1081,"op":82,"gas":"0xae9d9","gasCost":"0x6","memSize":896,"stack":["0x54610160527f50600160025560085450610140527f60006002610240527f6103","0x380"],"depth":1,"refund":0,"opName":"MSTORE"} +{"pc":1082,"op":126,"gas":"0xae9d3","gasCost":"0x3","memSize":928,"stack":[],"depth":1,"refund":0,"opName":"PUSH31"} +{"pc":1114,"op":97,"gas":"0xae9d0","gasCost":"0x3","memSize":928,"stack":["0x527f6102a0527f557fd86000606000527e600060610180527e600060005af1"],"depth":1,"refund":0,"opName":"PUSH2"} +{"pc":1117,"op":82,"gas":"0xae9cd","gasCost":"0x6","memSize":928,"stack":["0x527f6102a0527f557fd86000606000527e600060610180527e600060005af1","0x3a0"],"depth":1,"refund":0,"opName":"MSTORE"} +{"pc":1118,"op":127,"gas":"0xae9c7","gasCost":"0x3","memSize":960,"stack":[],"depth":1,"refund":0,"opName":"PUSH32"} +{"pc":1151,"op":97,"gas":"0xae9c4","gasCost":"0x3","memSize":960,"stack":["0x508612610320527f17145147356102c0527f610260527f610160527f5198a37e"],"depth":1,"refund":0,"opName":"PUSH2"} +{"pc":1154,"op":82,"gas":"0xae9c1","gasCost":"0x6","memSize":960,"stack":["0x508612610320527f17145147356102c0527f610260527f610160527f5198a37e","0x3c0"],"depth":1,"refund":0,"opName":"MSTORE"} +{"pc":1155,"op":127,"gas":"0xae9bb","gasCost":"0x3","memSize":992,"stack":[],"depth":1,"refund":0,"opName":"PUSH32"} +{"pc":1188,"op":97,"gas":"0xae9b8","gasCost":"0x3","memSize":992,"stack":["0x127a7efa7c600052610340527f6101a0527f606020527f6102e0527f60602053"],"depth":1,"refund":0,"opName":"PUSH2"} +{"pc":1191,"op":82,"gas":"0xae9b5","gasCost":"0x7","memSize":992,"stack":["0x127a7efa7c600052610340527f6101a0527f606020527f6102e0527f60602053","0x3e0"],"depth":1,"refund":0,"opName":"MSTORE"} +{"pc":1192,"op":127,"gas":"0xae9ae","gasCost":"0x3","memSize":1024,"stack":[],"depth":1,"refund":0,"opName":"PUSH32"} +{"pc":1225,"op":97,"gas":"0xae9ab","gasCost":"0x3","memSize":1024,"stack":["0x60610280527ff760215360ff60610360527f225360610180527fdb6023536037"],"depth":1,"refund":0,"opName":"PUSH2"} +{"pc":1228,"op":82,"gas":"0xae9a8","gasCost":"0x6","memSize":1024,"stack":["0x60610280527ff760215360ff60610360527f225360610180527fdb6023536037","0x400"],"depth":1,"refund":0,"opName":"MSTORE"} +{"pc":1229,"op":127,"gas":"0xae9a2","gasCost":"0x3","memSize":1056,"stack":[],"depth":1,"refund":0,"opName":"PUSH32"} +{"pc":1262,"op":97,"gas":"0xae99f","gasCost":"0x3","memSize":1056,"stack":["0x60610300527f6101c0527f24536075606102610380527fa0527f2553609f6060"],"depth":1,"refund":0,"opName":"PUSH2"} +{"pc":1265,"op":82,"gas":"0xae99c","gasCost":"0x6","memSize":1056,"stack":["0x60610300527f6101c0527f24536075606102610380527fa0527f2553609f6060","0x420"],"depth":1,"refund":0,"opName":"MSTORE"} +{"pc":1266,"op":127,"gas":"0xae996","gasCost":"0x3","memSize":1088,"stack":[],"depth":1,"refund":0,"opName":"PUSH32"} +{"pc":1299,"op":97,"gas":"0xae993","gasCost":"0x3","memSize":1088,"stack":["0x40527f265360fe60275360610320527f8f60286101a0526103a0527f7f536061"],"depth":1,"refund":0,"opName":"PUSH2"} +{"pc":1302,"op":82,"gas":"0xae990","gasCost":"0x6","memSize":1088,"stack":["0x40527f265360fe60275360610320527f8f60286101a0526103a0527f7f536061","0x440"],"depth":1,"refund":0,"opName":"MSTORE"} +{"pc":1303,"op":127,"gas":"0xae98a","gasCost":"0x3","memSize":1120,"stack":[],"depth":1,"refund":0,"opName":"PUSH32"} +{"pc":1336,"op":97,"gas":"0xae987","gasCost":"0x3","memSize":1120,"stack":["0x1e0527f6102c0527f0b6029536060602a53600060610340527f2b536103c052"],"depth":1,"refund":0,"opName":"PUSH2"} +{"pc":1339,"op":82,"gas":"0xae984","gasCost":"0x6","memSize":1120,"stack":["0x1e0527f6102c0527f0b6029536060602a53600060610340527f2b536103c052","0x460"],"depth":1,"refund":0,"opName":"MSTORE"} +{"pc":1340,"op":127,"gas":"0xae97e","gasCost":"0x3","memSize":1152,"stack":[],"depth":1,"refund":0,"opName":"PUSH32"} +{"pc":1373,"op":97,"gas":"0xae97b","gasCost":"0x3","memSize":1152,"stack":["0x7f6060602c53606052606060805360006061016102e0527f610200527fc05261"],"depth":1,"refund":0,"opName":"PUSH2"} +{"pc":1376,"op":82,"gas":"0xae978","gasCost":"0x6","memSize":1152,"stack":["0x7f6060602c53606052606060805360006061016102e0527f610200527fc05261","0x480"],"depth":1,"refund":0,"opName":"MSTORE"} +{"pc":1377,"op":127,"gas":"0xae972","gasCost":"0x3","memSize":1184,"stack":[],"depth":1,"refund":0,"opName":"PUSH32"} +{"pc":1410,"op":97,"gas":"0xae96f","gasCost":"0x3","memSize":1184,"stack":["0x36103e0527f60527f7f81536060608253602d60835360536084536060608553"],"depth":1,"refund":0,"opName":"PUSH2"} +{"pc":1413,"op":82,"gas":"0xae96c","gasCost":"0x6","memSize":1184,"stack":["0x36103e0527f60527f7f81536060608253602d60835360536084536060608553","0x4a0"],"depth":1,"refund":0,"opName":"MSTORE"} +{"pc":1414,"op":127,"gas":"0xae966","gasCost":"0x3","memSize":1216,"stack":[],"depth":1,"refund":0,"opName":"PUSH32"} +{"pc":1447,"op":97,"gas":"0xae963","gasCost":"0x3","memSize":1216,"stack":["0x60fd61030052610400527f7f6086610380527f536060610220527f6087536101"],"depth":1,"refund":0,"opName":"PUSH2"} +{"pc":1450,"op":82,"gas":"0xae960","gasCost":"0x6","memSize":1216,"stack":["0x60fd61030052610400527f7f6086610380527f536060610220527f6087536101","0x4c0"],"depth":1,"refund":0,"opName":"MSTORE"} +{"pc":1451,"op":127,"gas":"0xae95a","gasCost":"0x3","memSize":1248,"stack":[],"depth":1,"refund":0,"opName":"PUSH32"} +{"pc":1484,"op":97,"gas":"0xae957","gasCost":"0x3","memSize":1248,"stack":["0xe0527f602e608853605360610420527f89536060608a61036103a0527f20527f"],"depth":1,"refund":0,"opName":"PUSH2"} +{"pc":1487,"op":82,"gas":"0xae954","gasCost":"0x7","memSize":1248,"stack":["0xe0527f602e608853605360610420527f89536060608a61036103a0527f20527f","0x4e0"],"depth":1,"refund":0,"opName":"MSTORE"} +{"pc":1488,"op":127,"gas":"0xae94d","gasCost":"0x3","memSize":1280,"stack":[],"depth":1,"refund":0,"opName":"PUSH32"} +{"pc":1521,"op":97,"gas":"0xae94a","gasCost":"0x3","memSize":1280,"stack":["0x53602f608b536060608c610240527f53610440527f6000608d5360f361020052"],"depth":1,"refund":0,"opName":"PUSH2"} +{"pc":1524,"op":82,"gas":"0xae947","gasCost":"0x6","memSize":1280,"stack":["0x53602f608b536060608c610240527f53610440527f6000608d5360f361020052","0x500"],"depth":1,"refund":0,"opName":"MSTORE"} +{"pc":1525,"op":127,"gas":"0xae941","gasCost":"0x3","memSize":1312,"stack":[],"depth":1,"refund":0,"opName":"PUSH32"} +{"pc":1558,"op":97,"gas":"0xae93e","gasCost":"0x3","memSize":1312,"stack":["0x60606103c0527f610220610340527f53608e610221610460527f536053610222"],"depth":1,"refund":0,"opName":"PUSH2"} +{"pc":1561,"op":82,"gas":"0xae93b","gasCost":"0x6","memSize":1312,"stack":["0x60606103c0527f610220610340527f53608e610221610460527f536053610222","0x520"],"depth":1,"refund":0,"opName":"MSTORE"} +{"pc":1562,"op":127,"gas":"0xae935","gasCost":"0x3","memSize":1344,"stack":[],"depth":1,"refund":0,"opName":"PUSH32"} +{"pc":1595,"op":97,"gas":"0xae932","gasCost":"0x3","memSize":1344,"stack":["0x536060610260527f610223536103e0527f600061022453606061610480527f03"],"depth":1,"refund":0,"opName":"PUSH2"} +{"pc":1598,"op":82,"gas":"0xae92f","gasCost":"0x6","memSize":1344,"stack":["0x536060610260527f610223536103e0527f600061022453606061610480527f03","0x540"],"depth":1,"refund":0,"opName":"MSTORE"} +{"pc":1599,"op":127,"gas":"0xae929","gasCost":"0x3","memSize":1376,"stack":[],"depth":1,"refund":0,"opName":"PUSH32"} +{"pc":1632,"op":97,"gas":"0xae926","gasCost":"0x3","memSize":1376,"stack":["0x60527f61022553608f61022653606061022753600061610400527f0261028061"],"depth":1,"refund":0,"opName":"PUSH2"} +{"pc":1635,"op":82,"gas":"0xae923","gasCost":"0x6","memSize":1376,"stack":["0x60527f61022553608f61022653606061022753600061610400527f0261028061","0x560"],"depth":1,"refund":0,"opName":"MSTORE"} +{"pc":1636,"op":127,"gas":"0xae91d","gasCost":"0x3","memSize":1408,"stack":[],"depth":1,"refund":0,"opName":"PUSH32"} +{"pc":1669,"op":97,"gas":"0xae91a","gasCost":"0x3","memSize":1408,"stack":["0x4a0527f527f28536060610229610380527f53600061022a5360f561022b5360"],"depth":1,"refund":0,"opName":"PUSH2"} +{"pc":1672,"op":82,"gas":"0xae917","gasCost":"0x6","memSize":1408,"stack":["0x4a0527f527f28536060610229610380527f53600061022a5360f561022b5360","0x580"],"depth":1,"refund":0,"opName":"MSTORE"} +{"pc":1673,"op":127,"gas":"0xae911","gasCost":"0x3","memSize":1440,"stack":[],"depth":1,"refund":0,"opName":"PUSH32"} +{"pc":1706,"op":97,"gas":"0xae90e","gasCost":"0x3","memSize":1440,"stack":["0x610420526104c0527f7f6061022c53600061022d536102a0527f60606103a052"],"depth":1,"refund":0,"opName":"PUSH2"} +{"pc":1709,"op":82,"gas":"0xae90b","gasCost":"0x7","memSize":1440,"stack":["0x610420526104c0527f7f6061022c53600061022d536102a0527f60606103a052","0x5a0"],"depth":1,"refund":0,"opName":"MSTORE"} +{"pc":1710,"op":127,"gas":"0xae904","gasCost":"0x3","memSize":1472,"stack":[],"depth":1,"refund":0,"opName":"PUSH32"} +{"pc":1743,"op":97,"gas":"0xae901","gasCost":"0x3","memSize":1472,"stack":["0x7f61022e53600061026104e0527f2f610440527f536060610230536000610231"],"depth":1,"refund":0,"opName":"PUSH2"} +{"pc":1746,"op":82,"gas":"0xae8fe","gasCost":"0x6","memSize":1472,"stack":["0x7f61022e53600061026104e0527f2f610440527f536060610230536000610231","0x5c0"],"depth":1,"refund":0,"opName":"MSTORE"} +{"pc":1747,"op":127,"gas":"0xae8f8","gasCost":"0x3","memSize":1504,"stack":[],"depth":1,"refund":0,"opName":"PUSH32"} +{"pc":1780,"op":97,"gas":"0xae8f5","gasCost":"0x3","memSize":1504,"stack":["0x53606061023253600061026103c0610500527f527fc0527f61610460527f0233"],"depth":1,"refund":0,"opName":"PUSH2"} +{"pc":1783,"op":82,"gas":"0xae8f2","gasCost":"0x6","memSize":1504,"stack":["0x53606061023253600061026103c0610500527f527fc0527f61610460527f0233","0x5e0"],"depth":1,"refund":0,"opName":"MSTORE"} +{"pc":1784,"op":127,"gas":"0xae8ec","gasCost":"0x3","memSize":1536,"stack":[],"depth":1,"refund":0,"opName":"PUSH32"} +{"pc":1817,"op":97,"gas":"0xae8e9","gasCost":"0x3","memSize":1536,"stack":["0x53606061023453600061023553608561023653610520527f605a610237536061"],"depth":1,"refund":0,"opName":"PUSH2"} +{"pc":1820,"op":82,"gas":"0xae8e6","gasCost":"0x6","memSize":1536,"stack":["0x53606061023453600061023553608561023653610520527f605a610237536061","0x600"],"depth":1,"refund":0,"opName":"MSTORE"} +{"pc":1821,"op":127,"gas":"0xae8e0","gasCost":"0x3","memSize":1568,"stack":[],"depth":1,"refund":0,"opName":"PUSH32"} +{"pc":1854,"op":97,"gas":"0xae8dd","gasCost":"0x3","memSize":1568,"stack":["0x3e052610480527f7ff261026102e0526038610300536053610540527f610301"],"depth":1,"refund":0,"opName":"PUSH2"} +{"pc":1857,"op":82,"gas":"0xae8da","gasCost":"0x6","memSize":1568,"stack":["0x3e052610480527f7ff261026102e0526038610300536053610540527f610301","0x620"],"depth":1,"refund":0,"opName":"MSTORE"} +{"pc":1858,"op":127,"gas":"0xae8d4","gasCost":"0x3","memSize":1600,"stack":[],"depth":1,"refund":0,"opName":"PUSH32"} +{"pc":1891,"op":97,"gas":"0xae8d1","gasCost":"0x3","memSize":1600,"stack":["0x536060610302536050610303536104a0527f60610400527f6161030453610560"],"depth":1,"refund":0,"opName":"PUSH2"} +{"pc":1894,"op":82,"gas":"0xae8ce","gasCost":"0x7","memSize":1600,"stack":["0x536060610302536050610303536104a0527f60610400527f6161030453610560","0x640"],"depth":1,"refund":0,"opName":"MSTORE"} +{"pc":1895,"op":127,"gas":"0xae8c7","gasCost":"0x3","memSize":1632,"stack":[],"depth":1,"refund":0,"opName":"PUSH32"} +{"pc":1928,"op":97,"gas":"0xae8c4","gasCost":"0x3","memSize":1632,"stack":["0x527f6002610305536039610306536053610307536060616104c0527f03085360"],"depth":1,"refund":0,"opName":"PUSH2"} +{"pc":1931,"op":82,"gas":"0xae8c1","gasCost":"0x6","memSize":1632,"stack":["0x527f6002610305536039610306536053610307536060616104c0527f03085360","0x660"],"depth":1,"refund":0,"opName":"MSTORE"} +{"pc":1932,"op":127,"gas":"0xae8bb","gasCost":"0x3","memSize":1664,"stack":[],"depth":1,"refund":0,"opName":"PUSH32"} +{"pc":1965,"op":97,"gas":"0xae8b8","gasCost":"0x3","memSize":1664,"stack":["0x5061610580527f610420527f030953606161030a53600261030b53603a61030c"],"depth":1,"refund":0,"opName":"PUSH2"} +{"pc":1968,"op":82,"gas":"0xae8b5","gasCost":"0x6","memSize":1664,"stack":["0x5061610580527f610420527f030953606161030a53600261030b53603a61030c","0x680"],"depth":1,"refund":0,"opName":"MSTORE"} +{"pc":1969,"op":127,"gas":"0xae8af","gasCost":"0x3","memSize":1696,"stack":[],"depth":1,"refund":0,"opName":"PUSH32"} +{"pc":2002,"op":97,"gas":"0xae8ac","gasCost":"0x3","memSize":1696,"stack":["0x536104e0527f606105a0527f5361030d53606161030e610440527f5360026103"],"depth":1,"refund":0,"opName":"PUSH2"} +{"pc":2005,"op":82,"gas":"0xae8a9","gasCost":"0x6","memSize":1696,"stack":["0x536104e0527f606105a0527f5361030d53606161030e610440527f5360026103","0x6a0"],"depth":1,"refund":0,"opName":"MSTORE"} +{"pc":2006,"op":127,"gas":"0xae8a3","gasCost":"0x3","memSize":1728,"stack":[],"depth":1,"refund":0,"opName":"PUSH32"} +{"pc":2039,"op":97,"gas":"0xae8a0","gasCost":"0x3","memSize":1728,"stack":["0xf53603b61031053606061616105c0527f0500527f03115360006103125360f3"],"depth":1,"refund":0,"opName":"PUSH2"} +{"pc":2042,"op":82,"gas":"0xae89d","gasCost":"0x6","memSize":1728,"stack":["0xf53603b61031053606061616105c0527f0500527f03115360006103125360f3","0x6c0"],"depth":1,"refund":0,"opName":"MSTORE"} +{"pc":2043,"op":127,"gas":"0xae897","gasCost":"0x3","memSize":1760,"stack":[],"depth":1,"refund":0,"opName":"PUSH32"} +{"pc":2076,"op":97,"gas":"0xae894","gasCost":"0x3","memSize":1760,"stack":["0x61031353616104605260036104805360146105e0527f61048153610520527f60"],"depth":1,"refund":0,"opName":"PUSH2"} +{"pc":2079,"op":82,"gas":"0xae891","gasCost":"0x7","memSize":1760,"stack":["0x61031353616104605260036104805360146105e0527f61048153610520527f60","0x6e0"],"depth":1,"refund":0,"opName":"MSTORE"} +{"pc":2080,"op":127,"gas":"0xae88a","gasCost":"0x3","memSize":1792,"stack":[],"depth":1,"refund":0,"opName":"PUSH32"} +{"pc":2113,"op":97,"gas":"0xae887","gasCost":"0x3","memSize":1792,"stack":["0x60610482536000610483536060610484536000610485610600527f5360f06104"],"depth":1,"refund":0,"opName":"PUSH2"} +{"pc":2116,"op":82,"gas":"0xae884","gasCost":"0x6","memSize":1792,"stack":["0x60610482536000610483536060610484536000610485610600527f5360f06104","0x700"],"depth":1,"refund":0,"opName":"MSTORE"} +{"pc":2117,"op":127,"gas":"0xae87e","gasCost":"0x3","memSize":1824,"stack":[],"depth":1,"refund":0,"opName":"PUSH32"} +{"pc":2150,"op":97,"gas":"0xae87b","gasCost":"0x3","memSize":1824,"stack":["0x86536060610540527f610487536000610488536060610489536000610620527f"],"depth":1,"refund":0,"opName":"PUSH2"} +{"pc":2153,"op":82,"gas":"0xae878","gasCost":"0x6","memSize":1824,"stack":["0x86536060610540527f610487536000610488536060610489536000610620527f","0x720"],"depth":1,"refund":0,"opName":"MSTORE"} +{"pc":2154,"op":127,"gas":"0xae872","gasCost":"0x3","memSize":1856,"stack":[],"depth":1,"refund":0,"opName":"PUSH32"} +{"pc":2187,"op":97,"gas":"0xae86f","gasCost":"0x3","memSize":1856,"stack":["0x61048a53606061048b5360006104610560527f8c53606061048d53600061048e"],"depth":1,"refund":0,"opName":"PUSH2"} +{"pc":2190,"op":82,"gas":"0xae86c","gasCost":"0x6","memSize":1856,"stack":["0x61048a53606061048b5360006104610560527f8c53606061048d53600061048e","0x740"],"depth":1,"refund":0,"opName":"MSTORE"} +{"pc":2191,"op":127,"gas":"0xae866","gasCost":"0x3","memSize":1888,"stack":[],"depth":1,"refund":0,"opName":"PUSH32"} +{"pc":2224,"op":97,"gas":"0xae863","gasCost":"0x3","memSize":1888,"stack":["0x610640527f53608461048f53605a6104905360f4610491536105805260606105"],"depth":1,"refund":0,"opName":"PUSH2"} +{"pc":2227,"op":82,"gas":"0xae860","gasCost":"0x7","memSize":1888,"stack":["0x610640527f53608461048f53605a6104905360f4610491536105805260606105","0x760"],"depth":1,"refund":0,"opName":"MSTORE"} +{"pc":2228,"op":127,"gas":"0xae859","gasCost":"0x3","memSize":1920,"stack":[],"depth":1,"refund":0,"opName":"PUSH32"} +{"pc":2261,"op":97,"gas":"0xae856","gasCost":"0x3","memSize":1920,"stack":["0xa053605061610660527f05a15360616105a25360046105a35360926105a45360"],"depth":1,"refund":0,"opName":"PUSH2"} +{"pc":2264,"op":82,"gas":"0xae853","gasCost":"0x6","memSize":1920,"stack":["0xa053605061610660527f05a15360616105a25360046105a35360926105a45360","0x780"],"depth":1,"refund":0,"opName":"MSTORE"} +{"pc":2265,"op":127,"gas":"0xae84d","gasCost":"0x3","memSize":1952,"stack":[],"depth":1,"refund":0,"opName":"PUSH32"} +{"pc":2298,"op":97,"gas":"0xae84a","gasCost":"0x3","memSize":1952,"stack":["0x536105a55360606105a6610680527f5360506105a75360616105a85360046105"],"depth":1,"refund":0,"opName":"PUSH2"} +{"pc":2301,"op":82,"gas":"0xae847","gasCost":"0x6","memSize":1952,"stack":["0x536105a55360606105a6610680527f5360506105a75360616105a85360046105","0x7a0"],"depth":1,"refund":0,"opName":"MSTORE"} +{"pc":2302,"op":127,"gas":"0xae841","gasCost":"0x3","memSize":1984,"stack":[],"depth":1,"refund":0,"opName":"PUSH32"} +{"pc":2335,"op":97,"gas":"0xae83e","gasCost":"0x3","memSize":1984,"stack":["0xa95360936105aa5360536105ab53606106a0527f616105ac5360046105ad5360"],"depth":1,"refund":0,"opName":"PUSH2"} +{"pc":2338,"op":82,"gas":"0xae83b","gasCost":"0x6","memSize":1984,"stack":["0xa95360936105aa5360536105ab53606106a0527f616105ac5360046105ad5360","0x7c0"],"depth":1,"refund":0,"opName":"MSTORE"} +{"pc":2339,"op":127,"gas":"0xae835","gasCost":"0x3","memSize":2016,"stack":[],"depth":1,"refund":0,"opName":"PUSH32"} +{"pc":2372,"op":97,"gas":"0xae832","gasCost":"0x3","memSize":2016,"stack":["0x946105ae5360606105af5360006105b05360f3616106c05260056106e05360b1"],"depth":1,"refund":0,"opName":"PUSH2"} +{"pc":2375,"op":82,"gas":"0xae82f","gasCost":"0x7","memSize":2016,"stack":["0x946105ae5360606105af5360006105b05360f3616106c05260056106e05360b1","0x7e0"],"depth":1,"refund":0,"opName":"MSTORE"} +{"pc":2376,"op":127,"gas":"0xae828","gasCost":"0x3","memSize":2048,"stack":[],"depth":1,"refund":0,"opName":"PUSH32"} +{"pc":2409,"op":97,"gas":"0xae825","gasCost":"0x3","memSize":2048,"stack":["0x6106e15360536106e25360606106e35360006106e45360616106e55360056106"],"depth":1,"refund":0,"opName":"PUSH2"} +{"pc":2412,"op":82,"gas":"0xae822","gasCost":"0x6","memSize":2048,"stack":["0x6106e15360536106e25360606106e35360006106e45360616106e55360056106","0x800"],"depth":1,"refund":0,"opName":"MSTORE"} +{"pc":2413,"op":127,"gas":"0xae81c","gasCost":"0x3","memSize":2080,"stack":[],"depth":1,"refund":0,"opName":"PUSH32"} +{"pc":2446,"op":97,"gas":"0xae819","gasCost":"0x3","memSize":2080,"stack":["0xe65360b26106e75360606106e85360006106e95360606106ea5360006106eb53"],"depth":1,"refund":0,"opName":"PUSH2"} +{"pc":2449,"op":82,"gas":"0xae816","gasCost":"0x6","memSize":2080,"stack":["0xe65360b26106e75360606106e85360006106e95360606106ea5360006106eb53","0x820"],"depth":1,"refund":0,"opName":"MSTORE"} +{"pc":2450,"op":127,"gas":"0xae810","gasCost":"0x3","memSize":2112,"stack":[],"depth":1,"refund":0,"opName":"PUSH32"} +{"pc":2483,"op":97,"gas":"0xae80d","gasCost":"0x3","memSize":2112,"stack":["0x60f56106ec5360606106ed5360006106ee5360606106ef5360006106f0536060"],"depth":1,"refund":0,"opName":"PUSH2"} +{"pc":2486,"op":82,"gas":"0xae80a","gasCost":"0x6","memSize":2112,"stack":["0x60f56106ec5360606106ed5360006106ee5360606106ef5360006106f0536060","0x840"],"depth":1,"refund":0,"opName":"MSTORE"} +{"pc":2487,"op":127,"gas":"0xae804","gasCost":"0x3","memSize":2144,"stack":[],"depth":1,"refund":0,"opName":"PUSH32"} +{"pc":2520,"op":97,"gas":"0xae801","gasCost":"0x3","memSize":2144,"stack":["0x6106f15360006106f25360606106f35360006106f45360606106f55360006106"],"depth":1,"refund":0,"opName":"PUSH2"} +{"pc":2523,"op":82,"gas":"0xae7fe","gasCost":"0x7","memSize":2144,"stack":["0x6106f15360006106f25360606106f35360006106f45360606106f55360006106","0x860"],"depth":1,"refund":0,"opName":"MSTORE"} +{"pc":2524,"op":127,"gas":"0xae7f7","gasCost":"0x3","memSize":2176,"stack":[],"depth":1,"refund":0,"opName":"PUSH32"} +{"pc":2557,"op":97,"gas":"0xae7f4","gasCost":"0x3","memSize":2176,"stack":["0xf65360856106f753605a6106f85360f16106f95360506106fa5360506106fb53"],"depth":1,"refund":0,"opName":"PUSH2"} +{"pc":2560,"op":82,"gas":"0xae7f1","gasCost":"0x6","memSize":2176,"stack":["0xf65360856106f753605a6106f85360f16106f95360506106fa5360506106fb53","0x880"],"depth":1,"refund":0,"opName":"MSTORE"} +{"pc":2561,"op":96,"gas":"0xae7eb","gasCost":"0x3","memSize":2208,"stack":[],"depth":1,"refund":0,"opName":"PUSH1"} +{"pc":2563,"op":97,"gas":"0xae7e8","gasCost":"0x3","memSize":2208,"stack":["0x61"],"depth":1,"refund":0,"opName":"PUSH2"} +{"pc":2566,"op":83,"gas":"0xae7e5","gasCost":"0x6","memSize":2208,"stack":["0x61","0x8a0"],"depth":1,"refund":0,"opName":"MSTORE8"} +{"pc":2567,"op":96,"gas":"0xae7df","gasCost":"0x3","memSize":2240,"stack":[],"depth":1,"refund":0,"opName":"PUSH1"} +{"pc":2569,"op":97,"gas":"0xae7dc","gasCost":"0x3","memSize":2240,"stack":["0x6"],"depth":1,"refund":0,"opName":"PUSH2"} +{"pc":2572,"op":83,"gas":"0xae7d9","gasCost":"0x3","memSize":2240,"stack":["0x6","0x8a1"],"depth":1,"refund":0,"opName":"MSTORE8"} +{"pc":2573,"op":96,"gas":"0xae7d6","gasCost":"0x3","memSize":2240,"stack":[],"depth":1,"refund":0,"opName":"PUSH1"} +{"pc":2575,"op":97,"gas":"0xae7d3","gasCost":"0x3","memSize":2240,"stack":["0xfc"],"depth":1,"refund":0,"opName":"PUSH2"} +{"pc":2578,"op":83,"gas":"0xae7d0","gasCost":"0x3","memSize":2240,"stack":["0xfc","0x8a2"],"depth":1,"refund":0,"opName":"MSTORE8"} +{"pc":2579,"op":96,"gas":"0xae7cd","gasCost":"0x3","memSize":2240,"stack":[],"depth":1,"refund":0,"opName":"PUSH1"} +{"pc":2581,"op":97,"gas":"0xae7ca","gasCost":"0x3","memSize":2240,"stack":["0x60"],"depth":1,"refund":0,"opName":"PUSH2"} +{"pc":2584,"op":83,"gas":"0xae7c7","gasCost":"0x3","memSize":2240,"stack":["0x60","0x8a3"],"depth":1,"refund":0,"opName":"MSTORE8"} +{"pc":2585,"op":96,"gas":"0xae7c4","gasCost":"0x3","memSize":2240,"stack":[],"depth":1,"refund":0,"opName":"PUSH1"} +{"pc":2587,"op":97,"gas":"0xae7c1","gasCost":"0x3","memSize":2240,"stack":["0x0"],"depth":1,"refund":0,"opName":"PUSH2"} +{"pc":2590,"op":83,"gas":"0xae7be","gasCost":"0x3","memSize":2240,"stack":["0x0","0x8a4"],"depth":1,"refund":0,"opName":"MSTORE8"} +{"pc":2591,"op":96,"gas":"0xae7bb","gasCost":"0x3","memSize":2240,"stack":[],"depth":1,"refund":0,"opName":"PUSH1"} +{"pc":2593,"op":97,"gas":"0xae7b8","gasCost":"0x3","memSize":2240,"stack":["0xf3"],"depth":1,"refund":0,"opName":"PUSH2"} +{"pc":2596,"op":83,"gas":"0xae7b5","gasCost":"0x3","memSize":2240,"stack":["0xf3","0x8a5"],"depth":1,"refund":0,"opName":"MSTORE8"} +{"pc":2597,"op":97,"gas":"0xae7b2","gasCost":"0x3","memSize":2240,"stack":[],"depth":1,"refund":0,"opName":"PUSH2"} +{"pc":2600,"op":96,"gas":"0xae7af","gasCost":"0x3","memSize":2240,"stack":["0x8a6"],"depth":1,"refund":0,"opName":"PUSH1"} +{"pc":2602,"op":96,"gas":"0xae7ac","gasCost":"0x3","memSize":2240,"stack":["0x8a6","0x0"],"depth":1,"refund":0,"opName":"PUSH1"} +{"pc":2604,"op":240,"gas":"0xae7a9","gasCost":"0x7d00","memSize":2240,"stack":["0x8a6","0x0","0x0"],"depth":1,"refund":0,"opName":"CREATE"} +{"pc":0,"op":96,"gas":"0xa40ff","gasCost":"0x3","memSize":0,"stack":[],"depth":2,"refund":0,"opName":"PUSH1"} +{"pc":2,"op":84,"gas":"0xa40fc","gasCost":"0x834","memSize":0,"stack":["0x2"],"depth":2,"refund":0,"opName":"SLOAD"} +{"pc":3,"op":80,"gas":"0xa38c8","gasCost":"0x2","memSize":0,"stack":["0x0"],"depth":2,"refund":0,"opName":"POP"} +{"pc":4,"op":96,"gas":"0xa38c6","gasCost":"0x3","memSize":0,"stack":[],"depth":2,"refund":0,"opName":"PUSH1"} +{"pc":6,"op":84,"gas":"0xa38c3","gasCost":"0x834","memSize":0,"stack":["0x3"],"depth":2,"refund":0,"opName":"SLOAD"} +{"pc":7,"op":80,"gas":"0xa308f","gasCost":"0x2","memSize":0,"stack":["0x0"],"depth":2,"refund":0,"opName":"POP"} +{"pc":8,"op":96,"gas":"0xa308d","gasCost":"0x3","memSize":0,"stack":[],"depth":2,"refund":0,"opName":"PUSH1"} +{"pc":10,"op":96,"gas":"0xa308a","gasCost":"0x3","memSize":0,"stack":["0x1"],"depth":2,"refund":0,"opName":"PUSH1"} +{"pc":12,"op":85,"gas":"0xa3087","gasCost":"0x4e20","memSize":0,"stack":["0x1","0x3"],"depth":2,"refund":0,"opName":"SSTORE"} +{"pc":13,"op":127,"gas":"0x9e267","gasCost":"0x3","memSize":0,"stack":[],"depth":2,"refund":0,"opName":"PUSH32"} +{"pc":46,"op":96,"gas":"0x9e264","gasCost":"0x3","memSize":0,"stack":["0x7f6008545060006004557f600160045560006004556000600060006000600060"],"depth":2,"refund":0,"opName":"PUSH1"} +{"pc":48,"op":82,"gas":"0x9e261","gasCost":"0x6","memSize":0,"stack":["0x7f6008545060006004557f600160045560006004556000600060006000600060","0x0"],"depth":2,"refund":0,"opName":"MSTORE"} +{"pc":49,"op":127,"gas":"0x9e25b","gasCost":"0x3","memSize":32,"stack":[],"depth":2,"refund":0,"opName":"PUSH32"} +{"pc":82,"op":96,"gas":"0x9e258","gasCost":"0x3","memSize":32,"stack":["0xf96000527f5af250600060006000606000527e60f45af4506000600060006000"],"depth":2,"refund":0,"opName":"PUSH1"} +{"pc":84,"op":82,"gas":"0x9e255","gasCost":"0x6","memSize":32,"stack":["0xf96000527f5af250600060006000606000527e60f45af4506000600060006000","0x20"],"depth":2,"refund":0,"opName":"MSTORE"} +{"pc":85,"op":127,"gas":"0x9e24f","gasCost":"0x3","memSize":64,"stack":[],"depth":2,"refund":0,"opName":"PUSH32"} +{"pc":118,"op":96,"gas":"0x9e24c","gasCost":"0x3","memSize":64,"stack":["0x600060f55a6020527ff150f001075205846a44a283446020527f8ca260006000"],"depth":2,"refund":0,"opName":"PUSH1"} +{"pc":120,"op":82,"gas":"0x9e249","gasCost":"0x6","memSize":64,"stack":["0x600060f55a6020527ff150f001075205846a44a283446020527f8ca260006000","0x40"],"depth":2,"refund":0,"opName":"MSTORE"} +{"pc":121,"op":127,"gas":"0x9e243","gasCost":"0x3","memSize":96,"stack":[],"depth":2,"refund":0,"opName":"PUSH32"} +{"pc":154,"op":96,"gas":"0x9e240","gasCost":"0x3","memSize":96,"stack":["0x6000600060045af4506040527f519930847f3b631c54a49b5f60035450326040"],"depth":2,"refund":0,"opName":"PUSH1"} +{"pc":156,"op":82,"gas":"0x9e23d","gasCost":"0x6","memSize":96,"stack":["0x6000600060045af4506040527f519930847f3b631c54a49b5f60035450326040","0x60"],"depth":2,"refund":0,"opName":"MSTORE"} +{"pc":157,"op":127,"gas":"0x9e237","gasCost":"0x3","memSize":128,"stack":[],"depth":2,"refund":0,"opName":"PUSH32"} +{"pc":190,"op":96,"gas":"0x9e234","gasCost":"0x3","memSize":128,"stack":["0x527f77306b60006000600060006060527f6000600c5af1506000600060006000"],"depth":2,"refund":0,"opName":"PUSH1"} +{"pc":192,"op":82,"gas":"0x9e231","gasCost":"0x6","memSize":128,"stack":["0x527f77306b60006000600060006060527f6000600c5af1506000600060006000","0x80"],"depth":2,"refund":0,"opName":"MSTORE"} +{"pc":193,"op":127,"gas":"0x9e22b","gasCost":"0x3","memSize":160,"stack":[],"depth":2,"refund":0,"opName":"PUSH32"} +{"pc":226,"op":96,"gas":"0x9e228","gasCost":"0x3","memSize":160,"stack":["0x60f85af450506060527f066001600255606080527f0354506000600055600060"],"depth":2,"refund":0,"opName":"PUSH1"} +{"pc":228,"op":82,"gas":"0x9e225","gasCost":"0x6","memSize":160,"stack":["0x60f85af450506060527f066001600255606080527f0354506000600055600060","0xa0"],"depth":2,"refund":0,"opName":"MSTORE"} +{"pc":229,"op":127,"gas":"0x9e21f","gasCost":"0x3","memSize":192,"stack":[],"depth":2,"refund":0,"opName":"PUSH32"} +{"pc":262,"op":96,"gas":"0x9e21c","gasCost":"0x3","memSize":192,"stack":["0x1556c3394fff4607f7f1684317b6080527f387b9f60a0527f1920700184809d"],"depth":2,"refund":0,"opName":"PUSH1"} +{"pc":264,"op":82,"gas":"0x9e219","gasCost":"0x6","memSize":192,"stack":["0x1556c3394fff4607f7f1684317b6080527f387b9f60a0527f1920700184809d","0xc0"],"depth":2,"refund":0,"opName":"MSTORE"} +{"pc":265,"op":127,"gas":"0x9e213","gasCost":"0x3","memSize":224,"stack":[],"depth":2,"refund":0,"opName":"PUSH32"} +{"pc":298,"op":96,"gas":"0x9e210","gasCost":"0x3","memSize":224,"stack":["0x60015450011899016e6009ff60026001556000527f9f60a05260c0527f7f6000"],"depth":2,"refund":0,"opName":"PUSH1"} +{"pc":300,"op":82,"gas":"0x9e20d","gasCost":"0x6","memSize":224,"stack":["0x60015450011899016e6009ff60026001556000527f9f60a05260c0527f7f6000","0xe0"],"depth":2,"refund":0,"opName":"MSTORE"} +{"pc":301,"op":127,"gas":"0x9e207","gasCost":"0x3","memSize":256,"stack":[],"depth":2,"refund":0,"opName":"PUSH32"} +{"pc":334,"op":97,"gas":"0x9e204","gasCost":"0x3","memSize":256,"stack":["0x527f9981600160045582600eff600060006000600060f65af45060006060e052"],"depth":2,"refund":0,"opName":"PUSH2"} +{"pc":337,"op":82,"gas":"0x9e201","gasCost":"0x6","memSize":256,"stack":["0x527f9981600160045582600eff600060006000600060f65af45060006060e052","0x100"],"depth":2,"refund":0,"opName":"MSTORE"} +{"pc":338,"op":127,"gas":"0x9e1fb","gasCost":"0x3","memSize":288,"stack":[],"depth":2,"refund":0,"opName":"PUSH32"} +{"pc":371,"op":97,"gas":"0x9e1f8","gasCost":"0x3","memSize":288,"stack":["0x7f6060c0527f20527e600060006020527f60f75af4501d7f1903166660006000"],"depth":2,"refund":0,"opName":"PUSH2"} +{"pc":374,"op":82,"gas":"0x9e1f5","gasCost":"0x6","memSize":288,"stack":["0x7f6060c0527f20527e600060006020527f60f75af4501d7f1903166660006000","0x120"],"depth":2,"refund":0,"opName":"MSTORE"} +{"pc":375,"op":127,"gas":"0x9e1ef","gasCost":"0x3","memSize":320,"stack":[],"depth":2,"refund":0,"opName":"PUSH32"} +{"pc":408,"op":97,"gas":"0x9e1ec","gasCost":"0x3","memSize":320,"stack":["0x60610100527e6000600060e0527f60046040527f5af150600060006000604052"],"depth":2,"refund":0,"opName":"PUSH2"} +{"pc":411,"op":82,"gas":"0x9e1e9","gasCost":"0x6","memSize":320,"stack":["0x60610100527e6000600060e0527f60046040527f5af150600060006000604052","0x140"],"depth":2,"refund":0,"opName":"MSTORE"} +{"pc":412,"op":127,"gas":"0x9e1e3","gasCost":"0x3","memSize":352,"stack":[],"depth":2,"refund":0,"opName":"PUSH32"} +{"pc":445,"op":97,"gas":"0x9e1e0","gasCost":"0x3","memSize":352,"stack":["0x7f60006009610120527f5af4503c95138e5b8f610100527f7f605a6000536060"],"depth":2,"refund":0,"opName":"PUSH2"} +{"pc":448,"op":82,"gas":"0x9e1dd","gasCost":"0x6","memSize":352,"stack":["0x7f60006009610120527f5af4503c95138e5b8f610100527f7f605a6000536060","0x160"],"depth":2,"refund":0,"opName":"MSTORE"} +{"pc":449,"op":127,"gas":"0x9e1d7","gasCost":"0x3","memSize":384,"stack":[],"depth":2,"refund":0,"opName":"PUSH32"} +{"pc":482,"op":97,"gas":"0x9e1d4","gasCost":"0x3","memSize":384,"stack":["0x527f6031600153606b60610140527f02536010606060527f0353604560610120"],"depth":2,"refund":0,"opName":"PUSH2"} +{"pc":485,"op":82,"gas":"0x9e1d1","gasCost":"0x6","memSize":384,"stack":["0x527f6031600153606b60610140527f02536010606060527f0353604560610120","0x180"],"depth":2,"refund":0,"opName":"MSTORE"} +{"pc":486,"op":127,"gas":"0x9e1cb","gasCost":"0x3","memSize":416,"stack":[],"depth":2,"refund":0,"opName":"PUSH32"} +{"pc":519,"op":97,"gas":"0x9e1c8","gasCost":"0x3","memSize":416,"stack":["0x527f04536060600553600160608052610160527f7e527f600653606060075360"],"depth":2,"refund":0,"opName":"PUSH2"} +{"pc":522,"op":82,"gas":"0x9e1c5","gasCost":"0x6","memSize":416,"stack":["0x527f04536060600553600160608052610160527f7e527f600653606060075360","0x1a0"],"depth":2,"refund":0,"opName":"MSTORE"} +{"pc":523,"op":127,"gas":"0x9e1bf","gasCost":"0x3","memSize":448,"stack":[],"depth":2,"refund":0,"opName":"PUSH32"} +{"pc":556,"op":97,"gas":"0x9e1bc","gasCost":"0x3","memSize":448,"stack":["0x2600853606080610140527f527f556009536060610180527f600a53600160a0"],"depth":2,"refund":0,"opName":"PUSH2"} +{"pc":559,"op":82,"gas":"0x9e1b9","gasCost":"0x6","memSize":448,"stack":["0x2600853606080610140527f527f556009536060610180527f600a53600160a0","0x1c0"],"depth":2,"refund":0,"opName":"MSTORE"} +{"pc":560,"op":127,"gas":"0x9e1b3","gasCost":"0x3","memSize":480,"stack":[],"depth":2,"refund":0,"opName":"PUSH32"} +{"pc":593,"op":97,"gas":"0x9e1b0","gasCost":"0x3","memSize":480,"stack":["0x527f600b536060600c6020527f53600060610160527f0d53606101a0527f5560"],"depth":2,"refund":0,"opName":"PUSH2"} +{"pc":596,"op":82,"gas":"0x9e1ad","gasCost":"0x6","memSize":480,"stack":["0x527f600b536060600c6020527f53600060610160527f0d53606101a0527f5560","0x1e0"],"depth":2,"refund":0,"opName":"MSTORE"} +{"pc":597,"op":127,"gas":"0x9e1a7","gasCost":"0x3","memSize":512,"stack":[],"depth":2,"refund":0,"opName":"PUSH32"} +{"pc":630,"op":97,"gas":"0x9e1a4","gasCost":"0x3","memSize":512,"stack":["0xe60a0527f536060600f536060c0527f01601053606060115360026101806101"],"depth":2,"refund":0,"opName":"PUSH2"} +{"pc":633,"op":82,"gas":"0x9e1a1","gasCost":"0x6","memSize":512,"stack":["0xe60a0527f536060600f536060c0527f01601053606060115360026101806101","0x200"],"depth":2,"refund":0,"opName":"MSTORE"} +{"pc":634,"op":127,"gas":"0x9e19b","gasCost":"0x3","memSize":544,"stack":[],"depth":2,"refund":0,"opName":"PUSH32"} +{"pc":667,"op":97,"gas":"0x9e198","gasCost":"0x3","memSize":544,"stack":["0xc0527f527f601253606040527f55601353606060c0527f60145360e0527f6000"],"depth":2,"refund":0,"opName":"PUSH2"} +{"pc":670,"op":82,"gas":"0x9e195","gasCost":"0x6","memSize":544,"stack":["0xc0527f527f601253606040527f55601353606060c0527f60145360e0527f6000","0x220"],"depth":2,"refund":0,"opName":"MSTORE"} +{"pc":671,"op":127,"gas":"0x9e18f","gasCost":"0x3","memSize":576,"stack":[],"depth":2,"refund":0,"opName":"PUSH32"} +{"pc":704,"op":97,"gas":"0x9e18c","gasCost":"0x3","memSize":576,"stack":["0x6015536101e0527f60606101a0527f601653600060175360f360185360196060"],"depth":2,"refund":0,"opName":"PUSH2"} +{"pc":707,"op":82,"gas":"0x9e189","gasCost":"0x6","memSize":576,"stack":["0x6015536101e0527f60606101a0527f601653600060175360f360185360196060","0x240"],"depth":2,"refund":0,"opName":"MSTORE"} +{"pc":708,"op":127,"gas":"0x9e183","gasCost":"0x3","memSize":608,"stack":[],"depth":2,"refund":0,"opName":"PUSH32"} +{"pc":741,"op":97,"gas":"0x9e180","gasCost":"0x3","memSize":608,"stack":["0x605260006060e052610200527f610100527f7f806101c0527f5360f360815360"],"depth":2,"refund":0,"opName":"PUSH2"} +{"pc":744,"op":82,"gas":"0x9e17d","gasCost":"0x6","memSize":608,"stack":["0x605260006060e052610200527f610100527f7f806101c0527f5360f360815360","0x260"],"depth":2,"refund":0,"opName":"MSTORE"} +{"pc":745,"op":127,"gas":"0x9e177","gasCost":"0x3","memSize":640,"stack":[],"depth":2,"refund":0,"opName":"PUSH32"} +{"pc":778,"op":97,"gas":"0x9e174","gasCost":"0x3","memSize":640,"stack":["0x8260006000f060006000600060610220527e845af450506000600061016101e0"],"depth":2,"refund":0,"opName":"PUSH2"} +{"pc":781,"op":82,"gas":"0x9e171","gasCost":"0x6","memSize":640,"stack":["0x8260006000f060006000600060610220527e845af450506000600061016101e0","0x280"],"depth":2,"refund":0,"opName":"MSTORE"} +{"pc":782,"op":127,"gas":"0x9e16b","gasCost":"0x3","memSize":672,"stack":[],"depth":2,"refund":0,"opName":"PUSH32"} +{"pc":815,"op":97,"gas":"0x9e168","gasCost":"0x3","memSize":672,"stack":["0x527f20527f60610100527e600060006003610240527f5af15060005450c76000"],"depth":2,"refund":0,"opName":"PUSH2"} +{"pc":818,"op":82,"gas":"0x9e165","gasCost":"0x6","memSize":672,"stack":["0x527f20527f60610100527e600060006003610240527f5af15060005450c76000","0x2a0"],"depth":2,"refund":0,"opName":"MSTORE"} +{"pc":819,"op":127,"gas":"0x9e15f","gasCost":"0x3","memSize":704,"stack":[],"depth":2,"refund":0,"opName":"PUSH32"} +{"pc":852,"op":97,"gas":"0x9e15c","gasCost":"0x3","memSize":704,"stack":["0x6002551309f562610200527f66a486610140527f6b00610260527f1d45716101"],"depth":2,"refund":0,"opName":"PUSH2"} +{"pc":855,"op":82,"gas":"0x9e159","gasCost":"0x7","memSize":704,"stack":["0x6002551309f562610200527f66a486610140527f6b00610260527f1d45716101","0x2c0"],"depth":2,"refund":0,"opName":"MSTORE"} +{"pc":856,"op":127,"gas":"0x9e152","gasCost":"0x3","memSize":736,"stack":[],"depth":2,"refund":0,"opName":"PUSH32"} +{"pc":889,"op":97,"gas":"0x9e14f","gasCost":"0x3","memSize":736,"stack":["0x20527f600054501c641d373c7f60045450610220527f6000600155610280527f"],"depth":2,"refund":0,"opName":"PUSH2"} +{"pc":892,"op":82,"gas":"0x9e14c","gasCost":"0x6","memSize":736,"stack":["0x20527f600054501c641d373c7f60045450610220527f6000600155610280527f","0x2e0"],"depth":2,"refund":0,"opName":"MSTORE"} +{"pc":893,"op":127,"gas":"0x9e146","gasCost":"0x3","memSize":768,"stack":[],"depth":2,"refund":0,"opName":"PUSH32"} +{"pc":926,"op":97,"gas":"0x9e143","gasCost":"0x3","memSize":768,"stack":["0x600554610160527f50600160025560085450610140527f60006002610240527f"],"depth":2,"refund":0,"opName":"PUSH2"} +{"pc":929,"op":82,"gas":"0x9e140","gasCost":"0x6","memSize":768,"stack":["0x600554610160527f50600160025560085450610140527f60006002610240527f","0x300"],"depth":2,"refund":0,"opName":"MSTORE"} +{"pc":930,"op":127,"gas":"0x9e13a","gasCost":"0x3","memSize":800,"stack":[],"depth":2,"refund":0,"opName":"PUSH32"} +{"pc":963,"op":97,"gas":"0x9e137","gasCost":"0x3","memSize":800,"stack":["0x6102a0527f557fd86000606000527e600060610180527e600060005af1508612"],"depth":2,"refund":0,"opName":"PUSH2"} +{"pc":966,"op":82,"gas":"0x9e134","gasCost":"0x6","memSize":800,"stack":["0x6102a0527f557fd86000606000527e600060610180527e600060005af1508612","0x320"],"depth":2,"refund":0,"opName":"MSTORE"} +{"pc":967,"op":127,"gas":"0x9e12e","gasCost":"0x3","memSize":832,"stack":[],"depth":2,"refund":0,"opName":"PUSH32"} +{"pc":1000,"op":97,"gas":"0x9e12b","gasCost":"0x3","memSize":832,"stack":["0x17145147356102c0527f610260527f610160527f5198a37e127a7efa7c600052"],"depth":2,"refund":0,"opName":"PUSH2"} +{"pc":1003,"op":82,"gas":"0x9e128","gasCost":"0x6","memSize":832,"stack":["0x17145147356102c0527f610260527f610160527f5198a37e127a7efa7c600052","0x340"],"depth":2,"refund":0,"opName":"MSTORE"} +{"pc":1004,"op":127,"gas":"0x9e122","gasCost":"0x3","memSize":864,"stack":[],"depth":2,"refund":0,"opName":"PUSH32"} +{"pc":1037,"op":97,"gas":"0x9e11f","gasCost":"0x3","memSize":864,"stack":["0x6101a0527f606020527f6102e0527f6060205360610280527ff760215360ff60"],"depth":2,"refund":0,"opName":"PUSH2"} +{"pc":1040,"op":82,"gas":"0x9e11c","gasCost":"0x6","memSize":864,"stack":["0x6101a0527f606020527f6102e0527f6060205360610280527ff760215360ff60","0x360"],"depth":2,"refund":0,"opName":"MSTORE"} +{"pc":1041,"op":127,"gas":"0x9e116","gasCost":"0x3","memSize":896,"stack":[],"depth":2,"refund":0,"opName":"PUSH32"} +{"pc":1074,"op":97,"gas":"0x9e113","gasCost":"0x3","memSize":896,"stack":["0x225360610180527fdb602353603760610300527f6101c0527f24536075606102"],"depth":2,"refund":0,"opName":"PUSH2"} +{"pc":1077,"op":82,"gas":"0x9e110","gasCost":"0x6","memSize":896,"stack":["0x225360610180527fdb602353603760610300527f6101c0527f24536075606102","0x380"],"depth":2,"refund":0,"opName":"MSTORE"} +{"pc":1078,"op":127,"gas":"0x9e10a","gasCost":"0x3","memSize":928,"stack":[],"depth":2,"refund":0,"opName":"PUSH32"} +{"pc":1111,"op":97,"gas":"0x9e107","gasCost":"0x3","memSize":928,"stack":["0xa0527f2553609f606040527f265360fe60275360610320527f8f60286101a052"],"depth":2,"refund":0,"opName":"PUSH2"} +{"pc":1114,"op":82,"gas":"0x9e104","gasCost":"0x6","memSize":928,"stack":["0xa0527f2553609f606040527f265360fe60275360610320527f8f60286101a052","0x3a0"],"depth":2,"refund":0,"opName":"MSTORE"} +{"pc":1115,"op":127,"gas":"0x9e0fe","gasCost":"0x3","memSize":960,"stack":[],"depth":2,"refund":0,"opName":"PUSH32"} +{"pc":1148,"op":97,"gas":"0x9e0fb","gasCost":"0x3","memSize":960,"stack":["0x7f53606101e0527f6102c0527f0b6029536060602a53600060610340527f2b53"],"depth":2,"refund":0,"opName":"PUSH2"} +{"pc":1151,"op":82,"gas":"0x9e0f8","gasCost":"0x6","memSize":960,"stack":["0x7f53606101e0527f6102c0527f0b6029536060602a53600060610340527f2b53","0x3c0"],"depth":2,"refund":0,"opName":"MSTORE"} +{"pc":1152,"op":127,"gas":"0x9e0f2","gasCost":"0x3","memSize":992,"stack":[],"depth":2,"refund":0,"opName":"PUSH32"} +{"pc":1185,"op":97,"gas":"0x9e0ef","gasCost":"0x3","memSize":992,"stack":["0x6060602c53606052606060805360006061016102e0527f610200527fc0526103"],"depth":2,"refund":0,"opName":"PUSH2"} +{"pc":1188,"op":82,"gas":"0x9e0ec","gasCost":"0x7","memSize":992,"stack":["0x6060602c53606052606060805360006061016102e0527f610200527fc0526103","0x3e0"],"depth":2,"refund":0,"opName":"MSTORE"} +{"pc":1189,"op":127,"gas":"0x9e0e5","gasCost":"0x3","memSize":1024,"stack":[],"depth":2,"refund":0,"opName":"PUSH32"} +{"pc":1222,"op":97,"gas":"0x9e0e2","gasCost":"0x3","memSize":1024,"stack":["0x60527f7f81536060608253602d6083536053608453606060855360fd61030052"],"depth":2,"refund":0,"opName":"PUSH2"} +{"pc":1225,"op":82,"gas":"0x9e0df","gasCost":"0x6","memSize":1024,"stack":["0x60527f7f81536060608253602d6083536053608453606060855360fd61030052","0x400"],"depth":2,"refund":0,"opName":"MSTORE"} +{"pc":1226,"op":127,"gas":"0x9e0d9","gasCost":"0x3","memSize":1056,"stack":[],"depth":2,"refund":0,"opName":"PUSH32"} +{"pc":1259,"op":97,"gas":"0x9e0d6","gasCost":"0x3","memSize":1056,"stack":["0x7f6086610380527f536060610220527f6087536101e0527f602e608853605360"],"depth":2,"refund":0,"opName":"PUSH2"} +{"pc":1262,"op":82,"gas":"0x9e0d3","gasCost":"0x6","memSize":1056,"stack":["0x7f6086610380527f536060610220527f6087536101e0527f602e608853605360","0x420"],"depth":2,"refund":0,"opName":"MSTORE"} +{"pc":1263,"op":127,"gas":"0x9e0cd","gasCost":"0x3","memSize":1088,"stack":[],"depth":2,"refund":0,"opName":"PUSH32"} +{"pc":1296,"op":97,"gas":"0x9e0ca","gasCost":"0x3","memSize":1088,"stack":["0x89536060608a61036103a0527f20527f53602f608b536060608c610240527f53"],"depth":2,"refund":0,"opName":"PUSH2"} +{"pc":1299,"op":82,"gas":"0x9e0c7","gasCost":"0x6","memSize":1088,"stack":["0x89536060608a61036103a0527f20527f53602f608b536060608c610240527f53","0x440"],"depth":2,"refund":0,"opName":"MSTORE"} +{"pc":1300,"op":127,"gas":"0x9e0c1","gasCost":"0x3","memSize":1120,"stack":[],"depth":2,"refund":0,"opName":"PUSH32"} +{"pc":1333,"op":97,"gas":"0x9e0be","gasCost":"0x3","memSize":1120,"stack":["0x6000608d5360f36102005260606103c0527f610220610340527f53608e610221"],"depth":2,"refund":0,"opName":"PUSH2"} +{"pc":1336,"op":82,"gas":"0x9e0bb","gasCost":"0x6","memSize":1120,"stack":["0x6000608d5360f36102005260606103c0527f610220610340527f53608e610221","0x460"],"depth":2,"refund":0,"opName":"MSTORE"} +{"pc":1337,"op":127,"gas":"0x9e0b5","gasCost":"0x3","memSize":1152,"stack":[],"depth":2,"refund":0,"opName":"PUSH32"} +{"pc":1370,"op":97,"gas":"0x9e0b2","gasCost":"0x3","memSize":1152,"stack":["0x536053610222536060610260527f610223536103e0527f600061022453606061"],"depth":2,"refund":0,"opName":"PUSH2"} +{"pc":1373,"op":82,"gas":"0x9e0af","gasCost":"0x6","memSize":1152,"stack":["0x536053610222536060610260527f610223536103e0527f600061022453606061","0x480"],"depth":2,"refund":0,"opName":"MSTORE"} +{"pc":1374,"op":127,"gas":"0x9e0a9","gasCost":"0x3","memSize":1184,"stack":[],"depth":2,"refund":0,"opName":"PUSH32"} +{"pc":1407,"op":97,"gas":"0x9e0a6","gasCost":"0x3","memSize":1184,"stack":["0x360527f61022553608f61022653606061022753600061610400527f02610280"],"depth":2,"refund":0,"opName":"PUSH2"} +{"pc":1410,"op":82,"gas":"0x9e0a3","gasCost":"0x6","memSize":1184,"stack":["0x360527f61022553608f61022653606061022753600061610400527f02610280","0x4a0"],"depth":2,"refund":0,"opName":"MSTORE"} +{"pc":1411,"op":127,"gas":"0x9e09d","gasCost":"0x3","memSize":1216,"stack":[],"depth":2,"refund":0,"opName":"PUSH32"} +{"pc":1444,"op":97,"gas":"0x9e09a","gasCost":"0x3","memSize":1216,"stack":["0x527f28536060610229610380527f53600061022a5360f561022b536061042052"],"depth":2,"refund":0,"opName":"PUSH2"} +{"pc":1447,"op":82,"gas":"0x9e097","gasCost":"0x6","memSize":1216,"stack":["0x527f28536060610229610380527f53600061022a5360f561022b536061042052","0x4c0"],"depth":2,"refund":0,"opName":"MSTORE"} +{"pc":1448,"op":127,"gas":"0x9e091","gasCost":"0x3","memSize":1248,"stack":[],"depth":2,"refund":0,"opName":"PUSH32"} +{"pc":1481,"op":97,"gas":"0x9e08e","gasCost":"0x3","memSize":1248,"stack":["0x7f6061022c53600061022d536102a0527f60606103a0527f61022e5360006102"],"depth":2,"refund":0,"opName":"PUSH2"} +{"pc":1484,"op":82,"gas":"0x9e08b","gasCost":"0x7","memSize":1248,"stack":["0x7f6061022c53600061022d536102a0527f60606103a0527f61022e5360006102","0x4e0"],"depth":2,"refund":0,"opName":"MSTORE"} +{"pc":1485,"op":127,"gas":"0x9e084","gasCost":"0x3","memSize":1280,"stack":[],"depth":2,"refund":0,"opName":"PUSH32"} +{"pc":1518,"op":97,"gas":"0x9e081","gasCost":"0x3","memSize":1280,"stack":["0x2f610440527f53606061023053600061023153606061023253600061026103c0"],"depth":2,"refund":0,"opName":"PUSH2"} +{"pc":1521,"op":82,"gas":"0x9e07e","gasCost":"0x6","memSize":1280,"stack":["0x2f610440527f53606061023053600061023153606061023253600061026103c0","0x500"],"depth":2,"refund":0,"opName":"MSTORE"} +{"pc":1522,"op":127,"gas":"0x9e078","gasCost":"0x3","memSize":1312,"stack":[],"depth":2,"refund":0,"opName":"PUSH32"} +{"pc":1555,"op":97,"gas":"0x9e075","gasCost":"0x3","memSize":1312,"stack":["0x527fc0527f61610460527f023353606061023453600061023553608561023653"],"depth":2,"refund":0,"opName":"PUSH2"} +{"pc":1558,"op":82,"gas":"0x9e072","gasCost":"0x6","memSize":1312,"stack":["0x527fc0527f61610460527f023353606061023453600061023553608561023653","0x520"],"depth":2,"refund":0,"opName":"MSTORE"} +{"pc":1559,"op":127,"gas":"0x9e06c","gasCost":"0x3","memSize":1344,"stack":[],"depth":2,"refund":0,"opName":"PUSH32"} +{"pc":1592,"op":97,"gas":"0x9e069","gasCost":"0x3","memSize":1344,"stack":["0x605a61023753606103e052610480527f7ff261026102e0526038610300536053"],"depth":2,"refund":0,"opName":"PUSH2"} +{"pc":1595,"op":82,"gas":"0x9e066","gasCost":"0x6","memSize":1344,"stack":["0x605a61023753606103e052610480527f7ff261026102e0526038610300536053","0x540"],"depth":2,"refund":0,"opName":"MSTORE"} +{"pc":1596,"op":127,"gas":"0x9e060","gasCost":"0x3","memSize":1376,"stack":[],"depth":2,"refund":0,"opName":"PUSH32"} +{"pc":1629,"op":97,"gas":"0x9e05d","gasCost":"0x3","memSize":1376,"stack":["0x610301536060610302536050610303536104a0527f60610400527f6161030453"],"depth":2,"refund":0,"opName":"PUSH2"} +{"pc":1632,"op":82,"gas":"0x9e05a","gasCost":"0x6","memSize":1376,"stack":["0x610301536060610302536050610303536104a0527f60610400527f6161030453","0x560"],"depth":2,"refund":0,"opName":"MSTORE"} +{"pc":1633,"op":127,"gas":"0x9e054","gasCost":"0x3","memSize":1408,"stack":[],"depth":2,"refund":0,"opName":"PUSH32"} +{"pc":1666,"op":97,"gas":"0x9e051","gasCost":"0x3","memSize":1408,"stack":["0x6002610305536039610306536053610307536060616104c0527f030853605061"],"depth":2,"refund":0,"opName":"PUSH2"} +{"pc":1669,"op":82,"gas":"0x9e04e","gasCost":"0x6","memSize":1408,"stack":["0x6002610305536039610306536053610307536060616104c0527f030853605061","0x580"],"depth":2,"refund":0,"opName":"MSTORE"} +{"pc":1670,"op":127,"gas":"0x9e048","gasCost":"0x3","memSize":1440,"stack":[],"depth":2,"refund":0,"opName":"PUSH32"} +{"pc":1703,"op":97,"gas":"0x9e045","gasCost":"0x3","memSize":1440,"stack":["0x610420527f030953606161030a53600261030b53603a61030c536104e0527f60"],"depth":2,"refund":0,"opName":"PUSH2"} +{"pc":1706,"op":82,"gas":"0x9e042","gasCost":"0x7","memSize":1440,"stack":["0x610420527f030953606161030a53600261030b53603a61030c536104e0527f60","0x5a0"],"depth":2,"refund":0,"opName":"MSTORE"} +{"pc":1707,"op":127,"gas":"0x9e03b","gasCost":"0x3","memSize":1472,"stack":[],"depth":2,"refund":0,"opName":"PUSH32"} +{"pc":1740,"op":97,"gas":"0x9e038","gasCost":"0x3","memSize":1472,"stack":["0x5361030d53606161030e610440527f53600261030f53603b6103105360606161"],"depth":2,"refund":0,"opName":"PUSH2"} +{"pc":1743,"op":82,"gas":"0x9e035","gasCost":"0x6","memSize":1472,"stack":["0x5361030d53606161030e610440527f53600261030f53603b6103105360606161","0x5c0"],"depth":2,"refund":0,"opName":"MSTORE"} +{"pc":1744,"op":127,"gas":"0x9e02f","gasCost":"0x3","memSize":1504,"stack":[],"depth":2,"refund":0,"opName":"PUSH32"} +{"pc":1777,"op":97,"gas":"0x9e02c","gasCost":"0x3","memSize":1504,"stack":["0x500527f03115360006103125360f36103135361610460526003610480536014"],"depth":2,"refund":0,"opName":"PUSH2"} +{"pc":1780,"op":82,"gas":"0x9e029","gasCost":"0x6","memSize":1504,"stack":["0x500527f03115360006103125360f36103135361610460526003610480536014","0x5e0"],"depth":2,"refund":0,"opName":"MSTORE"} +{"pc":1781,"op":127,"gas":"0x9e023","gasCost":"0x3","memSize":1536,"stack":[],"depth":2,"refund":0,"opName":"PUSH32"} +{"pc":1814,"op":97,"gas":"0x9e020","gasCost":"0x3","memSize":1536,"stack":["0x61048153610520527f6060610482536000610483536060610484536000610485"],"depth":2,"refund":0,"opName":"PUSH2"} +{"pc":1817,"op":82,"gas":"0x9e01d","gasCost":"0x6","memSize":1536,"stack":["0x61048153610520527f6060610482536000610483536060610484536000610485","0x600"],"depth":2,"refund":0,"opName":"MSTORE"} +{"pc":1818,"op":127,"gas":"0x9e017","gasCost":"0x3","memSize":1568,"stack":[],"depth":2,"refund":0,"opName":"PUSH32"} +{"pc":1851,"op":97,"gas":"0x9e014","gasCost":"0x3","memSize":1568,"stack":["0x5360f0610486536060610540527f610487536000610488536060610489536000"],"depth":2,"refund":0,"opName":"PUSH2"} +{"pc":1854,"op":82,"gas":"0x9e011","gasCost":"0x6","memSize":1568,"stack":["0x5360f0610486536060610540527f610487536000610488536060610489536000","0x620"],"depth":2,"refund":0,"opName":"MSTORE"} +{"pc":1855,"op":127,"gas":"0x9e00b","gasCost":"0x3","memSize":1600,"stack":[],"depth":2,"refund":0,"opName":"PUSH32"} +{"pc":1888,"op":97,"gas":"0x9e008","gasCost":"0x3","memSize":1600,"stack":["0x61048a53606061048b5360006104610560527f8c53606061048d53600061048e"],"depth":2,"refund":0,"opName":"PUSH2"} +{"pc":1891,"op":82,"gas":"0x9e005","gasCost":"0x7","memSize":1600,"stack":["0x61048a53606061048b5360006104610560527f8c53606061048d53600061048e","0x640"],"depth":2,"refund":0,"opName":"MSTORE"} +{"pc":1892,"op":127,"gas":"0x9dffe","gasCost":"0x3","memSize":1632,"stack":[],"depth":2,"refund":0,"opName":"PUSH32"} +{"pc":1925,"op":97,"gas":"0x9dffb","gasCost":"0x3","memSize":1632,"stack":["0x53608461048f53605a6104905360f4610491536105805260606105a053605061"],"depth":2,"refund":0,"opName":"PUSH2"} +{"pc":1928,"op":82,"gas":"0x9dff8","gasCost":"0x6","memSize":1632,"stack":["0x53608461048f53605a6104905360f4610491536105805260606105a053605061","0x660"],"depth":2,"refund":0,"opName":"MSTORE"} +{"pc":1929,"op":127,"gas":"0x9dff2","gasCost":"0x3","memSize":1664,"stack":[],"depth":2,"refund":0,"opName":"PUSH32"} +{"pc":1962,"op":97,"gas":"0x9dfef","gasCost":"0x3","memSize":1664,"stack":["0x5a15360616105a25360046105a35360926105a45360536105a55360606105a6"],"depth":2,"refund":0,"opName":"PUSH2"} +{"pc":1965,"op":82,"gas":"0x9dfec","gasCost":"0x6","memSize":1664,"stack":["0x5a15360616105a25360046105a35360926105a45360536105a55360606105a6","0x680"],"depth":2,"refund":0,"opName":"MSTORE"} +{"pc":1966,"op":127,"gas":"0x9dfe6","gasCost":"0x3","memSize":1696,"stack":[],"depth":2,"refund":0,"opName":"PUSH32"} +{"pc":1999,"op":97,"gas":"0x9dfe3","gasCost":"0x3","memSize":1696,"stack":["0x5360506105a75360616105a85360046105a95360936105aa5360536105ab5360"],"depth":2,"refund":0,"opName":"PUSH2"} +{"pc":2002,"op":82,"gas":"0x9dfe0","gasCost":"0x6","memSize":1696,"stack":["0x5360506105a75360616105a85360046105a95360936105aa5360536105ab5360","0x6a0"],"depth":2,"refund":0,"opName":"MSTORE"} +{"pc":2003,"op":127,"gas":"0x9dfda","gasCost":"0x3","memSize":1728,"stack":[],"depth":2,"refund":0,"opName":"PUSH32"} +{"pc":2036,"op":97,"gas":"0x9dfd7","gasCost":"0x3","memSize":1728,"stack":["0x616105ac5360046105ad5360946105ae5360606105af5360006105b05360f361"],"depth":2,"refund":0,"opName":"PUSH2"} +{"pc":2039,"op":82,"gas":"0x9dfd4","gasCost":"0x6","memSize":1728,"stack":["0x616105ac5360046105ad5360946105ae5360606105af5360006105b05360f361","0x6c0"],"depth":2,"refund":0,"opName":"MSTORE"} +{"pc":2040,"op":96,"gas":"0x9dfce","gasCost":"0x3","memSize":1760,"stack":[],"depth":2,"refund":0,"opName":"PUSH1"} +{"pc":2042,"op":97,"gas":"0x9dfcb","gasCost":"0x3","memSize":1760,"stack":["0x5"],"depth":2,"refund":0,"opName":"PUSH2"} +{"pc":2045,"op":83,"gas":"0x9dfc8","gasCost":"0x7","memSize":1760,"stack":["0x5","0x6e0"],"depth":2,"refund":0,"opName":"MSTORE8"} +{"pc":2046,"op":96,"gas":"0x9dfc1","gasCost":"0x3","memSize":1792,"stack":[],"depth":2,"refund":0,"opName":"PUSH1"} +{"pc":2048,"op":97,"gas":"0x9dfbe","gasCost":"0x3","memSize":1792,"stack":["0xb1"],"depth":2,"refund":0,"opName":"PUSH2"} +{"pc":2051,"op":83,"gas":"0x9dfbb","gasCost":"0x3","memSize":1792,"stack":["0xb1","0x6e1"],"depth":2,"refund":0,"opName":"MSTORE8"} +{"pc":2052,"op":96,"gas":"0x9dfb8","gasCost":"0x3","memSize":1792,"stack":[],"depth":2,"refund":0,"opName":"PUSH1"} +{"pc":2054,"op":97,"gas":"0x9dfb5","gasCost":"0x3","memSize":1792,"stack":["0x53"],"depth":2,"refund":0,"opName":"PUSH2"} +{"pc":2057,"op":83,"gas":"0x9dfb2","gasCost":"0x3","memSize":1792,"stack":["0x53","0x6e2"],"depth":2,"refund":0,"opName":"MSTORE8"} +{"pc":2058,"op":96,"gas":"0x9dfaf","gasCost":"0x3","memSize":1792,"stack":[],"depth":2,"refund":0,"opName":"PUSH1"} +{"pc":2060,"op":97,"gas":"0x9dfac","gasCost":"0x3","memSize":1792,"stack":["0x60"],"depth":2,"refund":0,"opName":"PUSH2"} +{"pc":2063,"op":83,"gas":"0x9dfa9","gasCost":"0x3","memSize":1792,"stack":["0x60","0x6e3"],"depth":2,"refund":0,"opName":"MSTORE8"} +{"pc":2064,"op":96,"gas":"0x9dfa6","gasCost":"0x3","memSize":1792,"stack":[],"depth":2,"refund":0,"opName":"PUSH1"} +{"pc":2066,"op":97,"gas":"0x9dfa3","gasCost":"0x3","memSize":1792,"stack":["0x0"],"depth":2,"refund":0,"opName":"PUSH2"} +{"pc":2069,"op":83,"gas":"0x9dfa0","gasCost":"0x3","memSize":1792,"stack":["0x0","0x6e4"],"depth":2,"refund":0,"opName":"MSTORE8"} +{"pc":2070,"op":96,"gas":"0x9df9d","gasCost":"0x3","memSize":1792,"stack":[],"depth":2,"refund":0,"opName":"PUSH1"} +{"pc":2072,"op":97,"gas":"0x9df9a","gasCost":"0x3","memSize":1792,"stack":["0x61"],"depth":2,"refund":0,"opName":"PUSH2"} +{"pc":2075,"op":83,"gas":"0x9df97","gasCost":"0x3","memSize":1792,"stack":["0x61","0x6e5"],"depth":2,"refund":0,"opName":"MSTORE8"} +{"pc":2076,"op":96,"gas":"0x9df94","gasCost":"0x3","memSize":1792,"stack":[],"depth":2,"refund":0,"opName":"PUSH1"} +{"pc":2078,"op":97,"gas":"0x9df91","gasCost":"0x3","memSize":1792,"stack":["0x5"],"depth":2,"refund":0,"opName":"PUSH2"} +{"pc":2081,"op":83,"gas":"0x9df8e","gasCost":"0x3","memSize":1792,"stack":["0x5","0x6e6"],"depth":2,"refund":0,"opName":"MSTORE8"} +{"pc":2082,"op":96,"gas":"0x9df8b","gasCost":"0x3","memSize":1792,"stack":[],"depth":2,"refund":0,"opName":"PUSH1"} +{"pc":2084,"op":97,"gas":"0x9df88","gasCost":"0x3","memSize":1792,"stack":["0xb2"],"depth":2,"refund":0,"opName":"PUSH2"} +{"pc":2087,"op":83,"gas":"0x9df85","gasCost":"0x3","memSize":1792,"stack":["0xb2","0x6e7"],"depth":2,"refund":0,"opName":"MSTORE8"} +{"pc":2088,"op":96,"gas":"0x9df82","gasCost":"0x3","memSize":1792,"stack":[],"depth":2,"refund":0,"opName":"PUSH1"} +{"pc":2090,"op":97,"gas":"0x9df7f","gasCost":"0x3","memSize":1792,"stack":["0x60"],"depth":2,"refund":0,"opName":"PUSH2"} +{"pc":2093,"op":83,"gas":"0x9df7c","gasCost":"0x3","memSize":1792,"stack":["0x60","0x6e8"],"depth":2,"refund":0,"opName":"MSTORE8"} +{"pc":2094,"op":96,"gas":"0x9df79","gasCost":"0x3","memSize":1792,"stack":[],"depth":2,"refund":0,"opName":"PUSH1"} +{"pc":2096,"op":97,"gas":"0x9df76","gasCost":"0x3","memSize":1792,"stack":["0x0"],"depth":2,"refund":0,"opName":"PUSH2"} +{"pc":2099,"op":83,"gas":"0x9df73","gasCost":"0x3","memSize":1792,"stack":["0x0","0x6e9"],"depth":2,"refund":0,"opName":"MSTORE8"} +{"pc":2100,"op":96,"gas":"0x9df70","gasCost":"0x3","memSize":1792,"stack":[],"depth":2,"refund":0,"opName":"PUSH1"} +{"pc":2102,"op":97,"gas":"0x9df6d","gasCost":"0x3","memSize":1792,"stack":["0x60"],"depth":2,"refund":0,"opName":"PUSH2"} +{"pc":2105,"op":83,"gas":"0x9df6a","gasCost":"0x3","memSize":1792,"stack":["0x60","0x6ea"],"depth":2,"refund":0,"opName":"MSTORE8"} +{"pc":2106,"op":96,"gas":"0x9df67","gasCost":"0x3","memSize":1792,"stack":[],"depth":2,"refund":0,"opName":"PUSH1"} +{"pc":2108,"op":97,"gas":"0x9df64","gasCost":"0x3","memSize":1792,"stack":["0x0"],"depth":2,"refund":0,"opName":"PUSH2"} +{"pc":2111,"op":83,"gas":"0x9df61","gasCost":"0x3","memSize":1792,"stack":["0x0","0x6eb"],"depth":2,"refund":0,"opName":"MSTORE8"} +{"pc":2112,"op":96,"gas":"0x9df5e","gasCost":"0x3","memSize":1792,"stack":[],"depth":2,"refund":0,"opName":"PUSH1"} +{"pc":2114,"op":97,"gas":"0x9df5b","gasCost":"0x3","memSize":1792,"stack":["0xf5"],"depth":2,"refund":0,"opName":"PUSH2"} +{"pc":2117,"op":83,"gas":"0x9df58","gasCost":"0x3","memSize":1792,"stack":["0xf5","0x6ec"],"depth":2,"refund":0,"opName":"MSTORE8"} +{"pc":2118,"op":96,"gas":"0x9df55","gasCost":"0x3","memSize":1792,"stack":[],"depth":2,"refund":0,"opName":"PUSH1"} +{"pc":2120,"op":97,"gas":"0x9df52","gasCost":"0x3","memSize":1792,"stack":["0x60"],"depth":2,"refund":0,"opName":"PUSH2"} +{"pc":2123,"op":83,"gas":"0x9df4f","gasCost":"0x3","memSize":1792,"stack":["0x60","0x6ed"],"depth":2,"refund":0,"opName":"MSTORE8"} +{"pc":2124,"op":96,"gas":"0x9df4c","gasCost":"0x3","memSize":1792,"stack":[],"depth":2,"refund":0,"opName":"PUSH1"} +{"pc":2126,"op":97,"gas":"0x9df49","gasCost":"0x3","memSize":1792,"stack":["0x0"],"depth":2,"refund":0,"opName":"PUSH2"} +{"pc":2129,"op":83,"gas":"0x9df46","gasCost":"0x3","memSize":1792,"stack":["0x0","0x6ee"],"depth":2,"refund":0,"opName":"MSTORE8"} +{"pc":2130,"op":96,"gas":"0x9df43","gasCost":"0x3","memSize":1792,"stack":[],"depth":2,"refund":0,"opName":"PUSH1"} +{"pc":2132,"op":97,"gas":"0x9df40","gasCost":"0x3","memSize":1792,"stack":["0x60"],"depth":2,"refund":0,"opName":"PUSH2"} +{"pc":2135,"op":83,"gas":"0x9df3d","gasCost":"0x3","memSize":1792,"stack":["0x60","0x6ef"],"depth":2,"refund":0,"opName":"MSTORE8"} +{"pc":2136,"op":96,"gas":"0x9df3a","gasCost":"0x3","memSize":1792,"stack":[],"depth":2,"refund":0,"opName":"PUSH1"} +{"pc":2138,"op":97,"gas":"0x9df37","gasCost":"0x3","memSize":1792,"stack":["0x0"],"depth":2,"refund":0,"opName":"PUSH2"} +{"pc":2141,"op":83,"gas":"0x9df34","gasCost":"0x3","memSize":1792,"stack":["0x0","0x6f0"],"depth":2,"refund":0,"opName":"MSTORE8"} +{"pc":2142,"op":96,"gas":"0x9df31","gasCost":"0x3","memSize":1792,"stack":[],"depth":2,"refund":0,"opName":"PUSH1"} +{"pc":2144,"op":97,"gas":"0x9df2e","gasCost":"0x3","memSize":1792,"stack":["0x60"],"depth":2,"refund":0,"opName":"PUSH2"} +{"pc":2147,"op":83,"gas":"0x9df2b","gasCost":"0x3","memSize":1792,"stack":["0x60","0x6f1"],"depth":2,"refund":0,"opName":"MSTORE8"} +{"pc":2148,"op":96,"gas":"0x9df28","gasCost":"0x3","memSize":1792,"stack":[],"depth":2,"refund":0,"opName":"PUSH1"} +{"pc":2150,"op":97,"gas":"0x9df25","gasCost":"0x3","memSize":1792,"stack":["0x0"],"depth":2,"refund":0,"opName":"PUSH2"} +{"pc":2153,"op":83,"gas":"0x9df22","gasCost":"0x3","memSize":1792,"stack":["0x0","0x6f2"],"depth":2,"refund":0,"opName":"MSTORE8"} +{"pc":2154,"op":96,"gas":"0x9df1f","gasCost":"0x3","memSize":1792,"stack":[],"depth":2,"refund":0,"opName":"PUSH1"} +{"pc":2156,"op":97,"gas":"0x9df1c","gasCost":"0x3","memSize":1792,"stack":["0x60"],"depth":2,"refund":0,"opName":"PUSH2"} +{"pc":2159,"op":83,"gas":"0x9df19","gasCost":"0x3","memSize":1792,"stack":["0x60","0x6f3"],"depth":2,"refund":0,"opName":"MSTORE8"} +{"pc":2160,"op":96,"gas":"0x9df16","gasCost":"0x3","memSize":1792,"stack":[],"depth":2,"refund":0,"opName":"PUSH1"} +{"pc":2162,"op":97,"gas":"0x9df13","gasCost":"0x3","memSize":1792,"stack":["0x0"],"depth":2,"refund":0,"opName":"PUSH2"} +{"pc":2165,"op":83,"gas":"0x9df10","gasCost":"0x3","memSize":1792,"stack":["0x0","0x6f4"],"depth":2,"refund":0,"opName":"MSTORE8"} +{"pc":2166,"op":96,"gas":"0x9df0d","gasCost":"0x3","memSize":1792,"stack":[],"depth":2,"refund":0,"opName":"PUSH1"} +{"pc":2168,"op":97,"gas":"0x9df0a","gasCost":"0x3","memSize":1792,"stack":["0x60"],"depth":2,"refund":0,"opName":"PUSH2"} +{"pc":2171,"op":83,"gas":"0x9df07","gasCost":"0x3","memSize":1792,"stack":["0x60","0x6f5"],"depth":2,"refund":0,"opName":"MSTORE8"} +{"pc":2172,"op":96,"gas":"0x9df04","gasCost":"0x3","memSize":1792,"stack":[],"depth":2,"refund":0,"opName":"PUSH1"} +{"pc":2174,"op":97,"gas":"0x9df01","gasCost":"0x3","memSize":1792,"stack":["0x0"],"depth":2,"refund":0,"opName":"PUSH2"} +{"pc":2177,"op":83,"gas":"0x9defe","gasCost":"0x3","memSize":1792,"stack":["0x0","0x6f6"],"depth":2,"refund":0,"opName":"MSTORE8"} +{"pc":2178,"op":96,"gas":"0x9defb","gasCost":"0x3","memSize":1792,"stack":[],"depth":2,"refund":0,"opName":"PUSH1"} +{"pc":2180,"op":97,"gas":"0x9def8","gasCost":"0x3","memSize":1792,"stack":["0x85"],"depth":2,"refund":0,"opName":"PUSH2"} +{"pc":2183,"op":83,"gas":"0x9def5","gasCost":"0x3","memSize":1792,"stack":["0x85","0x6f7"],"depth":2,"refund":0,"opName":"MSTORE8"} +{"pc":2184,"op":96,"gas":"0x9def2","gasCost":"0x3","memSize":1792,"stack":[],"depth":2,"refund":0,"opName":"PUSH1"} +{"pc":2186,"op":97,"gas":"0x9deef","gasCost":"0x3","memSize":1792,"stack":["0x5a"],"depth":2,"refund":0,"opName":"PUSH2"} +{"pc":2189,"op":83,"gas":"0x9deec","gasCost":"0x3","memSize":1792,"stack":["0x5a","0x6f8"],"depth":2,"refund":0,"opName":"MSTORE8"} +{"pc":2190,"op":96,"gas":"0x9dee9","gasCost":"0x3","memSize":1792,"stack":[],"depth":2,"refund":0,"opName":"PUSH1"} +{"pc":2192,"op":97,"gas":"0x9dee6","gasCost":"0x3","memSize":1792,"stack":["0xf1"],"depth":2,"refund":0,"opName":"PUSH2"} +{"pc":2195,"op":83,"gas":"0x9dee3","gasCost":"0x3","memSize":1792,"stack":["0xf1","0x6f9"],"depth":2,"refund":0,"opName":"MSTORE8"} +{"pc":2196,"op":96,"gas":"0x9dee0","gasCost":"0x3","memSize":1792,"stack":[],"depth":2,"refund":0,"opName":"PUSH1"} +{"pc":2198,"op":97,"gas":"0x9dedd","gasCost":"0x3","memSize":1792,"stack":["0x50"],"depth":2,"refund":0,"opName":"PUSH2"} +{"pc":2201,"op":83,"gas":"0x9deda","gasCost":"0x3","memSize":1792,"stack":["0x50","0x6fa"],"depth":2,"refund":0,"opName":"MSTORE8"} +{"pc":2202,"op":96,"gas":"0x9ded7","gasCost":"0x3","memSize":1792,"stack":[],"depth":2,"refund":0,"opName":"PUSH1"} +{"pc":2204,"op":97,"gas":"0x9ded4","gasCost":"0x3","memSize":1792,"stack":["0x50"],"depth":2,"refund":0,"opName":"PUSH2"} +{"pc":2207,"op":83,"gas":"0x9ded1","gasCost":"0x3","memSize":1792,"stack":["0x50","0x6fb"],"depth":2,"refund":0,"opName":"MSTORE8"} +{"pc":2208,"op":97,"gas":"0x9dece","gasCost":"0x3","memSize":1792,"stack":[],"depth":2,"refund":0,"opName":"PUSH2"} +{"pc":2211,"op":96,"gas":"0x9decb","gasCost":"0x3","memSize":1792,"stack":["0x6fc"],"depth":2,"refund":0,"opName":"PUSH1"} +{"pc":2213,"op":243,"gas":"0x9dec8","gasCost":"0x0","memSize":1792,"stack":["0x6fc","0x0"],"depth":2,"refund":0,"opName":"RETURN"} +{"output":"7f6008545060006004557f600160045560006004556000600060006000600060f96000527f5af250600060006000606000527e60f45af4506000600060006000600060f55a6020527ff150f001075205846a44a283446020527f8ca2600060006000600060045af4506040527f519930847f3b631c54a49b5f60035450326040527f77306b60006000600060006060527f6000600c5af150600060006000600060f85af450506060527f066001600255606080527f035450600060005560006001556c3394fff4607f7f1684317b6080527f387b9f60a0527f1920700184809d60015450011899016e6009ff60026001556000527f9f60a05260c0527f7f6000527f9981600160045582600eff600060006000600060f65af45060006060e0527f6060c0527f20527e600060006020527f60f75af4501d7f190316666000600060610100527e6000600060e0527f60046040527f5af1506000600060006040527f60006009610120527f5af4503c95138e5b8f610100527f7f605a6000536060527f6031600153606b60610140527f02536010606060527f0353604560610120527f04536060600553600160608052610160527f7e527f60065360606007536002600853606080610140527f527f556009536060610180527f600a53600160a0527f600b536060600c6020527f53600060610160527f0d53606101a0527f55600e60a0527f536060600f536060c0527f01601053606060115360026101806101c0527f527f601253606040527f55601353606060c0527f60145360e0527f60006015536101e0527f60606101a0527f601653600060175360f360185360196060605260006060e052610200527f610100527f7f806101c0527f5360f3608153608260006000f060006000600060610220527e845af450506000600061016101e0527f20527f60610100527e600060006003610240527f5af15060005450c760006002551309f562610200527f66a486610140527f6b00610260527f1d4571610120527f600054501c641d373c7f60045450610220527f6000600155610280527f600554610160527f50600160025560085450610140527f60006002610240527f6102a0527f557fd86000606000527e600060610180527e600060005af150861217145147356102c0527f610260527f610160527f5198a37e127a7efa7c6000526101a0527f606020527f6102e0527f6060205360610280527ff760215360ff60225360610180527fdb602353603760610300527f6101c0527f24536075606102a0527f2553609f606040527f265360fe60275360610320527f8f60286101a0527f53606101e0527f6102c0527f0b6029536060602a53600060610340527f2b536060602c53606052606060805360006061016102e0527f610200527fc052610360527f7f81536060608253602d6083536053608453606060855360fd610300527f6086610380527f536060610220527f6087536101e0527f602e60885360536089536060608a61036103a0527f20527f53602f608b536060608c610240527f536000608d5360f36102005260606103c0527f610220610340527f53608e610221536053610222536060610260527f610223536103e0527f6000610224536060610360527f61022553608f61022653606061022753600061610400527f02610280527f28536060610229610380527f53600061022a5360f561022b5360610420527f6061022c53600061022d536102a0527f60606103a0527f61022e53600061022f610440527f53606061023053600061023153606061023253600061026103c0527fc0527f61610460527f023353606061023453600061023553608561023653605a61023753606103e052610480527f7ff261026102e0526038610300536053610301536060610302536050610303536104a0527f60610400527f61610304536002610305536039610306536053610307536060616104c0527f030853605061610420527f030953606161030a53600261030b53603a61030c536104e0527f605361030d53606161030e610440527f53600261030f53603b61031053606061610500527f03115360006103125360f3610313536161046052600361048053601461048153610520527f60606104825360006104835360606104845360006104855360f0610486536060610540527f61048753600061048853606061048953600061048a53606061048b5360006104610560527f8c53606061048d53600061048e53608461048f53605a6104905360f4610491536105805260606105a05360506105a15360616105a25360046105a35360926105a45360536105a55360606105a65360506105a75360616105a85360046105a95360936105aa5360536105ab5360616105ac5360046105ad5360946105ae5360606105af5360006105b05360f36105b15360006105b260006000f560006000600060006000855af15050","gasUsed":"0x5d717"} +{"pc":2605,"op":96,"gas":"0x49392","gasCost":"0x3","memSize":2240,"stack":["0x7dce2faf43218578e3fcf2ad22df9918a89e2fba"],"depth":1,"refund":0,"opName":"PUSH1"} +{"pc":2607,"op":96,"gas":"0x4938f","gasCost":"0x3","memSize":2240,"stack":["0x7dce2faf43218578e3fcf2ad22df9918a89e2fba","0x0"],"depth":1,"refund":0,"opName":"PUSH1"} +{"pc":2609,"op":96,"gas":"0x4938c","gasCost":"0x3","memSize":2240,"stack":["0x7dce2faf43218578e3fcf2ad22df9918a89e2fba","0x0","0x0"],"depth":1,"refund":0,"opName":"PUSH1"} +{"pc":2611,"op":96,"gas":"0x49389","gasCost":"0x3","memSize":2240,"stack":["0x7dce2faf43218578e3fcf2ad22df9918a89e2fba","0x0","0x0","0x0"],"depth":1,"refund":0,"opName":"PUSH1"} +{"pc":2613,"op":96,"gas":"0x49386","gasCost":"0x3","memSize":2240,"stack":["0x7dce2faf43218578e3fcf2ad22df9918a89e2fba","0x0","0x0","0x0","0x0"],"depth":1,"refund":0,"opName":"PUSH1"} +{"pc":2615,"op":133,"gas":"0x49383","gasCost":"0x3","memSize":2240,"stack":["0x7dce2faf43218578e3fcf2ad22df9918a89e2fba","0x0","0x0","0x0","0x0","0x0"],"depth":1,"refund":0,"opName":"DUP6"} +{"pc":2616,"op":90,"gas":"0x49380","gasCost":"0x2","memSize":2240,"stack":["0x7dce2faf43218578e3fcf2ad22df9918a89e2fba","0x0","0x0","0x0","0x0","0x0","0x7dce2faf43218578e3fcf2ad22df9918a89e2fba"],"depth":1,"refund":0,"opName":"GAS"} +{"pc":2617,"op":241,"gas":"0x4937e","gasCost":"0x48132","memSize":2240,"stack":["0x7dce2faf43218578e3fcf2ad22df9918a89e2fba","0x0","0x0","0x0","0x0","0x0","0x7dce2faf43218578e3fcf2ad22df9918a89e2fba","0x4937e"],"depth":1,"refund":0,"opName":"CALL"} +{"pc":0,"op":127,"gas":"0x480ce","gasCost":"0x3","memSize":0,"stack":[],"depth":2,"refund":0,"opName":"PUSH32"} +{"pc":33,"op":96,"gas":"0x480cb","gasCost":"0x3","memSize":0,"stack":["0x6008545060006004557f600160045560006004556000600060006000600060f9"],"depth":2,"refund":0,"opName":"PUSH1"} +{"pc":35,"op":82,"gas":"0x480c8","gasCost":"0x6","memSize":0,"stack":["0x6008545060006004557f600160045560006004556000600060006000600060f9","0x0"],"depth":2,"refund":0,"opName":"MSTORE"} +{"pc":36,"op":127,"gas":"0x480c2","gasCost":"0x3","memSize":32,"stack":[],"depth":2,"refund":0,"opName":"PUSH32"} +{"pc":69,"op":96,"gas":"0x480bf","gasCost":"0x3","memSize":32,"stack":["0x5af250600060006000606000527e60f45af4506000600060006000600060f55a"],"depth":2,"refund":0,"opName":"PUSH1"} +{"pc":71,"op":82,"gas":"0x480bc","gasCost":"0x6","memSize":32,"stack":["0x5af250600060006000606000527e60f45af4506000600060006000600060f55a","0x20"],"depth":2,"refund":0,"opName":"MSTORE"} +{"pc":72,"op":127,"gas":"0x480b6","gasCost":"0x3","memSize":64,"stack":[],"depth":2,"refund":0,"opName":"PUSH32"} +{"pc":105,"op":96,"gas":"0x480b3","gasCost":"0x3","memSize":64,"stack":["0xf150f001075205846a44a283446020527f8ca2600060006000600060045af450"],"depth":2,"refund":0,"opName":"PUSH1"} +{"pc":107,"op":82,"gas":"0x480b0","gasCost":"0x6","memSize":64,"stack":["0xf150f001075205846a44a283446020527f8ca2600060006000600060045af450","0x40"],"depth":2,"refund":0,"opName":"MSTORE"} +{"pc":108,"op":127,"gas":"0x480aa","gasCost":"0x3","memSize":96,"stack":[],"depth":2,"refund":0,"opName":"PUSH32"} +{"pc":141,"op":96,"gas":"0x480a7","gasCost":"0x3","memSize":96,"stack":["0x519930847f3b631c54a49b5f60035450326040527f77306b6000600060006000"],"depth":2,"refund":0,"opName":"PUSH1"} +{"pc":143,"op":82,"gas":"0x480a4","gasCost":"0x6","memSize":96,"stack":["0x519930847f3b631c54a49b5f60035450326040527f77306b6000600060006000","0x60"],"depth":2,"refund":0,"opName":"MSTORE"} +{"pc":144,"op":127,"gas":"0x4809e","gasCost":"0x3","memSize":128,"stack":[],"depth":2,"refund":0,"opName":"PUSH32"} +{"pc":177,"op":96,"gas":"0x4809b","gasCost":"0x3","memSize":128,"stack":["0x6000600c5af150600060006000600060f85af450506060527f06600160025560"],"depth":2,"refund":0,"opName":"PUSH1"} +{"pc":179,"op":82,"gas":"0x48098","gasCost":"0x6","memSize":128,"stack":["0x6000600c5af150600060006000600060f85af450506060527f06600160025560","0x80"],"depth":2,"refund":0,"opName":"MSTORE"} +{"pc":180,"op":127,"gas":"0x48092","gasCost":"0x3","memSize":160,"stack":[],"depth":2,"refund":0,"opName":"PUSH32"} +{"pc":213,"op":96,"gas":"0x4808f","gasCost":"0x3","memSize":160,"stack":["0x35450600060005560006001556c3394fff4607f7f1684317b6080527f387b9f"],"depth":2,"refund":0,"opName":"PUSH1"} +{"pc":215,"op":82,"gas":"0x4808c","gasCost":"0x6","memSize":160,"stack":["0x35450600060005560006001556c3394fff4607f7f1684317b6080527f387b9f","0xa0"],"depth":2,"refund":0,"opName":"MSTORE"} +{"pc":216,"op":127,"gas":"0x48086","gasCost":"0x3","memSize":192,"stack":[],"depth":2,"refund":0,"opName":"PUSH32"} +{"pc":249,"op":96,"gas":"0x48083","gasCost":"0x3","memSize":192,"stack":["0x1920700184809d60015450011899016e6009ff60026001556000527f9f60a052"],"depth":2,"refund":0,"opName":"PUSH1"} +{"pc":251,"op":82,"gas":"0x48080","gasCost":"0x6","memSize":192,"stack":["0x1920700184809d60015450011899016e6009ff60026001556000527f9f60a052","0xc0"],"depth":2,"refund":0,"opName":"MSTORE"} +{"pc":252,"op":127,"gas":"0x4807a","gasCost":"0x3","memSize":224,"stack":[],"depth":2,"refund":0,"opName":"PUSH32"} +{"pc":285,"op":96,"gas":"0x48077","gasCost":"0x3","memSize":224,"stack":["0x7f6000527f9981600160045582600eff600060006000600060f65af450600060"],"depth":2,"refund":0,"opName":"PUSH1"} +{"pc":287,"op":82,"gas":"0x48074","gasCost":"0x6","memSize":224,"stack":["0x7f6000527f9981600160045582600eff600060006000600060f65af450600060","0xe0"],"depth":2,"refund":0,"opName":"MSTORE"} +{"pc":288,"op":127,"gas":"0x4806e","gasCost":"0x3","memSize":256,"stack":[],"depth":2,"refund":0,"opName":"PUSH32"} +{"pc":321,"op":97,"gas":"0x4806b","gasCost":"0x3","memSize":256,"stack":["0x6060c0527f20527e600060006020527f60f75af4501d7f190316666000600060"],"depth":2,"refund":0,"opName":"PUSH2"} +{"pc":324,"op":82,"gas":"0x48068","gasCost":"0x6","memSize":256,"stack":["0x6060c0527f20527e600060006020527f60f75af4501d7f190316666000600060","0x100"],"depth":2,"refund":0,"opName":"MSTORE"} +{"pc":325,"op":126,"gas":"0x48062","gasCost":"0x3","memSize":288,"stack":[],"depth":2,"refund":0,"opName":"PUSH31"} +{"pc":357,"op":97,"gas":"0x4805f","gasCost":"0x3","memSize":288,"stack":["0x6000600060e0527f60046040527f5af1506000600060006040527f60006009"],"depth":2,"refund":0,"opName":"PUSH2"} +{"pc":360,"op":82,"gas":"0x4805c","gasCost":"0x6","memSize":288,"stack":["0x6000600060e0527f60046040527f5af1506000600060006040527f60006009","0x120"],"depth":2,"refund":0,"opName":"MSTORE"} +{"pc":361,"op":127,"gas":"0x48056","gasCost":"0x3","memSize":320,"stack":[],"depth":2,"refund":0,"opName":"PUSH32"} +{"pc":394,"op":97,"gas":"0x48053","gasCost":"0x3","memSize":320,"stack":["0x5af4503c95138e5b8f610100527f7f605a6000536060527f6031600153606b60"],"depth":2,"refund":0,"opName":"PUSH2"} +{"pc":397,"op":82,"gas":"0x48050","gasCost":"0x6","memSize":320,"stack":["0x5af4503c95138e5b8f610100527f7f605a6000536060527f6031600153606b60","0x140"],"depth":2,"refund":0,"opName":"MSTORE"} +{"pc":398,"op":127,"gas":"0x4804a","gasCost":"0x3","memSize":352,"stack":[],"depth":2,"refund":0,"opName":"PUSH32"} +{"pc":431,"op":97,"gas":"0x48047","gasCost":"0x3","memSize":352,"stack":["0x2536010606060527f0353604560610120527f04536060600553600160608052"],"depth":2,"refund":0,"opName":"PUSH2"} +{"pc":434,"op":82,"gas":"0x48044","gasCost":"0x6","memSize":352,"stack":["0x2536010606060527f0353604560610120527f04536060600553600160608052","0x160"],"depth":2,"refund":0,"opName":"MSTORE"} +{"pc":435,"op":127,"gas":"0x4803e","gasCost":"0x3","memSize":384,"stack":[],"depth":2,"refund":0,"opName":"PUSH32"} +{"pc":468,"op":97,"gas":"0x4803b","gasCost":"0x3","memSize":384,"stack":["0x7e527f60065360606007536002600853606080610140527f527f556009536060"],"depth":2,"refund":0,"opName":"PUSH2"} +{"pc":471,"op":82,"gas":"0x48038","gasCost":"0x6","memSize":384,"stack":["0x7e527f60065360606007536002600853606080610140527f527f556009536060","0x180"],"depth":2,"refund":0,"opName":"MSTORE"} +{"pc":472,"op":127,"gas":"0x48032","gasCost":"0x3","memSize":416,"stack":[],"depth":2,"refund":0,"opName":"PUSH32"} +{"pc":505,"op":97,"gas":"0x4802f","gasCost":"0x3","memSize":416,"stack":["0x600a53600160a0527f600b536060600c6020527f53600060610160527f0d5360"],"depth":2,"refund":0,"opName":"PUSH2"} +{"pc":508,"op":82,"gas":"0x4802c","gasCost":"0x6","memSize":416,"stack":["0x600a53600160a0527f600b536060600c6020527f53600060610160527f0d5360","0x1a0"],"depth":2,"refund":0,"opName":"MSTORE"} +{"pc":509,"op":127,"gas":"0x48026","gasCost":"0x3","memSize":448,"stack":[],"depth":2,"refund":0,"opName":"PUSH32"} +{"pc":542,"op":97,"gas":"0x48023","gasCost":"0x3","memSize":448,"stack":["0x55600e60a0527f536060600f536060c0527f0160105360606011536002610180"],"depth":2,"refund":0,"opName":"PUSH2"} +{"pc":545,"op":82,"gas":"0x48020","gasCost":"0x6","memSize":448,"stack":["0x55600e60a0527f536060600f536060c0527f0160105360606011536002610180","0x1c0"],"depth":2,"refund":0,"opName":"MSTORE"} +{"pc":546,"op":127,"gas":"0x4801a","gasCost":"0x3","memSize":480,"stack":[],"depth":2,"refund":0,"opName":"PUSH32"} +{"pc":579,"op":97,"gas":"0x48017","gasCost":"0x3","memSize":480,"stack":["0x527f601253606040527f55601353606060c0527f60145360e0527f6000601553"],"depth":2,"refund":0,"opName":"PUSH2"} +{"pc":582,"op":82,"gas":"0x48014","gasCost":"0x6","memSize":480,"stack":["0x527f601253606040527f55601353606060c0527f60145360e0527f6000601553","0x1e0"],"depth":2,"refund":0,"opName":"MSTORE"} +{"pc":583,"op":127,"gas":"0x4800e","gasCost":"0x3","memSize":512,"stack":[],"depth":2,"refund":0,"opName":"PUSH32"} +{"pc":616,"op":97,"gas":"0x4800b","gasCost":"0x3","memSize":512,"stack":["0x60606101a0527f601653600060175360f360185360196060605260006060e052"],"depth":2,"refund":0,"opName":"PUSH2"} +{"pc":619,"op":82,"gas":"0x48008","gasCost":"0x6","memSize":512,"stack":["0x60606101a0527f601653600060175360f360185360196060605260006060e052","0x200"],"depth":2,"refund":0,"opName":"MSTORE"} +{"pc":620,"op":127,"gas":"0x48002","gasCost":"0x3","memSize":544,"stack":[],"depth":2,"refund":0,"opName":"PUSH32"} +{"pc":653,"op":97,"gas":"0x47fff","gasCost":"0x3","memSize":544,"stack":["0x610100527f7f806101c0527f5360f3608153608260006000f060006000600060"],"depth":2,"refund":0,"opName":"PUSH2"} +{"pc":656,"op":82,"gas":"0x47ffc","gasCost":"0x6","memSize":544,"stack":["0x610100527f7f806101c0527f5360f3608153608260006000f060006000600060","0x220"],"depth":2,"refund":0,"opName":"MSTORE"} +{"pc":657,"op":126,"gas":"0x47ff6","gasCost":"0x3","memSize":576,"stack":[],"depth":2,"refund":0,"opName":"PUSH31"} +{"pc":689,"op":97,"gas":"0x47ff3","gasCost":"0x3","memSize":576,"stack":["0x845af450506000600061016101e0527f20527f60610100527e600060006003"],"depth":2,"refund":0,"opName":"PUSH2"} +{"pc":692,"op":82,"gas":"0x47ff0","gasCost":"0x6","memSize":576,"stack":["0x845af450506000600061016101e0527f20527f60610100527e600060006003","0x240"],"depth":2,"refund":0,"opName":"MSTORE"} +{"pc":693,"op":127,"gas":"0x47fea","gasCost":"0x3","memSize":608,"stack":[],"depth":2,"refund":0,"opName":"PUSH32"} +{"pc":726,"op":97,"gas":"0x47fe7","gasCost":"0x3","memSize":608,"stack":["0x5af15060005450c760006002551309f562610200527f66a486610140527f6b00"],"depth":2,"refund":0,"opName":"PUSH2"} +{"pc":729,"op":82,"gas":"0x47fe4","gasCost":"0x6","memSize":608,"stack":["0x5af15060005450c760006002551309f562610200527f66a486610140527f6b00","0x260"],"depth":2,"refund":0,"opName":"MSTORE"} +{"pc":730,"op":127,"gas":"0x47fde","gasCost":"0x3","memSize":640,"stack":[],"depth":2,"refund":0,"opName":"PUSH32"} +{"pc":763,"op":97,"gas":"0x47fdb","gasCost":"0x3","memSize":640,"stack":["0x1d4571610120527f600054501c641d373c7f60045450610220527f6000600155"],"depth":2,"refund":0,"opName":"PUSH2"} +{"pc":766,"op":82,"gas":"0x47fd8","gasCost":"0x6","memSize":640,"stack":["0x1d4571610120527f600054501c641d373c7f60045450610220527f6000600155","0x280"],"depth":2,"refund":0,"opName":"MSTORE"} +{"pc":767,"op":127,"gas":"0x47fd2","gasCost":"0x3","memSize":672,"stack":[],"depth":2,"refund":0,"opName":"PUSH32"} +{"pc":800,"op":97,"gas":"0x47fcf","gasCost":"0x3","memSize":672,"stack":["0x600554610160527f50600160025560085450610140527f60006002610240527f"],"depth":2,"refund":0,"opName":"PUSH2"} +{"pc":803,"op":82,"gas":"0x47fcc","gasCost":"0x6","memSize":672,"stack":["0x600554610160527f50600160025560085450610140527f60006002610240527f","0x2a0"],"depth":2,"refund":0,"opName":"MSTORE"} +{"pc":804,"op":127,"gas":"0x47fc6","gasCost":"0x3","memSize":704,"stack":[],"depth":2,"refund":0,"opName":"PUSH32"} +{"pc":837,"op":97,"gas":"0x47fc3","gasCost":"0x3","memSize":704,"stack":["0x557fd86000606000527e600060610180527e600060005af15086121714514735"],"depth":2,"refund":0,"opName":"PUSH2"} +{"pc":840,"op":82,"gas":"0x47fc0","gasCost":"0x7","memSize":704,"stack":["0x557fd86000606000527e600060610180527e600060005af15086121714514735","0x2c0"],"depth":2,"refund":0,"opName":"MSTORE"} +{"pc":841,"op":127,"gas":"0x47fb9","gasCost":"0x3","memSize":736,"stack":[],"depth":2,"refund":0,"opName":"PUSH32"} +{"pc":874,"op":97,"gas":"0x47fb6","gasCost":"0x3","memSize":736,"stack":["0x610260527f610160527f5198a37e127a7efa7c6000526101a0527f606020527f"],"depth":2,"refund":0,"opName":"PUSH2"} +{"pc":877,"op":82,"gas":"0x47fb3","gasCost":"0x6","memSize":736,"stack":["0x610260527f610160527f5198a37e127a7efa7c6000526101a0527f606020527f","0x2e0"],"depth":2,"refund":0,"opName":"MSTORE"} +{"pc":878,"op":127,"gas":"0x47fad","gasCost":"0x3","memSize":768,"stack":[],"depth":2,"refund":0,"opName":"PUSH32"} +{"pc":911,"op":97,"gas":"0x47faa","gasCost":"0x3","memSize":768,"stack":["0x6060205360610280527ff760215360ff60225360610180527fdb602353603760"],"depth":2,"refund":0,"opName":"PUSH2"} +{"pc":914,"op":82,"gas":"0x47fa7","gasCost":"0x6","memSize":768,"stack":["0x6060205360610280527ff760215360ff60225360610180527fdb602353603760","0x300"],"depth":2,"refund":0,"opName":"MSTORE"} +{"pc":915,"op":127,"gas":"0x47fa1","gasCost":"0x3","memSize":800,"stack":[],"depth":2,"refund":0,"opName":"PUSH32"} +{"pc":948,"op":97,"gas":"0x47f9e","gasCost":"0x3","memSize":800,"stack":["0x6101c0527f24536075606102a0527f2553609f606040527f265360fe60275360"],"depth":2,"refund":0,"opName":"PUSH2"} +{"pc":951,"op":82,"gas":"0x47f9b","gasCost":"0x6","memSize":800,"stack":["0x6101c0527f24536075606102a0527f2553609f606040527f265360fe60275360","0x320"],"depth":2,"refund":0,"opName":"MSTORE"} +{"pc":952,"op":127,"gas":"0x47f95","gasCost":"0x3","memSize":832,"stack":[],"depth":2,"refund":0,"opName":"PUSH32"} +{"pc":985,"op":97,"gas":"0x47f92","gasCost":"0x3","memSize":832,"stack":["0x8f60286101a0527f53606101e0527f6102c0527f0b6029536060602a53600060"],"depth":2,"refund":0,"opName":"PUSH2"} +{"pc":988,"op":82,"gas":"0x47f8f","gasCost":"0x6","memSize":832,"stack":["0x8f60286101a0527f53606101e0527f6102c0527f0b6029536060602a53600060","0x340"],"depth":2,"refund":0,"opName":"MSTORE"} +{"pc":989,"op":127,"gas":"0x47f89","gasCost":"0x3","memSize":864,"stack":[],"depth":2,"refund":0,"opName":"PUSH32"} +{"pc":1022,"op":97,"gas":"0x47f86","gasCost":"0x3","memSize":864,"stack":["0x2b536060602c53606052606060805360006061016102e0527f610200527fc052"],"depth":2,"refund":0,"opName":"PUSH2"} +{"pc":1025,"op":82,"gas":"0x47f83","gasCost":"0x6","memSize":864,"stack":["0x2b536060602c53606052606060805360006061016102e0527f610200527fc052","0x360"],"depth":2,"refund":0,"opName":"MSTORE"} +{"pc":1026,"op":127,"gas":"0x47f7d","gasCost":"0x3","memSize":896,"stack":[],"depth":2,"refund":0,"opName":"PUSH32"} +{"pc":1059,"op":97,"gas":"0x47f7a","gasCost":"0x3","memSize":896,"stack":["0x7f81536060608253602d6083536053608453606060855360fd610300527f6086"],"depth":2,"refund":0,"opName":"PUSH2"} +{"pc":1062,"op":82,"gas":"0x47f77","gasCost":"0x6","memSize":896,"stack":["0x7f81536060608253602d6083536053608453606060855360fd610300527f6086","0x380"],"depth":2,"refund":0,"opName":"MSTORE"} +{"pc":1063,"op":127,"gas":"0x47f71","gasCost":"0x3","memSize":928,"stack":[],"depth":2,"refund":0,"opName":"PUSH32"} +{"pc":1096,"op":97,"gas":"0x47f6e","gasCost":"0x3","memSize":928,"stack":["0x536060610220527f6087536101e0527f602e60885360536089536060608a6103"],"depth":2,"refund":0,"opName":"PUSH2"} +{"pc":1099,"op":82,"gas":"0x47f6b","gasCost":"0x6","memSize":928,"stack":["0x536060610220527f6087536101e0527f602e60885360536089536060608a6103","0x3a0"],"depth":2,"refund":0,"opName":"MSTORE"} +{"pc":1100,"op":127,"gas":"0x47f65","gasCost":"0x3","memSize":960,"stack":[],"depth":2,"refund":0,"opName":"PUSH32"} +{"pc":1133,"op":97,"gas":"0x47f62","gasCost":"0x3","memSize":960,"stack":["0x20527f53602f608b536060608c610240527f536000608d5360f3610200526060"],"depth":2,"refund":0,"opName":"PUSH2"} +{"pc":1136,"op":82,"gas":"0x47f5f","gasCost":"0x6","memSize":960,"stack":["0x20527f53602f608b536060608c610240527f536000608d5360f3610200526060","0x3c0"],"depth":2,"refund":0,"opName":"MSTORE"} +{"pc":1137,"op":127,"gas":"0x47f59","gasCost":"0x3","memSize":992,"stack":[],"depth":2,"refund":0,"opName":"PUSH32"} +{"pc":1170,"op":97,"gas":"0x47f56","gasCost":"0x3","memSize":992,"stack":["0x610220610340527f53608e610221536053610222536060610260527f61022353"],"depth":2,"refund":0,"opName":"PUSH2"} +{"pc":1173,"op":82,"gas":"0x47f53","gasCost":"0x7","memSize":992,"stack":["0x610220610340527f53608e610221536053610222536060610260527f61022353","0x3e0"],"depth":2,"refund":0,"opName":"MSTORE"} +{"pc":1174,"op":127,"gas":"0x47f4c","gasCost":"0x3","memSize":1024,"stack":[],"depth":2,"refund":0,"opName":"PUSH32"} +{"pc":1207,"op":97,"gas":"0x47f49","gasCost":"0x3","memSize":1024,"stack":["0x6000610224536060610360527f61022553608f61022653606061022753600061"],"depth":2,"refund":0,"opName":"PUSH2"} +{"pc":1210,"op":82,"gas":"0x47f46","gasCost":"0x6","memSize":1024,"stack":["0x6000610224536060610360527f61022553608f61022653606061022753600061","0x400"],"depth":2,"refund":0,"opName":"MSTORE"} +{"pc":1211,"op":127,"gas":"0x47f40","gasCost":"0x3","memSize":1056,"stack":[],"depth":2,"refund":0,"opName":"PUSH32"} +{"pc":1244,"op":97,"gas":"0x47f3d","gasCost":"0x3","memSize":1056,"stack":["0x2610280527f28536060610229610380527f53600061022a5360f561022b5360"],"depth":2,"refund":0,"opName":"PUSH2"} +{"pc":1247,"op":82,"gas":"0x47f3a","gasCost":"0x6","memSize":1056,"stack":["0x2610280527f28536060610229610380527f53600061022a5360f561022b5360","0x420"],"depth":2,"refund":0,"opName":"MSTORE"} +{"pc":1248,"op":127,"gas":"0x47f34","gasCost":"0x3","memSize":1088,"stack":[],"depth":2,"refund":0,"opName":"PUSH32"} +{"pc":1281,"op":97,"gas":"0x47f31","gasCost":"0x3","memSize":1088,"stack":["0x6061022c53600061022d536102a0527f60606103a0527f61022e53600061022f"],"depth":2,"refund":0,"opName":"PUSH2"} +{"pc":1284,"op":82,"gas":"0x47f2e","gasCost":"0x6","memSize":1088,"stack":["0x6061022c53600061022d536102a0527f60606103a0527f61022e53600061022f","0x440"],"depth":2,"refund":0,"opName":"MSTORE"} +{"pc":1285,"op":127,"gas":"0x47f28","gasCost":"0x3","memSize":1120,"stack":[],"depth":2,"refund":0,"opName":"PUSH32"} +{"pc":1318,"op":97,"gas":"0x47f25","gasCost":"0x3","memSize":1120,"stack":["0x53606061023053600061023153606061023253600061026103c0527fc0527f61"],"depth":2,"refund":0,"opName":"PUSH2"} +{"pc":1321,"op":82,"gas":"0x47f22","gasCost":"0x6","memSize":1120,"stack":["0x53606061023053600061023153606061023253600061026103c0527fc0527f61","0x460"],"depth":2,"refund":0,"opName":"MSTORE"} +{"pc":1322,"op":127,"gas":"0x47f1c","gasCost":"0x3","memSize":1152,"stack":[],"depth":2,"refund":0,"opName":"PUSH32"} +{"pc":1355,"op":97,"gas":"0x47f19","gasCost":"0x3","memSize":1152,"stack":["0x23353606061023453600061023553608561023653605a61023753606103e052"],"depth":2,"refund":0,"opName":"PUSH2"} +{"pc":1358,"op":82,"gas":"0x47f16","gasCost":"0x6","memSize":1152,"stack":["0x23353606061023453600061023553608561023653605a61023753606103e052","0x480"],"depth":2,"refund":0,"opName":"MSTORE"} +{"pc":1359,"op":127,"gas":"0x47f10","gasCost":"0x3","memSize":1184,"stack":[],"depth":2,"refund":0,"opName":"PUSH32"} +{"pc":1392,"op":97,"gas":"0x47f0d","gasCost":"0x3","memSize":1184,"stack":["0x7ff261026102e052603861030053605361030153606061030253605061030353"],"depth":2,"refund":0,"opName":"PUSH2"} +{"pc":1395,"op":82,"gas":"0x47f0a","gasCost":"0x6","memSize":1184,"stack":["0x7ff261026102e052603861030053605361030153606061030253605061030353","0x4a0"],"depth":2,"refund":0,"opName":"MSTORE"} +{"pc":1396,"op":127,"gas":"0x47f04","gasCost":"0x3","memSize":1216,"stack":[],"depth":2,"refund":0,"opName":"PUSH32"} +{"pc":1429,"op":97,"gas":"0x47f01","gasCost":"0x3","memSize":1216,"stack":["0x60610400527f6161030453600261030553603961030653605361030753606061"],"depth":2,"refund":0,"opName":"PUSH2"} +{"pc":1432,"op":82,"gas":"0x47efe","gasCost":"0x6","memSize":1216,"stack":["0x60610400527f6161030453600261030553603961030653605361030753606061","0x4c0"],"depth":2,"refund":0,"opName":"MSTORE"} +{"pc":1433,"op":127,"gas":"0x47ef8","gasCost":"0x3","memSize":1248,"stack":[],"depth":2,"refund":0,"opName":"PUSH32"} +{"pc":1466,"op":97,"gas":"0x47ef5","gasCost":"0x3","memSize":1248,"stack":["0x30853605061610420527f030953606161030a53600261030b53603a61030c53"],"depth":2,"refund":0,"opName":"PUSH2"} +{"pc":1469,"op":82,"gas":"0x47ef2","gasCost":"0x7","memSize":1248,"stack":["0x30853605061610420527f030953606161030a53600261030b53603a61030c53","0x4e0"],"depth":2,"refund":0,"opName":"MSTORE"} +{"pc":1470,"op":127,"gas":"0x47eeb","gasCost":"0x3","memSize":1280,"stack":[],"depth":2,"refund":0,"opName":"PUSH32"} +{"pc":1503,"op":97,"gas":"0x47ee8","gasCost":"0x3","memSize":1280,"stack":["0x605361030d53606161030e610440527f53600261030f53603b61031053606061"],"depth":2,"refund":0,"opName":"PUSH2"} +{"pc":1506,"op":82,"gas":"0x47ee5","gasCost":"0x6","memSize":1280,"stack":["0x605361030d53606161030e610440527f53600261030f53603b61031053606061","0x500"],"depth":2,"refund":0,"opName":"MSTORE"} +{"pc":1507,"op":127,"gas":"0x47edf","gasCost":"0x3","memSize":1312,"stack":[],"depth":2,"refund":0,"opName":"PUSH32"} +{"pc":1540,"op":97,"gas":"0x47edc","gasCost":"0x3","memSize":1312,"stack":["0x3115360006103125360f3610313536161046052600361048053601461048153"],"depth":2,"refund":0,"opName":"PUSH2"} +{"pc":1543,"op":82,"gas":"0x47ed9","gasCost":"0x6","memSize":1312,"stack":["0x3115360006103125360f3610313536161046052600361048053601461048153","0x520"],"depth":2,"refund":0,"opName":"MSTORE"} +{"pc":1544,"op":127,"gas":"0x47ed3","gasCost":"0x3","memSize":1344,"stack":[],"depth":2,"refund":0,"opName":"PUSH32"} +{"pc":1577,"op":97,"gas":"0x47ed0","gasCost":"0x3","memSize":1344,"stack":["0x60606104825360006104835360606104845360006104855360f0610486536060"],"depth":2,"refund":0,"opName":"PUSH2"} +{"pc":1580,"op":82,"gas":"0x47ecd","gasCost":"0x6","memSize":1344,"stack":["0x60606104825360006104835360606104845360006104855360f0610486536060","0x540"],"depth":2,"refund":0,"opName":"MSTORE"} +{"pc":1581,"op":127,"gas":"0x47ec7","gasCost":"0x3","memSize":1376,"stack":[],"depth":2,"refund":0,"opName":"PUSH32"} +{"pc":1614,"op":97,"gas":"0x47ec4","gasCost":"0x3","memSize":1376,"stack":["0x61048753600061048853606061048953600061048a53606061048b5360006104"],"depth":2,"refund":0,"opName":"PUSH2"} +{"pc":1617,"op":82,"gas":"0x47ec1","gasCost":"0x6","memSize":1376,"stack":["0x61048753600061048853606061048953600061048a53606061048b5360006104","0x560"],"depth":2,"refund":0,"opName":"MSTORE"} +{"pc":1618,"op":127,"gas":"0x47ebb","gasCost":"0x3","memSize":1408,"stack":[],"depth":2,"refund":0,"opName":"PUSH32"} +{"pc":1651,"op":97,"gas":"0x47eb8","gasCost":"0x3","memSize":1408,"stack":["0x8c53606061048d53600061048e53608461048f53605a6104905360f461049153"],"depth":2,"refund":0,"opName":"PUSH2"} +{"pc":1654,"op":82,"gas":"0x47eb5","gasCost":"0x6","memSize":1408,"stack":["0x8c53606061048d53600061048e53608461048f53605a6104905360f461049153","0x580"],"depth":2,"refund":0,"opName":"MSTORE"} +{"pc":1655,"op":96,"gas":"0x47eaf","gasCost":"0x3","memSize":1440,"stack":[],"depth":2,"refund":0,"opName":"PUSH1"} +{"pc":1657,"op":97,"gas":"0x47eac","gasCost":"0x3","memSize":1440,"stack":["0x60"],"depth":2,"refund":0,"opName":"PUSH2"} +{"pc":1660,"op":83,"gas":"0x47ea9","gasCost":"0x7","memSize":1440,"stack":["0x60","0x5a0"],"depth":2,"refund":0,"opName":"MSTORE8"} +{"pc":1661,"op":96,"gas":"0x47ea2","gasCost":"0x3","memSize":1472,"stack":[],"depth":2,"refund":0,"opName":"PUSH1"} +{"pc":1663,"op":97,"gas":"0x47e9f","gasCost":"0x3","memSize":1472,"stack":["0x50"],"depth":2,"refund":0,"opName":"PUSH2"} +{"pc":1666,"op":83,"gas":"0x47e9c","gasCost":"0x3","memSize":1472,"stack":["0x50","0x5a1"],"depth":2,"refund":0,"opName":"MSTORE8"} +{"pc":1667,"op":96,"gas":"0x47e99","gasCost":"0x3","memSize":1472,"stack":[],"depth":2,"refund":0,"opName":"PUSH1"} +{"pc":1669,"op":97,"gas":"0x47e96","gasCost":"0x3","memSize":1472,"stack":["0x61"],"depth":2,"refund":0,"opName":"PUSH2"} +{"pc":1672,"op":83,"gas":"0x47e93","gasCost":"0x3","memSize":1472,"stack":["0x61","0x5a2"],"depth":2,"refund":0,"opName":"MSTORE8"} +{"pc":1673,"op":96,"gas":"0x47e90","gasCost":"0x3","memSize":1472,"stack":[],"depth":2,"refund":0,"opName":"PUSH1"} +{"pc":1675,"op":97,"gas":"0x47e8d","gasCost":"0x3","memSize":1472,"stack":["0x4"],"depth":2,"refund":0,"opName":"PUSH2"} +{"pc":1678,"op":83,"gas":"0x47e8a","gasCost":"0x3","memSize":1472,"stack":["0x4","0x5a3"],"depth":2,"refund":0,"opName":"MSTORE8"} +{"pc":1679,"op":96,"gas":"0x47e87","gasCost":"0x3","memSize":1472,"stack":[],"depth":2,"refund":0,"opName":"PUSH1"} +{"pc":1681,"op":97,"gas":"0x47e84","gasCost":"0x3","memSize":1472,"stack":["0x92"],"depth":2,"refund":0,"opName":"PUSH2"} +{"pc":1684,"op":83,"gas":"0x47e81","gasCost":"0x3","memSize":1472,"stack":["0x92","0x5a4"],"depth":2,"refund":0,"opName":"MSTORE8"} +{"pc":1685,"op":96,"gas":"0x47e7e","gasCost":"0x3","memSize":1472,"stack":[],"depth":2,"refund":0,"opName":"PUSH1"} +{"pc":1687,"op":97,"gas":"0x47e7b","gasCost":"0x3","memSize":1472,"stack":["0x53"],"depth":2,"refund":0,"opName":"PUSH2"} +{"pc":1690,"op":83,"gas":"0x47e78","gasCost":"0x3","memSize":1472,"stack":["0x53","0x5a5"],"depth":2,"refund":0,"opName":"MSTORE8"} +{"pc":1691,"op":96,"gas":"0x47e75","gasCost":"0x3","memSize":1472,"stack":[],"depth":2,"refund":0,"opName":"PUSH1"} +{"pc":1693,"op":97,"gas":"0x47e72","gasCost":"0x3","memSize":1472,"stack":["0x60"],"depth":2,"refund":0,"opName":"PUSH2"} +{"pc":1696,"op":83,"gas":"0x47e6f","gasCost":"0x3","memSize":1472,"stack":["0x60","0x5a6"],"depth":2,"refund":0,"opName":"MSTORE8"} +{"pc":1697,"op":96,"gas":"0x47e6c","gasCost":"0x3","memSize":1472,"stack":[],"depth":2,"refund":0,"opName":"PUSH1"} +{"pc":1699,"op":97,"gas":"0x47e69","gasCost":"0x3","memSize":1472,"stack":["0x50"],"depth":2,"refund":0,"opName":"PUSH2"} +{"pc":1702,"op":83,"gas":"0x47e66","gasCost":"0x3","memSize":1472,"stack":["0x50","0x5a7"],"depth":2,"refund":0,"opName":"MSTORE8"} +{"pc":1703,"op":96,"gas":"0x47e63","gasCost":"0x3","memSize":1472,"stack":[],"depth":2,"refund":0,"opName":"PUSH1"} +{"pc":1705,"op":97,"gas":"0x47e60","gasCost":"0x3","memSize":1472,"stack":["0x61"],"depth":2,"refund":0,"opName":"PUSH2"} +{"pc":1708,"op":83,"gas":"0x47e5d","gasCost":"0x3","memSize":1472,"stack":["0x61","0x5a8"],"depth":2,"refund":0,"opName":"MSTORE8"} +{"pc":1709,"op":96,"gas":"0x47e5a","gasCost":"0x3","memSize":1472,"stack":[],"depth":2,"refund":0,"opName":"PUSH1"} +{"pc":1711,"op":97,"gas":"0x47e57","gasCost":"0x3","memSize":1472,"stack":["0x4"],"depth":2,"refund":0,"opName":"PUSH2"} +{"pc":1714,"op":83,"gas":"0x47e54","gasCost":"0x3","memSize":1472,"stack":["0x4","0x5a9"],"depth":2,"refund":0,"opName":"MSTORE8"} +{"pc":1715,"op":96,"gas":"0x47e51","gasCost":"0x3","memSize":1472,"stack":[],"depth":2,"refund":0,"opName":"PUSH1"} +{"pc":1717,"op":97,"gas":"0x47e4e","gasCost":"0x3","memSize":1472,"stack":["0x93"],"depth":2,"refund":0,"opName":"PUSH2"} +{"pc":1720,"op":83,"gas":"0x47e4b","gasCost":"0x3","memSize":1472,"stack":["0x93","0x5aa"],"depth":2,"refund":0,"opName":"MSTORE8"} +{"pc":1721,"op":96,"gas":"0x47e48","gasCost":"0x3","memSize":1472,"stack":[],"depth":2,"refund":0,"opName":"PUSH1"} +{"pc":1723,"op":97,"gas":"0x47e45","gasCost":"0x3","memSize":1472,"stack":["0x53"],"depth":2,"refund":0,"opName":"PUSH2"} +{"pc":1726,"op":83,"gas":"0x47e42","gasCost":"0x3","memSize":1472,"stack":["0x53","0x5ab"],"depth":2,"refund":0,"opName":"MSTORE8"} +{"pc":1727,"op":96,"gas":"0x47e3f","gasCost":"0x3","memSize":1472,"stack":[],"depth":2,"refund":0,"opName":"PUSH1"} +{"pc":1729,"op":97,"gas":"0x47e3c","gasCost":"0x3","memSize":1472,"stack":["0x61"],"depth":2,"refund":0,"opName":"PUSH2"} +{"pc":1732,"op":83,"gas":"0x47e39","gasCost":"0x3","memSize":1472,"stack":["0x61","0x5ac"],"depth":2,"refund":0,"opName":"MSTORE8"} +{"pc":1733,"op":96,"gas":"0x47e36","gasCost":"0x3","memSize":1472,"stack":[],"depth":2,"refund":0,"opName":"PUSH1"} +{"pc":1735,"op":97,"gas":"0x47e33","gasCost":"0x3","memSize":1472,"stack":["0x4"],"depth":2,"refund":0,"opName":"PUSH2"} +{"pc":1738,"op":83,"gas":"0x47e30","gasCost":"0x3","memSize":1472,"stack":["0x4","0x5ad"],"depth":2,"refund":0,"opName":"MSTORE8"} +{"pc":1739,"op":96,"gas":"0x47e2d","gasCost":"0x3","memSize":1472,"stack":[],"depth":2,"refund":0,"opName":"PUSH1"} +{"pc":1741,"op":97,"gas":"0x47e2a","gasCost":"0x3","memSize":1472,"stack":["0x94"],"depth":2,"refund":0,"opName":"PUSH2"} +{"pc":1744,"op":83,"gas":"0x47e27","gasCost":"0x3","memSize":1472,"stack":["0x94","0x5ae"],"depth":2,"refund":0,"opName":"MSTORE8"} +{"pc":1745,"op":96,"gas":"0x47e24","gasCost":"0x3","memSize":1472,"stack":[],"depth":2,"refund":0,"opName":"PUSH1"} +{"pc":1747,"op":97,"gas":"0x47e21","gasCost":"0x3","memSize":1472,"stack":["0x60"],"depth":2,"refund":0,"opName":"PUSH2"} +{"pc":1750,"op":83,"gas":"0x47e1e","gasCost":"0x3","memSize":1472,"stack":["0x60","0x5af"],"depth":2,"refund":0,"opName":"MSTORE8"} +{"pc":1751,"op":96,"gas":"0x47e1b","gasCost":"0x3","memSize":1472,"stack":[],"depth":2,"refund":0,"opName":"PUSH1"} +{"pc":1753,"op":97,"gas":"0x47e18","gasCost":"0x3","memSize":1472,"stack":["0x0"],"depth":2,"refund":0,"opName":"PUSH2"} +{"pc":1756,"op":83,"gas":"0x47e15","gasCost":"0x3","memSize":1472,"stack":["0x0","0x5b0"],"depth":2,"refund":0,"opName":"MSTORE8"} +{"pc":1757,"op":96,"gas":"0x47e12","gasCost":"0x3","memSize":1472,"stack":[],"depth":2,"refund":0,"opName":"PUSH1"} +{"pc":1759,"op":97,"gas":"0x47e0f","gasCost":"0x3","memSize":1472,"stack":["0xf3"],"depth":2,"refund":0,"opName":"PUSH2"} +{"pc":1762,"op":83,"gas":"0x47e0c","gasCost":"0x3","memSize":1472,"stack":["0xf3","0x5b1"],"depth":2,"refund":0,"opName":"MSTORE8"} +{"pc":1763,"op":96,"gas":"0x47e09","gasCost":"0x3","memSize":1472,"stack":[],"depth":2,"refund":0,"opName":"PUSH1"} +{"pc":1765,"op":97,"gas":"0x47e06","gasCost":"0x3","memSize":1472,"stack":["0x0"],"depth":2,"refund":0,"opName":"PUSH2"} +{"pc":1768,"op":96,"gas":"0x47e03","gasCost":"0x3","memSize":1472,"stack":["0x0","0x5b2"],"depth":2,"refund":0,"opName":"PUSH1"} +{"pc":1770,"op":96,"gas":"0x47e00","gasCost":"0x3","memSize":1472,"stack":["0x0","0x5b2","0x0"],"depth":2,"refund":0,"opName":"PUSH1"} +{"pc":1772,"op":245,"gas":"0x47dfd","gasCost":"0x7e14","memSize":1472,"stack":["0x0","0x5b2","0x0","0x0"],"depth":2,"refund":0,"opName":"CREATE2"} +{"pc":0,"op":96,"gas":"0x3efea","gasCost":"0x3","memSize":0,"stack":[],"depth":3,"refund":0,"opName":"PUSH1"} +{"pc":2,"op":84,"gas":"0x3efe7","gasCost":"0x834","memSize":0,"stack":["0x8"],"depth":3,"refund":0,"opName":"SLOAD"} +{"pc":3,"op":80,"gas":"0x3e7b3","gasCost":"0x2","memSize":0,"stack":["0x0"],"depth":3,"refund":0,"opName":"POP"} +{"pc":4,"op":96,"gas":"0x3e7b1","gasCost":"0x3","memSize":0,"stack":[],"depth":3,"refund":0,"opName":"PUSH1"} +{"pc":6,"op":96,"gas":"0x3e7ae","gasCost":"0x3","memSize":0,"stack":["0x0"],"depth":3,"refund":0,"opName":"PUSH1"} +{"pc":8,"op":85,"gas":"0x3e7ab","gasCost":"0x898","memSize":0,"stack":["0x0","0x4"],"depth":3,"refund":0,"opName":"SSTORE"} +{"pc":9,"op":127,"gas":"0x3df13","gasCost":"0x3","memSize":0,"stack":[],"depth":3,"refund":0,"opName":"PUSH32"} +{"pc":42,"op":96,"gas":"0x3df10","gasCost":"0x3","memSize":0,"stack":["0x600160045560006004556000600060006000600060f95af25060006000600060"],"depth":3,"refund":0,"opName":"PUSH1"} +{"pc":44,"op":82,"gas":"0x3df0d","gasCost":"0x6","memSize":0,"stack":["0x600160045560006004556000600060006000600060f95af25060006000600060","0x0"],"depth":3,"refund":0,"opName":"MSTORE"} +{"pc":45,"op":126,"gas":"0x3df07","gasCost":"0x3","memSize":32,"stack":[],"depth":3,"refund":0,"opName":"PUSH31"} +{"pc":77,"op":96,"gas":"0x3df04","gasCost":"0x3","memSize":32,"stack":["0x60f45af4506000600060006000600060f55af150f001075205846a44a28344"],"depth":3,"refund":0,"opName":"PUSH1"} +{"pc":79,"op":82,"gas":"0x3df01","gasCost":"0x6","memSize":32,"stack":["0x60f45af4506000600060006000600060f55af150f001075205846a44a28344","0x20"],"depth":3,"refund":0,"opName":"MSTORE"} +{"pc":80,"op":127,"gas":"0x3defb","gasCost":"0x3","memSize":64,"stack":[],"depth":3,"refund":0,"opName":"PUSH32"} +{"pc":113,"op":96,"gas":"0x3def8","gasCost":"0x3","memSize":64,"stack":["0x8ca2600060006000600060045af450519930847f3b631c54a49b5f6003545032"],"depth":3,"refund":0,"opName":"PUSH1"} +{"pc":115,"op":82,"gas":"0x3def5","gasCost":"0x6","memSize":64,"stack":["0x8ca2600060006000600060045af450519930847f3b631c54a49b5f6003545032","0x40"],"depth":3,"refund":0,"opName":"MSTORE"} +{"pc":116,"op":127,"gas":"0x3deef","gasCost":"0x3","memSize":96,"stack":[],"depth":3,"refund":0,"opName":"PUSH32"} +{"pc":149,"op":96,"gas":"0x3deec","gasCost":"0x3","memSize":96,"stack":["0x77306b60006000600060006000600c5af150600060006000600060f85af45050"],"depth":3,"refund":0,"opName":"PUSH1"} +{"pc":151,"op":82,"gas":"0x3dee9","gasCost":"0x6","memSize":96,"stack":["0x77306b60006000600060006000600c5af150600060006000600060f85af45050","0x60"],"depth":3,"refund":0,"opName":"MSTORE"} +{"pc":152,"op":127,"gas":"0x3dee3","gasCost":"0x3","memSize":128,"stack":[],"depth":3,"refund":0,"opName":"PUSH32"} +{"pc":185,"op":96,"gas":"0x3dee0","gasCost":"0x3","memSize":128,"stack":["0x6600160025560035450600060005560006001556c3394fff4607f7f1684317b"],"depth":3,"refund":0,"opName":"PUSH1"} +{"pc":187,"op":82,"gas":"0x3dedd","gasCost":"0x6","memSize":128,"stack":["0x6600160025560035450600060005560006001556c3394fff4607f7f1684317b","0x80"],"depth":3,"refund":0,"opName":"MSTORE"} +{"pc":188,"op":127,"gas":"0x3ded7","gasCost":"0x3","memSize":160,"stack":[],"depth":3,"refund":0,"opName":"PUSH32"} +{"pc":221,"op":96,"gas":"0x3ded4","gasCost":"0x3","memSize":160,"stack":["0x387b9f1920700184809d60015450011899016e6009ff60026001556000527f9f"],"depth":3,"refund":0,"opName":"PUSH1"} +{"pc":223,"op":82,"gas":"0x3ded1","gasCost":"0x6","memSize":160,"stack":["0x387b9f1920700184809d60015450011899016e6009ff60026001556000527f9f","0xa0"],"depth":3,"refund":0,"opName":"MSTORE"} +{"pc":224,"op":127,"gas":"0x3decb","gasCost":"0x3","memSize":192,"stack":[],"depth":3,"refund":0,"opName":"PUSH32"} +{"pc":257,"op":96,"gas":"0x3dec8","gasCost":"0x3","memSize":192,"stack":["0x6000527f9981600160045582600eff600060006000600060f65af45060006060"],"depth":3,"refund":0,"opName":"PUSH1"} +{"pc":259,"op":82,"gas":"0x3dec5","gasCost":"0x6","memSize":192,"stack":["0x6000527f9981600160045582600eff600060006000600060f65af45060006060","0xc0"],"depth":3,"refund":0,"opName":"MSTORE"} +{"pc":260,"op":127,"gas":"0x3debf","gasCost":"0x3","memSize":224,"stack":[],"depth":3,"refund":0,"opName":"PUSH32"} +{"pc":293,"op":96,"gas":"0x3debc","gasCost":"0x3","memSize":224,"stack":["0x20527e600060006020527f60f75af4501d7f1903166660006000600060006000"],"depth":3,"refund":0,"opName":"PUSH1"} +{"pc":295,"op":82,"gas":"0x3deb9","gasCost":"0x6","memSize":224,"stack":["0x20527e600060006020527f60f75af4501d7f1903166660006000600060006000","0xe0"],"depth":3,"refund":0,"opName":"MSTORE"} +{"pc":296,"op":127,"gas":"0x3deb3","gasCost":"0x3","memSize":256,"stack":[],"depth":3,"refund":0,"opName":"PUSH32"} +{"pc":329,"op":97,"gas":"0x3deb0","gasCost":"0x3","memSize":256,"stack":["0x60046040527f5af1506000600060006040527f600060095af4503c95138e5b8f"],"depth":3,"refund":0,"opName":"PUSH2"} +{"pc":332,"op":82,"gas":"0x3dead","gasCost":"0x6","memSize":256,"stack":["0x60046040527f5af1506000600060006040527f600060095af4503c95138e5b8f","0x100"],"depth":3,"refund":0,"opName":"MSTORE"} +{"pc":333,"op":127,"gas":"0x3dea7","gasCost":"0x3","memSize":288,"stack":[],"depth":3,"refund":0,"opName":"PUSH32"} +{"pc":366,"op":97,"gas":"0x3dea4","gasCost":"0x3","memSize":288,"stack":["0x7f605a6000536060527f6031600153606b6002536010606060527f0353604560"],"depth":3,"refund":0,"opName":"PUSH2"} +{"pc":369,"op":82,"gas":"0x3dea1","gasCost":"0x6","memSize":288,"stack":["0x7f605a6000536060527f6031600153606b6002536010606060527f0353604560","0x120"],"depth":3,"refund":0,"opName":"MSTORE"} +{"pc":370,"op":127,"gas":"0x3de9b","gasCost":"0x3","memSize":320,"stack":[],"depth":3,"refund":0,"opName":"PUSH32"} +{"pc":403,"op":97,"gas":"0x3de98","gasCost":"0x3","memSize":320,"stack":["0x45360606005536001606080527e527f60065360606007536002600853606080"],"depth":3,"refund":0,"opName":"PUSH2"} +{"pc":406,"op":82,"gas":"0x3de95","gasCost":"0x6","memSize":320,"stack":["0x45360606005536001606080527e527f60065360606007536002600853606080","0x140"],"depth":3,"refund":0,"opName":"MSTORE"} +{"pc":407,"op":127,"gas":"0x3de8f","gasCost":"0x3","memSize":352,"stack":[],"depth":3,"refund":0,"opName":"PUSH32"} +{"pc":440,"op":97,"gas":"0x3de8c","gasCost":"0x3","memSize":352,"stack":["0x527f556009536060600a53600160a0527f600b536060600c6020527f53600060"],"depth":3,"refund":0,"opName":"PUSH2"} +{"pc":443,"op":82,"gas":"0x3de89","gasCost":"0x6","memSize":352,"stack":["0x527f556009536060600a53600160a0527f600b536060600c6020527f53600060","0x160"],"depth":3,"refund":0,"opName":"MSTORE"} +{"pc":444,"op":127,"gas":"0x3de83","gasCost":"0x3","memSize":384,"stack":[],"depth":3,"refund":0,"opName":"PUSH32"} +{"pc":477,"op":97,"gas":"0x3de80","gasCost":"0x3","memSize":384,"stack":["0xd536055600e60a0527f536060600f536060c0527f0160105360606011536002"],"depth":3,"refund":0,"opName":"PUSH2"} +{"pc":480,"op":82,"gas":"0x3de7d","gasCost":"0x6","memSize":384,"stack":["0xd536055600e60a0527f536060600f536060c0527f0160105360606011536002","0x180"],"depth":3,"refund":0,"opName":"MSTORE"} +{"pc":481,"op":127,"gas":"0x3de77","gasCost":"0x3","memSize":416,"stack":[],"depth":3,"refund":0,"opName":"PUSH32"} +{"pc":514,"op":97,"gas":"0x3de74","gasCost":"0x3","memSize":416,"stack":["0x601253606040527f55601353606060c0527f60145360e0527f60006015536060"],"depth":3,"refund":0,"opName":"PUSH2"} +{"pc":517,"op":82,"gas":"0x3de71","gasCost":"0x6","memSize":416,"stack":["0x601253606040527f55601353606060c0527f60145360e0527f60006015536060","0x1a0"],"depth":3,"refund":0,"opName":"MSTORE"} +{"pc":518,"op":127,"gas":"0x3de6b","gasCost":"0x3","memSize":448,"stack":[],"depth":3,"refund":0,"opName":"PUSH32"} +{"pc":551,"op":97,"gas":"0x3de68","gasCost":"0x3","memSize":448,"stack":["0x601653600060175360f360185360196060605260006060e052610100527f7f80"],"depth":3,"refund":0,"opName":"PUSH2"} +{"pc":554,"op":82,"gas":"0x3de65","gasCost":"0x6","memSize":448,"stack":["0x601653600060175360f360185360196060605260006060e052610100527f7f80","0x1c0"],"depth":3,"refund":0,"opName":"MSTORE"} +{"pc":555,"op":127,"gas":"0x3de5f","gasCost":"0x3","memSize":480,"stack":[],"depth":3,"refund":0,"opName":"PUSH32"} +{"pc":588,"op":97,"gas":"0x3de5c","gasCost":"0x3","memSize":480,"stack":["0x5360f3608153608260006000f06000600060006000845af45050600060006101"],"depth":3,"refund":0,"opName":"PUSH2"} +{"pc":591,"op":82,"gas":"0x3de59","gasCost":"0x6","memSize":480,"stack":["0x5360f3608153608260006000f06000600060006000845af45050600060006101","0x1e0"],"depth":3,"refund":0,"opName":"MSTORE"} +{"pc":592,"op":127,"gas":"0x3de53","gasCost":"0x3","memSize":512,"stack":[],"depth":3,"refund":0,"opName":"PUSH32"} +{"pc":625,"op":97,"gas":"0x3de50","gasCost":"0x3","memSize":512,"stack":["0x20527f60610100527e6000600060035af15060005450c760006002551309f562"],"depth":3,"refund":0,"opName":"PUSH2"} +{"pc":628,"op":82,"gas":"0x3de4d","gasCost":"0x6","memSize":512,"stack":["0x20527f60610100527e6000600060035af15060005450c760006002551309f562","0x200"],"depth":3,"refund":0,"opName":"MSTORE"} +{"pc":629,"op":127,"gas":"0x3de47","gasCost":"0x3","memSize":544,"stack":[],"depth":3,"refund":0,"opName":"PUSH32"} +{"pc":662,"op":97,"gas":"0x3de44","gasCost":"0x3","memSize":544,"stack":["0x66a486610140527f6b001d4571610120527f600054501c641d373c7f60045450"],"depth":3,"refund":0,"opName":"PUSH2"} +{"pc":665,"op":82,"gas":"0x3de41","gasCost":"0x6","memSize":544,"stack":["0x66a486610140527f6b001d4571610120527f600054501c641d373c7f60045450","0x220"],"depth":3,"refund":0,"opName":"MSTORE"} +{"pc":666,"op":127,"gas":"0x3de3b","gasCost":"0x3","memSize":576,"stack":[],"depth":3,"refund":0,"opName":"PUSH32"} +{"pc":699,"op":97,"gas":"0x3de38","gasCost":"0x3","memSize":576,"stack":["0x6000600155600554610160527f50600160025560085450610140527f60006002"],"depth":3,"refund":0,"opName":"PUSH2"} +{"pc":702,"op":82,"gas":"0x3de35","gasCost":"0x6","memSize":576,"stack":["0x6000600155600554610160527f50600160025560085450610140527f60006002","0x240"],"depth":3,"refund":0,"opName":"MSTORE"} +{"pc":703,"op":127,"gas":"0x3de2f","gasCost":"0x3","memSize":608,"stack":[],"depth":3,"refund":0,"opName":"PUSH32"} +{"pc":736,"op":97,"gas":"0x3de2c","gasCost":"0x3","memSize":608,"stack":["0x557fd86000606000527e600060610180527e600060005af15086121714514735"],"depth":3,"refund":0,"opName":"PUSH2"} +{"pc":739,"op":82,"gas":"0x3de29","gasCost":"0x6","memSize":608,"stack":["0x557fd86000606000527e600060610180527e600060005af15086121714514735","0x260"],"depth":3,"refund":0,"opName":"MSTORE"} +{"pc":740,"op":127,"gas":"0x3de23","gasCost":"0x3","memSize":640,"stack":[],"depth":3,"refund":0,"opName":"PUSH32"} +{"pc":773,"op":97,"gas":"0x3de20","gasCost":"0x3","memSize":640,"stack":["0x610160527f5198a37e127a7efa7c6000526101a0527f606020527f6060205360"],"depth":3,"refund":0,"opName":"PUSH2"} +{"pc":776,"op":82,"gas":"0x3de1d","gasCost":"0x6","memSize":640,"stack":["0x610160527f5198a37e127a7efa7c6000526101a0527f606020527f6060205360","0x280"],"depth":3,"refund":0,"opName":"MSTORE"} +{"pc":777,"op":127,"gas":"0x3de17","gasCost":"0x3","memSize":672,"stack":[],"depth":3,"refund":0,"opName":"PUSH32"} +{"pc":810,"op":97,"gas":"0x3de14","gasCost":"0x3","memSize":672,"stack":["0xf760215360ff60225360610180527fdb6023536037606101c0527f2453607560"],"depth":3,"refund":0,"opName":"PUSH2"} +{"pc":813,"op":82,"gas":"0x3de11","gasCost":"0x6","memSize":672,"stack":["0xf760215360ff60225360610180527fdb6023536037606101c0527f2453607560","0x2a0"],"depth":3,"refund":0,"opName":"MSTORE"} +{"pc":814,"op":127,"gas":"0x3de0b","gasCost":"0x3","memSize":704,"stack":[],"depth":3,"refund":0,"opName":"PUSH32"} +{"pc":847,"op":97,"gas":"0x3de08","gasCost":"0x3","memSize":704,"stack":["0x2553609f606040527f265360fe602753608f60286101a0527f53606101e0527f"],"depth":3,"refund":0,"opName":"PUSH2"} +{"pc":850,"op":82,"gas":"0x3de05","gasCost":"0x7","memSize":704,"stack":["0x2553609f606040527f265360fe602753608f60286101a0527f53606101e0527f","0x2c0"],"depth":3,"refund":0,"opName":"MSTORE"} +{"pc":851,"op":127,"gas":"0x3ddfe","gasCost":"0x3","memSize":736,"stack":[],"depth":3,"refund":0,"opName":"PUSH32"} +{"pc":884,"op":97,"gas":"0x3ddfb","gasCost":"0x3","memSize":736,"stack":["0xb6029536060602a536000602b536060602c5360605260606080536000606101"],"depth":3,"refund":0,"opName":"PUSH2"} +{"pc":887,"op":82,"gas":"0x3ddf8","gasCost":"0x6","memSize":736,"stack":["0xb6029536060602a536000602b536060602c5360605260606080536000606101","0x2e0"],"depth":3,"refund":0,"opName":"MSTORE"} +{"pc":888,"op":127,"gas":"0x3ddf2","gasCost":"0x3","memSize":768,"stack":[],"depth":3,"refund":0,"opName":"PUSH32"} +{"pc":921,"op":97,"gas":"0x3ddef","gasCost":"0x3","memSize":768,"stack":["0x610200527fc0527f81536060608253602d6083536053608453606060855360fd"],"depth":3,"refund":0,"opName":"PUSH2"} +{"pc":924,"op":82,"gas":"0x3ddec","gasCost":"0x6","memSize":768,"stack":["0x610200527fc0527f81536060608253602d6083536053608453606060855360fd","0x300"],"depth":3,"refund":0,"opName":"MSTORE"} +{"pc":925,"op":127,"gas":"0x3dde6","gasCost":"0x3","memSize":800,"stack":[],"depth":3,"refund":0,"opName":"PUSH32"} +{"pc":958,"op":97,"gas":"0x3dde3","gasCost":"0x3","memSize":800,"stack":["0x6086536060610220527f6087536101e0527f602e60885360536089536060608a"],"depth":3,"refund":0,"opName":"PUSH2"} +{"pc":961,"op":82,"gas":"0x3dde0","gasCost":"0x6","memSize":800,"stack":["0x6086536060610220527f6087536101e0527f602e60885360536089536060608a","0x320"],"depth":3,"refund":0,"opName":"MSTORE"} +{"pc":962,"op":127,"gas":"0x3ddda","gasCost":"0x3","memSize":832,"stack":[],"depth":3,"refund":0,"opName":"PUSH32"} +{"pc":995,"op":97,"gas":"0x3ddd7","gasCost":"0x3","memSize":832,"stack":["0x53602f608b536060608c610240527f536000608d5360f3610200526060610220"],"depth":3,"refund":0,"opName":"PUSH2"} +{"pc":998,"op":82,"gas":"0x3ddd4","gasCost":"0x6","memSize":832,"stack":["0x53602f608b536060608c610240527f536000608d5360f3610200526060610220","0x340"],"depth":3,"refund":0,"opName":"MSTORE"} +{"pc":999,"op":127,"gas":"0x3ddce","gasCost":"0x3","memSize":864,"stack":[],"depth":3,"refund":0,"opName":"PUSH32"} +{"pc":1032,"op":97,"gas":"0x3ddcb","gasCost":"0x3","memSize":864,"stack":["0x53608e610221536053610222536060610260527f610223536000610224536060"],"depth":3,"refund":0,"opName":"PUSH2"} +{"pc":1035,"op":82,"gas":"0x3ddc8","gasCost":"0x6","memSize":864,"stack":["0x53608e610221536053610222536060610260527f610223536000610224536060","0x360"],"depth":3,"refund":0,"opName":"MSTORE"} +{"pc":1036,"op":127,"gas":"0x3ddc2","gasCost":"0x3","memSize":896,"stack":[],"depth":3,"refund":0,"opName":"PUSH32"} +{"pc":1069,"op":97,"gas":"0x3ddbf","gasCost":"0x3","memSize":896,"stack":["0x61022553608f6102265360606102275360006102610280527f28536060610229"],"depth":3,"refund":0,"opName":"PUSH2"} +{"pc":1072,"op":82,"gas":"0x3ddbc","gasCost":"0x6","memSize":896,"stack":["0x61022553608f6102265360606102275360006102610280527f28536060610229","0x380"],"depth":3,"refund":0,"opName":"MSTORE"} +{"pc":1073,"op":127,"gas":"0x3ddb6","gasCost":"0x3","memSize":928,"stack":[],"depth":3,"refund":0,"opName":"PUSH32"} +{"pc":1106,"op":97,"gas":"0x3ddb3","gasCost":"0x3","memSize":928,"stack":["0x53600061022a5360f561022b53606061022c53600061022d536102a0527f6060"],"depth":3,"refund":0,"opName":"PUSH2"} +{"pc":1109,"op":82,"gas":"0x3ddb0","gasCost":"0x6","memSize":928,"stack":["0x53600061022a5360f561022b53606061022c53600061022d536102a0527f6060","0x3a0"],"depth":3,"refund":0,"opName":"MSTORE"} +{"pc":1110,"op":127,"gas":"0x3ddaa","gasCost":"0x3","memSize":960,"stack":[],"depth":3,"refund":0,"opName":"PUSH32"} +{"pc":1143,"op":97,"gas":"0x3dda7","gasCost":"0x3","memSize":960,"stack":["0x61022e53600061022f5360606102305360006102315360606102325360006102"],"depth":3,"refund":0,"opName":"PUSH2"} +{"pc":1146,"op":82,"gas":"0x3dda4","gasCost":"0x6","memSize":960,"stack":["0x61022e53600061022f5360606102305360006102315360606102325360006102","0x3c0"],"depth":3,"refund":0,"opName":"MSTORE"} +{"pc":1147,"op":127,"gas":"0x3dd9e","gasCost":"0x3","memSize":992,"stack":[],"depth":3,"refund":0,"opName":"PUSH32"} +{"pc":1180,"op":97,"gas":"0x3dd9b","gasCost":"0x3","memSize":992,"stack":["0xc0527f61023353606061023453600061023553608561023653605a6102375360"],"depth":3,"refund":0,"opName":"PUSH2"} +{"pc":1183,"op":82,"gas":"0x3dd98","gasCost":"0x7","memSize":992,"stack":["0xc0527f61023353606061023453600061023553608561023653605a6102375360","0x3e0"],"depth":3,"refund":0,"opName":"MSTORE"} +{"pc":1184,"op":127,"gas":"0x3dd91","gasCost":"0x3","memSize":1024,"stack":[],"depth":3,"refund":0,"opName":"PUSH32"} +{"pc":1217,"op":97,"gas":"0x3dd8e","gasCost":"0x3","memSize":1024,"stack":["0xf261026102e05260386103005360536103015360606103025360506103035360"],"depth":3,"refund":0,"opName":"PUSH2"} +{"pc":1220,"op":82,"gas":"0x3dd8b","gasCost":"0x6","memSize":1024,"stack":["0xf261026102e05260386103005360536103015360606103025360506103035360","0x400"],"depth":3,"refund":0,"opName":"MSTORE"} +{"pc":1221,"op":127,"gas":"0x3dd85","gasCost":"0x3","memSize":1056,"stack":[],"depth":3,"refund":0,"opName":"PUSH32"} +{"pc":1254,"op":97,"gas":"0x3dd82","gasCost":"0x3","memSize":1056,"stack":["0x6161030453600261030553603961030653605361030753606061030853605061"],"depth":3,"refund":0,"opName":"PUSH2"} +{"pc":1257,"op":82,"gas":"0x3dd7f","gasCost":"0x6","memSize":1056,"stack":["0x6161030453600261030553603961030653605361030753606061030853605061","0x420"],"depth":3,"refund":0,"opName":"MSTORE"} +{"pc":1258,"op":127,"gas":"0x3dd79","gasCost":"0x3","memSize":1088,"stack":[],"depth":3,"refund":0,"opName":"PUSH32"} +{"pc":1291,"op":97,"gas":"0x3dd76","gasCost":"0x3","memSize":1088,"stack":["0x30953606161030a53600261030b53603a61030c53605361030d53606161030e"],"depth":3,"refund":0,"opName":"PUSH2"} +{"pc":1294,"op":82,"gas":"0x3dd73","gasCost":"0x6","memSize":1088,"stack":["0x30953606161030a53600261030b53603a61030c53605361030d53606161030e","0x440"],"depth":3,"refund":0,"opName":"MSTORE"} +{"pc":1295,"op":127,"gas":"0x3dd6d","gasCost":"0x3","memSize":1120,"stack":[],"depth":3,"refund":0,"opName":"PUSH32"} +{"pc":1328,"op":97,"gas":"0x3dd6a","gasCost":"0x3","memSize":1120,"stack":["0x53600261030f53603b6103105360606103115360006103125360f36103135361"],"depth":3,"refund":0,"opName":"PUSH2"} +{"pc":1331,"op":82,"gas":"0x3dd67","gasCost":"0x6","memSize":1120,"stack":["0x53600261030f53603b6103105360606103115360006103125360f36103135361","0x460"],"depth":3,"refund":0,"opName":"MSTORE"} +{"pc":1332,"op":96,"gas":"0x3dd61","gasCost":"0x3","memSize":1152,"stack":[],"depth":3,"refund":0,"opName":"PUSH1"} +{"pc":1334,"op":97,"gas":"0x3dd5e","gasCost":"0x3","memSize":1152,"stack":["0x3"],"depth":3,"refund":0,"opName":"PUSH2"} +{"pc":1337,"op":83,"gas":"0x3dd5b","gasCost":"0x6","memSize":1152,"stack":["0x3","0x480"],"depth":3,"refund":0,"opName":"MSTORE8"} +{"pc":1338,"op":96,"gas":"0x3dd55","gasCost":"0x3","memSize":1184,"stack":[],"depth":3,"refund":0,"opName":"PUSH1"} +{"pc":1340,"op":97,"gas":"0x3dd52","gasCost":"0x3","memSize":1184,"stack":["0x14"],"depth":3,"refund":0,"opName":"PUSH2"} +{"pc":1343,"op":83,"gas":"0x3dd4f","gasCost":"0x3","memSize":1184,"stack":["0x14","0x481"],"depth":3,"refund":0,"opName":"MSTORE8"} +{"pc":1344,"op":96,"gas":"0x3dd4c","gasCost":"0x3","memSize":1184,"stack":[],"depth":3,"refund":0,"opName":"PUSH1"} +{"pc":1346,"op":97,"gas":"0x3dd49","gasCost":"0x3","memSize":1184,"stack":["0x60"],"depth":3,"refund":0,"opName":"PUSH2"} +{"pc":1349,"op":83,"gas":"0x3dd46","gasCost":"0x3","memSize":1184,"stack":["0x60","0x482"],"depth":3,"refund":0,"opName":"MSTORE8"} +{"pc":1350,"op":96,"gas":"0x3dd43","gasCost":"0x3","memSize":1184,"stack":[],"depth":3,"refund":0,"opName":"PUSH1"} +{"pc":1352,"op":97,"gas":"0x3dd40","gasCost":"0x3","memSize":1184,"stack":["0x0"],"depth":3,"refund":0,"opName":"PUSH2"} +{"pc":1355,"op":83,"gas":"0x3dd3d","gasCost":"0x3","memSize":1184,"stack":["0x0","0x483"],"depth":3,"refund":0,"opName":"MSTORE8"} +{"pc":1356,"op":96,"gas":"0x3dd3a","gasCost":"0x3","memSize":1184,"stack":[],"depth":3,"refund":0,"opName":"PUSH1"} +{"pc":1358,"op":97,"gas":"0x3dd37","gasCost":"0x3","memSize":1184,"stack":["0x60"],"depth":3,"refund":0,"opName":"PUSH2"} +{"pc":1361,"op":83,"gas":"0x3dd34","gasCost":"0x3","memSize":1184,"stack":["0x60","0x484"],"depth":3,"refund":0,"opName":"MSTORE8"} +{"pc":1362,"op":96,"gas":"0x3dd31","gasCost":"0x3","memSize":1184,"stack":[],"depth":3,"refund":0,"opName":"PUSH1"} +{"pc":1364,"op":97,"gas":"0x3dd2e","gasCost":"0x3","memSize":1184,"stack":["0x0"],"depth":3,"refund":0,"opName":"PUSH2"} +{"pc":1367,"op":83,"gas":"0x3dd2b","gasCost":"0x3","memSize":1184,"stack":["0x0","0x485"],"depth":3,"refund":0,"opName":"MSTORE8"} +{"pc":1368,"op":96,"gas":"0x3dd28","gasCost":"0x3","memSize":1184,"stack":[],"depth":3,"refund":0,"opName":"PUSH1"} +{"pc":1370,"op":97,"gas":"0x3dd25","gasCost":"0x3","memSize":1184,"stack":["0xf0"],"depth":3,"refund":0,"opName":"PUSH2"} +{"pc":1373,"op":83,"gas":"0x3dd22","gasCost":"0x3","memSize":1184,"stack":["0xf0","0x486"],"depth":3,"refund":0,"opName":"MSTORE8"} +{"pc":1374,"op":96,"gas":"0x3dd1f","gasCost":"0x3","memSize":1184,"stack":[],"depth":3,"refund":0,"opName":"PUSH1"} +{"pc":1376,"op":97,"gas":"0x3dd1c","gasCost":"0x3","memSize":1184,"stack":["0x60"],"depth":3,"refund":0,"opName":"PUSH2"} +{"pc":1379,"op":83,"gas":"0x3dd19","gasCost":"0x3","memSize":1184,"stack":["0x60","0x487"],"depth":3,"refund":0,"opName":"MSTORE8"} +{"pc":1380,"op":96,"gas":"0x3dd16","gasCost":"0x3","memSize":1184,"stack":[],"depth":3,"refund":0,"opName":"PUSH1"} +{"pc":1382,"op":97,"gas":"0x3dd13","gasCost":"0x3","memSize":1184,"stack":["0x0"],"depth":3,"refund":0,"opName":"PUSH2"} +{"pc":1385,"op":83,"gas":"0x3dd10","gasCost":"0x3","memSize":1184,"stack":["0x0","0x488"],"depth":3,"refund":0,"opName":"MSTORE8"} +{"pc":1386,"op":96,"gas":"0x3dd0d","gasCost":"0x3","memSize":1184,"stack":[],"depth":3,"refund":0,"opName":"PUSH1"} +{"pc":1388,"op":97,"gas":"0x3dd0a","gasCost":"0x3","memSize":1184,"stack":["0x60"],"depth":3,"refund":0,"opName":"PUSH2"} +{"pc":1391,"op":83,"gas":"0x3dd07","gasCost":"0x3","memSize":1184,"stack":["0x60","0x489"],"depth":3,"refund":0,"opName":"MSTORE8"} +{"pc":1392,"op":96,"gas":"0x3dd04","gasCost":"0x3","memSize":1184,"stack":[],"depth":3,"refund":0,"opName":"PUSH1"} +{"pc":1394,"op":97,"gas":"0x3dd01","gasCost":"0x3","memSize":1184,"stack":["0x0"],"depth":3,"refund":0,"opName":"PUSH2"} +{"pc":1397,"op":83,"gas":"0x3dcfe","gasCost":"0x3","memSize":1184,"stack":["0x0","0x48a"],"depth":3,"refund":0,"opName":"MSTORE8"} +{"pc":1398,"op":96,"gas":"0x3dcfb","gasCost":"0x3","memSize":1184,"stack":[],"depth":3,"refund":0,"opName":"PUSH1"} +{"pc":1400,"op":97,"gas":"0x3dcf8","gasCost":"0x3","memSize":1184,"stack":["0x60"],"depth":3,"refund":0,"opName":"PUSH2"} +{"pc":1403,"op":83,"gas":"0x3dcf5","gasCost":"0x3","memSize":1184,"stack":["0x60","0x48b"],"depth":3,"refund":0,"opName":"MSTORE8"} +{"pc":1404,"op":96,"gas":"0x3dcf2","gasCost":"0x3","memSize":1184,"stack":[],"depth":3,"refund":0,"opName":"PUSH1"} +{"pc":1406,"op":97,"gas":"0x3dcef","gasCost":"0x3","memSize":1184,"stack":["0x0"],"depth":3,"refund":0,"opName":"PUSH2"} +{"pc":1409,"op":83,"gas":"0x3dcec","gasCost":"0x3","memSize":1184,"stack":["0x0","0x48c"],"depth":3,"refund":0,"opName":"MSTORE8"} +{"pc":1410,"op":96,"gas":"0x3dce9","gasCost":"0x3","memSize":1184,"stack":[],"depth":3,"refund":0,"opName":"PUSH1"} +{"pc":1412,"op":97,"gas":"0x3dce6","gasCost":"0x3","memSize":1184,"stack":["0x60"],"depth":3,"refund":0,"opName":"PUSH2"} +{"pc":1415,"op":83,"gas":"0x3dce3","gasCost":"0x3","memSize":1184,"stack":["0x60","0x48d"],"depth":3,"refund":0,"opName":"MSTORE8"} +{"pc":1416,"op":96,"gas":"0x3dce0","gasCost":"0x3","memSize":1184,"stack":[],"depth":3,"refund":0,"opName":"PUSH1"} +{"pc":1418,"op":97,"gas":"0x3dcdd","gasCost":"0x3","memSize":1184,"stack":["0x0"],"depth":3,"refund":0,"opName":"PUSH2"} +{"pc":1421,"op":83,"gas":"0x3dcda","gasCost":"0x3","memSize":1184,"stack":["0x0","0x48e"],"depth":3,"refund":0,"opName":"MSTORE8"} +{"pc":1422,"op":96,"gas":"0x3dcd7","gasCost":"0x3","memSize":1184,"stack":[],"depth":3,"refund":0,"opName":"PUSH1"} +{"pc":1424,"op":97,"gas":"0x3dcd4","gasCost":"0x3","memSize":1184,"stack":["0x84"],"depth":3,"refund":0,"opName":"PUSH2"} +{"pc":1427,"op":83,"gas":"0x3dcd1","gasCost":"0x3","memSize":1184,"stack":["0x84","0x48f"],"depth":3,"refund":0,"opName":"MSTORE8"} +{"pc":1428,"op":96,"gas":"0x3dcce","gasCost":"0x3","memSize":1184,"stack":[],"depth":3,"refund":0,"opName":"PUSH1"} +{"pc":1430,"op":97,"gas":"0x3dccb","gasCost":"0x3","memSize":1184,"stack":["0x5a"],"depth":3,"refund":0,"opName":"PUSH2"} +{"pc":1433,"op":83,"gas":"0x3dcc8","gasCost":"0x3","memSize":1184,"stack":["0x5a","0x490"],"depth":3,"refund":0,"opName":"MSTORE8"} +{"pc":1434,"op":96,"gas":"0x3dcc5","gasCost":"0x3","memSize":1184,"stack":[],"depth":3,"refund":0,"opName":"PUSH1"} +{"pc":1436,"op":97,"gas":"0x3dcc2","gasCost":"0x3","memSize":1184,"stack":["0xf4"],"depth":3,"refund":0,"opName":"PUSH2"} +{"pc":1439,"op":83,"gas":"0x3dcbf","gasCost":"0x3","memSize":1184,"stack":["0xf4","0x491"],"depth":3,"refund":0,"opName":"MSTORE8"} +{"pc":1440,"op":96,"gas":"0x3dcbc","gasCost":"0x3","memSize":1184,"stack":[],"depth":3,"refund":0,"opName":"PUSH1"} +{"pc":1442,"op":97,"gas":"0x3dcb9","gasCost":"0x3","memSize":1184,"stack":["0x50"],"depth":3,"refund":0,"opName":"PUSH2"} +{"pc":1445,"op":83,"gas":"0x3dcb6","gasCost":"0x3","memSize":1184,"stack":["0x50","0x492"],"depth":3,"refund":0,"opName":"MSTORE8"} +{"pc":1446,"op":96,"gas":"0x3dcb3","gasCost":"0x3","memSize":1184,"stack":[],"depth":3,"refund":0,"opName":"PUSH1"} +{"pc":1448,"op":97,"gas":"0x3dcb0","gasCost":"0x3","memSize":1184,"stack":["0x50"],"depth":3,"refund":0,"opName":"PUSH2"} +{"pc":1451,"op":83,"gas":"0x3dcad","gasCost":"0x3","memSize":1184,"stack":["0x50","0x493"],"depth":3,"refund":0,"opName":"MSTORE8"} +{"pc":1452,"op":97,"gas":"0x3dcaa","gasCost":"0x3","memSize":1184,"stack":[],"depth":3,"refund":0,"opName":"PUSH2"} +{"pc":1455,"op":96,"gas":"0x3dca7","gasCost":"0x3","memSize":1184,"stack":["0x494"],"depth":3,"refund":0,"opName":"PUSH1"} +{"pc":1457,"op":243,"gas":"0x3dca4","gasCost":"0x0","memSize":1184,"stack":["0x494","0x0"],"depth":3,"refund":0,"opName":"RETURN"} +{"output":"600160045560006004556000600060006000600060f95af250600060006000600060f45af4506000600060006000600060f55af150f001075205846a44a283448ca2600060006000600060045af450519930847f3b631c54a49b5f600354503277306b60006000600060006000600c5af150600060006000600060f85af4505006600160025560035450600060005560006001556c3394fff4607f7f1684317b387b9f1920700184809d60015450011899016e6009ff60026001556000527f9f6000527f9981600160045582600eff600060006000600060f65af4506000606020527e600060006020527f60f75af4501d7f190316666000600060006000600060046040527f5af1506000600060006040527f600060095af4503c95138e5b8f7f605a6000536060527f6031600153606b6002536010606060527f0353604560045360606005536001606080527e527f60065360606007536002600853606080527f556009536060600a53600160a0527f600b536060600c6020527f536000600d536055600e60a0527f536060600f536060c0527f0160105360606011536002601253606040527f55601353606060c0527f60145360e0527f60006015536060601653600060175360f360185360196060605260006060e052610100527f7f805360f3608153608260006000f06000600060006000845af4505060006000610120527f60610100527e6000600060035af15060005450c760006002551309f56266a486610140527f6b001d4571610120527f600054501c641d373c7f600454506000600155600554610160527f50600160025560085450610140527f60006002557fd86000606000527e600060610180527e600060005af15086121714514735610160527f5198a37e127a7efa7c6000526101a0527f606020527f6060205360f760215360ff60225360610180527fdb6023536037606101c0527f24536075602553609f606040527f265360fe602753608f60286101a0527f53606101e0527f0b6029536060602a536000602b536060602c5360605260606080536000606101610200527fc0527f81536060608253602d6083536053608453606060855360fd6086536060610220527f6087536101e0527f602e60885360536089536060608a53602f608b536060608c610240527f536000608d5360f361020052606061022053608e610221536053610222536060610260527f61022353600061022453606061022553608f6102265360606102275360006102610280527f2853606061022953600061022a5360f561022b53606061022c53600061022d536102a0527f606061022e53600061022f5360606102305360006102315360606102325360006102c0527f61023353606061023453600061023553608561023653605a6102375360f261026102e052603861030053605361030153606061030253605061030353606161030453600261030553603961030653605361030753606061030853605061030953606161030a53600261030b53603a61030c53605361030d53606161030e53600261030f53603b6103105360606103115360006103125360f36103135361031460006000f06000600060006000845af45050","gasUsed":"0x3a6e6"} +{"pc":1773,"op":96,"gas":"0x5903","gasCost":"0x3","memSize":1472,"stack":["0x94a843a7335fc63be036fbdecc40b1365f3c5f26"],"depth":2,"refund":0,"opName":"PUSH1"} +{"pc":1775,"op":96,"gas":"0x5900","gasCost":"0x3","memSize":1472,"stack":["0x94a843a7335fc63be036fbdecc40b1365f3c5f26","0x0"],"depth":2,"refund":0,"opName":"PUSH1"} +{"pc":1777,"op":96,"gas":"0x58fd","gasCost":"0x3","memSize":1472,"stack":["0x94a843a7335fc63be036fbdecc40b1365f3c5f26","0x0","0x0"],"depth":2,"refund":0,"opName":"PUSH1"} +{"pc":1779,"op":96,"gas":"0x58fa","gasCost":"0x3","memSize":1472,"stack":["0x94a843a7335fc63be036fbdecc40b1365f3c5f26","0x0","0x0","0x0"],"depth":2,"refund":0,"opName":"PUSH1"} +{"pc":1781,"op":96,"gas":"0x58f7","gasCost":"0x3","memSize":1472,"stack":["0x94a843a7335fc63be036fbdecc40b1365f3c5f26","0x0","0x0","0x0","0x0"],"depth":2,"refund":0,"opName":"PUSH1"} +{"pc":1783,"op":133,"gas":"0x58f4","gasCost":"0x3","memSize":1472,"stack":["0x94a843a7335fc63be036fbdecc40b1365f3c5f26","0x0","0x0","0x0","0x0","0x0"],"depth":2,"refund":0,"opName":"DUP6"} +{"pc":1784,"op":90,"gas":"0x58f1","gasCost":"0x2","memSize":1472,"stack":["0x94a843a7335fc63be036fbdecc40b1365f3c5f26","0x0","0x0","0x0","0x0","0x0","0x94a843a7335fc63be036fbdecc40b1365f3c5f26"],"depth":2,"refund":0,"opName":"GAS"} +{"pc":1785,"op":241,"gas":"0x58ef","gasCost":"0x578d","memSize":1472,"stack":["0x94a843a7335fc63be036fbdecc40b1365f3c5f26","0x0","0x0","0x0","0x0","0x0","0x94a843a7335fc63be036fbdecc40b1365f3c5f26","0x58ef"],"depth":2,"refund":0,"opName":"CALL"} +{"pc":0,"op":96,"gas":"0x5729","gasCost":"0x3","memSize":0,"stack":[],"depth":3,"refund":0,"opName":"PUSH1"} +{"pc":2,"op":96,"gas":"0x5726","gasCost":"0x3","memSize":0,"stack":["0x1"],"depth":3,"refund":0,"opName":"PUSH1"} +{"pc":4,"op":85,"gas":"0x5723","gasCost":"0x4e20","memSize":0,"stack":["0x1","0x4"],"depth":3,"refund":0,"opName":"SSTORE"} +{"pc":5,"op":96,"gas":"0x903","gasCost":"0x3","memSize":0,"stack":[],"depth":3,"refund":0,"opName":"PUSH1"} +{"pc":7,"op":96,"gas":"0x900","gasCost":"0x3","memSize":0,"stack":["0x0"],"depth":3,"refund":0,"opName":"PUSH1"} +{"pc":9,"op":85,"gas":"0x8fd","gasCost":"0x64","memSize":0,"stack":["0x0","0x4"],"depth":3,"refund":19900,"opName":"SSTORE"} +{"pc":10,"op":96,"gas":"0x899","gasCost":"0x3","memSize":0,"stack":[],"depth":3,"refund":19900,"opName":"PUSH1"} +{"pc":12,"op":96,"gas":"0x896","gasCost":"0x3","memSize":0,"stack":["0x0"],"depth":3,"refund":19900,"opName":"PUSH1"} +{"pc":14,"op":96,"gas":"0x893","gasCost":"0x3","memSize":0,"stack":["0x0","0x0"],"depth":3,"refund":19900,"opName":"PUSH1"} +{"pc":16,"op":96,"gas":"0x890","gasCost":"0x3","memSize":0,"stack":["0x0","0x0","0x0"],"depth":3,"refund":19900,"opName":"PUSH1"} +{"pc":18,"op":96,"gas":"0x88d","gasCost":"0x3","memSize":0,"stack":["0x0","0x0","0x0","0x0"],"depth":3,"refund":19900,"opName":"PUSH1"} +{"pc":20,"op":96,"gas":"0x88a","gasCost":"0x3","memSize":0,"stack":["0x0","0x0","0x0","0x0","0x0"],"depth":3,"refund":19900,"opName":"PUSH1"} +{"pc":22,"op":90,"gas":"0x887","gasCost":"0x2","memSize":0,"stack":["0x0","0x0","0x0","0x0","0x0","0xf9"],"depth":3,"refund":19900,"opName":"GAS"} +{"pc":23,"op":242,"gas":"0x885","gasCost":"0x64","memSize":0,"stack":["0x0","0x0","0x0","0x0","0x0","0xf9","0x885"],"depth":3,"refund":19900,"opName":"CALLCODE","error":"out of gas: out of gas"} +{"output":"","gasUsed":"0x5729","error":"out of gas: out of gas"} +{"pc":1786,"op":80,"gas":"0x162","gasCost":"0x2","memSize":1472,"stack":["0x94a843a7335fc63be036fbdecc40b1365f3c5f26","0x0"],"depth":2,"refund":0,"opName":"POP"} +{"pc":1787,"op":80,"gas":"0x160","gasCost":"0x2","memSize":1472,"stack":["0x94a843a7335fc63be036fbdecc40b1365f3c5f26"],"depth":2,"refund":0,"opName":"POP"} +{"pc":1788,"op":0,"gas":"0x15e","gasCost":"0x0","memSize":1472,"stack":[],"depth":2,"refund":0,"opName":"STOP"} +{"output":"","gasUsed":"0x47f70"} +{"pc":2618,"op":80,"gas":"0x13aa","gasCost":"0x2","memSize":2240,"stack":["0x7dce2faf43218578e3fcf2ad22df9918a89e2fba","0x1"],"depth":1,"refund":0,"opName":"POP"} +{"pc":2619,"op":80,"gas":"0x13a8","gasCost":"0x2","memSize":2240,"stack":["0x7dce2faf43218578e3fcf2ad22df9918a89e2fba"],"depth":1,"refund":0,"opName":"POP"} +{"pc":2620,"op":0,"gas":"0x13a6","gasCost":"0x0","memSize":2240,"stack":[],"depth":1,"refund":0,"opName":"STOP"} +{"output":"","gasUsed":"0xb2e6d"} +{"stateRoot": "0xad1024c87b5548e77c937aa50f72b6cb620d278f4dd79bae7f78f71ff75af458"} diff --git a/cmd/evm/testdata/statetest.json b/cmd/evm/testdata/statetest.json new file mode 100755 index 0000000000..3b2a91987a --- /dev/null +++ b/cmd/evm/testdata/statetest.json @@ -0,0 +1,57 @@ +{ + "00000006-naivefuzz-0": { + "env": { + "currentCoinbase": "b94f5374fce5edbc8e2a8697c15331677e6ebf0b", + "currentDifficulty": "0x200000", + "currentGasLimit": "0x26e1f476fe1e22", + "currentNumber": "0x1", + "currentTimestamp": "0x3e8", + "previousHash": "0x0000000000000000000000000000000000000000000000000000000000000000", + "currentBaseFee": "0x10" + }, + "pre": { + "0x00000000000000000000000000000000000000f1": { + "code": "0x60026003556000600060006000600060045af2507f600254506003545060016003557f7f6008545060006004557f600160045560006000527f60045560006000600060006000606000527ff96000527f5af2506000600060006020527f606000527e60f45af45060006000600060006020527f600060f55a6020527ff16040527f50f001075205846a44a283446020527f8ca2600060006040527f6000600060046060527f5af4506040527f519930847f3b631c54a49b5f600354503260406060527f527f6080527f77306b60006000600060006060527f6000600c5af1506000600060006000608060a0527f527f60f85af450506060527f066001600255606080527f03545060006000556060c0527e6060a0527f01556c3394fff4607f7f1684317b6080527f387b9f60a0527f1960e0527f20700184809d60c0527f60015450011899016e6009ff60026001556000527f9f610100527f60a05260c0527f7f600060e0527f527f9981600160045582600eff6000600060610120527e600060f65af45060006060e052610100527f7f6060c0527f20527e60006000610140527f6020527f60f75af4501d7f1903166660006000610120527f60610100527e6000610160527f600060e0527f60046040527f5af150600060006000604052610140527f7f6000610180527f6009610120527f5af4503c95138e5b8f610100527f7f605a60005360606101606101a0527f527f527f6031600153606b60610140527f02536010606060527f0353604560616101c0527f0120610180527f527f04536060600553600160608052610160527f7e527f60066101e0527f536060600753606101a0527f02600853606080610140527f527f556009536060610200527f610180527f600a53600160a06101c0527f527f600b536060600c6020527f5360610220527e60610160527f0d53606101a0527f55606101e0527f0e60a0527f536060600f610240527f536060c0527f01601053606060115360026101806101610200527fc0527f527f610260527f601253606040527f55601353606060c0527f60145360e0527f6000610220527f610280527f6015536101e0527f60606101a0527f601653600060175360f3601853601960606102a0527f610240527f605260006060e052610200527f610100527f7f806101c0527f53606102c0527ff360815360610260527f8260006000f060006000600060610220527e845af4506102e0527f506000600061016101e0610280527f527f20527f60610100527e600060006003610300527f610240527f5af15060005450c760006102a0527f6002551309f562610200527f610320527f66a486610140527f6b00610260527f1d457161016102c0527f20527f60005450610340527f1c641d373c7f60045450610220527f6000600155610280527f6102e0527f6005610360527f54610160527f50600160025560085450610140527f60006002610240527f6103610380527e527f6102a0527f557fd86000606000527e600060610180527e600060005af16103a0527f508612610320527f17145147356102c0527f610260527f610160527f5198a37e6103c0527f127a7efa7c600052610340527f6101a0527f606020527f6102e0527f606020536103e0527f60610280527ff760215360ff60610360527f225360610180527fdb6023536037610400527f60610300527f6101c0527f24536075606102610380527fa0527f2553609f6060610420527f40527f265360fe60275360610320527f8f60286101a0526103a0527f7f536061610440527f01e0527f6102c0527f0b6029536060602a53600060610340527f2b536103c052610460527f7f6060602c53606052606060805360006061016102e0527f610200527fc05261610480527f036103e0527f60527f7f81536060608253602d608353605360845360606085536104a0527f60fd61030052610400527f7f6086610380527f536060610220527f60875361016104c0527fe0527f602e608853605360610420527f89536060608a61036103a0527f20527f6104e0527f53602f608b536060608c610240527f53610440527f6000608d5360f361020052610500527f60606103c0527f610220610340527f53608e610221610460527f536053610222610520527f536060610260527f610223536103e0527f600061022453606061610480527f03610540527f60527f61022553608f61022653606061022753600061610400527f0261028061610560527f04a0527f527f28536060610229610380527f53600061022a5360f561022b5360610580527f610420526104c0527f7f6061022c53600061022d536102a0527f60606103a0526105a0527f7f61022e53600061026104e0527f2f610440527f5360606102305360006102316105c0527f53606061023253600061026103c0610500527f527fc0527f61610460527f02336105e0527f53606061023453600061023553608561023653610520527f605a610237536061610600527f03e052610480527f7ff261026102e0526038610300536053610540527f610301610620527f536060610302536050610303536104a0527f60610400527f6161030453610560610640527f527f6002610305536039610306536053610307536060616104c0527f03085360610660527f5061610580527f610420527f030953606161030a53600261030b53603a61030c610680527f536104e0527f606105a0527f5361030d53606161030e610440527f53600261036106a0527f0f53603b61031053606061616105c0527f0500527f03115360006103125360f36106c0527f61031353616104605260036104805360146105e0527f61048153610520527f606106e0527f60610482536000610483536060610484536000610485610600527f5360f06104610700527f86536060610540527f610487536000610488536060610489536000610620527f610720527f61048a53606061048b5360006104610560527f8c53606061048d53600061048e610740527f610640527f53608461048f53605a6104905360f4610491536105805260606105610760527fa053605061610660527f05a15360616105a25360046105a35360926105a45360610780527f536105a55360606105a6610680527f5360506105a75360616105a853600461056107a0527fa95360936105aa5360536105ab53606106a0527f616105ac5360046105ad53606107c0527f946105ae5360606105af5360006105b05360f3616106c05260056106e05360b16107e0527f6106e15360536106e25360606106e35360006106e45360616106e55360056106610800527fe65360b26106e75360606106e85360006106e95360606106ea5360006106eb53610820527f60f56106ec5360606106ed5360006106ee5360606106ef5360006106f0536060610840527f6106f15360006106f25360606106f35360006106f45360606106f55360006106610860527ff65360856106f753605a6106f85360f16106f95360506106fa5360506106fb536108805260616108a05360066108a15360fc6108a25360606108a35360006108a45360f36108a5536108a660006000f060006000600060006000855af15050", + "storage": {}, + "balance": "0x0", + "nonce": "0x0" + }, + "0xa94f5374fce5edbc8e2a8697c15331677e6ebf0b": { + "code": "0x", + "storage": {}, + "balance": "0xffffffffff", + "nonce": "0x0" + } + }, + "transaction": { + "sender": "0xa94f5374fce5edbc8e2a8697c15331677e6ebf0b", + "gasPrice": "0x10", + "nonce": "0x0", + "to": "0x00000000000000000000000000000000000000f1", + "data": [ + "0x81fbe24d1e33d7944b2e62ee0ff24811bbbcf8cb311e5617c80623dec4477cc14849fc042b9bbaebca9f03f66cca76c46353c5a68c2e134ef75f8c2425d9702f3a4bd3c5527e93d27579bdbd7d237eaa1c0278fce26479aaf11fb8d00e7478" + ], + "gasLimit": [ + "0xb9a0b" + ], + "value": [ + "0x01" + ], + "secretKey": "0x45a915e4d060149eb4365960e6a7a45f334393093061116b197e3240065ff2d8" + }, + "out": "0x", + "post": { + "London": [ + { + "hash": "0x0000000000000000000000000000000000000000000000000000000000000000", + "logs": "0x0000000000000000000000000000000000000000000000000000000000000000", + "indexes": { + "data": 0, + "gas": 0, + "value": 0 + } + } + ] + } + } +} \ No newline at end of file diff --git a/cmd/geth/chaincmd.go b/cmd/geth/chaincmd.go index 477c55681f..91773224c8 100644 --- a/cmd/geth/chaincmd.go +++ b/cmd/geth/chaincmd.go @@ -46,7 +46,6 @@ import ( "github.com/ethereum/go-ethereum/ethdb" "github.com/ethereum/go-ethereum/internal/era" "github.com/ethereum/go-ethereum/log" - "github.com/ethereum/go-ethereum/metrics" "github.com/ethereum/go-ethereum/node" "github.com/ethereum/go-ethereum/p2p/enode" "github.com/ethereum/go-ethereum/params" @@ -514,14 +513,12 @@ func importChain(ctx *cli.Context) error { if ctx.Args().Len() < 1 { utils.Fatalf("This command requires an argument.") } - // Start metrics export if enabled - utils.SetupMetrics(ctx) - // Start system runtime metrics collection - go metrics.CollectProcessMetrics(3 * time.Second) - stack, cfg := makeConfigNode(ctx) defer stack.Close() + // Start metrics export if enabled + utils.SetupMetrics(&cfg.Metrics) + backend, err := eth.New(stack, &cfg.Eth) if err != nil { return err diff --git a/cmd/geth/config.go b/cmd/geth/config.go index 150affda5c..24a6b25316 100644 --- a/cmd/geth/config.go +++ b/cmd/geth/config.go @@ -234,6 +234,9 @@ func makeFullNode(ctx *cli.Context) (*node.Node, ethapi.Backend) { params.FixedTurnLength = ctx.Uint64(utils.OverrideFixedTurnLength.Name) } + // Start metrics export if enabled + utils.SetupMetrics(&cfg.Metrics) + backend, eth := utils.RegisterEthService(stack, &cfg.Eth) // Create gauge with geth system and build information @@ -273,7 +276,7 @@ func makeFullNode(ctx *cli.Context) (*node.Node, ethapi.Backend) { } git, _ := version.VCS() - utils.SetupMetrics(ctx, + utils.SetupMetrics(&cfg.Metrics, utils.EnableBuildInfo(git.Commit, git.Date), utils.EnableMinerInfo(ctx, &cfg.Eth.Miner), utils.EnableNodeInfo(&cfg.Eth.TxPool, stack.Server().NodeInfo()), @@ -354,6 +357,27 @@ func applyMetricConfig(ctx *cli.Context, cfg *gethConfig) { if ctx.IsSet(utils.MetricsInfluxDBOrganizationFlag.Name) { cfg.Metrics.InfluxDBOrganization = ctx.String(utils.MetricsInfluxDBOrganizationFlag.Name) } + // Sanity-check the commandline flags. It is fine if some unused fields is part + // of the toml-config, but we expect the commandline to only contain relevant + // arguments, otherwise it indicates an error. + var ( + enableExport = ctx.Bool(utils.MetricsEnableInfluxDBFlag.Name) + enableExportV2 = ctx.Bool(utils.MetricsEnableInfluxDBV2Flag.Name) + ) + if enableExport || enableExportV2 { + v1FlagIsSet := ctx.IsSet(utils.MetricsInfluxDBUsernameFlag.Name) || + ctx.IsSet(utils.MetricsInfluxDBPasswordFlag.Name) + + v2FlagIsSet := ctx.IsSet(utils.MetricsInfluxDBTokenFlag.Name) || + ctx.IsSet(utils.MetricsInfluxDBOrganizationFlag.Name) || + ctx.IsSet(utils.MetricsInfluxDBBucketFlag.Name) + + if enableExport && v2FlagIsSet { + utils.Fatalf("Flags --influxdb.metrics.organization, --influxdb.metrics.token, --influxdb.metrics.bucket are only available for influxdb-v2") + } else if enableExportV2 && v1FlagIsSet { + utils.Fatalf("Flags --influxdb.metrics.username, --influxdb.metrics.password are only available for influxdb-v1") + } + } } func setAccountManagerBackends(conf *node.Config, am *accounts.Manager, keydir string) error { diff --git a/cmd/utils/flags.go b/cmd/utils/flags.go index baefc0aa7a..2ca0f86346 100644 --- a/cmd/utils/flags.go +++ b/cmd/utils/flags.go @@ -50,7 +50,6 @@ import ( "github.com/ethereum/go-ethereum/crypto" "github.com/ethereum/go-ethereum/crypto/kzg4844" "github.com/ethereum/go-ethereum/eth" - "github.com/ethereum/go-ethereum/eth/downloader" "github.com/ethereum/go-ethereum/eth/ethconfig" "github.com/ethereum/go-ethereum/eth/filters" "github.com/ethereum/go-ethereum/eth/gasprice" @@ -66,7 +65,7 @@ import ( "github.com/ethereum/go-ethereum/metrics" "github.com/ethereum/go-ethereum/metrics/exp" "github.com/ethereum/go-ethereum/metrics/influxdb" - "github.com/ethereum/go-ethereum/miner" + "github.com/ethereum/go-ethereum/miner/minerconfig" "github.com/ethereum/go-ethereum/node" "github.com/ethereum/go-ethereum/p2p" "github.com/ethereum/go-ethereum/p2p/enode" @@ -1852,7 +1851,7 @@ func setBlobPool(ctx *cli.Context, cfg *blobpool.Config) { } } -func setMiner(ctx *cli.Context, cfg *miner.Config) { +func setMiner(ctx *cli.Context, cfg *minerconfig.Config) { if ctx.IsSet(MinerExtraDataFlag.Name) { cfg.ExtraData = []byte(ctx.String(MinerExtraDataFlag.Name)) } @@ -1985,7 +1984,7 @@ func SetEthConfig(ctx *cli.Context, stack *node.Node, cfg *ethconfig.Config) { godebug.SetGCPercent(int(gogc)) if ctx.IsSet(SyncTargetFlag.Name) { - cfg.SyncMode = downloader.FullSync // dev sync target forces full sync + cfg.SyncMode = ethconfig.FullSync // dev sync target forces full sync } else if ctx.IsSet(SyncModeFlag.Name) { if err = cfg.SyncMode.UnmarshalText([]byte(ctx.String(SyncModeFlag.Name))); err != nil { Fatalf("invalid --syncmode flag: %v", err) @@ -2011,7 +2010,7 @@ func SetEthConfig(ctx *cli.Context, stack *node.Node, cfg *ethconfig.Config) { cfg.DiffBlock = ctx.Uint64(DiffBlockFlag.Name) } if ctx.IsSet(PruneAncientDataFlag.Name) { - if cfg.SyncMode == downloader.FullSync { + if cfg.SyncMode == ethconfig.FullSync { cfg.PruneAncientData = ctx.Bool(PruneAncientDataFlag.Name) } else { log.Crit("pruneancient parameter can only be used with syncmode=full") @@ -2102,9 +2101,9 @@ func SetEthConfig(ctx *cli.Context, stack *node.Node, cfg *ethconfig.Config) { cfg.DisableSnapProtocol = true } - if cfg.SyncMode == downloader.SnapSync && cfg.TriesVerifyMode.NoTries() { + if cfg.SyncMode == ethconfig.SnapSync && cfg.TriesVerifyMode.NoTries() { log.Warn("Only local TriesVerifyMode can support snap sync, resetting to full sync", "mode", cfg.TriesVerifyMode) - cfg.SyncMode = downloader.FullSync + cfg.SyncMode = ethconfig.FullSync } } if ctx.IsSet(CacheFlag.Name) || ctx.IsSet(CacheSnapshotFlag.Name) { @@ -2115,7 +2114,7 @@ func SetEthConfig(ctx *cli.Context, stack *node.Node, cfg *ethconfig.Config) { } if !ctx.Bool(SnapshotFlag.Name) || cfg.SnapshotCache == 0 { // If snap-sync is requested, this flag is also required - if cfg.SyncMode == downloader.SnapSync { + if cfg.SyncMode == ethconfig.SnapSync { if !ctx.Bool(SnapshotFlag.Name) { log.Warn("Snap sync requested, enabling --snapshot") } @@ -2175,7 +2174,7 @@ func SetEthConfig(ctx *cli.Context, stack *node.Node, cfg *ethconfig.Config) { if !ctx.IsSet(NetworkIdFlag.Name) { cfg.NetworkId = 1337 } - cfg.SyncMode = downloader.FullSync + cfg.SyncMode = ethconfig.FullSync // Create new developer account or reuse existing one var ( developer accounts.Account @@ -2344,7 +2343,7 @@ func EnableBuildInfo(gitCommit, gitDate string) SetupMetricsOption { } } -func EnableMinerInfo(ctx *cli.Context, minerConfig *miner.Config) SetupMetricsOption { +func EnableMinerInfo(ctx *cli.Context, minerConfig *minerconfig.Config) SetupMetricsOption { return func() { if ctx.Bool(MiningEnabledFlag.Name) { // register miner info into metrics @@ -2447,74 +2446,59 @@ func parseMiningFeatures(ctx *cli.Context, cfg *ethconfig.Config) string { return strings.Join(features, "|") } -func SetupMetrics(ctx *cli.Context, options ...SetupMetricsOption) { - if metrics.Enabled { - log.Info("Enabling metrics collection") - - var ( - enableExport = ctx.Bool(MetricsEnableInfluxDBFlag.Name) - enableExportV2 = ctx.Bool(MetricsEnableInfluxDBV2Flag.Name) - ) - - if enableExport || enableExportV2 { - CheckExclusive(ctx, MetricsEnableInfluxDBFlag, MetricsEnableInfluxDBV2Flag) - - v1FlagIsSet := ctx.IsSet(MetricsInfluxDBUsernameFlag.Name) || - ctx.IsSet(MetricsInfluxDBPasswordFlag.Name) - - v2FlagIsSet := ctx.IsSet(MetricsInfluxDBTokenFlag.Name) || - ctx.IsSet(MetricsInfluxDBOrganizationFlag.Name) || - ctx.IsSet(MetricsInfluxDBBucketFlag.Name) - - if enableExport && v2FlagIsSet { - Fatalf("Flags --influxdb.metrics.organization, --influxdb.metrics.token, --influxdb.metrics.bucket are only available for influxdb-v2") - } else if enableExportV2 && v1FlagIsSet { - Fatalf("Flags --influxdb.metrics.username, --influxdb.metrics.password are only available for influxdb-v1") - } - } - - var ( - endpoint = ctx.String(MetricsInfluxDBEndpointFlag.Name) - database = ctx.String(MetricsInfluxDBDatabaseFlag.Name) - username = ctx.String(MetricsInfluxDBUsernameFlag.Name) - password = ctx.String(MetricsInfluxDBPasswordFlag.Name) - - token = ctx.String(MetricsInfluxDBTokenFlag.Name) - bucket = ctx.String(MetricsInfluxDBBucketFlag.Name) - organization = ctx.String(MetricsInfluxDBOrganizationFlag.Name) - ) - - if enableExport { - tagsMap := SplitTagsFlag(ctx.String(MetricsInfluxDBTagsFlag.Name)) - - log.Info("Enabling metrics export to InfluxDB") - - go influxdb.InfluxDBWithTags(metrics.DefaultRegistry, 10*time.Second, endpoint, database, username, password, "geth.", tagsMap) - } else if enableExportV2 { - tagsMap := SplitTagsFlag(ctx.String(MetricsInfluxDBTagsFlag.Name)) - - log.Info("Enabling metrics export to InfluxDB (v2)") - - go influxdb.InfluxDBV2WithTags(metrics.DefaultRegistry, 10*time.Second, endpoint, token, bucket, organization, "geth.", tagsMap) - } +func SetupMetrics(cfg *metrics.Config, options ...SetupMetricsOption) { + if !cfg.Enabled { + return + } + log.Info("Enabling metrics collection") + metrics.Enable() - if ctx.IsSet(MetricsHTTPFlag.Name) { - address := net.JoinHostPort(ctx.String(MetricsHTTPFlag.Name), fmt.Sprintf("%d", ctx.Int(MetricsPortFlag.Name))) - log.Info("Enabling stand-alone metrics HTTP endpoint", "address", address) - exp.Setup(address) - } else if ctx.IsSet(MetricsPortFlag.Name) { - log.Warn(fmt.Sprintf("--%s specified without --%s, metrics server will not start.", MetricsPortFlag.Name, MetricsHTTPFlag.Name)) - } + // InfluxDB exporter. + var ( + enableExport = cfg.EnableInfluxDB + enableExportV2 = cfg.EnableInfluxDBV2 + ) + if cfg.EnableInfluxDB && cfg.EnableInfluxDBV2 { + Fatalf("Flags %v can't be used at the same time", strings.Join([]string{MetricsEnableInfluxDBFlag.Name, MetricsEnableInfluxDBV2Flag.Name}, ", ")) + } + var ( + endpoint = cfg.InfluxDBEndpoint + database = cfg.InfluxDBDatabase + username = cfg.InfluxDBUsername + password = cfg.InfluxDBPassword + + token = cfg.InfluxDBToken + bucket = cfg.InfluxDBBucket + organization = cfg.InfluxDBOrganization + tagsMap = SplitTagsFlag(cfg.InfluxDBTags) + ) + if enableExport { + log.Info("Enabling metrics export to InfluxDB") + go influxdb.InfluxDBWithTags(metrics.DefaultRegistry, 10*time.Second, endpoint, database, username, password, "geth.", tagsMap) + } else if enableExportV2 { + tagsMap := SplitTagsFlag(cfg.InfluxDBTags) + log.Info("Enabling metrics export to InfluxDB (v2)") + go influxdb.InfluxDBV2WithTags(metrics.DefaultRegistry, 10*time.Second, endpoint, token, bucket, organization, "geth.", tagsMap) + } - for _, opt := range options { - opt() - } + // Expvar exporter. + if cfg.HTTP != "" { + address := net.JoinHostPort(cfg.HTTP, fmt.Sprintf("%d", cfg.Port)) + log.Info("Enabling stand-alone metrics HTTP endpoint", "address", address) + exp.Setup(address) + } else if cfg.HTTP == "" && cfg.Port != 0 { + log.Warn(fmt.Sprintf("--%s specified without --%s, metrics server will not start.", MetricsPortFlag.Name, MetricsHTTPFlag.Name)) + } - // Start system runtime metrics collection - go metrics.CollectProcessMetrics(3 * time.Second) + for _, opt := range options { + opt() } + + // Enable system metrics collection. + go metrics.CollectProcessMetrics(3 * time.Second) } +// SplitTagsFlag parses a comma-separated list of k=v metrics tags. func SplitTagsFlag(tagsFlag string) map[string]string { tags := strings.Split(tagsFlag, ",") tagsMap := map[string]string{} diff --git a/consensus/parlia/parlia.go b/consensus/parlia/parlia.go index 20067baf39..5279bde204 100644 --- a/consensus/parlia/parlia.go +++ b/consensus/parlia/parlia.go @@ -21,7 +21,6 @@ import ( "github.com/willf/bitset" "golang.org/x/crypto/sha3" - "github.com/ethereum/go-ethereum" "github.com/ethereum/go-ethereum/accounts" "github.com/ethereum/go-ethereum/accounts/abi" "github.com/ethereum/go-ethereum/common" @@ -1925,33 +1924,31 @@ func (p *Parlia) distributeToValidator(amount *big.Int, validator common.Address } // get system message -func (p *Parlia) getSystemMessage(from, toAddress common.Address, data []byte, value *big.Int) callmsg { - return callmsg{ - ethereum.CallMsg{ - From: from, - Gas: math.MaxUint64 / 2, - GasPrice: big.NewInt(0), - Value: value, - To: &toAddress, - Data: data, - }, +func (p *Parlia) getSystemMessage(from, toAddress common.Address, data []byte, value *big.Int) *core.Message { + return &core.Message{ + From: from, + GasLimit: math.MaxUint64 / 2, + GasPrice: big.NewInt(0), + Value: value, + To: &toAddress, + Data: data, } } func (p *Parlia) applyTransaction( - msg callmsg, + msg *core.Message, state vm.StateDB, header *types.Header, chainContext core.ChainContext, txs *[]*types.Transaction, receipts *[]*types.Receipt, receivedTxs *[]*types.Transaction, usedGas *uint64, mining bool, ) (err error) { - nonce := state.GetNonce(msg.From()) - expectedTx := types.NewTransaction(nonce, *msg.To(), msg.Value(), msg.Gas(), msg.GasPrice(), msg.Data()) + nonce := state.GetNonce(msg.From) + expectedTx := types.NewTransaction(nonce, *msg.To, msg.Value, msg.GasLimit, msg.GasPrice, msg.Data) expectedHash := p.signer.Hash(expectedTx) - if msg.From() == p.val && mining { - expectedTx, err = p.signTxFn(accounts.Account{Address: msg.From()}, expectedTx, p.chainConfig.ChainID) + if msg.From == p.val && mining { + expectedTx, err = p.signTxFn(accounts.Account{Address: msg.From}, expectedTx, p.chainConfig.ChainID) if err != nil { return err } @@ -2137,23 +2134,9 @@ func (c chainContext) GetHeader(hash common.Hash, number uint64) *types.Header { return c.Chain.GetHeader(hash, number) } -// callmsg implements core.Message to allow passing it as a transaction simulator. -type callmsg struct { - ethereum.CallMsg -} - -func (m callmsg) From() common.Address { return m.CallMsg.From } -func (m callmsg) Nonce() uint64 { return 0 } -func (m callmsg) CheckNonce() bool { return false } -func (m callmsg) To() *common.Address { return m.CallMsg.To } -func (m callmsg) GasPrice() *big.Int { return m.CallMsg.GasPrice } -func (m callmsg) Gas() uint64 { return m.CallMsg.Gas } -func (m callmsg) Value() *big.Int { return m.CallMsg.Value } -func (m callmsg) Data() []byte { return m.CallMsg.Data } - // apply message func applyMessage( - msg callmsg, + msg *core.Message, state vm.StateDB, header *types.Header, chainConfig *params.ChainConfig, @@ -2163,28 +2146,29 @@ func applyMessage( context := core.NewEVMBlockContext(header, chainContext, nil) // Create a new environment which holds all relevant information // about the transaction and calling mechanisms. - vmenv := vm.NewEVM(context, vm.TxContext{Origin: msg.From(), GasPrice: big.NewInt(0)}, state, chainConfig, vm.Config{}) + evm := vm.NewEVM(context, state, chainConfig, vm.Config{}) + evm.SetTxContext(core.NewEVMTxContext(msg)) // Apply the transaction to the current state (included in the env) if chainConfig.IsCancun(header.Number, header.Time) { - rules := vmenv.ChainConfig().Rules(vmenv.Context.BlockNumber, vmenv.Context.Random != nil, vmenv.Context.Time) - state.Prepare(rules, msg.From(), vmenv.Context.Coinbase, msg.To(), vm.ActivePrecompiles(rules), msg.AccessList) + rules := evm.ChainConfig().Rules(evm.Context.BlockNumber, evm.Context.Random != nil, evm.Context.Time) + state.Prepare(rules, msg.From, evm.Context.Coinbase, msg.To, vm.ActivePrecompiles(rules), msg.AccessList) } else { state.ClearAccessList() } // Increment the nonce for the next transaction - state.SetNonce(msg.From(), state.GetNonce(msg.From())+1) - - ret, returnGas, err := vmenv.Call( - vm.AccountRef(msg.From()), - *msg.To(), - msg.Data(), - msg.Gas(), - uint256.MustFromBig(msg.Value()), + state.SetNonce(msg.From, state.GetNonce(msg.From)+1) + + ret, returnGas, err := evm.Call( + vm.AccountRef(msg.From), + *msg.To, + msg.Data, + msg.GasLimit, + uint256.MustFromBig(msg.Value), ) if err != nil { log.Error("apply message failed", "msg", string(ret), "err", err) } - return msg.Gas() - returnGas, err + return msg.GasLimit - returnGas, err } // proposalKey build a key which is a combination of the block number and the proposer address. diff --git a/console/console_test.go b/console/console_test.go index 6e14e09729..1442440991 100644 --- a/console/console_test.go +++ b/console/console_test.go @@ -31,7 +31,7 @@ import ( "github.com/ethereum/go-ethereum/eth" "github.com/ethereum/go-ethereum/eth/ethconfig" "github.com/ethereum/go-ethereum/internal/jsre" - "github.com/ethereum/go-ethereum/miner" + "github.com/ethereum/go-ethereum/miner/minerconfig" "github.com/ethereum/go-ethereum/node" ) @@ -95,7 +95,7 @@ func newTester(t *testing.T, confOverride func(*ethconfig.Config)) *tester { } ethConf := ðconfig.Config{ Genesis: core.DeveloperGenesisBlock(11_500_000, nil), - Miner: miner.Config{ + Miner: minerconfig.Config{ Etherbase: common.HexToAddress(testAddress), }, } diff --git a/core/block_validator.go b/core/block_validator.go index 0b92993a55..6e96d9658e 100644 --- a/core/block_validator.go +++ b/core/block_validator.go @@ -111,7 +111,7 @@ func (v *BlockValidator) ValidateBody(block *types.Block) error { } // The individual checks for blob validity (version-check + not empty) - // happens in StateTransition. + // happens in state transition. } // Check blob gas usage. @@ -188,9 +188,7 @@ func (v *BlockValidator) ValidateState(block *types.Block, statedb *state.StateD if receiptSha != header.ReceiptHash { return fmt.Errorf("invalid receipt root hash (remote: %x local: %x)", header.ReceiptHash, receiptSha) } - return nil - }) - validateFuns = append(validateFuns, func() error { + // Validate the parsed requests match the expected header value. if header.RequestsHash != nil { reqhash := types.CalcRequestsHash(res.Requests) diff --git a/core/blockchain.go b/core/blockchain.go index 01e336cad5..e141a48ec4 100644 --- a/core/blockchain.go +++ b/core/blockchain.go @@ -2405,7 +2405,7 @@ func (bc *BlockChain) processBlock(block *types.Block, statedb *state.StateDB, s task := types.NewBlockWithHeader(context).WithBody(*block.Body()) // Run the stateless self-cross-validation - crossStateRoot, crossReceiptRoot, err := ExecuteStateless(bc.chainConfig, task, witness) + crossStateRoot, crossReceiptRoot, err := ExecuteStateless(bc.chainConfig, bc.vmConfig, task, witness) if err != nil { return nil, fmt.Errorf("stateless self-validation failed: %v", err) } diff --git a/core/blockchain_reader.go b/core/blockchain_reader.go index ad247ae58a..105fd5a81a 100644 --- a/core/blockchain_reader.go +++ b/core/blockchain_reader.go @@ -320,6 +320,13 @@ func (bc *BlockChain) GetTransactionLookup(hash common.Hash) (*rawdb.LegacyTxLoo if tx == nil { progress, err := bc.TxIndexProgress() if err != nil { + // No error is returned if the transaction indexing progress is unreachable + // due to unexpected internal errors. In such cases, it is impossible to + // determine whether the transaction does not exist or has simply not been + // indexed yet without a progress marker. + // + // In such scenarios, the transaction is treated as unreachable, though + // this is clearly an unintended and unexpected situation. return nil, nil, nil } // The transaction indexing is not finished yet, returning an @@ -383,10 +390,7 @@ func (bc *BlockChain) stateRecoverable(root common.Hash) bool { // ContractCodeWithPrefix retrieves a blob of data associated with a contract // hash either from ephemeral in-memory cache, or from persistent storage. -// -// If the code doesn't exist in the in-memory cache, check the storage with -// new code scheme. -func (bc *BlockChain) ContractCodeWithPrefix(hash common.Hash) ([]byte, error) { +func (bc *BlockChain) ContractCodeWithPrefix(hash common.Hash) []byte { // TODO(rjl493456442) The associated account address is also required // in Verkle scheme. Fix it once snap-sync is supported for Verkle. return bc.statedb.ContractCodeWithPrefix(common.Address{}, hash) diff --git a/core/chain_makers.go b/core/chain_makers.go index 1d33aef4c2..65201ed96d 100644 --- a/core/chain_makers.go +++ b/core/chain_makers.go @@ -102,11 +102,8 @@ func (b *BlockGen) Difficulty() *big.Int { // block. func (b *BlockGen) SetParentBeaconRoot(root common.Hash) { b.header.ParentBeaconRoot = &root - var ( - blockContext = NewEVMBlockContext(b.header, b.cm, &b.header.Coinbase) - vmenv = vm.NewEVM(blockContext, vm.TxContext{}, b.statedb, b.cm.config, vm.Config{}) - ) - ProcessBeaconBlockRoot(root, vmenv, b.statedb) + blockContext := NewEVMBlockContext(b.header, b.cm, &b.header.Coinbase) + ProcessBeaconBlockRoot(root, vm.NewEVM(blockContext, b.statedb, b.cm.config, vm.Config{})) } // addTx adds a transaction to the generated block. If no coinbase has @@ -120,8 +117,12 @@ func (b *BlockGen) addTx(bc *BlockChain, vmConfig vm.Config, tx *types.Transacti if b.gasPool == nil { b.SetCoinbase(common.Address{}) } + var ( + blockContext = NewEVMBlockContext(b.header, bc, &b.header.Coinbase) + evm = vm.NewEVM(blockContext, b.statedb, b.cm.config, vmConfig) + ) b.statedb.SetTxContext(tx.Hash(), len(b.txs)) - receipt, err := ApplyTransaction(b.cm.config, bc, &b.header.Coinbase, b.gasPool, b.statedb, b.header, tx, &b.header.GasUsed, vmConfig, NewReceiptBloomGenerator()) + receipt, err := ApplyTransaction(evm, b.gasPool, b.statedb, b.header, tx, &b.header.GasUsed, NewReceiptBloomGenerator()) if err != nil { panic(err) } @@ -372,25 +373,22 @@ func GenerateChain(config *params.ChainConfig, parent *types.Block, engine conse var requests [][]byte if config.IsPrague(b.header.Number, b.header.Time) { + requests = [][]byte{} // EIP-6110 deposits var blockLogs []*types.Log for _, r := range b.receipts { blockLogs = append(blockLogs, r.Logs...) } - depositRequests, err := ParseDepositLogs(blockLogs, config) - if err != nil { + if err := ParseDepositLogs(&requests, blockLogs, config); err != nil { panic(fmt.Sprintf("failed to parse deposit log: %v", err)) } - requests = append(requests, depositRequests) // create EVM for system calls blockContext := NewEVMBlockContext(b.header, cm, &b.header.Coinbase) - vmenv := vm.NewEVM(blockContext, vm.TxContext{}, statedb, cm.config, vm.Config{}) - // EIP-7002 withdrawals - withdrawalRequests := ProcessWithdrawalQueue(vmenv, statedb) - requests = append(requests, withdrawalRequests) - // EIP-7251 consolidations - consolidationRequests := ProcessConsolidationQueue(vmenv, statedb) - requests = append(requests, consolidationRequests) + evm := vm.NewEVM(blockContext, statedb, cm.config, vm.Config{}) + // EIP-7002 + ProcessWithdrawalQueue(&requests, evm) + // EIP-7251 + ProcessConsolidationQueue(&requests, evm) } if requests != nil { reqHash := types.CalcRequestsHash(requests) @@ -497,8 +495,8 @@ func GenerateVerkleChain(config *params.ChainConfig, parent *types.Block, engine if config.IsPrague(b.header.Number, b.header.Time) { // EIP-2935 blockContext := NewEVMBlockContext(b.header, cm, &b.header.Coinbase) - vmenv := vm.NewEVM(blockContext, vm.TxContext{}, statedb, cm.config, vm.Config{}) - ProcessParentBlockHash(b.header.ParentHash, vmenv, statedb) + evm := vm.NewEVM(blockContext, statedb, cm.config, vm.Config{}) + ProcessParentBlockHash(b.header.ParentHash, evm) } // Execute any user modifications to the block. diff --git a/core/genesis.go b/core/genesis.go index 4a45d12942..9d625157ea 100644 --- a/core/genesis.go +++ b/core/genesis.go @@ -508,9 +508,7 @@ func (g *Genesis) toBlockWithRoot(root common.Hash) *types.Block { } } if conf.IsPrague(num, g.Timestamp) { - emptyRequests := [][]byte{{0x00}, {0x01}, {0x02}} - rhash := types.CalcRequestsHash(emptyRequests) - head.RequestsHash = &rhash + head.RequestsHash = &types.EmptyRequestsHash } } return types.NewBlock(head, &types.Body{Withdrawals: withdrawals}, nil, trie.NewStackTrie(nil)) diff --git a/core/genesis_test.go b/core/genesis_test.go index e5cd60f5f7..c916387565 100644 --- a/core/genesis_test.go +++ b/core/genesis_test.go @@ -317,7 +317,7 @@ func TestVerkleGenesisCommit(t *testing.T) { }, } - expected := common.FromHex("4a83dc39eb688dbcfaf581d60e82de18f875e38786ebce5833342011d6fef37b") + expected := common.FromHex("018d20eebb130b5e2b796465fe36aafab650650729a92435aec071bf2386f080") got := genesis.ToBlock().Root().Bytes() if !bytes.Equal(got, expected) { t.Fatalf("invalid genesis state root, expected %x, got %x", expected, got) diff --git a/core/rawdb/ancient_utils.go b/core/rawdb/ancient_utils.go index deac0ad782..3d642e10b0 100644 --- a/core/rawdb/ancient_utils.go +++ b/core/rawdb/ancient_utils.go @@ -179,7 +179,7 @@ func ResetStateFreezerTableOffset(ancient string, virtualTail uint64) error { for name, disableSnappy := range tables { log.Info("Handle table", "name", name, "disableSnappy", disableSnappy) - table, err := newTable(path, name, metrics.NilMeter{}, metrics.NilMeter{}, metrics.NilGauge{}, freezerTableSize, disableSnappy, false) + table, err := newTable(path, name, metrics.NewInactiveMeter(), metrics.NewInactiveMeter(), metrics.NewGauge(), freezerTableSize, disableSnappy, false) if err != nil { log.Error("New table failed", "error", err) return err diff --git a/core/rawdb/freezer.go b/core/rawdb/freezer.go index c455f58a9b..5ea1d5a244 100644 --- a/core/rawdb/freezer.go +++ b/core/rawdb/freezer.go @@ -173,7 +173,7 @@ func NewFreezer(datadir string, namespace string, readonly bool, offset uint64, } // openAdditionTable create table, it will auto create new files when it was first initialized -func openAdditionTable(datadir, name string, readMeter, writeMeter metrics.Meter, sizeGauge metrics.Gauge, maxTableSize uint32, disableSnappy, readonly bool) (*freezerTable, error) { +func openAdditionTable(datadir, name string, readMeter, writeMeter *metrics.Meter, sizeGauge *metrics.Gauge, maxTableSize uint32, disableSnappy, readonly bool) (*freezerTable, error) { if readonly { f, err := newTable(datadir, name, readMeter, writeMeter, sizeGauge, maxTableSize, disableSnappy, false) if err != nil { diff --git a/core/rawdb/freezer_table.go b/core/rawdb/freezer_table.go index 45569eb539..04781c0e07 100644 --- a/core/rawdb/freezer_table.go +++ b/core/rawdb/freezer_table.go @@ -115,10 +115,10 @@ type freezerTable struct { headId uint32 // number of the currently active head file tailId uint32 // number of the earliest file - headBytes int64 // Number of bytes written to the head file - readMeter metrics.Meter // Meter for measuring the effective amount of data read - writeMeter metrics.Meter // Meter for measuring the effective amount of data written - sizeGauge metrics.Gauge // Gauge for tracking the combined size of all freezer tables + headBytes int64 // Number of bytes written to the head file + readMeter *metrics.Meter // Meter for measuring the effective amount of data read + writeMeter *metrics.Meter // Meter for measuring the effective amount of data written + sizeGauge *metrics.Gauge // Gauge for tracking the combined size of all freezer tables logger log.Logger // Logger with database path and table name embedded lock sync.RWMutex // Mutex protecting the data file descriptors @@ -126,18 +126,18 @@ type freezerTable struct { // newFreezerTable opens the given path as a freezer table. func newFreezerTable(path, name string, disableSnappy, readonly bool) (*freezerTable, error) { - return newTable(path, name, metrics.NilMeter{}, metrics.NilMeter{}, metrics.NilGauge{}, freezerTableSize, disableSnappy, readonly) + return newTable(path, name, metrics.NewInactiveMeter(), metrics.NewInactiveMeter(), metrics.NewGauge(), freezerTableSize, disableSnappy, readonly) } // newAdditionTable opens the given path as a addition table. func newAdditionTable(path, name string, disableSnappy, readonly bool) (*freezerTable, error) { - return openAdditionTable(path, name, metrics.NilMeter{}, metrics.NilMeter{}, metrics.NilGauge{}, freezerTableSize, disableSnappy, readonly) + return openAdditionTable(path, name, metrics.NewInactiveMeter(), metrics.NewInactiveMeter(), metrics.NewGauge(), freezerTableSize, disableSnappy, readonly) } // newTable opens a freezer table, creating the data and index files if they are // non-existent. Both files are truncated to the shortest common length to ensure // they don't go out of sync. -func newTable(path string, name string, readMeter metrics.Meter, writeMeter metrics.Meter, sizeGauge metrics.Gauge, maxFilesize uint32, noCompression, readonly bool) (*freezerTable, error) { +func newTable(path string, name string, readMeter, writeMeter *metrics.Meter, sizeGauge *metrics.Gauge, maxFilesize uint32, noCompression, readonly bool) (*freezerTable, error) { // Ensure the containing directory exists and open the indexEntry file if err := os.MkdirAll(path, 0755); err != nil { return nil, err diff --git a/core/remote_state_verifier.go b/core/remote_state_verifier.go index 41bae89a9e..59057b9f88 100644 --- a/core/remote_state_verifier.go +++ b/core/remote_state_verifier.go @@ -448,7 +448,7 @@ func (mode VerifyMode) NoTries() bool { return mode != LocalVerify } -func newVerifyMsgTypeGauge(msgType uint16, peerId string) metrics.Gauge { +func newVerifyMsgTypeGauge(msgType uint16, peerId string) *metrics.Gauge { m := fmt.Sprintf("verifymanager/message/%d/peer/%s", msgType, peerId) return metrics.GetOrRegisterGauge(m, nil) } diff --git a/core/state/database.go b/core/state/database.go index 2bbac99b04..64baa62e4d 100644 --- a/core/state/database.go +++ b/core/state/database.go @@ -17,7 +17,6 @@ package state import ( - "errors" "fmt" "github.com/ethereum/go-ethereum/common" @@ -55,12 +54,6 @@ type Database interface { // OpenStorageTrie opens the storage trie of an account. OpenStorageTrie(stateRoot common.Hash, address common.Address, root common.Hash, trie Trie) (Trie, error) - // ContractCode retrieves a particular contract's code. - ContractCode(addr common.Address, codeHash common.Hash) ([]byte, error) - - // ContractCodeSize retrieves a particular contracts code's size. - ContractCodeSize(addr common.Address, codeHash common.Hash) (int, error) - // PointCache returns the cache holding points used in verkle tree key computation PointCache() *utils.PointCache @@ -187,15 +180,24 @@ func NewDatabaseForTesting() *CachingDB { // Reader returns a state reader associated with the specified state root. func (db *CachingDB) Reader(stateRoot common.Hash) (Reader, error) { - var readers []Reader + var readers []StateReader // Set up the state snapshot reader if available. This feature // is optional and may be partially useful if it's not fully // generated. if db.snap != nil { + // If standalone state snapshot is available (hash scheme), + // then construct the legacy snap reader. snap := db.snap.Snapshot(stateRoot) if snap != nil { - readers = append(readers, newStateReader(snap)) // snap reader is optional + readers = append(readers, newFlatReader(snap)) + } + } else { + // If standalone state snapshot is not available, try to construct + // the state reader with database. + reader, err := db.triedb.StateReader(stateRoot) + if err == nil { + readers = append(readers, newFlatReader(reader)) // state reader is optional } } if !db.NoTries() { @@ -208,7 +210,11 @@ func (db *CachingDB) Reader(stateRoot common.Hash) (Reader, error) { readers = append(readers, tr) } - return newMultiReader(readers...) + combined, err := newMultiStateReader(readers...) + if err != nil { + return nil, err + } + return newReader(newCachingCodeReader(db.disk, db.codeCache, db.codeSizeCache), combined), nil } // OpenTrie opens the main account trie at a specific root hash. @@ -249,45 +255,20 @@ func (db *CachingDB) NoTries() bool { return db.noTries } -// ContractCode retrieves a particular contract's code. -func (db *CachingDB) ContractCode(address common.Address, codeHash common.Hash) ([]byte, error) { - code, _ := db.codeCache.Get(codeHash) - if len(code) > 0 { - return code, nil - } - code = rawdb.ReadCode(db.disk, codeHash) - if len(code) > 0 { - db.codeCache.Add(codeHash, code) - db.codeSizeCache.Add(codeHash, len(code)) - return code, nil - } - return nil, errors.New("not found") -} - // ContractCodeWithPrefix retrieves a particular contract's code. If the // code can't be found in the cache, then check the existence with **new** // db scheme. -func (db *CachingDB) ContractCodeWithPrefix(address common.Address, codeHash common.Hash) ([]byte, error) { +func (db *CachingDB) ContractCodeWithPrefix(address common.Address, codeHash common.Hash) []byte { code, _ := db.codeCache.Get(codeHash) if len(code) > 0 { - return code, nil + return code } code = rawdb.ReadCodeWithPrefix(db.disk, codeHash) if len(code) > 0 { db.codeCache.Add(codeHash, code) db.codeSizeCache.Add(codeHash, len(code)) - return code, nil - } - return nil, errors.New("not found") -} - -// ContractCodeSize retrieves a particular contracts code's size. -func (db *CachingDB) ContractCodeSize(addr common.Address, codeHash common.Hash) (int, error) { - if cached, ok := db.codeSizeCache.Get(codeHash); ok { - return cached, nil } - code, err := db.ContractCode(addr, codeHash) - return len(code), err + return code } // TrieDB retrieves any intermediate trie-node caching layer. diff --git a/core/state/iterator.go b/core/state/iterator.go index 83c552ca1a..5ea52c6183 100644 --- a/core/state/iterator.go +++ b/core/state/iterator.go @@ -136,10 +136,13 @@ func (it *nodeIterator) step() error { } if !bytes.Equal(account.CodeHash, types.EmptyCodeHash.Bytes()) { it.codeHash = common.BytesToHash(account.CodeHash) - it.code, err = it.state.db.ContractCode(address, common.BytesToHash(account.CodeHash)) + it.code, err = it.state.reader.Code(address, common.BytesToHash(account.CodeHash)) if err != nil { return fmt.Errorf("code %x: %v", account.CodeHash, err) } + if len(it.code) == 0 { + return fmt.Errorf("code is not found: %x", account.CodeHash) + } } it.accountHash = it.stateIt.Parent() return nil diff --git a/core/state/reader.go b/core/state/reader.go index 85842adde8..a0f15dfcc8 100644 --- a/core/state/reader.go +++ b/core/state/reader.go @@ -18,11 +18,13 @@ package state import ( "errors" - "maps" "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/common/lru" + "github.com/ethereum/go-ethereum/core/rawdb" "github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/crypto" + "github.com/ethereum/go-ethereum/ethdb" "github.com/ethereum/go-ethereum/rlp" "github.com/ethereum/go-ethereum/trie" "github.com/ethereum/go-ethereum/trie/utils" @@ -30,9 +32,26 @@ import ( "github.com/ethereum/go-ethereum/triedb/database" ) -// Reader defines the interface for accessing accounts and storage slots +// ContractCodeReader defines the interface for accessing contract code. +type ContractCodeReader interface { + // Code retrieves a particular contract's code. + // + // - Returns nil code along with nil error if the requested contract code + // doesn't exist + // - Returns an error only if an unexpected issue occurs + Code(addr common.Address, codeHash common.Hash) ([]byte, error) + + // CodeSize retrieves a particular contracts code's size. + // + // - Returns zero code size along with nil error if the requested contract code + // doesn't exist + // - Returns an error only if an unexpected issue occurs + CodeSize(addr common.Address, codeHash common.Hash) (int, error) +} + +// StateReader defines the interface for accessing accounts and storage slots // associated with a specific state. -type Reader interface { +type StateReader interface { // Account retrieves the account associated with a particular address. // // - Returns a nil account if it does not exist @@ -47,32 +66,84 @@ type Reader interface { // - Returns an error only if an unexpected issue occurs // - The returned storage slot is safe to modify after the call Storage(addr common.Address, slot common.Hash) (common.Hash, error) +} + +// Reader defines the interface for accessing accounts, storage slots and contract +// code associated with a specific state. +type Reader interface { + ContractCodeReader + StateReader +} + +// cachingCodeReader implements ContractCodeReader, accessing contract code either in +// local key-value store or the shared code cache. +type cachingCodeReader struct { + db ethdb.KeyValueReader + + // These caches could be shared by multiple code reader instances, + // they are natively thread-safe. + codeCache *lru.SizeConstrainedCache[common.Hash, []byte] + codeSizeCache *lru.Cache[common.Hash, int] +} + +// newCachingCodeReader constructs the code reader. +func newCachingCodeReader(db ethdb.KeyValueReader, codeCache *lru.SizeConstrainedCache[common.Hash, []byte], codeSizeCache *lru.Cache[common.Hash, int]) *cachingCodeReader { + return &cachingCodeReader{ + db: db, + codeCache: codeCache, + codeSizeCache: codeSizeCache, + } +} + +// Code implements ContractCodeReader, retrieving a particular contract's code. +// If the contract code doesn't exist, no error will be returned. +func (r *cachingCodeReader) Code(addr common.Address, codeHash common.Hash) ([]byte, error) { + code, _ := r.codeCache.Get(codeHash) + if len(code) > 0 { + return code, nil + } + code = rawdb.ReadCode(r.db, codeHash) + if len(code) > 0 { + r.codeCache.Add(codeHash, code) + r.codeSizeCache.Add(codeHash, len(code)) + } + return code, nil +} - // Copy returns a deep-copied state reader. - Copy() Reader +// CodeSize implements ContractCodeReader, retrieving a particular contracts code's size. +// If the contract code doesn't exist, no error will be returned. +func (r *cachingCodeReader) CodeSize(addr common.Address, codeHash common.Hash) (int, error) { + if cached, ok := r.codeSizeCache.Get(codeHash); ok { + return cached, nil + } + code, err := r.Code(addr, codeHash) + if err != nil { + return 0, err + } + return len(code), nil } -// stateReader wraps a database state reader. -type stateReader struct { +// flatReader wraps a database state reader. +type flatReader struct { reader database.StateReader buff crypto.KeccakState } -// newStateReader constructs a state reader with on the given state root. -func newStateReader(reader database.StateReader) *stateReader { - return &stateReader{ +// newFlatReader constructs a state reader with on the given state root. +func newFlatReader(reader database.StateReader) *flatReader { + return &flatReader{ reader: reader, buff: crypto.NewKeccakState(), } } -// Account implements Reader, retrieving the account specified by the address. +// Account implements StateReader, retrieving the account specified by the address. // // An error will be returned if the associated snapshot is already stale or // the requested account is not yet covered by the snapshot. // // The returned account might be nil if it's not existent. -func (r *stateReader) Account(addr common.Address) (*types.StateAccount, error) { +func (r *flatReader) Account(addr common.Address) (*types.StateAccount, error) { account, err := r.reader.Account(crypto.HashData(r.buff, addr.Bytes())) if err != nil { return nil, err @@ -95,14 +166,14 @@ func (r *stateReader) Account(addr common.Address) (*types.StateAccount, error) return acct, nil } -// Storage implements Reader, retrieving the storage slot specified by the +// Storage implements StateReader, retrieving the storage slot specified by the // address and slot key. // // An error will be returned if the associated snapshot is already stale or // the requested storage slot is not yet covered by the snapshot. // // The returned storage slot might be empty if it's not existent. -func (r *stateReader) Storage(addr common.Address, key common.Hash) (common.Hash, error) { +func (r *flatReader) Storage(addr common.Address, key common.Hash) (common.Hash, error) { addrHash := crypto.HashData(r.buff, addr.Bytes()) slotHash := crypto.HashData(r.buff, key.Bytes()) ret, err := r.reader.Storage(addrHash, slotHash) @@ -123,15 +194,7 @@ func (r *stateReader) Storage(addr common.Address, key common.Hash) (common.Hash return value, nil } -// Copy implements Reader, returning a deep-copied snap reader. -func (r *stateReader) Copy() Reader { - return &stateReader{ - reader: r.reader, - buff: crypto.NewKeccakState(), - } -} - -// trieReader implements the Reader interface, providing functions to access +// trieReader implements the StateReader interface, providing functions to access // state from the referenced trie. type trieReader struct { root common.Hash // State root which uniquely represent a state @@ -167,7 +230,7 @@ func newTrieReader(root common.Hash, db *triedb.Database, cache *utils.PointCach }, nil } -// Account implements Reader, retrieving the account specified by the address. +// Account implements StateReader, retrieving the account specified by the address. // // An error will be returned if the trie state is corrupted. An nil account // will be returned if it's not existent in the trie. @@ -184,7 +247,7 @@ func (r *trieReader) Account(addr common.Address) (*types.StateAccount, error) { return account, nil } -// Storage implements Reader, retrieving the storage slot specified by the +// Storage implements StateReader, retrieving the storage slot specified by the // address and slot key. // // An error will be returned if the trie state is corrupted. An empty storage @@ -227,48 +290,32 @@ func (r *trieReader) Storage(addr common.Address, key common.Hash) (common.Hash, return value, nil } -// Copy implements Reader, returning a deep-copied trie reader. -func (r *trieReader) Copy() Reader { - tries := make(map[common.Address]Trie) - for addr, tr := range r.subTries { - tries[addr] = mustCopyTrie(tr) - } - return &trieReader{ - root: r.root, - db: r.db, - buff: crypto.NewKeccakState(), - mainTrie: mustCopyTrie(r.mainTrie), - subRoots: maps.Clone(r.subRoots), - subTries: tries, - } +// multiStateReader is the aggregation of a list of StateReader interface, +// providing state access by leveraging all readers. The checking priority +// is determined by the position in the reader list. +type multiStateReader struct { + readers []StateReader // List of state readers, sorted by checking priority } -// multiReader is the aggregation of a list of Reader interface, providing state -// access by leveraging all readers. The checking priority is determined by the -// position in the reader list. -type multiReader struct { - readers []Reader // List of readers, sorted by checking priority -} - -// newMultiReader constructs a multiReader instance with the given readers. The -// priority among readers is assumed to be sorted. Note, it must contain at least -// one reader for constructing a multiReader. -func newMultiReader(readers ...Reader) (*multiReader, error) { +// newMultiStateReader constructs a multiStateReader instance with the given +// readers. The priority among readers is assumed to be sorted. Note, it must +// contain at least one reader for constructing a multiStateReader. +func newMultiStateReader(readers ...StateReader) (*multiStateReader, error) { if len(readers) == 0 { return nil, errors.New("empty reader set") } - return &multiReader{ + return &multiStateReader{ readers: readers, }, nil } -// Account implementing Reader interface, retrieving the account associated with -// a particular address. +// Account implementing StateReader interface, retrieving the account associated +// with a particular address. // // - Returns a nil account if it does not exist // - Returns an error only if an unexpected issue occurs // - The returned account is safe to modify after the call -func (r *multiReader) Account(addr common.Address) (*types.StateAccount, error) { +func (r *multiStateReader) Account(addr common.Address) (*types.StateAccount, error) { var errs []error for _, reader := range r.readers { acct, err := reader.Account(addr) @@ -280,13 +327,13 @@ func (r *multiReader) Account(addr common.Address) (*types.StateAccount, error) return nil, errors.Join(errs...) } -// Storage implementing Reader interface, retrieving the storage slot associated -// with a particular account address and slot key. +// Storage implementing StateReader interface, retrieving the storage slot +// associated with a particular account address and slot key. // // - Returns an empty slot if it does not exist // - Returns an error only if an unexpected issue occurs // - The returned storage slot is safe to modify after the call -func (r *multiReader) Storage(addr common.Address, slot common.Hash) (common.Hash, error) { +func (r *multiStateReader) Storage(addr common.Address, slot common.Hash) (common.Hash, error) { var errs []error for _, reader := range r.readers { slot, err := reader.Storage(addr, slot) @@ -298,11 +345,16 @@ func (r *multiReader) Storage(addr common.Address, slot common.Hash) (common.Has return common.Hash{}, errors.Join(errs...) } -// Copy implementing Reader interface, returning a deep-copied state reader. -func (r *multiReader) Copy() Reader { - var readers []Reader - for _, reader := range r.readers { - readers = append(readers, reader.Copy()) +// reader is the wrapper of ContractCodeReader and StateReader interface. +type reader struct { + ContractCodeReader + StateReader +} + +// newReader constructs a reader with the supplied code reader and state reader. +func newReader(codeReader ContractCodeReader, stateReader StateReader) *reader { + return &reader{ + ContractCodeReader: codeReader, + StateReader: stateReader, } - return &multiReader{readers: readers} } diff --git a/core/state/snapshot/conversion.go b/core/state/snapshot/conversion.go index 3a0d6e342f..526e8f2ef4 100644 --- a/core/state/snapshot/conversion.go +++ b/core/state/snapshot/conversion.go @@ -52,16 +52,6 @@ type ( leafCallbackFn func(db ethdb.KeyValueWriter, accountHash, codeHash common.Hash, stat *generateStats) (common.Hash, error) ) -// GenerateAccountTrieRoot takes an account iterator and reproduces the root hash. -func GenerateAccountTrieRoot(it AccountIterator) (common.Hash, error) { - return generateTrieRoot(nil, "", it, common.Hash{}, stackTrieGenerate, nil, newGenerateStats(), true) -} - -// GenerateStorageTrieRoot takes a storage iterator and reproduces the root hash. -func GenerateStorageTrieRoot(account common.Hash, it StorageIterator) (common.Hash, error) { - return generateTrieRoot(nil, "", it, account, stackTrieGenerate, nil, newGenerateStats(), true) -} - // GenerateTrie takes the whole snapshot tree as the input, traverses all the // accounts as well as the corresponding storages and regenerate the whole state // (account trie + all storage tries). diff --git a/core/state/snapshot/difflayer.go b/core/state/snapshot/difflayer.go index ea77b649fd..4f1c6b850b 100644 --- a/core/state/snapshot/difflayer.go +++ b/core/state/snapshot/difflayer.go @@ -30,6 +30,7 @@ import ( "github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/rlp" bloomfilter "github.com/holiman/bloomfilter/v2" + "golang.org/x/exp/maps" ) var ( @@ -73,23 +74,14 @@ var ( // bloom key for an account/slot. This is randomized at init(), so that the // global population of nodes do not all display the exact same behaviour with // regards to bloom content - bloomDestructHasherOffset = 0 - bloomAccountHasherOffset = 0 - bloomStorageHasherOffset = 0 + bloomAccountHasherOffset = 0 + bloomStorageHasherOffset = 0 ) func init() { // Init the bloom offsets in the range [0:24] (requires 8 bytes) - bloomDestructHasherOffset = rand.Intn(25) bloomAccountHasherOffset = rand.Intn(25) bloomStorageHasherOffset = rand.Intn(25) - - // The destruct and account blooms must be different, as the storage slots - // will check for destruction too for every bloom miss. It should not collide - // with modified accounts. - for bloomAccountHasherOffset == bloomDestructHasherOffset { - bloomAccountHasherOffset = rand.Intn(25) - } } // diffLayer represents a collection of modifications made to a state snapshot @@ -106,29 +98,16 @@ type diffLayer struct { root common.Hash // Root hash to which this snapshot diff belongs to stale atomic.Bool // Signals that the layer became stale (state progressed) - // destructSet is a very special helper marker. If an account is marked as - // deleted, then it's recorded in this set. However it's allowed that an account - // is included here but still available in other sets(e.g. storageData). The - // reason is the diff layer includes all the changes in a *block*. It can - // happen that in the tx_1, account A is self-destructed while in the tx_2 - // it's recreated. But we still need this marker to indicate the "old" A is - // deleted, all data in other set belongs to the "new" A. - destructSet map[common.Hash]struct{} // Keyed markers for deleted (and potentially) recreated accounts - accountList []common.Hash // List of account for iteration. If it exists, it's sorted, otherwise it's nil accountData map[common.Hash][]byte // Keyed accounts for direct retrieval (nil means deleted) - storageList map[common.Hash][]common.Hash // List of storage slots for iterated retrievals, one per account. Any existing lists are sorted if non-nil storageData map[common.Hash]map[common.Hash][]byte // Keyed storage slots for direct retrieval. one per account (nil means deleted) + accountList []common.Hash // List of account for iteration. If it exists, it's sorted, otherwise it's nil + storageList map[common.Hash][]common.Hash // List of storage slots for iterated retrievals, one per account. Any existing lists are sorted if non-nil diffed *bloomfilter.Filter // Bloom filter tracking all the diffed items up to the disk layer lock sync.RWMutex } -// destructBloomHash is used to convert a destruct event into a 64 bit mini hash. -func destructBloomHash(h common.Hash) uint64 { - return binary.BigEndian.Uint64(h[bloomDestructHasherOffset : bloomDestructHasherOffset+8]) -} - // accountBloomHash is used to convert an account hash into a 64 bit mini hash. func accountBloomHash(h common.Hash) uint64 { return binary.BigEndian.Uint64(h[bloomAccountHasherOffset : bloomAccountHasherOffset+8]) @@ -142,12 +121,11 @@ func storageBloomHash(h0, h1 common.Hash) uint64 { // newDiffLayer creates a new diff on top of an existing snapshot, whether that's a low // level persistent database or a hierarchical diff already. -func newDiffLayer(parent snapshot, root common.Hash, destructs map[common.Hash]struct{}, accounts map[common.Hash][]byte, storage map[common.Hash]map[common.Hash][]byte) *diffLayer { +func newDiffLayer(parent snapshot, root common.Hash, accounts map[common.Hash][]byte, storage map[common.Hash]map[common.Hash][]byte) *diffLayer { // Create the new layer with some pre-allocated data segments dl := &diffLayer{ parent: parent, root: root, - destructSet: destructs, accountData: accounts, storageData: storage, storageList: make(map[common.Hash][]common.Hash), @@ -163,10 +141,7 @@ func newDiffLayer(parent snapshot, root common.Hash, destructs map[common.Hash]s } // Sanity check that accounts or storage slots are never nil - for accountHash, blob := range accounts { - if blob == nil { - panic(fmt.Sprintf("account %#x nil", accountHash)) - } + for _, blob := range accounts { // Determine memory size and track the dirty writes dl.memory += uint64(common.HashLength + len(blob)) snapshotDirtyAccountWriteMeter.Mark(int64(len(blob))) @@ -181,7 +156,6 @@ func newDiffLayer(parent snapshot, root common.Hash, destructs map[common.Hash]s snapshotDirtyStorageWriteMeter.Mark(int64(len(data))) } } - dl.memory += uint64(len(destructs) * common.HashLength) return dl } @@ -206,10 +180,6 @@ func (dl *diffLayer) rebloom(origin *diskLayer) { } else { dl.diffed, _ = bloomfilter.New(uint64(bloomSize), uint64(bloomFuncs)) } - // Iterate over all the accounts and storage slots and index them - for hash := range dl.destructSet { - dl.diffed.AddHash(destructBloomHash(hash)) - } for hash := range dl.accountData { dl.diffed.AddHash(accountBloomHash(hash)) } @@ -294,11 +264,8 @@ func (dl *diffLayer) AccountRLP(hash common.Hash) ([]byte, error) { } // Check the bloom filter first whether there's even a point in reaching into // all the maps in all the layers below - hit := dl.diffed.ContainsHash(accountBloomHash(hash)) - if !hit { - hit = dl.diffed.ContainsHash(destructBloomHash(hash)) - } var origin *diskLayer + hit := dl.diffed.ContainsHash(accountBloomHash(hash)) if !hit { origin = dl.origin // extract origin while holding the lock } @@ -330,18 +297,14 @@ func (dl *diffLayer) accountRLP(hash common.Hash, depth int) ([]byte, error) { if data, ok := dl.accountData[hash]; ok { snapshotDirtyAccountHitMeter.Mark(1) snapshotDirtyAccountHitDepthHist.Update(int64(depth)) - snapshotDirtyAccountReadMeter.Mark(int64(len(data))) + if n := len(data); n > 0 { + snapshotDirtyAccountReadMeter.Mark(int64(n)) + } else { + snapshotDirtyAccountInexMeter.Mark(1) + } snapshotBloomAccountTrueHitMeter.Mark(1) return data, nil } - // If the account is known locally, but deleted, return it - if _, ok := dl.destructSet[hash]; ok { - snapshotDirtyAccountHitMeter.Mark(1) - snapshotDirtyAccountHitDepthHist.Update(int64(depth)) - snapshotDirtyAccountInexMeter.Mark(1) - snapshotBloomAccountTrueHitMeter.Mark(1) - return nil, nil - } // Account unknown to this diff, resolve from parent if diff, ok := dl.parent.(*diffLayer); ok { return diff.accountRLP(hash, depth+1) @@ -365,11 +328,8 @@ func (dl *diffLayer) Storage(accountHash, storageHash common.Hash) ([]byte, erro dl.lock.RUnlock() return nil, ErrSnapshotStale } - hit := dl.diffed.ContainsHash(storageBloomHash(accountHash, storageHash)) - if !hit { - hit = dl.diffed.ContainsHash(destructBloomHash(accountHash)) - } var origin *diskLayer + hit := dl.diffed.ContainsHash(storageBloomHash(accountHash, storageHash)) if !hit { origin = dl.origin // extract origin while holding the lock } @@ -411,14 +371,6 @@ func (dl *diffLayer) storage(accountHash, storageHash common.Hash, depth int) ([ return data, nil } } - // If the account is known locally, but deleted, return an empty slot - if _, ok := dl.destructSet[accountHash]; ok { - snapshotDirtyStorageHitMeter.Mark(1) - //snapshotDirtyStorageHitDepthHist.Update(int64(depth)) - snapshotDirtyStorageInexMeter.Mark(1) - snapshotBloomStorageTrueHitMeter.Mark(1) - return nil, nil - } // Storage slot unknown to this diff, resolve from parent if diff, ok := dl.parent.(*diffLayer); ok { return diff.storage(accountHash, storageHash, depth+1) @@ -430,8 +382,8 @@ func (dl *diffLayer) storage(accountHash, storageHash common.Hash, depth int) ([ // Update creates a new layer on top of the existing snapshot diff tree with // the specified data items. -func (dl *diffLayer) Update(blockRoot common.Hash, destructs map[common.Hash]struct{}, accounts map[common.Hash][]byte, storage map[common.Hash]map[common.Hash][]byte) *diffLayer { - return newDiffLayer(dl, blockRoot, destructs, accounts, storage) +func (dl *diffLayer) Update(blockRoot common.Hash, accounts map[common.Hash][]byte, storage map[common.Hash]map[common.Hash][]byte) *diffLayer { + return newDiffLayer(dl, blockRoot, accounts, storage) } // flatten pushes all data from this point downwards, flattening everything into @@ -456,12 +408,6 @@ func (dl *diffLayer) flatten() snapshot { if parent.stale.Swap(true) { panic("parent diff layer is stale") // we've flattened into the same parent from two children, boo } - // Overwrite all the updated accounts blindly, merge the sorted list - for hash := range dl.destructSet { - parent.destructSet[hash] = struct{}{} - delete(parent.accountData, hash) - delete(parent.storageData, hash) - } for hash, data := range dl.accountData { parent.accountData[hash] = data } @@ -473,17 +419,13 @@ func (dl *diffLayer) flatten() snapshot { continue } // Storage exists in both parent and child, merge the slots - comboData := parent.storageData[accountHash] - for storageHash, data := range storage { - comboData[storageHash] = data - } + maps.Copy(parent.storageData[accountHash], storage) } // Return the combo parent return &diffLayer{ parent: parent.parent, origin: parent.origin, root: dl.root, - destructSet: parent.destructSet, accountData: parent.accountData, storageData: parent.storageData, storageList: make(map[common.Hash][]common.Hash), @@ -509,15 +451,7 @@ func (dl *diffLayer) AccountList() []common.Hash { dl.lock.Lock() defer dl.lock.Unlock() - dl.accountList = make([]common.Hash, 0, len(dl.destructSet)+len(dl.accountData)) - for hash := range dl.accountData { - dl.accountList = append(dl.accountList, hash) - } - for hash := range dl.destructSet { - if _, ok := dl.accountData[hash]; !ok { - dl.accountList = append(dl.accountList, hash) - } - } + dl.accountList = maps.Keys(dl.accountData) slices.SortFunc(dl.accountList, common.Hash.Cmp) dl.memory += uint64(len(dl.accountList) * common.HashLength) return dl.accountList @@ -532,18 +466,17 @@ func (dl *diffLayer) AccountList() []common.Hash { // not empty but the flag is true. // // Note, the returned slice is not a copy, so do not modify it. -func (dl *diffLayer) StorageList(accountHash common.Hash) ([]common.Hash, bool) { +func (dl *diffLayer) StorageList(accountHash common.Hash) []common.Hash { dl.lock.RLock() - _, destructed := dl.destructSet[accountHash] if _, ok := dl.storageData[accountHash]; !ok { // Account not tracked by this layer dl.lock.RUnlock() - return nil, destructed + return nil } // If an old list already exists, return it if list, exist := dl.storageList[accountHash]; exist { dl.lock.RUnlock() - return list, destructed // the cached list can't be nil + return list // the cached list can't be nil } dl.lock.RUnlock() @@ -551,13 +484,9 @@ func (dl *diffLayer) StorageList(accountHash common.Hash) ([]common.Hash, bool) dl.lock.Lock() defer dl.lock.Unlock() - storageMap := dl.storageData[accountHash] - storageList := make([]common.Hash, 0, len(storageMap)) - for k := range storageMap { - storageList = append(storageList, k) - } + storageList := maps.Keys(dl.storageData[accountHash]) slices.SortFunc(storageList, common.Hash.Cmp) dl.storageList[accountHash] = storageList dl.memory += uint64(len(dl.storageList)*common.HashLength + common.HashLength) - return storageList, destructed + return storageList } diff --git a/core/state/snapshot/difflayer_test.go b/core/state/snapshot/difflayer_test.go index 674a031b16..b212098e75 100644 --- a/core/state/snapshot/difflayer_test.go +++ b/core/state/snapshot/difflayer_test.go @@ -28,14 +28,6 @@ import ( "github.com/ethereum/go-ethereum/ethdb/memorydb" ) -func copyDestructs(destructs map[common.Hash]struct{}) map[common.Hash]struct{} { - copy := make(map[common.Hash]struct{}) - for hash := range destructs { - copy[hash] = struct{}{} - } - return copy -} - func copyAccounts(accounts map[common.Hash][]byte) map[common.Hash][]byte { copy := make(map[common.Hash][]byte) for hash, blob := range accounts { @@ -58,9 +50,8 @@ func copyStorage(storage map[common.Hash]map[common.Hash][]byte) map[common.Hash // TestMergeBasics tests some simple merges func TestMergeBasics(t *testing.T) { var ( - destructs = make(map[common.Hash]struct{}) - accounts = make(map[common.Hash][]byte) - storage = make(map[common.Hash]map[common.Hash][]byte) + accounts = make(map[common.Hash][]byte) + storage = make(map[common.Hash]map[common.Hash][]byte) ) // Fill up a parent for i := 0; i < 100; i++ { @@ -69,7 +60,7 @@ func TestMergeBasics(t *testing.T) { accounts[h] = data if rand.Intn(4) == 0 { - destructs[h] = struct{}{} + accounts[h] = nil } if rand.Intn(2) == 0 { accStorage := make(map[common.Hash][]byte) @@ -80,11 +71,12 @@ func TestMergeBasics(t *testing.T) { } } // Add some (identical) layers on top - parent := newDiffLayer(emptyLayer(), common.Hash{}, copyDestructs(destructs), copyAccounts(accounts), copyStorage(storage)) - child := newDiffLayer(parent, common.Hash{}, copyDestructs(destructs), copyAccounts(accounts), copyStorage(storage)) - child = newDiffLayer(child, common.Hash{}, copyDestructs(destructs), copyAccounts(accounts), copyStorage(storage)) - child = newDiffLayer(child, common.Hash{}, copyDestructs(destructs), copyAccounts(accounts), copyStorage(storage)) - child = newDiffLayer(child, common.Hash{}, copyDestructs(destructs), copyAccounts(accounts), copyStorage(storage)) + parent := newDiffLayer(emptyLayer(), common.Hash{}, copyAccounts(accounts), copyStorage(storage)) + child := newDiffLayer(parent, common.Hash{}, copyAccounts(accounts), copyStorage(storage)) + child = newDiffLayer(child, common.Hash{}, copyAccounts(accounts), copyStorage(storage)) + child = newDiffLayer(child, common.Hash{}, copyAccounts(accounts), copyStorage(storage)) + child = newDiffLayer(child, common.Hash{}, copyAccounts(accounts), copyStorage(storage)) + // And flatten merged := (child.flatten()).(*diffLayer) @@ -99,18 +91,13 @@ func TestMergeBasics(t *testing.T) { t.Errorf("accountList [2] wrong: have %v, want %v", have, want) } } - { // Check account drops - if have, want := len(merged.destructSet), len(destructs); have != want { - t.Errorf("accountDrop wrong: have %v, want %v", have, want) - } - } { // Check storage lists i := 0 for aHash, sMap := range storage { if have, want := len(merged.storageList), i; have != want { t.Errorf("[1] storageList wrong: have %v, want %v", have, want) } - list, _ := merged.StorageList(aHash) + list := merged.StorageList(aHash) if have, want := len(list), len(sMap); have != want { t.Errorf("[2] StorageList() wrong: have %v, want %v", have, want) } @@ -124,41 +111,32 @@ func TestMergeBasics(t *testing.T) { // TestMergeDelete tests some deletion func TestMergeDelete(t *testing.T) { - var ( - storage = make(map[common.Hash]map[common.Hash][]byte) - ) + storage := make(map[common.Hash]map[common.Hash][]byte) + // Fill up a parent h1 := common.HexToHash("0x01") h2 := common.HexToHash("0x02") - flipDrops := func() map[common.Hash]struct{} { - return map[common.Hash]struct{}{ - h2: {}, - } - } - flipAccs := func() map[common.Hash][]byte { + flip := func() map[common.Hash][]byte { return map[common.Hash][]byte{ h1: randomAccount(), + h2: nil, } } - flopDrops := func() map[common.Hash]struct{} { - return map[common.Hash]struct{}{ - h1: {}, - } - } - flopAccs := func() map[common.Hash][]byte { + flop := func() map[common.Hash][]byte { return map[common.Hash][]byte{ + h1: nil, h2: randomAccount(), } } // Add some flipAccs-flopping layers on top - parent := newDiffLayer(emptyLayer(), common.Hash{}, flipDrops(), flipAccs(), storage) - child := parent.Update(common.Hash{}, flopDrops(), flopAccs(), storage) - child = child.Update(common.Hash{}, flipDrops(), flipAccs(), storage) - child = child.Update(common.Hash{}, flopDrops(), flopAccs(), storage) - child = child.Update(common.Hash{}, flipDrops(), flipAccs(), storage) - child = child.Update(common.Hash{}, flopDrops(), flopAccs(), storage) - child = child.Update(common.Hash{}, flipDrops(), flipAccs(), storage) + parent := newDiffLayer(emptyLayer(), common.Hash{}, flip(), storage) + child := parent.Update(common.Hash{}, flop(), storage) + child = child.Update(common.Hash{}, flip(), storage) + child = child.Update(common.Hash{}, flop(), storage) + child = child.Update(common.Hash{}, flip(), storage) + child = child.Update(common.Hash{}, flop(), storage) + child = child.Update(common.Hash{}, flip(), storage) if data, _ := child.Account(h1); data == nil { t.Errorf("last diff layer: expected %x account to be non-nil", h1) @@ -166,12 +144,7 @@ func TestMergeDelete(t *testing.T) { if data, _ := child.Account(h2); data != nil { t.Errorf("last diff layer: expected %x account to be nil", h2) } - if _, ok := child.destructSet[h1]; ok { - t.Errorf("last diff layer: expected %x drop to be missing", h1) - } - if _, ok := child.destructSet[h2]; !ok { - t.Errorf("last diff layer: expected %x drop to be present", h1) - } + // And flatten merged := (child.flatten()).(*diffLayer) @@ -181,12 +154,6 @@ func TestMergeDelete(t *testing.T) { if data, _ := merged.Account(h2); data != nil { t.Errorf("merged layer: expected %x account to be nil", h2) } - if _, ok := merged.destructSet[h1]; !ok { // Note, drops stay alive until persisted to disk! - t.Errorf("merged diff layer: expected %x drop to be present", h1) - } - if _, ok := merged.destructSet[h2]; !ok { // Note, drops stay alive until persisted to disk! - t.Errorf("merged diff layer: expected %x drop to be present", h1) - } // If we add more granular metering of memory, we can enable this again, // but it's not implemented for now //if have, want := merged.memory, child.memory; have != want { @@ -206,22 +173,20 @@ func TestInsertAndMerge(t *testing.T) { ) { var ( - destructs = make(map[common.Hash]struct{}) - accounts = make(map[common.Hash][]byte) - storage = make(map[common.Hash]map[common.Hash][]byte) + accounts = make(map[common.Hash][]byte) + storage = make(map[common.Hash]map[common.Hash][]byte) ) - parent = newDiffLayer(emptyLayer(), common.Hash{}, destructs, accounts, storage) + parent = newDiffLayer(emptyLayer(), common.Hash{}, accounts, storage) } { var ( - destructs = make(map[common.Hash]struct{}) - accounts = make(map[common.Hash][]byte) - storage = make(map[common.Hash]map[common.Hash][]byte) + accounts = make(map[common.Hash][]byte) + storage = make(map[common.Hash]map[common.Hash][]byte) ) accounts[acc] = randomAccount() storage[acc] = make(map[common.Hash][]byte) storage[acc][slot] = []byte{0x01} - child = newDiffLayer(parent, common.Hash{}, destructs, accounts, storage) + child = newDiffLayer(parent, common.Hash{}, accounts, storage) } // And flatten merged := (child.flatten()).(*diffLayer) @@ -250,14 +215,13 @@ func BenchmarkSearch(b *testing.B) { // First, we set up 128 diff layers, with 1K items each fill := func(parent snapshot) *diffLayer { var ( - destructs = make(map[common.Hash]struct{}) - accounts = make(map[common.Hash][]byte) - storage = make(map[common.Hash]map[common.Hash][]byte) + accounts = make(map[common.Hash][]byte) + storage = make(map[common.Hash]map[common.Hash][]byte) ) for i := 0; i < 10000; i++ { accounts[randomHash()] = randomAccount() } - return newDiffLayer(parent, common.Hash{}, destructs, accounts, storage) + return newDiffLayer(parent, common.Hash{}, accounts, storage) } var layer snapshot layer = emptyLayer() @@ -286,9 +250,8 @@ func BenchmarkSearchSlot(b *testing.B) { accountRLP := randomAccount() fill := func(parent snapshot) *diffLayer { var ( - destructs = make(map[common.Hash]struct{}) - accounts = make(map[common.Hash][]byte) - storage = make(map[common.Hash]map[common.Hash][]byte) + accounts = make(map[common.Hash][]byte) + storage = make(map[common.Hash]map[common.Hash][]byte) ) accounts[accountKey] = accountRLP @@ -299,7 +262,7 @@ func BenchmarkSearchSlot(b *testing.B) { accStorage[randomHash()] = value storage[accountKey] = accStorage } - return newDiffLayer(parent, common.Hash{}, destructs, accounts, storage) + return newDiffLayer(parent, common.Hash{}, accounts, storage) } var layer snapshot layer = emptyLayer() @@ -320,9 +283,8 @@ func BenchmarkSearchSlot(b *testing.B) { func BenchmarkFlatten(b *testing.B) { fill := func(parent snapshot) *diffLayer { var ( - destructs = make(map[common.Hash]struct{}) - accounts = make(map[common.Hash][]byte) - storage = make(map[common.Hash]map[common.Hash][]byte) + accounts = make(map[common.Hash][]byte) + storage = make(map[common.Hash]map[common.Hash][]byte) ) for i := 0; i < 100; i++ { accountKey := randomHash() @@ -336,7 +298,7 @@ func BenchmarkFlatten(b *testing.B) { } storage[accountKey] = accStorage } - return newDiffLayer(parent, common.Hash{}, destructs, accounts, storage) + return newDiffLayer(parent, common.Hash{}, accounts, storage) } b.ResetTimer() for i := 0; i < b.N; i++ { @@ -369,9 +331,8 @@ func BenchmarkFlatten(b *testing.B) { func BenchmarkJournal(b *testing.B) { fill := func(parent snapshot) *diffLayer { var ( - destructs = make(map[common.Hash]struct{}) - accounts = make(map[common.Hash][]byte) - storage = make(map[common.Hash]map[common.Hash][]byte) + accounts = make(map[common.Hash][]byte) + storage = make(map[common.Hash]map[common.Hash][]byte) ) for i := 0; i < 200; i++ { accountKey := randomHash() @@ -385,7 +346,7 @@ func BenchmarkJournal(b *testing.B) { } storage[accountKey] = accStorage } - return newDiffLayer(parent, common.Hash{}, destructs, accounts, storage) + return newDiffLayer(parent, common.Hash{}, accounts, storage) } layer := snapshot(emptyLayer()) for i := 1; i < 128; i++ { diff --git a/core/state/snapshot/disklayer.go b/core/state/snapshot/disklayer.go index 708f21e3b7..6aaa0e00d7 100644 --- a/core/state/snapshot/disklayer.go +++ b/core/state/snapshot/disklayer.go @@ -186,8 +186,8 @@ func (dl *diskLayer) Storage(accountHash, storageHash common.Hash) ([]byte, erro // Update creates a new layer on top of the existing snapshot diff tree with // the specified data items. Note, the maps are retained by the method to avoid // copying everything. -func (dl *diskLayer) Update(blockHash common.Hash, destructs map[common.Hash]struct{}, accounts map[common.Hash][]byte, storage map[common.Hash]map[common.Hash][]byte) *diffLayer { - return newDiffLayer(dl, blockHash, destructs, accounts, storage) +func (dl *diskLayer) Update(blockHash common.Hash, accounts map[common.Hash][]byte, storage map[common.Hash]map[common.Hash][]byte) *diffLayer { + return newDiffLayer(dl, blockHash, accounts, storage) } // stopGeneration aborts the state snapshot generation if it is currently running. diff --git a/core/state/snapshot/disklayer_test.go b/core/state/snapshot/disklayer_test.go index 168458c405..6d99a90d61 100644 --- a/core/state/snapshot/disklayer_test.go +++ b/core/state/snapshot/disklayer_test.go @@ -117,20 +117,22 @@ func TestDiskMerge(t *testing.T) { base.Storage(conNukeCache, conNukeCacheSlot) // Modify or delete some accounts, flatten everything onto disk - if err := snaps.Update(diffRoot, baseRoot, map[common.Hash]struct{}{ - accDelNoCache: {}, - accDelCache: {}, - conNukeNoCache: {}, - conNukeCache: {}, - }, map[common.Hash][]byte{ - accModNoCache: reverse(accModNoCache[:]), - accModCache: reverse(accModCache[:]), - }, map[common.Hash]map[common.Hash][]byte{ - conModNoCache: {conModNoCacheSlot: reverse(conModNoCacheSlot[:])}, - conModCache: {conModCacheSlot: reverse(conModCacheSlot[:])}, - conDelNoCache: {conDelNoCacheSlot: nil}, - conDelCache: {conDelCacheSlot: nil}, - }); err != nil { + if err := snaps.Update(diffRoot, baseRoot, + map[common.Hash][]byte{ + accDelNoCache: nil, + accDelCache: nil, + conNukeNoCache: nil, + conNukeCache: nil, + accModNoCache: reverse(accModNoCache[:]), + accModCache: reverse(accModCache[:]), + }, map[common.Hash]map[common.Hash][]byte{ + conNukeNoCache: {conNukeNoCacheSlot: nil}, + conNukeCache: {conNukeCacheSlot: nil}, + conModNoCache: {conModNoCacheSlot: reverse(conModNoCacheSlot[:])}, + conModCache: {conModCacheSlot: reverse(conModCacheSlot[:])}, + conDelNoCache: {conDelNoCacheSlot: nil}, + conDelCache: {conDelCacheSlot: nil}, + }); err != nil { t.Fatalf("failed to update snapshot tree: %v", err) } if err := snaps.Cap(diffRoot, 0); err != nil { @@ -340,20 +342,27 @@ func TestDiskPartialMerge(t *testing.T) { assertStorage(conNukeCache, conNukeCacheSlot, conNukeCacheSlot[:]) // Modify or delete some accounts, flatten everything onto disk - if err := snaps.Update(diffRoot, baseRoot, map[common.Hash]struct{}{ - accDelNoCache: {}, - accDelCache: {}, - conNukeNoCache: {}, - conNukeCache: {}, - }, map[common.Hash][]byte{ - accModNoCache: reverse(accModNoCache[:]), - accModCache: reverse(accModCache[:]), - }, map[common.Hash]map[common.Hash][]byte{ - conModNoCache: {conModNoCacheSlot: reverse(conModNoCacheSlot[:])}, - conModCache: {conModCacheSlot: reverse(conModCacheSlot[:])}, - conDelNoCache: {conDelNoCacheSlot: nil}, - conDelCache: {conDelCacheSlot: nil}, - }); err != nil { + if err := snaps.Update(diffRoot, baseRoot, + map[common.Hash][]byte{ + accDelNoCache: nil, + accDelCache: nil, + conNukeNoCache: nil, + conNukeCache: nil, + accModNoCache: reverse(accModNoCache[:]), + accModCache: reverse(accModCache[:]), + }, + map[common.Hash]map[common.Hash][]byte{ + conNukeNoCache: { + conNukeNoCacheSlot: nil, + }, + conNukeCache: { + conNukeCacheSlot: nil, + }, + conModNoCache: {conModNoCacheSlot: reverse(conModNoCacheSlot[:])}, + conModCache: {conModCacheSlot: reverse(conModCacheSlot[:])}, + conDelNoCache: {conDelNoCacheSlot: nil}, + conDelCache: {conDelCacheSlot: nil}, + }); err != nil { t.Fatalf("test %d: failed to update snapshot tree: %v", i, err) } if err := snaps.Cap(diffRoot, 0); err != nil { @@ -462,9 +471,11 @@ func TestDiskGeneratorPersistence(t *testing.T) { }, } // Modify or delete some accounts, flatten everything onto disk - if err := snaps.Update(diffRoot, baseRoot, nil, map[common.Hash][]byte{ - accTwo: accTwo[:], - }, nil); err != nil { + if err := snaps.Update(diffRoot, baseRoot, + map[common.Hash][]byte{ + accTwo: accTwo[:], + }, nil, + ); err != nil { t.Fatalf("failed to update snapshot tree: %v", err) } if err := snaps.Cap(diffRoot, 0); err != nil { @@ -480,11 +491,14 @@ func TestDiskGeneratorPersistence(t *testing.T) { } // Test scenario 2, the disk layer is fully generated // Modify or delete some accounts, flatten everything onto disk - if err := snaps.Update(diffTwoRoot, diffRoot, nil, map[common.Hash][]byte{ - accThree: accThree.Bytes(), - }, map[common.Hash]map[common.Hash][]byte{ - accThree: {accThreeSlot: accThreeSlot.Bytes()}, - }); err != nil { + if err := snaps.Update(diffTwoRoot, diffRoot, + map[common.Hash][]byte{ + accThree: accThree.Bytes(), + }, + map[common.Hash]map[common.Hash][]byte{ + accThree: {accThreeSlot: accThreeSlot.Bytes()}, + }, + ); err != nil { t.Fatalf("failed to update snapshot tree: %v", err) } diskLayer := snaps.layers[snaps.diskRoot()].(*diskLayer) diff --git a/core/state/snapshot/generate_test.go b/core/state/snapshot/generate_test.go index 56abff348d..661610840a 100644 --- a/core/state/snapshot/generate_test.go +++ b/core/state/snapshot/generate_test.go @@ -134,7 +134,7 @@ func checkSnapRoot(t *testing.T, snap *diskLayer, trieRoot common.Hash) { snapRoot, err := generateTrieRoot(nil, "", accIt, common.Hash{}, stackTrieGenerate, func(db ethdb.KeyValueWriter, accountHash, codeHash common.Hash, stat *generateStats) (common.Hash, error) { - storageIt, _ := snap.StorageIterator(accountHash, common.Hash{}) + storageIt := snap.StorageIterator(accountHash, common.Hash{}) defer storageIt.Release() hash, err := generateTrieRoot(nil, "", storageIt, accountHash, stackTrieGenerate, nil, stat, false) diff --git a/core/state/snapshot/iterator.go b/core/state/snapshot/iterator.go index c1a196c7ff..cd9c225844 100644 --- a/core/state/snapshot/iterator.go +++ b/core/state/snapshot/iterator.go @@ -115,6 +115,7 @@ func (it *diffAccountIterator) Next() bool { } // Iterator seems to be still alive, retrieve and cache the live hash it.curHash = it.keys[0] + // key cached, shift the iterator and notify the user of success it.keys = it.keys[1:] return true @@ -135,7 +136,7 @@ func (it *diffAccountIterator) Hash() common.Hash { // This method may _fail_, if the underlying layer has been flattened between // the call to Next and Account. That type of error will set it.Err. // This method assumes that flattening does not delete elements from -// the accountdata mapping (writing nil into it is fine though), and will panic +// the accountData mapping (writing nil into it is fine though), and will panic // if elements have been deleted. // // Note the returned account is not a copy, please don't modify it. @@ -143,10 +144,6 @@ func (it *diffAccountIterator) Account() []byte { it.layer.lock.RLock() blob, ok := it.layer.accountData[it.curHash] if !ok { - if _, ok := it.layer.destructSet[it.curHash]; ok { - it.layer.lock.RUnlock() - return nil - } panic(fmt.Sprintf("iterator referenced non-existent account: %x", it.curHash)) } it.layer.lock.RUnlock() @@ -247,11 +244,11 @@ type diffStorageIterator struct { // "destructed" returned. If it's true then it means the whole storage is // destructed in this layer(maybe recreated too), don't bother deeper layer // for storage retrieval. -func (dl *diffLayer) StorageIterator(account common.Hash, seek common.Hash) (StorageIterator, bool) { +func (dl *diffLayer) StorageIterator(account common.Hash, seek common.Hash) StorageIterator { // Create the storage for this account even it's marked // as destructed. The iterator is for the new one which // just has the same address as the deleted one. - hashes, destructed := dl.StorageList(account) + hashes := dl.StorageList(account) index := sort.Search(len(hashes), func(i int) bool { return bytes.Compare(seek[:], hashes[i][:]) <= 0 }) @@ -260,7 +257,7 @@ func (dl *diffLayer) StorageIterator(account common.Hash, seek common.Hash) (Sto layer: dl, account: account, keys: hashes[index:], - }, destructed + } } // Next steps the iterator forward one element, returning false if exhausted. @@ -339,13 +336,13 @@ type diskStorageIterator struct { // If the whole storage is destructed, then all entries in the disk // layer are deleted already. So the "destructed" flag returned here // is always false. -func (dl *diskLayer) StorageIterator(account common.Hash, seek common.Hash) (StorageIterator, bool) { +func (dl *diskLayer) StorageIterator(account common.Hash, seek common.Hash) StorageIterator { pos := common.TrimRightZeroes(seek[:]) return &diskStorageIterator{ layer: dl, account: account, it: dl.diskdb.NewIterator(append(rawdb.SnapshotStoragePrefix, account.Bytes()...), pos), - }, false + } } // Next steps the iterator forward one element, returning false if exhausted. diff --git a/core/state/snapshot/iterator_binary.go b/core/state/snapshot/iterator_binary.go index 523c7b9946..254c4b860f 100644 --- a/core/state/snapshot/iterator_binary.go +++ b/core/state/snapshot/iterator_binary.go @@ -67,44 +67,17 @@ func (dl *diffLayer) initBinaryAccountIterator(seek common.Hash) Iterator { func (dl *diffLayer) initBinaryStorageIterator(account, seek common.Hash) Iterator { parent, ok := dl.parent.(*diffLayer) if !ok { - // If the storage in this layer is already destructed, discard all - // deeper layers but still return a valid single-branch iterator. - a, destructed := dl.StorageIterator(account, seek) - if destructed { - l := &binaryIterator{ - a: a, - account: account, - } - l.aDone = !l.a.Next() - l.bDone = true - return l - } - // The parent is disk layer, don't need to take care "destructed" - // anymore. - b, _ := dl.Parent().StorageIterator(account, seek) l := &binaryIterator{ - a: a, - b: b, + a: dl.StorageIterator(account, seek), + b: dl.Parent().StorageIterator(account, seek), account: account, } l.aDone = !l.a.Next() l.bDone = !l.b.Next() return l } - // If the storage in this layer is already destructed, discard all - // deeper layers but still return a valid single-branch iterator. - a, destructed := dl.StorageIterator(account, seek) - if destructed { - l := &binaryIterator{ - a: a, - account: account, - } - l.aDone = !l.a.Next() - l.bDone = true - return l - } l := &binaryIterator{ - a: a, + a: dl.StorageIterator(account, seek), b: parent.initBinaryStorageIterator(account, seek), account: account, } diff --git a/core/state/snapshot/iterator_fast.go b/core/state/snapshot/iterator_fast.go index fa0daea7ba..7f7ba876ff 100644 --- a/core/state/snapshot/iterator_fast.go +++ b/core/state/snapshot/iterator_fast.go @@ -90,18 +90,10 @@ func newFastIterator(tree *Tree, root common.Hash, account common.Hash, seek com priority: depth, }) } else { - // If the whole storage is destructed in this layer, don't - // bother deeper layer anymore. But we should still keep - // the iterator for this layer, since the iterator can contain - // some valid slots which belongs to the re-created account. - it, destructed := current.StorageIterator(account, seek) fi.iterators = append(fi.iterators, &weightedIterator{ - it: it, + it: current.StorageIterator(account, seek), priority: depth, }) - if destructed { - break - } } current = current.Parent() } diff --git a/core/state/snapshot/iterator_test.go b/core/state/snapshot/iterator_test.go index f2f4d19f83..b9fe370b86 100644 --- a/core/state/snapshot/iterator_test.go +++ b/core/state/snapshot/iterator_test.go @@ -32,9 +32,8 @@ import ( // TestAccountIteratorBasics tests some simple single-layer(diff and disk) iteration func TestAccountIteratorBasics(t *testing.T) { var ( - destructs = make(map[common.Hash]struct{}) - accounts = make(map[common.Hash][]byte) - storage = make(map[common.Hash]map[common.Hash][]byte) + accounts = make(map[common.Hash][]byte) + storage = make(map[common.Hash]map[common.Hash][]byte) ) // Fill up a parent for i := 0; i < 100; i++ { @@ -42,9 +41,6 @@ func TestAccountIteratorBasics(t *testing.T) { data := randomAccount() accounts[h] = data - if rand.Intn(4) == 0 { - destructs[h] = struct{}{} - } if rand.Intn(2) == 0 { accStorage := make(map[common.Hash][]byte) value := make([]byte, 32) @@ -54,7 +50,7 @@ func TestAccountIteratorBasics(t *testing.T) { } } // Add some (identical) layers on top - diffLayer := newDiffLayer(emptyLayer(), common.Hash{}, copyDestructs(destructs), copyAccounts(accounts), copyStorage(storage)) + diffLayer := newDiffLayer(emptyLayer(), common.Hash{}, copyAccounts(accounts), copyStorage(storage)) it := diffLayer.AccountIterator(common.Hash{}) verifyIterator(t, 100, it, verifyNothing) // Nil is allowed for single layer iterator @@ -95,15 +91,15 @@ func TestStorageIteratorBasics(t *testing.T) { nilStorage[h] = nilstorage } // Add some (identical) layers on top - diffLayer := newDiffLayer(emptyLayer(), common.Hash{}, nil, copyAccounts(accounts), copyStorage(storage)) + diffLayer := newDiffLayer(emptyLayer(), common.Hash{}, copyAccounts(accounts), copyStorage(storage)) for account := range accounts { - it, _ := diffLayer.StorageIterator(account, common.Hash{}) + it := diffLayer.StorageIterator(account, common.Hash{}) verifyIterator(t, 100, it, verifyNothing) // Nil is allowed for single layer iterator } diskLayer := diffToDisk(diffLayer) for account := range accounts { - it, _ := diskLayer.StorageIterator(account, common.Hash{}) + it := diskLayer.StorageIterator(account, common.Hash{}) verifyIterator(t, 100-nilStorage[account], it, verifyNothing) // Nil is allowed for single layer iterator } } @@ -225,13 +221,13 @@ func TestAccountIteratorTraversal(t *testing.T) { }, } // Stack three diff layers on top with various overlaps - snaps.Update(common.HexToHash("0x02"), common.HexToHash("0x01"), nil, + snaps.Update(common.HexToHash("0x02"), common.HexToHash("0x01"), randomAccountSet("0xaa", "0xee", "0xff", "0xf0"), nil) - snaps.Update(common.HexToHash("0x03"), common.HexToHash("0x02"), nil, + snaps.Update(common.HexToHash("0x03"), common.HexToHash("0x02"), randomAccountSet("0xbb", "0xdd", "0xf0"), nil) - snaps.Update(common.HexToHash("0x04"), common.HexToHash("0x03"), nil, + snaps.Update(common.HexToHash("0x04"), common.HexToHash("0x03"), randomAccountSet("0xcc", "0xf0", "0xff"), nil) // Verify the single and multi-layer iterators @@ -272,19 +268,19 @@ func TestStorageIteratorTraversal(t *testing.T) { }, } // Stack three diff layers on top with various overlaps - snaps.Update(common.HexToHash("0x02"), common.HexToHash("0x01"), nil, + snaps.Update(common.HexToHash("0x02"), common.HexToHash("0x01"), randomAccountSet("0xaa"), randomStorageSet([]string{"0xaa"}, [][]string{{"0x01", "0x02", "0x03"}}, nil)) - snaps.Update(common.HexToHash("0x03"), common.HexToHash("0x02"), nil, + snaps.Update(common.HexToHash("0x03"), common.HexToHash("0x02"), randomAccountSet("0xaa"), randomStorageSet([]string{"0xaa"}, [][]string{{"0x04", "0x05", "0x06"}}, nil)) - snaps.Update(common.HexToHash("0x04"), common.HexToHash("0x03"), nil, + snaps.Update(common.HexToHash("0x04"), common.HexToHash("0x03"), randomAccountSet("0xaa"), randomStorageSet([]string{"0xaa"}, [][]string{{"0x01", "0x02", "0x03"}}, nil)) // Verify the single and multi-layer iterators head := snaps.Snapshot(common.HexToHash("0x04")) - diffIter, _ := head.(snapshot).StorageIterator(common.HexToHash("0xaa"), common.Hash{}) + diffIter := head.(snapshot).StorageIterator(common.HexToHash("0xaa"), common.Hash{}) verifyIterator(t, 3, diffIter, verifyNothing) verifyIterator(t, 6, head.(*diffLayer).newBinaryStorageIterator(common.HexToHash("0xaa"), common.Hash{}), verifyStorage) @@ -357,14 +353,14 @@ func TestAccountIteratorTraversalValues(t *testing.T) { } } // Assemble a stack of snapshots from the account layers - snaps.Update(common.HexToHash("0x02"), common.HexToHash("0x01"), nil, a, nil) - snaps.Update(common.HexToHash("0x03"), common.HexToHash("0x02"), nil, b, nil) - snaps.Update(common.HexToHash("0x04"), common.HexToHash("0x03"), nil, c, nil) - snaps.Update(common.HexToHash("0x05"), common.HexToHash("0x04"), nil, d, nil) - snaps.Update(common.HexToHash("0x06"), common.HexToHash("0x05"), nil, e, nil) - snaps.Update(common.HexToHash("0x07"), common.HexToHash("0x06"), nil, f, nil) - snaps.Update(common.HexToHash("0x08"), common.HexToHash("0x07"), nil, g, nil) - snaps.Update(common.HexToHash("0x09"), common.HexToHash("0x08"), nil, h, nil) + snaps.Update(common.HexToHash("0x02"), common.HexToHash("0x01"), a, nil) + snaps.Update(common.HexToHash("0x03"), common.HexToHash("0x02"), b, nil) + snaps.Update(common.HexToHash("0x04"), common.HexToHash("0x03"), c, nil) + snaps.Update(common.HexToHash("0x05"), common.HexToHash("0x04"), d, nil) + snaps.Update(common.HexToHash("0x06"), common.HexToHash("0x05"), e, nil) + snaps.Update(common.HexToHash("0x07"), common.HexToHash("0x06"), f, nil) + snaps.Update(common.HexToHash("0x08"), common.HexToHash("0x07"), g, nil) + snaps.Update(common.HexToHash("0x09"), common.HexToHash("0x08"), h, nil) it, _ := snaps.AccountIterator(common.HexToHash("0x09"), common.Hash{}) head := snaps.Snapshot(common.HexToHash("0x09")) @@ -456,14 +452,14 @@ func TestStorageIteratorTraversalValues(t *testing.T) { } } // Assemble a stack of snapshots from the account layers - snaps.Update(common.HexToHash("0x02"), common.HexToHash("0x01"), nil, randomAccountSet("0xaa"), wrapStorage(a)) - snaps.Update(common.HexToHash("0x03"), common.HexToHash("0x02"), nil, randomAccountSet("0xaa"), wrapStorage(b)) - snaps.Update(common.HexToHash("0x04"), common.HexToHash("0x03"), nil, randomAccountSet("0xaa"), wrapStorage(c)) - snaps.Update(common.HexToHash("0x05"), common.HexToHash("0x04"), nil, randomAccountSet("0xaa"), wrapStorage(d)) - snaps.Update(common.HexToHash("0x06"), common.HexToHash("0x05"), nil, randomAccountSet("0xaa"), wrapStorage(e)) - snaps.Update(common.HexToHash("0x07"), common.HexToHash("0x06"), nil, randomAccountSet("0xaa"), wrapStorage(e)) - snaps.Update(common.HexToHash("0x08"), common.HexToHash("0x07"), nil, randomAccountSet("0xaa"), wrapStorage(g)) - snaps.Update(common.HexToHash("0x09"), common.HexToHash("0x08"), nil, randomAccountSet("0xaa"), wrapStorage(h)) + snaps.Update(common.HexToHash("0x02"), common.HexToHash("0x01"), randomAccountSet("0xaa"), wrapStorage(a)) + snaps.Update(common.HexToHash("0x03"), common.HexToHash("0x02"), randomAccountSet("0xaa"), wrapStorage(b)) + snaps.Update(common.HexToHash("0x04"), common.HexToHash("0x03"), randomAccountSet("0xaa"), wrapStorage(c)) + snaps.Update(common.HexToHash("0x05"), common.HexToHash("0x04"), randomAccountSet("0xaa"), wrapStorage(d)) + snaps.Update(common.HexToHash("0x06"), common.HexToHash("0x05"), randomAccountSet("0xaa"), wrapStorage(e)) + snaps.Update(common.HexToHash("0x07"), common.HexToHash("0x06"), randomAccountSet("0xaa"), wrapStorage(e)) + snaps.Update(common.HexToHash("0x08"), common.HexToHash("0x07"), randomAccountSet("0xaa"), wrapStorage(g)) + snaps.Update(common.HexToHash("0x09"), common.HexToHash("0x08"), randomAccountSet("0xaa"), wrapStorage(h)) it, _ := snaps.StorageIterator(common.HexToHash("0x09"), common.HexToHash("0xaa"), common.Hash{}) head := snaps.Snapshot(common.HexToHash("0x09")) @@ -526,7 +522,7 @@ func TestAccountIteratorLargeTraversal(t *testing.T) { }, } for i := 1; i < 128; i++ { - snaps.Update(common.HexToHash(fmt.Sprintf("0x%02x", i+1)), common.HexToHash(fmt.Sprintf("0x%02x", i)), nil, makeAccounts(200), nil) + snaps.Update(common.HexToHash(fmt.Sprintf("0x%02x", i+1)), common.HexToHash(fmt.Sprintf("0x%02x", i)), makeAccounts(200), nil) } // Iterate the entire stack and ensure everything is hit only once head := snaps.Snapshot(common.HexToHash("0x80")) @@ -584,13 +580,13 @@ func testAccountIteratorFlattening(t *testing.T, newIterator func(snaps *Tree, r }, } // Create a stack of diffs on top - snaps.Update(common.HexToHash("0x02"), common.HexToHash("0x01"), nil, + snaps.Update(common.HexToHash("0x02"), common.HexToHash("0x01"), randomAccountSet("0xaa", "0xee", "0xff", "0xf0"), nil) - snaps.Update(common.HexToHash("0x03"), common.HexToHash("0x02"), nil, + snaps.Update(common.HexToHash("0x03"), common.HexToHash("0x02"), randomAccountSet("0xbb", "0xdd", "0xf0"), nil) - snaps.Update(common.HexToHash("0x04"), common.HexToHash("0x03"), nil, + snaps.Update(common.HexToHash("0x04"), common.HexToHash("0x03"), randomAccountSet("0xcc", "0xf0", "0xff"), nil) // Create an iterator and flatten the data from underneath it @@ -630,13 +626,13 @@ func testAccountIteratorSeek(t *testing.T, newIterator func(snaps *Tree, root, s base.root: base, }, } - snaps.Update(common.HexToHash("0x02"), common.HexToHash("0x01"), nil, + snaps.Update(common.HexToHash("0x02"), common.HexToHash("0x01"), randomAccountSet("0xaa", "0xee", "0xff", "0xf0"), nil) - snaps.Update(common.HexToHash("0x03"), common.HexToHash("0x02"), nil, + snaps.Update(common.HexToHash("0x03"), common.HexToHash("0x02"), randomAccountSet("0xbb", "0xdd", "0xf0"), nil) - snaps.Update(common.HexToHash("0x04"), common.HexToHash("0x03"), nil, + snaps.Update(common.HexToHash("0x04"), common.HexToHash("0x03"), randomAccountSet("0xcc", "0xf0", "0xff"), nil) // Account set is now @@ -708,13 +704,13 @@ func testStorageIteratorSeek(t *testing.T, newIterator func(snaps *Tree, root, a }, } // Stack three diff layers on top with various overlaps - snaps.Update(common.HexToHash("0x02"), common.HexToHash("0x01"), nil, + snaps.Update(common.HexToHash("0x02"), common.HexToHash("0x01"), randomAccountSet("0xaa"), randomStorageSet([]string{"0xaa"}, [][]string{{"0x01", "0x03", "0x05"}}, nil)) - snaps.Update(common.HexToHash("0x03"), common.HexToHash("0x02"), nil, + snaps.Update(common.HexToHash("0x03"), common.HexToHash("0x02"), randomAccountSet("0xaa"), randomStorageSet([]string{"0xaa"}, [][]string{{"0x02", "0x05", "0x06"}}, nil)) - snaps.Update(common.HexToHash("0x04"), common.HexToHash("0x03"), nil, + snaps.Update(common.HexToHash("0x04"), common.HexToHash("0x03"), randomAccountSet("0xaa"), randomStorageSet([]string{"0xaa"}, [][]string{{"0x01", "0x05", "0x08"}}, nil)) // Account set is now @@ -785,21 +781,16 @@ func testAccountIteratorDeletions(t *testing.T, newIterator func(snaps *Tree, ro }, } // Stack three diff layers on top with various overlaps - snaps.Update(common.HexToHash("0x02"), common.HexToHash("0x01"), - nil, randomAccountSet("0x11", "0x22", "0x33"), nil) - - deleted := common.HexToHash("0x22") - destructed := map[common.Hash]struct{}{ - deleted: {}, - } - snaps.Update(common.HexToHash("0x03"), common.HexToHash("0x02"), - destructed, randomAccountSet("0x11", "0x33"), nil) + snaps.Update(common.HexToHash("0x02"), common.HexToHash("0x01"), randomAccountSet("0x11", "0x22", "0x33"), nil) + set := randomAccountSet("0x11", "0x33") + set[common.HexToHash("0x22")] = nil + snaps.Update(common.HexToHash("0x03"), common.HexToHash("0x02"), set, nil) snaps.Update(common.HexToHash("0x04"), common.HexToHash("0x03"), - nil, randomAccountSet("0x33", "0x44", "0x55"), nil) + randomAccountSet("0x33", "0x44", "0x55"), nil) // The output should be 11,33,44,55 - it := newIterator(snaps, common.HexToHash("0x04"), (common.Hash{})) + it := newIterator(snaps, common.HexToHash("0x04"), common.Hash{}) // Do a quick check verifyIterator(t, 4, it, verifyAccount) @@ -813,8 +804,8 @@ func testAccountIteratorDeletions(t *testing.T, newIterator func(snaps *Tree, ro if it.Account() == nil { t.Errorf("iterator returned nil-value for hash %x", hash) } - if hash == deleted { - t.Errorf("expected deleted elem %x to not be returned by iterator", deleted) + if hash == common.HexToHash("0x22") { + t.Errorf("expected deleted elem %x to not be returned by iterator", common.HexToHash("0x22")) } } } @@ -846,10 +837,10 @@ func testStorageIteratorDeletions(t *testing.T, newIterator func(snaps *Tree, ro }, } // Stack three diff layers on top with various overlaps - snaps.Update(common.HexToHash("0x02"), common.HexToHash("0x01"), nil, + snaps.Update(common.HexToHash("0x02"), common.HexToHash("0x01"), randomAccountSet("0xaa"), randomStorageSet([]string{"0xaa"}, [][]string{{"0x01", "0x03", "0x05"}}, nil)) - snaps.Update(common.HexToHash("0x03"), common.HexToHash("0x02"), nil, + snaps.Update(common.HexToHash("0x03"), common.HexToHash("0x02"), randomAccountSet("0xaa"), randomStorageSet([]string{"0xaa"}, [][]string{{"0x02", "0x04", "0x06"}}, [][]string{{"0x01", "0x03"}})) // The output should be 02,04,05,06 @@ -863,17 +854,16 @@ func testStorageIteratorDeletions(t *testing.T, newIterator func(snaps *Tree, ro it.Release() // Destruct the whole storage - destructed := map[common.Hash]struct{}{ - common.HexToHash("0xaa"): {}, - } - snaps.Update(common.HexToHash("0x04"), common.HexToHash("0x03"), destructed, nil, nil) + snaps.Update(common.HexToHash("0x04"), common.HexToHash("0x03"), + map[common.Hash][]byte{common.HexToHash("0xaa"): nil}, + randomStorageSet([]string{"0xaa"}, nil, [][]string{{"0x02", "0x04", "0x05", "0x06"}})) it = newIterator(snaps, common.HexToHash("0x04"), common.HexToHash("0xaa"), common.Hash{}) verifyIterator(t, 0, it, verifyStorage) it.Release() // Re-insert the slots of the same account - snaps.Update(common.HexToHash("0x05"), common.HexToHash("0x04"), nil, + snaps.Update(common.HexToHash("0x05"), common.HexToHash("0x04"), randomAccountSet("0xaa"), randomStorageSet([]string{"0xaa"}, [][]string{{"0x07", "0x08", "0x09"}}, nil)) // The output should be 07,08,09 @@ -883,7 +873,9 @@ func testStorageIteratorDeletions(t *testing.T, newIterator func(snaps *Tree, ro it.Release() // Destruct the whole storage but re-create the account in the same layer - snaps.Update(common.HexToHash("0x06"), common.HexToHash("0x05"), destructed, randomAccountSet("0xaa"), randomStorageSet([]string{"0xaa"}, [][]string{{"0x11", "0x12"}}, nil)) + snaps.Update(common.HexToHash("0x06"), common.HexToHash("0x05"), + randomAccountSet("0xaa"), + randomStorageSet([]string{"0xaa"}, [][]string{{"0x11", "0x12"}}, [][]string{{"0x07", "0x08", "0x09"}})) it = newIterator(snaps, common.HexToHash("0x06"), common.HexToHash("0xaa"), common.Hash{}) verifyIterator(t, 2, it, verifyStorage) // The output should be 11,12 it.Release() @@ -928,7 +920,7 @@ func BenchmarkAccountIteratorTraversal(b *testing.B) { }, } for i := 1; i <= 100; i++ { - snaps.Update(common.HexToHash(fmt.Sprintf("0x%02x", i+1)), common.HexToHash(fmt.Sprintf("0x%02x", i)), nil, makeAccounts(200), nil) + snaps.Update(common.HexToHash(fmt.Sprintf("0x%02x", i+1)), common.HexToHash(fmt.Sprintf("0x%02x", i)), makeAccounts(200), nil) } // We call this once before the benchmark, so the creation of // sorted accountlists are not included in the results. @@ -1023,9 +1015,9 @@ func BenchmarkAccountIteratorLargeBaselayer(b *testing.B) { base.root: base, }, } - snaps.Update(common.HexToHash("0x02"), common.HexToHash("0x01"), nil, makeAccounts(2000), nil) + snaps.Update(common.HexToHash("0x02"), common.HexToHash("0x01"), makeAccounts(2000), nil) for i := 2; i <= 100; i++ { - snaps.Update(common.HexToHash(fmt.Sprintf("0x%02x", i+1)), common.HexToHash(fmt.Sprintf("0x%02x", i)), nil, makeAccounts(20), nil) + snaps.Update(common.HexToHash(fmt.Sprintf("0x%02x", i+1)), common.HexToHash(fmt.Sprintf("0x%02x", i)), makeAccounts(20), nil) } // We call this once before the benchmark, so the creation of // sorted accountlists are not included in the results. diff --git a/core/state/snapshot/journal.go b/core/state/snapshot/journal.go index 40fb51ae1d..57841550a7 100644 --- a/core/state/snapshot/journal.go +++ b/core/state/snapshot/journal.go @@ -33,7 +33,11 @@ import ( "github.com/ethereum/go-ethereum/triedb" ) -const journalVersion uint64 = 0 +const ( + journalV0 uint64 = 0 // initial version + journalV1 uint64 = 1 // current version, with destruct flag (in diff layers) removed + journalCurrentVersion = journalV1 +) // journalGenerator is a disk layer entry containing the generator progress marker. type journalGenerator struct { @@ -109,8 +113,8 @@ func loadAndParseJournal(db ethdb.KeyValueStore, base *diskLayer) (snapshot, jou // is not matched with disk layer; or the it's the legacy-format journal, // etc.), we just discard all diffs and try to recover them later. var current snapshot = base - err := iterateJournal(db, func(parent common.Hash, root common.Hash, destructSet map[common.Hash]struct{}, accountData map[common.Hash][]byte, storageData map[common.Hash]map[common.Hash][]byte) error { - current = newDiffLayer(current, root, destructSet, accountData, storageData) + err := iterateJournal(db, func(parent common.Hash, root common.Hash, accountData map[common.Hash][]byte, storageData map[common.Hash]map[common.Hash][]byte) error { + current = newDiffLayer(current, root, accountData, storageData) return nil }) if err != nil { @@ -244,16 +248,12 @@ func (dl *diffLayer) Journal(buffer *bytes.Buffer) (common.Hash, error) { if err := rlp.Encode(buffer, dl.root); err != nil { return common.Hash{}, err } - destructs := make([]journalDestruct, 0, len(dl.destructSet)) - for hash := range dl.destructSet { - destructs = append(destructs, journalDestruct{Hash: hash}) - } - if err := rlp.Encode(buffer, destructs); err != nil { - return common.Hash{}, err - } accounts := make([]journalAccount, 0, len(dl.accountData)) for hash, blob := range dl.accountData { - accounts = append(accounts, journalAccount{Hash: hash, Blob: blob}) + accounts = append(accounts, journalAccount{ + Hash: hash, + Blob: blob, + }) } if err := rlp.Encode(buffer, accounts); err != nil { return common.Hash{}, err @@ -277,7 +277,7 @@ func (dl *diffLayer) Journal(buffer *bytes.Buffer) (common.Hash, error) { // journalCallback is a function which is invoked by iterateJournal, every // time a difflayer is loaded from disk. -type journalCallback = func(parent common.Hash, root common.Hash, destructs map[common.Hash]struct{}, accounts map[common.Hash][]byte, storage map[common.Hash]map[common.Hash][]byte) error +type journalCallback = func(parent common.Hash, root common.Hash, accounts map[common.Hash][]byte, storage map[common.Hash]map[common.Hash][]byte) error // iterateJournal iterates through the journalled difflayers, loading them from // the database, and invoking the callback for each loaded layer. @@ -298,8 +298,8 @@ func iterateJournal(db ethdb.KeyValueReader, callback journalCallback) error { log.Warn("Failed to resolve the journal version", "error", err) return errors.New("failed to resolve journal version") } - if version != journalVersion { - log.Warn("Discarded the snapshot journal with wrong version", "required", journalVersion, "got", version) + if version != journalV0 && version != journalCurrentVersion { + log.Warn("Discarded journal with wrong version", "required", journalCurrentVersion, "got", version) return errors.New("wrong journal version") } // Secondly, resolve the disk layer root, ensure it's continuous @@ -316,10 +316,8 @@ func iterateJournal(db ethdb.KeyValueReader, callback journalCallback) error { for { var ( root common.Hash - destructs []journalDestruct accounts []journalAccount storage []journalStorage - destructSet = make(map[common.Hash]struct{}) accountData = make(map[common.Hash][]byte) storageData = make(map[common.Hash]map[common.Hash][]byte) ) @@ -331,8 +329,35 @@ func iterateJournal(db ethdb.KeyValueReader, callback journalCallback) error { } return fmt.Errorf("load diff root: %v", err) } - if err := r.Decode(&destructs); err != nil { - return fmt.Errorf("load diff destructs: %v", err) + // If a legacy journal is detected, decode the destruct set from the stream. + // The destruct set has been deprecated. If the journal contains non-empty + // destruct set, then it is deemed incompatible. + // + // Since self-destruction has been deprecated following the cancun fork, + // the destruct set is expected to be nil for layers above the fork block. + // However, an exception occurs during contract deployment: pre-funded accounts + // may self-destruct, causing accounts with non-zero balances to be removed + // from the state. For example, + // https://etherscan.io/tx/0xa087333d83f0cd63b96bdafb686462e1622ce25f40bd499e03efb1051f31fe49). + // + // For nodes with a fully synced state, the legacy journal is likely compatible + // with the updated definition, eliminating the need for regeneration. Unfortunately, + // nodes performing a full sync of historical chain segments or encountering + // pre-funded account deletions may face incompatibilities, leading to automatic + // snapshot regeneration. + // + // This approach minimizes snapshot regeneration for Geth nodes upgrading from a + // legacy version that are already synced. The workaround can be safely removed + // after the next hard fork. + if version == journalV0 { + var destructs []journalDestruct + if err := r.Decode(&destructs); err != nil { + return fmt.Errorf("load diff destructs: %v", err) + } + if len(destructs) > 0 { + log.Warn("Incompatible legacy journal detected", "version", journalV0) + return fmt.Errorf("incompatible legacy journal detected") + } } if err := r.Decode(&accounts); err != nil { return fmt.Errorf("load diff accounts: %v", err) @@ -340,9 +365,6 @@ func iterateJournal(db ethdb.KeyValueReader, callback journalCallback) error { if err := r.Decode(&storage); err != nil { return fmt.Errorf("load diff storage: %v", err) } - for _, entry := range destructs { - destructSet[entry.Hash] = struct{}{} - } for _, entry := range accounts { if len(entry.Blob) > 0 { // RLP loses nil-ness, but `[]byte{}` is not a valid item, so reinterpret that accountData[entry.Hash] = entry.Blob @@ -361,7 +383,7 @@ func iterateJournal(db ethdb.KeyValueReader, callback journalCallback) error { } storageData[entry.Hash] = slots } - if err := callback(parent, root, destructSet, accountData, storageData); err != nil { + if err := callback(parent, root, accountData, storageData); err != nil { return err } parent = root diff --git a/core/state/snapshot/snapshot.go b/core/state/snapshot/snapshot.go index 717ff197a9..5290c72286 100644 --- a/core/state/snapshot/snapshot.go +++ b/core/state/snapshot/snapshot.go @@ -130,7 +130,7 @@ type snapshot interface { // the specified data items. // // Note, the maps are retained by the method to avoid copying everything. - Update(blockRoot common.Hash, destructs map[common.Hash]struct{}, accounts map[common.Hash][]byte, storage map[common.Hash]map[common.Hash][]byte) *diffLayer + Update(blockRoot common.Hash, accounts map[common.Hash][]byte, storage map[common.Hash]map[common.Hash][]byte) *diffLayer // Journal commits an entire diff hierarchy to disk into a single journal entry. // This is meant to be used during shutdown to persist the snapshot without @@ -145,7 +145,7 @@ type snapshot interface { AccountIterator(seek common.Hash) AccountIterator // StorageIterator creates a storage iterator over an arbitrary layer. - StorageIterator(account common.Hash, seek common.Hash) (StorageIterator, bool) + StorageIterator(account common.Hash, seek common.Hash) StorageIterator } // Config includes the configurations for snapshots. @@ -342,7 +342,7 @@ func (t *Tree) Snapshots(root common.Hash, limits int, nodisk bool) []Snapshot { // Update adds a new snapshot into the tree, if that can be linked to an existing // old parent. It is disallowed to insert a disk layer (the origin of all). -func (t *Tree) Update(blockRoot common.Hash, parentRoot common.Hash, destructs map[common.Hash]struct{}, accounts map[common.Hash][]byte, storage map[common.Hash]map[common.Hash][]byte) error { +func (t *Tree) Update(blockRoot common.Hash, parentRoot common.Hash, accounts map[common.Hash][]byte, storage map[common.Hash]map[common.Hash][]byte) error { // Reject noop updates to avoid self-loops in the snapshot tree. This is a // special case that can only happen for Clique networks where empty blocks // don't modify the state (0 block subsidy). @@ -357,7 +357,7 @@ func (t *Tree) Update(blockRoot common.Hash, parentRoot common.Hash, destructs m if parent == nil { return fmt.Errorf("parent [%#x] snapshot missing", parentRoot) } - snap := parent.(snapshot).Update(blockRoot, destructs, accounts, storage) + snap := parent.(snapshot).Update(blockRoot, accounts, storage) // Save the new snapshot for later t.lock.Lock() @@ -552,35 +552,6 @@ func diffToDisk(bottom *diffLayer) *diskLayer { base.stale = true base.lock.Unlock() - // Destroy all the destructed accounts from the database - for hash := range bottom.destructSet { - // Skip any account not covered yet by the snapshot - if base.genMarker != nil && bytes.Compare(hash[:], base.genMarker) > 0 { - continue - } - // Remove all storage slots - rawdb.DeleteAccountSnapshot(batch, hash) - base.cache.Set(hash[:], nil) - - it := rawdb.IterateStorageSnapshots(base.diskdb, hash) - for it.Next() { - key := it.Key() - batch.Delete(key) - base.cache.Del(key[1:]) - snapshotFlushStorageItemMeter.Mark(1) - - // Ensure we don't delete too much data blindly (contract can be - // huge). It's ok to flush, the root will go missing in case of a - // crash and we'll detect and regenerate the snapshot. - if batch.ValueSize() > 64*1024*1024 { - if err := batch.Write(); err != nil { - log.Crit("Failed to write storage deletions", "err", err) - } - batch.Reset() - } - } - it.Release() - } // Push all updated accounts into the database for hash, data := range bottom.accountData { // Skip any account not covered yet by the snapshot @@ -588,10 +559,14 @@ func diffToDisk(bottom *diffLayer) *diskLayer { continue } // Push the account to disk - rawdb.WriteAccountSnapshot(batch, hash, data) - base.cache.Set(hash[:], data) - snapshotCleanAccountWriteMeter.Mark(int64(len(data))) - + if len(data) != 0 { + rawdb.WriteAccountSnapshot(batch, hash, data) + base.cache.Set(hash[:], data) + snapshotCleanAccountWriteMeter.Mark(int64(len(data))) + } else { + rawdb.DeleteAccountSnapshot(batch, hash) + base.cache.Set(hash[:], nil) + } snapshotFlushAccountItemMeter.Mark(1) snapshotFlushAccountSizeMeter.Mark(int64(len(data))) @@ -600,7 +575,7 @@ func diffToDisk(bottom *diffLayer) *diskLayer { // the snapshot. if batch.ValueSize() > 64*1024*1024 { if err := batch.Write(); err != nil { - log.Crit("Failed to write storage deletions", "err", err) + log.Crit("Failed to write state changes", "err", err) } batch.Reset() } @@ -629,6 +604,16 @@ func diffToDisk(bottom *diffLayer) *diskLayer { } snapshotFlushStorageItemMeter.Mark(1) snapshotFlushStorageSizeMeter.Mark(int64(len(data))) + + // Ensure we don't write too much data blindly. It's ok to flush, the + // root will go missing in case of a crash and we'll detect and regen + // the snapshot. + if batch.ValueSize() > 64*1024*1024 { + if err := batch.Write(); err != nil { + log.Crit("Failed to write state changes", "err", err) + } + batch.Reset() + } } } // Update the snapshot block marker and write any remainder data @@ -692,7 +677,7 @@ func (t *Tree) Journal(root common.Hash) (common.Hash, error) { // Firstly write out the metadata of journal journal := new(bytes.Buffer) - if err := rlp.Encode(journal, journalVersion); err != nil { + if err := rlp.Encode(journal, journalCurrentVersion); err != nil { return common.Hash{}, err } diskroot := t.diskRoot() diff --git a/core/state/snapshot/snapshot_test.go b/core/state/snapshot/snapshot_test.go index f6b5324e7b..ceeaae107b 100644 --- a/core/state/snapshot/snapshot_test.go +++ b/core/state/snapshot/snapshot_test.go @@ -107,7 +107,7 @@ func TestDiskLayerExternalInvalidationFullFlatten(t *testing.T) { accounts := map[common.Hash][]byte{ common.HexToHash("0xa1"): randomAccount(), } - if err := snaps.Update(common.HexToHash("0x02"), common.HexToHash("0x01"), nil, accounts, nil); err != nil { + if err := snaps.Update(common.HexToHash("0x02"), common.HexToHash("0x01"), accounts, nil); err != nil { t.Fatalf("failed to create a diff layer: %v", err) } if n := len(snaps.layers); n != 2 { @@ -151,10 +151,10 @@ func TestDiskLayerExternalInvalidationPartialFlatten(t *testing.T) { accounts := map[common.Hash][]byte{ common.HexToHash("0xa1"): randomAccount(), } - if err := snaps.Update(common.HexToHash("0x02"), common.HexToHash("0x01"), nil, accounts, nil); err != nil { + if err := snaps.Update(common.HexToHash("0x02"), common.HexToHash("0x01"), accounts, nil); err != nil { t.Fatalf("failed to create a diff layer: %v", err) } - if err := snaps.Update(common.HexToHash("0x03"), common.HexToHash("0x02"), nil, accounts, nil); err != nil { + if err := snaps.Update(common.HexToHash("0x03"), common.HexToHash("0x02"), accounts, nil); err != nil { t.Fatalf("failed to create a diff layer: %v", err) } if n := len(snaps.layers); n != 3 { @@ -203,13 +203,13 @@ func TestDiffLayerExternalInvalidationPartialFlatten(t *testing.T) { accounts := map[common.Hash][]byte{ common.HexToHash("0xa1"): randomAccount(), } - if err := snaps.Update(common.HexToHash("0x02"), common.HexToHash("0x01"), nil, accounts, nil); err != nil { + if err := snaps.Update(common.HexToHash("0x02"), common.HexToHash("0x01"), accounts, nil); err != nil { t.Fatalf("failed to create a diff layer: %v", err) } - if err := snaps.Update(common.HexToHash("0x03"), common.HexToHash("0x02"), nil, accounts, nil); err != nil { + if err := snaps.Update(common.HexToHash("0x03"), common.HexToHash("0x02"), accounts, nil); err != nil { t.Fatalf("failed to create a diff layer: %v", err) } - if err := snaps.Update(common.HexToHash("0x04"), common.HexToHash("0x03"), nil, accounts, nil); err != nil { + if err := snaps.Update(common.HexToHash("0x04"), common.HexToHash("0x03"), accounts, nil); err != nil { t.Fatalf("failed to create a diff layer: %v", err) } if n := len(snaps.layers); n != 4 { @@ -263,12 +263,12 @@ func TestPostCapBasicDataAccess(t *testing.T) { }, } // The lowest difflayer - snaps.Update(common.HexToHash("0xa1"), common.HexToHash("0x01"), nil, setAccount("0xa1"), nil) - snaps.Update(common.HexToHash("0xa2"), common.HexToHash("0xa1"), nil, setAccount("0xa2"), nil) - snaps.Update(common.HexToHash("0xb2"), common.HexToHash("0xa1"), nil, setAccount("0xb2"), nil) + snaps.Update(common.HexToHash("0xa1"), common.HexToHash("0x01"), setAccount("0xa1"), nil) + snaps.Update(common.HexToHash("0xa2"), common.HexToHash("0xa1"), setAccount("0xa2"), nil) + snaps.Update(common.HexToHash("0xb2"), common.HexToHash("0xa1"), setAccount("0xb2"), nil) - snaps.Update(common.HexToHash("0xa3"), common.HexToHash("0xa2"), nil, setAccount("0xa3"), nil) - snaps.Update(common.HexToHash("0xb3"), common.HexToHash("0xb2"), nil, setAccount("0xb3"), nil) + snaps.Update(common.HexToHash("0xa3"), common.HexToHash("0xa2"), setAccount("0xa3"), nil) + snaps.Update(common.HexToHash("0xb3"), common.HexToHash("0xb2"), setAccount("0xb3"), nil) // checkExist verifies if an account exists in a snapshot checkExist := func(layer *diffLayer, key string) error { @@ -363,7 +363,7 @@ func TestSnaphots(t *testing.T) { ) for i := 0; i < 129; i++ { head = makeRoot(uint64(i + 2)) - snaps.Update(head, last, nil, setAccount(fmt.Sprintf("%d", i+2)), nil) + snaps.Update(head, last, setAccount(fmt.Sprintf("%d", i+2)), nil) last = head snaps.Cap(head, 128) // 130 layers (128 diffs + 1 accumulator + 1 disk) } @@ -456,9 +456,9 @@ func TestReadStateDuringFlattening(t *testing.T) { }, } // 4 layers in total, 3 diff layers and 1 disk layers - snaps.Update(common.HexToHash("0xa1"), common.HexToHash("0x01"), nil, setAccount("0xa1"), nil) - snaps.Update(common.HexToHash("0xa2"), common.HexToHash("0xa1"), nil, setAccount("0xa2"), nil) - snaps.Update(common.HexToHash("0xa3"), common.HexToHash("0xa2"), nil, setAccount("0xa3"), nil) + snaps.Update(common.HexToHash("0xa1"), common.HexToHash("0x01"), setAccount("0xa1"), nil) + snaps.Update(common.HexToHash("0xa2"), common.HexToHash("0xa1"), setAccount("0xa2"), nil) + snaps.Update(common.HexToHash("0xa3"), common.HexToHash("0xa2"), setAccount("0xa3"), nil) // Obtain the topmost snapshot handler for state accessing snap := snaps.Snapshot(common.HexToHash("0xa3")) diff --git a/core/state/snapshot/utils.go b/core/state/snapshot/utils.go index 62f073d2e1..c35c82f67a 100644 --- a/core/state/snapshot/utils.go +++ b/core/state/snapshot/utils.go @@ -75,7 +75,7 @@ func checkDanglingDiskStorage(chaindb ethdb.KeyValueStore) error { func checkDanglingMemStorage(db ethdb.KeyValueStore) error { start := time.Now() log.Info("Checking dangling journalled storage") - err := iterateJournal(db, func(pRoot, root common.Hash, destructs map[common.Hash]struct{}, accounts map[common.Hash][]byte, storage map[common.Hash]map[common.Hash][]byte) error { + err := iterateJournal(db, func(pRoot, root common.Hash, accounts map[common.Hash][]byte, storage map[common.Hash]map[common.Hash][]byte) error { for accHash := range storage { if _, ok := accounts[accHash]; !ok { log.Error("Dangling storage - missing account", "account", fmt.Sprintf("%#x", accHash), "root", root) @@ -119,12 +119,11 @@ func CheckJournalAccount(db ethdb.KeyValueStore, hash common.Hash) error { } var depth = 0 - return iterateJournal(db, func(pRoot, root common.Hash, destructs map[common.Hash]struct{}, accounts map[common.Hash][]byte, storage map[common.Hash]map[common.Hash][]byte) error { + return iterateJournal(db, func(pRoot, root common.Hash, accounts map[common.Hash][]byte, storage map[common.Hash]map[common.Hash][]byte) error { _, a := accounts[hash] - _, b := destructs[hash] - _, c := storage[hash] + _, b := storage[hash] depth++ - if !a && !b && !c { + if !a && !b { return nil } fmt.Printf("Disklayer+%d: Root: %x, parent %x\n", depth, root, pRoot) @@ -138,9 +137,6 @@ func CheckJournalAccount(db ethdb.KeyValueStore, hash common.Hash) error { fmt.Printf("\taccount.root: %x\n", account.Root) fmt.Printf("\taccount.codehash: %x\n", account.CodeHash) } - if _, ok := destructs[hash]; ok { - fmt.Printf("\t Destructed!") - } if data, ok := storage[hash]; ok { fmt.Printf("\tStorage\n") for k, v := range data { diff --git a/core/state/state_object.go b/core/state/state_object.go index f4a33887cd..707cd30d43 100644 --- a/core/state/state_object.go +++ b/core/state/state_object.go @@ -552,10 +552,13 @@ func (s *stateObject) Code() []byte { if bytes.Equal(s.CodeHash(), types.EmptyCodeHash.Bytes()) { return nil } - code, err := s.db.db.ContractCode(s.address, common.BytesToHash(s.CodeHash())) + code, err := s.db.reader.Code(s.address, common.BytesToHash(s.CodeHash())) if err != nil { s.db.setError(fmt.Errorf("can't load code hash %x: %v", s.CodeHash(), err)) } + if len(code) == 0 { + s.db.setError(fmt.Errorf("code is not found %x", s.CodeHash())) + } s.code = code return code } @@ -570,10 +573,13 @@ func (s *stateObject) CodeSize() int { if bytes.Equal(s.CodeHash(), types.EmptyCodeHash.Bytes()) { return 0 } - size, err := s.db.db.ContractCodeSize(s.address, common.BytesToHash(s.CodeHash())) + size, err := s.db.reader.CodeSize(s.address, common.BytesToHash(s.CodeHash())) if err != nil { s.db.setError(fmt.Errorf("can't load code size %x: %v", s.CodeHash(), err)) } + if size == 0 { + s.db.setError(fmt.Errorf("code is not found %x", s.CodeHash())) + } return size } diff --git a/core/state/statedb.go b/core/state/statedb.go index f0e61780ab..7b45ef9b40 100644 --- a/core/state/statedb.go +++ b/core/state/statedb.go @@ -775,11 +775,12 @@ func (s *StateDB) CopyDoPrefetch() *StateDB { // otherwise, just do inactive copy trie prefetcher. func (s *StateDB) copyInternal(doPrefetch bool) *StateDB { // Copy all the basic fields, initialize the memory ones + reader, _ := s.db.Reader(s.originalRoot) // impossible to fail state := &StateDB{ db: s.db, trie: mustCopyTrie(s.trie), // noTrie:s.noTrie, - reader: s.reader.Copy(), + reader: reader, // expectedRoot: s.expectedRoot, originalRoot: s.originalRoot, // fullProcessed: s.fullProcessed, @@ -1087,16 +1088,17 @@ func (s *StateDB) clearJournalAndRefund() { // of a specific account. It leverages the associated state snapshot for fast // storage iteration and constructs trie node deletion markers by creating // stack trie with iterated slots. -func (s *StateDB) fastDeleteStorage(snaps *snapshot.Tree, addrHash common.Hash, root common.Hash) (map[common.Hash][]byte, *trienode.NodeSet, error) { +func (s *StateDB) fastDeleteStorage(snaps *snapshot.Tree, addrHash common.Hash, root common.Hash) (map[common.Hash][]byte, map[common.Hash][]byte, *trienode.NodeSet, error) { iter, err := snaps.StorageIterator(s.originalRoot, addrHash, common.Hash{}) if err != nil { - return nil, nil, err + return nil, nil, nil, err } defer iter.Release() var ( - nodes = trienode.NewNodeSet(addrHash) - slots = make(map[common.Hash][]byte) + nodes = trienode.NewNodeSet(addrHash) // the set for trie node mutations (value is nil) + storages = make(map[common.Hash][]byte) // the set for storage mutations (value is nil) + storageOrigins = make(map[common.Hash][]byte) // the set for tracking the original value of slot ) stack := trie.NewStackTrie(func(path []byte, hash common.Hash, blob []byte) { nodes.AddNode(path, trienode.NewDeleted()) @@ -1104,46 +1106,51 @@ func (s *StateDB) fastDeleteStorage(snaps *snapshot.Tree, addrHash common.Hash, for iter.Next() { slot := common.CopyBytes(iter.Slot()) if err := iter.Error(); err != nil { // error might occur after Slot function - return nil, nil, err + return nil, nil, nil, err } - slots[iter.Hash()] = slot + key := iter.Hash() + storages[key] = nil + storageOrigins[key] = slot - if err := stack.Update(iter.Hash().Bytes(), slot); err != nil { - return nil, nil, err + if err := stack.Update(key.Bytes(), slot); err != nil { + return nil, nil, nil, err } } if err := iter.Error(); err != nil { // error might occur during iteration - return nil, nil, err + return nil, nil, nil, err } if stack.Hash() != root { - return nil, nil, fmt.Errorf("snapshot is not matched, exp %x, got %x", root, stack.Hash()) + return nil, nil, nil, fmt.Errorf("snapshot is not matched, exp %x, got %x", root, stack.Hash()) } - return slots, nodes, nil + return storages, storageOrigins, nodes, nil } // slowDeleteStorage serves as a less-efficient alternative to "fastDeleteStorage," // employed when the associated state snapshot is not available. It iterates the // storage slots along with all internal trie nodes via trie directly. -func (s *StateDB) slowDeleteStorage(addr common.Address, addrHash common.Hash, root common.Hash) (map[common.Hash][]byte, *trienode.NodeSet, error) { +func (s *StateDB) slowDeleteStorage(addr common.Address, addrHash common.Hash, root common.Hash) (map[common.Hash][]byte, map[common.Hash][]byte, *trienode.NodeSet, error) { tr, err := s.db.OpenStorageTrie(s.originalRoot, addr, root, s.trie) if err != nil { - return nil, nil, fmt.Errorf("failed to open storage trie, err: %w", err) + return nil, nil, nil, fmt.Errorf("failed to open storage trie, err: %w", err) } // skip deleting storages for EmptyTrie if _, ok := tr.(*trie.EmptyTrie); ok { - return nil, nil, nil + return nil, nil, nil, nil } it, err := tr.NodeIterator(nil) if err != nil { - return nil, nil, fmt.Errorf("failed to open storage iterator, err: %w", err) + return nil, nil, nil, fmt.Errorf("failed to open storage iterator, err: %w", err) } var ( - nodes = trienode.NewNodeSet(addrHash) - slots = make(map[common.Hash][]byte) + nodes = trienode.NewNodeSet(addrHash) // the set for trie node mutations (value is nil) + storages = make(map[common.Hash][]byte) // the set for storage mutations (value is nil) + storageOrigins = make(map[common.Hash][]byte) // the set for tracking the original value of slot ) for it.Next(true) { if it.Leaf() { - slots[common.BytesToHash(it.LeafKey())] = common.CopyBytes(it.LeafBlob()) + key := common.BytesToHash(it.LeafKey()) + storages[key] = nil + storageOrigins[key] = common.CopyBytes(it.LeafBlob()) continue } if it.Hash() == (common.Hash{}) { @@ -1152,35 +1159,36 @@ func (s *StateDB) slowDeleteStorage(addr common.Address, addrHash common.Hash, r nodes.AddNode(it.Path(), trienode.NewDeleted()) } if err := it.Error(); err != nil { - return nil, nil, err + return nil, nil, nil, err } - return slots, nodes, nil + return storages, storageOrigins, nodes, nil } // deleteStorage is designed to delete the storage trie of a designated account. // The function will make an attempt to utilize an efficient strategy if the // associated state snapshot is reachable; otherwise, it will resort to a less // efficient approach. -func (s *StateDB) deleteStorage(addr common.Address, addrHash common.Hash, root common.Hash) (map[common.Hash][]byte, *trienode.NodeSet, error) { +func (s *StateDB) deleteStorage(addr common.Address, addrHash common.Hash, root common.Hash) (map[common.Hash][]byte, map[common.Hash][]byte, *trienode.NodeSet, error) { var ( - err error - slots map[common.Hash][]byte - nodes *trienode.NodeSet + err error + nodes *trienode.NodeSet // the set for trie node mutations (value is nil) + storages map[common.Hash][]byte // the set for storage mutations (value is nil) + storageOrigins map[common.Hash][]byte // the set for tracking the original value of slot ) // The fast approach can be failed if the snapshot is not fully // generated, or it's internally corrupted. Fallback to the slow // one just in case. snaps := s.db.Snapshot() if snaps != nil { - slots, nodes, err = s.fastDeleteStorage(snaps, addrHash, root) + storages, storageOrigins, nodes, err = s.fastDeleteStorage(snaps, addrHash, root) } if snaps == nil || err != nil { - slots, nodes, err = s.slowDeleteStorage(addr, addrHash, root) + storages, storageOrigins, nodes, err = s.slowDeleteStorage(addr, addrHash, root) } if err != nil { - return nil, nil, err + return nil, nil, nil, err } - return slots, nodes, nil + return storages, storageOrigins, nodes, nil } // handleDestruction processes all destruction markers and deletes the account @@ -1227,16 +1235,16 @@ func (s *StateDB) handleDestruction() (map[common.Hash]*accountDelete, []*trieno deletes[addrHash] = op // Short circuit if the origin storage was empty. - if prev.Root == types.EmptyRootHash || s.db.TrieDB().IsVerkle() { continue } // Remove storage slots belonging to the account. - slots, set, err := s.deleteStorage(addr, addrHash, prev.Root) + storages, storagesOrigin, set, err := s.deleteStorage(addr, addrHash, prev.Root) if err != nil { return nil, nil, fmt.Errorf("failed to delete storage, err: %w", err) } - op.storagesOrigin = slots + op.storages = storages + op.storagesOrigin = storagesOrigin // Aggregate the associated trie node changes. nodes = append(nodes, set) @@ -1447,7 +1455,7 @@ func (s *StateDB) commitAndFlush(block uint64, deleteEmptyObjects bool) (*stateU // If snapshotting is enabled, update the snapshot tree with this new version if snap := s.db.Snapshot(); snap != nil && snap.Snapshot(ret.originRoot) != nil { start := time.Now() - if err := snap.Update(ret.root, ret.originRoot, ret.destructs, ret.accounts, ret.storages); err != nil { + if err := snap.Update(ret.root, ret.originRoot, ret.accounts, ret.storages); err != nil { log.Warn("Failed to update snapshot tree", "from", ret.originRoot, "to", ret.root, "err", err) } // Keep 128 diff layers in the memory, persistent layer is 129th. diff --git a/core/state/statedb_fuzz_test.go b/core/state/statedb_fuzz_test.go index bc12aff70a..3735db0a4e 100644 --- a/core/state/statedb_fuzz_test.go +++ b/core/state/statedb_fuzz_test.go @@ -21,6 +21,7 @@ import ( "encoding/binary" "errors" "fmt" + "maps" "math" "math/rand" "reflect" @@ -176,24 +177,16 @@ func (test *stateTest) String() string { func (test *stateTest) run() bool { var ( - roots []common.Hash - accountList []map[common.Address][]byte - storageList []map[common.Address]map[common.Hash][]byte - copyUpdate = func(update *stateUpdate) { - accounts := make(map[common.Address][]byte, len(update.accountsOrigin)) - for key, val := range update.accountsOrigin { - accounts[key] = common.CopyBytes(val) - } - accountList = append(accountList, accounts) - - storages := make(map[common.Address]map[common.Hash][]byte, len(update.storagesOrigin)) - for addr, subset := range update.storagesOrigin { - storages[addr] = make(map[common.Hash][]byte, len(subset)) - for key, val := range subset { - storages[addr][key] = common.CopyBytes(val) - } - } - storageList = append(storageList, storages) + roots []common.Hash + accounts []map[common.Hash][]byte + accountOrigin []map[common.Address][]byte + storages []map[common.Hash]map[common.Hash][]byte + storageOrigin []map[common.Address]map[common.Hash][]byte + copyUpdate = func(update *stateUpdate) { + accounts = append(accounts, maps.Clone(update.accounts)) + accountOrigin = append(accountOrigin, maps.Clone(update.accountsOrigin)) + storages = append(storages, maps.Clone(update.storages)) + storageOrigin = append(storageOrigin, maps.Clone(update.storagesOrigin)) } disk = rawdb.NewMemoryDatabase() tdb = triedb.NewDatabase(disk, &triedb.Config{PathDB: pathdb.Defaults}) @@ -250,7 +243,7 @@ func (test *stateTest) run() bool { if i != 0 { root = roots[i-1] } - test.err = test.verify(root, roots[i], tdb, accountList[i], storageList[i]) + test.err = test.verify(root, roots[i], tdb, accounts[i], accountOrigin[i], storages[i], storageOrigin[i]) if test.err != nil { return false } @@ -265,7 +258,7 @@ func (test *stateTest) run() bool { // - the account was indeed not present in trie // - the account is present in new trie, nil->nil is regarded as invalid // - the slots transition is correct -func (test *stateTest) verifyAccountCreation(next common.Hash, db *triedb.Database, otr, ntr *trie.Trie, addr common.Address, slots map[common.Hash][]byte) error { +func (test *stateTest) verifyAccountCreation(next common.Hash, db *triedb.Database, otr, ntr *trie.Trie, addr common.Address, account []byte, storages map[common.Hash][]byte, storagesOrigin map[common.Hash][]byte) error { // Verify account change addrHash := crypto.Keccak256Hash(addr.Bytes()) oBlob, err := otr.Get(addrHash.Bytes()) @@ -282,6 +275,13 @@ func (test *stateTest) verifyAccountCreation(next common.Hash, db *triedb.Databa if len(nBlob) == 0 { return fmt.Errorf("missing account in new trie, %x", addrHash) } + full, err := types.FullAccountRLP(account) + if err != nil { + return err + } + if !bytes.Equal(nBlob, full) { + return fmt.Errorf("unexpected account data, want: %v, got: %v", full, nBlob) + } // Verify storage changes var nAcct types.StateAccount @@ -290,7 +290,10 @@ func (test *stateTest) verifyAccountCreation(next common.Hash, db *triedb.Databa } // Account has no slot, empty slot set is expected if nAcct.Root == types.EmptyRootHash { - if len(slots) != 0 { + if len(storagesOrigin) != 0 { + return fmt.Errorf("unexpected slot changes %x", addrHash) + } + if len(storages) != 0 { return fmt.Errorf("unexpected slot changes %x", addrHash) } return nil @@ -300,9 +303,22 @@ func (test *stateTest) verifyAccountCreation(next common.Hash, db *triedb.Databa if err != nil { return err } - for key, val := range slots { + for key, val := range storagesOrigin { + if _, exist := storages[key]; !exist { + return errors.New("storage data is not found") + } + got, err := st.Get(key.Bytes()) + if err != nil { + return err + } + if !bytes.Equal(got, storages[key]) { + return fmt.Errorf("unexpected storage data, want: %v, got: %v", storages[key], got) + } st.Update(key.Bytes(), val) } + if len(storagesOrigin) != len(storages) { + return fmt.Errorf("extra storage found, want: %d, got: %d", len(storagesOrigin), len(storages)) + } if st.Hash() != types.EmptyRootHash { return errors.New("invalid slot changes") } @@ -316,7 +332,7 @@ func (test *stateTest) verifyAccountCreation(next common.Hash, db *triedb.Databa // - the account was indeed present in trie // - the account in old trie matches the provided value // - the slots transition is correct -func (test *stateTest) verifyAccountUpdate(next common.Hash, db *triedb.Database, otr, ntr *trie.Trie, addr common.Address, origin []byte, slots map[common.Hash][]byte) error { +func (test *stateTest) verifyAccountUpdate(next common.Hash, db *triedb.Database, otr, ntr *trie.Trie, addr common.Address, account []byte, accountOrigin []byte, storages map[common.Hash][]byte, storageOrigin map[common.Hash][]byte) error { // Verify account change addrHash := crypto.Keccak256Hash(addr.Bytes()) oBlob, err := otr.Get(addrHash.Bytes()) @@ -330,14 +346,23 @@ func (test *stateTest) verifyAccountUpdate(next common.Hash, db *triedb.Database if len(oBlob) == 0 { return fmt.Errorf("missing account in old trie, %x", addrHash) } - full, err := types.FullAccountRLP(origin) + full, err := types.FullAccountRLP(accountOrigin) if err != nil { return err } if !bytes.Equal(full, oBlob) { return fmt.Errorf("account value is not matched, %x", addrHash) } - + if len(nBlob) == 0 { + if len(account) != 0 { + return errors.New("unexpected account data") + } + } else { + full, _ = types.FullAccountRLP(account) + if !bytes.Equal(full, nBlob) { + return fmt.Errorf("unexpected account data, %x, want %v, got: %v", addrHash, full, nBlob) + } + } // Decode accounts var ( oAcct types.StateAccount @@ -361,16 +386,29 @@ func (test *stateTest) verifyAccountUpdate(next common.Hash, db *triedb.Database if err != nil { return err } - for key, val := range slots { + for key, val := range storageOrigin { + if _, exist := storages[key]; !exist { + return errors.New("storage data is not found") + } + got, err := st.Get(key.Bytes()) + if err != nil { + return err + } + if !bytes.Equal(got, storages[key]) { + return fmt.Errorf("unexpected storage data, want: %v, got: %v", storages[key], got) + } st.Update(key.Bytes(), val) } + if len(storageOrigin) != len(storages) { + return fmt.Errorf("extra storage found, want: %d, got: %d", len(storageOrigin), len(storages)) + } if st.Hash() != oAcct.Root { return errors.New("invalid slot changes") } return nil } -func (test *stateTest) verify(root common.Hash, next common.Hash, db *triedb.Database, accountsOrigin map[common.Address][]byte, storagesOrigin map[common.Address]map[common.Hash][]byte) error { +func (test *stateTest) verify(root common.Hash, next common.Hash, db *triedb.Database, accounts map[common.Hash][]byte, accountsOrigin map[common.Address][]byte, storages map[common.Hash]map[common.Hash][]byte, storagesOrigin map[common.Address]map[common.Hash][]byte) error { otr, err := trie.New(trie.StateTrieID(root), db) if err != nil { return err @@ -379,12 +417,15 @@ func (test *stateTest) verify(root common.Hash, next common.Hash, db *triedb.Dat if err != nil { return err } - for addr, account := range accountsOrigin { - var err error - if len(account) == 0 { - err = test.verifyAccountCreation(next, db, otr, ntr, addr, storagesOrigin[addr]) + for addr, accountOrigin := range accountsOrigin { + var ( + err error + addrHash = crypto.Keccak256Hash(addr.Bytes()) + ) + if len(accountOrigin) == 0 { + err = test.verifyAccountCreation(next, db, otr, ntr, addr, accounts[addrHash], storages[addrHash], storagesOrigin[addr]) } else { - err = test.verifyAccountUpdate(next, db, otr, ntr, addr, accountsOrigin[addr], storagesOrigin[addr]) + err = test.verifyAccountUpdate(next, db, otr, ntr, addr, accounts[addrHash], accountsOrigin[addr], storages[addrHash], storagesOrigin[addr]) } if err != nil { return err diff --git a/core/state/statedb_test.go b/core/state/statedb_test.go index a14e985a4f..ea33f08732 100644 --- a/core/state/statedb_test.go +++ b/core/state/statedb_test.go @@ -1304,12 +1304,12 @@ func TestDeleteStorage(t *testing.T) { obj := fastState.getOrNewStateObject(addr) storageRoot := obj.data.Root - _, fastNodes, err := fastState.deleteStorage(addr, crypto.Keccak256Hash(addr[:]), storageRoot) + _, _, fastNodes, err := fastState.deleteStorage(addr, crypto.Keccak256Hash(addr[:]), storageRoot) if err != nil { t.Fatal(err) } - _, slowNodes, err := slowState.deleteStorage(addr, crypto.Keccak256Hash(addr[:]), storageRoot) + _, _, slowNodes, err := slowState.deleteStorage(addr, crypto.Keccak256Hash(addr[:]), storageRoot) if err != nil { t.Fatal(err) } diff --git a/core/state/stateupdate.go b/core/state/stateupdate.go index bbfc84e9a9..528d69ac34 100644 --- a/core/state/stateupdate.go +++ b/core/state/stateupdate.go @@ -17,6 +17,8 @@ package state import ( + "maps" + "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/trie/trienode" @@ -33,6 +35,7 @@ type contractCode struct { type accountDelete struct { address common.Address // address is the unique account identifier origin []byte // origin is the original value of account data in slim-RLP encoding. + storages map[common.Hash][]byte // storages stores mutated slots, the value should be nil. storagesOrigin map[common.Hash][]byte // storagesOrigin stores the original values of mutated slots in prefix-zero-trimmed RLP format. } @@ -52,7 +55,6 @@ type accountUpdate struct { type stateUpdate struct { originRoot common.Hash // hash of the state before applying mutation root common.Hash // hash of the state after applying mutation - destructs map[common.Hash]struct{} // destructs contains the list of destructed accounts accounts map[common.Hash][]byte // accounts stores mutated accounts in 'slim RLP' encoding accountsOrigin map[common.Address][]byte // accountsOrigin stores the original values of mutated accounts in 'slim RLP' encoding storages map[common.Hash]map[common.Hash][]byte // storages stores mutated slots in 'prefix-zero-trimmed' RLP format @@ -74,7 +76,6 @@ func (sc *stateUpdate) empty() bool { // account deletions and account updates to form a comprehensive state update. func newStateUpdate(originRoot common.Hash, root common.Hash, deletes map[common.Hash]*accountDelete, updates map[common.Hash]*accountUpdate, nodes *trienode.MergedNodeSet) *stateUpdate { var ( - destructs = make(map[common.Hash]struct{}) accounts = make(map[common.Hash][]byte) accountsOrigin = make(map[common.Address][]byte) storages = make(map[common.Hash]map[common.Hash][]byte) @@ -86,9 +87,13 @@ func newStateUpdate(originRoot common.Hash, root common.Hash, deletes map[common // within the same block, the deletions must be aggregated first. for addrHash, op := range deletes { addr := op.address - destructs[addrHash] = struct{}{} + accounts[addrHash] = nil destructsAddrs[addr] = struct{}{} accountsOrigin[addr] = op.origin + + if len(op.storages) > 0 { + storages[addrHash] = op.storages + } if len(op.storagesOrigin) > 0 { storagesOrigin[addr] = op.storagesOrigin } @@ -100,35 +105,41 @@ func newStateUpdate(originRoot common.Hash, root common.Hash, deletes map[common if op.code != nil { codes[addr] = *op.code } - // Aggregate the account changes. The original account value will only - // be tracked if it's not present yet. accounts[addrHash] = op.data + + // Aggregate the account original value. If the account is already + // present in the aggregated accountsOrigin set, skip it. if _, found := accountsOrigin[addr]; !found { accountsOrigin[addr] = op.origin } - // Aggregate the storage changes. The original storage slot value will - // only be tracked if it's not present yet. + // Aggregate the storage mutation list. If a slot in op.storages is + // already present in aggregated storages set, the value will be + // overwritten. if len(op.storages) > 0 { - storages[addrHash] = op.storages + if _, exist := storages[addrHash]; !exist { + storages[addrHash] = op.storages + } else { + maps.Copy(storages[addrHash], op.storages) + } } + // Aggregate the storage original values. If the slot is already present + // in aggregated storagesOrigin set, skip it. if len(op.storagesOrigin) > 0 { - origin := storagesOrigin[addr] - if origin == nil { + origin, exist := storagesOrigin[addr] + if !exist { storagesOrigin[addr] = op.storagesOrigin - continue - } - for key, slot := range op.storagesOrigin { - if _, found := origin[key]; !found { - origin[key] = slot + } else { + for key, slot := range op.storagesOrigin { + if _, found := origin[key]; !found { + origin[key] = slot + } } } - storagesOrigin[addr] = origin } } sc := &stateUpdate{ originRoot: types.TrieRootHash(originRoot), root: types.TrieRootHash(root), - destructs: destructs, accounts: accounts, accountsOrigin: accountsOrigin, storages: storages, @@ -173,7 +184,6 @@ func newStateUpdate(originRoot common.Hash, root common.Hash, deletes map[common // package. func (sc *stateUpdate) stateSet() *triedb.StateSet { return &triedb.StateSet{ - Destructs: sc.destructs, Accounts: sc.accounts, AccountsOrigin: sc.accountsOrigin, Storages: sc.storages, diff --git a/core/state/sync_test.go b/core/state/sync_test.go index f43475552f..c44f4579e6 100644 --- a/core/state/sync_test.go +++ b/core/state/sync_test.go @@ -210,14 +210,18 @@ func testIterativeStateSync(t *testing.T, count int, commit bool, bypath bool, s if err != nil { t.Fatalf("state is not existent, %#x", srcRoot) } + cReader, err := srcDb.Reader(srcRoot) + if err != nil { + t.Fatalf("state is not existent, %#x", srcRoot) + } for len(nodeElements)+len(codeElements) > 0 { var ( nodeResults = make([]trie.NodeSyncResult, len(nodeElements)) codeResults = make([]trie.CodeSyncResult, len(codeElements)) ) for i, element := range codeElements { - data, err := srcDb.ContractCode(common.Address{}, element.code) - if err != nil { + data, err := cReader.Code(common.Address{}, element.code) + if err != nil || len(data) == 0 { t.Fatalf("failed to retrieve contract bytecode for hash %x", element.code) } codeResults[i] = trie.CodeSyncResult{Hash: element.code, Data: data} @@ -329,6 +333,10 @@ func testIterativeDelayedStateSync(t *testing.T, scheme string) { if err != nil { t.Fatalf("state is not existent, %#x", srcRoot) } + cReader, err := srcDb.Reader(srcRoot) + if err != nil { + t.Fatalf("state is not existent, %#x", srcRoot) + } for len(nodeElements)+len(codeElements) > 0 { // Sync only half of the scheduled nodes var nodeProcessed int @@ -336,8 +344,8 @@ func testIterativeDelayedStateSync(t *testing.T, scheme string) { if len(codeElements) > 0 { codeResults := make([]trie.CodeSyncResult, len(codeElements)/2+1) for i, element := range codeElements[:len(codeResults)] { - data, err := srcDb.ContractCode(common.Address{}, element.code) - if err != nil { + data, err := cReader.Code(common.Address{}, element.code) + if err != nil || len(data) == 0 { t.Fatalf("failed to retrieve contract bytecode for %x", element.code) } codeResults[i] = trie.CodeSyncResult{Hash: element.code, Data: data} @@ -433,13 +441,17 @@ func testIterativeRandomStateSync(t *testing.T, count int, scheme string) { if err != nil { t.Fatalf("state is not existent, %#x", srcRoot) } + cReader, err := srcDb.Reader(srcRoot) + if err != nil { + t.Fatalf("state is not existent, %#x", srcRoot) + } for len(nodeQueue)+len(codeQueue) > 0 { // Fetch all the queued nodes in a random order if len(codeQueue) > 0 { results := make([]trie.CodeSyncResult, 0, len(codeQueue)) for hash := range codeQueue { - data, err := srcDb.ContractCode(common.Address{}, hash) - if err != nil { + data, err := cReader.Code(common.Address{}, hash) + if err != nil || len(data) == 0 { t.Fatalf("failed to retrieve node data for %x", hash) } results = append(results, trie.CodeSyncResult{Hash: hash, Data: data}) @@ -526,6 +538,10 @@ func testIterativeRandomDelayedStateSync(t *testing.T, scheme string) { if err != nil { t.Fatalf("state is not existent, %#x", srcRoot) } + cReader, err := srcDb.Reader(srcRoot) + if err != nil { + t.Fatalf("state is not existent, %#x", srcRoot) + } for len(nodeQueue)+len(codeQueue) > 0 { // Sync only half of the scheduled nodes, even those in random order if len(codeQueue) > 0 { @@ -533,8 +549,8 @@ func testIterativeRandomDelayedStateSync(t *testing.T, scheme string) { for hash := range codeQueue { delete(codeQueue, hash) - data, err := srcDb.ContractCode(common.Address{}, hash) - if err != nil { + data, err := cReader.Code(common.Address{}, hash) + if err != nil || len(data) == 0 { t.Fatalf("failed to retrieve node data for %x", hash) } results = append(results, trie.CodeSyncResult{Hash: hash, Data: data}) @@ -631,6 +647,10 @@ func testIncompleteStateSync(t *testing.T, scheme string) { if err != nil { t.Fatalf("state is not available %x", srcRoot) } + cReader, err := srcDb.Reader(srcRoot) + if err != nil { + t.Fatalf("state is not existent, %#x", srcRoot) + } nodeQueue := make(map[string]stateElement) codeQueue := make(map[common.Hash]struct{}) paths, nodes, codes := sched.Missing(1) @@ -649,8 +669,8 @@ func testIncompleteStateSync(t *testing.T, scheme string) { if len(codeQueue) > 0 { results := make([]trie.CodeSyncResult, 0, len(codeQueue)) for hash := range codeQueue { - data, err := srcDb.ContractCode(common.Address{}, hash) - if err != nil { + data, err := cReader.Code(common.Address{}, hash) + if err != nil || len(data) == 0 { t.Fatalf("failed to retrieve node data for %x", hash) } results = append(results, trie.CodeSyncResult{Hash: hash, Data: data}) @@ -713,6 +733,11 @@ func testIncompleteStateSync(t *testing.T, scheme string) { // Sanity check that removing any node from the database is detected for _, node := range addedCodes { val := rawdb.ReadCode(dstDb, node) + if len(val) == 0 { + t.Logf("no code: %v", node) + } else { + t.Logf("has code: %v", node) + } rawdb.DeleteCode(dstDb, node) if err := checkStateConsistency(dstDb, ndb.Scheme(), srcRoot); err == nil { t.Errorf("trie inconsistency not caught, missing: %x", node) diff --git a/core/state/trie_prefetcher.go b/core/state/trie_prefetcher.go index f8595a5f3f..d7b0d0f37e 100644 --- a/core/state/trie_prefetcher.go +++ b/core/state/trie_prefetcher.go @@ -65,20 +65,20 @@ type triePrefetcher struct { fetchersMutex sync.RWMutex prefetchChan chan *prefetchMsg // no need to wait for return - deliveryMissMeter metrics.Meter - accountLoadMeter metrics.Meter - accountDupMeter metrics.Meter - accountSkipMeter metrics.Meter - accountWasteMeter metrics.Meter - storageLoadMeter metrics.Meter - storageDupMeter metrics.Meter - storageSkipMeter metrics.Meter - storageWasteMeter metrics.Meter - - accountStaleLoadMeter metrics.Meter - accountStaleDupMeter metrics.Meter - accountStaleSkipMeter metrics.Meter - accountStaleWasteMeter metrics.Meter + deliveryMissMeter *metrics.Meter + accountLoadMeter *metrics.Meter + accountDupMeter *metrics.Meter + accountSkipMeter *metrics.Meter + accountWasteMeter *metrics.Meter + storageLoadMeter *metrics.Meter + storageDupMeter *metrics.Meter + storageSkipMeter *metrics.Meter + storageWasteMeter *metrics.Meter + + accountStaleLoadMeter *metrics.Meter + accountStaleDupMeter *metrics.Meter + accountStaleSkipMeter *metrics.Meter + accountStaleWasteMeter *metrics.Meter } // newTriePrefetcher diff --git a/core/state_prefetcher.go b/core/state_prefetcher.go index 53df5babb6..c5be14f591 100644 --- a/core/state_prefetcher.go +++ b/core/state_prefetcher.go @@ -61,7 +61,7 @@ func (p *statePrefetcher) Prefetch(block *types.Block, statedb *state.StateDB, c } gaspool := new(GasPool).AddGas(block.GasLimit()) blockContext := NewEVMBlockContext(header, p.chain, nil) - evm := vm.NewEVM(blockContext, vm.TxContext{}, statedb, p.config, *cfg) + evm := vm.NewEVM(blockContext, newStatedb, p.config, *cfg) // Iterate over and process the individual transactions for { select { @@ -75,7 +75,9 @@ func (p *statePrefetcher) Prefetch(block *types.Block, statedb *state.StateDB, c return // Also invalid block, bail out } newStatedb.SetTxContext(tx.Hash(), txIndex) - precacheTransaction(msg, p.config, gaspool, newStatedb, header, evm) + // We attempt to apply a transaction. The goal is not to execute + // the transaction successfully, rather to warm up touched data slots. + ApplyMessage(evm, msg, gaspool) case <-interruptCh: // If block precaching was interrupted, abort @@ -111,7 +113,7 @@ func (p *statePrefetcher) PrefetchMining(txs TransactionsByPriceAndNonce, header } gaspool := new(GasPool).AddGas(gasLimit) blockContext := NewEVMBlockContext(header, p.chain, nil) - evm := vm.NewEVM(blockContext, vm.TxContext{}, statedb, p.config, cfg) + evm := vm.NewEVM(blockContext, newStatedb, p.config, cfg) // Iterate over and process the individual transactions for { select { @@ -125,7 +127,7 @@ func (p *statePrefetcher) PrefetchMining(txs TransactionsByPriceAndNonce, header } idx++ newStatedb.SetTxContext(tx.Hash(), idx) - precacheTransaction(msg, p.config, gaspool, newStatedb, header, evm) + ApplyMessage(evm, msg, gaspool) gaspool = new(GasPool).AddGas(gasLimit) case <-stopCh: return @@ -159,17 +161,3 @@ func (p *statePrefetcher) PrefetchMining(txs TransactionsByPriceAndNonce, header } }(txs) } - -// precacheTransaction attempts to apply a transaction to the given state database -// and uses the input parameters for its environment. The goal is not to execute -// the transaction successfully, rather to warm up touched data slots. -func precacheTransaction(msg *Message, config *params.ChainConfig, gaspool *GasPool, statedb *state.StateDB, header *types.Header, evm *vm.EVM) error { - // Update the evm with the new transaction context. - evm.Reset(NewEVMTxContext(msg), statedb) - // Add addresses to access list if applicable - _, err := ApplyMessage(evm, msg, gaspool) - if err == nil { - statedb.Finalise(true) - } - return err -} diff --git a/core/state_processor.go b/core/state_processor.go index c7f4f39917..db9160d891 100644 --- a/core/state_processor.go +++ b/core/state_processor.go @@ -26,6 +26,7 @@ import ( "github.com/ethereum/go-ethereum/consensus/misc" "github.com/ethereum/go-ethereum/core/state" "github.com/ethereum/go-ethereum/core/systemcontracts" + "github.com/ethereum/go-ethereum/core/tracing" "github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/core/vm" "github.com/ethereum/go-ethereum/crypto" @@ -87,18 +88,18 @@ func (p *StateProcessor) Process(block *types.Block, statedb *state.StateDB, cfg ) // Apply pre-execution system calls. - context = NewEVMBlockContext(header, p.chain, nil) - - vmenv := vm.NewEVM(context, vm.TxContext{}, statedb, p.config, cfg) var tracingStateDB = vm.StateDB(statedb) if hooks := cfg.Tracer; hooks != nil { tracingStateDB = state.NewHookedState(statedb, hooks) } + context = NewEVMBlockContext(header, p.chain, nil) + evm := vm.NewEVM(context, tracingStateDB, p.config, cfg) + if beaconRoot := block.BeaconRoot(); beaconRoot != nil { - ProcessBeaconBlockRoot(*beaconRoot, vmenv, tracingStateDB) + ProcessBeaconBlockRoot(*beaconRoot, evm) } if p.config.IsPrague(block.Number(), block.Time()) { - ProcessParentBlockHash(block.ParentHash(), vmenv, tracingStateDB) + ProcessParentBlockHash(block.ParentHash(), evm) } // Iterate over and process the individual transactions @@ -137,7 +138,7 @@ func (p *StateProcessor) Process(block *types.Block, statedb *state.StateDB, cfg } statedb.SetTxContext(tx.Hash(), i) - receipt, err := ApplyTransactionWithEVM(msg, p.config, gp, statedb, blockNumber, blockHash, tx, usedGas, vmenv, bloomProcessors) + receipt, err := ApplyTransactionWithEVM(msg, gp, statedb, blockNumber, blockHash, tx, usedGas, evm, bloomProcessors) if err != nil { bloomProcessors.Close() return nil, fmt.Errorf("could not apply tx %d [%v]: %w", i, tx.Hash().Hex(), err) @@ -154,17 +155,15 @@ func (p *StateProcessor) Process(block *types.Block, statedb *state.StateDB, cfg for _, receipt := range receipts { allCommonLogs = append(allCommonLogs, receipt.Logs...) } - depositRequests, err := ParseDepositLogs(allCommonLogs, p.config) - if err != nil { + requests = [][]byte{} + // EIP-6110 + if err := ParseDepositLogs(&requests, allCommonLogs, p.config); err != nil { return nil, err } - requests = append(requests, depositRequests) - // EIP-7002 withdrawals - withdrawalRequests := ProcessWithdrawalQueue(vmenv, tracingStateDB) - requests = append(requests, withdrawalRequests) - // EIP-7251 consolidations - consolidationRequests := ProcessConsolidationQueue(vmenv, tracingStateDB) - requests = append(requests, consolidationRequests) + // EIP-7002 + ProcessWithdrawalQueue(&requests, evm) + // EIP-7251 + ProcessConsolidationQueue(&requests, evm) } // Finalize the block, applying any consensus engine specific extras (e.g. block rewards) @@ -187,10 +186,8 @@ func (p *StateProcessor) Process(block *types.Block, statedb *state.StateDB, cfg // ApplyTransactionWithEVM attempts to apply a transaction to the given state database // and uses the input parameters for its environment similar to ApplyTransaction. However, // this method takes an already created EVM instance as input. -func ApplyTransactionWithEVM(msg *Message, config *params.ChainConfig, gp *GasPool, statedb *state.StateDB, blockNumber *big.Int, blockHash common.Hash, tx *types.Transaction, usedGas *uint64, evm *vm.EVM, receiptProcessors ...ReceiptProcessor) (receipt *types.Receipt, err error) { - var tracingStateDB = vm.StateDB(statedb) +func ApplyTransactionWithEVM(msg *Message, gp *GasPool, statedb *state.StateDB, blockNumber *big.Int, blockHash common.Hash, tx *types.Transaction, usedGas *uint64, evm *vm.EVM, receiptProcessors ...ReceiptProcessor) (receipt *types.Receipt, err error) { if hooks := evm.Config.Tracer; hooks != nil { - tracingStateDB = state.NewHookedState(statedb, hooks) if hooks.OnTxStart != nil { hooks.OnTxStart(evm.GetVMContext(), tx, msg.From) } @@ -198,23 +195,17 @@ func ApplyTransactionWithEVM(msg *Message, config *params.ChainConfig, gp *GasPo defer func() { hooks.OnTxEnd(receipt, err) }() } } - - // Create a new context to be used in the EVM environment. - txContext := NewEVMTxContext(msg) - evm.Reset(txContext, tracingStateDB) - // Apply the transaction to the current state (included in the env). result, err := ApplyMessage(evm, msg, gp) if err != nil { return nil, err } - // Update the state with pending changes. var root []byte - if config.IsByzantium(blockNumber) { - tracingStateDB.Finalise(true) + if evm.ChainConfig().IsByzantium(blockNumber) { + evm.StateDB.Finalise(true) } else { - root = statedb.IntermediateRoot(config.IsEIP158(blockNumber)).Bytes() + root = statedb.IntermediateRoot(evm.ChainConfig().IsEIP158(blockNumber)).Bytes() } *usedGas += result.UsedGas @@ -265,36 +256,26 @@ func MakeReceipt(evm *vm.EVM, result *ExecutionResult, statedb *state.StateDB, b // and uses the input parameters for its environment. It returns the receipt // for the transaction, gas used and an error if the transaction failed, // indicating the block was invalid. -func ApplyTransaction(config *params.ChainConfig, bc ChainContext, author *common.Address, gp *GasPool, statedb *state.StateDB, header *types.Header, tx *types.Transaction, usedGas *uint64, cfg vm.Config, receiptProcessors ...ReceiptProcessor) (*types.Receipt, error) { - msg, err := TransactionToMessage(tx, types.MakeSigner(config, header.Number, header.Time), header.BaseFee) +func ApplyTransaction(evm *vm.EVM, gp *GasPool, statedb *state.StateDB, header *types.Header, tx *types.Transaction, usedGas *uint64, receiptProcessors ...ReceiptProcessor) (*types.Receipt, error) { + msg, err := TransactionToMessage(tx, types.MakeSigner(evm.ChainConfig(), header.Number, header.Time), header.BaseFee) if err != nil { return nil, err } // Create a new context to be used in the EVM environment - blockContext := NewEVMBlockContext(header, bc, author) - txContext := NewEVMTxContext(msg) - vmenv := vm.NewEVM(blockContext, txContext, statedb, config, cfg) - defer func() { - ite := vmenv.Interpreter() - vm.EVMInterpreterPool.Put(ite) - vm.EvmPool.Put(vmenv) - }() - return ApplyTransactionWithEVM(msg, config, gp, statedb, header.Number, header.Hash(), tx, usedGas, vmenv, receiptProcessors...) + return ApplyTransactionWithEVM(msg, gp, statedb, header.Number, header.Hash(), tx, usedGas, evm, receiptProcessors...) } // ProcessBeaconBlockRoot applies the EIP-4788 system call to the beacon block root // contract. This method is exported to be used in tests. -func ProcessBeaconBlockRoot(beaconRoot common.Hash, vmenv *vm.EVM, statedb vm.StateDB) { +func ProcessBeaconBlockRoot(beaconRoot common.Hash, evm *vm.EVM) { // Return immediately if beaconRoot equals the zero hash when using the Parlia engine. if beaconRoot == (common.Hash{}) { - if chainConfig := vmenv.ChainConfig(); chainConfig != nil && chainConfig.Parlia != nil { + if chainConfig := evm.ChainConfig(); chainConfig != nil && chainConfig.Parlia != nil { return } } - if tracer := vmenv.Config.Tracer; tracer != nil { - if tracer.OnSystemCallStart != nil { - tracer.OnSystemCallStart() - } + if tracer := evm.Config.Tracer; tracer != nil { + onSystemCallStart(tracer, evm.GetVMContext()) if tracer.OnSystemCallEnd != nil { defer tracer.OnSystemCallEnd() } @@ -308,19 +289,17 @@ func ProcessBeaconBlockRoot(beaconRoot common.Hash, vmenv *vm.EVM, statedb vm.St To: ¶ms.BeaconRootsAddress, Data: beaconRoot[:], } - vmenv.Reset(NewEVMTxContext(msg), statedb) - statedb.AddAddressToAccessList(params.BeaconRootsAddress) - _, _, _ = vmenv.Call(vm.AccountRef(msg.From), *msg.To, msg.Data, 30_000_000, common.U2560) - statedb.Finalise(true) + evm.SetTxContext(NewEVMTxContext(msg)) + evm.StateDB.AddAddressToAccessList(params.BeaconRootsAddress) + _, _, _ = evm.Call(vm.AccountRef(msg.From), *msg.To, msg.Data, 30_000_000, common.U2560) + evm.StateDB.Finalise(true) } // ProcessParentBlockHash stores the parent block hash in the history storage contract // as per EIP-2935. -func ProcessParentBlockHash(prevHash common.Hash, vmenv *vm.EVM, statedb vm.StateDB) { - if tracer := vmenv.Config.Tracer; tracer != nil { - if tracer.OnSystemCallStart != nil { - tracer.OnSystemCallStart() - } +func ProcessParentBlockHash(prevHash common.Hash, evm *vm.EVM) { + if tracer := evm.Config.Tracer; tracer != nil { + onSystemCallStart(tracer, evm.GetVMContext()) if tracer.OnSystemCallEnd != nil { defer tracer.OnSystemCallEnd() } @@ -334,34 +313,31 @@ func ProcessParentBlockHash(prevHash common.Hash, vmenv *vm.EVM, statedb vm.Stat To: ¶ms.HistoryStorageAddress, Data: prevHash.Bytes(), } - vmenv.Reset(NewEVMTxContext(msg), statedb) - statedb.AddAddressToAccessList(params.HistoryStorageAddress) - _, _, _ = vmenv.Call(vm.AccountRef(msg.From), *msg.To, msg.Data, 30_000_000, common.U2560) - statedb.Finalise(true) + evm.SetTxContext(NewEVMTxContext(msg)) + evm.StateDB.AddAddressToAccessList(params.HistoryStorageAddress) + _, _, _ = evm.Call(vm.AccountRef(msg.From), *msg.To, msg.Data, 30_000_000, common.U2560) + evm.StateDB.Finalise(true) } // ProcessWithdrawalQueue calls the EIP-7002 withdrawal queue contract. // It returns the opaque request data returned by the contract. -func ProcessWithdrawalQueue(vmenv *vm.EVM, statedb vm.StateDB) []byte { - return processRequestsSystemCall(vmenv, statedb, 0x01, params.WithdrawalQueueAddress) +func ProcessWithdrawalQueue(requests *[][]byte, evm *vm.EVM) { + processRequestsSystemCall(requests, evm, 0x01, params.WithdrawalQueueAddress) } // ProcessConsolidationQueue calls the EIP-7251 consolidation queue contract. // It returns the opaque request data returned by the contract. -func ProcessConsolidationQueue(vmenv *vm.EVM, statedb vm.StateDB) []byte { - return processRequestsSystemCall(vmenv, statedb, 0x02, params.ConsolidationQueueAddress) +func ProcessConsolidationQueue(requests *[][]byte, evm *vm.EVM) { + processRequestsSystemCall(requests, evm, 0x02, params.ConsolidationQueueAddress) } -func processRequestsSystemCall(vmenv *vm.EVM, statedb vm.StateDB, requestType byte, addr common.Address) []byte { - if tracer := vmenv.Config.Tracer; tracer != nil { - if tracer.OnSystemCallStart != nil { - tracer.OnSystemCallStart() - } +func processRequestsSystemCall(requests *[][]byte, evm *vm.EVM, requestType byte, addr common.Address) { + if tracer := evm.Config.Tracer; tracer != nil { + onSystemCallStart(tracer, evm.GetVMContext()) if tracer.OnSystemCallEnd != nil { defer tracer.OnSystemCallEnd() } } - msg := &Message{ From: params.SystemAddress, GasLimit: 30_000_000, @@ -370,30 +346,44 @@ func processRequestsSystemCall(vmenv *vm.EVM, statedb vm.StateDB, requestType by GasTipCap: common.Big0, To: &addr, } - vmenv.Reset(NewEVMTxContext(msg), statedb) - statedb.AddAddressToAccessList(addr) - ret, _, _ := vmenv.Call(vm.AccountRef(msg.From), *msg.To, msg.Data, 30_000_000, common.U2560) - statedb.Finalise(true) + evm.SetTxContext(NewEVMTxContext(msg)) + evm.StateDB.AddAddressToAccessList(addr) + ret, _, _ := evm.Call(vm.AccountRef(msg.From), *msg.To, msg.Data, 30_000_000, common.U2560) + evm.StateDB.Finalise(true) + if len(ret) == 0 { + return // skip empty output + } - // Create withdrawals requestsData with prefix 0x01 + // Append prefixed requestsData to the requests list. requestsData := make([]byte, len(ret)+1) requestsData[0] = requestType copy(requestsData[1:], ret) - return requestsData + *requests = append(*requests, requestsData) } // ParseDepositLogs extracts the EIP-6110 deposit values from logs emitted by // BeaconDepositContract. -func ParseDepositLogs(logs []*types.Log, config *params.ChainConfig) ([]byte, error) { +func ParseDepositLogs(requests *[][]byte, logs []*types.Log, config *params.ChainConfig) error { deposits := make([]byte, 1) // note: first byte is 0x00 (== deposit request type) for _, log := range logs { if log.Address == config.DepositContractAddress { request, err := types.DepositLogToRequest(log.Data) if err != nil { - return nil, fmt.Errorf("unable to parse deposit data: %v", err) + return fmt.Errorf("unable to parse deposit data: %v", err) } deposits = append(deposits, request...) } } - return deposits, nil + if len(deposits) > 1 { + *requests = append(*requests, deposits) + } + return nil +} + +func onSystemCallStart(tracer *tracing.Hooks, ctx *tracing.VMContext) { + if tracer.OnSystemCallStartV2 != nil { + tracer.OnSystemCallStartV2(ctx) + } else if tracer.OnSystemCallStart != nil { + tracer.OnSystemCallStart() + } } diff --git a/core/state_transition.go b/core/state_transition.go index 6e23f49745..ec5980d6d1 100644 --- a/core/state_transition.go +++ b/core/state_transition.go @@ -189,10 +189,11 @@ func TransactionToMessage(tx *types.Transaction, s types.Signer, baseFee *big.In // indicates a core error meaning that the message would always fail for that particular // state and would never be accepted within a block. func ApplyMessage(evm *vm.EVM, msg *Message, gp *GasPool) (*ExecutionResult, error) { - return NewStateTransition(evm, msg, gp).TransitionDb() + evm.SetTxContext(NewEVMTxContext(msg)) + return newStateTransition(evm, msg, gp).execute() } -// StateTransition represents a state transition. +// stateTransition represents a state transition. // // == The State Transitioning Model // @@ -214,7 +215,7 @@ func ApplyMessage(evm *vm.EVM, msg *Message, gp *GasPool) (*ExecutionResult, err // // 5. Run Script section // 6. Derive new state root -type StateTransition struct { +type stateTransition struct { gp *GasPool msg *Message gasRemaining uint64 @@ -223,9 +224,9 @@ type StateTransition struct { evm *vm.EVM } -// NewStateTransition initialises and returns a new state transition object. -func NewStateTransition(evm *vm.EVM, msg *Message, gp *GasPool) *StateTransition { - return &StateTransition{ +// newStateTransition initialises and returns a new state transition object. +func newStateTransition(evm *vm.EVM, msg *Message, gp *GasPool) *stateTransition { + return &stateTransition{ gp: gp, evm: evm, msg: msg, @@ -234,14 +235,14 @@ func NewStateTransition(evm *vm.EVM, msg *Message, gp *GasPool) *StateTransition } // to returns the recipient of the message. -func (st *StateTransition) to() common.Address { +func (st *stateTransition) to() common.Address { if st.msg == nil || st.msg.To == nil /* contract creation */ { return common.Address{} } return *st.msg.To } -func (st *StateTransition) buyGas() error { +func (st *stateTransition) buyGas() error { mgval := new(big.Int).SetUint64(st.msg.GasLimit) mgval.Mul(mgval, st.msg.GasPrice) balanceCheck := new(big.Int).Set(mgval) @@ -285,7 +286,7 @@ func (st *StateTransition) buyGas() error { return nil } -func (st *StateTransition) preCheck() error { +func (st *stateTransition) preCheck() error { // Only check transactions that are not fake msg := st.msg if !msg.SkipNonceChecks { @@ -370,7 +371,7 @@ func (st *StateTransition) preCheck() error { return st.buyGas() } -// TransitionDb will transition the state by applying the current message and +// execute will transition the state by applying the current message and // returning the evm execution result with following fields. // // - used gas: total gas used (including gas being refunded) @@ -380,7 +381,7 @@ func (st *StateTransition) preCheck() error { // // However if any consensus issue encountered, return the error directly with // nil evm execution result. -func (st *StateTransition) TransitionDb() (*ExecutionResult, error) { +func (st *stateTransition) execute() (*ExecutionResult, error) { // First check this message satisfies all consensus rules before // applying the message. The rules include these clauses // @@ -510,7 +511,7 @@ func (st *StateTransition) TransitionDb() (*ExecutionResult, error) { }, nil } -func (st *StateTransition) refundGas(refundQuotient uint64) uint64 { +func (st *stateTransition) refundGas(refundQuotient uint64) uint64 { // Apply refund counter, capped to a refund quotient refund := st.gasUsed() / refundQuotient if refund > st.state.GetRefund() { @@ -540,11 +541,11 @@ func (st *StateTransition) refundGas(refundQuotient uint64) uint64 { } // gasUsed returns the amount of gas used up by the state transition. -func (st *StateTransition) gasUsed() uint64 { +func (st *stateTransition) gasUsed() uint64 { return st.initialGas - st.gasRemaining } // blobGasUsed returns the amount of blob gas used by the message. -func (st *StateTransition) blobGasUsed() uint64 { +func (st *stateTransition) blobGasUsed() uint64 { return uint64(len(st.msg.BlobHashes) * params.BlobTxBlobGasPerBlob) } diff --git a/core/stateless.go b/core/stateless.go index 5b37d5020e..d21a62b4a5 100644 --- a/core/stateless.go +++ b/core/stateless.go @@ -40,7 +40,7 @@ import ( // - It cannot be placed outside of core, because it needs to construct a dud headerchain // // TODO(karalabe): Would be nice to resolve both issues above somehow and move it. -func ExecuteStateless(config *params.ChainConfig, block *types.Block, witness *stateless.Witness) (common.Hash, common.Hash, error) { +func ExecuteStateless(config *params.ChainConfig, vmconfig vm.Config, block *types.Block, witness *stateless.Witness) (common.Hash, common.Hash, error) { // Sanity check if the supplied block accidentally contains a set root or // receipt hash. If so, be very loud, but still continue. if block.Root() != (common.Hash{}) { @@ -66,7 +66,7 @@ func ExecuteStateless(config *params.ChainConfig, block *types.Block, witness *s validator := NewBlockValidator(config, nil) // No chain, we only validate the state, not the block // Run the stateless blocks processing and self-validate certain fields - res, err := processor.Process(block, db, vm.Config{}) + res, err := processor.Process(block, db, vmconfig) if err != nil { return common.Hash{}, common.Hash{}, err } diff --git a/core/tracing/hooks.go b/core/tracing/hooks.go index 66eca64631..18c0b66a89 100644 --- a/core/tracing/hooks.go +++ b/core/tracing/hooks.go @@ -42,6 +42,7 @@ type StateDB interface { GetBalance(common.Address) *uint256.Int GetNonce(common.Address) uint64 GetCode(common.Address) []byte + GetCodeHash(common.Address) common.Hash GetState(common.Address, common.Hash) common.Hash GetTransientState(common.Address, common.Hash) common.Hash Exist(common.Address) bool @@ -54,9 +55,8 @@ type VMContext struct { BlockNumber *big.Int Time uint64 Random *common.Hash - // Effective tx gas price - GasPrice *big.Int - StateDB StateDB + BaseFee *big.Int + StateDB StateDB } // BlockEvent is emitted upon tracing an incoming block. @@ -145,6 +145,10 @@ type ( // will not be invoked. OnSystemCallStartHook = func() + // OnSystemCallStartHookV2 is called when a system call is about to be executed. Refer + // to `OnSystemCallStartHook` for more information. + OnSystemCallStartHookV2 = func(vm *VMContext) + // OnSystemCallEndHook is called when a system call has finished executing. Today, // this hook is invoked when the EIP-4788 system call is about to be executed to set the // beacon block root. @@ -184,14 +188,15 @@ type Hooks struct { OnFault FaultHook OnGasChange GasChangeHook // Chain events - OnBlockchainInit BlockchainInitHook - OnClose CloseHook - OnBlockStart BlockStartHook - OnBlockEnd BlockEndHook - OnSkippedBlock SkippedBlockHook - OnGenesisBlock GenesisBlockHook - OnSystemCallStart OnSystemCallStartHook - OnSystemCallEnd OnSystemCallEndHook + OnBlockchainInit BlockchainInitHook + OnClose CloseHook + OnBlockStart BlockStartHook + OnBlockEnd BlockEndHook + OnSkippedBlock SkippedBlockHook + OnGenesisBlock GenesisBlockHook + OnSystemCallStart OnSystemCallStartHook + OnSystemCallStartV2 OnSystemCallStartHookV2 + OnSystemCallEnd OnSystemCallEndHook OnSystemTxEnd OnSystemTxEndHook diff --git a/core/txpool/legacypool/legacypool.go b/core/txpool/legacypool/legacypool.go index f7aec0e6ad..328452a6eb 100644 --- a/core/txpool/legacypool/legacypool.go +++ b/core/txpool/legacypool/legacypool.go @@ -703,7 +703,7 @@ func (pool *LegacyPool) validateTxBasics(tx *types.Transaction, local bool) erro // validateTx checks whether a transaction is valid according to the consensus // rules and adheres to some heuristic limits of the local node (price and size). -func (pool *LegacyPool) validateTx(tx *types.Transaction, local bool) error { +func (pool *LegacyPool) validateTx(tx *types.Transaction) error { sender, err := types.Sender(pool.signer, tx) if err != nil { return err @@ -770,7 +770,7 @@ func (pool *LegacyPool) add(tx *types.Transaction, local bool) (replaced bool, e isLocal := local || pool.locals.containsTx(tx) // If the transaction fails basic validation, discard it - if err := pool.validateTx(tx, isLocal); err != nil { + if err := pool.validateTx(tx); err != nil { log.Trace("Discarding invalid transaction", "hash", hash, "err", err) invalidTxMeter.Mark(1) return false, err diff --git a/core/txpool/txpool.go b/core/txpool/txpool.go index ef2a9e8c91..ead7330ea1 100644 --- a/core/txpool/txpool.go +++ b/core/txpool/txpool.go @@ -126,7 +126,7 @@ func (p *TxPool) reserver(id int, subpool SubPool) AddressReserver { return ErrAlreadyReserved } p.reservations[addr] = subpool - if metrics.Enabled { + if metrics.Enabled() { m := fmt.Sprintf("%s/%d", reservationsGaugeName, id) metrics.GetOrRegisterGauge(m, nil).Inc(1) } @@ -143,7 +143,7 @@ func (p *TxPool) reserver(id int, subpool SubPool) AddressReserver { return errors.New("address not owned") } delete(p.reservations, addr) - if metrics.Enabled { + if metrics.Enabled() { m := fmt.Sprintf("%s/%d", reservationsGaugeName, id) metrics.GetOrRegisterGauge(m, nil).Dec(1) } diff --git a/core/types/block.go b/core/types/block.go index d56c985994..09330fba28 100644 --- a/core/types/block.go +++ b/core/types/block.go @@ -509,9 +509,11 @@ func CalcRequestsHash(requests [][]byte) common.Hash { h1, h2 := sha256.New(), sha256.New() var buf common.Hash for _, item := range requests { - h1.Reset() - h1.Write(item) - h2.Write(h1.Sum(buf[:0])) + if len(item) > 1 { // skip items with only requestType and no data. + h1.Reset() + h1.Write(item) + h2.Write(h1.Sum(buf[:0])) + } } h2.Sum(buf[:0]) return buf diff --git a/core/types/hashes.go b/core/types/hashes.go index 43e9130fd1..55506d63d0 100644 --- a/core/types/hashes.go +++ b/core/types/hashes.go @@ -41,6 +41,9 @@ var ( // EmptyWithdrawalsHash is the known hash of the empty withdrawal set. EmptyWithdrawalsHash = common.HexToHash("56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421") + // EmptyRequestsHash is the known hash of an empty request set, sha256(""). + EmptyRequestsHash = common.HexToHash("e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855") + // EmptyVerkleHash is the known hash of an empty verkle trie. EmptyVerkleHash = common.Hash{} ) diff --git a/core/verkle_witness_test.go b/core/verkle_witness_test.go index dd489e561e..be3ac0db24 100644 --- a/core/verkle_witness_test.go +++ b/core/verkle_witness_test.go @@ -229,8 +229,8 @@ func TestProcessParentBlockHash(t *testing.T) { for i := 1; i <= num; i++ { header := &types.Header{ParentHash: common.Hash{byte(i)}, Number: big.NewInt(int64(i)), Difficulty: new(big.Int)} vmContext := NewEVMBlockContext(header, nil, new(common.Address)) - evm := vm.NewEVM(vmContext, vm.TxContext{}, statedb, params.MergedTestChainConfig, vm.Config{}) - ProcessParentBlockHash(header.ParentHash, evm, statedb) + evm := vm.NewEVM(vmContext, statedb, params.MergedTestChainConfig, vm.Config{}) + ProcessParentBlockHash(header.ParentHash, evm) } // Read block hashes for block 0 .. num-1 for i := 0; i < num; i++ { @@ -1037,7 +1037,7 @@ func TestProcessVerkleSelfDestructInSameTxWithSelfBeneficiaryAndPrefundedAccount ) // Prefund the account, at an address that the contract will be deployed at, // before it selfdestrucs. We can therefore check that the account itseld is - // NOT destroyed, which is what the currrent version of the spec requires. + // NOT destroyed, which is what the current version of the spec requires. // TODO(gballet) revisit after the spec has been modified. gspec.Alloc[contract] = types.Account{ Balance: big.NewInt(100), diff --git a/core/vm/eof.go b/core/vm/eof.go index fcced6ae75..a5406283d5 100644 --- a/core/vm/eof.go +++ b/core/vm/eof.go @@ -96,7 +96,7 @@ func (meta *functionMetadata) checkInputs(stackMin int) error { } // checkStackMax checks the if current maximum stack combined with the -// functin max stack will result in a stack overflow, and if so returns an error. +// function max stack will result in a stack overflow, and if so returns an error. func (meta *functionMetadata) checkStackMax(stackMax int) error { newMaxStack := stackMax + int(meta.maxStackHeight) - int(meta.inputs) if newMaxStack > int(params.StackLimit) { diff --git a/core/vm/evm.go b/core/vm/evm.go index b8e908f0b7..02cace7b68 100644 --- a/core/vm/evm.go +++ b/core/vm/evm.go @@ -124,25 +124,29 @@ type EVM struct { precompiles map[common.Address]PrecompiledContract } -// NewEVM returns a new EVM. The returned EVM is not thread safe and should -// only ever be used *once*. -func NewEVM(blockCtx BlockContext, txCtx TxContext, statedb StateDB, chainConfig *params.ChainConfig, config Config) *EVM { - evm := EvmPool.Get().(*EVM) - evm.Context = blockCtx - evm.TxContext = txCtx - evm.StateDB = statedb - evm.Config = config - evm.chainConfig = chainConfig - evm.chainRules = chainConfig.Rules(blockCtx.BlockNumber, blockCtx.Random != nil, blockCtx.Time) - evm.abort.Store(false) - evm.callGasTemp = 0 - evm.depth = 0 +// NewEVM constructs an EVM instance with the supplied block context, state +// database and several configs. It meant to be used throughout the entire +// state transition of a block, with the transaction context switched as +// needed by calling evm.SetTxContext. +func NewEVM(blockCtx BlockContext, statedb StateDB, chainConfig *params.ChainConfig, config Config) *EVM { + evm := &EVM{ + Context: blockCtx, + StateDB: statedb, + Config: config, + chainConfig: chainConfig, + chainRules: chainConfig.Rules(blockCtx.BlockNumber, blockCtx.Random != nil, blockCtx.Time), + } evm.precompiles = activePrecompiledContracts(evm.chainRules) evm.interpreter = NewEVMInterpreter(evm) return evm } +// SetTracer sets the tracer for following state transition. +func (evm *EVM) SetTracer(tracer *tracing.Hooks) { + evm.Config.Tracer = tracer +} + // SetPrecompiles sets the precompiled contracts for the EVM. // This method is only used through RPC calls. // It is not thread-safe. @@ -150,14 +154,13 @@ func (evm *EVM) SetPrecompiles(precompiles PrecompiledContracts) { evm.precompiles = precompiles } -// Reset resets the EVM with a new transaction context.Reset +// SetTxContext resets the EVM with a new transaction context. // This is not threadsafe and should only be done very cautiously. -func (evm *EVM) Reset(txCtx TxContext, statedb StateDB) { +func (evm *EVM) SetTxContext(txCtx TxContext) { if evm.chainRules.IsEIP4762 { - txCtx.AccessEvents = state.NewAccessEvents(statedb.PointCache()) + txCtx.AccessEvents = state.NewAccessEvents(evm.StateDB.PointCache()) } evm.TxContext = txCtx - evm.StateDB = statedb } // Cancel cancels any running EVM operation. This may be called concurrently and @@ -611,7 +614,7 @@ func (evm *EVM) GetVMContext() *tracing.VMContext { BlockNumber: evm.Context.BlockNumber, Time: evm.Context.Time, Random: evm.Context.Random, - GasPrice: evm.TxContext.GasPrice, + BaseFee: evm.Context.BaseFee, StateDB: evm.StateDB, } } diff --git a/core/vm/gas_table_test.go b/core/vm/gas_table_test.go index babe9a5b6a..be86885261 100644 --- a/core/vm/gas_table_test.go +++ b/core/vm/gas_table_test.go @@ -95,16 +95,16 @@ func TestEIP2200(t *testing.T) { CanTransfer: func(StateDB, common.Address, *uint256.Int) bool { return true }, Transfer: func(StateDB, common.Address, common.Address, *uint256.Int) {}, } - vmenv := NewEVM(vmctx, TxContext{}, statedb, params.AllEthashProtocolChanges, Config{ExtraEips: []int{2200}}) + evm := NewEVM(vmctx, statedb, params.AllEthashProtocolChanges, Config{ExtraEips: []int{2200}}) - _, gas, err := vmenv.Call(AccountRef(common.Address{}), address, nil, tt.gaspool, new(uint256.Int)) + _, gas, err := evm.Call(AccountRef(common.Address{}), address, nil, tt.gaspool, new(uint256.Int)) if !errors.Is(err, tt.failure) { t.Errorf("test %d: failure mismatch: have %v, want %v", i, err, tt.failure) } if used := tt.gaspool - gas; used != tt.used { t.Errorf("test %d: gas used mismatch: have %v, want %v", i, used, tt.used) } - if refund := vmenv.StateDB.GetRefund(); refund != tt.refund { + if refund := evm.StateDB.GetRefund(); refund != tt.refund { t.Errorf("test %d: gas refund mismatch: have %v, want %v", i, refund, tt.refund) } } @@ -151,9 +151,9 @@ func TestCreateGas(t *testing.T) { config.ExtraEips = []int{3860} } - vmenv := NewEVM(vmctx, TxContext{}, statedb, params.AllEthashProtocolChanges, config) + evm := NewEVM(vmctx, statedb, params.AllEthashProtocolChanges, config) var startGas = uint64(testGas) - ret, gas, err := vmenv.Call(AccountRef(common.Address{}), address, nil, startGas, new(uint256.Int)) + ret, gas, err := evm.Call(AccountRef(common.Address{}), address, nil, startGas, new(uint256.Int)) if err != nil { return false } diff --git a/core/vm/instructions.go b/core/vm/instructions.go index 47eb62be08..9b9a31a855 100644 --- a/core/vm/instructions.go +++ b/core/vm/instructions.go @@ -501,7 +501,6 @@ func opMload(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]by } func opMstore(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) { - // pop value of the stack mStart, val := scope.Stack.pop(), scope.Stack.pop() scope.Memory.Set32(mStart.Uint64(), &val) return nil, nil diff --git a/core/vm/instructions_test.go b/core/vm/instructions_test.go index b8e62e1de5..08f2b2bfea 100644 --- a/core/vm/instructions_test.go +++ b/core/vm/instructions_test.go @@ -104,10 +104,9 @@ func init() { func testTwoOperandOp(t *testing.T, tests []TwoOperandTestcase, opFn executionFunc, name string) { var ( - env = NewEVM(BlockContext{}, TxContext{}, nil, params.TestChainConfig, Config{}) - stack = newstack() - pc = uint64(0) - evmInterpreter = env.interpreter + evm = NewEVM(BlockContext{}, nil, params.TestChainConfig, Config{}) + stack = newstack() + pc = uint64(0) ) for i, test := range tests { @@ -116,7 +115,7 @@ func testTwoOperandOp(t *testing.T, tests []TwoOperandTestcase, opFn executionFu expected := new(uint256.Int).SetBytes(common.Hex2Bytes(test.Expected)) stack.push(x) stack.push(y) - opFn(&pc, evmInterpreter, &ScopeContext{nil, stack, nil}) + opFn(&pc, evm.interpreter, &ScopeContext{nil, stack, nil}) if len(stack.data) != 1 { t.Errorf("Expected one item on stack after %v, got %d: ", name, len(stack.data)) } @@ -203,10 +202,9 @@ func TestSAR(t *testing.T) { func TestAddMod(t *testing.T) { var ( - env = NewEVM(BlockContext{}, TxContext{}, nil, params.TestChainConfig, Config{}) - stack = newstack() - evmInterpreter = NewEVMInterpreter(env) - pc = uint64(0) + evm = NewEVM(BlockContext{}, nil, params.TestChainConfig, Config{}) + stack = newstack() + pc = uint64(0) ) tests := []struct { x string @@ -231,7 +229,7 @@ func TestAddMod(t *testing.T) { stack.push(z) stack.push(y) stack.push(x) - opAddmod(&pc, evmInterpreter, &ScopeContext{nil, stack, nil}) + opAddmod(&pc, evm.interpreter, &ScopeContext{nil, stack, nil}) actual := stack.pop() if actual.Cmp(expected) != 0 { t.Errorf("Testcase %d, expected %x, got %x", i, expected, actual) @@ -247,10 +245,9 @@ func TestWriteExpectedValues(t *testing.T) { // getResult is a convenience function to generate the expected values getResult := func(args []*twoOperandParams, opFn executionFunc) []TwoOperandTestcase { var ( - env = NewEVM(BlockContext{}, TxContext{}, nil, params.TestChainConfig, Config{}) - stack = newstack() - pc = uint64(0) - interpreter = env.interpreter + evm = NewEVM(BlockContext{}, nil, params.TestChainConfig, Config{}) + stack = newstack() + pc = uint64(0) ) result := make([]TwoOperandTestcase, len(args)) for i, param := range args { @@ -258,7 +255,7 @@ func TestWriteExpectedValues(t *testing.T) { y := new(uint256.Int).SetBytes(common.Hex2Bytes(param.y)) stack.push(x) stack.push(y) - opFn(&pc, interpreter, &ScopeContext{nil, stack, nil}) + opFn(&pc, evm.interpreter, &ScopeContext{nil, stack, nil}) actual := stack.pop() result[i] = TwoOperandTestcase{param.x, param.y, fmt.Sprintf("%064x", actual)} } @@ -292,13 +289,10 @@ func TestJsonTestcases(t *testing.T) { func opBenchmark(bench *testing.B, op executionFunc, args ...string) { var ( - env = NewEVM(BlockContext{}, TxContext{}, nil, params.TestChainConfig, Config{}) - stack = newstack() - scope = &ScopeContext{nil, stack, nil} - evmInterpreter = NewEVMInterpreter(env) + evm = NewEVM(BlockContext{}, nil, params.TestChainConfig, Config{}) + stack = newstack() + scope = &ScopeContext{nil, stack, nil} ) - - env.interpreter = evmInterpreter // convert args intArgs := make([]*uint256.Int, len(args)) for i, arg := range args { @@ -310,7 +304,7 @@ func opBenchmark(bench *testing.B, op executionFunc, args ...string) { for _, arg := range intArgs { stack.push(arg) } - op(&pc, evmInterpreter, scope) + op(&pc, evm.interpreter, scope) stack.pop() } bench.StopTimer() @@ -533,25 +527,22 @@ func BenchmarkOpIsZero(b *testing.B) { func TestOpMstore(t *testing.T) { var ( - env = NewEVM(BlockContext{}, TxContext{}, nil, params.TestChainConfig, Config{}) - stack = newstack() - mem = NewMemory() - evmInterpreter = NewEVMInterpreter(env) + evm = NewEVM(BlockContext{}, nil, params.TestChainConfig, Config{}) + stack = newstack() + mem = NewMemory() ) - - env.interpreter = evmInterpreter mem.Resize(64) pc := uint64(0) v := "abcdef00000000000000abba000000000deaf000000c0de00100000000133700" stack.push(new(uint256.Int).SetBytes(common.Hex2Bytes(v))) stack.push(new(uint256.Int)) - opMstore(&pc, evmInterpreter, &ScopeContext{mem, stack, nil}) + opMstore(&pc, evm.interpreter, &ScopeContext{mem, stack, nil}) if got := common.Bytes2Hex(mem.GetCopy(0, 32)); got != v { t.Fatalf("Mstore fail, got %v, expected %v", got, v) } stack.push(new(uint256.Int).SetUint64(0x1)) stack.push(new(uint256.Int)) - opMstore(&pc, evmInterpreter, &ScopeContext{mem, stack, nil}) + opMstore(&pc, evm.interpreter, &ScopeContext{mem, stack, nil}) if common.Bytes2Hex(mem.GetCopy(0, 32)) != "0000000000000000000000000000000000000000000000000000000000000001" { t.Fatalf("Mstore failed to overwrite previous value") } @@ -559,13 +550,10 @@ func TestOpMstore(t *testing.T) { func BenchmarkOpMstore(bench *testing.B) { var ( - env = NewEVM(BlockContext{}, TxContext{}, nil, params.TestChainConfig, Config{}) - stack = newstack() - mem = NewMemory() - evmInterpreter = NewEVMInterpreter(env) + evm = NewEVM(BlockContext{}, nil, params.TestChainConfig, Config{}) + stack = newstack() + mem = NewMemory() ) - - env.interpreter = evmInterpreter mem.Resize(64) pc := uint64(0) memStart := new(uint256.Int) @@ -575,43 +563,41 @@ func BenchmarkOpMstore(bench *testing.B) { for i := 0; i < bench.N; i++ { stack.push(value) stack.push(memStart) - opMstore(&pc, evmInterpreter, &ScopeContext{mem, stack, nil}) + opMstore(&pc, evm.interpreter, &ScopeContext{mem, stack, nil}) } } func TestOpTstore(t *testing.T) { var ( - statedb, _ = state.New(types.EmptyRootHash, state.NewDatabaseForTesting()) - env = NewEVM(BlockContext{}, TxContext{}, statedb, params.TestChainConfig, Config{}) - stack = newstack() - mem = NewMemory() - evmInterpreter = NewEVMInterpreter(env) - caller = common.Address{} - to = common.Address{1} - contractRef = contractRef{caller} - contract = NewContract(contractRef, AccountRef(to), new(uint256.Int), 0) - scopeContext = ScopeContext{mem, stack, contract} - value = common.Hex2Bytes("abcdef00000000000000abba000000000deaf000000c0de00100000000133700") + statedb, _ = state.New(types.EmptyRootHash, state.NewDatabaseForTesting()) + evm = NewEVM(BlockContext{}, statedb, params.TestChainConfig, Config{}) + stack = newstack() + mem = NewMemory() + caller = common.Address{} + to = common.Address{1} + contractRef = contractRef{caller} + contract = NewContract(contractRef, AccountRef(to), new(uint256.Int), 0) + scopeContext = ScopeContext{mem, stack, contract} + value = common.Hex2Bytes("abcdef00000000000000abba000000000deaf000000c0de00100000000133700") ) // Add a stateObject for the caller and the contract being called statedb.CreateAccount(caller) statedb.CreateAccount(to) - env.interpreter = evmInterpreter pc := uint64(0) // push the value to the stack stack.push(new(uint256.Int).SetBytes(value)) // push the location to the stack stack.push(new(uint256.Int)) - opTstore(&pc, evmInterpreter, &scopeContext) + opTstore(&pc, evm.interpreter, &scopeContext) // there should be no elements on the stack after TSTORE if stack.len() != 0 { t.Fatal("stack wrong size") } // push the location to the stack stack.push(new(uint256.Int)) - opTload(&pc, evmInterpreter, &scopeContext) + opTload(&pc, evm.interpreter, &scopeContext) // there should be one element on the stack after TLOAD if stack.len() != 1 { t.Fatal("stack wrong size") @@ -624,12 +610,10 @@ func TestOpTstore(t *testing.T) { func BenchmarkOpKeccak256(bench *testing.B) { var ( - env = NewEVM(BlockContext{}, TxContext{}, nil, params.TestChainConfig, Config{}) - stack = newstack() - mem = NewMemory() - evmInterpreter = NewEVMInterpreter(env) + evm = NewEVM(BlockContext{}, nil, params.TestChainConfig, Config{}) + stack = newstack() + mem = NewMemory() ) - env.interpreter = evmInterpreter mem.Resize(32) pc := uint64(0) start := new(uint256.Int) @@ -638,7 +622,7 @@ func BenchmarkOpKeccak256(bench *testing.B) { for i := 0; i < bench.N; i++ { stack.push(uint256.NewInt(32)) stack.push(start) - opKeccak256(&pc, evmInterpreter, &ScopeContext{mem, stack, nil}) + opKeccak256(&pc, evm.interpreter, &ScopeContext{mem, stack, nil}) } } @@ -728,12 +712,11 @@ func TestRandom(t *testing.T) { {name: "hash(0x010203)", random: crypto.Keccak256Hash([]byte{0x01, 0x02, 0x03})}, } { var ( - env = NewEVM(BlockContext{Random: &tt.random}, TxContext{}, nil, params.TestChainConfig, Config{}) - stack = newstack() - pc = uint64(0) - evmInterpreter = env.interpreter + evm = NewEVM(BlockContext{Random: &tt.random}, nil, params.TestChainConfig, Config{}) + stack = newstack() + pc = uint64(0) ) - opRandom(&pc, evmInterpreter, &ScopeContext{nil, stack, nil}) + opRandom(&pc, evm.interpreter, &ScopeContext{nil, stack, nil}) if len(stack.data) != 1 { t.Errorf("Expected one item on stack after %v, got %d: ", tt.name, len(stack.data)) } @@ -769,13 +752,13 @@ func TestBlobHash(t *testing.T) { {name: "out-of-bounds (nil)", idx: 25, expect: zero, hashes: nil}, } { var ( - env = NewEVM(BlockContext{}, TxContext{BlobHashes: tt.hashes}, nil, params.TestChainConfig, Config{}) - stack = newstack() - pc = uint64(0) - evmInterpreter = env.interpreter + evm = NewEVM(BlockContext{}, nil, params.TestChainConfig, Config{}) + stack = newstack() + pc = uint64(0) ) + evm.SetTxContext(TxContext{BlobHashes: tt.hashes}) stack.push(uint256.NewInt(tt.idx)) - opBlobHash(&pc, evmInterpreter, &ScopeContext{nil, stack, nil}) + opBlobHash(&pc, evm.interpreter, &ScopeContext{nil, stack, nil}) if len(stack.data) != 1 { t.Errorf("Expected one item on stack after %v, got %d: ", tt.name, len(stack.data)) } @@ -872,10 +855,9 @@ func TestOpMCopy(t *testing.T) { }, } { var ( - env = NewEVM(BlockContext{}, TxContext{}, nil, params.TestChainConfig, Config{}) - stack = newstack() - pc = uint64(0) - evmInterpreter = env.interpreter + evm = NewEVM(BlockContext{}, nil, params.TestChainConfig, Config{}) + stack = newstack() + pc = uint64(0) ) data := common.FromHex(strings.ReplaceAll(tc.pre, " ", "")) // Set pre @@ -906,7 +888,7 @@ func TestOpMCopy(t *testing.T) { } // and the dynamic cost var haveGas uint64 - if dynamicCost, err := gasMcopy(env, nil, stack, mem, memorySize); err != nil { + if dynamicCost, err := gasMcopy(evm, nil, stack, mem, memorySize); err != nil { t.Error(err) } else { haveGas = GasFastestStep + dynamicCost @@ -916,7 +898,7 @@ func TestOpMCopy(t *testing.T) { mem.Resize(memorySize) } // Do the copy - opMcopy(&pc, evmInterpreter, &ScopeContext{mem, stack, nil}) + opMcopy(&pc, evm.interpreter, &ScopeContext{mem, stack, nil}) want := common.FromHex(strings.ReplaceAll(tc.want, " ", "")) if have := mem.store; !bytes.Equal(want, have) { t.Errorf("case %d: \nwant: %#x\nhave: %#x\n", i, want, have) diff --git a/core/vm/interpreter.go b/core/vm/interpreter.go index 83dd7881da..9a525725e0 100644 --- a/core/vm/interpreter.go +++ b/core/vm/interpreter.go @@ -18,7 +18,6 @@ package vm import ( "fmt" - "sync" "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common/math" @@ -28,12 +27,6 @@ import ( "github.com/holiman/uint256" ) -var EVMInterpreterPool = sync.Pool{ - New: func() interface{} { - return &EVMInterpreter{} - }, -} - // Config are the configuration options for the Interpreter type Config struct { Tracer *tracing.Hooks @@ -156,13 +149,7 @@ func NewEVMInterpreter(evm *EVM) *EVMInterpreter { } } evm.Config.ExtraEips = extraEips - evmInterpreter := EVMInterpreterPool.Get().(*EVMInterpreter) - evmInterpreter.evm = evm - evmInterpreter.table = table - evmInterpreter.readOnly = false - evmInterpreter.returnData = nil - - return evmInterpreter + return &EVMInterpreter{evm: evm, table: table} } // Run loops and evaluates the contract's code with the given input data and returns diff --git a/core/vm/interpreter_test.go b/core/vm/interpreter_test.go index 7d4b2ddf36..cacad8f813 100644 --- a/core/vm/interpreter_test.go +++ b/core/vm/interpreter_test.go @@ -47,7 +47,7 @@ func TestLoopInterrupt(t *testing.T) { statedb.SetCode(address, common.Hex2Bytes(tt)) statedb.Finalise(true) - evm := NewEVM(vmctx, TxContext{}, statedb, params.AllEthashProtocolChanges, Config{}) + evm := NewEVM(vmctx, statedb, params.AllEthashProtocolChanges, Config{}) errChannel := make(chan error) timeout := make(chan bool) diff --git a/core/vm/program/program.go b/core/vm/program/program.go new file mode 100644 index 0000000000..acc7fd25fc --- /dev/null +++ b/core/vm/program/program.go @@ -0,0 +1,430 @@ +// Copyright 2024 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the goevmlab library. If not, see . + +// package program is a utility to create EVM bytecode for testing, but _not_ for production. As such: +// +// - There are not package guarantees. We might iterate heavily on this package, and do backwards-incompatible changes without warning +// - There are no quality-guarantees. These utilities may produce evm-code that is non-functional. YMMV. +// - There are no stability-guarantees. The utility will `panic` if the inputs do not align / make sense. +package program + +import ( + "fmt" + "math/big" + + "github.com/ethereum/go-ethereum/core/vm" + "github.com/holiman/uint256" +) + +// Program is a simple bytecode container. It can be used to construct +// simple EVM programs. Errors during construction of a Program typically +// cause panics: so avoid using these programs in production settings or on +// untrusted input. +// This package is mainly meant to aid in testing. This is not a production +// -level "compiler". +type Program struct { + code []byte +} + +// New creates a new Program +func New() *Program { + return &Program{ + code: make([]byte, 0), + } +} + +// add adds the op to the code. +func (p *Program) add(op byte) *Program { + p.code = append(p.code, op) + return p +} + +// pushBig creates a PUSHX instruction and pushes the given val. +// - If the val is nil, it pushes zero +// - If the val is bigger than 32 bytes, it panics +func (p *Program) doPush(val *uint256.Int) { + if val == nil { + val = new(uint256.Int) + } + valBytes := val.Bytes() + if len(valBytes) == 0 { + valBytes = append(valBytes, 0) + } + bLen := len(valBytes) + p.add(byte(vm.PUSH1) - 1 + byte(bLen)) + p.Append(valBytes) +} + +// Append appends the given data to the code. +func (p *Program) Append(data []byte) *Program { + p.code = append(p.code, data...) + return p +} + +// Bytes returns the Program bytecode. OBS: This is not a copy. +func (p *Program) Bytes() []byte { + return p.code +} + +// SetBytes sets the Program bytecode. The combination of Bytes and SetBytes means +// that external callers can implement missing functionality: +// +// ... +// prog.Push(1) +// code := prog.Bytes() +// manipulate(code) +// prog.SetBytes(code) +func (p *Program) SetBytes(code []byte) { + p.code = code +} + +// Hex returns the Program bytecode as a hex string. +func (p *Program) Hex() string { + return fmt.Sprintf("%02x", p.Bytes()) +} + +// Op appends the given opcode(s). +func (p *Program) Op(ops ...vm.OpCode) *Program { + for _, op := range ops { + p.add(byte(op)) + } + return p +} + +// Push creates a PUSHX instruction with the data provided. If zero is being pushed, +// PUSH0 will be avoided in favour of [PUSH1 0], to ensure backwards compatibility. +func (p *Program) Push(val any) *Program { + switch v := val.(type) { + case int: + p.doPush(new(uint256.Int).SetUint64(uint64(v))) + case uint64: + p.doPush(new(uint256.Int).SetUint64(v)) + case uint32: + p.doPush(new(uint256.Int).SetUint64(uint64(v))) + case uint16: + p.doPush(new(uint256.Int).SetUint64(uint64(v))) + case *big.Int: + p.doPush(uint256.MustFromBig(v)) + case *uint256.Int: + p.doPush(v) + case uint256.Int: + p.doPush(&v) + case []byte: + p.doPush(new(uint256.Int).SetBytes(v)) + case byte: + p.doPush(new(uint256.Int).SetUint64(uint64(v))) + case interface{ Bytes() []byte }: + // Here, we jump through some hoops in order to avoid depending on + // go-ethereum types.Address and common.Hash, and instead use the + // interface. This works on both values and pointers! + p.doPush(new(uint256.Int).SetBytes(v.Bytes())) + case nil: + p.doPush(nil) + default: + panic(fmt.Sprintf("unsupported type %T", v)) + } + return p +} + +// Push0 implements PUSH0 (0x5f). +func (p *Program) Push0() *Program { + return p.Op(vm.PUSH0) +} + +// ExtcodeCopy performs an extcodecopy invocation. +func (p *Program) ExtcodeCopy(address, memOffset, codeOffset, length any) *Program { + p.Push(length) + p.Push(codeOffset) + p.Push(memOffset) + p.Push(address) + return p.Op(vm.EXTCODECOPY) +} + +// Call is a convenience function to make a call. If 'gas' is nil, the opcode GAS will +// be used to provide all gas. +func (p *Program) Call(gas *uint256.Int, address, value, inOffset, inSize, outOffset, outSize any) *Program { + if outOffset == outSize && inSize == outSize && inOffset == outSize && value == outSize { + p.Push(outSize).Op(vm.DUP1, vm.DUP1, vm.DUP1, vm.DUP1) + } else { + p.Push(outSize).Push(outOffset).Push(inSize).Push(inOffset).Push(value) + } + p.Push(address) + if gas == nil { + p.Op(vm.GAS) + } else { + p.doPush(gas) + } + return p.Op(vm.CALL) +} + +// DelegateCall is a convenience function to make a delegatecall. If 'gas' is nil, the opcode GAS will +// be used to provide all gas. +func (p *Program) DelegateCall(gas *uint256.Int, address, inOffset, inSize, outOffset, outSize any) *Program { + if outOffset == outSize && inSize == outSize && inOffset == outSize { + p.Push(outSize).Op(vm.DUP1, vm.DUP1, vm.DUP1) + } else { + p.Push(outSize).Push(outOffset).Push(inSize).Push(inOffset) + } + p.Push(address) + if gas == nil { + p.Op(vm.GAS) + } else { + p.doPush(gas) + } + return p.Op(vm.DELEGATECALL) +} + +// StaticCall is a convenience function to make a staticcall. If 'gas' is nil, the opcode GAS will +// be used to provide all gas. +func (p *Program) StaticCall(gas *uint256.Int, address, inOffset, inSize, outOffset, outSize any) *Program { + if outOffset == outSize && inSize == outSize && inOffset == outSize { + p.Push(outSize).Op(vm.DUP1, vm.DUP1, vm.DUP1) + } else { + p.Push(outSize).Push(outOffset).Push(inSize).Push(inOffset) + } + p.Push(address) + if gas == nil { + p.Op(vm.GAS) + } else { + p.doPush(gas) + } + return p.Op(vm.STATICCALL) +} + +// StaticCall is a convenience function to make a callcode. If 'gas' is nil, the opcode GAS will +// be used to provide all gas. +func (p *Program) CallCode(gas *uint256.Int, address, value, inOffset, inSize, outOffset, outSize any) *Program { + if outOffset == outSize && inSize == outSize && inOffset == outSize { + p.Push(outSize).Op(vm.DUP1, vm.DUP1, vm.DUP1) + } else { + p.Push(outSize).Push(outOffset).Push(inSize).Push(inOffset) + } + p.Push(value) + p.Push(address) + if gas == nil { + p.Op(vm.GAS) + } else { + p.doPush(gas) + } + return p.Op(vm.CALLCODE) +} + +// Label returns the PC (of the next instruction). +func (p *Program) Label() uint64 { + return uint64(len(p.code)) +} + +// Jumpdest adds a JUMPDEST op, and returns the PC of that instruction. +func (p *Program) Jumpdest() (*Program, uint64) { + here := p.Label() + p.Op(vm.JUMPDEST) + return p, here +} + +// Jump pushes the destination and adds a JUMP. +func (p *Program) Jump(loc any) *Program { + p.Push(loc) + p.Op(vm.JUMP) + return p +} + +// JumpIf implements JUMPI. +func (p *Program) JumpIf(loc any, condition any) *Program { + p.Push(condition) + p.Push(loc) + p.Op(vm.JUMPI) + return p +} + +// Size returns the current size of the bytecode. +func (p *Program) Size() int { + return len(p.code) +} + +// InputAddressToStack stores the input (calldata) to memory as address (20 bytes). +func (p *Program) InputAddressToStack(inputOffset uint32) *Program { + p.Push(inputOffset) + p.Op(vm.CALLDATALOAD) // Loads [n -> n + 32] of input data to stack top + mask, _ := big.NewInt(0).SetString("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF", 16) + p.Push(mask) // turn into address + return p.Op(vm.AND) +} + +// MStore stores the provided data (into the memory area starting at memStart). +func (p *Program) Mstore(data []byte, memStart uint32) *Program { + var idx = 0 + // We need to store it in chunks of 32 bytes + for ; idx+32 <= len(data); idx += 32 { + chunk := data[idx : idx+32] + // push the value + p.Push(chunk) + // push the memory index + p.Push(uint32(idx) + memStart) + p.Op(vm.MSTORE) + } + // Remainders become stored using MSTORE8 + for ; idx < len(data); idx++ { + b := data[idx] + // push the byte + p.Push(b) + p.Push(uint32(idx) + memStart) + p.Op(vm.MSTORE8) + } + return p +} + +// MstoreSmall stores the provided data, which must be smaller than 32 bytes, +// into the memory area starting at memStart. +// The data will be LHS zero-added to align on 32 bytes. +// For example, providing data 0x1122, it will do a PUSH2: +// PUSH2 0x1122, resulting in +// stack: 0x0000000000000000000000000000000000000000000000000000000000001122 +// followed by MSTORE(0,0) +// And thus, the resulting memory will be +// [ 0000000000000000000000000000000000000000000000000000000000001122 ] +func (p *Program) MstoreSmall(data []byte, memStart uint32) *Program { + if len(data) > 32 { + // For larger sizes, use Mstore instead. + panic("only <=32 byte data size supported") + } + if len(data) == 0 { + // Storing 0-length data smells of an error somewhere. + panic("data is zero length") + } + // push the value + p.Push(data) + // push the memory index + p.Push(memStart) + p.Op(vm.MSTORE) + return p +} + +// MemToStorage copies the given memory area into SSTORE slots, +// It expects data to be aligned to 32 byte, and does not zero out +// remainders if some data is not +// I.e, if given a 1-byte area, it will still copy the full 32 bytes to storage. +func (p *Program) MemToStorage(memStart, memSize, startSlot int) *Program { + // We need to store it in chunks of 32 bytes + for idx := memStart; idx < (memStart + memSize); idx += 32 { + dataStart := idx + // Mload the chunk + p.Push(dataStart) + p.Op(vm.MLOAD) + // Value is now on stack, + p.Push(startSlot) + p.Op(vm.SSTORE) + startSlot++ + } + return p +} + +// ReturnViaCodeCopy utilises CODECOPY to place the given data in the bytecode of +// p, loads into memory (offset 0) and returns the code. +// This is a typical "constructor". +// Note: since all indexing is calculated immediately, the preceding bytecode +// must not be expanded or shortened. +func (p *Program) ReturnViaCodeCopy(data []byte) *Program { + p.Push(len(data)) + // For convenience, we'll use PUSH2 for the offset. Then we know we can always + // fit, since code is limited to 0xc000 + p.Op(vm.PUSH2) + offsetPos := p.Size() // Need to update this position later on + p.Append([]byte{0, 0}) // Offset of the code to be copied + p.Push(0) // Offset in memory (destination) + p.Op(vm.CODECOPY) // Copy from code[offset:offset+len] to memory[0:] + p.Return(0, len(data)) // Return memory[0:len] + offset := p.Size() + p.Append(data) // And add the data + + // Now, go back and fix the offset + p.code[offsetPos] = byte(offset >> 8) + p.code[offsetPos+1] = byte(offset) + return p +} + +// Sstore stores the given byte array to the given slot. +// OBS! Does not verify that the value indeed fits into 32 bytes. +// If it does not, it will panic later on via doPush. +func (p *Program) Sstore(slot any, value any) *Program { + p.Push(value) + p.Push(slot) + return p.Op(vm.SSTORE) +} + +// Tstore stores the given byte array to the given t-slot. +// OBS! Does not verify that the value indeed fits into 32 bytes. +// If it does not, it will panic later on via doPush. +func (p *Program) Tstore(slot any, value any) *Program { + p.Push(value) + p.Push(slot) + return p.Op(vm.TSTORE) +} + +// Return implements RETURN +func (p *Program) Return(offset, len int) *Program { + p.Push(len) + p.Push(offset) + return p.Op(vm.RETURN) +} + +// ReturnData loads the given data into memory, and does a return with it +func (p *Program) ReturnData(data []byte) *Program { + p.Mstore(data, 0) + return p.Return(0, len(data)) +} + +// Create2 uses create2 to construct a contract with the given bytecode. +// This operation leaves either '0' or address on the stack. +func (p *Program) Create2(code []byte, salt any) *Program { + var ( + value = 0 + offset = 0 + size = len(code) + ) + // Load the code into mem + p.Mstore(code, 0) + // Create it + return p.Push(salt). + Push(size). + Push(offset). + Push(value). + Op(vm.CREATE2) + // On the stack now, is either + // - zero: in case of failure, OR + // - address: in case of success +} + +// Create2ThenCall calls create2 with the given initcode and salt, and then calls +// into the created contract (or calls into zero, if the creation failed). +func (p *Program) Create2ThenCall(code []byte, salt any) *Program { + p.Create2(code, salt) + // If there happen to be a zero on the stack, it doesn't matter, we're + // not sending any value anyway + p.Push(0).Push(0) // mem out + p.Push(0).Push(0) // mem in + p.Push(0) // value + p.Op(vm.DUP6) // address + p.Op(vm.GAS) + p.Op(vm.CALL) + p.Op(vm.POP) // pop the retval + return p.Op(vm.POP) // pop the address +} + +// Selfdestruct pushes beneficiary and invokes selfdestruct. +func (p *Program) Selfdestruct(beneficiary any) *Program { + p.Push(beneficiary) + return p.Op(vm.SELFDESTRUCT) +} diff --git a/core/vm/program/program_test.go b/core/vm/program/program_test.go new file mode 100644 index 0000000000..0b34210067 --- /dev/null +++ b/core/vm/program/program_test.go @@ -0,0 +1,311 @@ +// Copyright 2024 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the goevmlab library. If not, see . + +package program + +import ( + "bytes" + "math/big" + "testing" + + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/core/vm" + "github.com/holiman/uint256" +) + +func TestPush(t *testing.T) { + tests := []struct { + input interface{} + expected string + }{ + // native ints + {0, "6000"}, + {0xfff, "610fff"}, + {nil, "6000"}, + {uint8(1), "6001"}, + {uint16(1), "6001"}, + {uint32(1), "6001"}, + {uint64(1), "6001"}, + // bigints + {big.NewInt(0), "6000"}, + {big.NewInt(1), "6001"}, + {big.NewInt(0xfff), "610fff"}, + // uint256 + {uint256.NewInt(1), "6001"}, + {uint256.Int{1, 0, 0, 0}, "6001"}, + // Addresses + {common.HexToAddress("0xdeadbeefdeadbeefdeadbeefdeadbeefdeadbeef"), "73deadbeefdeadbeefdeadbeefdeadbeefdeadbeef"}, + {&common.Address{}, "6000"}, + } + for i, tc := range tests { + have := New().Push(tc.input).Hex() + if have != tc.expected { + t.Errorf("test %d: got %v expected %v", i, have, tc.expected) + } + } +} + +func TestCall(t *testing.T) { + { // Nil gas + have := New().Call(nil, common.HexToAddress("0x1337"), big.NewInt(1), 1, 2, 3, 4).Hex() + want := "600460036002600160016113375af1" + if have != want { + t.Errorf("have %v want %v", have, want) + } + } + { // Non nil gas + have := New().Call(uint256.NewInt(0xffff), common.HexToAddress("0x1337"), big.NewInt(1), 1, 2, 3, 4).Hex() + want := "6004600360026001600161133761fffff1" + if have != want { + t.Errorf("have %v want %v", have, want) + } + } +} + +func TestMstore(t *testing.T) { + { + have := New().Mstore(common.FromHex("0xaabb"), 0).Hex() + want := "60aa60005360bb600153" + if have != want { + t.Errorf("have %v want %v", have, want) + } + } + { // store at offset + have := New().Mstore(common.FromHex("0xaabb"), 3).Hex() + want := "60aa60035360bb600453" + if have != want { + t.Errorf("have %v want %v", have, want) + } + } + { // 34 bytes + data := common.FromHex("0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" + + "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" + + "FFFF") + + have := New().Mstore(data, 0).Hex() + want := "7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff60005260ff60205360ff602153" + if have != want { + t.Errorf("have %v want %v", have, want) + } + } +} + +func TestMemToStorage(t *testing.T) { + have := New().MemToStorage(0, 33, 1).Hex() + want := "600051600155602051600255" + if have != want { + t.Errorf("have %v want %v", have, want) + } +} + +func TestSstore(t *testing.T) { + have := New().Sstore(0x1337, []byte("1234")).Hex() + want := "633132333461133755" + if have != want { + t.Errorf("have %v want %v", have, want) + } +} + +func TestReturnData(t *testing.T) { + { + have := New().ReturnData([]byte{0xFF}).Hex() + want := "60ff60005360016000f3" + if have != want { + t.Errorf("have %v want %v", have, want) + } + } + { + // 32 bytes + data := common.FromHex("0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF") + have := New().ReturnData(data).Hex() + want := "7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff60005260206000f3" + if have != want { + t.Errorf("have %v want %v", have, want) + } + } + { // ReturnViaCodeCopy + data := common.FromHex("0x6001") + have := New().Append([]byte{0x5b, 0x5b, 0x5b}).ReturnViaCodeCopy(data).Hex() + want := "5b5b5b600261001060003960026000f36001" + if have != want { + t.Errorf("have %v want %v", have, want) + } + } + { // ReturnViaCodeCopy larger code + data := common.FromHex("7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff60005260206000f3") + have := New().Append([]byte{0x5b, 0x5b, 0x5b}).ReturnViaCodeCopy(data).Hex() + want := "5b5b5b602961001060003960296000f37fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff60005260206000f3" + if have != want { + t.Errorf("have %v want %v", have, want) + } + } +} + +func TestCreateAndCall(t *testing.T) { + // A constructor that stores a slot + ctor := New().Sstore(0, big.NewInt(5)) + + // A runtime bytecode which reads the slot and returns + deployed := New() + deployed.Push(0).Op(vm.SLOAD) // [value] in stack + deployed.Push(0) // [value, 0] + deployed.Op(vm.MSTORE) + deployed.Return(0, 32) + + // Pack them + ctor.ReturnData(deployed.Bytes()) + // Verify constructor + runtime code + { + want := "6005600055606060005360006001536054600253606060035360006004536052600553606060065360206007536060600853600060095360f3600a53600b6000f3" + if got := ctor.Hex(); got != want { + t.Fatalf("1: got %v expected %v", got, want) + } + } +} + +func TestCreate2Call(t *testing.T) { + // Some runtime code + runtime := New().Op(vm.ADDRESS, vm.SELFDESTRUCT).Bytes() + want := common.FromHex("0x30ff") + if !bytes.Equal(want, runtime) { + t.Fatalf("runtime code error\nwant: %x\nhave: %x\n", want, runtime) + } + // A constructor returning the runtime code + initcode := New().ReturnData(runtime).Bytes() + want = common.FromHex("603060005360ff60015360026000f3") + if !bytes.Equal(want, initcode) { + t.Fatalf("initcode error\nwant: %x\nhave: %x\n", want, initcode) + } + // A factory invoking the constructor + outer := New().Create2ThenCall(initcode, nil).Bytes() + want = common.FromHex("60606000536030600153606060025360006003536053600453606060055360ff6006536060600753600160085360536009536060600a536002600b536060600c536000600d5360f3600e536000600f60006000f560006000600060006000855af15050") + if !bytes.Equal(want, outer) { + t.Fatalf("factory error\nwant: %x\nhave: %x\n", want, outer) + } +} + +func TestGenerator(t *testing.T) { + for i, tc := range []struct { + want []byte + haveFn func() []byte + }{ + { // CREATE + want: []byte{ + // Store initcode in memory at 0x00 (5 bytes left-padded to 32 bytes) + byte(vm.PUSH5), + // Init code: PUSH1 0, PUSH1 0, RETURN (3 steps) + byte(vm.PUSH1), 0, byte(vm.PUSH1), 0, byte(vm.RETURN), + byte(vm.PUSH1), 0, + byte(vm.MSTORE), + // length, offset, value + byte(vm.PUSH1), 5, byte(vm.PUSH1), 27, byte(vm.PUSH1), 0, + byte(vm.CREATE), + byte(vm.POP), + }, + haveFn: func() []byte { + initcode := New().Return(0, 0).Bytes() + return New().MstoreSmall(initcode, 0). + Push(len(initcode)). // length + Push(32 - len(initcode)). // offset + Push(0). // value + Op(vm.CREATE). + Op(vm.POP).Bytes() + }, + }, + { // CREATE2 + want: []byte{ + // Store initcode in memory at 0x00 (5 bytes left-padded to 32 bytes) + byte(vm.PUSH5), + // Init code: PUSH1 0, PUSH1 0, RETURN (3 steps) + byte(vm.PUSH1), 0, byte(vm.PUSH1), 0, byte(vm.RETURN), + byte(vm.PUSH1), 0, + byte(vm.MSTORE), + // salt, length, offset, value + byte(vm.PUSH1), 1, byte(vm.PUSH1), 5, byte(vm.PUSH1), 27, byte(vm.PUSH1), 0, + byte(vm.CREATE2), + byte(vm.POP), + }, + haveFn: func() []byte { + initcode := New().Return(0, 0).Bytes() + return New().MstoreSmall(initcode, 0). + Push(1). // salt + Push(len(initcode)). // length + Push(32 - len(initcode)). // offset + Push(0). // value + Op(vm.CREATE2). + Op(vm.POP).Bytes() + }, + }, + { // CALL + want: []byte{ + // outsize, outoffset, insize, inoffset + byte(vm.PUSH1), 0, byte(vm.DUP1), byte(vm.DUP1), byte(vm.DUP1), + byte(vm.DUP1), // value + byte(vm.PUSH1), 0xbb, //address + byte(vm.GAS), // gas + byte(vm.CALL), + byte(vm.POP), + }, + haveFn: func() []byte { + return New().Call(nil, 0xbb, 0, 0, 0, 0, 0).Op(vm.POP).Bytes() + }, + }, + { // CALLCODE + want: []byte{ + // outsize, outoffset, insize, inoffset + byte(vm.PUSH1), 0, byte(vm.DUP1), byte(vm.DUP1), byte(vm.DUP1), + byte(vm.PUSH1), 0, // value + byte(vm.PUSH1), 0xcc, //address + byte(vm.GAS), // gas + byte(vm.CALLCODE), + byte(vm.POP), + }, + haveFn: func() []byte { + return New().CallCode(nil, 0xcc, 0, 0, 0, 0, 0).Op(vm.POP).Bytes() + }, + }, + { // STATICCALL + want: []byte{ + // outsize, outoffset, insize, inoffset + byte(vm.PUSH1), 0, byte(vm.DUP1), byte(vm.DUP1), byte(vm.DUP1), + byte(vm.PUSH1), 0xdd, //address + byte(vm.GAS), // gas + byte(vm.STATICCALL), + byte(vm.POP), + }, + haveFn: func() []byte { + return New().StaticCall(nil, 0xdd, 0, 0, 0, 0).Op(vm.POP).Bytes() + }, + }, + { // DELEGATECALL + want: []byte{ + // outsize, outoffset, insize, inoffset + byte(vm.PUSH1), 0, byte(vm.DUP1), byte(vm.DUP1), byte(vm.DUP1), + byte(vm.PUSH1), 0xee, //address + byte(vm.GAS), // gas + byte(vm.DELEGATECALL), + byte(vm.POP), + }, + haveFn: func() []byte { + return New().DelegateCall(nil, 0xee, 0, 0, 0, 0).Op(vm.POP).Bytes() + }, + }, + } { + if have := tc.haveFn(); !bytes.Equal(have, tc.want) { + t.Fatalf("test %d error\nhave: %x\nwant: %x\n", i, have, tc.want) + } + } +} diff --git a/core/vm/program/readme.md b/core/vm/program/readme.md new file mode 100644 index 0000000000..0e4a54d8f1 --- /dev/null +++ b/core/vm/program/readme.md @@ -0,0 +1,30 @@ +### What is this + +In many cases, we have a need to create somewhat nontrivial bytecode, for testing various +quirks related to state transition or evm execution. + +For example, we want to have a `CREATE2`- op create a contract, which is then invoked, and when invoked does a selfdestruct-to-self. + +It is overkill to go full solidity, but it is also a bit tricky do assemble this by concatenating bytes. + +This utility takes an approach from [goevmlab](https://github.com/holiman/goevmlab/) where it has been used for several years, +a go-lang utility to assemble evm bytecode. + +Using this utility, the case above can be expressed as: +```golang + // Some runtime code + runtime := program.New().Ops(vm.ADDRESS, vm.SELFDESTRUCT).Bytecode() + // A constructor returning the runtime code + initcode := program.New().ReturnData(runtime).Bytecode() + // A factory invoking the constructor + outer := program.New().Create2AndCall(initcode, nil).Bytecode() +``` + +### Warning + +This package is a utility for testing, _not_ for production. As such: + +- There are not package guarantees. We might iterate heavily on this package, and do backwards-incompatible changes without warning +- There are no quality-guarantees. These utilities may produce evm-code that is non-functional. YMMV. +- There are no stability-guarantees. The utility will `panic` if the inputs do not align / make sense. + diff --git a/core/vm/runtime/env.go b/core/vm/runtime/env.go index 34335b8e9e..e54041f7e2 100644 --- a/core/vm/runtime/env.go +++ b/core/vm/runtime/env.go @@ -42,5 +42,7 @@ func NewEnv(cfg *Config) *vm.EVM { Random: cfg.Random, } - return vm.NewEVM(blockContext, txContext, cfg.State, cfg.ChainConfig, cfg.EVMConfig) + evm := vm.NewEVM(blockContext, cfg.State, cfg.ChainConfig, cfg.EVMConfig) + evm.SetTxContext(txContext) + return evm } diff --git a/core/vm/runtime/runtime_test.go b/core/vm/runtime/runtime_test.go index 97234368ee..0e774a01c2 100644 --- a/core/vm/runtime/runtime_test.go +++ b/core/vm/runtime/runtime_test.go @@ -31,8 +31,10 @@ import ( "github.com/ethereum/go-ethereum/core" "github.com/ethereum/go-ethereum/core/asm" "github.com/ethereum/go-ethereum/core/state" + "github.com/ethereum/go-ethereum/core/tracing" "github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/core/vm" + "github.com/ethereum/go-ethereum/core/vm/program" "github.com/ethereum/go-ethereum/eth/tracers" "github.com/ethereum/go-ethereum/eth/tracers/logger" "github.com/ethereum/go-ethereum/params" @@ -436,110 +438,46 @@ func benchmarkNonModifyingCode(gas uint64, code []byte, name string, tracerCode // BenchmarkSimpleLoop test a pretty simple loop which loops until OOG // 55 ms func BenchmarkSimpleLoop(b *testing.B) { - staticCallIdentity := []byte{ - byte(vm.JUMPDEST), // [ count ] - // push args for the call - byte(vm.PUSH1), 0, // out size - byte(vm.DUP1), // out offset - byte(vm.DUP1), // out insize - byte(vm.DUP1), // in offset - byte(vm.PUSH1), 0x4, // address of identity - byte(vm.GAS), // gas - byte(vm.STATICCALL), - byte(vm.POP), // pop return value - byte(vm.PUSH1), 0, // jumpdestination - byte(vm.JUMP), - } - - callIdentity := []byte{ - byte(vm.JUMPDEST), // [ count ] - // push args for the call - byte(vm.PUSH1), 0, // out size - byte(vm.DUP1), // out offset - byte(vm.DUP1), // out insize - byte(vm.DUP1), // in offset - byte(vm.DUP1), // value - byte(vm.PUSH1), 0x4, // address of identity - byte(vm.GAS), // gas - byte(vm.CALL), - byte(vm.POP), // pop return value - byte(vm.PUSH1), 0, // jumpdestination - byte(vm.JUMP), - } - - callInexistant := []byte{ - byte(vm.JUMPDEST), // [ count ] - // push args for the call - byte(vm.PUSH1), 0, // out size - byte(vm.DUP1), // out offset - byte(vm.DUP1), // out insize - byte(vm.DUP1), // in offset - byte(vm.DUP1), // value - byte(vm.PUSH1), 0xff, // address of existing contract - byte(vm.GAS), // gas - byte(vm.CALL), - byte(vm.POP), // pop return value - byte(vm.PUSH1), 0, // jumpdestination - byte(vm.JUMP), - } - - callEOA := []byte{ - byte(vm.JUMPDEST), // [ count ] - // push args for the call - byte(vm.PUSH1), 0, // out size - byte(vm.DUP1), // out offset - byte(vm.DUP1), // out insize - byte(vm.DUP1), // in offset - byte(vm.DUP1), // value - byte(vm.PUSH1), 0xE0, // address of EOA - byte(vm.GAS), // gas - byte(vm.CALL), - byte(vm.POP), // pop return value - byte(vm.PUSH1), 0, // jumpdestination - byte(vm.JUMP), - } - - loopingCode := []byte{ - byte(vm.JUMPDEST), // [ count ] - // push args for the call - byte(vm.PUSH1), 0, // out size - byte(vm.DUP1), // out offset - byte(vm.DUP1), // out insize - byte(vm.DUP1), // in offset - byte(vm.PUSH1), 0x4, // address of identity - byte(vm.GAS), // gas - - byte(vm.POP), byte(vm.POP), byte(vm.POP), byte(vm.POP), byte(vm.POP), byte(vm.POP), - byte(vm.PUSH1), 0, // jumpdestination - byte(vm.JUMP), - } - - loopingCode2 := []byte{ - byte(vm.JUMPDEST), // [ count ] - // push args for the call - byte(vm.PUSH4), 1, 2, 3, 4, - byte(vm.PUSH5), 1, 2, 3, 4, 5, - - byte(vm.POP), byte(vm.POP), - byte(vm.PUSH6), 0, 0, 0, 0, 0, 0, // jumpdestination - byte(vm.JUMP), - } - - callRevertingContractWithInput := []byte{ - byte(vm.JUMPDEST), // - // push args for the call - byte(vm.PUSH1), 0, // out size - byte(vm.DUP1), // out offset - byte(vm.PUSH1), 0x20, // in size - byte(vm.PUSH1), 0x00, // in offset - byte(vm.PUSH1), 0x00, // value - byte(vm.PUSH1), 0xEE, // address of reverting contract - byte(vm.GAS), // gas - byte(vm.CALL), - byte(vm.POP), // pop return value - byte(vm.PUSH1), 0, // jumpdestination - byte(vm.JUMP), - } + p, lbl := program.New().Jumpdest() + // Call identity, and pop return value + staticCallIdentity := p. + StaticCall(nil, 0x4, 0, 0, 0, 0). + Op(vm.POP).Jump(lbl).Bytes() // pop return value and jump to label + + p, lbl = program.New().Jumpdest() + callIdentity := p. + Call(nil, 0x4, 0, 0, 0, 0, 0). + Op(vm.POP).Jump(lbl).Bytes() // pop return value and jump to label + + p, lbl = program.New().Jumpdest() + callInexistant := p. + Call(nil, 0xff, 0, 0, 0, 0, 0). + Op(vm.POP).Jump(lbl).Bytes() // pop return value and jump to label + + p, lbl = program.New().Jumpdest() + callEOA := p. + Call(nil, 0xE0, 0, 0, 0, 0, 0). // call addr of EOA + Op(vm.POP).Jump(lbl).Bytes() // pop return value and jump to label + + p, lbl = program.New().Jumpdest() + // Push as if we were making call, then pop it off again, and loop + loopingCode := p.Push(0). + Op(vm.DUP1, vm.DUP1, vm.DUP1). + Push(0x4). + Op(vm.GAS, vm.POP, vm.POP, vm.POP, vm.POP, vm.POP, vm.POP). + Jump(lbl).Bytes() + + p, lbl = program.New().Jumpdest() + loopingCode2 := p. + Push(0x01020304).Push(uint64(0x0102030405)). + Op(vm.POP, vm.POP). + Op(vm.PUSH6).Append(make([]byte, 6)).Op(vm.JUMP). // Jumpdest zero expressed in 6 bytes + Jump(lbl).Bytes() + + p, lbl = program.New().Jumpdest() + callRevertingContractWithInput := p. + Call(nil, 0xee, 0, 0, 0x20, 0x0, 0x0). + Op(vm.POP).Jump(lbl).Bytes() // pop return value and jump to label //tracer := logger.NewJSONLogger(nil, os.Stdout) //Execute(loopingCode, nil, &Config{ @@ -733,17 +671,23 @@ func TestColdAccountAccessCost(t *testing.T) { want: 7600, }, } { - tracer := logger.NewStructLogger(nil) + var step = 0 + var have = uint64(0) Execute(tc.code, nil, &Config{ EVMConfig: vm.Config{ - Tracer: tracer.Hooks(), + Tracer: &tracing.Hooks{ + OnOpcode: func(pc uint64, op byte, gas, cost uint64, scope tracing.OpContext, rData []byte, depth int, err error) { + // Uncomment to investigate failures: + //t.Logf("%d: %v %d", step, vm.OpCode(op).String(), cost) + if step == tc.step { + have = cost + } + step++ + }, + }, }, }) - have := tracer.StructLogs()[tc.step].GasCost if want := tc.want; have != want { - for ii, op := range tracer.StructLogs() { - t.Logf("%d: %v %d", ii, op.OpName(), op.GasCost) - } t.Fatalf("testcase %d, gas report wrong, step %d, have %d want %d", i, tc.step, have, want) } } @@ -778,104 +722,49 @@ func TestRuntimeJSTracer(t *testing.T) { this.exits++; this.gasUsed = res.getGasUsed(); }}`} + initcode := program.New().Return(0, 0).Bytes() tests := []struct { code []byte // One result per tracer results []string }{ - { - // CREATE - code: []byte{ - // Store initcode in memory at 0x00 (5 bytes left-padded to 32 bytes) - byte(vm.PUSH5), - // Init code: PUSH1 0, PUSH1 0, RETURN (3 steps) - byte(vm.PUSH1), 0, byte(vm.PUSH1), 0, byte(vm.RETURN), - byte(vm.PUSH1), 0, - byte(vm.MSTORE), - // length, offset, value - byte(vm.PUSH1), 5, byte(vm.PUSH1), 27, byte(vm.PUSH1), 0, - byte(vm.CREATE), - byte(vm.POP), - }, + { // CREATE + code: program.New().MstoreSmall(initcode, 0). + Push(len(initcode)). // length + Push(32 - len(initcode)). // offset + Push(0). // value + Op(vm.CREATE). + Op(vm.POP).Bytes(), results: []string{`"1,1,952853,6,12"`, `"1,1,952853,6,0"`}, }, - { - // CREATE2 - code: []byte{ - // Store initcode in memory at 0x00 (5 bytes left-padded to 32 bytes) - byte(vm.PUSH5), - // Init code: PUSH1 0, PUSH1 0, RETURN (3 steps) - byte(vm.PUSH1), 0, byte(vm.PUSH1), 0, byte(vm.RETURN), - byte(vm.PUSH1), 0, - byte(vm.MSTORE), - // salt, length, offset, value - byte(vm.PUSH1), 1, byte(vm.PUSH1), 5, byte(vm.PUSH1), 27, byte(vm.PUSH1), 0, - byte(vm.CREATE2), - byte(vm.POP), - }, + { // CREATE2 + code: program.New().MstoreSmall(initcode, 0). + Push(1). // salt + Push(len(initcode)). // length + Push(32 - len(initcode)). // offset + Push(0). // value + Op(vm.CREATE2). + Op(vm.POP).Bytes(), results: []string{`"1,1,952844,6,13"`, `"1,1,952844,6,0"`}, }, - { - // CALL - code: []byte{ - // outsize, outoffset, insize, inoffset - byte(vm.PUSH1), 0, byte(vm.PUSH1), 0, byte(vm.PUSH1), 0, byte(vm.PUSH1), 0, - byte(vm.PUSH1), 0, // value - byte(vm.PUSH1), 0xbb, //address - byte(vm.GAS), // gas - byte(vm.CALL), - byte(vm.POP), - }, + { // CALL + code: program.New().Call(nil, 0xbb, 0, 0, 0, 0, 0).Op(vm.POP).Bytes(), results: []string{`"1,1,981796,6,13"`, `"1,1,981796,6,0"`}, }, - { - // CALLCODE - code: []byte{ - // outsize, outoffset, insize, inoffset - byte(vm.PUSH1), 0, byte(vm.PUSH1), 0, byte(vm.PUSH1), 0, byte(vm.PUSH1), 0, - byte(vm.PUSH1), 0, // value - byte(vm.PUSH1), 0xcc, //address - byte(vm.GAS), // gas - byte(vm.CALLCODE), - byte(vm.POP), - }, + { // CALLCODE + code: program.New().CallCode(nil, 0xcc, 0, 0, 0, 0, 0).Op(vm.POP).Bytes(), results: []string{`"1,1,981796,6,13"`, `"1,1,981796,6,0"`}, }, - { - // STATICCALL - code: []byte{ - // outsize, outoffset, insize, inoffset - byte(vm.PUSH1), 0, byte(vm.PUSH1), 0, byte(vm.PUSH1), 0, byte(vm.PUSH1), 0, - byte(vm.PUSH1), 0xdd, //address - byte(vm.GAS), // gas - byte(vm.STATICCALL), - byte(vm.POP), - }, + { // STATICCALL + code: program.New().StaticCall(nil, 0xdd, 0, 0, 0, 0).Op(vm.POP).Bytes(), results: []string{`"1,1,981799,6,12"`, `"1,1,981799,6,0"`}, }, - { - // DELEGATECALL - code: []byte{ - // outsize, outoffset, insize, inoffset - byte(vm.PUSH1), 0, byte(vm.PUSH1), 0, byte(vm.PUSH1), 0, byte(vm.PUSH1), 0, - byte(vm.PUSH1), 0xee, //address - byte(vm.GAS), // gas - byte(vm.DELEGATECALL), - byte(vm.POP), - }, + { // DELEGATECALL + code: program.New().DelegateCall(nil, 0xee, 0, 0, 0, 0).Op(vm.POP).Bytes(), results: []string{`"1,1,981799,6,12"`, `"1,1,981799,6,0"`}, }, - { - // CALL self-destructing contract - code: []byte{ - // outsize, outoffset, insize, inoffset - byte(vm.PUSH1), 0, byte(vm.PUSH1), 0, byte(vm.PUSH1), 0, byte(vm.PUSH1), 0, - byte(vm.PUSH1), 0, // value - byte(vm.PUSH1), 0xff, //address - byte(vm.GAS), // gas - byte(vm.CALL), - byte(vm.POP), - }, + { // CALL self-destructing contract + code: program.New().Call(nil, 0xff, 0, 0, 0, 0, 0).Op(vm.POP).Bytes(), results: []string{`"2,2,0,5003,12"`, `"2,2,0,5003,0"`}, }, } @@ -958,16 +847,8 @@ func TestJSTracerCreateTx(t *testing.T) { func BenchmarkTracerStepVsCallFrame(b *testing.B) { // Simply pushes and pops some values in a loop - code := []byte{ - byte(vm.JUMPDEST), - byte(vm.PUSH1), 0, - byte(vm.PUSH1), 0, - byte(vm.POP), - byte(vm.POP), - byte(vm.PUSH1), 0, // jumpdestination - byte(vm.JUMP), - } - + p, lbl := program.New().Jumpdest() + code := p.Push(0).Push(0).Op(vm.POP, vm.POP).Jump(lbl).Bytes() stepTracer := ` { step: function() {}, diff --git a/eth/api_backend.go b/eth/api_backend.go index 0048e2f16c..a114decf7d 100644 --- a/eth/api_backend.go +++ b/eth/api_backend.go @@ -255,18 +255,17 @@ func (b *EthAPIBackend) GetTd(ctx context.Context, hash common.Hash) *big.Int { return nil } -func (b *EthAPIBackend) GetEVM(ctx context.Context, msg *core.Message, state *state.StateDB, header *types.Header, vmConfig *vm.Config, blockCtx *vm.BlockContext) *vm.EVM { +func (b *EthAPIBackend) GetEVM(ctx context.Context, state *state.StateDB, header *types.Header, vmConfig *vm.Config, blockCtx *vm.BlockContext) *vm.EVM { if vmConfig == nil { vmConfig = b.eth.blockchain.GetVMConfig() } - txContext := core.NewEVMTxContext(msg) var context vm.BlockContext if blockCtx != nil { context = *blockCtx } else { context = core.NewEVMBlockContext(header, b.eth.BlockChain(), nil) } - return vm.NewEVM(context, txContext, state, b.ChainConfig(), *vmConfig) + return vm.NewEVM(context, state, b.ChainConfig(), *vmConfig) } func (b *EthAPIBackend) SubscribeRemovedLogsEvent(ch chan<- core.RemovedLogsEvent) event.Subscription { diff --git a/eth/catalyst/api.go b/eth/catalyst/api.go index 1adcec601e..05b6c27959 100644 --- a/eth/catalyst/api.go +++ b/eth/catalyst/api.go @@ -31,8 +31,9 @@ import ( "github.com/ethereum/go-ethereum/core/rawdb" "github.com/ethereum/go-ethereum/core/stateless" "github.com/ethereum/go-ethereum/core/types" + "github.com/ethereum/go-ethereum/core/vm" "github.com/ethereum/go-ethereum/eth" - "github.com/ethereum/go-ethereum/eth/downloader" + "github.com/ethereum/go-ethereum/eth/ethconfig" "github.com/ethereum/go-ethereum/internal/version" "github.com/ethereum/go-ethereum/log" "github.com/ethereum/go-ethereum/miner" @@ -917,7 +918,7 @@ func (api *ConsensusAPI) newPayload(params engine.ExecutableData, versionedHashe // tries to make it import a block. That should be denied as pushing something // into the database directly will conflict with the assumptions of snap sync // that it has an empty db that it can fill itself. - if api.eth.SyncMode() != downloader.FullSync { + if api.eth.SyncMode() != ethconfig.FullSync { return api.delayPayloadImport(block), nil } if !api.eth.BlockChain().HasBlockAndState(block.ParentHash(), block.NumberU64()-1) { @@ -995,7 +996,7 @@ func (api *ConsensusAPI) executeStatelessPayload(params engine.ExecutableData, v api.lastNewPayloadLock.Unlock() log.Trace("Executing block statelessly", "number", block.Number(), "hash", params.BlockHash) - stateRoot, receiptRoot, err := core.ExecuteStateless(api.eth.BlockChain().Config(), block, witness) + stateRoot, receiptRoot, err := core.ExecuteStateless(api.eth.BlockChain().Config(), vm.Config{}, block, witness) if err != nil { log.Warn("ExecuteStatelessPayload: execution failed", "err", err) errorMsg := err.Error() @@ -1031,7 +1032,7 @@ func (api *ConsensusAPI) delayPayloadImport(block *types.Block) engine.PayloadSt // payload as non-integratable on top of the existing sync. We'll just // have to rely on the beacon client to forcefully update the head with // a forkchoice update request. - if api.eth.SyncMode() == downloader.FullSync { + if api.eth.SyncMode() == ethconfig.FullSync { // In full sync mode, failure to import a well-formed block can only mean // that the parent state is missing and the syncer rejected extending the // current cycle with the new payload. diff --git a/eth/catalyst/simulated_beacon_test.go b/eth/catalyst/simulated_beacon_test.go index 7e9fd7b324..c0f1136553 100644 --- a/eth/catalyst/simulated_beacon_test.go +++ b/eth/catalyst/simulated_beacon_test.go @@ -27,9 +27,8 @@ import ( "github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/crypto" "github.com/ethereum/go-ethereum/eth" - "github.com/ethereum/go-ethereum/eth/downloader" "github.com/ethereum/go-ethereum/eth/ethconfig" - "github.com/ethereum/go-ethereum/miner" + "github.com/ethereum/go-ethereum/miner/minerconfig" "github.com/ethereum/go-ethereum/node" "github.com/ethereum/go-ethereum/p2p" "github.com/ethereum/go-ethereum/params" @@ -49,7 +48,7 @@ func startSimulatedBeaconEthService(t *testing.T, genesis *core.Genesis, period t.Fatal("can't create node:", err) } - ethcfg := ðconfig.Config{Genesis: genesis, SyncMode: downloader.FullSync, TrieTimeout: time.Minute, TrieDirtyCache: 256, TrieCleanCache: 256, Miner: miner.DefaultConfig} + ethcfg := ðconfig.Config{Genesis: genesis, SyncMode: ethconfig.FullSync, TrieTimeout: time.Minute, TrieDirtyCache: 256, TrieCleanCache: 256, Miner: minerconfig.DefaultConfig} ethservice, err := eth.New(n, ethcfg) if err != nil { t.Fatal("can't create eth service:", err) diff --git a/eth/downloader/downloader.go b/eth/downloader/downloader.go index 949149355f..788474cb49 100644 --- a/eth/downloader/downloader.go +++ b/eth/downloader/downloader.go @@ -31,6 +31,7 @@ import ( "github.com/ethereum/go-ethereum/core/rawdb" "github.com/ethereum/go-ethereum/core/state/snapshot" "github.com/ethereum/go-ethereum/core/types" + "github.com/ethereum/go-ethereum/eth/ethconfig" "github.com/ethereum/go-ethereum/eth/protocols/snap" "github.com/ethereum/go-ethereum/ethdb" "github.com/ethereum/go-ethereum/event" @@ -81,6 +82,17 @@ var ( errNoAncestorFound = errors.New("no common ancestor found") ) +// SyncMode defines the sync method of the downloader. +// Deprecated: use ethconfig.SyncMode instead +type SyncMode = ethconfig.SyncMode + +const ( + // Deprecated: use ethconfig.FullSync + FullSync = ethconfig.FullSync + // Deprecated: use ethconfig.SnapSync + SnapSync = ethconfig.SnapSync +) + // peerDropFn is a callback type for dropping a peer detected as malicious. type peerDropFn func(id string) @@ -243,9 +255,9 @@ func (d *Downloader) Progress() ethereum.SyncProgress { current := uint64(0) mode := d.getMode() switch mode { - case FullSync: + case ethconfig.FullSync: current = d.blockchain.CurrentBlock().Number.Uint64() - case SnapSync: + case ethconfig.SnapSync: current = d.blockchain.CurrentSnapBlock().Number.Uint64() default: log.Error("Unknown downloader mode", "mode", mode) @@ -369,7 +381,7 @@ func (d *Downloader) synchronise(id string, hash common.Hash, td, ttd *big.Int, if d.notified.CompareAndSwap(false, true) { log.Info("Block synchronisation started") } - if mode == SnapSync { + if mode == ethconfig.SnapSync { // Snap sync will directly modify the persistent state, making the entire // trie database unusable until the state is fully synced. To prevent any // subsequent state reads, explicitly disable the trie database and state @@ -465,7 +477,7 @@ func (d *Downloader) syncWithPeer(p *peerConnection, hash common.Hash, td, ttd * // threshold (i.e. new chain). In that case we won't really snap sync // anyway, but still need a valid pivot block to avoid some code hitting // nil panics on access. - if mode == SnapSync && pivot == nil { + if mode == ethconfig.SnapSync && pivot == nil { pivot = d.blockchain.CurrentBlock() } @@ -502,7 +514,7 @@ func (d *Downloader) syncWithPeer(p *peerConnection, hash common.Hash, td, ttd * d.syncStatsLock.Unlock() // Ensure our origin point is below any snap sync pivot point - if mode == SnapSync { + if mode == ethconfig.SnapSync { if remoteHeight <= uint64(fsMinFullBlocks) { origin = 0 } else { @@ -516,10 +528,10 @@ func (d *Downloader) syncWithPeer(p *peerConnection, hash common.Hash, td, ttd * } } d.committed.Store(true) - if mode == SnapSync && pivot.Number.Uint64() != 0 { + if mode == ethconfig.SnapSync && pivot.Number.Uint64() != 0 { d.committed.Store(false) } - if mode == SnapSync { + if mode == ethconfig.SnapSync { // Set the ancient data limitation. If we are running snap sync, all block // data older than ancientLimit will be written to the ancient store. More // recent data will be written to the active database and will wait for the @@ -573,13 +585,13 @@ func (d *Downloader) syncWithPeer(p *peerConnection, hash common.Hash, td, ttd * func() error { return d.fetchReceipts(origin+1, beaconMode) }, // Receipts are retrieved during snap sync func() error { return d.processHeaders(origin+1, td, ttd, beaconMode) }, } - if mode == SnapSync { + if mode == ethconfig.SnapSync { d.pivotLock.Lock() d.pivotHeader = pivot d.pivotLock.Unlock() fetchers = append(fetchers, func() error { return d.processSnapSyncContent() }) - } else if mode == FullSync { + } else if mode == ethconfig.FullSync { fetchers = append(fetchers, func() error { return d.processFullSyncContent(ttd, beaconMode) }) } // update the chasing head @@ -1271,7 +1283,7 @@ func (d *Downloader) processHeaders(origin uint64, td, ttd *big.Int, beaconMode chunkHashes := hashes[:limit] // In case of header only syncing, validate the chunk immediately - if mode == SnapSync { + if mode == ethconfig.SnapSync { if len(chunkHeaders) > 0 { if n, err := d.blockchain.InsertHeaderChain(chunkHeaders); err != nil { log.Warn("Invalid header encountered", "number", chunkHeaders[n].Number, "hash", chunkHashes[n], "parent", chunkHeaders[n].ParentHash, "err", err) diff --git a/eth/downloader/queue.go b/eth/downloader/queue.go index 8ff5e0e60e..706d01a323 100644 --- a/eth/downloader/queue.go +++ b/eth/downloader/queue.go @@ -30,6 +30,7 @@ import ( "github.com/ethereum/go-ethereum/common/prque" "github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/crypto/kzg4844" + "github.com/ethereum/go-ethereum/eth/ethconfig" "github.com/ethereum/go-ethereum/log" "github.com/ethereum/go-ethereum/metrics" "github.com/ethereum/go-ethereum/params" @@ -183,7 +184,7 @@ func (q *queue) Reset(blockCacheLimit int, thresholdInitialSize int) { defer q.lock.Unlock() q.closed = false - q.mode = FullSync + q.mode = ethconfig.FullSync q.headerHead = common.Hash{} q.headerPendPool = make(map[string]*fetchRequest) @@ -331,7 +332,7 @@ func (q *queue) Schedule(headers []*types.Header, hashes []common.Hash, from uin q.blockTaskQueue.Push(header, -int64(header.Number.Uint64())) } // Queue for receipt retrieval - if q.mode == SnapSync && !header.EmptyReceipts() { + if q.mode == ethconfig.SnapSync && !header.EmptyReceipts() { if _, ok := q.receiptTaskPool[hash]; ok { log.Warn("Header already scheduled for receipt fetch", "number", header.Number, "hash", hash) } else { @@ -526,7 +527,7 @@ func (q *queue) reserveHeaders(p *peerConnection, count int, taskPool map[common // we can ask the resultcache if this header is within the // "prioritized" segment of blocks. If it is not, we need to throttle - stale, throttle, item, err := q.resultCache.AddFetch(header, q.mode == SnapSync, p.id) + stale, throttle, item, err := q.resultCache.AddFetch(header, q.mode == ethconfig.SnapSync, p.id) if stale { // Don't put back in the task queue, this item has already been // delivered upstream @@ -895,7 +896,7 @@ func (q *queue) DeliverReceipts(id string, receiptList [][]*types.Receipt, recei // to access the queue, so they already need a lock anyway. func (q *queue) deliver(id string, taskPool map[common.Hash]*types.Header, taskQueue *prque.Prque[int64, *types.Header], pendPool map[string]*fetchRequest, - reqTimer metrics.Timer, resInMeter metrics.Meter, resDropMeter metrics.Meter, + reqTimer *metrics.Timer, resInMeter, resDropMeter *metrics.Meter, results int, validate func(index int, header *types.Header) error, reconstruct func(index int, result *fetchResult)) (int, error) { // Short circuit if the data was never requested diff --git a/eth/ethconfig/config.go b/eth/ethconfig/config.go index 194ba1041f..d41499c6a8 100644 --- a/eth/ethconfig/config.go +++ b/eth/ethconfig/config.go @@ -30,11 +30,11 @@ import ( "github.com/ethereum/go-ethereum/core" "github.com/ethereum/go-ethereum/core/txpool/blobpool" "github.com/ethereum/go-ethereum/core/txpool/legacypool" - "github.com/ethereum/go-ethereum/eth/downloader" "github.com/ethereum/go-ethereum/eth/gasprice" "github.com/ethereum/go-ethereum/ethdb" "github.com/ethereum/go-ethereum/internal/ethapi" - "github.com/ethereum/go-ethereum/miner" + "github.com/ethereum/go-ethereum/log" + "github.com/ethereum/go-ethereum/miner/minerconfig" "github.com/ethereum/go-ethereum/params" ) @@ -49,7 +49,7 @@ var FullNodeGPO = gasprice.Config{ // Defaults contains default settings for use on the BSC main net. var Defaults = Config{ - SyncMode: downloader.SnapSync, + SyncMode: SnapSync, NetworkId: 0, // enable auto configuration of networkID == chainID TxLookupLimit: 2350000, TransactionHistory: 2350000, @@ -63,7 +63,7 @@ var Defaults = Config{ SnapshotCache: 102, DiffBlock: uint64(86400), FilterLogCacheSize: 32, - Miner: miner.DefaultConfig, + Miner: minerconfig.DefaultConfig, TxPool: legacypool.DefaultConfig, BlobPool: blobpool.DefaultConfig, RPCGasCap: 50000000, @@ -84,7 +84,7 @@ type Config struct { // Network ID separates blockchains on the peer-to-peer networking level. When left // zero, the chain ID is used as network ID. NetworkId uint64 - SyncMode downloader.SyncMode + SyncMode SyncMode // DisablePeerTxBroadcast is an optional config and disabled by default, and usually you do not need it. // When this flag is enabled, you are requesting remote peers to stop broadcasting new transactions to you, and @@ -156,7 +156,7 @@ type Config struct { FilterLogCacheSize int // Mining options - Miner miner.Config + Miner minerconfig.Config // Transaction pool options TxPool legacypool.Config @@ -206,7 +206,8 @@ func CreateConsensusEngine(config *params.ChainConfig, db ethdb.Database, ee *et return parlia.New(config, db, ee, genesisHash), nil } if config.TerminalTotalDifficulty == nil { - return nil, fmt.Errorf("only PoS networks are supported, please transition old ones with Geth v1.13.x") + log.Error("Geth only supports PoS networks. Please transition legacy networks using Geth v1.13.x.") + return nil, fmt.Errorf("'terminalTotalDifficulty' is not set in genesis block") } // If proof-of-authority is requested, set it up if config.Clique != nil { diff --git a/eth/ethconfig/gen_config.go b/eth/ethconfig/gen_config.go index 0c5d629282..5448993c58 100644 --- a/eth/ethconfig/gen_config.go +++ b/eth/ethconfig/gen_config.go @@ -9,9 +9,8 @@ import ( "github.com/ethereum/go-ethereum/core" "github.com/ethereum/go-ethereum/core/txpool/blobpool" "github.com/ethereum/go-ethereum/core/txpool/legacypool" - "github.com/ethereum/go-ethereum/eth/downloader" "github.com/ethereum/go-ethereum/eth/gasprice" - "github.com/ethereum/go-ethereum/miner" + "github.com/ethereum/go-ethereum/miner/minerconfig" ) // MarshalTOML marshals as TOML. @@ -19,7 +18,7 @@ func (c Config) MarshalTOML() (interface{}, error) { type Config struct { Genesis *core.Genesis `toml:",omitempty"` NetworkId uint64 - SyncMode downloader.SyncMode + SyncMode SyncMode DisablePeerTxBroadcast bool EthDiscoveryURLs []string SnapDiscoveryURLs []string @@ -54,7 +53,7 @@ func (c Config) MarshalTOML() (interface{}, error) { TriesVerifyMode core.VerifyMode Preimages bool FilterLogCacheSize int - Miner miner.Config + Miner minerconfig.Config TxPool legacypool.Config BlobPool blobpool.Config GPO gasprice.Config @@ -131,7 +130,7 @@ func (c *Config) UnmarshalTOML(unmarshal func(interface{}) error) error { type Config struct { Genesis *core.Genesis `toml:",omitempty"` NetworkId *uint64 - SyncMode *downloader.SyncMode + SyncMode *SyncMode DisablePeerTxBroadcast *bool EthDiscoveryURLs []string SnapDiscoveryURLs []string @@ -166,7 +165,7 @@ func (c *Config) UnmarshalTOML(unmarshal func(interface{}) error) error { TriesVerifyMode *core.VerifyMode Preimages *bool FilterLogCacheSize *int - Miner *miner.Config + Miner *minerconfig.Config TxPool *legacypool.Config BlobPool *blobpool.Config GPO *gasprice.Config diff --git a/eth/downloader/modes.go b/eth/ethconfig/syncmode.go similarity index 99% rename from eth/downloader/modes.go rename to eth/ethconfig/syncmode.go index 9d8e1f313c..af5dbbb961 100644 --- a/eth/downloader/modes.go +++ b/eth/ethconfig/syncmode.go @@ -14,7 +14,7 @@ // You should have received a copy of the GNU Lesser General Public License // along with the go-ethereum library. If not, see . -package downloader +package ethconfig import "fmt" diff --git a/eth/gasestimator/gasestimator.go b/eth/gasestimator/gasestimator.go index d43057dda2..9b24bfbf96 100644 --- a/eth/gasestimator/gasestimator.go +++ b/eth/gasestimator/gasestimator.go @@ -217,20 +217,19 @@ func execute(ctx context.Context, call *core.Message, opts *Options, gasLimit ui func run(ctx context.Context, call *core.Message, opts *Options) (*core.ExecutionResult, error) { // Assemble the call and the call context var ( - msgContext = core.NewEVMTxContext(call) evmContext = core.NewEVMBlockContext(opts.Header, opts.Chain, nil) - dirtyState = opts.State.Copy() ) // Lower the basefee to 0 to avoid breaking EVM // invariants (basefee < feecap). - if msgContext.GasPrice.Sign() == 0 { + if call.GasPrice.Sign() == 0 { evmContext.BaseFee = new(big.Int) } - if msgContext.BlobFeeCap != nil && msgContext.BlobFeeCap.BitLen() == 0 { + if call.BlobGasFeeCap != nil && call.BlobGasFeeCap.BitLen() == 0 { evmContext.BlobBaseFee = new(big.Int) } - evm := vm.NewEVM(evmContext, msgContext, dirtyState, opts.Config, vm.Config{NoBaseFee: true}) + evm := vm.NewEVM(evmContext, dirtyState, opts.Config, vm.Config{NoBaseFee: true}) + // Monitor the outer context and interrupt the EVM upon cancellation. To avoid // a dangling goroutine until the outer estimation finishes, create an internal // context for the lifetime of this method call. diff --git a/eth/handler.go b/eth/handler.go index 363db37dd6..ebab149502 100644 --- a/eth/handler.go +++ b/eth/handler.go @@ -35,6 +35,7 @@ import ( "github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/crypto" "github.com/ethereum/go-ethereum/eth/downloader" + "github.com/ethereum/go-ethereum/eth/ethconfig" "github.com/ethereum/go-ethereum/eth/fetcher" "github.com/ethereum/go-ethereum/eth/protocols/bsc" "github.com/ethereum/go-ethereum/eth/protocols/eth" @@ -118,7 +119,7 @@ type handlerConfig struct { TxPool txPool // Transaction pool to propagate from VotePool votePool Network uint64 // Network identifier to adfvertise - Sync downloader.SyncMode // Whether to snap or full sync + Sync ethconfig.SyncMode // Whether to snap or full sync BloomCache uint64 // Megabytes to alloc for snap sync bloom EventMux *event.TypeMux // Legacy event mux, deprecate for `feed` RequiredBlocks map[uint64]common.Hash // Hard coded map of required block hashes for sync challenges @@ -203,7 +204,7 @@ func newHandler(config *handlerConfig) (*handler, error) { handlerStartCh: make(chan struct{}), stopCh: make(chan struct{}), } - if config.Sync == downloader.FullSync { + if config.Sync == ethconfig.FullSync { // The database seems empty as the current block is the genesis. Yet the snap // block is ahead, so snap sync was enabled for this node at a certain point. // The scenarios where this can happen is @@ -528,7 +529,7 @@ func (h *handler) runSnapExtension(peer *snap.Peer, handler snap.Handler) error defer h.decHandlers() if err := h.peers.registerSnapExtension(peer); err != nil { - if metrics.Enabled { + if metrics.Enabled() { if peer.Inbound() { snap.IngressRegistrationErrorMeter.Mark(1) } else { @@ -552,7 +553,7 @@ func (h *handler) runTrustExtension(peer *trust.Peer, handler trust.Handler) err defer h.decHandlers() if err := h.peers.registerTrustExtension(peer); err != nil { - if metrics.Enabled { + if metrics.Enabled() { if peer.Inbound() { trust.IngressRegistrationErrorMeter.Mark(1) } else { @@ -576,7 +577,7 @@ func (h *handler) runBscExtension(peer *bsc.Peer, handler bsc.Handler) error { defer h.decHandlers() if err := h.peers.registerBscExtension(peer); err != nil { - if metrics.Enabled { + if metrics.Enabled() { if peer.Inbound() { bsc.IngressRegistrationErrorMeter.Mark(1) } else { diff --git a/eth/handler_eth_test.go b/eth/handler_eth_test.go index fab9283739..b4f1bd0706 100644 --- a/eth/handler_eth_test.go +++ b/eth/handler_eth_test.go @@ -31,7 +31,7 @@ import ( "github.com/ethereum/go-ethereum/core/rawdb" "github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/core/vm" - "github.com/ethereum/go-ethereum/eth/downloader" + "github.com/ethereum/go-ethereum/eth/ethconfig" "github.com/ethereum/go-ethereum/eth/protocols/bsc" "github.com/ethereum/go-ethereum/eth/protocols/eth" "github.com/ethereum/go-ethereum/event" @@ -128,7 +128,7 @@ func testForkIDSplit(t *testing.T, protocol uint) { TxPool: newTestTxPool(), VotePool: newTestVotePool(), Network: 1, - Sync: downloader.FullSync, + Sync: ethconfig.FullSync, BloomCache: 1, }) ethProFork, _ = newHandler(&handlerConfig{ @@ -137,7 +137,7 @@ func testForkIDSplit(t *testing.T, protocol uint) { TxPool: newTestTxPool(), VotePool: newTestVotePool(), Network: 1, - Sync: downloader.FullSync, + Sync: ethconfig.FullSync, BloomCache: 1, }) ) diff --git a/eth/handler_test.go b/eth/handler_test.go index ceda219ad6..9315877419 100644 --- a/eth/handler_test.go +++ b/eth/handler_test.go @@ -37,7 +37,7 @@ import ( "github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/core/vm" "github.com/ethereum/go-ethereum/crypto" - "github.com/ethereum/go-ethereum/eth/downloader" + "github.com/ethereum/go-ethereum/eth/ethconfig" "github.com/ethereum/go-ethereum/ethdb" "github.com/ethereum/go-ethereum/event" "github.com/ethereum/go-ethereum/params" @@ -194,7 +194,7 @@ func newTestHandlerWithBlocks(blocks int) *testHandler { TxPool: txpool, VotePool: votepool, Network: 1, - Sync: downloader.SnapSync, + Sync: ethconfig.SnapSync, BloomCache: 1, }) handler.Start(1000, 3) @@ -253,7 +253,7 @@ func (c *mockParlia) CalcDifficulty(chain consensus.ChainHeaderReader, time uint return big.NewInt(1) } -func newTestParliaHandlerAfterCancun(t *testing.T, config *params.ChainConfig, mode downloader.SyncMode, preCancunBlks, postCancunBlks uint64) *testHandler { +func newTestParliaHandlerAfterCancun(t *testing.T, config *params.ChainConfig, mode ethconfig.SyncMode, preCancunBlks, postCancunBlks uint64) *testHandler { // Have N headers in the freezer frdir := t.TempDir() db, err := rawdb.NewDatabaseWithFreezer(rawdb.NewMemoryDatabase(), frdir, "", false, false, false, false, false) diff --git a/eth/protocols/bsc/handler.go b/eth/protocols/bsc/handler.go index 989c7c71b2..04f5ff3795 100644 --- a/eth/protocols/bsc/handler.go +++ b/eth/protocols/bsc/handler.go @@ -99,7 +99,7 @@ func handleMessage(backend Backend, peer *Peer) error { var handlers = bsc1 // Track the amount of time it takes to serve the request and run the handler - if metrics.Enabled { + if metrics.Enabled() { h := fmt.Sprintf("%s/%s/%d/%#02x", p2p.HandleHistName, ProtocolName, peer.Version(), msg.Code) defer func(start time.Time) { sampler := func() metrics.Sample { diff --git a/eth/protocols/eth/handler.go b/eth/protocols/eth/handler.go index dc32559c47..28db93a209 100644 --- a/eth/protocols/eth/handler.go +++ b/eth/protocols/eth/handler.go @@ -190,7 +190,7 @@ func handleMessage(backend Backend, peer *Peer) error { var handlers = eth68 // Track the amount of time it takes to serve the request and run the handler - if metrics.Enabled { + if metrics.Enabled() { h := fmt.Sprintf("%s/%s/%d/%#02x", p2p.HandleHistName, ProtocolName, peer.Version(), msg.Code) defer func(start time.Time) { sampler := func() metrics.Sample { diff --git a/eth/protocols/eth/handshake.go b/eth/protocols/eth/handshake.go index e7d42aa0da..c4f8c754c5 100644 --- a/eth/protocols/eth/handshake.go +++ b/eth/protocols/eth/handshake.go @@ -173,7 +173,7 @@ func (p *Peer) readUpgradeStatus(status *UpgradeStatusPacket) error { // markError registers the error with the corresponding metric. func markError(p *Peer, err error) { - if !metrics.Enabled { + if !metrics.Enabled() { return } m := meters.get(p.Inbound()) diff --git a/eth/protocols/eth/metrics.go b/eth/protocols/eth/metrics.go index 5e0aee39f8..adfa84936d 100644 --- a/eth/protocols/eth/metrics.go +++ b/eth/protocols/eth/metrics.go @@ -41,23 +41,23 @@ func (h *bidirectionalMeters) get(ingress bool) *hsMeters { type hsMeters struct { // peerError measures the number of errors related to incorrect peer // behaviour, such as invalid message code, size, encoding, etc. - peerError metrics.Meter + peerError *metrics.Meter // timeoutError measures the number of timeouts. - timeoutError metrics.Meter + timeoutError *metrics.Meter // networkIDMismatch measures the number of network id mismatch errors. - networkIDMismatch metrics.Meter + networkIDMismatch *metrics.Meter // protocolVersionMismatch measures the number of differing protocol // versions. - protocolVersionMismatch metrics.Meter + protocolVersionMismatch *metrics.Meter // genesisMismatch measures the number of differing genesises. - genesisMismatch metrics.Meter + genesisMismatch *metrics.Meter // forkidRejected measures the number of differing forkids. - forkidRejected metrics.Meter + forkidRejected *metrics.Meter } // newHandshakeMeters registers and returns handshake meters for the given diff --git a/eth/protocols/snap/handler.go b/eth/protocols/snap/handler.go index d36f9621b1..924aff7ac9 100644 --- a/eth/protocols/snap/handler.go +++ b/eth/protocols/snap/handler.go @@ -132,7 +132,7 @@ func HandleMessage(backend Backend, peer *Peer) error { defer msg.Discard() start := time.Now() // Track the amount of time it takes to serve the request and run the handler - if metrics.Enabled { + if metrics.Enabled() { h := fmt.Sprintf("%s/%s/%d/%#02x", p2p.HandleHistName, ProtocolName, peer.Version(), msg.Code) defer func(start time.Time) { sampler := func() metrics.Sample { @@ -454,7 +454,7 @@ func ServiceGetByteCodesQuery(chain *core.BlockChain, req *GetByteCodesPacket) [ // Peers should not request the empty code, but if they do, at // least sent them back a correct response without db lookups codes = append(codes, []byte{}) - } else if blob, err := chain.ContractCodeWithPrefix(hash); err == nil { + } else if blob := chain.ContractCodeWithPrefix(hash); len(blob) > 0 { codes = append(codes, blob) bytes += uint64(len(blob)) } diff --git a/eth/protocols/trust/handler.go b/eth/protocols/trust/handler.go index 958b6fd80e..48ef4b7fbf 100644 --- a/eth/protocols/trust/handler.go +++ b/eth/protocols/trust/handler.go @@ -82,7 +82,7 @@ func handleMessage(backend Backend, peer *Peer) error { defer msg.Discard() // Track the amount of time it takes to serve the request and run the handler - if metrics.Enabled { + if metrics.Enabled() { h := fmt.Sprintf("%s/%s/%d/%#02x", p2p.HandleHistName, ProtocolName, peer.Version(), msg.Code) defer func(start time.Time) { sampler := func() metrics.Sample { diff --git a/eth/state_accessor.go b/eth/state_accessor.go index 36991f2215..6b99835dba 100644 --- a/eth/state_accessor.go +++ b/eth/state_accessor.go @@ -244,16 +244,14 @@ func (eth *Ethereum) stateAtTransaction(ctx context.Context, block *types.Block, // upgrade build-in system contract before normal txs if Feynman is not enabled systemcontracts.TryUpdateBuildInSystemContract(eth.blockchain.Config(), block.Number(), parent.Time(), block.Time(), statedb, true) // Insert parent beacon block root in the state as per EIP-4788. + context := core.NewEVMBlockContext(block.Header(), eth.blockchain, nil) + evm := vm.NewEVM(context, statedb, eth.blockchain.Config(), vm.Config{}) if beaconRoot := block.BeaconRoot(); beaconRoot != nil { - context := core.NewEVMBlockContext(block.Header(), eth.blockchain, nil) - vmenv := vm.NewEVM(context, vm.TxContext{}, statedb, eth.blockchain.Config(), vm.Config{}) - core.ProcessBeaconBlockRoot(*beaconRoot, vmenv, statedb) + core.ProcessBeaconBlockRoot(*beaconRoot, evm) } // If prague hardfork, insert parent block hash in the state as per EIP-2935. if eth.blockchain.Config().IsPrague(block.Number(), block.Time()) { - context := core.NewEVMBlockContext(block.Header(), eth.blockchain, nil) - vmenv := vm.NewEVM(context, vm.TxContext{}, statedb, eth.blockchain.Config(), vm.Config{}) - core.ProcessParentBlockHash(block.ParentHash(), vmenv, statedb) + core.ProcessParentBlockHash(block.ParentHash(), evm) } if txIndex == 0 && len(block.Transactions()) == 0 { return nil, vm.BlockContext{}, statedb, release, nil @@ -280,22 +278,20 @@ func (eth *Ethereum) stateAtTransaction(ctx context.Context, block *types.Block, } } - // Assemble the transaction call message and return if the requested offset - msg, _ := core.TransactionToMessage(tx, signer, block.BaseFee()) - txContext := core.NewEVMTxContext(msg) - context := core.NewEVMBlockContext(block.Header(), eth.blockchain, nil) if idx == txIndex { return tx, context, statedb, release, nil } + // Assemble the transaction call message and return if the requested offset + msg, _ := core.TransactionToMessage(tx, signer, block.BaseFee()) + // Not yet the searched for transaction, execute on top of the current state - vmenv := vm.NewEVM(context, txContext, statedb, eth.blockchain.Config(), vm.Config{}) statedb.SetTxContext(tx.Hash(), idx) - if _, err := core.ApplyMessage(vmenv, msg, new(core.GasPool).AddGas(tx.Gas())); err != nil { + if _, err := core.ApplyMessage(evm, msg, new(core.GasPool).AddGas(tx.Gas())); err != nil { return nil, vm.BlockContext{}, nil, nil, fmt.Errorf("transaction %#x failed: %v", tx.Hash(), err) } // Ensure any modifications are committed to the state // Only delete empty objects if EIP158/161 (a.k.a Spurious Dragon) is in effect - statedb.Finalise(vmenv.ChainConfig().IsEIP158(block.Number())) + statedb.Finalise(evm.ChainConfig().IsEIP158(block.Number())) } return nil, vm.BlockContext{}, nil, nil, fmt.Errorf("transaction index %d out of range for block %#x", txIndex, block.Hash()) } diff --git a/eth/sync.go b/eth/sync.go index e887f86df7..f98eccb067 100644 --- a/eth/sync.go +++ b/eth/sync.go @@ -24,6 +24,7 @@ import ( "github.com/ethereum/go-ethereum/core/rawdb" "github.com/ethereum/go-ethereum/core/txpool" "github.com/ethereum/go-ethereum/eth/downloader" + "github.com/ethereum/go-ethereum/eth/ethconfig" "github.com/ethereum/go-ethereum/eth/protocols/eth" "github.com/ethereum/go-ethereum/log" ) @@ -184,7 +185,7 @@ func (cs *chainSyncer) modeAndLocalHead() (downloader.SyncMode, *big.Int) { if cs.handler.snapSync.Load() { block := cs.handler.chain.CurrentSnapBlock() td := cs.handler.chain.GetTd(block.Hash(), block.Number.Uint64()) - return downloader.SnapSync, td + return ethconfig.SnapSync, td } // We are probably in full sync, but we might have rewound to before the // snap sync pivot, check if we should re-enable snap sync. @@ -196,7 +197,7 @@ func (cs *chainSyncer) modeAndLocalHead() (downloader.SyncMode, *big.Int) { } block := cs.handler.chain.CurrentSnapBlock() td := cs.handler.chain.GetTd(block.Hash(), block.Number.Uint64()) - return downloader.SnapSync, td + return ethconfig.SnapSync, td } } // We are in a full sync, but the associated head state is missing. To complete @@ -206,11 +207,11 @@ func (cs *chainSyncer) modeAndLocalHead() (downloader.SyncMode, *big.Int) { block := cs.handler.chain.CurrentSnapBlock() td := cs.handler.chain.GetTd(block.Hash(), block.Number.Uint64()) log.Info("Reenabled snap sync as chain is stateless") - return downloader.SnapSync, td + return ethconfig.SnapSync, td } // Nope, we're really full syncing td := cs.handler.chain.GetTd(head.Hash(), head.Number.Uint64()) - return downloader.FullSync, td + return ethconfig.FullSync, td } // startSync launches doSync in a new goroutine. diff --git a/eth/sync_test.go b/eth/sync_test.go index 72d018527e..cc20b00ee4 100644 --- a/eth/sync_test.go +++ b/eth/sync_test.go @@ -21,14 +21,14 @@ import ( "time" "github.com/ethereum/go-ethereum/core" - "github.com/ethereum/go-ethereum/params" - "github.com/stretchr/testify/require" - "github.com/ethereum/go-ethereum/eth/downloader" + "github.com/ethereum/go-ethereum/eth/ethconfig" "github.com/ethereum/go-ethereum/eth/protocols/eth" "github.com/ethereum/go-ethereum/eth/protocols/snap" "github.com/ethereum/go-ethereum/p2p" "github.com/ethereum/go-ethereum/p2p/enode" + "github.com/ethereum/go-ethereum/params" + "github.com/stretchr/testify/require" ) // Tests that snap sync is disabled after a successful sync cycle. @@ -89,7 +89,7 @@ func testSnapSyncDisabling(t *testing.T, ethVer uint, snapVer uint) { time.Sleep(250 * time.Millisecond) // Check that snap sync was disabled - op := peerToSyncOp(downloader.SnapSync, empty.handler.peers.peerWithHighestTD()) + op := peerToSyncOp(ethconfig.SnapSync, empty.handler.peers.peerWithHighestTD()) if err := empty.handler.doSync(op); err != nil { t.Fatal("sync failed:", err) } @@ -99,11 +99,11 @@ func testSnapSyncDisabling(t *testing.T, ethVer uint, snapVer uint) { } func TestFullSyncWithBlobs(t *testing.T) { - testChainSyncWithBlobs(t, downloader.FullSync, 128, 128) + testChainSyncWithBlobs(t, ethconfig.FullSync, 128, 128) } func TestSnapSyncWithBlobs(t *testing.T) { - testChainSyncWithBlobs(t, downloader.SnapSync, 128, 128) + testChainSyncWithBlobs(t, ethconfig.SnapSync, 128, 128) } func testChainSyncWithBlobs(t *testing.T, mode downloader.SyncMode, preCancunBlks, postCancunBlks uint64) { @@ -115,14 +115,14 @@ func testChainSyncWithBlobs(t *testing.T, mode downloader.SyncMode, preCancunBlk // Create an empty handler empty := newTestParliaHandlerAfterCancun(t, &config, mode, 0, 0) defer empty.close() - if downloader.SnapSync == mode && !empty.handler.snapSync.Load() { + if ethconfig.SnapSync == mode && !empty.handler.snapSync.Load() { t.Fatalf("snap sync disabled on pristine blockchain") } // Create a full handler full := newTestParliaHandlerAfterCancun(t, &config, mode, preCancunBlks, postCancunBlks) defer full.close() - if downloader.SnapSync == mode && full.handler.snapSync.Load() { + if ethconfig.SnapSync == mode && full.handler.snapSync.Load() { t.Fatalf("snap sync not disabled on non-empty blockchain") } diff --git a/eth/tracers/api.go b/eth/tracers/api.go index 7ec67d6ac4..a06cc39a1d 100644 --- a/eth/tracers/api.go +++ b/eth/tracers/api.go @@ -406,16 +406,14 @@ func (api *API) traceChain(start, end *types.Block, config *TraceConfig, closed // Insert block's parent beacon block root in the state // as per EIP-4788. + context := core.NewEVMBlockContext(next.Header(), api.chainContext(ctx), nil) + evm := vm.NewEVM(context, statedb, api.backend.ChainConfig(), vm.Config{}) if beaconRoot := next.BeaconRoot(); beaconRoot != nil { - context := core.NewEVMBlockContext(next.Header(), api.chainContext(ctx), nil) - vmenv := vm.NewEVM(context, vm.TxContext{}, statedb, api.backend.ChainConfig(), vm.Config{}) - core.ProcessBeaconBlockRoot(*beaconRoot, vmenv, statedb) + core.ProcessBeaconBlockRoot(*beaconRoot, evm) } // Insert parent hash in history contract. if api.backend.ChainConfig().IsPrague(next.Number(), next.Time()) { - context := core.NewEVMBlockContext(next.Header(), api.chainContext(ctx), nil) - vmenv := vm.NewEVM(context, vm.TxContext{}, statedb, api.backend.ChainConfig(), vm.Config{}) - core.ProcessParentBlockHash(next.ParentHash(), vmenv, statedb) + core.ProcessParentBlockHash(next.ParentHash(), evm) } // Clean out any pending release functions of trace state. Note this // step must be done after constructing tracing state, because the @@ -570,23 +568,17 @@ func (api *API) IntermediateRoots(ctx context.Context, hash common.Hash, config deleteEmptyObjects = chainConfig.IsEIP158(block.Number()) beforeSystemTx = true ) + evm := vm.NewEVM(vmctx, statedb, chainConfig, vm.Config{}) if beaconRoot := block.BeaconRoot(); beaconRoot != nil { - vmenv := vm.NewEVM(vmctx, vm.TxContext{}, statedb, chainConfig, vm.Config{}) - core.ProcessBeaconBlockRoot(*beaconRoot, vmenv, statedb) + core.ProcessBeaconBlockRoot(*beaconRoot, evm) } if chainConfig.IsPrague(block.Number(), block.Time()) { - core.ProcessParentBlockHash(block.ParentHash(), vm.NewEVM(vmctx, vm.TxContext{}, statedb, chainConfig, vm.Config{}), statedb) + core.ProcessParentBlockHash(block.ParentHash(), evm) } for i, tx := range block.Transactions() { if err := ctx.Err(); err != nil { return nil, err } - var ( - msg, _ = core.TransactionToMessage(tx, signer, block.BaseFee()) - txContext = core.NewEVMTxContext(msg) - vmenv = vm.NewEVM(vmctx, txContext, statedb, chainConfig, vm.Config{}) - ) - if posa, ok := api.backend.Engine().(consensus.PoSA); ok { if isSystem, _ := posa.IsSystemTransaction(tx, block.Header()); isSystem { balance := statedb.GetBalance(consensus.SystemAddress) @@ -602,8 +594,9 @@ func (api *API) IntermediateRoots(ctx context.Context, hash common.Hash, config } } + msg, _ := core.TransactionToMessage(tx, signer, block.BaseFee()) statedb.SetTxContext(tx.Hash(), i) - if _, err := core.ApplyMessage(vmenv, msg, new(core.GasPool).AddGas(msg.GasLimit)); err != nil { + if _, err := core.ApplyMessage(evm, msg, new(core.GasPool).AddGas(msg.GasLimit)); err != nil { log.Warn("Tracing intermediate roots did not complete", "txindex", i, "txhash", tx.Hash(), "err", err) // We intentionally don't return the error here: if we do, then the RPC server will not // return the roots. Most likely, the caller already knows that a certain transaction fails to @@ -658,13 +651,12 @@ func (api *API) traceBlock(ctx context.Context, block *types.Block, config *Trac // upgrade build-in system contract before normal txs if Feynman is not enabled systemcontracts.TryUpdateBuildInSystemContract(api.backend.ChainConfig(), block.Number(), parent.Time(), block.Time(), statedb, true) blockCtx := core.NewEVMBlockContext(block.Header(), api.chainContext(ctx), nil) + evm := vm.NewEVM(blockCtx, statedb, api.backend.ChainConfig(), vm.Config{}) if beaconRoot := block.BeaconRoot(); beaconRoot != nil { - vmenv := vm.NewEVM(blockCtx, vm.TxContext{}, statedb, api.backend.ChainConfig(), vm.Config{}) - core.ProcessBeaconBlockRoot(*beaconRoot, vmenv, statedb) + core.ProcessBeaconBlockRoot(*beaconRoot, evm) } if api.backend.ChainConfig().IsPrague(block.Number(), block.Time()) { - vmenv := vm.NewEVM(blockCtx, vm.TxContext{}, statedb, api.backend.ChainConfig(), vm.Config{}) - core.ProcessParentBlockHash(block.ParentHash(), vmenv, statedb) + core.ProcessParentBlockHash(block.ParentHash(), evm) } // JS tracers have high overhead. In this case run a parallel @@ -774,6 +766,8 @@ func (api *API) traceBlockParallel(ctx context.Context, block *types.Block, stat beforeSystemTx = true ) blockCtx := core.NewEVMBlockContext(block.Header(), api.chainContext(ctx), nil) + evm := vm.NewEVM(blockCtx, statedb, api.backend.ChainConfig(), vm.Config{}) + txloop: for i, tx := range txs { // upgrade build-in system contract before system txs if Feynman is enabled @@ -804,14 +798,13 @@ txloop: // Generate the next state snapshot fast without tracing msg, _ := core.TransactionToMessage(tx, signer, block.BaseFee()) statedb.SetTxContext(tx.Hash(), i) - vmenv := vm.NewEVM(blockCtx, core.NewEVMTxContext(msg), statedb, api.backend.ChainConfig(), vm.Config{}) - if _, err := core.ApplyMessage(vmenv, msg, new(core.GasPool).AddGas(msg.GasLimit)); err != nil { + if _, err := core.ApplyMessage(evm, msg, new(core.GasPool).AddGas(msg.GasLimit)); err != nil { failed = err break txloop } // Finalize the state so any modifications are written to the trie // Only delete empty objects if EIP158/161 (a.k.a Spurious Dragon) is in effect - statedb.Finalise(vmenv.ChainConfig().IsEIP158(block.Number())) + statedb.Finalise(evm.ChainConfig().IsEIP158(block.Number())) } close(jobs) @@ -863,7 +856,6 @@ func (api *API) standardTraceBlockToFile(ctx context.Context, block *types.Block logConfig = config.Config txHash = config.TxHash } - logConfig.Debug = true // Execute transaction, either tracing all or just the requested one var ( @@ -883,13 +875,12 @@ func (api *API) standardTraceBlockToFile(ctx context.Context, block *types.Block // Note: This copies the config, to not screw up the main config chainConfig, canon = overrideConfig(chainConfig, config.Overrides) } + evm := vm.NewEVM(vmctx, statedb, chainConfig, vm.Config{}) if beaconRoot := block.BeaconRoot(); beaconRoot != nil { - vmenv := vm.NewEVM(vmctx, vm.TxContext{}, statedb, chainConfig, vm.Config{}) - core.ProcessBeaconBlockRoot(*beaconRoot, vmenv, statedb) + core.ProcessBeaconBlockRoot(*beaconRoot, evm) } if chainConfig.IsPrague(block.Number(), block.Time()) { - vmenv := vm.NewEVM(vmctx, vm.TxContext{}, statedb, chainConfig, vm.Config{}) - core.ProcessParentBlockHash(block.ParentHash(), vmenv, statedb) + core.ProcessParentBlockHash(block.ParentHash(), evm) } for i, tx := range block.Transactions() { // upgrade build-in system contract before system txs if Feynman is enabled @@ -910,12 +901,11 @@ func (api *API) standardTraceBlockToFile(ctx context.Context, block *types.Block // Prepare the transaction for un-traced execution var ( - msg, _ = core.TransactionToMessage(tx, signer, block.BaseFee()) - txContext = core.NewEVMTxContext(msg) - vmConf vm.Config - dump *os.File - writer *bufio.Writer - err error + msg, _ = core.TransactionToMessage(tx, signer, block.BaseFee()) + vmConf vm.Config + dump *os.File + writer *bufio.Writer + err error ) // If the transaction needs tracing, swap out the configs if tx.Hash() == txHash || txHash == (common.Hash{}) { @@ -938,12 +928,11 @@ func (api *API) standardTraceBlockToFile(ctx context.Context, block *types.Block } } // Execute the transaction and flush any traces to disk - vmenv := vm.NewEVM(vmctx, txContext, statedb, chainConfig, vmConf) statedb.SetTxContext(tx.Hash(), i) if vmConf.Tracer.OnTxStart != nil { - vmConf.Tracer.OnTxStart(vmenv.GetVMContext(), tx, msg.From) + vmConf.Tracer.OnTxStart(evm.GetVMContext(), tx, msg.From) } - vmRet, err := core.ApplyMessage(vmenv, msg, new(core.GasPool).AddGas(msg.GasLimit)) + vmRet, err := core.ApplyMessage(evm, msg, new(core.GasPool).AddGas(msg.GasLimit)) if vmConf.Tracer.OnTxEnd != nil { vmConf.Tracer.OnTxEnd(&types.Receipt{GasUsed: vmRet.UsedGas}, err) } @@ -959,7 +948,7 @@ func (api *API) standardTraceBlockToFile(ctx context.Context, block *types.Block } // Finalize the state so any modifications are written to the trie // Only delete empty objects if EIP158/161 (a.k.a Spurious Dragon) is in effect - statedb.Finalise(vmenv.ChainConfig().IsEIP158(block.Number())) + statedb.Finalise(evm.ChainConfig().IsEIP158(block.Number())) // If we've traced the transaction we were looking for, abort if tx.Hash() == txHash { @@ -1148,8 +1137,8 @@ func (api *API) traceTx(ctx context.Context, tx *types.Transaction, message *cor return nil, err } } - // The actual TxContext will be created as part of ApplyTransactionWithEVM. - vmenv := vm.NewEVM(vmctx, vm.TxContext{GasPrice: message.GasPrice, BlobFeeCap: message.BlobGasFeeCap}, statedb, api.backend.ChainConfig(), vm.Config{Tracer: tracer.Hooks, NoBaseFee: true}) + tracingStateDB := state.NewHookedState(statedb, tracer.Hooks) + evm := vm.NewEVM(vmctx, tracingStateDB, api.backend.ChainConfig(), vm.Config{Tracer: tracer.Hooks, NoBaseFee: true}) // Define a meaningful timeout of a single transaction trace if config.Timeout != nil { @@ -1163,7 +1152,7 @@ func (api *API) traceTx(ctx context.Context, tx *types.Transaction, message *cor if errors.Is(deadlineCtx.Err(), context.DeadlineExceeded) { tracer.Stop(errors.New("execution timeout")) // Stop evm execution. Note cancellation is not necessarily immediate. - vmenv.Cancel() + evm.Cancel() } }() defer cancel() @@ -1176,7 +1165,7 @@ func (api *API) traceTx(ctx context.Context, tx *types.Transaction, message *cor // Call Prepare to clear out the statedb access list statedb.SetTxContext(txctx.TxHash, txctx.TxIndex) - _, err = core.ApplyTransactionWithEVM(message, api.backend.ChainConfig(), new(core.GasPool).AddGas(message.GasLimit), statedb, vmctx.BlockNumber, txctx.BlockHash, tx, &usedGas, vmenv) + _, err = core.ApplyTransactionWithEVM(message, new(core.GasPool).AddGas(message.GasLimit), statedb, vmctx.BlockNumber, txctx.BlockHash, tx, &usedGas, evm) if err != nil { return nil, fmt.Errorf("tracing failed: %w", err) } diff --git a/eth/tracers/api_test.go b/eth/tracers/api_test.go index e1507ef375..a71f9edfba 100644 --- a/eth/tracers/api_test.go +++ b/eth/tracers/api_test.go @@ -37,6 +37,7 @@ import ( "github.com/ethereum/go-ethereum/core" "github.com/ethereum/go-ethereum/core/rawdb" "github.com/ethereum/go-ethereum/core/state" + "github.com/ethereum/go-ethereum/core/tracing" "github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/core/vm" "github.com/ethereum/go-ethereum/crypto" @@ -171,22 +172,109 @@ func (b *testBackend) StateAtTransaction(ctx context.Context, block *types.Block } // Recompute transactions up to the target index. signer := types.MakeSigner(b.chainConfig, block.Number(), block.Time()) + context := core.NewEVMBlockContext(block.Header(), b.chain, nil) + evm := vm.NewEVM(context, statedb, b.chainConfig, vm.Config{}) for idx, tx := range block.Transactions() { - msg, _ := core.TransactionToMessage(tx, signer, block.BaseFee()) - txContext := core.NewEVMTxContext(msg) - context := core.NewEVMBlockContext(block.Header(), b.chain, nil) if idx == txIndex { return tx, context, statedb, release, nil } - vmenv := vm.NewEVM(context, txContext, statedb, b.chainConfig, vm.Config{}) - if _, err := core.ApplyMessage(vmenv, msg, new(core.GasPool).AddGas(tx.Gas())); err != nil { + msg, _ := core.TransactionToMessage(tx, signer, block.BaseFee()) + if _, err := core.ApplyMessage(evm, msg, new(core.GasPool).AddGas(tx.Gas())); err != nil { return nil, vm.BlockContext{}, nil, nil, fmt.Errorf("transaction %#x failed: %v", tx.Hash(), err) } - statedb.Finalise(vmenv.ChainConfig().IsEIP158(block.Number())) + statedb.Finalise(evm.ChainConfig().IsEIP158(block.Number())) } return nil, vm.BlockContext{}, nil, nil, fmt.Errorf("transaction index %d out of range for block %#x", txIndex, block.Hash()) } +type stateTracer struct { + Balance map[common.Address]*hexutil.Big + Nonce map[common.Address]hexutil.Uint64 + Storage map[common.Address]map[common.Hash]common.Hash +} + +func newStateTracer(ctx *Context, cfg json.RawMessage, chainCfg *params.ChainConfig) (*Tracer, error) { + t := &stateTracer{ + Balance: make(map[common.Address]*hexutil.Big), + Nonce: make(map[common.Address]hexutil.Uint64), + Storage: make(map[common.Address]map[common.Hash]common.Hash), + } + return &Tracer{ + GetResult: func() (json.RawMessage, error) { + return json.Marshal(t) + }, + Hooks: &tracing.Hooks{ + OnBalanceChange: func(addr common.Address, prev, new *big.Int, reason tracing.BalanceChangeReason) { + t.Balance[addr] = (*hexutil.Big)(new) + }, + OnNonceChange: func(addr common.Address, prev, new uint64) { + t.Nonce[addr] = hexutil.Uint64(new) + }, + OnStorageChange: func(addr common.Address, slot common.Hash, prev, new common.Hash) { + if t.Storage[addr] == nil { + t.Storage[addr] = make(map[common.Hash]common.Hash) + } + t.Storage[addr][slot] = new + }, + }, + }, nil +} + +func TestStateHooks(t *testing.T) { + t.Parallel() + + // Initialize test accounts + var ( + key, _ = crypto.HexToECDSA("b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291") + from = crypto.PubkeyToAddress(key.PublicKey) + to = common.HexToAddress("0x00000000000000000000000000000000deadbeef") + genesis = &core.Genesis{ + Config: params.TestChainConfig, + Alloc: types.GenesisAlloc{ + from: {Balance: big.NewInt(params.Ether)}, + to: { + Code: []byte{ + byte(vm.PUSH1), 0x2a, // stack: [42] + byte(vm.PUSH1), 0x0, // stack: [0, 42] + byte(vm.SSTORE), // stack: [] + byte(vm.STOP), + }, + }, + }, + } + genBlocks = 2 + signer = types.HomesteadSigner{} + nonce = uint64(0) + backend = newTestBackend(t, genBlocks, genesis, func(i int, b *core.BlockGen) { + // Transfer from account[0] to account[1] + // value: 1000 wei + // fee: 0 wei + tx, _ := types.SignTx(types.NewTx(&types.LegacyTx{ + Nonce: nonce, + To: &to, + Value: big.NewInt(1000), + Gas: params.TxGas, + GasPrice: b.BaseFee(), + Data: nil}), + signer, key) + b.AddTx(tx) + nonce++ + }) + ) + defer backend.teardown() + DefaultDirectory.Register("stateTracer", newStateTracer, false) + api := NewAPI(backend) + tracer := "stateTracer" + res, err := api.TraceCall(context.Background(), ethapi.TransactionArgs{From: &from, To: &to, Value: (*hexutil.Big)(big.NewInt(1000))}, rpc.BlockNumberOrHashWithNumber(rpc.LatestBlockNumber), &TraceCallConfig{TraceConfig: TraceConfig{Tracer: &tracer}}) + if err != nil { + t.Fatalf("failed to trace call: %v", err) + } + expected := `{"Balance":{"0x00000000000000000000000000000000deadbeef":"0x3e8","0x71562b71999873db5b286df957af199ec94617f7":"0xde0975924ed6f90"},"Nonce":{"0x71562b71999873db5b286df957af199ec94617f7":"0x3"},"Storage":{"0x00000000000000000000000000000000deadbeef":{"0x0000000000000000000000000000000000000000000000000000000000000000":"0x000000000000000000000000000000000000000000000000000000000000002a"}}}` + if expected != fmt.Sprintf("%s", res) { + t.Fatalf("unexpected trace result: have %s want %s", res, expected) + } +} + func TestTraceCall(t *testing.T) { t.Parallel() @@ -448,7 +536,7 @@ func TestTraceTransaction(t *testing.T) { Gas: params.TxGas, Failed: false, ReturnValue: "", - StructLogs: []logger.StructLogRes{}, + StructLogs: []json.RawMessage{}, }) { t.Error("Transaction tracing result is different") } diff --git a/eth/tracers/internal/tracetest/calltrace_test.go b/eth/tracers/internal/tracetest/calltrace_test.go index cb635e7127..999ab211c0 100644 --- a/eth/tracers/internal/tracetest/calltrace_test.go +++ b/eth/tracers/internal/tracetest/calltrace_test.go @@ -132,7 +132,7 @@ func testCallTracer(tracerName string, dirPath string, t *testing.T) { if err != nil { t.Fatalf("failed to prepare transaction for tracing: %v", err) } - evm := vm.NewEVM(context, core.NewEVMTxContext(msg), logState, test.Genesis.Config, vm.Config{Tracer: tracer.Hooks}) + evm := vm.NewEVM(context, logState, test.Genesis.Config, vm.Config{Tracer: tracer.Hooks}) tracer.OnTxStart(evm.GetVMContext(), tx, msg.From) vmRet, err := core.ApplyMessage(evm, msg, new(core.GasPool).AddGas(tx.Gas())) if err != nil { @@ -205,11 +205,6 @@ func benchTracer(tracerName string, test *callTracerTest, b *testing.B) { b.Fatalf("failed to parse testcase input: %v", err) } signer := types.MakeSigner(test.Genesis.Config, new(big.Int).SetUint64(uint64(test.Context.Number)), uint64(test.Context.Time)) - origin, _ := signer.Sender(tx) - txContext := vm.TxContext{ - Origin: origin, - GasPrice: tx.GasPrice(), - } context := test.Context.toBlockContext(test.Genesis) msg, err := core.TransactionToMessage(tx, signer, context.BaseFee) if err != nil { @@ -220,17 +215,26 @@ func benchTracer(tracerName string, test *callTracerTest, b *testing.B) { b.ReportAllocs() b.ResetTimer() + + evm := vm.NewEVM(context, state.StateDB, test.Genesis.Config, vm.Config{}) + for i := 0; i < b.N; i++ { + snap := state.StateDB.Snapshot() tracer, err := tracers.DefaultDirectory.New(tracerName, new(tracers.Context), nil, test.Genesis.Config) if err != nil { b.Fatalf("failed to create call tracer: %v", err) } - evm := vm.NewEVM(context, txContext, state.StateDB, test.Genesis.Config, vm.Config{Tracer: tracer.Hooks}) - snap := state.StateDB.Snapshot() - st := core.NewStateTransition(evm, msg, new(core.GasPool).AddGas(tx.Gas())) - if _, err = st.TransitionDb(); err != nil { + evm.Config.Tracer = tracer.Hooks + if tracer.OnTxStart != nil { + tracer.OnTxStart(evm.GetVMContext(), tx, msg.From) + } + _, err = core.ApplyMessage(evm, msg, new(core.GasPool).AddGas(tx.Gas())) + if err != nil { b.Fatalf("failed to execute transaction: %v", err) } + if tracer.OnTxEnd != nil { + tracer.OnTxEnd(&types.Receipt{GasUsed: tx.Gas()}, nil) + } if _, err = tracer.GetResult(); err != nil { b.Fatal(err) } @@ -368,11 +372,7 @@ func TestInternals(t *testing.T) { if err != nil { t.Fatalf("test %v: failed to sign transaction: %v", tc.name, err) } - txContext := vm.TxContext{ - Origin: origin, - GasPrice: tx.GasPrice(), - } - evm := vm.NewEVM(context, txContext, logState, config, vm.Config{Tracer: tc.tracer.Hooks}) + evm := vm.NewEVM(context, logState, config, vm.Config{Tracer: tc.tracer.Hooks}) msg, err := core.TransactionToMessage(tx, signer, big.NewInt(0)) if err != nil { t.Fatalf("test %v: failed to create message: %v", tc.name, err) diff --git a/eth/tracers/internal/tracetest/flat_calltrace_test.go b/eth/tracers/internal/tracetest/flat_calltrace_test.go index 0ec3c367bc..553eaf1b57 100644 --- a/eth/tracers/internal/tracetest/flat_calltrace_test.go +++ b/eth/tracers/internal/tracetest/flat_calltrace_test.go @@ -98,7 +98,7 @@ func flatCallTracerTestRunner(tracerName string, filename string, dirPath string if err != nil { return fmt.Errorf("failed to prepare transaction for tracing: %v", err) } - evm := vm.NewEVM(context, core.NewEVMTxContext(msg), state.StateDB, test.Genesis.Config, vm.Config{Tracer: tracer.Hooks}) + evm := vm.NewEVM(context, state.StateDB, test.Genesis.Config, vm.Config{Tracer: tracer.Hooks}) tracer.OnTxStart(evm.GetVMContext(), tx, msg.From) vmRet, err := core.ApplyMessage(evm, msg, new(core.GasPool).AddGas(tx.Gas())) if err != nil { diff --git a/eth/tracers/internal/tracetest/prestate_test.go b/eth/tracers/internal/tracetest/prestate_test.go index c6cf10a483..ad3d75d8de 100644 --- a/eth/tracers/internal/tracetest/prestate_test.go +++ b/eth/tracers/internal/tracetest/prestate_test.go @@ -106,7 +106,7 @@ func testPrestateDiffTracer(tracerName string, dirPath string, t *testing.T) { if err != nil { t.Fatalf("failed to prepare transaction for tracing: %v", err) } - evm := vm.NewEVM(context, core.NewEVMTxContext(msg), state.StateDB, test.Genesis.Config, vm.Config{Tracer: tracer.Hooks}) + evm := vm.NewEVM(context, state.StateDB, test.Genesis.Config, vm.Config{Tracer: tracer.Hooks}) tracer.OnTxStart(evm.GetVMContext(), tx, msg.From) vmRet, err := core.ApplyMessage(evm, msg, new(core.GasPool).AddGas(tx.Gas())) if err != nil { diff --git a/eth/tracers/internal/tracetest/supply_test.go b/eth/tracers/internal/tracetest/supply_test.go index 19fdcf9f6e..8bd84d9ce1 100644 --- a/eth/tracers/internal/tracetest/supply_test.go +++ b/eth/tracers/internal/tracetest/supply_test.go @@ -86,7 +86,7 @@ func TestSupplyOmittedFields(t *testing.T) { expected := supplyInfo{ Number: 0, - Hash: common.HexToHash("0xc02ee8ee5b54a40e43f0fa827d431e1bd4f217e941790dda10b2521d1925a20b"), + Hash: common.HexToHash("0x3055fc27d6b4a08eb07033a0d1ee755a4b2988086f28a6189eac1b507525eeb1"), ParentHash: common.HexToHash("0x0000000000000000000000000000000000000000000000000000000000000000"), } actual := out[expected.Number] diff --git a/eth/tracers/js/goja.go b/eth/tracers/js/goja.go index 35abd00017..227ea57226 100644 --- a/eth/tracers/js/goja.go +++ b/eth/tracers/js/goja.go @@ -260,7 +260,7 @@ func (t *jsTracer) OnTxStart(env *tracing.VMContext, tx *types.Transaction, from t.activePrecompiles = vm.ActivePrecompiles(rules) t.ctx["block"] = t.vm.ToValue(t.env.BlockNumber.Uint64()) t.ctx["gas"] = t.vm.ToValue(tx.Gas()) - gasPriceBig, err := t.toBig(t.vm, env.GasPrice.String()) + gasPriceBig, err := t.toBig(t.vm, tx.EffectiveGasTipValue(env.BaseFee).String()) if err != nil { t.err = err return diff --git a/eth/tracers/js/tracer_test.go b/eth/tracers/js/tracer_test.go index d1db5b3765..4a369d5086 100644 --- a/eth/tracers/js/tracer_test.go +++ b/eth/tracers/js/tracer_test.go @@ -59,25 +59,26 @@ type vmContext struct { } func testCtx() *vmContext { - return &vmContext{blockCtx: vm.BlockContext{BlockNumber: big.NewInt(1)}, txCtx: vm.TxContext{GasPrice: big.NewInt(100000)}} + return &vmContext{blockCtx: vm.BlockContext{BlockNumber: big.NewInt(1), BaseFee: big.NewInt(0)}, txCtx: vm.TxContext{GasPrice: big.NewInt(100000)}} } func runTrace(tracer *tracers.Tracer, vmctx *vmContext, chaincfg *params.ChainConfig, contractCode []byte) (json.RawMessage, error) { var ( - env = vm.NewEVM(vmctx.blockCtx, vmctx.txCtx, &dummyStatedb{}, chaincfg, vm.Config{Tracer: tracer.Hooks}) + evm = vm.NewEVM(vmctx.blockCtx, &dummyStatedb{}, chaincfg, vm.Config{Tracer: tracer.Hooks}) gasLimit uint64 = 31000 startGas uint64 = 10000 value = uint256.NewInt(0) contract = vm.NewContract(account{}, account{}, value, startGas) ) + evm.SetTxContext(vmctx.txCtx) contract.Code = []byte{byte(vm.PUSH1), 0x1, byte(vm.PUSH1), 0x1, 0x0} if contractCode != nil { contract.Code = contractCode } - tracer.OnTxStart(env.GetVMContext(), types.NewTx(&types.LegacyTx{Gas: gasLimit}), contract.Caller()) + tracer.OnTxStart(evm.GetVMContext(), types.NewTx(&types.LegacyTx{Gas: gasLimit, GasPrice: vmctx.txCtx.GasPrice}), contract.Caller()) tracer.OnEnter(0, byte(vm.CALL), contract.Caller(), contract.Address(), []byte{}, startGas, value.ToBig()) - ret, err := env.Interpreter().Run(contract, []byte{}, false) + ret, err := evm.Interpreter().Run(contract, []byte{}, false) tracer.OnExit(0, ret, startGas-contract.Gas, err, true) // Rest gas assumes no refund tracer.OnTxEnd(&types.Receipt{GasUsed: gasLimit - contract.Gas}, nil) @@ -191,8 +192,9 @@ func TestHaltBetweenSteps(t *testing.T) { scope := &vm.ScopeContext{ Contract: vm.NewContract(&account{}, &account{}, uint256.NewInt(0), 0), } - env := vm.NewEVM(vm.BlockContext{BlockNumber: big.NewInt(1)}, vm.TxContext{GasPrice: big.NewInt(1)}, &dummyStatedb{}, chainConfig, vm.Config{Tracer: tracer.Hooks}) - tracer.OnTxStart(env.GetVMContext(), types.NewTx(&types.LegacyTx{}), common.Address{}) + evm := vm.NewEVM(vm.BlockContext{BlockNumber: big.NewInt(1)}, &dummyStatedb{}, chainConfig, vm.Config{Tracer: tracer.Hooks}) + evm.SetTxContext(vm.TxContext{GasPrice: big.NewInt(1)}) + tracer.OnTxStart(evm.GetVMContext(), types.NewTx(&types.LegacyTx{}), common.Address{}) tracer.OnEnter(0, byte(vm.CALL), common.Address{}, common.Address{}, []byte{}, 0, big.NewInt(0)) tracer.OnOpcode(0, 0, 0, 0, scope, nil, 0, nil) timeout := errors.New("stahp") @@ -214,8 +216,9 @@ func TestNoStepExec(t *testing.T) { if err != nil { t.Fatal(err) } - env := vm.NewEVM(vm.BlockContext{BlockNumber: big.NewInt(1)}, vm.TxContext{GasPrice: big.NewInt(100)}, &dummyStatedb{}, chainConfig, vm.Config{Tracer: tracer.Hooks}) - tracer.OnTxStart(env.GetVMContext(), types.NewTx(&types.LegacyTx{}), common.Address{}) + evm := vm.NewEVM(vm.BlockContext{BlockNumber: big.NewInt(1)}, &dummyStatedb{}, chainConfig, vm.Config{Tracer: tracer.Hooks}) + evm.SetTxContext(vm.TxContext{GasPrice: big.NewInt(100)}) + tracer.OnTxStart(evm.GetVMContext(), types.NewTx(&types.LegacyTx{}), common.Address{}) tracer.OnEnter(0, byte(vm.CALL), common.Address{}, common.Address{}, []byte{}, 1000, big.NewInt(0)) tracer.OnExit(0, nil, 0, nil, false) ret, err := tracer.GetResult() diff --git a/eth/tracers/logger/logger.go b/eth/tracers/logger/logger.go index 8393aa13cb..ae1c0786a5 100644 --- a/eth/tracers/logger/logger.go +++ b/eth/tracers/logger/logger.go @@ -21,6 +21,7 @@ import ( "encoding/json" "fmt" "io" + "maps" "math/big" "strings" "sync/atomic" @@ -38,31 +39,21 @@ import ( // Storage represents a contract's storage. type Storage map[common.Hash]common.Hash -// Copy duplicates the current storage. -func (s Storage) Copy() Storage { - cpy := make(Storage, len(s)) - for key, value := range s { - cpy[key] = value - } - return cpy -} - // Config are the configuration options for structured logger the EVM type Config struct { EnableMemory bool // enable memory capture DisableStack bool // disable stack capture DisableStorage bool // disable storage capture EnableReturnData bool // enable return data capture - Debug bool // print output during capture end - Limit int // maximum length of output, but zero means unlimited + Limit int // maximum size of output, but zero means unlimited // Chain overrides, can be used to execute a trace using future fork rules Overrides *params.ChainConfig `json:"overrides,omitempty"` } //go:generate go run github.com/fjl/gencodec -type StructLog -field-override structLogMarshaling -out gen_structlog.go -// StructLog is emitted to the EVM each cycle and lists information about the current internal state -// prior to the execution of the statement. +// StructLog is emitted to the EVM each cycle and lists information about the +// current internal state prior to the execution of the statement. type StructLog struct { Pc uint64 `json:"pc"` Op vm.OpCode `json:"op"` @@ -102,29 +93,144 @@ func (s *StructLog) ErrorString() string { return "" } +// WriteTo writes the human-readable log data into the supplied writer. +func (s *StructLog) WriteTo(writer io.Writer) { + fmt.Fprintf(writer, "%-16spc=%08d gas=%v cost=%v", s.Op, s.Pc, s.Gas, s.GasCost) + if s.Err != nil { + fmt.Fprintf(writer, " ERROR: %v", s.Err) + } + fmt.Fprintln(writer) + + if len(s.Stack) > 0 { + fmt.Fprintln(writer, "Stack:") + for i := len(s.Stack) - 1; i >= 0; i-- { + fmt.Fprintf(writer, "%08d %s\n", len(s.Stack)-i-1, s.Stack[i].Hex()) + } + } + if len(s.Memory) > 0 { + fmt.Fprintln(writer, "Memory:") + fmt.Fprint(writer, hex.Dump(s.Memory)) + } + if len(s.Storage) > 0 { + fmt.Fprintln(writer, "Storage:") + for h, item := range s.Storage { + fmt.Fprintf(writer, "%x: %x\n", h, item) + } + } + if len(s.ReturnData) > 0 { + fmt.Fprintln(writer, "ReturnData:") + fmt.Fprint(writer, hex.Dump(s.ReturnData)) + } + fmt.Fprintln(writer) +} + +// structLogLegacy stores a structured log emitted by the EVM while replaying a +// transaction in debug mode. It's the legacy format used in tracer. The differences +// between the structLog json and the 'legacy' json are: +// +// op: +// Legacy uses string (e.g. "SSTORE"), non-legacy uses a byte. +// non-legacy has an 'opName' field containing the op name. +// +// gas, gasCost: +// Legacy uses integers, non-legacy hex-strings +// +// memory: +// Legacy uses a list of 64-char strings, each representing 32-byte chunks +// of evm memory. Non-legacy just uses a string of hexdata, no chunking. +// +// storage: +// Legacy has a storage field while non-legacy doesn't. +type structLogLegacy struct { + Pc uint64 `json:"pc"` + Op string `json:"op"` + Gas uint64 `json:"gas"` + GasCost uint64 `json:"gasCost"` + Depth int `json:"depth"` + Error string `json:"error,omitempty"` + Stack *[]string `json:"stack,omitempty"` + ReturnData string `json:"returnData,omitempty"` + Memory *[]string `json:"memory,omitempty"` + Storage *map[string]string `json:"storage,omitempty"` + RefundCounter uint64 `json:"refund,omitempty"` +} + +// toLegacyJSON converts the structLog to legacy json-encoded legacy form. +func (s *StructLog) toLegacyJSON() json.RawMessage { + msg := structLogLegacy{ + Pc: s.Pc, + Op: s.Op.String(), + Gas: s.Gas, + GasCost: s.GasCost, + Depth: s.Depth, + Error: s.ErrorString(), + RefundCounter: s.RefundCounter, + } + if s.Stack != nil { + stack := make([]string, len(s.Stack)) + for i, stackValue := range s.Stack { + stack[i] = stackValue.Hex() + } + msg.Stack = &stack + } + if len(s.ReturnData) > 0 { + msg.ReturnData = hexutil.Bytes(s.ReturnData).String() + } + if s.Memory != nil { + memory := make([]string, 0, (len(s.Memory)+31)/32) + for i := 0; i+32 <= len(s.Memory); i += 32 { + memory = append(memory, fmt.Sprintf("%x", s.Memory[i:i+32])) + } + msg.Memory = &memory + } + if s.Storage != nil { + storage := make(map[string]string) + for i, storageValue := range s.Storage { + storage[fmt.Sprintf("%x", i)] = fmt.Sprintf("%x", storageValue) + } + msg.Storage = &storage + } + element, _ := json.Marshal(msg) + return element +} + // StructLogger is an EVM state logger and implements EVMLogger. // // StructLogger can capture state based on the given Log configuration and also keeps // a track record of modified storage which is used in reporting snapshots of the // contract their storage. +// +// A StructLogger can either yield it's output immediately (streaming) or store for +// later output. type StructLogger struct { cfg Config env *tracing.VMContext storage map[common.Address]Storage - logs []StructLog output []byte err error usedGas uint64 + writer io.Writer // If set, the logger will stream instead of store logs + logs []json.RawMessage // buffer of json-encoded logs + resultSize int + interrupt atomic.Bool // Atomic flag to signal execution interruption reason error // Textual reason for the interruption } -// NewStructLogger returns a new logger +// NewStreamingStructLogger returns a new streaming logger. +func NewStreamingStructLogger(cfg *Config, writer io.Writer) *StructLogger { + l := NewStructLogger(cfg) + l.writer = writer + return l +} + +// NewStructLogger construct a new (non-streaming) struct logger. func NewStructLogger(cfg *Config) *StructLogger { logger := &StructLogger{ storage: make(map[common.Address]Storage), + logs: make([]json.RawMessage, 0), } if cfg != nil { logger.cfg = *cfg @@ -142,44 +248,36 @@ func (l *StructLogger) Hooks() *tracing.Hooks { } } -// Reset clears the data held by the logger. -func (l *StructLogger) Reset() { - l.storage = make(map[common.Address]Storage) - l.output = make([]byte, 0) - l.logs = l.logs[:0] - l.err = nil -} - // OnOpcode logs a new structured log message and pushes it out to the environment // // OnOpcode also tracks SLOAD/SSTORE ops to track storage change. func (l *StructLogger) OnOpcode(pc uint64, opcode byte, gas, cost uint64, scope tracing.OpContext, rData []byte, depth int, err error) { - // If tracing was interrupted, set the error and stop + // If tracing was interrupted, exit if l.interrupt.Load() { return } - // check if already accumulated the specified number of logs - if l.cfg.Limit != 0 && l.cfg.Limit <= len(l.logs) { + // check if already accumulated the size of the response. + if l.cfg.Limit != 0 && l.resultSize > l.cfg.Limit { return } - - op := vm.OpCode(opcode) - memory := scope.MemoryData() - stack := scope.StackData() - // Copy a snapshot of the current memory state to a new buffer - var mem []byte + var ( + op = vm.OpCode(opcode) + memory = scope.MemoryData() + contractAddr = scope.Address() + stack = scope.StackData() + stackLen = len(stack) + ) + log := StructLog{pc, op, gas, cost, nil, len(memory), nil, nil, nil, depth, l.env.StateDB.GetRefund(), err} if l.cfg.EnableMemory { - mem = make([]byte, len(memory)) - copy(mem, memory) + log.Memory = memory } - // Copy a snapshot of the current stack state to a new buffer - var stck []uint256.Int if !l.cfg.DisableStack { - stck = make([]uint256.Int, len(stack)) - copy(stck, stack) + log.Stack = scope.StackData() } - contractAddr := scope.Address() - stackLen := len(stack) + if l.cfg.EnableReturnData { + log.ReturnData = rData + } + // Copy a snapshot of the current storage to a new container var storage Storage if !l.cfg.DisableStorage && (op == vm.SLOAD || op == vm.SSTORE) { @@ -195,7 +293,7 @@ func (l *StructLogger) OnOpcode(pc uint64, opcode byte, gas, cost uint64, scope value = l.env.StateDB.GetState(contractAddr, address) ) l.storage[contractAddr][address] = value - storage = l.storage[contractAddr].Copy() + storage = maps.Clone(l.storage[contractAddr]) } else if op == vm.SSTORE && stackLen >= 2 { // capture SSTORE opcodes and record the written entry in the local storage. var ( @@ -203,17 +301,19 @@ func (l *StructLogger) OnOpcode(pc uint64, opcode byte, gas, cost uint64, scope address = common.Hash(stack[stackLen-1].Bytes32()) ) l.storage[contractAddr][address] = value - storage = l.storage[contractAddr].Copy() + storage = maps.Clone(l.storage[contractAddr]) } } - var rdata []byte - if l.cfg.EnableReturnData { - rdata = make([]byte, len(rData)) - copy(rdata, rData) + log.Storage = storage + + // create a log + if l.writer == nil { + entry := log.toLegacyJSON() + l.resultSize += len(entry) + l.logs = append(l.logs, entry) + return } - // create a new snapshot of the EVM. - log := StructLog{pc, op, gas, cost, mem, len(memory), stck, rdata, storage, depth, l.env.StateDB.GetRefund(), err} - l.logs = append(l.logs, log) + log.WriteTo(l.writer) } // OnExit is called a call frame finishes processing. @@ -223,12 +323,13 @@ func (l *StructLogger) OnExit(depth int, output []byte, gasUsed uint64, err erro } l.output = output l.err = err - if l.cfg.Debug { - fmt.Printf("%#x\n", output) - if err != nil { - fmt.Printf(" error: %v\n", err) - } - } + // TODO @holiman, should we output the per-scope output? + //if l.cfg.Debug { + // fmt.Printf("%#x\n", output) + // if err != nil { + // fmt.Printf(" error: %v\n", err) + // } + //} } func (l *StructLogger) GetResult() (json.RawMessage, error) { @@ -247,7 +348,7 @@ func (l *StructLogger) GetResult() (json.RawMessage, error) { Gas: l.usedGas, Failed: failed, ReturnValue: returnVal, - StructLogs: formatLogs(l.StructLogs()), + StructLogs: l.logs, }) } @@ -278,9 +379,6 @@ func (l *StructLogger) OnSystemTxEnd(intrinsicGas uint64) { l.usedGas -= intrinsicGas } -// StructLogs returns the captured log entries. -func (l *StructLogger) StructLogs() []StructLog { return l.logs } - // Error returns the VM error captured by the trace. func (l *StructLogger) Error() error { return l.err } @@ -288,49 +386,10 @@ func (l *StructLogger) Error() error { return l.err } func (l *StructLogger) Output() []byte { return l.output } // WriteTrace writes a formatted trace to the given writer +// @deprecated func WriteTrace(writer io.Writer, logs []StructLog) { for _, log := range logs { - fmt.Fprintf(writer, "%-16spc=%08d gas=%v cost=%v", log.Op, log.Pc, log.Gas, log.GasCost) - if log.Err != nil { - fmt.Fprintf(writer, " ERROR: %v", log.Err) - } - fmt.Fprintln(writer) - - if len(log.Stack) > 0 { - fmt.Fprintln(writer, "Stack:") - for i := len(log.Stack) - 1; i >= 0; i-- { - fmt.Fprintf(writer, "%08d %s\n", len(log.Stack)-i-1, log.Stack[i].Hex()) - } - } - if len(log.Memory) > 0 { - fmt.Fprintln(writer, "Memory:") - fmt.Fprint(writer, hex.Dump(log.Memory)) - } - if len(log.Storage) > 0 { - fmt.Fprintln(writer, "Storage:") - for h, item := range log.Storage { - fmt.Fprintf(writer, "%x: %x\n", h, item) - } - } - if len(log.ReturnData) > 0 { - fmt.Fprintln(writer, "ReturnData:") - fmt.Fprint(writer, hex.Dump(log.ReturnData)) - } - fmt.Fprintln(writer) - } -} - -// WriteLogs writes vm logs in a readable format to the given writer -func WriteLogs(writer io.Writer, logs []*types.Log) { - for _, log := range logs { - fmt.Fprintf(writer, "LOG%d: %x bn=%d txi=%x\n", len(log.Topics), log.Address, log.BlockNumber, log.TxIndex) - - for i, topic := range log.Topics { - fmt.Fprintf(writer, "%08d %x\n", i, topic) - } - - fmt.Fprint(writer, hex.Dump(log.Data)) - fmt.Fprintln(writer) + log.WriteTo(writer) } } @@ -368,26 +427,35 @@ func (t *mdLogger) OnEnter(depth int, typ byte, from common.Address, to common.A if depth != 0 { return } - create := vm.OpCode(typ) == vm.CREATE - if !create { - fmt.Fprintf(t.out, "From: `%v`\nTo: `%v`\nData: `%#x`\nGas: `%d`\nValue `%v` wei\n", - from.String(), to.String(), - input, gas, value) + if create := vm.OpCode(typ) == vm.CREATE; !create { + fmt.Fprintf(t.out, "Pre-execution info:\n"+ + " - from: `%v`\n"+ + " - to: `%v`\n"+ + " - data: `%#x`\n"+ + " - gas: `%d`\n"+ + " - value: `%v` wei\n", + from.String(), to.String(), input, gas, value) } else { - fmt.Fprintf(t.out, "From: `%v`\nCreate at: `%v`\nData: `%#x`\nGas: `%d`\nValue `%v` wei\n", - from.String(), to.String(), - input, gas, value) + fmt.Fprintf(t.out, "Pre-execution info:\n"+ + " - from: `%v`\n"+ + " - create: `%v`\n"+ + " - data: `%#x`\n"+ + " - gas: `%d`\n"+ + " - value: `%v` wei\n", + from.String(), to.String(), input, gas, value) } - fmt.Fprintf(t.out, ` -| Pc | Op | Cost | Stack | RStack | Refund | -|-------|-------------|------|-----------|-----------|---------| +| Pc | Op | Cost | Refund | Stack | +|-------|-------------|------|-----------|-----------| `) } func (t *mdLogger) OnExit(depth int, output []byte, gasUsed uint64, err error, reverted bool) { if depth == 0 { - fmt.Fprintf(t.out, "\nOutput: `%#x`\nConsumed gas: `%d`\nError: `%v`\n", + fmt.Fprintf(t.out, "\nPost-execution info:\n"+ + " - output: `%#x`\n"+ + " - consumed gas: `%d`\n"+ + " - error: `%v`\n", output, gasUsed, err) } } @@ -395,7 +463,8 @@ func (t *mdLogger) OnExit(depth int, output []byte, gasUsed uint64, err error, r // OnOpcode also tracks SLOAD/SSTORE ops to track storage change. func (t *mdLogger) OnOpcode(pc uint64, op byte, gas, cost uint64, scope tracing.OpContext, rData []byte, depth int, err error) { stack := scope.StackData() - fmt.Fprintf(t.out, "| %4d | %10v | %3d |", pc, vm.OpCode(op).String(), cost) + fmt.Fprintf(t.out, "| %4d | %10v | %3d |%10v |", pc, vm.OpCode(op).String(), + cost, t.env.StateDB.GetRefund()) if !t.cfg.DisableStack { // format stack @@ -406,7 +475,6 @@ func (t *mdLogger) OnOpcode(pc uint64, op byte, gas, cost uint64, scope tracing. b := fmt.Sprintf("[%v]", strings.Join(a, ",")) fmt.Fprintf(t.out, "%10v |", b) } - fmt.Fprintf(t.out, "%10v |", t.env.StateDB.GetRefund()) fmt.Fprintln(t.out, "") if err != nil { fmt.Fprintf(t.out, "Error: %v\n", err) @@ -421,65 +489,8 @@ func (t *mdLogger) OnFault(pc uint64, op byte, gas, cost uint64, scope tracing.O // while replaying a transaction in debug mode as well as transaction // execution status, the amount of gas used and the return value type ExecutionResult struct { - Gas uint64 `json:"gas"` - Failed bool `json:"failed"` - ReturnValue string `json:"returnValue"` - StructLogs []StructLogRes `json:"structLogs"` -} - -// StructLogRes stores a structured log emitted by the EVM while replaying a -// transaction in debug mode -type StructLogRes struct { - Pc uint64 `json:"pc"` - Op string `json:"op"` - Gas uint64 `json:"gas"` - GasCost uint64 `json:"gasCost"` - Depth int `json:"depth"` - Error string `json:"error,omitempty"` - Stack *[]string `json:"stack,omitempty"` - ReturnData string `json:"returnData,omitempty"` - Memory *[]string `json:"memory,omitempty"` - Storage *map[string]string `json:"storage,omitempty"` - RefundCounter uint64 `json:"refund,omitempty"` -} - -// formatLogs formats EVM returned structured logs for json output -func formatLogs(logs []StructLog) []StructLogRes { - formatted := make([]StructLogRes, len(logs)) - for index, trace := range logs { - formatted[index] = StructLogRes{ - Pc: trace.Pc, - Op: trace.Op.String(), - Gas: trace.Gas, - GasCost: trace.GasCost, - Depth: trace.Depth, - Error: trace.ErrorString(), - RefundCounter: trace.RefundCounter, - } - if trace.Stack != nil { - stack := make([]string, len(trace.Stack)) - for i, stackValue := range trace.Stack { - stack[i] = stackValue.Hex() - } - formatted[index].Stack = &stack - } - if len(trace.ReturnData) > 0 { - formatted[index].ReturnData = hexutil.Bytes(trace.ReturnData).String() - } - if trace.Memory != nil { - memory := make([]string, 0, (len(trace.Memory)+31)/32) - for i := 0; i+32 <= len(trace.Memory); i += 32 { - memory = append(memory, fmt.Sprintf("%x", trace.Memory[i:i+32])) - } - formatted[index].Memory = &memory - } - if trace.Storage != nil { - storage := make(map[string]string) - for i, storageValue := range trace.Storage { - storage[fmt.Sprintf("%x", i)] = fmt.Sprintf("%x", storageValue) - } - formatted[index].Storage = &storage - } - } - return formatted + Gas uint64 `json:"gas"` + Failed bool `json:"failed"` + ReturnValue string `json:"returnValue"` + StructLogs []json.RawMessage `json:"structLogs"` } diff --git a/eth/tracers/logger/logger_json.go b/eth/tracers/logger/logger_json.go index 797f7ac658..52ac3945d4 100644 --- a/eth/tracers/logger/logger_json.go +++ b/eth/tracers/logger/logger_json.go @@ -71,7 +71,7 @@ func NewJSONLogger(cfg *Config, writer io.Writer) *tracing.Hooks { l.hooks = &tracing.Hooks{ OnTxStart: l.OnTxStart, OnSystemCallStart: l.onSystemCallStart, - OnExit: l.OnEnd, + OnExit: l.OnExit, OnOpcode: l.OnOpcode, OnFault: l.OnFault, } @@ -152,13 +152,6 @@ func (l *jsonLogger) OnEnter(depth int, typ byte, from common.Address, to common l.encoder.Encode(frame) } -func (l *jsonLogger) OnEnd(depth int, output []byte, gasUsed uint64, err error, reverted bool) { - if depth > 0 { - return - } - l.OnExit(depth, output, gasUsed, err, false) -} - func (l *jsonLogger) OnExit(depth int, output []byte, gasUsed uint64, err error, reverted bool) { type endLog struct { Output string `json:"output"` diff --git a/eth/tracers/logger/logger_test.go b/eth/tracers/logger/logger_test.go index fb1a0154e3..e7799dde35 100644 --- a/eth/tracers/logger/logger_test.go +++ b/eth/tracers/logger/logger_test.go @@ -58,13 +58,13 @@ func (*dummyStatedb) SetState(_ common.Address, _ common.Hash, _ common.Hash) co func TestStoreCapture(t *testing.T) { var ( logger = NewStructLogger(nil) - env = vm.NewEVM(vm.BlockContext{}, vm.TxContext{}, &dummyStatedb{}, params.TestChainConfig, vm.Config{Tracer: logger.Hooks()}) + evm = vm.NewEVM(vm.BlockContext{}, &dummyStatedb{}, params.TestChainConfig, vm.Config{Tracer: logger.Hooks()}) contract = vm.NewContract(&dummyContractRef{}, &dummyContractRef{}, new(uint256.Int), 100000) ) contract.Code = []byte{byte(vm.PUSH1), 0x1, byte(vm.PUSH1), 0x0, byte(vm.SSTORE)} var index common.Hash - logger.OnTxStart(env.GetVMContext(), nil, common.Address{}) - _, err := env.Interpreter().Run(contract, []byte{}, false) + logger.OnTxStart(evm.GetVMContext(), nil, common.Address{}) + _, err := evm.Interpreter().Run(contract, []byte{}, false) if err != nil { t.Fatal(err) } diff --git a/eth/tracers/tracers_test.go b/eth/tracers/tracers_test.go index 31e14b9112..a72dbf6ee6 100644 --- a/eth/tracers/tracers_test.go +++ b/eth/tracers/tracers_test.go @@ -31,7 +31,7 @@ import ( "github.com/ethereum/go-ethereum/tests" ) -func BenchmarkTransactionTrace(b *testing.B) { +func BenchmarkTransactionTraceV2(b *testing.B) { key, _ := crypto.HexToECDSA("b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291") from := crypto.PubkeyToAddress(key.PublicKey) gas := uint64(1000000) // 1M gas @@ -47,10 +47,6 @@ func BenchmarkTransactionTrace(b *testing.B) { if err != nil { b.Fatal(err) } - txContext := vm.TxContext{ - Origin: from, - GasPrice: tx.GasPrice(), - } context := vm.BlockContext{ CanTransfer: core.CanTransfer, Transfer: core.Transfer, @@ -82,14 +78,8 @@ func BenchmarkTransactionTrace(b *testing.B) { state := tests.MakePreState(rawdb.NewMemoryDatabase(), alloc, false, rawdb.HashScheme) defer state.Close() - // Create the tracer, the EVM environment and run it - tracer := logger.NewStructLogger(&logger.Config{ - Debug: false, - //DisableStorage: true, - //EnableMemory: false, - //EnableReturnData: false, - }) - evm := vm.NewEVM(context, txContext, state.StateDB, params.AllEthashProtocolChanges, vm.Config{Tracer: tracer.Hooks()}) + evm := vm.NewEVM(context, state.StateDB, params.AllEthashProtocolChanges, vm.Config{}) + msg, err := core.TransactionToMessage(tx, signer, context.BaseFee) if err != nil { b.Fatalf("failed to prepare transaction for tracing: %v", err) @@ -98,18 +88,15 @@ func BenchmarkTransactionTrace(b *testing.B) { b.ReportAllocs() for i := 0; i < b.N; i++ { - snap := state.StateDB.Snapshot() + tracer := logger.NewStructLogger(&logger.Config{}).Hooks() tracer.OnTxStart(evm.GetVMContext(), tx, msg.From) - st := core.NewStateTransition(evm, msg, new(core.GasPool).AddGas(tx.Gas())) - res, err := st.TransitionDb() + evm.Config.Tracer = tracer + + snap := state.StateDB.Snapshot() + _, err := core.ApplyMessage(evm, msg, new(core.GasPool).AddGas(tx.Gas())) if err != nil { b.Fatal(err) } - tracer.OnTxEnd(&types.Receipt{GasUsed: res.UsedGas}, nil) state.StateDB.RevertToSnapshot(snap) - if have, want := len(tracer.StructLogs()), 244752; have != want { - b.Fatalf("trace wrong, want %d steps, have %d", want, have) - } - tracer.Reset() } } diff --git a/ethclient/simulated/backend.go b/ethclient/simulated/backend.go index a716548b52..4aa1cc5151 100644 --- a/ethclient/simulated/backend.go +++ b/ethclient/simulated/backend.go @@ -27,7 +27,6 @@ import ( "github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/eth" "github.com/ethereum/go-ethereum/eth/catalyst" - "github.com/ethereum/go-ethereum/eth/downloader" "github.com/ethereum/go-ethereum/eth/ethconfig" "github.com/ethereum/go-ethereum/eth/filters" "github.com/ethereum/go-ethereum/ethclient" @@ -92,7 +91,7 @@ func NewBackend(alloc types.GenesisAlloc, options ...func(nodeConf *node.Config, GasLimit: ethconfig.Defaults.Miner.GasCeil, Alloc: alloc, } - ethConf.SyncMode = downloader.FullSync + ethConf.SyncMode = ethconfig.FullSync ethConf.TxPool.NoLocals = true for _, option := range options { diff --git a/ethdb/leveldb/leveldb.go b/ethdb/leveldb/leveldb.go index ce7d823561..f18503c941 100644 --- a/ethdb/leveldb/leveldb.go +++ b/ethdb/leveldb/leveldb.go @@ -62,21 +62,21 @@ type Database struct { fn string // filename for reporting db *leveldb.DB // LevelDB instance - compTimeMeter metrics.Meter // Meter for measuring the total time spent in database compaction - compReadMeter metrics.Meter // Meter for measuring the data read during compaction - compWriteMeter metrics.Meter // Meter for measuring the data written during compaction - writeDelayNMeter metrics.Meter // Meter for measuring the write delay number due to database compaction - writeDelayMeter metrics.Meter // Meter for measuring the write delay duration due to database compaction - diskSizeGauge metrics.Gauge // Gauge for tracking the size of all the levels in the database - diskReadMeter metrics.Meter // Meter for measuring the effective amount of data read - diskWriteMeter metrics.Meter // Meter for measuring the effective amount of data written - memCompGauge metrics.Gauge // Gauge for tracking the number of memory compaction - level0CompGauge metrics.Gauge // Gauge for tracking the number of table compaction in level0 - nonlevel0CompGauge metrics.Gauge // Gauge for tracking the number of table compaction in non0 level - seekCompGauge metrics.Gauge // Gauge for tracking the number of table compaction caused by read opt - manualMemAllocGauge metrics.Gauge // Gauge to track the amount of memory that has been manually allocated (not a part of runtime/GC) - - levelsGauge []metrics.Gauge // Gauge for tracking the number of tables in levels + compTimeMeter *metrics.Meter // Meter for measuring the total time spent in database compaction + compReadMeter *metrics.Meter // Meter for measuring the data read during compaction + compWriteMeter *metrics.Meter // Meter for measuring the data written during compaction + writeDelayNMeter *metrics.Meter // Meter for measuring the write delay number due to database compaction + writeDelayMeter *metrics.Meter // Meter for measuring the write delay duration due to database compaction + diskSizeGauge *metrics.Gauge // Gauge for tracking the size of all the levels in the database + diskReadMeter *metrics.Meter // Meter for measuring the effective amount of data read + diskWriteMeter *metrics.Meter // Meter for measuring the effective amount of data written + memCompGauge *metrics.Gauge // Gauge for tracking the number of memory compaction + level0CompGauge *metrics.Gauge // Gauge for tracking the number of table compaction in level0 + nonlevel0CompGauge *metrics.Gauge // Gauge for tracking the number of table compaction in non0 level + seekCompGauge *metrics.Gauge // Gauge for tracking the number of table compaction caused by read opt + manualMemAllocGauge *metrics.Gauge // Gauge to track the amount of memory that has been manually allocated (not a part of runtime/GC) + + levelsGauge []*metrics.Gauge // Gauge for tracking the number of tables in levels quitLock sync.Mutex // Mutex protecting the quit channel access quitChan chan chan error // Quit channel to stop the metrics collection before closing the database diff --git a/ethdb/pebble/pebble.go b/ethdb/pebble/pebble.go index 28957582be..0287446a7d 100644 --- a/ethdb/pebble/pebble.go +++ b/ethdb/pebble/pebble.go @@ -60,21 +60,21 @@ type Database struct { fn string // filename for reporting db *pebble.DB // Underlying pebble storage engine - compTimeMeter metrics.Meter // Meter for measuring the total time spent in database compaction - compReadMeter metrics.Meter // Meter for measuring the data read during compaction - compWriteMeter metrics.Meter // Meter for measuring the data written during compaction - writeDelayNMeter metrics.Meter // Meter for measuring the write delay number due to database compaction - writeDelayMeter metrics.Meter // Meter for measuring the write delay duration due to database compaction - diskSizeGauge metrics.Gauge // Gauge for tracking the size of all the levels in the database - diskReadMeter metrics.Meter // Meter for measuring the effective amount of data read - diskWriteMeter metrics.Meter // Meter for measuring the effective amount of data written - memCompGauge metrics.Gauge // Gauge for tracking the number of memory compaction - level0CompGauge metrics.Gauge // Gauge for tracking the number of table compaction in level0 - nonlevel0CompGauge metrics.Gauge // Gauge for tracking the number of table compaction in non0 level - seekCompGauge metrics.Gauge // Gauge for tracking the number of table compaction caused by read opt - manualMemAllocGauge metrics.Gauge // Gauge for tracking amount of non-managed memory currently allocated - - levelsGauge []metrics.Gauge // Gauge for tracking the number of tables in levels + compTimeMeter *metrics.Meter // Meter for measuring the total time spent in database compaction + compReadMeter *metrics.Meter // Meter for measuring the data read during compaction + compWriteMeter *metrics.Meter // Meter for measuring the data written during compaction + writeDelayNMeter *metrics.Meter // Meter for measuring the write delay number due to database compaction + writeDelayMeter *metrics.Meter // Meter for measuring the write delay duration due to database compaction + diskSizeGauge *metrics.Gauge // Gauge for tracking the size of all the levels in the database + diskReadMeter *metrics.Meter // Meter for measuring the effective amount of data read + diskWriteMeter *metrics.Meter // Meter for measuring the effective amount of data written + memCompGauge *metrics.Gauge // Gauge for tracking the number of memory compaction + level0CompGauge *metrics.Gauge // Gauge for tracking the number of table compaction in level0 + nonlevel0CompGauge *metrics.Gauge // Gauge for tracking the number of table compaction in non0 level + seekCompGauge *metrics.Gauge // Gauge for tracking the number of table compaction caused by read opt + manualMemAllocGauge *metrics.Gauge // Gauge for tracking amount of non-managed memory currently allocated + + levelsGauge []*metrics.Gauge // Gauge for tracking the number of tables in levels quitLock sync.RWMutex // Mutex protecting the quit channel and the closed flag quitChan chan chan error // Quit channel to stop the metrics collection before closing the database diff --git a/go.mod b/go.mod index 8451cec367..667d89c890 100644 --- a/go.mod +++ b/go.mod @@ -26,7 +26,7 @@ require ( github.com/decred/dcrd/dcrec/secp256k1/v4 v4.2.0 github.com/dop251/goja v0.0.0-20230806174421-c933cf95e127 github.com/ethereum/c-kzg-4844 v1.0.0 - github.com/ethereum/go-verkle v0.1.1-0.20240829091221-dffa7562dbe9 + github.com/ethereum/go-verkle v0.2.2 github.com/fatih/color v1.16.0 github.com/fatih/structs v1.1.0 github.com/fjl/gencodec v0.0.0-20230517082657-f9840df7b83e @@ -80,13 +80,13 @@ require ( github.com/wealdtech/go-eth2-wallet-encryptor-keystorev4 v1.1.3 github.com/willf/bitset v1.1.3 go.uber.org/automaxprocs v1.5.2 - golang.org/x/crypto v0.22.0 + golang.org/x/crypto v0.26.0 golang.org/x/exp v0.0.0-20240213143201-ec583247a57a - golang.org/x/sync v0.7.0 - golang.org/x/sys v0.22.0 - golang.org/x/text v0.14.0 + golang.org/x/sync v0.10.0 + golang.org/x/sys v0.28.0 + golang.org/x/text v0.17.0 golang.org/x/time v0.5.0 - golang.org/x/tools v0.20.0 + golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d google.golang.org/protobuf v1.34.2 gopkg.in/natefinch/lumberjack.v2 v2.2.1 gopkg.in/yaml.v3 v3.0.1 @@ -112,7 +112,7 @@ require ( github.com/aws/smithy-go v1.15.0 // indirect github.com/benbjohnson/clock v1.3.5 // indirect github.com/beorn7/perks v1.0.1 // indirect - github.com/bits-and-blooms/bitset v1.13.0 // indirect + github.com/bits-and-blooms/bitset v1.17.0 // indirect github.com/btcsuite/btcd/btcec/v2 v2.3.2 // indirect github.com/cespare/xxhash v1.1.0 // indirect github.com/cespare/xxhash/v2 v2.3.0 // indirect @@ -122,7 +122,7 @@ require ( github.com/cockroachdb/logtags v0.0.0-20230118201751-21c54148d20b // indirect github.com/cockroachdb/redact v1.1.5 // indirect github.com/cockroachdb/tokenbucket v0.0.0-20230807174530-cc333fc44b06 // indirect - github.com/consensys/bavard v0.1.13 // indirect + github.com/consensys/bavard v0.1.22 // indirect github.com/containerd/cgroups v1.1.0 // indirect github.com/coreos/go-systemd/v22 v22.5.0 // indirect github.com/cosmos/gogoproto v1.4.1 // indirect @@ -246,7 +246,7 @@ require ( github.com/raulk/go-watchdog v1.3.0 // indirect github.com/rcrowley/go-metrics v0.0.0-20201227073835-cf1acfcdf475 // indirect github.com/rivo/uniseg v0.4.4 // indirect - github.com/rogpeppe/go-internal v1.11.0 // indirect + github.com/rogpeppe/go-internal v1.12.0 // indirect github.com/russross/blackfriday/v2 v2.1.0 // indirect github.com/sasha-s/go-deadlock v0.3.1 // indirect github.com/schollz/progressbar/v3 v3.3.4 // indirect @@ -277,9 +277,9 @@ require ( go.uber.org/multierr v1.11.0 // indirect go.uber.org/zap v1.27.0 // indirect golang.org/x/mod v0.17.0 // indirect - golang.org/x/net v0.24.0 // indirect + golang.org/x/net v0.25.0 // indirect golang.org/x/oauth2 v0.16.0 // indirect - golang.org/x/term v0.19.0 // indirect + golang.org/x/term v0.23.0 // indirect google.golang.org/api v0.44.0 // indirect google.golang.org/appengine v1.6.7 // indirect google.golang.org/genproto v0.0.0-20231016165738-49dd2c1f3d0b // indirect diff --git a/go.sum b/go.sum index 0ecc0fd83f..18975c923b 100644 --- a/go.sum +++ b/go.sum @@ -157,8 +157,8 @@ github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= github.com/bgentry/go-netrc v0.0.0-20140422174119-9fd32a8b3d3d/go.mod h1:6QX/PXZ00z/TKoufEY6K/a0k6AhaJrQKdFe6OfVXsa4= github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs= -github.com/bits-and-blooms/bitset v1.13.0 h1:bAQ9OPNFYbGHV6Nez0tmNI0RiEu7/hxlYJRUA0wFAVE= -github.com/bits-and-blooms/bitset v1.13.0/go.mod h1:7hO7Gc7Pp1vODcmWvKMRA9BNmbv6a/7QIWpPxHddWR8= +github.com/bits-and-blooms/bitset v1.17.0 h1:1X2TS7aHz1ELcC0yU1y2stUs/0ig5oMU6STFZGrhvHI= +github.com/bits-and-blooms/bitset v1.17.0/go.mod h1:7hO7Gc7Pp1vODcmWvKMRA9BNmbv6a/7QIWpPxHddWR8= github.com/bketelsen/crypt v0.0.3-0.20200106085610-5cbc8cc4026c/go.mod h1:MKsuJmJgSg28kpZDP6UIiPt0e0Oz0kqKNGyRaWEPv84= github.com/bnb-chain/fastssz v0.1.2 h1:vTcXw5SwCtRYnl/BEclujiml7GXiVOZ74tub4GHpvlM= github.com/bnb-chain/fastssz v0.1.2/go.mod h1:KcabV+OEw2QwgyY8Fc88ZG79CKYkFdu0kKWyfA3dI6o= @@ -226,8 +226,8 @@ github.com/cockroachdb/tokenbucket v0.0.0-20230807174530-cc333fc44b06/go.mod h1: github.com/codahale/hdrhistogram v0.0.0-20161010025455-3a0bb77429bd/go.mod h1:sE/e/2PUdi/liOCUjSTXgM1o87ZssimdTWN964YiIeI= github.com/cometbft/cometbft-db v0.7.0 h1:uBjbrBx4QzU0zOEnU8KxoDl18dMNgDh+zZRUE0ucsbo= github.com/cometbft/cometbft-db v0.7.0/go.mod h1:yiKJIm2WKrt6x8Cyxtq9YTEcIMPcEe4XPxhgX59Fzf0= -github.com/consensys/bavard v0.1.13 h1:oLhMLOFGTLdlda/kma4VOJazblc7IM5y5QPd2A/YjhQ= -github.com/consensys/bavard v0.1.13/go.mod h1:9ItSMtA/dXMAiL7BG6bqW2m3NdSEObYWoH223nGHukI= +github.com/consensys/bavard v0.1.22 h1:Uw2CGvbXSZWhqK59X0VG/zOjpTFuOMcPLStrp1ihI0A= +github.com/consensys/bavard v0.1.22/go.mod h1:k/zVjHHC4B+PQy1Pg7fgvG3ALicQw540Crag8qx+dZs= github.com/consensys/gnark-crypto v0.12.1 h1:lHH39WuuFgVHONRl3J0LRBtuYdQTumFSDtJF7HpyG8M= github.com/consensys/gnark-crypto v0.12.1/go.mod h1:v2Gy7L/4ZRosZ7Ivs+9SfUDr0f5UlG+EM5t7MPHiLuY= github.com/containerd/cgroups v0.0.0-20201119153540-4cbc285b3327/go.mod h1:ZJeTFisyysqgcCdecO57Dj79RfL0LNeGiFUqLYQRYLE= @@ -327,8 +327,8 @@ github.com/etcd-io/bbolt v1.3.3 h1:gSJmxrs37LgTqR/oyJBWok6k6SvXEUerFTbltIhXkBM= github.com/etcd-io/bbolt v1.3.3/go.mod h1:ZF2nL25h33cCyBtcyWeZ2/I3HQOfTP+0PIEvHjkjCrw= github.com/ethereum/c-kzg-4844 v1.0.0 h1:0X1LBXxaEtYD9xsyj9B9ctQEZIpnvVDeoBx8aHEwTNA= github.com/ethereum/c-kzg-4844 v1.0.0/go.mod h1:VewdlzQmpT5QSrVhbBuGoCdFJkpaJlO1aQputP83wc0= -github.com/ethereum/go-verkle v0.1.1-0.20240829091221-dffa7562dbe9 h1:8NfxH2iXvJ60YRB8ChToFTUzl8awsc3cJ8CbLjGIl/A= -github.com/ethereum/go-verkle v0.1.1-0.20240829091221-dffa7562dbe9/go.mod h1:M3b90YRnzqKyyzBEWJGqj8Qff4IDeXnzFw0P9bFw3uk= +github.com/ethereum/go-verkle v0.2.2 h1:I2W0WjnrFUIzzVPwm8ykY+7pL2d4VhlsePn4j7cnFk8= +github.com/ethereum/go-verkle v0.2.2/go.mod h1:M3b90YRnzqKyyzBEWJGqj8Qff4IDeXnzFw0P9bFw3uk= github.com/evanphx/json-patch v4.9.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk= github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4= github.com/fatih/color v1.16.0 h1:zmkK9Ngbjj+K0yRhTVONQh1p/HknKYSlNT+vZCzyokM= @@ -1045,8 +1045,8 @@ github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6L github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= github.com/rogpeppe/go-internal v1.6.1/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc= github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs= -github.com/rogpeppe/go-internal v1.11.0 h1:cWPaGQEPrBb5/AsnsZesgZZ9yb1OQ+GOISoDNXVBh4M= -github.com/rogpeppe/go-internal v1.11.0/go.mod h1:ddIwULY96R17DhadqLgMfk9H9tvdUzkipdSkR5nkCZA= +github.com/rogpeppe/go-internal v1.12.0 h1:exVL4IDcn6na9z1rAb56Vxr+CgyK3nn3O+epU5NdKM8= +github.com/rogpeppe/go-internal v1.12.0/go.mod h1:E+RYuTGaKKdloAfM02xzb0FW3Paa99yedzYV+kq4uf4= github.com/rs/cors v1.8.2 h1:KCooALfAYGs415Cwu5ABvv9n9509fSiG5SQJn/AQo4U= github.com/rs/cors v1.8.2/go.mod h1:XyqrcTp5zjWr1wsJ8PIRZssZ8b/WMcMf71DJnit4EMU= github.com/russross/blackfriday v1.5.2/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g= @@ -1291,8 +1291,8 @@ golang.org/x/crypto v0.0.0-20210322153248-0c34fe9e7dc2/go.mod h1:T9bdIzuCu7OtxOm golang.org/x/crypto v0.0.0-20210421170649-83a5a9bb288b/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4= golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= golang.org/x/crypto v0.0.0-20220722155217-630584e8d5aa/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= -golang.org/x/crypto v0.22.0 h1:g1v0xeRhjcugydODzvb3mEM9SQ0HGp9s/nh3COQ/C30= -golang.org/x/crypto v0.22.0/go.mod h1:vr6Su+7cTlO45qkww3VDJlzDn0ctJvRgYbC2NvXHt+M= +golang.org/x/crypto v0.26.0 h1:RrRspgV4mU+YwB4FYnuBoKsUapNIL5cohGAmSH3azsw= +golang.org/x/crypto v0.26.0/go.mod h1:GY7jblb9wI+FOo5y8/S2oY4zWP07AkOJ4+jxCqdqn54= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8= @@ -1384,8 +1384,8 @@ golang.org/x/net v0.0.0-20210316092652-d523dce5a7f4/go.mod h1:RBQZq4jEuRlivfhVLd golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM= golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= -golang.org/x/net v0.24.0 h1:1PcaxkF854Fu3+lvBIx5SYn9wRlBzzcnHZSiaFFAb0w= -golang.org/x/net v0.24.0/go.mod h1:2Q7sJY5mzlzWjKtYUEXSlBWCdyaioyXzRB2RtU8KVE8= +golang.org/x/net v0.25.0 h1:d/OCCoBEUq33pjydKrGQhw7IlUPI2Oylr+8qLx49kac= +golang.org/x/net v0.25.0/go.mod h1:JkAGAh7GEvH74S6FOH42FLoXpXbE/aqXSrIQjXgsiwM= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20181017192945-9dcd33a902f4/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20181203162652-d668ce993890/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= @@ -1416,8 +1416,8 @@ golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJ golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.7.0 h1:YsImfSBoP9QPYL0xyKJPq0gcaJdG3rInoqxTWbfQu9M= -golang.org/x/sync v0.7.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= +golang.org/x/sync v0.10.0 h1:3NQrjDixjgGwUOCaF8w2+VYHv0Ve/vGYSbdkTa98gmQ= +golang.org/x/sync v0.10.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= golang.org/x/sys v0.0.0-20180810173357-98c5dad5d1a0/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= @@ -1508,13 +1508,13 @@ golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.14.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= -golang.org/x/sys v0.22.0 h1:RI27ohtqKCnwULzJLqkv897zojh5/DwS/ENaMzUOaWI= -golang.org/x/sys v0.22.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/sys v0.28.0 h1:Fksou7UEQUWlKvIdsqzJmUmCX3cZuD2+P3XyyzwMhlA= +golang.org/x/sys v0.28.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= -golang.org/x/term v0.19.0 h1:+ThwsDv+tYfnJFhF4L8jITxu1tdTWRTZpdsWgEgjL6Q= -golang.org/x/term v0.19.0/go.mod h1:2CuTdWZ7KHSQwUzKva0cbMg6q2DMI3Mmxp+gKJbskEk= +golang.org/x/term v0.23.0 h1:F6D4vR+EHoL9/sWAWgAR1H2DcHr4PareCbAaCo1RpuU= +golang.org/x/term v0.23.0/go.mod h1:DgV24QBUrK6jhZXl+20l6UWznPlwAHm1Q1mGHtydmSk= golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= @@ -1525,8 +1525,8 @@ golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= golang.org/x/text v0.3.8/go.mod h1:E6s5w1FMmriuDzIBO73fBruAKo1PCIq6d2Q6DHfQ8WQ= -golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ= -golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= +golang.org/x/text v0.17.0 h1:XtiM5bkSOt+ewxlOE/aE/AKEHibwj/6gvWMl9Rsh0Qc= +golang.org/x/text v0.17.0/go.mod h1:BuEKDfySbSR4drPmRPG/7iBdf8hvFMuRexcpahXilzY= golang.org/x/time v0.0.0-20180412165947-fbb02b2291d2/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= @@ -1600,8 +1600,8 @@ golang.org/x/tools v0.0.0-20210108195828-e2f9c7f1fc8e/go.mod h1:emZCQorbCU4vsT4f golang.org/x/tools v0.1.0/go.mod h1:xkSsbof2nBLbhDlRMhhhyNLN/zl3eTqcnHD5viDpcZ0= golang.org/x/tools v0.1.5/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= -golang.org/x/tools v0.20.0 h1:hz/CVckiOxybQvFw6h7b/q80NTr9IUQb4s1IIzW7KNY= -golang.org/x/tools v0.20.0/go.mod h1:WvitBU7JJf6A4jOdg4S1tviW9bhUxkgeCui/0JHctQg= +golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d h1:vU5i/LfpvrRCpgM/VPfJLg5KjxD3E+hfT1SH+d9zLwg= +golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d/go.mod h1:aiJjzUbINMkxbQROHiO6hDPo2LHcIPhhQsa9DLh0yGk= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= diff --git a/internal/debug/flags.go b/internal/debug/flags.go index 0e05975c7e..adb652d59b 100644 --- a/internal/debug/flags.go +++ b/internal/debug/flags.go @@ -136,8 +136,8 @@ var ( Category: flags.LoggingCategory, } traceFlag = &cli.StringFlag{ - Name: "trace", - Usage: "Write execution trace to the given file", + Name: "go-execution-trace", + Usage: "Write Go execution trace to the given file", Category: flags.LoggingCategory, } ) diff --git a/internal/ethapi/api.go b/internal/ethapi/api.go index 5373dc6411..452ef91f0f 100644 --- a/internal/ethapi/api.go +++ b/internal/ethapi/api.go @@ -21,7 +21,6 @@ import ( "encoding/hex" "errors" "fmt" - "maps" gomath "math" "math/big" "strings" @@ -266,7 +265,7 @@ func (api *TxPoolAPI) Inspect() map[string]map[string]map[string]string { pending, queue := api.b.TxPoolContent() // Define a formatter to flatten a transaction into a string - var format = func(tx *types.Transaction) string { + format := func(tx *types.Transaction) string { if to := tx.To(); to != nil { return fmt.Sprintf("%s: %v wei + %v gas × %v wei", tx.To().Hex(), tx.Value(), tx.Gas(), tx.GasPrice()) } @@ -964,7 +963,7 @@ func doCall(ctx context.Context, b Backend, args TransactionArgs, state *state.S blockOverrides.Apply(&blockCtx) } rules := b.ChainConfig().Rules(blockCtx.BlockNumber, blockCtx.Random != nil, blockCtx.Time) - precompiles := maps.Clone(vm.ActivePrecompiledContracts(rules)) + precompiles := vm.ActivePrecompiledContracts(rules) if err := overrides.Apply(state, precompiles); err != nil { return nil, err } @@ -1003,7 +1002,7 @@ func applyMessage(ctx context.Context, b Backend, args TransactionArgs, state *s if msg.BlobGasFeeCap != nil && msg.BlobGasFeeCap.BitLen() == 0 { blockContext.BlobBaseFee = new(big.Int) } - evm := b.GetEVM(ctx, msg, state, header, vmConfig, blockContext) + evm := b.GetEVM(ctx, state, header, vmConfig, blockContext) if precompiles != nil { evm.SetPrecompiles(precompiles) } @@ -1272,9 +1271,8 @@ func (api *BlockChainAPI) replay(ctx context.Context, block *types.Block, accoun // Apply transaction msg, _ := core.TransactionToMessage(tx, signer, parent.Header().BaseFee) - txContext := core.NewEVMTxContext(msg) context := core.NewEVMBlockContext(block.Header(), api.b.Chain(), nil) - vmenv := vm.NewEVM(context, txContext, statedb, api.b.ChainConfig(), vm.Config{}) + evm := vm.NewEVM(context, statedb, api.b.ChainConfig(), vm.Config{}) if posa, ok := api.b.Engine().(consensus.PoSA); ok { if isSystem, _ := posa.IsSystemTransaction(tx, block.Header()); isSystem { @@ -1286,10 +1284,10 @@ func (api *BlockChainAPI) replay(ctx context.Context, block *types.Block, accoun } } - if _, err := core.ApplyMessage(vmenv, msg, new(core.GasPool).AddGas(tx.Gas())); err != nil { + if _, err := core.ApplyMessage(evm, msg, new(core.GasPool).AddGas(tx.Gas())); err != nil { return nil, nil, fmt.Errorf("transaction %#x failed: %v", tx.Hash(), err) } - statedb.Finalise(vmenv.ChainConfig().IsEIP158(block.Number())) + statedb.Finalise(evm.ChainConfig().IsEIP158(block.Number())) if !skip { // Compute account balance diff. @@ -1657,16 +1655,17 @@ func AccessList(ctx context.Context, b Backend, blockNrOrHash rpc.BlockNumberOrH // Apply the transaction with the access list tracer tracer := logger.NewAccessListTracer(accessList, args.from(), to, precompiles) config := vm.Config{Tracer: tracer.Hooks(), NoBaseFee: true} - vmenv := b.GetEVM(ctx, msg, statedb, header, &config, nil) + evm := b.GetEVM(ctx, statedb, header, &config, nil) + // Lower the basefee to 0 to avoid breaking EVM // invariants (basefee < feecap). if msg.GasPrice.Sign() == 0 { - vmenv.Context.BaseFee = new(big.Int) + evm.Context.BaseFee = new(big.Int) } if msg.BlobGasFeeCap != nil && msg.BlobGasFeeCap.BitLen() == 0 { - vmenv.Context.BlobBaseFee = new(big.Int) + evm.Context.BlobBaseFee = new(big.Int) } - res, err := core.ApplyMessage(vmenv, msg, new(core.GasPool).AddGas(msg.GasLimit)) + res, err := core.ApplyMessage(evm, msg, new(core.GasPool).AddGas(msg.GasLimit)) if err != nil { return nil, 0, nil, fmt.Errorf("failed to apply transaction: %v err: %v", args.ToTransaction(types.LegacyTxType).Hash(), err) } @@ -2257,11 +2256,11 @@ func (api *TransactionAPI) Resend(ctx context.Context, sendArgs TransactionArgs, matchTx := sendArgs.ToTransaction(types.LegacyTxType) // Before replacing the old transaction, ensure the _new_ transaction fee is reasonable. - var price = matchTx.GasPrice() + price := matchTx.GasPrice() if gasPrice != nil { price = gasPrice.ToInt() } - var gas = matchTx.Gas() + gas := matchTx.Gas() if gasLimit != nil { gas = uint64(*gasLimit) } diff --git a/internal/ethapi/api_test.go b/internal/ethapi/api_test.go index e8d02fbabc..35a2e68d45 100644 --- a/internal/ethapi/api_test.go +++ b/internal/ethapi/api_test.go @@ -585,16 +585,15 @@ func (b testBackend) GetTd(ctx context.Context, hash common.Hash) *big.Int { } return big.NewInt(1) } -func (b testBackend) GetEVM(ctx context.Context, msg *core.Message, state *state.StateDB, header *types.Header, vmConfig *vm.Config, blockContext *vm.BlockContext) *vm.EVM { +func (b testBackend) GetEVM(ctx context.Context, state *state.StateDB, header *types.Header, vmConfig *vm.Config, blockContext *vm.BlockContext) *vm.EVM { if vmConfig == nil { vmConfig = b.chain.GetVMConfig() } - txContext := core.NewEVMTxContext(msg) context := core.NewEVMBlockContext(header, b.chain, nil) if blockContext != nil { context = *blockContext } - return vm.NewEVM(context, txContext, state, b.chain.Config(), *vmConfig) + return vm.NewEVM(context, state, b.chain.Config(), *vmConfig) } func (b testBackend) SubscribeChainEvent(ch chan<- core.ChainEvent) event.Subscription { panic("implement me") diff --git a/internal/ethapi/backend.go b/internal/ethapi/backend.go index 6cae960fb5..d362443c58 100644 --- a/internal/ethapi/backend.go +++ b/internal/ethapi/backend.go @@ -70,7 +70,7 @@ type Backend interface { StateAndHeaderByNumberOrHash(ctx context.Context, blockNrOrHash rpc.BlockNumberOrHash) (*state.StateDB, *types.Header, error) Pending() (*types.Block, types.Receipts, *state.StateDB) GetReceipts(ctx context.Context, hash common.Hash) (types.Receipts, error) - GetEVM(ctx context.Context, msg *core.Message, state *state.StateDB, header *types.Header, vmConfig *vm.Config, blockCtx *vm.BlockContext) *vm.EVM + GetEVM(ctx context.Context, state *state.StateDB, header *types.Header, vmConfig *vm.Config, blockCtx *vm.BlockContext) *vm.EVM SubscribeChainEvent(ch chan<- core.ChainEvent) event.Subscription SubscribeChainHeadEvent(ch chan<- core.ChainHeadEvent) event.Subscription GetBlobSidecars(ctx context.Context, hash common.Hash) (types.BlobSidecars, error) diff --git a/internal/ethapi/simulate.go b/internal/ethapi/simulate.go index 81b4633d42..f6647c7ba4 100644 --- a/internal/ethapi/simulate.go +++ b/internal/ethapi/simulate.go @@ -21,7 +21,6 @@ import ( "encoding/json" "errors" "fmt" - "maps" "math/big" "time" @@ -185,12 +184,12 @@ func (sim *simulator) processBlock(ctx context.Context, block *simBlock, header, NoBaseFee: !sim.validate, Tracer: tracer.Hooks(), } - evm = vm.NewEVM(blockContext, vm.TxContext{GasPrice: new(big.Int)}, sim.state, sim.chainConfig, *vmConfig) ) - var tracingStateDB = vm.StateDB(sim.state) + tracingStateDB := vm.StateDB(sim.state) if hooks := tracer.Hooks(); hooks != nil { tracingStateDB = state.NewHookedState(sim.state, hooks) } + evm := vm.NewEVM(blockContext, tracingStateDB, sim.chainConfig, *vmConfig) // It is possible to override precompiles with EVM bytecode, or // move them to another address. if precompiles != nil { @@ -208,7 +207,6 @@ func (sim *simulator) processBlock(ctx context.Context, block *simBlock, header, tracer.reset(tx.Hash(), uint(i)) // EoA check is always skipped, even in validation mode. msg := call.ToMessage(header.BaseFee, !sim.validate, true) - evm.Reset(core.NewEVMTxContext(msg), tracingStateDB) result, err := applyMessageWithEVM(ctx, evm, msg, timeout, sim.gp) if err != nil { txErr := txValidationError(err) @@ -265,7 +263,7 @@ func repairLogs(calls []simCallResult, hash common.Hash) { } } -func (sim *simulator) sanitizeCall(call *TransactionArgs, state *state.StateDB, header *types.Header, blockContext vm.BlockContext, gasUsed *uint64) error { +func (sim *simulator) sanitizeCall(call *TransactionArgs, state vm.StateDB, header *types.Header, blockContext vm.BlockContext, gasUsed *uint64) error { if call.Nonce == nil { nonce := state.GetNonce(call.from()) call.Nonce = (*hexutil.Uint64)(&nonce) @@ -289,7 +287,7 @@ func (sim *simulator) activePrecompiles(base *types.Header) vm.PrecompiledContra isMerge = (base.Difficulty.Sign() == 0) rules = sim.chainConfig.Rules(base.Number, isMerge, base.Time) ) - return maps.Clone(vm.ActivePrecompiledContracts(rules)) + return vm.ActivePrecompiledContracts(rules) } // sanitizeChain checks the chain integrity. Specifically it checks that diff --git a/internal/ethapi/testdata/eth_getBlobSidecarByTxHash-block-with-blobSidecars-show-little.json b/internal/ethapi/testdata/eth_getBlobSidecarByTxHash-block-with-blobSidecars-show-little.json index 1397745cf4..ab94004259 100644 --- a/internal/ethapi/testdata/eth_getBlobSidecarByTxHash-block-with-blobSidecars-show-little.json +++ b/internal/ethapi/testdata/eth_getBlobSidecarByTxHash-block-with-blobSidecars-show-little.json @@ -10,7 +10,7 @@ "0xc00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" ] }, - "blockHash": "0x22c19d38b338157697f939991a74da4129c53e196517a9aa8077f5246716fb0a", + "blockHash": "0xab1209c48e0ac197c939df10dce8ff6daaaaec6fec58d8b3773320bac82f9c9c", "blockNumber": "0x7", "txHash": "0xc520427e696154779f6b21ab03a0735769e1c029035a484f5876f60383a0a7ce", "txIndex": "0x0" diff --git a/internal/ethapi/testdata/eth_getBlobSidecarByTxHash-block-with-blobSidecars.json b/internal/ethapi/testdata/eth_getBlobSidecarByTxHash-block-with-blobSidecars.json index 980c693855..9f3454c27c 100644 --- a/internal/ethapi/testdata/eth_getBlobSidecarByTxHash-block-with-blobSidecars.json +++ b/internal/ethapi/testdata/eth_getBlobSidecarByTxHash-block-with-blobSidecars.json @@ -10,7 +10,7 @@ "0xc00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" ] }, - "blockHash": "0x22c19d38b338157697f939991a74da4129c53e196517a9aa8077f5246716fb0a", + "blockHash": "0xab1209c48e0ac197c939df10dce8ff6daaaaec6fec58d8b3773320bac82f9c9c", "blockNumber": "0x7", "txHash": "0xc520427e696154779f6b21ab03a0735769e1c029035a484f5876f60383a0a7ce", "txIndex": "0x0" diff --git a/internal/ethapi/testdata/eth_getBlobSidecars-block-with-blobSidecars-show-little.json b/internal/ethapi/testdata/eth_getBlobSidecars-block-with-blobSidecars-show-little.json index 78b64ff1b1..d8ab18d146 100644 --- a/internal/ethapi/testdata/eth_getBlobSidecars-block-with-blobSidecars-show-little.json +++ b/internal/ethapi/testdata/eth_getBlobSidecars-block-with-blobSidecars-show-little.json @@ -11,7 +11,7 @@ "0xc00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" ] }, - "blockHash": "0x22c19d38b338157697f939991a74da4129c53e196517a9aa8077f5246716fb0a", + "blockHash": "0xab1209c48e0ac197c939df10dce8ff6daaaaec6fec58d8b3773320bac82f9c9c", "blockNumber": "0x7", "txHash": "0xc520427e696154779f6b21ab03a0735769e1c029035a484f5876f60383a0a7ce", "txIndex": "0x0" diff --git a/internal/ethapi/testdata/eth_getBlobSidecars-block-with-blobSidecars.json b/internal/ethapi/testdata/eth_getBlobSidecars-block-with-blobSidecars.json index 7acbc98013..c5662dbdb4 100644 --- a/internal/ethapi/testdata/eth_getBlobSidecars-block-with-blobSidecars.json +++ b/internal/ethapi/testdata/eth_getBlobSidecars-block-with-blobSidecars.json @@ -11,7 +11,7 @@ "0xc00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" ] }, - "blockHash": "0x22c19d38b338157697f939991a74da4129c53e196517a9aa8077f5246716fb0a", + "blockHash": "0xab1209c48e0ac197c939df10dce8ff6daaaaec6fec58d8b3773320bac82f9c9c", "blockNumber": "0x7", "txHash": "0xc520427e696154779f6b21ab03a0735769e1c029035a484f5876f60383a0a7ce", "txIndex": "0x0" diff --git a/internal/ethapi/testdata/eth_getBlobSidecars-tag-latest.json b/internal/ethapi/testdata/eth_getBlobSidecars-tag-latest.json index 7acbc98013..c5662dbdb4 100644 --- a/internal/ethapi/testdata/eth_getBlobSidecars-tag-latest.json +++ b/internal/ethapi/testdata/eth_getBlobSidecars-tag-latest.json @@ -11,7 +11,7 @@ "0xc00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" ] }, - "blockHash": "0x22c19d38b338157697f939991a74da4129c53e196517a9aa8077f5246716fb0a", + "blockHash": "0xab1209c48e0ac197c939df10dce8ff6daaaaec6fec58d8b3773320bac82f9c9c", "blockNumber": "0x7", "txHash": "0xc520427e696154779f6b21ab03a0735769e1c029035a484f5876f60383a0a7ce", "txIndex": "0x0" diff --git a/internal/ethapi/testdata/eth_getBlockReceipts-block-with-blob-tx.json b/internal/ethapi/testdata/eth_getBlockReceipts-block-with-blob-tx.json index d315353ec6..df2d9349fb 100644 --- a/internal/ethapi/testdata/eth_getBlockReceipts-block-with-blob-tx.json +++ b/internal/ethapi/testdata/eth_getBlockReceipts-block-with-blob-tx.json @@ -2,7 +2,7 @@ { "blobGasPrice": "0x1", "blobGasUsed": "0x20000", - "blockHash": "0x11e6318d77a45c01f89f76b56d36c6936c5250f4e2bd238cb7b09df73cf0cb7d", + "blockHash": "0x17124e31fb075a301b1d7d4135683b0a09fe4e6d453c54e2e734d5ee00744a49", "blockNumber": "0x6", "contractAddress": null, "cumulativeGasUsed": "0x5208", diff --git a/internal/ethapi/testdata/eth_getBlockReceipts-block-with-contract-create-tx.json b/internal/ethapi/testdata/eth_getBlockReceipts-block-with-contract-create-tx.json index f2e5ced2be..453e0abe8a 100644 --- a/internal/ethapi/testdata/eth_getBlockReceipts-block-with-contract-create-tx.json +++ b/internal/ethapi/testdata/eth_getBlockReceipts-block-with-contract-create-tx.json @@ -1,6 +1,6 @@ [ { - "blockHash": "0x5526cd89bc188f20fd5e9bb50d8054dc5a51a81a74ed07eacf36a4a8b10de4b1", + "blockHash": "0xb3e447c77374fd285964cba692e96b1673a88a959726826b5b6e2dca15472b0a", "blockNumber": "0x2", "contractAddress": "0xae9bea628c4ce503dcfd7e305cab4e29e7476592", "cumulativeGasUsed": "0xcf50", diff --git a/internal/ethapi/testdata/eth_getBlockReceipts-block-with-dynamic-fee-tx.json b/internal/ethapi/testdata/eth_getBlockReceipts-block-with-dynamic-fee-tx.json index 71afd85e54..b01400e605 100644 --- a/internal/ethapi/testdata/eth_getBlockReceipts-block-with-dynamic-fee-tx.json +++ b/internal/ethapi/testdata/eth_getBlockReceipts-block-with-dynamic-fee-tx.json @@ -1,6 +1,6 @@ [ { - "blockHash": "0x3e946aa9e252873af511b257d9d89a1bcafa54ce7c6a6442f8407ecdf81e288d", + "blockHash": "0x102e50de30318ee99a03a09db74387e79cad3165bf6840cc84249806a2a302f3", "blockNumber": "0x4", "contractAddress": null, "cumulativeGasUsed": "0x538d", diff --git a/internal/ethapi/testdata/eth_getBlockReceipts-block-with-legacy-contract-call-tx.json b/internal/ethapi/testdata/eth_getBlockReceipts-block-with-legacy-contract-call-tx.json index f089ac45ae..ae964b3d3b 100644 --- a/internal/ethapi/testdata/eth_getBlockReceipts-block-with-legacy-contract-call-tx.json +++ b/internal/ethapi/testdata/eth_getBlockReceipts-block-with-legacy-contract-call-tx.json @@ -1,6 +1,6 @@ [ { - "blockHash": "0xc281d4299fc4e8ce5bba7ecb8deb50f5403d604c806b36aa887dfe2ff84c064f", + "blockHash": "0xcc6225bf39327429a3d869af71182d619a354155187d0b5a8ecd6a9309cffcaa", "blockNumber": "0x3", "contractAddress": null, "cumulativeGasUsed": "0x5e28", @@ -19,7 +19,7 @@ "blockNumber": "0x3", "transactionHash": "0xeaf3921cbf03ba45bad4e6ab807b196ce3b2a0b5bacc355b6272fa96b11b4287", "transactionIndex": "0x0", - "blockHash": "0xc281d4299fc4e8ce5bba7ecb8deb50f5403d604c806b36aa887dfe2ff84c064f", + "blockHash": "0xcc6225bf39327429a3d869af71182d619a354155187d0b5a8ecd6a9309cffcaa", "logIndex": "0x0", "removed": false } diff --git a/internal/ethapi/testdata/eth_getBlockReceipts-block-with-legacy-transfer-tx.json b/internal/ethapi/testdata/eth_getBlockReceipts-block-with-legacy-transfer-tx.json index 8b69dddd66..7af5016079 100644 --- a/internal/ethapi/testdata/eth_getBlockReceipts-block-with-legacy-transfer-tx.json +++ b/internal/ethapi/testdata/eth_getBlockReceipts-block-with-legacy-transfer-tx.json @@ -1,6 +1,6 @@ [ { - "blockHash": "0xda50d57d8802553b00bb8e4d777bd5c4114086941119ca04edb15429f4818ed9", + "blockHash": "0xe9bd1d8c303b1af5c704b9d78e62c54a34af47e0db04ac1389a5ef74a619b9da", "blockNumber": "0x1", "contractAddress": null, "cumulativeGasUsed": "0x5208", diff --git a/internal/ethapi/testdata/eth_getBlockReceipts-tag-latest.json b/internal/ethapi/testdata/eth_getBlockReceipts-tag-latest.json index d315353ec6..df2d9349fb 100644 --- a/internal/ethapi/testdata/eth_getBlockReceipts-tag-latest.json +++ b/internal/ethapi/testdata/eth_getBlockReceipts-tag-latest.json @@ -2,7 +2,7 @@ { "blobGasPrice": "0x1", "blobGasUsed": "0x20000", - "blockHash": "0x11e6318d77a45c01f89f76b56d36c6936c5250f4e2bd238cb7b09df73cf0cb7d", + "blockHash": "0x17124e31fb075a301b1d7d4135683b0a09fe4e6d453c54e2e734d5ee00744a49", "blockNumber": "0x6", "contractAddress": null, "cumulativeGasUsed": "0x5208", diff --git a/internal/ethapi/testdata/eth_getTransactionReceipt-blob-tx.json b/internal/ethapi/testdata/eth_getTransactionReceipt-blob-tx.json index 5debbd4447..8e0669d10a 100644 --- a/internal/ethapi/testdata/eth_getTransactionReceipt-blob-tx.json +++ b/internal/ethapi/testdata/eth_getTransactionReceipt-blob-tx.json @@ -1,7 +1,7 @@ { "blobGasPrice": "0x1", "blobGasUsed": "0x20000", - "blockHash": "0x11e6318d77a45c01f89f76b56d36c6936c5250f4e2bd238cb7b09df73cf0cb7d", + "blockHash": "0x17124e31fb075a301b1d7d4135683b0a09fe4e6d453c54e2e734d5ee00744a49", "blockNumber": "0x6", "contractAddress": null, "cumulativeGasUsed": "0x5208", diff --git a/internal/ethapi/testdata/eth_getTransactionReceipt-create-contract-tx.json b/internal/ethapi/testdata/eth_getTransactionReceipt-create-contract-tx.json index 8cf2ead10f..a9391cb578 100644 --- a/internal/ethapi/testdata/eth_getTransactionReceipt-create-contract-tx.json +++ b/internal/ethapi/testdata/eth_getTransactionReceipt-create-contract-tx.json @@ -1,5 +1,5 @@ { - "blockHash": "0x5526cd89bc188f20fd5e9bb50d8054dc5a51a81a74ed07eacf36a4a8b10de4b1", + "blockHash": "0xb3e447c77374fd285964cba692e96b1673a88a959726826b5b6e2dca15472b0a", "blockNumber": "0x2", "contractAddress": "0xae9bea628c4ce503dcfd7e305cab4e29e7476592", "cumulativeGasUsed": "0xcf50", diff --git a/internal/ethapi/testdata/eth_getTransactionReceipt-create-contract-with-access-list.json b/internal/ethapi/testdata/eth_getTransactionReceipt-create-contract-with-access-list.json index 34c318faca..49c06aad62 100644 --- a/internal/ethapi/testdata/eth_getTransactionReceipt-create-contract-with-access-list.json +++ b/internal/ethapi/testdata/eth_getTransactionReceipt-create-contract-with-access-list.json @@ -1,5 +1,5 @@ { - "blockHash": "0xa04ad6be58c45fe483991b89416572bc50426b0de44b769757e95c704250f874", + "blockHash": "0x53bffe54375c0a31fe7bc0db7455db7d48278234c2400efa4d40d1c57cbe868d", "blockNumber": "0x5", "contractAddress": "0xfdaa97661a584d977b4d3abb5370766ff5b86a18", "cumulativeGasUsed": "0xe01c", diff --git a/internal/ethapi/testdata/eth_getTransactionReceipt-dynamic-tx-with-logs.json b/internal/ethapi/testdata/eth_getTransactionReceipt-dynamic-tx-with-logs.json index 9f023ed6e3..13bd7bd12c 100644 --- a/internal/ethapi/testdata/eth_getTransactionReceipt-dynamic-tx-with-logs.json +++ b/internal/ethapi/testdata/eth_getTransactionReceipt-dynamic-tx-with-logs.json @@ -1,5 +1,5 @@ { - "blockHash": "0x3e946aa9e252873af511b257d9d89a1bcafa54ce7c6a6442f8407ecdf81e288d", + "blockHash": "0x102e50de30318ee99a03a09db74387e79cad3165bf6840cc84249806a2a302f3", "blockNumber": "0x4", "contractAddress": null, "cumulativeGasUsed": "0x538d", diff --git a/internal/ethapi/testdata/eth_getTransactionReceipt-normal-transfer-tx.json b/internal/ethapi/testdata/eth_getTransactionReceipt-normal-transfer-tx.json index f180a21977..779223af98 100644 --- a/internal/ethapi/testdata/eth_getTransactionReceipt-normal-transfer-tx.json +++ b/internal/ethapi/testdata/eth_getTransactionReceipt-normal-transfer-tx.json @@ -1,5 +1,5 @@ { - "blockHash": "0xda50d57d8802553b00bb8e4d777bd5c4114086941119ca04edb15429f4818ed9", + "blockHash": "0xe9bd1d8c303b1af5c704b9d78e62c54a34af47e0db04ac1389a5ef74a619b9da", "blockNumber": "0x1", "contractAddress": null, "cumulativeGasUsed": "0x5208", diff --git a/internal/ethapi/testdata/eth_getTransactionReceipt-with-logs.json b/internal/ethapi/testdata/eth_getTransactionReceipt-with-logs.json index 61aed4b7bd..1a1edb7887 100644 --- a/internal/ethapi/testdata/eth_getTransactionReceipt-with-logs.json +++ b/internal/ethapi/testdata/eth_getTransactionReceipt-with-logs.json @@ -1,5 +1,5 @@ { - "blockHash": "0xc281d4299fc4e8ce5bba7ecb8deb50f5403d604c806b36aa887dfe2ff84c064f", + "blockHash": "0xcc6225bf39327429a3d869af71182d619a354155187d0b5a8ecd6a9309cffcaa", "blockNumber": "0x3", "contractAddress": null, "cumulativeGasUsed": "0x5e28", @@ -18,7 +18,7 @@ "blockNumber": "0x3", "transactionHash": "0xeaf3921cbf03ba45bad4e6ab807b196ce3b2a0b5bacc355b6272fa96b11b4287", "transactionIndex": "0x0", - "blockHash": "0xc281d4299fc4e8ce5bba7ecb8deb50f5403d604c806b36aa887dfe2ff84c064f", + "blockHash": "0xcc6225bf39327429a3d869af71182d619a354155187d0b5a8ecd6a9309cffcaa", "logIndex": "0x0", "removed": false } diff --git a/internal/ethapi/transaction_args_test.go b/internal/ethapi/transaction_args_test.go index 935ad9f971..a1ee544d2d 100644 --- a/internal/ethapi/transaction_args_test.go +++ b/internal/ethapi/transaction_args_test.go @@ -375,7 +375,7 @@ func (b *backendMock) GetLogs(ctx context.Context, blockHash common.Hash, number return nil, nil } func (b *backendMock) GetTd(ctx context.Context, hash common.Hash) *big.Int { return nil } -func (b *backendMock) GetEVM(ctx context.Context, msg *core.Message, state *state.StateDB, header *types.Header, vmConfig *vm.Config, blockCtx *vm.BlockContext) *vm.EVM { +func (b *backendMock) GetEVM(ctx context.Context, state *state.StateDB, header *types.Header, vmConfig *vm.Config, blockCtx *vm.BlockContext) *vm.EVM { return nil } func (b *backendMock) SubscribeChainEvent(ch chan<- core.ChainEvent) event.Subscription { return nil } diff --git a/internal/flags/flags.go b/internal/flags/flags.go index 407c576337..8971810be8 100644 --- a/internal/flags/flags.go +++ b/internal/flags/flags.go @@ -90,7 +90,7 @@ func (f *DirectoryFlag) Apply(set *flag.FlagSet) error { } } eachName(f, func(name string) { - set.Var(&f.Value, f.Name, f.Usage) + set.Var(&f.Value, name, f.Usage) }) return nil } @@ -172,7 +172,7 @@ func (f *BigFlag) Apply(set *flag.FlagSet) error { } eachName(f, func(name string) { f.Value = new(big.Int) - set.Var((*bigValue)(f.Value), f.Name, f.Usage) + set.Var((*bigValue)(f.Value), name, f.Usage) }) return nil } diff --git a/metrics/counter.go b/metrics/counter.go index dbe8e16a90..0f373b0d92 100644 --- a/metrics/counter.go +++ b/metrics/counter.go @@ -4,109 +4,55 @@ import ( "sync/atomic" ) -type CounterSnapshot interface { - Count() int64 -} - -// Counter hold an int64 value that can be incremented and decremented. -type Counter interface { - Clear() - Dec(int64) - Inc(int64) - Snapshot() CounterSnapshot -} - // GetOrRegisterCounter returns an existing Counter or constructs and registers -// a new StandardCounter. -func GetOrRegisterCounter(name string, r Registry) Counter { - if nil == r { +// a new Counter. +func GetOrRegisterCounter(name string, r Registry) *Counter { + if r == nil { r = DefaultRegistry } - return r.GetOrRegister(name, NewCounter).(Counter) + return r.GetOrRegister(name, NewCounter).(*Counter) } -// GetOrRegisterCounterForced returns an existing Counter or constructs and registers a -// new Counter no matter the global switch is enabled or not. -// Be sure to unregister the counter from the registry once it is of no use to -// allow for garbage collection. -func GetOrRegisterCounterForced(name string, r Registry) Counter { - if nil == r { - r = DefaultRegistry - } - return r.GetOrRegister(name, NewCounterForced).(Counter) +// NewCounter constructs a new Counter. +func NewCounter() *Counter { + return new(Counter) } -// NewCounter constructs a new StandardCounter. -func NewCounter() Counter { - if !Enabled { - return NilCounter{} - } - return new(StandardCounter) -} - -// NewCounterForced constructs a new StandardCounter and returns it no matter if -// the global switch is enabled or not. -func NewCounterForced() Counter { - return new(StandardCounter) -} - -// NewRegisteredCounter constructs and registers a new StandardCounter. -func NewRegisteredCounter(name string, r Registry) Counter { +// NewRegisteredCounter constructs and registers a new Counter. +func NewRegisteredCounter(name string, r Registry) *Counter { c := NewCounter() - if nil == r { - r = DefaultRegistry - } - r.Register(name, c) - return c -} - -// NewRegisteredCounterForced constructs and registers a new StandardCounter -// and launches a goroutine no matter the global switch is enabled or not. -// Be sure to unregister the counter from the registry once it is of no use to -// allow for garbage collection. -func NewRegisteredCounterForced(name string, r Registry) Counter { - c := NewCounterForced() - if nil == r { + if r == nil { r = DefaultRegistry } r.Register(name, c) return c } -// counterSnapshot is a read-only copy of another Counter. -type counterSnapshot int64 +// CounterSnapshot is a read-only copy of a Counter. +type CounterSnapshot int64 // Count returns the count at the time the snapshot was taken. -func (c counterSnapshot) Count() int64 { return int64(c) } - -// NilCounter is a no-op Counter. -type NilCounter struct{} +func (c CounterSnapshot) Count() int64 { return int64(c) } -func (NilCounter) Clear() {} -func (NilCounter) Dec(i int64) {} -func (NilCounter) Inc(i int64) {} -func (NilCounter) Snapshot() CounterSnapshot { return (*emptySnapshot)(nil) } - -// StandardCounter is the standard implementation of a Counter and uses the -// sync/atomic package to manage a single int64 value. -type StandardCounter atomic.Int64 +// Counter hold an int64 value that can be incremented and decremented. +type Counter atomic.Int64 // Clear sets the counter to zero. -func (c *StandardCounter) Clear() { +func (c *Counter) Clear() { (*atomic.Int64)(c).Store(0) } // Dec decrements the counter by the given amount. -func (c *StandardCounter) Dec(i int64) { +func (c *Counter) Dec(i int64) { (*atomic.Int64)(c).Add(-i) } // Inc increments the counter by the given amount. -func (c *StandardCounter) Inc(i int64) { +func (c *Counter) Inc(i int64) { (*atomic.Int64)(c).Add(i) } // Snapshot returns a read-only copy of the counter. -func (c *StandardCounter) Snapshot() CounterSnapshot { - return counterSnapshot((*atomic.Int64)(c).Load()) +func (c *Counter) Snapshot() CounterSnapshot { + return CounterSnapshot((*atomic.Int64)(c).Load()) } diff --git a/metrics/counter_float64.go b/metrics/counter_float64.go index 15c81494ef..91c4215c4d 100644 --- a/metrics/counter_float64.go +++ b/metrics/counter_float64.go @@ -5,114 +5,57 @@ import ( "sync/atomic" ) -type CounterFloat64Snapshot interface { - Count() float64 -} - -// CounterFloat64 holds a float64 value that can be incremented and decremented. -type CounterFloat64 interface { - Clear() - Dec(float64) - Inc(float64) - Snapshot() CounterFloat64Snapshot -} - -// GetOrRegisterCounterFloat64 returns an existing CounterFloat64 or constructs and registers -// a new StandardCounterFloat64. -func GetOrRegisterCounterFloat64(name string, r Registry) CounterFloat64 { +// GetOrRegisterCounterFloat64 returns an existing *CounterFloat64 or constructs and registers +// a new CounterFloat64. +func GetOrRegisterCounterFloat64(name string, r Registry) *CounterFloat64 { if nil == r { r = DefaultRegistry } - return r.GetOrRegister(name, NewCounterFloat64).(CounterFloat64) -} - -// GetOrRegisterCounterFloat64Forced returns an existing CounterFloat64 or constructs and registers a -// new CounterFloat64 no matter the global switch is enabled or not. -// Be sure to unregister the counter from the registry once it is of no use to -// allow for garbage collection. -func GetOrRegisterCounterFloat64Forced(name string, r Registry) CounterFloat64 { - if nil == r { - r = DefaultRegistry - } - return r.GetOrRegister(name, NewCounterFloat64Forced).(CounterFloat64) -} - -// NewCounterFloat64 constructs a new StandardCounterFloat64. -func NewCounterFloat64() CounterFloat64 { - if !Enabled { - return NilCounterFloat64{} - } - return &StandardCounterFloat64{} + return r.GetOrRegister(name, NewCounterFloat64).(*CounterFloat64) } -// NewCounterFloat64Forced constructs a new StandardCounterFloat64 and returns it no matter if -// the global switch is enabled or not. -func NewCounterFloat64Forced() CounterFloat64 { - return &StandardCounterFloat64{} +// NewCounterFloat64 constructs a new CounterFloat64. +func NewCounterFloat64() *CounterFloat64 { + return new(CounterFloat64) } -// NewRegisteredCounterFloat64 constructs and registers a new StandardCounterFloat64. -func NewRegisteredCounterFloat64(name string, r Registry) CounterFloat64 { +// NewRegisteredCounterFloat64 constructs and registers a new CounterFloat64. +func NewRegisteredCounterFloat64(name string, r Registry) *CounterFloat64 { c := NewCounterFloat64() - if nil == r { - r = DefaultRegistry - } - r.Register(name, c) - return c -} - -// NewRegisteredCounterFloat64Forced constructs and registers a new StandardCounterFloat64 -// and launches a goroutine no matter the global switch is enabled or not. -// Be sure to unregister the counter from the registry once it is of no use to -// allow for garbage collection. -func NewRegisteredCounterFloat64Forced(name string, r Registry) CounterFloat64 { - c := NewCounterFloat64Forced() - if nil == r { + if r == nil { r = DefaultRegistry } r.Register(name, c) return c } -// counterFloat64Snapshot is a read-only copy of another CounterFloat64. -type counterFloat64Snapshot float64 +// CounterFloat64Snapshot is a read-only copy of a float64 counter. +type CounterFloat64Snapshot float64 // Count returns the value at the time the snapshot was taken. -func (c counterFloat64Snapshot) Count() float64 { return float64(c) } - -type NilCounterFloat64 struct{} +func (c CounterFloat64Snapshot) Count() float64 { return float64(c) } -func (NilCounterFloat64) Clear() {} -func (NilCounterFloat64) Count() float64 { return 0.0 } -func (NilCounterFloat64) Dec(i float64) {} -func (NilCounterFloat64) Inc(i float64) {} -func (NilCounterFloat64) Snapshot() CounterFloat64Snapshot { return NilCounterFloat64{} } - -// StandardCounterFloat64 is the standard implementation of a CounterFloat64 and uses the -// atomic to manage a single float64 value. -type StandardCounterFloat64 struct { - floatBits atomic.Uint64 -} +// CounterFloat64 holds a float64 value that can be incremented and decremented. +type CounterFloat64 atomic.Uint64 // Clear sets the counter to zero. -func (c *StandardCounterFloat64) Clear() { - c.floatBits.Store(0) +func (c *CounterFloat64) Clear() { + (*atomic.Uint64)(c).Store(0) } // Dec decrements the counter by the given amount. -func (c *StandardCounterFloat64) Dec(v float64) { - atomicAddFloat(&c.floatBits, -v) +func (c *CounterFloat64) Dec(v float64) { + atomicAddFloat((*atomic.Uint64)(c), -v) } // Inc increments the counter by the given amount. -func (c *StandardCounterFloat64) Inc(v float64) { - atomicAddFloat(&c.floatBits, v) +func (c *CounterFloat64) Inc(v float64) { + atomicAddFloat((*atomic.Uint64)(c), v) } // Snapshot returns a read-only copy of the counter. -func (c *StandardCounterFloat64) Snapshot() CounterFloat64Snapshot { - v := math.Float64frombits(c.floatBits.Load()) - return counterFloat64Snapshot(v) +func (c *CounterFloat64) Snapshot() CounterFloat64Snapshot { + return CounterFloat64Snapshot(math.Float64frombits((*atomic.Uint64)(c).Load())) } func atomicAddFloat(fbits *atomic.Uint64, v float64) { diff --git a/metrics/counter_float_64_test.go b/metrics/counter_float_64_test.go index c21bd3307f..618cbbbc2b 100644 --- a/metrics/counter_float_64_test.go +++ b/metrics/counter_float_64_test.go @@ -32,61 +32,35 @@ func BenchmarkCounterFloat64Parallel(b *testing.B) { } } -func TestCounterFloat64Clear(t *testing.T) { +func TestCounterFloat64(t *testing.T) { c := NewCounterFloat64() - c.Inc(1.0) - c.Clear() if count := c.Snapshot().Count(); count != 0 { - t.Errorf("c.Count(): 0 != %v\n", count) + t.Errorf("wrong count: %v", count) } -} - -func TestCounterFloat64Dec1(t *testing.T) { - c := NewCounterFloat64() c.Dec(1.0) if count := c.Snapshot().Count(); count != -1.0 { - t.Errorf("c.Count(): -1.0 != %v\n", count) + t.Errorf("wrong count: %v", count) } -} - -func TestCounterFloat64Dec2(t *testing.T) { - c := NewCounterFloat64() + snapshot := c.Snapshot() c.Dec(2.0) - if count := c.Snapshot().Count(); count != -2.0 { - t.Errorf("c.Count(): -2.0 != %v\n", count) + if count := c.Snapshot().Count(); count != -3.0 { + t.Errorf("wrong count: %v", count) } -} - -func TestCounterFloat64Inc1(t *testing.T) { - c := NewCounterFloat64() c.Inc(1.0) - if count := c.Snapshot().Count(); count != 1.0 { - t.Errorf("c.Count(): 1.0 != %v\n", count) + if count := c.Snapshot().Count(); count != -2.0 { + t.Errorf("wrong count: %v", count) } -} - -func TestCounterFloat64Inc2(t *testing.T) { - c := NewCounterFloat64() c.Inc(2.0) - if count := c.Snapshot().Count(); count != 2.0 { - t.Errorf("c.Count(): 2.0 != %v\n", count) + if count := c.Snapshot().Count(); count != 0.0 { + t.Errorf("wrong count: %v", count) } -} - -func TestCounterFloat64Snapshot(t *testing.T) { - c := NewCounterFloat64() - c.Inc(1.0) - snapshot := c.Snapshot() - c.Inc(1.0) - if count := snapshot.Count(); count != 1.0 { - t.Errorf("c.Count(): 1.0 != %v\n", count) + if count := snapshot.Count(); count != -1.0 { + t.Errorf("snapshot count wrong: %v", count) } -} - -func TestCounterFloat64Zero(t *testing.T) { - c := NewCounterFloat64() - if count := c.Snapshot().Count(); count != 0 { - t.Errorf("c.Count(): 0 != %v\n", count) + c.Inc(1.0) + c.Clear() + if count := c.Snapshot().Count(); count != 0.0 { + t.Errorf("wrong count: %v", count) } } diff --git a/metrics/counter_test.go b/metrics/counter_test.go index 1b15b23f21..bf0ca6bae4 100644 --- a/metrics/counter_test.go +++ b/metrics/counter_test.go @@ -19,35 +19,26 @@ func TestCounterClear(t *testing.T) { } } -func TestCounterDec1(t *testing.T) { +func TestCounter(t *testing.T) { c := NewCounter() + if count := c.Snapshot().Count(); count != 0 { + t.Errorf("wrong count: %v", count) + } c.Dec(1) if count := c.Snapshot().Count(); count != -1 { - t.Errorf("c.Count(): -1 != %v\n", count) + t.Errorf("wrong count: %v", count) } -} - -func TestCounterDec2(t *testing.T) { - c := NewCounter() c.Dec(2) - if count := c.Snapshot().Count(); count != -2 { - t.Errorf("c.Count(): -2 != %v\n", count) + if count := c.Snapshot().Count(); count != -3 { + t.Errorf("wrong count: %v", count) } -} - -func TestCounterInc1(t *testing.T) { - c := NewCounter() c.Inc(1) - if count := c.Snapshot().Count(); count != 1 { - t.Errorf("c.Count(): 1 != %v\n", count) + if count := c.Snapshot().Count(); count != -2 { + t.Errorf("wrong count: %v", count) } -} - -func TestCounterInc2(t *testing.T) { - c := NewCounter() c.Inc(2) - if count := c.Snapshot().Count(); count != 2 { - t.Errorf("c.Count(): 2 != %v\n", count) + if count := c.Snapshot().Count(); count != 0 { + t.Errorf("wrong count: %v", count) } } @@ -61,13 +52,6 @@ func TestCounterSnapshot(t *testing.T) { } } -func TestCounterZero(t *testing.T) { - c := NewCounter() - if count := c.Snapshot().Count(); count != 0 { - t.Errorf("c.Count(): 0 != %v\n", count) - } -} - func TestGetOrRegisterCounter(t *testing.T) { r := NewRegistry() NewRegisteredCounter("foo", r).Inc(47) diff --git a/metrics/debug.go b/metrics/debug.go index 9dfee1a866..5d0d3992f1 100644 --- a/metrics/debug.go +++ b/metrics/debug.go @@ -8,13 +8,13 @@ import ( var ( debugMetrics struct { GCStats struct { - LastGC Gauge - NumGC Gauge + LastGC *Gauge + NumGC *Gauge Pause Histogram //PauseQuantiles Histogram - PauseTotal Gauge + PauseTotal *Gauge } - ReadGCStats Timer + ReadGCStats *Timer } gcStats debug.GCStats ) diff --git a/metrics/ewma.go b/metrics/ewma.go index 1d7a4f00cf..194527a798 100644 --- a/metrics/ewma.go +++ b/metrics/ewma.go @@ -7,56 +7,36 @@ import ( "time" ) -type EWMASnapshot interface { - Rate() float64 -} +// EWMASnapshot is a read-only copy of an EWMA. +type EWMASnapshot float64 -// EWMAs continuously calculate an exponentially-weighted moving average -// based on an outside source of clock ticks. -type EWMA interface { - Snapshot() EWMASnapshot - Tick() - Update(int64) -} +// Rate returns the rate of events per second at the time the snapshot was +// taken. +func (a EWMASnapshot) Rate() float64 { return float64(a) } // NewEWMA constructs a new EWMA with the given alpha. -func NewEWMA(alpha float64) EWMA { - return &StandardEWMA{alpha: alpha} +func NewEWMA(alpha float64) *EWMA { + return &EWMA{alpha: alpha} } // NewEWMA1 constructs a new EWMA for a one-minute moving average. -func NewEWMA1() EWMA { +func NewEWMA1() *EWMA { return NewEWMA(1 - math.Exp(-5.0/60.0/1)) } // NewEWMA5 constructs a new EWMA for a five-minute moving average. -func NewEWMA5() EWMA { +func NewEWMA5() *EWMA { return NewEWMA(1 - math.Exp(-5.0/60.0/5)) } // NewEWMA15 constructs a new EWMA for a fifteen-minute moving average. -func NewEWMA15() EWMA { +func NewEWMA15() *EWMA { return NewEWMA(1 - math.Exp(-5.0/60.0/15)) } -// ewmaSnapshot is a read-only copy of another EWMA. -type ewmaSnapshot float64 - -// Rate returns the rate of events per second at the time the snapshot was -// taken. -func (a ewmaSnapshot) Rate() float64 { return float64(a) } - -// NilEWMA is a no-op EWMA. -type NilEWMA struct{} - -func (NilEWMA) Snapshot() EWMASnapshot { return (*emptySnapshot)(nil) } -func (NilEWMA) Tick() {} -func (NilEWMA) Update(n int64) {} - -// StandardEWMA is the standard implementation of an EWMA and tracks the number -// of uncounted events and processes them on each tick. It uses the -// sync/atomic package to manage uncounted events. -type StandardEWMA struct { +// EWMA continuously calculate an exponentially-weighted moving average +// based on an outside source of clock ticks. +type EWMA struct { uncounted atomic.Int64 alpha float64 rate atomic.Uint64 @@ -65,27 +45,27 @@ type StandardEWMA struct { } // Snapshot returns a read-only copy of the EWMA. -func (a *StandardEWMA) Snapshot() EWMASnapshot { +func (a *EWMA) Snapshot() EWMASnapshot { r := math.Float64frombits(a.rate.Load()) * float64(time.Second) - return ewmaSnapshot(r) + return EWMASnapshot(r) } -// Tick ticks the clock to update the moving average. It assumes it is called +// tick ticks the clock to update the moving average. It assumes it is called // every five seconds. -func (a *StandardEWMA) Tick() { +func (a *EWMA) tick() { // Optimization to avoid mutex locking in the hot-path. if a.init.Load() { a.updateRate(a.fetchInstantRate()) return } - // Slow-path: this is only needed on the first Tick() and preserves transactional updating + // Slow-path: this is only needed on the first tick() and preserves transactional updating // of init and rate in the else block. The first conditional is needed below because // a different thread could have set a.init = 1 between the time of the first atomic load and when // the lock was acquired. a.mutex.Lock() if a.init.Load() { // The fetchInstantRate() uses atomic loading, which is unnecessary in this critical section - // but again, this section is only invoked on the first successful Tick() operation. + // but again, this section is only invoked on the first successful tick() operation. a.updateRate(a.fetchInstantRate()) } else { a.init.Store(true) @@ -94,18 +74,18 @@ func (a *StandardEWMA) Tick() { a.mutex.Unlock() } -func (a *StandardEWMA) fetchInstantRate() float64 { +func (a *EWMA) fetchInstantRate() float64 { count := a.uncounted.Swap(0) return float64(count) / float64(5*time.Second) } -func (a *StandardEWMA) updateRate(instantRate float64) { +func (a *EWMA) updateRate(instantRate float64) { currentRate := math.Float64frombits(a.rate.Load()) currentRate += a.alpha * (instantRate - currentRate) a.rate.Store(math.Float64bits(currentRate)) } // Update adds n uncounted events. -func (a *StandardEWMA) Update(n int64) { +func (a *EWMA) Update(n int64) { a.uncounted.Add(n) } diff --git a/metrics/ewma_test.go b/metrics/ewma_test.go index 9a91b43db8..4b9bde3a4b 100644 --- a/metrics/ewma_test.go +++ b/metrics/ewma_test.go @@ -12,7 +12,7 @@ func BenchmarkEWMA(b *testing.B) { b.ResetTimer() for i := 0; i < b.N; i++ { a.Update(1) - a.Tick() + a.tick() } } @@ -23,7 +23,7 @@ func BenchmarkEWMAParallel(b *testing.B) { b.RunParallel(func(pb *testing.PB) { for pb.Next() { a.Update(1) - a.Tick() + a.tick() } }) } @@ -31,7 +31,7 @@ func BenchmarkEWMAParallel(b *testing.B) { func TestEWMA1(t *testing.T) { a := NewEWMA1() a.Update(3) - a.Tick() + a.tick() for i, want := range []float64{0.6, 0.22072766470286553, 0.08120116994196772, 0.029872241020718428, 0.01098938333324054, 0.004042768199451294, 0.0014872513059998212, @@ -49,7 +49,7 @@ func TestEWMA1(t *testing.T) { func TestEWMA5(t *testing.T) { a := NewEWMA5() a.Update(3) - a.Tick() + a.tick() for i, want := range []float64{ 0.6, 0.49123845184678905, 0.4021920276213837, 0.32928698165641596, 0.269597378470333, 0.2207276647028654, 0.18071652714732128, @@ -67,7 +67,7 @@ func TestEWMA5(t *testing.T) { func TestEWMA15(t *testing.T) { a := NewEWMA15() a.Update(3) - a.Tick() + a.tick() for i, want := range []float64{ 0.6, 0.5613041910189706, 0.5251039914257684, 0.4912384518467888184678905, 0.459557003018789, 0.4299187863442732, 0.4021920276213831, @@ -82,8 +82,8 @@ func TestEWMA15(t *testing.T) { } } -func elapseMinute(a EWMA) { +func elapseMinute(a *EWMA) { for i := 0; i < 12; i++ { - a.Tick() + a.tick() } } diff --git a/metrics/exp/exp.go b/metrics/exp/exp.go index f50ad52644..6e992222e1 100644 --- a/metrics/exp/exp.go +++ b/metrics/exp/exp.go @@ -160,7 +160,7 @@ func (exp *exp) publishHistogram(name string, metric metrics.Histogram) { exp.getFloat(name + ".999-percentile").Set(ps[4]) } -func (exp *exp) publishMeter(name string, metric metrics.Meter) { +func (exp *exp) publishMeter(name string, metric *metrics.Meter) { m := metric.Snapshot() exp.getInt(name + ".count").Set(m.Count()) exp.getFloat(name + ".one-minute").Set(m.Rate1()) @@ -169,7 +169,7 @@ func (exp *exp) publishMeter(name string, metric metrics.Meter) { exp.getFloat(name + ".mean").Set(m.RateMean()) } -func (exp *exp) publishTimer(name string, metric metrics.Timer) { +func (exp *exp) publishTimer(name string, metric *metrics.Timer) { t := metric.Snapshot() ps := t.Percentiles([]float64{0.5, 0.75, 0.95, 0.99, 0.999}) exp.getInt(name + ".count").Set(t.Count()) @@ -188,7 +188,7 @@ func (exp *exp) publishTimer(name string, metric metrics.Timer) { exp.getFloat(name + ".mean-rate").Set(t.RateMean()) } -func (exp *exp) publishResettingTimer(name string, metric metrics.ResettingTimer) { +func (exp *exp) publishResettingTimer(name string, metric *metrics.ResettingTimer) { t := metric.Snapshot() ps := t.Percentiles([]float64{0.50, 0.75, 0.95, 0.99}) exp.getInt(name + ".count").Set(int64(t.Count())) @@ -256,23 +256,23 @@ func (exp *exp) interfaceToExpVal(v interface{}) expvar.Var { func (exp *exp) syncToExpvar() { exp.registry.Each(func(name string, i interface{}) { switch i := i.(type) { - case metrics.Counter: + case *metrics.Counter: exp.publishCounter(name, i.Snapshot()) - case metrics.CounterFloat64: + case *metrics.CounterFloat64: exp.publishCounterFloat64(name, i.Snapshot()) - case metrics.Gauge: + case *metrics.Gauge: exp.publishGauge(name, i.Snapshot()) - case metrics.GaugeFloat64: + case *metrics.GaugeFloat64: exp.publishGaugeFloat64(name, i.Snapshot()) - case metrics.GaugeInfo: + case *metrics.GaugeInfo: exp.publishGaugeInfo(name, i.Snapshot()) case metrics.Histogram: exp.publishHistogram(name, i) - case metrics.Meter: + case *metrics.Meter: exp.publishMeter(name, i) - case metrics.Timer: + case *metrics.Timer: exp.publishTimer(name, i) - case metrics.ResettingTimer: + case *metrics.ResettingTimer: exp.publishResettingTimer(name, i) case metrics.Label: exp.publishLabel(name, i) diff --git a/metrics/gauge.go b/metrics/gauge.go index 6d10bbf856..ba7843e03b 100644 --- a/metrics/gauge.go +++ b/metrics/gauge.go @@ -2,97 +2,69 @@ package metrics import "sync/atomic" -// GaugeSnapshot contains a readonly int64. -type GaugeSnapshot interface { - Value() int64 -} +// GaugeSnapshot is a read-only copy of a Gauge. +type GaugeSnapshot int64 -// Gauge holds an int64 value that can be set arbitrarily. -type Gauge interface { - Snapshot() GaugeSnapshot - Update(int64) - UpdateIfGt(int64) - Dec(int64) - Inc(int64) -} +// Value returns the value at the time the snapshot was taken. +func (g GaugeSnapshot) Value() int64 { return int64(g) } // GetOrRegisterGauge returns an existing Gauge or constructs and registers a -// new StandardGauge. -func GetOrRegisterGauge(name string, r Registry) Gauge { - if nil == r { +// new Gauge. +func GetOrRegisterGauge(name string, r Registry) *Gauge { + if r == nil { r = DefaultRegistry } - return r.GetOrRegister(name, NewGauge).(Gauge) + return r.GetOrRegister(name, NewGauge).(*Gauge) } -// NewGauge constructs a new StandardGauge. -func NewGauge() Gauge { - if !Enabled { - return NilGauge{} - } - return &StandardGauge{} +// NewGauge constructs a new Gauge. +func NewGauge() *Gauge { + return &Gauge{} } -// NewRegisteredGauge constructs and registers a new StandardGauge. -func NewRegisteredGauge(name string, r Registry) Gauge { +// NewRegisteredGauge constructs and registers a new Gauge. +func NewRegisteredGauge(name string, r Registry) *Gauge { c := NewGauge() - if nil == r { + if r == nil { r = DefaultRegistry } r.Register(name, c) return c } -// gaugeSnapshot is a read-only copy of another Gauge. -type gaugeSnapshot int64 - -// Value returns the value at the time the snapshot was taken. -func (g gaugeSnapshot) Value() int64 { return int64(g) } - -// NilGauge is a no-op Gauge. -type NilGauge struct{} - -func (NilGauge) Snapshot() GaugeSnapshot { return (*emptySnapshot)(nil) } -func (NilGauge) Update(v int64) {} -func (NilGauge) UpdateIfGt(v int64) {} -func (NilGauge) Dec(i int64) {} -func (NilGauge) Inc(i int64) {} - -// StandardGauge is the standard implementation of a Gauge and uses the -// sync/atomic package to manage a single int64 value. -type StandardGauge struct { - value atomic.Int64 -} +// Gauge holds an int64 value that can be set arbitrarily. +type Gauge atomic.Int64 // Snapshot returns a read-only copy of the gauge. -func (g *StandardGauge) Snapshot() GaugeSnapshot { - return gaugeSnapshot(g.value.Load()) +func (g *Gauge) Snapshot() GaugeSnapshot { + return GaugeSnapshot((*atomic.Int64)(g).Load()) } // Update updates the gauge's value. -func (g *StandardGauge) Update(v int64) { - g.value.Store(v) +func (g *Gauge) Update(v int64) { + (*atomic.Int64)(g).Store(v) } // UpdateIfGt updates the gauge's value if v is larger then the current value. -func (g *StandardGauge) UpdateIfGt(v int64) { +func (g *Gauge) UpdateIfGt(v int64) { + value := (*atomic.Int64)(g) for { - exist := g.value.Load() + exist := value.Load() if exist >= v { break } - if g.value.CompareAndSwap(exist, v) { + if value.CompareAndSwap(exist, v) { break } } } // Dec decrements the gauge's current value by the given amount. -func (g *StandardGauge) Dec(i int64) { - g.value.Add(-i) +func (g *Gauge) Dec(i int64) { + (*atomic.Int64)(g).Add(-i) } // Inc increments the gauge's current value by the given amount. -func (g *StandardGauge) Inc(i int64) { - g.value.Add(i) +func (g *Gauge) Inc(i int64) { + (*atomic.Int64)(g).Add(i) } diff --git a/metrics/gauge_float64.go b/metrics/gauge_float64.go index c1c3c6b6e6..05b401ef9c 100644 --- a/metrics/gauge_float64.go +++ b/metrics/gauge_float64.go @@ -5,35 +5,28 @@ import ( "sync/atomic" ) -type GaugeFloat64Snapshot interface { - Value() float64 -} - -// GaugeFloat64 hold a float64 value that can be set arbitrarily. -type GaugeFloat64 interface { - Snapshot() GaugeFloat64Snapshot - Update(float64) -} - // GetOrRegisterGaugeFloat64 returns an existing GaugeFloat64 or constructs and registers a -// new StandardGaugeFloat64. -func GetOrRegisterGaugeFloat64(name string, r Registry) GaugeFloat64 { +// new GaugeFloat64. +func GetOrRegisterGaugeFloat64(name string, r Registry) *GaugeFloat64 { if nil == r { r = DefaultRegistry } - return r.GetOrRegister(name, NewGaugeFloat64()).(GaugeFloat64) + return r.GetOrRegister(name, NewGaugeFloat64()).(*GaugeFloat64) } -// NewGaugeFloat64 constructs a new StandardGaugeFloat64. -func NewGaugeFloat64() GaugeFloat64 { - if !Enabled { - return NilGaugeFloat64{} - } - return &StandardGaugeFloat64{} +// GaugeFloat64Snapshot is a read-only copy of a GaugeFloat64. +type GaugeFloat64Snapshot float64 + +// Value returns the value at the time the snapshot was taken. +func (g GaugeFloat64Snapshot) Value() float64 { return float64(g) } + +// NewGaugeFloat64 constructs a new GaugeFloat64. +func NewGaugeFloat64() *GaugeFloat64 { + return new(GaugeFloat64) } -// NewRegisteredGaugeFloat64 constructs and registers a new StandardGaugeFloat64. -func NewRegisteredGaugeFloat64(name string, r Registry) GaugeFloat64 { +// NewRegisteredGaugeFloat64 constructs and registers a new GaugeFloat64. +func NewRegisteredGaugeFloat64(name string, r Registry) *GaugeFloat64 { c := NewGaugeFloat64() if nil == r { r = DefaultRegistry @@ -42,32 +35,16 @@ func NewRegisteredGaugeFloat64(name string, r Registry) GaugeFloat64 { return c } -// gaugeFloat64Snapshot is a read-only copy of another GaugeFloat64. -type gaugeFloat64Snapshot float64 - -// Value returns the value at the time the snapshot was taken. -func (g gaugeFloat64Snapshot) Value() float64 { return float64(g) } - -// NilGaugeFloat64 is a no-op Gauge. -type NilGaugeFloat64 struct{} - -func (NilGaugeFloat64) Snapshot() GaugeFloat64Snapshot { return NilGaugeFloat64{} } -func (NilGaugeFloat64) Update(v float64) {} -func (NilGaugeFloat64) Value() float64 { return 0.0 } - -// StandardGaugeFloat64 is the standard implementation of a GaugeFloat64 and uses -// atomic to manage a single float64 value. -type StandardGaugeFloat64 struct { - floatBits atomic.Uint64 -} +// GaugeFloat64 hold a float64 value that can be set arbitrarily. +type GaugeFloat64 atomic.Uint64 // Snapshot returns a read-only copy of the gauge. -func (g *StandardGaugeFloat64) Snapshot() GaugeFloat64Snapshot { - v := math.Float64frombits(g.floatBits.Load()) - return gaugeFloat64Snapshot(v) +func (g *GaugeFloat64) Snapshot() GaugeFloat64Snapshot { + v := math.Float64frombits((*atomic.Uint64)(g).Load()) + return GaugeFloat64Snapshot(v) } // Update updates the gauge's value. -func (g *StandardGaugeFloat64) Update(v float64) { - g.floatBits.Store(math.Float64bits(v)) +func (g *GaugeFloat64) Update(v float64) { + (*atomic.Uint64)(g).Store(math.Float64bits(v)) } diff --git a/metrics/gauge_info.go b/metrics/gauge_info.go index 0010edc324..2f78455649 100644 --- a/metrics/gauge_info.go +++ b/metrics/gauge_info.go @@ -5,16 +5,6 @@ import ( "sync" ) -type GaugeInfoSnapshot interface { - Value() GaugeInfoValue -} - -// GaugeInfo holds a GaugeInfoValue value that can be set arbitrarily. -type GaugeInfo interface { - Update(GaugeInfoValue) - Snapshot() GaugeInfoSnapshot -} - // GaugeInfoValue is a mapping of keys to values type GaugeInfoValue map[string]string @@ -24,26 +14,23 @@ func (val GaugeInfoValue) String() string { } // GetOrRegisterGaugeInfo returns an existing GaugeInfo or constructs and registers a -// new StandardGaugeInfo. -func GetOrRegisterGaugeInfo(name string, r Registry) GaugeInfo { +// new GaugeInfo. +func GetOrRegisterGaugeInfo(name string, r Registry) *GaugeInfo { if nil == r { r = DefaultRegistry } - return r.GetOrRegister(name, NewGaugeInfo()).(GaugeInfo) + return r.GetOrRegister(name, NewGaugeInfo()).(*GaugeInfo) } -// NewGaugeInfo constructs a new StandardGaugeInfo. -func NewGaugeInfo() GaugeInfo { - if !Enabled { - return NilGaugeInfo{} - } - return &StandardGaugeInfo{ +// NewGaugeInfo constructs a new GaugeInfo. +func NewGaugeInfo() *GaugeInfo { + return &GaugeInfo{ value: GaugeInfoValue{}, } } -// NewRegisteredGaugeInfo constructs and registers a new StandardGaugeInfo. -func NewRegisteredGaugeInfo(name string, r Registry) GaugeInfo { +// NewRegisteredGaugeInfo constructs and registers a new GaugeInfo. +func NewRegisteredGaugeInfo(name string, r Registry) *GaugeInfo { c := NewGaugeInfo() if nil == r { r = DefaultRegistry @@ -53,31 +40,24 @@ func NewRegisteredGaugeInfo(name string, r Registry) GaugeInfo { } // gaugeInfoSnapshot is a read-only copy of another GaugeInfo. -type gaugeInfoSnapshot GaugeInfoValue +type GaugeInfoSnapshot GaugeInfoValue // Value returns the value at the time the snapshot was taken. -func (g gaugeInfoSnapshot) Value() GaugeInfoValue { return GaugeInfoValue(g) } - -type NilGaugeInfo struct{} - -func (NilGaugeInfo) Snapshot() GaugeInfoSnapshot { return NilGaugeInfo{} } -func (NilGaugeInfo) Update(v GaugeInfoValue) {} -func (NilGaugeInfo) Value() GaugeInfoValue { return GaugeInfoValue{} } +func (g GaugeInfoSnapshot) Value() GaugeInfoValue { return GaugeInfoValue(g) } -// StandardGaugeInfo is the standard implementation of a GaugeInfo and uses -// sync.Mutex to manage a single string value. -type StandardGaugeInfo struct { +// GaugeInfo maintains a set of key/value mappings. +type GaugeInfo struct { mutex sync.Mutex value GaugeInfoValue } // Snapshot returns a read-only copy of the gauge. -func (g *StandardGaugeInfo) Snapshot() GaugeInfoSnapshot { - return gaugeInfoSnapshot(g.value) +func (g *GaugeInfo) Snapshot() GaugeInfoSnapshot { + return GaugeInfoSnapshot(g.value) } // Update updates the gauge's value. -func (g *StandardGaugeInfo) Update(v GaugeInfoValue) { +func (g *GaugeInfo) Update(v GaugeInfoValue) { g.mutex.Lock() defer g.mutex.Unlock() g.value = v diff --git a/metrics/graphite.go b/metrics/graphite.go deleted file mode 100644 index aba752e0ed..0000000000 --- a/metrics/graphite.go +++ /dev/null @@ -1,117 +0,0 @@ -package metrics - -import ( - "bufio" - "fmt" - "log" - "net" - "strconv" - "strings" - "time" -) - -// GraphiteConfig provides a container with configuration parameters for -// the Graphite exporter -type GraphiteConfig struct { - Addr *net.TCPAddr // Network address to connect to - Registry Registry // Registry to be exported - FlushInterval time.Duration // Flush interval - DurationUnit time.Duration // Time conversion unit for durations - Prefix string // Prefix to be prepended to metric names - Percentiles []float64 // Percentiles to export from timers and histograms -} - -// Graphite is a blocking exporter function which reports metrics in r -// to a graphite server located at addr, flushing them every d duration -// and prepending metric names with prefix. -func Graphite(r Registry, d time.Duration, prefix string, addr *net.TCPAddr) { - GraphiteWithConfig(GraphiteConfig{ - Addr: addr, - Registry: r, - FlushInterval: d, - DurationUnit: time.Nanosecond, - Prefix: prefix, - Percentiles: []float64{0.5, 0.75, 0.95, 0.99, 0.999}, - }) -} - -// GraphiteWithConfig is a blocking exporter function just like Graphite, -// but it takes a GraphiteConfig instead. -func GraphiteWithConfig(c GraphiteConfig) { - log.Printf("WARNING: This go-metrics client has been DEPRECATED! It has been moved to https://github.com/cyberdelia/go-metrics-graphite and will be removed from rcrowley/go-metrics on August 12th 2015") - for range time.Tick(c.FlushInterval) { - if err := graphite(&c); nil != err { - log.Println(err) - } - } -} - -// GraphiteOnce performs a single submission to Graphite, returning a -// non-nil error on failed connections. This can be used in a loop -// similar to GraphiteWithConfig for custom error handling. -func GraphiteOnce(c GraphiteConfig) error { - log.Printf("WARNING: This go-metrics client has been DEPRECATED! It has been moved to https://github.com/cyberdelia/go-metrics-graphite and will be removed from rcrowley/go-metrics on August 12th 2015") - return graphite(&c) -} - -func graphite(c *GraphiteConfig) error { - now := time.Now().Unix() - du := float64(c.DurationUnit) - conn, err := net.DialTCP("tcp", nil, c.Addr) - if nil != err { - return err - } - defer conn.Close() - w := bufio.NewWriter(conn) - c.Registry.Each(func(name string, i interface{}) { - switch metric := i.(type) { - case Counter: - fmt.Fprintf(w, "%s.%s.count %d %d\n", c.Prefix, name, metric.Snapshot().Count(), now) - case CounterFloat64: - fmt.Fprintf(w, "%s.%s.count %f %d\n", c.Prefix, name, metric.Snapshot().Count(), now) - case Gauge: - fmt.Fprintf(w, "%s.%s.value %d %d\n", c.Prefix, name, metric.Snapshot().Value(), now) - case GaugeFloat64: - fmt.Fprintf(w, "%s.%s.value %f %d\n", c.Prefix, name, metric.Snapshot().Value(), now) - case GaugeInfo: - fmt.Fprintf(w, "%s.%s.value %s %d\n", c.Prefix, name, metric.Snapshot().Value().String(), now) - case Histogram: - h := metric.Snapshot() - ps := h.Percentiles(c.Percentiles) - fmt.Fprintf(w, "%s.%s.count %d %d\n", c.Prefix, name, h.Count(), now) - fmt.Fprintf(w, "%s.%s.min %d %d\n", c.Prefix, name, h.Min(), now) - fmt.Fprintf(w, "%s.%s.max %d %d\n", c.Prefix, name, h.Max(), now) - fmt.Fprintf(w, "%s.%s.mean %.2f %d\n", c.Prefix, name, h.Mean(), now) - fmt.Fprintf(w, "%s.%s.std-dev %.2f %d\n", c.Prefix, name, h.StdDev(), now) - for psIdx, psKey := range c.Percentiles { - key := strings.Replace(strconv.FormatFloat(psKey*100.0, 'f', -1, 64), ".", "", 1) - fmt.Fprintf(w, "%s.%s.%s-percentile %.2f %d\n", c.Prefix, name, key, ps[psIdx], now) - } - case Meter: - m := metric.Snapshot() - fmt.Fprintf(w, "%s.%s.count %d %d\n", c.Prefix, name, m.Count(), now) - fmt.Fprintf(w, "%s.%s.one-minute %.2f %d\n", c.Prefix, name, m.Rate1(), now) - fmt.Fprintf(w, "%s.%s.five-minute %.2f %d\n", c.Prefix, name, m.Rate5(), now) - fmt.Fprintf(w, "%s.%s.fifteen-minute %.2f %d\n", c.Prefix, name, m.Rate15(), now) - fmt.Fprintf(w, "%s.%s.mean %.2f %d\n", c.Prefix, name, m.RateMean(), now) - case Timer: - t := metric.Snapshot() - ps := t.Percentiles(c.Percentiles) - fmt.Fprintf(w, "%s.%s.count %d %d\n", c.Prefix, name, t.Count(), now) - fmt.Fprintf(w, "%s.%s.min %d %d\n", c.Prefix, name, t.Min()/int64(du), now) - fmt.Fprintf(w, "%s.%s.max %d %d\n", c.Prefix, name, t.Max()/int64(du), now) - fmt.Fprintf(w, "%s.%s.mean %.2f %d\n", c.Prefix, name, t.Mean()/du, now) - fmt.Fprintf(w, "%s.%s.std-dev %.2f %d\n", c.Prefix, name, t.StdDev()/du, now) - for psIdx, psKey := range c.Percentiles { - key := strings.Replace(strconv.FormatFloat(psKey*100.0, 'f', -1, 64), ".", "", 1) - fmt.Fprintf(w, "%s.%s.%s-percentile %.2f %d\n", c.Prefix, name, key, ps[psIdx], now) - } - fmt.Fprintf(w, "%s.%s.one-minute %.2f %d\n", c.Prefix, name, t.Rate1(), now) - fmt.Fprintf(w, "%s.%s.five-minute %.2f %d\n", c.Prefix, name, t.Rate5(), now) - fmt.Fprintf(w, "%s.%s.fifteen-minute %.2f %d\n", c.Prefix, name, t.Rate15(), now) - fmt.Fprintf(w, "%s.%s.mean-rate %.2f %d\n", c.Prefix, name, t.RateMean(), now) - } - w.Flush() - }) - return nil -} diff --git a/metrics/graphite_test.go b/metrics/graphite_test.go deleted file mode 100644 index c797c781df..0000000000 --- a/metrics/graphite_test.go +++ /dev/null @@ -1,22 +0,0 @@ -package metrics - -import ( - "net" - "time" -) - -func ExampleGraphite() { - addr, _ := net.ResolveTCPAddr("net", ":2003") - go Graphite(DefaultRegistry, 1*time.Second, "some.prefix", addr) -} - -func ExampleGraphiteWithConfig() { - addr, _ := net.ResolveTCPAddr("net", ":2003") - go GraphiteWithConfig(GraphiteConfig{ - Addr: addr, - Registry: DefaultRegistry, - FlushInterval: 1 * time.Second, - DurationUnit: time.Millisecond, - Percentiles: []float64{0.5, 0.75, 0.99, 0.999}, - }) -} diff --git a/metrics/healthcheck.go b/metrics/healthcheck.go index adcd15ab58..435e5e0bf9 100644 --- a/metrics/healthcheck.go +++ b/metrics/healthcheck.go @@ -1,61 +1,35 @@ package metrics -// Healthcheck holds an error value describing an arbitrary up/down status. -type Healthcheck interface { - Check() - Error() error - Healthy() - Unhealthy(error) -} - // NewHealthcheck constructs a new Healthcheck which will use the given // function to update its status. -func NewHealthcheck(f func(Healthcheck)) Healthcheck { - if !Enabled { - return NilHealthcheck{} - } - return &StandardHealthcheck{nil, f} +func NewHealthcheck(f func(*Healthcheck)) *Healthcheck { + return &Healthcheck{nil, f} } -// NilHealthcheck is a no-op. -type NilHealthcheck struct{} - -// Check is a no-op. -func (NilHealthcheck) Check() {} - -// Error is a no-op. -func (NilHealthcheck) Error() error { return nil } - -// Healthy is a no-op. -func (NilHealthcheck) Healthy() {} - -// Unhealthy is a no-op. -func (NilHealthcheck) Unhealthy(error) {} - -// StandardHealthcheck is the standard implementation of a Healthcheck and +// Healthcheck is the standard implementation of a Healthcheck and // stores the status and a function to call to update the status. -type StandardHealthcheck struct { +type Healthcheck struct { err error - f func(Healthcheck) + f func(*Healthcheck) } // Check runs the healthcheck function to update the healthcheck's status. -func (h *StandardHealthcheck) Check() { +func (h *Healthcheck) Check() { h.f(h) } // Error returns the healthcheck's status, which will be nil if it is healthy. -func (h *StandardHealthcheck) Error() error { +func (h *Healthcheck) Error() error { return h.err } // Healthy marks the healthcheck as healthy. -func (h *StandardHealthcheck) Healthy() { +func (h *Healthcheck) Healthy() { h.err = nil } // Unhealthy marks the healthcheck as unhealthy. The error is stored and // may be retrieved by the Error method. -func (h *StandardHealthcheck) Unhealthy(err error) { +func (h *Healthcheck) Unhealthy(err error) { h.err = err } diff --git a/metrics/histogram.go b/metrics/histogram.go index 10259a2463..7c27bcc928 100644 --- a/metrics/histogram.go +++ b/metrics/histogram.go @@ -1,7 +1,16 @@ package metrics type HistogramSnapshot interface { - SampleSnapshot + Count() int64 + Max() int64 + Mean() float64 + Min() int64 + Percentile(float64) float64 + Percentiles([]float64) []float64 + Size() int + StdDev() float64 + Sum() int64 + Variance() float64 } // Histogram calculates distribution statistics from a series of int64 values. @@ -31,10 +40,7 @@ func GetOrRegisterHistogramLazy(name string, r Registry, s func() Sample) Histog // NewHistogram constructs a new StandardHistogram from a Sample. func NewHistogram(s Sample) Histogram { - if !Enabled { - return NilHistogram{} - } - return &StandardHistogram{sample: s} + return &StandardHistogram{s} } // NewRegisteredHistogram constructs and registers a new StandardHistogram from @@ -48,13 +54,6 @@ func NewRegisteredHistogram(name string, r Registry, s Sample) Histogram { return c } -// NilHistogram is a no-op Histogram. -type NilHistogram struct{} - -func (NilHistogram) Clear() {} -func (NilHistogram) Snapshot() HistogramSnapshot { return (*emptySnapshot)(nil) } -func (NilHistogram) Update(v int64) {} - // StandardHistogram is the standard implementation of a Histogram and uses a // Sample to bound its memory use. type StandardHistogram struct { diff --git a/metrics/inactive.go b/metrics/inactive.go deleted file mode 100644 index 1f47f0210a..0000000000 --- a/metrics/inactive.go +++ /dev/null @@ -1,48 +0,0 @@ -// Copyright 2023 The go-ethereum Authors -// This file is part of the go-ethereum library. -// -// The go-ethereum library is free software: you can redistribute it and/or modify -// it under the terms of the GNU Lesser General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// The go-ethereum library is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU Lesser General Public License for more details. -// -// You should have received a copy of the GNU Lesser General Public License -// along with the go-ethereum library. If not, see . - -package metrics - -// compile-time checks that interfaces are implemented. -var ( - _ SampleSnapshot = (*emptySnapshot)(nil) - _ HistogramSnapshot = (*emptySnapshot)(nil) - _ CounterSnapshot = (*emptySnapshot)(nil) - _ GaugeSnapshot = (*emptySnapshot)(nil) - _ MeterSnapshot = (*emptySnapshot)(nil) - _ EWMASnapshot = (*emptySnapshot)(nil) - _ TimerSnapshot = (*emptySnapshot)(nil) -) - -type emptySnapshot struct{} - -func (*emptySnapshot) Count() int64 { return 0 } -func (*emptySnapshot) Max() int64 { return 0 } -func (*emptySnapshot) Mean() float64 { return 0.0 } -func (*emptySnapshot) Min() int64 { return 0 } -func (*emptySnapshot) Percentile(p float64) float64 { return 0.0 } -func (*emptySnapshot) Percentiles(ps []float64) []float64 { return make([]float64, len(ps)) } -func (*emptySnapshot) Size() int { return 0 } -func (*emptySnapshot) StdDev() float64 { return 0.0 } -func (*emptySnapshot) Sum() int64 { return 0 } -func (*emptySnapshot) Values() []int64 { return []int64{} } -func (*emptySnapshot) Variance() float64 { return 0.0 } -func (*emptySnapshot) Value() int64 { return 0 } -func (*emptySnapshot) Rate() float64 { return 0.0 } -func (*emptySnapshot) Rate1() float64 { return 0.0 } -func (*emptySnapshot) Rate5() float64 { return 0.0 } -func (*emptySnapshot) Rate15() float64 { return 0.0 } -func (*emptySnapshot) RateMean() float64 { return 0.0 } diff --git a/metrics/influxdb/influxdb.go b/metrics/influxdb/influxdb.go index 5c8501fd9d..11f6c3ad22 100644 --- a/metrics/influxdb/influxdb.go +++ b/metrics/influxdb/influxdb.go @@ -8,31 +8,31 @@ import ( func readMeter(namespace, name string, i interface{}) (string, map[string]interface{}) { switch metric := i.(type) { - case metrics.Counter: + case *metrics.Counter: measurement := fmt.Sprintf("%s%s.count", namespace, name) fields := map[string]interface{}{ "value": metric.Snapshot().Count(), } return measurement, fields - case metrics.CounterFloat64: + case *metrics.CounterFloat64: measurement := fmt.Sprintf("%s%s.count", namespace, name) fields := map[string]interface{}{ "value": metric.Snapshot().Count(), } return measurement, fields - case metrics.Gauge: + case *metrics.Gauge: measurement := fmt.Sprintf("%s%s.gauge", namespace, name) fields := map[string]interface{}{ "value": metric.Snapshot().Value(), } return measurement, fields - case metrics.GaugeFloat64: + case *metrics.GaugeFloat64: measurement := fmt.Sprintf("%s%s.gauge", namespace, name) fields := map[string]interface{}{ "value": metric.Snapshot().Value(), } return measurement, fields - case metrics.GaugeInfo: + case *metrics.GaugeInfo: ms := metric.Snapshot() measurement := fmt.Sprintf("%s%s.gauge", namespace, name) fields := map[string]interface{}{ @@ -62,7 +62,7 @@ func readMeter(namespace, name string, i interface{}) (string, map[string]interf "p9999": ps[6], } return measurement, fields - case metrics.Meter: + case *metrics.Meter: ms := metric.Snapshot() measurement := fmt.Sprintf("%s%s.meter", namespace, name) fields := map[string]interface{}{ @@ -73,7 +73,7 @@ func readMeter(namespace, name string, i interface{}) (string, map[string]interf "mean": ms.RateMean(), } return measurement, fields - case metrics.Timer: + case *metrics.Timer: ms := metric.Snapshot() ps := ms.Percentiles([]float64{0.5, 0.75, 0.95, 0.99, 0.999, 0.9999}) @@ -97,7 +97,7 @@ func readMeter(namespace, name string, i interface{}) (string, map[string]interf "meanrate": ms.RateMean(), } return measurement, fields - case metrics.ResettingTimer: + case *metrics.ResettingTimer: ms := metric.Snapshot() if ms.Count() == 0 { break diff --git a/metrics/influxdb/influxdb_test.go b/metrics/influxdb/influxdb_test.go index 2b757897d5..e0cbbf7860 100644 --- a/metrics/influxdb/influxdb_test.go +++ b/metrics/influxdb/influxdb_test.go @@ -33,7 +33,7 @@ import ( ) func TestMain(m *testing.M) { - metrics.Enabled = true + metrics.Enable() os.Exit(m.Run()) } diff --git a/metrics/init_test.go b/metrics/init_test.go index 43401e833c..af75bee425 100644 --- a/metrics/init_test.go +++ b/metrics/init_test.go @@ -1,5 +1,5 @@ package metrics func init() { - Enabled = true + metricsEnabled = true } diff --git a/metrics/log.go b/metrics/log.go index 3b9773faa7..3380bbf9c4 100644 --- a/metrics/log.go +++ b/metrics/log.go @@ -21,25 +21,21 @@ func LogScaled(r Registry, freq time.Duration, scale time.Duration, l Logger) { for range time.Tick(freq) { r.Each(func(name string, i interface{}) { switch metric := i.(type) { - case Counter: + case *Counter: l.Printf("counter %s\n", name) l.Printf(" count: %9d\n", metric.Snapshot().Count()) - case CounterFloat64: + case *CounterFloat64: l.Printf("counter %s\n", name) l.Printf(" count: %f\n", metric.Snapshot().Count()) - case Gauge: + case *Gauge: l.Printf("gauge %s\n", name) l.Printf(" value: %9d\n", metric.Snapshot().Value()) - case GaugeFloat64: + case *GaugeFloat64: l.Printf("gauge %s\n", name) l.Printf(" value: %f\n", metric.Snapshot().Value()) - case GaugeInfo: + case *GaugeInfo: l.Printf("gauge %s\n", name) l.Printf(" value: %s\n", metric.Snapshot().Value()) - case Healthcheck: - metric.Check() - l.Printf("healthcheck %s\n", name) - l.Printf(" error: %v\n", metric.Error()) case Histogram: h := metric.Snapshot() ps := h.Percentiles([]float64{0.5, 0.75, 0.95, 0.99, 0.999}) @@ -54,7 +50,7 @@ func LogScaled(r Registry, freq time.Duration, scale time.Duration, l Logger) { l.Printf(" 95%%: %12.2f\n", ps[2]) l.Printf(" 99%%: %12.2f\n", ps[3]) l.Printf(" 99.9%%: %12.2f\n", ps[4]) - case Meter: + case *Meter: m := metric.Snapshot() l.Printf("meter %s\n", name) l.Printf(" count: %9d\n", m.Count()) @@ -62,7 +58,7 @@ func LogScaled(r Registry, freq time.Duration, scale time.Duration, l Logger) { l.Printf(" 5-min rate: %12.2f\n", m.Rate5()) l.Printf(" 15-min rate: %12.2f\n", m.Rate15()) l.Printf(" mean rate: %12.2f\n", m.RateMean()) - case Timer: + case *Timer: t := metric.Snapshot() ps := t.Percentiles([]float64{0.5, 0.75, 0.95, 0.99, 0.999}) l.Printf("timer %s\n", name) diff --git a/metrics/meter.go b/metrics/meter.go index 432838f4ef..194bd1f304 100644 --- a/metrics/meter.go +++ b/metrics/meter.go @@ -7,114 +7,78 @@ import ( "time" ) -type MeterSnapshot interface { - Count() int64 - Rate1() float64 - Rate5() float64 - Rate15() float64 - RateMean() float64 -} - -// Meters count events to produce exponentially-weighted moving average rates -// at one-, five-, and fifteen-minutes and a mean rate. -type Meter interface { - Mark(int64) - Snapshot() MeterSnapshot - Stop() -} - // GetOrRegisterMeter returns an existing Meter or constructs and registers a -// new StandardMeter. +// new Meter. // Be sure to unregister the meter from the registry once it is of no use to // allow for garbage collection. -func GetOrRegisterMeter(name string, r Registry) Meter { - if nil == r { +func GetOrRegisterMeter(name string, r Registry) *Meter { + if r == nil { r = DefaultRegistry } - return r.GetOrRegister(name, NewMeter).(Meter) + return r.GetOrRegister(name, NewMeter).(*Meter) } -// NewMeter constructs a new StandardMeter and launches a goroutine. +// NewMeter constructs a new Meter and launches a goroutine. // Be sure to call Stop() once the meter is of no use to allow for garbage collection. -func NewMeter() Meter { - if !Enabled { - return NilMeter{} - } - m := newStandardMeter() - arbiter.Lock() - defer arbiter.Unlock() - arbiter.meters[m] = struct{}{} - if !arbiter.started { - arbiter.started = true - go arbiter.tick() - } +func NewMeter() *Meter { + m := newMeter() + arbiter.add(m) return m } // NewInactiveMeter returns a meter but does not start any goroutines. This // method is mainly intended for testing. -func NewInactiveMeter() Meter { - if !Enabled { - return NilMeter{} - } - m := newStandardMeter() - return m +func NewInactiveMeter() *Meter { + return newMeter() } -// NewRegisteredMeter constructs and registers a new StandardMeter +// NewRegisteredMeter constructs and registers a new Meter // and launches a goroutine. // Be sure to unregister the meter from the registry once it is of no use to // allow for garbage collection. -func NewRegisteredMeter(name string, r Registry) Meter { +func NewRegisteredMeter(name string, r Registry) *Meter { return GetOrRegisterMeter(name, r) } -// meterSnapshot is a read-only copy of the meter's internal values. -type meterSnapshot struct { +// MeterSnapshot is a read-only copy of the meter's internal values. +type MeterSnapshot struct { count int64 rate1, rate5, rate15, rateMean float64 } // Count returns the count of events at the time the snapshot was taken. -func (m *meterSnapshot) Count() int64 { return m.count } +func (m *MeterSnapshot) Count() int64 { return m.count } // Rate1 returns the one-minute moving average rate of events per second at the // time the snapshot was taken. -func (m *meterSnapshot) Rate1() float64 { return m.rate1 } +func (m *MeterSnapshot) Rate1() float64 { return m.rate1 } // Rate5 returns the five-minute moving average rate of events per second at // the time the snapshot was taken. -func (m *meterSnapshot) Rate5() float64 { return m.rate5 } +func (m *MeterSnapshot) Rate5() float64 { return m.rate5 } // Rate15 returns the fifteen-minute moving average rate of events per second // at the time the snapshot was taken. -func (m *meterSnapshot) Rate15() float64 { return m.rate15 } +func (m *MeterSnapshot) Rate15() float64 { return m.rate15 } // RateMean returns the meter's mean rate of events per second at the time the // snapshot was taken. -func (m *meterSnapshot) RateMean() float64 { return m.rateMean } - -// NilMeter is a no-op Meter. -type NilMeter struct{} +func (m *MeterSnapshot) RateMean() float64 { return m.rateMean } -func (NilMeter) Count() int64 { return 0 } -func (NilMeter) Mark(n int64) {} -func (NilMeter) Snapshot() MeterSnapshot { return (*emptySnapshot)(nil) } -func (NilMeter) Stop() {} - -// StandardMeter is the standard implementation of a Meter. -type StandardMeter struct { +// Meter count events to produce exponentially-weighted moving average rates +// at one-, five-, and fifteen-minutes and a mean rate. +type Meter struct { count atomic.Int64 uncounted atomic.Int64 // not yet added to the EWMAs rateMean atomic.Uint64 - a1, a5, a15 EWMA + a1, a5, a15 *EWMA startTime time.Time stopped atomic.Bool } -func newStandardMeter() *StandardMeter { - return &StandardMeter{ +func newMeter() *Meter { + return &Meter{ a1: NewEWMA1(), a5: NewEWMA5(), a15: NewEWMA15(), @@ -123,22 +87,20 @@ func newStandardMeter() *StandardMeter { } // Stop stops the meter, Mark() will be a no-op if you use it after being stopped. -func (m *StandardMeter) Stop() { +func (m *Meter) Stop() { if stopped := m.stopped.Swap(true); !stopped { - arbiter.Lock() - delete(arbiter.meters, m) - arbiter.Unlock() + arbiter.remove(m) } } // Mark records the occurrence of n events. -func (m *StandardMeter) Mark(n int64) { +func (m *Meter) Mark(n int64) { m.uncounted.Add(n) } // Snapshot returns a read-only copy of the meter. -func (m *StandardMeter) Snapshot() MeterSnapshot { - return &meterSnapshot{ +func (m *Meter) Snapshot() *MeterSnapshot { + return &MeterSnapshot{ count: m.count.Load() + m.uncounted.Load(), rate1: m.a1.Snapshot().Rate(), rate5: m.a5.Snapshot().Rate(), @@ -147,7 +109,7 @@ func (m *StandardMeter) Snapshot() MeterSnapshot { } } -func (m *StandardMeter) tick() { +func (m *Meter) tick() { // Take the uncounted values, add to count n := m.uncounted.Swap(0) count := m.count.Add(n) @@ -157,33 +119,51 @@ func (m *StandardMeter) tick() { m.a5.Update(n) m.a15.Update(n) // And trigger them to calculate the rates - m.a1.Tick() - m.a5.Tick() - m.a15.Tick() + m.a1.tick() + m.a5.tick() + m.a15.tick() } -// meterArbiter ticks meters every 5s from a single goroutine. +var arbiter = meterTicker{meters: make(map[*Meter]struct{})} + +// meterTicker ticks meters every 5s from a single goroutine. // meters are references in a set for future stopping. -type meterArbiter struct { - sync.RWMutex +type meterTicker struct { + mu sync.RWMutex + started bool - meters map[*StandardMeter]struct{} - ticker *time.Ticker + meters map[*Meter]struct{} } -var arbiter = meterArbiter{ticker: time.NewTicker(5 * time.Second), meters: make(map[*StandardMeter]struct{})} - -// tick meters on the scheduled interval -func (ma *meterArbiter) tick() { - for range ma.ticker.C { - ma.tickMeters() +// add adds another *Meter ot the arbiter, and starts the arbiter ticker. +func (ma *meterTicker) add(m *Meter) { + ma.mu.Lock() + defer ma.mu.Unlock() + ma.meters[m] = struct{}{} + if !ma.started { + ma.started = true + go ma.loop() } } -func (ma *meterArbiter) tickMeters() { - ma.RLock() - defer ma.RUnlock() - for meter := range ma.meters { - meter.tick() +// remove removes a meter from the set of ticked meters. +func (ma *meterTicker) remove(m *Meter) { + ma.mu.Lock() + delete(ma.meters, m) + ma.mu.Unlock() +} + +// loop ticks meters on a 5 second interval. +func (ma *meterTicker) loop() { + ticker := time.NewTicker(5 * time.Second) + for range ticker.C { + if !metricsEnabled { + continue + } + ma.mu.RLock() + for meter := range ma.meters { + meter.tick() + } + ma.mu.RUnlock() } } diff --git a/metrics/meter_test.go b/metrics/meter_test.go index 019c4d765b..e3f39684bd 100644 --- a/metrics/meter_test.go +++ b/metrics/meter_test.go @@ -28,18 +28,12 @@ func TestGetOrRegisterMeter(t *testing.T) { } func TestMeterDecay(t *testing.T) { - ma := meterArbiter{ - ticker: time.NewTicker(time.Millisecond), - meters: make(map[*StandardMeter]struct{}), - } - defer ma.ticker.Stop() - m := newStandardMeter() - ma.meters[m] = struct{}{} + m := newMeter() m.Mark(1) - ma.tickMeters() + m.tick() rateMean := m.Snapshot().RateMean() time.Sleep(100 * time.Millisecond) - ma.tickMeters() + m.tick() if m.Snapshot().RateMean() >= rateMean { t.Error("m.RateMean() didn't decrease") } diff --git a/metrics/metrics.go b/metrics/metrics.go index 213ebf6b56..3602e88d5e 100644 --- a/metrics/metrics.go +++ b/metrics/metrics.go @@ -6,52 +6,29 @@ package metrics import ( - "os" "runtime/metrics" "runtime/pprof" - "strconv" - "strings" - "syscall" "time" +) - "github.com/ethereum/go-ethereum/log" +var ( + metricsEnabled = false ) -// Enabled is checked by the constructor functions for all of the -// standard metrics. If it is true, the metric returned is a stub. +// Enabled is checked by functions that are deemed 'expensive', e.g. if a +// meter-type does locking and/or non-trivial math operations during update. +func Enabled() bool { + return metricsEnabled +} + +// Enable enables the metrics system. +// The Enabled-flag is expected to be set, once, during startup, but toggling off and on +// is not supported. // -// This global kill-switch helps quantify the observer effect and makes -// for less cluttered pprof profiles. -var Enabled = false - -// enablerFlags is the CLI flag names to use to enable metrics collections. -var enablerFlags = []string{"metrics"} - -// enablerEnvVars is the env var names to use to enable metrics collections. -var enablerEnvVars = []string{"GETH_METRICS"} - -// init enables or disables the metrics system. Since we need this to run before -// any other code gets to create meters and timers, we'll actually do an ugly hack -// and peek into the command line args for the metrics flag. -func init() { - for _, enabler := range enablerEnvVars { - if val, found := syscall.Getenv(enabler); found && !Enabled { - if enable, _ := strconv.ParseBool(val); enable { // ignore error, flag parser will choke on it later - log.Info("Enabling metrics collection") - Enabled = true - } - } - } - for _, arg := range os.Args { - flag := strings.TrimLeft(arg, "-") - - for _, enabler := range enablerFlags { - if !Enabled && flag == enabler { - log.Info("Enabling metrics collection") - Enabled = true - } - } - } +// Enable is not safe to call concurrently. You need to call this as early as possible in +// the program, before any metrics collection will happen. +func Enable() { + metricsEnabled = true } var threadCreateProfile = pprof.Lookup("threadcreate") @@ -128,7 +105,7 @@ func readRuntimeStats(v *runtimeStats) { // CollectProcessMetrics periodically collects various metrics about the running process. func CollectProcessMetrics(refresh time.Duration) { // Short circuit if the metrics system is disabled - if !Enabled { + if !metricsEnabled { return } diff --git a/metrics/metrics_test.go b/metrics/metrics_test.go index 2861d5f2ca..dc144f2425 100644 --- a/metrics/metrics_test.go +++ b/metrics/metrics_test.go @@ -7,8 +7,6 @@ import ( "time" ) -const FANOUT = 128 - func TestReadRuntimeValues(t *testing.T) { var v runtimeStats readRuntimeStats(&v) @@ -16,60 +14,23 @@ func TestReadRuntimeValues(t *testing.T) { } func BenchmarkMetrics(b *testing.B) { - r := NewRegistry() - c := NewRegisteredCounter("counter", r) - cf := NewRegisteredCounterFloat64("counterfloat64", r) - g := NewRegisteredGauge("gauge", r) - gf := NewRegisteredGaugeFloat64("gaugefloat64", r) - h := NewRegisteredHistogram("histogram", r, NewUniformSample(100)) - m := NewRegisteredMeter("meter", r) - t := NewRegisteredTimer("timer", r) + var ( + r = NewRegistry() + c = NewRegisteredCounter("counter", r) + cf = NewRegisteredCounterFloat64("counterfloat64", r) + g = NewRegisteredGauge("gauge", r) + gf = NewRegisteredGaugeFloat64("gaugefloat64", r) + h = NewRegisteredHistogram("histogram", r, NewUniformSample(100)) + m = NewRegisteredMeter("meter", r) + t = NewRegisteredTimer("timer", r) + ) RegisterDebugGCStats(r) b.ResetTimer() - ch := make(chan bool) - - wgD := &sync.WaitGroup{} - /* - wgD.Add(1) - go func() { - defer wgD.Done() - //log.Println("go CaptureDebugGCStats") - for { - select { - case <-ch: - //log.Println("done CaptureDebugGCStats") - return - default: - CaptureDebugGCStatsOnce(r) - } - } - }() - //*/ - - wgW := &sync.WaitGroup{} - /* - wgW.Add(1) + var wg sync.WaitGroup + wg.Add(128) + for i := 0; i < 128; i++ { go func() { - defer wgW.Done() - //log.Println("go Write") - for { - select { - case <-ch: - //log.Println("done Write") - return - default: - WriteOnce(r, io.Discard) - } - } - }() - //*/ - - wg := &sync.WaitGroup{} - wg.Add(FANOUT) - for i := 0; i < FANOUT; i++ { - go func(i int) { defer wg.Done() - //log.Println("go", i) for i := 0; i < b.N; i++ { c.Inc(1) cf.Inc(1.0) @@ -79,13 +40,9 @@ func BenchmarkMetrics(b *testing.B) { m.Mark(1) t.Update(1) } - //log.Println("done", i) - }(i) + }() } wg.Wait() - close(ch) - wgD.Wait() - wgW.Wait() } func Example() { diff --git a/metrics/opentsdb.go b/metrics/opentsdb.go index e81690f943..57af3d025e 100644 --- a/metrics/opentsdb.go +++ b/metrics/opentsdb.go @@ -64,15 +64,15 @@ func (c *OpenTSDBConfig) writeRegistry(w io.Writer, now int64, shortHostname str c.Registry.Each(func(name string, i interface{}) { switch metric := i.(type) { - case Counter: + case *Counter: fmt.Fprintf(w, "put %s.%s.count %d %d host=%s\n", c.Prefix, name, now, metric.Snapshot().Count(), shortHostname) - case CounterFloat64: + case *CounterFloat64: fmt.Fprintf(w, "put %s.%s.count %d %f host=%s\n", c.Prefix, name, now, metric.Snapshot().Count(), shortHostname) - case Gauge: + case *Gauge: fmt.Fprintf(w, "put %s.%s.value %d %d host=%s\n", c.Prefix, name, now, metric.Snapshot().Value(), shortHostname) - case GaugeFloat64: + case *GaugeFloat64: fmt.Fprintf(w, "put %s.%s.value %d %f host=%s\n", c.Prefix, name, now, metric.Snapshot().Value(), shortHostname) - case GaugeInfo: + case *GaugeInfo: fmt.Fprintf(w, "put %s.%s.value %d %s host=%s\n", c.Prefix, name, now, metric.Snapshot().Value().String(), shortHostname) case Histogram: h := metric.Snapshot() @@ -87,14 +87,14 @@ func (c *OpenTSDBConfig) writeRegistry(w io.Writer, now int64, shortHostname str fmt.Fprintf(w, "put %s.%s.95-percentile %d %.2f host=%s\n", c.Prefix, name, now, ps[2], shortHostname) fmt.Fprintf(w, "put %s.%s.99-percentile %d %.2f host=%s\n", c.Prefix, name, now, ps[3], shortHostname) fmt.Fprintf(w, "put %s.%s.999-percentile %d %.2f host=%s\n", c.Prefix, name, now, ps[4], shortHostname) - case Meter: + case *Meter: m := metric.Snapshot() fmt.Fprintf(w, "put %s.%s.count %d %d host=%s\n", c.Prefix, name, now, m.Count(), shortHostname) fmt.Fprintf(w, "put %s.%s.one-minute %d %.2f host=%s\n", c.Prefix, name, now, m.Rate1(), shortHostname) fmt.Fprintf(w, "put %s.%s.five-minute %d %.2f host=%s\n", c.Prefix, name, now, m.Rate5(), shortHostname) fmt.Fprintf(w, "put %s.%s.fifteen-minute %d %.2f host=%s\n", c.Prefix, name, now, m.Rate15(), shortHostname) fmt.Fprintf(w, "put %s.%s.mean %d %.2f host=%s\n", c.Prefix, name, now, m.RateMean(), shortHostname) - case Timer: + case *Timer: t := metric.Snapshot() ps := t.Percentiles([]float64{0.5, 0.75, 0.95, 0.99, 0.999}) fmt.Fprintf(w, "put %s.%s.count %d %d host=%s\n", c.Prefix, name, now, t.Count(), shortHostname) diff --git a/metrics/prometheus/collector.go b/metrics/prometheus/collector.go index f330e62a2e..8665f1191c 100644 --- a/metrics/prometheus/collector.go +++ b/metrics/prometheus/collector.go @@ -52,23 +52,23 @@ func newCollector() *collector { // metric type is not supported/known. func (c *collector) Add(name string, i any) error { switch m := i.(type) { - case metrics.Counter: + case *metrics.Counter: c.addCounter(name, m.Snapshot()) - case metrics.CounterFloat64: + case *metrics.CounterFloat64: c.addCounterFloat64(name, m.Snapshot()) - case metrics.Gauge: + case *metrics.Gauge: c.addGauge(name, m.Snapshot()) - case metrics.GaugeFloat64: + case *metrics.GaugeFloat64: c.addGaugeFloat64(name, m.Snapshot()) - case metrics.GaugeInfo: + case *metrics.GaugeInfo: c.addGaugeInfo(name, m.Snapshot()) case metrics.Histogram: c.addHistogram(name, m.Snapshot()) - case metrics.Meter: + case *metrics.Meter: c.addMeter(name, m.Snapshot()) - case metrics.Timer: + case *metrics.Timer: c.addTimer(name, m.Snapshot()) - case metrics.ResettingTimer: + case *metrics.ResettingTimer: c.addResettingTimer(name, m.Snapshot()) case metrics.Label: c.addLabel(name, m) @@ -109,11 +109,11 @@ func (c *collector) addHistogram(name string, m metrics.HistogramSnapshot) { c.buff.WriteRune('\n') } -func (c *collector) addMeter(name string, m metrics.MeterSnapshot) { +func (c *collector) addMeter(name string, m *metrics.MeterSnapshot) { c.writeGaugeCounter(name, m.Count()) } -func (c *collector) addTimer(name string, m metrics.TimerSnapshot) { +func (c *collector) addTimer(name string, m *metrics.TimerSnapshot) { pv := []float64{0.5, 0.75, 0.95, 0.99, 0.999, 0.9999} ps := m.Percentiles(pv) c.writeSummaryCounter(name, m.Count()) @@ -124,7 +124,7 @@ func (c *collector) addTimer(name string, m metrics.TimerSnapshot) { c.buff.WriteRune('\n') } -func (c *collector) addResettingTimer(name string, m metrics.ResettingTimerSnapshot) { +func (c *collector) addResettingTimer(name string, m *metrics.ResettingTimerSnapshot) { if m.Count() <= 0 { return } diff --git a/metrics/prometheus/collector_test.go b/metrics/prometheus/collector_test.go index ea17aac458..a8585d1226 100644 --- a/metrics/prometheus/collector_test.go +++ b/metrics/prometheus/collector_test.go @@ -27,7 +27,7 @@ import ( ) func TestMain(m *testing.M) { - metrics.Enabled = true + metrics.Enable() os.Exit(m.Run()) } diff --git a/metrics/registry.go b/metrics/registry.go index 57324160f8..f65334d755 100644 --- a/metrics/registry.go +++ b/metrics/registry.go @@ -1,6 +1,7 @@ package metrics import ( + "errors" "fmt" "reflect" "sort" @@ -8,14 +9,10 @@ import ( "sync" ) -// DuplicateMetric is the error returned by Registry. Register when a metric +// ErrDuplicateMetric is the error returned by Registry.Register when a metric // already exists. If you mean to Register that metric you must first // Unregister the existing metric. -type DuplicateMetric string - -func (err DuplicateMetric) Error() string { - return fmt.Sprintf("duplicate metric: %s", string(err)) -} +var ErrDuplicateMetric = errors.New("duplicate metric") // A Registry holds references to a set of metrics by name and can iterate // over them, calling callback functions provided by the user. @@ -114,13 +111,13 @@ func (r *StandardRegistry) GetOrRegister(name string, i interface{}) interface{} return item } -// Register the given metric under the given name. Returns a DuplicateMetric +// Register the given metric under the given name. Returns a ErrDuplicateMetric // if a metric by the given name is already registered. func (r *StandardRegistry) Register(name string, i interface{}) error { // fast path _, ok := r.metrics.Load(name) if ok { - return DuplicateMetric(name) + return fmt.Errorf("%w: %v", ErrDuplicateMetric, name) } if v := reflect.ValueOf(i); v.Kind() == reflect.Func { @@ -128,7 +125,7 @@ func (r *StandardRegistry) Register(name string, i interface{}) error { } _, loaded, _ := r.loadOrRegister(name, i) if loaded { - return DuplicateMetric(name) + return fmt.Errorf("%w: %v", ErrDuplicateMetric, name) } return nil } @@ -136,7 +133,7 @@ func (r *StandardRegistry) Register(name string, i interface{}) error { // RunHealthchecks run all registered healthchecks. func (r *StandardRegistry) RunHealthchecks() { r.metrics.Range(func(key, value any) bool { - if h, ok := value.(Healthcheck); ok { + if h, ok := value.(*Healthcheck); ok { h.Check() } return true @@ -149,15 +146,15 @@ func (r *StandardRegistry) GetAll() map[string]map[string]interface{} { r.Each(func(name string, i interface{}) { values := make(map[string]interface{}) switch metric := i.(type) { - case Counter: + case *Counter: values["count"] = metric.Snapshot().Count() - case CounterFloat64: + case *CounterFloat64: values["count"] = metric.Snapshot().Count() - case Gauge: + case *Gauge: values["value"] = metric.Snapshot().Value() - case GaugeFloat64: + case *GaugeFloat64: values["value"] = metric.Snapshot().Value() - case Healthcheck: + case *Healthcheck: values["error"] = nil metric.Check() if err := metric.Error(); nil != err { @@ -176,14 +173,14 @@ func (r *StandardRegistry) GetAll() map[string]map[string]interface{} { values["95%"] = ps[2] values["99%"] = ps[3] values["99.9%"] = ps[4] - case Meter: + case *Meter: m := metric.Snapshot() values["count"] = m.Count() values["1m.rate"] = m.Rate1() values["5m.rate"] = m.Rate5() values["15m.rate"] = m.Rate15() values["mean.rate"] = m.RateMean() - case Timer: + case *Timer: t := metric.Snapshot() ps := t.Percentiles([]float64{0.5, 0.75, 0.95, 0.99, 0.999}) values["count"] = t.Count() @@ -214,7 +211,7 @@ func (r *StandardRegistry) Unregister(name string) { func (r *StandardRegistry) loadOrRegister(name string, i interface{}) (interface{}, bool, bool) { switch i.(type) { - case Counter, CounterFloat64, Gauge, GaugeFloat64, GaugeInfo, Healthcheck, Histogram, Meter, Timer, ResettingTimer, Label: + case *Counter, *CounterFloat64, *Gauge, *GaugeFloat64, *GaugeInfo, *Healthcheck, Histogram, *Meter, *Timer, *ResettingTimer, *Label: default: return nil, false, false } @@ -326,9 +323,7 @@ func (r *PrefixedRegistry) Unregister(name string) { } var ( - DefaultRegistry = NewRegistry() - EphemeralRegistry = NewRegistry() - AccountingRegistry = NewRegistry() // registry used in swarm + DefaultRegistry = NewRegistry() ) // Each call the given function for each registered metric. @@ -347,7 +342,7 @@ func GetOrRegister(name string, i interface{}) interface{} { return DefaultRegistry.GetOrRegister(name, i) } -// Register the given metric under the given name. Returns a DuplicateMetric +// Register the given metric under the given name. Returns a ErrDuplicateMetric // if a metric by the given name is already registered. func Register(name string, i interface{}) error { return DefaultRegistry.Register(name, i) diff --git a/metrics/registry_test.go b/metrics/registry_test.go index 75012dd4ac..bdc58fee6c 100644 --- a/metrics/registry_test.go +++ b/metrics/registry_test.go @@ -47,7 +47,7 @@ func TestRegistry(t *testing.T) { if name != "foo" { t.Fatal(name) } - if _, ok := iface.(Counter); !ok { + if _, ok := iface.(*Counter); !ok { t.Fatal(iface) } }) @@ -73,7 +73,7 @@ func TestRegistryDuplicate(t *testing.T) { i := 0 r.Each(func(name string, iface interface{}) { i++ - if _, ok := iface.(Counter); !ok { + if _, ok := iface.(*Counter); !ok { t.Fatal(iface) } }) @@ -85,11 +85,11 @@ func TestRegistryDuplicate(t *testing.T) { func TestRegistryGet(t *testing.T) { r := NewRegistry() r.Register("foo", NewCounter()) - if count := r.Get("foo").(Counter).Snapshot().Count(); count != 0 { + if count := r.Get("foo").(*Counter).Snapshot().Count(); count != 0 { t.Fatal(count) } - r.Get("foo").(Counter).Inc(1) - if count := r.Get("foo").(Counter).Snapshot().Count(); count != 1 { + r.Get("foo").(*Counter).Inc(1) + if count := r.Get("foo").(*Counter).Snapshot().Count(); count != 1 { t.Fatal(count) } } @@ -100,7 +100,7 @@ func TestRegistryGetOrRegister(t *testing.T) { // First metric wins with GetOrRegister _ = r.GetOrRegister("foo", NewCounter()) m := r.GetOrRegister("foo", NewGauge()) - if _, ok := m.(Counter); !ok { + if _, ok := m.(*Counter); !ok { t.Fatal(m) } @@ -110,7 +110,7 @@ func TestRegistryGetOrRegister(t *testing.T) { if name != "foo" { t.Fatal(name) } - if _, ok := iface.(Counter); !ok { + if _, ok := iface.(*Counter); !ok { t.Fatal(iface) } }) @@ -125,7 +125,7 @@ func TestRegistryGetOrRegisterWithLazyInstantiation(t *testing.T) { // First metric wins with GetOrRegister _ = r.GetOrRegister("foo", NewCounter) m := r.GetOrRegister("foo", NewGauge) - if _, ok := m.(Counter); !ok { + if _, ok := m.(*Counter); !ok { t.Fatal(m) } @@ -135,7 +135,7 @@ func TestRegistryGetOrRegisterWithLazyInstantiation(t *testing.T) { if name != "foo" { t.Fatal(name) } - if _, ok := iface.(Counter); !ok { + if _, ok := iface.(*Counter); !ok { t.Fatal(iface) } }) diff --git a/metrics/resetting_sample.go b/metrics/resetting_sample.go index c38ffcd3ec..730ef93416 100644 --- a/metrics/resetting_sample.go +++ b/metrics/resetting_sample.go @@ -17,7 +17,7 @@ type resettingSample struct { } // Snapshot returns a read-only copy of the sample with the original reset. -func (rs *resettingSample) Snapshot() SampleSnapshot { +func (rs *resettingSample) Snapshot() *sampleSnapshot { s := rs.Sample.Snapshot() rs.Sample.Clear() return s diff --git a/metrics/resetting_timer.go b/metrics/resetting_timer.go index 6802e3fcea..1b3e87bc3d 100644 --- a/metrics/resetting_timer.go +++ b/metrics/resetting_timer.go @@ -5,36 +5,17 @@ import ( "time" ) -// Initial slice capacity for the values stored in a ResettingTimer -const InitialResettingTimerSliceCap = 10 - -type ResettingTimerSnapshot interface { - Count() int - Mean() float64 - Max() int64 - Min() int64 - Percentiles([]float64) []float64 -} - -// ResettingTimer is used for storing aggregated values for timers, which are reset on every flush interval. -type ResettingTimer interface { - Snapshot() ResettingTimerSnapshot - Time(func()) - Update(time.Duration) - UpdateSince(time.Time) -} - // GetOrRegisterResettingTimer returns an existing ResettingTimer or constructs and registers a -// new StandardResettingTimer. -func GetOrRegisterResettingTimer(name string, r Registry) ResettingTimer { +// new ResettingTimer. +func GetOrRegisterResettingTimer(name string, r Registry) *ResettingTimer { if nil == r { r = DefaultRegistry } - return r.GetOrRegister(name, NewResettingTimer).(ResettingTimer) + return r.GetOrRegister(name, NewResettingTimer).(*ResettingTimer) } -// NewRegisteredResettingTimer constructs and registers a new StandardResettingTimer. -func NewRegisteredResettingTimer(name string, r Registry) ResettingTimer { +// NewRegisteredResettingTimer constructs and registers a new ResettingTimer. +func NewRegisteredResettingTimer(name string, r Registry) *ResettingTimer { c := NewResettingTimer() if nil == r { r = DefaultRegistry @@ -43,33 +24,15 @@ func NewRegisteredResettingTimer(name string, r Registry) ResettingTimer { return c } -// NewResettingTimer constructs a new StandardResettingTimer -func NewResettingTimer() ResettingTimer { - if !Enabled { - return NilResettingTimer{} - } - return &StandardResettingTimer{ - values: make([]int64, 0, InitialResettingTimerSliceCap), +// NewResettingTimer constructs a new ResettingTimer +func NewResettingTimer() *ResettingTimer { + return &ResettingTimer{ + values: make([]int64, 0, 10), } } -// NilResettingTimer is a no-op ResettingTimer. -type NilResettingTimer struct{} - -func (NilResettingTimer) Values() []int64 { return nil } -func (n NilResettingTimer) Snapshot() ResettingTimerSnapshot { return n } -func (NilResettingTimer) Time(f func()) { f() } -func (NilResettingTimer) Update(time.Duration) {} -func (NilResettingTimer) Percentiles([]float64) []float64 { return nil } -func (NilResettingTimer) Mean() float64 { return 0.0 } -func (NilResettingTimer) Max() int64 { return 0 } -func (NilResettingTimer) Min() int64 { return 0 } -func (NilResettingTimer) UpdateSince(time.Time) {} -func (NilResettingTimer) Count() int { return 0 } - -// StandardResettingTimer is the standard implementation of a ResettingTimer. -// and Meter. -type StandardResettingTimer struct { +// ResettingTimer is used for storing aggregated values for timers, which are reset on every flush interval. +type ResettingTimer struct { values []int64 sum int64 // sum is a running count of the total sum, used later to calculate mean @@ -77,28 +40,31 @@ type StandardResettingTimer struct { } // Snapshot resets the timer and returns a read-only copy of its contents. -func (t *StandardResettingTimer) Snapshot() ResettingTimerSnapshot { +func (t *ResettingTimer) Snapshot() *ResettingTimerSnapshot { t.mutex.Lock() defer t.mutex.Unlock() - snapshot := &resettingTimerSnapshot{} + snapshot := &ResettingTimerSnapshot{} if len(t.values) > 0 { snapshot.mean = float64(t.sum) / float64(len(t.values)) snapshot.values = t.values - t.values = make([]int64, 0, InitialResettingTimerSliceCap) + t.values = make([]int64, 0, 10) } t.sum = 0 return snapshot } // Record the duration of the execution of the given function. -func (t *StandardResettingTimer) Time(f func()) { +func (t *ResettingTimer) Time(f func()) { ts := time.Now() f() t.Update(time.Since(ts)) } // Record the duration of an event. -func (t *StandardResettingTimer) Update(d time.Duration) { +func (t *ResettingTimer) Update(d time.Duration) { + if !metricsEnabled { + return + } t.mutex.Lock() defer t.mutex.Unlock() t.values = append(t.values, int64(d)) @@ -106,12 +72,12 @@ func (t *StandardResettingTimer) Update(d time.Duration) { } // Record the duration of an event that started at a time and ends now. -func (t *StandardResettingTimer) UpdateSince(ts time.Time) { +func (t *ResettingTimer) UpdateSince(ts time.Time) { t.Update(time.Since(ts)) } -// resettingTimerSnapshot is a point-in-time copy of another ResettingTimer. -type resettingTimerSnapshot struct { +// ResettingTimerSnapshot is a point-in-time copy of another ResettingTimer. +type ResettingTimerSnapshot struct { values []int64 mean float64 max int64 @@ -121,20 +87,20 @@ type resettingTimerSnapshot struct { } // Count return the length of the values from snapshot. -func (t *resettingTimerSnapshot) Count() int { +func (t *ResettingTimerSnapshot) Count() int { return len(t.values) } // Percentiles returns the boundaries for the input percentiles. // note: this method is not thread safe -func (t *resettingTimerSnapshot) Percentiles(percentiles []float64) []float64 { +func (t *ResettingTimerSnapshot) Percentiles(percentiles []float64) []float64 { t.calc(percentiles) return t.thresholdBoundaries } // Mean returns the mean of the snapshotted values // note: this method is not thread safe -func (t *resettingTimerSnapshot) Mean() float64 { +func (t *ResettingTimerSnapshot) Mean() float64 { if !t.calculated { t.calc(nil) } @@ -144,7 +110,7 @@ func (t *resettingTimerSnapshot) Mean() float64 { // Max returns the max of the snapshotted values // note: this method is not thread safe -func (t *resettingTimerSnapshot) Max() int64 { +func (t *ResettingTimerSnapshot) Max() int64 { if !t.calculated { t.calc(nil) } @@ -153,14 +119,14 @@ func (t *resettingTimerSnapshot) Max() int64 { // Min returns the min of the snapshotted values // note: this method is not thread safe -func (t *resettingTimerSnapshot) Min() int64 { +func (t *ResettingTimerSnapshot) Min() int64 { if !t.calculated { t.calc(nil) } return t.min } -func (t *resettingTimerSnapshot) calc(percentiles []float64) { +func (t *ResettingTimerSnapshot) calc(percentiles []float64) { scores := CalculatePercentiles(t.values, percentiles) t.thresholdBoundaries = scores if len(t.values) == 0 { diff --git a/metrics/sample.go b/metrics/sample.go index 17b2bee28f..dc8167809f 100644 --- a/metrics/sample.go +++ b/metrics/sample.go @@ -10,27 +10,123 @@ import ( const rescaleThreshold = time.Hour -type SampleSnapshot interface { - Count() int64 - Max() int64 - Mean() float64 - Min() int64 - Percentile(float64) float64 - Percentiles([]float64) []float64 - Size() int - StdDev() float64 - Sum() int64 - Variance() float64 -} - -// Samples maintain a statistically-significant selection of values from +// Sample maintains a statistically-significant selection of values from // a stream. type Sample interface { - Snapshot() SampleSnapshot + Snapshot() *sampleSnapshot Clear() Update(int64) } +var ( + _ Sample = (*ExpDecaySample)(nil) + _ Sample = (*UniformSample)(nil) + _ Sample = (*resettingSample)(nil) +) + +// sampleSnapshot is a read-only copy of a Sample. +type sampleSnapshot struct { + count int64 + values []int64 + + max int64 + min int64 + mean float64 + sum int64 + variance float64 +} + +// newSampleSnapshotPrecalculated creates a read-only sampleSnapShot, using +// precalculated sums to avoid iterating the values +func newSampleSnapshotPrecalculated(count int64, values []int64, min, max, sum int64) *sampleSnapshot { + if len(values) == 0 { + return &sampleSnapshot{ + count: count, + values: values, + } + } + return &sampleSnapshot{ + count: count, + values: values, + max: max, + min: min, + mean: float64(sum) / float64(len(values)), + sum: sum, + } +} + +// newSampleSnapshot creates a read-only sampleSnapShot, and calculates some +// numbers. +func newSampleSnapshot(count int64, values []int64) *sampleSnapshot { + var ( + max int64 = math.MinInt64 + min int64 = math.MaxInt64 + sum int64 + ) + for _, v := range values { + sum += v + if v > max { + max = v + } + if v < min { + min = v + } + } + return newSampleSnapshotPrecalculated(count, values, min, max, sum) +} + +// Count returns the count of inputs at the time the snapshot was taken. +func (s *sampleSnapshot) Count() int64 { return s.count } + +// Max returns the maximal value at the time the snapshot was taken. +func (s *sampleSnapshot) Max() int64 { return s.max } + +// Mean returns the mean value at the time the snapshot was taken. +func (s *sampleSnapshot) Mean() float64 { return s.mean } + +// Min returns the minimal value at the time the snapshot was taken. +func (s *sampleSnapshot) Min() int64 { return s.min } + +// Percentile returns an arbitrary percentile of values at the time the +// snapshot was taken. +func (s *sampleSnapshot) Percentile(p float64) float64 { + return SamplePercentile(s.values, p) +} + +// Percentiles returns a slice of arbitrary percentiles of values at the time +// the snapshot was taken. +func (s *sampleSnapshot) Percentiles(ps []float64) []float64 { + return CalculatePercentiles(s.values, ps) +} + +// Size returns the size of the sample at the time the snapshot was taken. +func (s *sampleSnapshot) Size() int { return len(s.values) } + +// StdDev returns the standard deviation of values at the time the snapshot was +// taken. +func (s *sampleSnapshot) StdDev() float64 { + if s.variance == 0.0 { + s.variance = SampleVariance(s.mean, s.values) + } + return math.Sqrt(s.variance) +} + +// Sum returns the sum of values at the time the snapshot was taken. +func (s *sampleSnapshot) Sum() int64 { return s.sum } + +// Values returns a copy of the values in the sample. +func (s *sampleSnapshot) Values() []int64 { + return slices.Clone(s.values) +} + +// Variance returns the variance of values at the time the snapshot was taken. +func (s *sampleSnapshot) Variance() float64 { + if s.variance == 0.0 { + s.variance = SampleVariance(s.mean, s.values) + } + return s.variance +} + // ExpDecaySample is an exponentially-decaying sample using a forward-decaying // priority reservoir. See Cormode et al's "Forward Decay: A Practical Time // Decay Model for Streaming Systems". @@ -49,9 +145,6 @@ type ExpDecaySample struct { // NewExpDecaySample constructs a new exponentially-decaying sample with the // given reservoir size and alpha. func NewExpDecaySample(reservoirSize int, alpha float64) Sample { - if !Enabled { - return NilSample{} - } s := &ExpDecaySample{ alpha: alpha, reservoirSize: reservoirSize, @@ -79,7 +172,7 @@ func (s *ExpDecaySample) Clear() { } // Snapshot returns a read-only copy of the sample. -func (s *ExpDecaySample) Snapshot() SampleSnapshot { +func (s *ExpDecaySample) Snapshot() *sampleSnapshot { s.mutex.Lock() defer s.mutex.Unlock() var ( @@ -105,6 +198,9 @@ func (s *ExpDecaySample) Snapshot() SampleSnapshot { // Update samples a new value. func (s *ExpDecaySample) Update(v int64) { + if !metricsEnabled { + return + } s.update(time.Now(), v) } @@ -140,13 +236,6 @@ func (s *ExpDecaySample) update(t time.Time, v int64) { } } -// NilSample is a no-op Sample. -type NilSample struct{} - -func (NilSample) Clear() {} -func (NilSample) Snapshot() SampleSnapshot { return (*emptySnapshot)(nil) } -func (NilSample) Update(v int64) {} - // SamplePercentile returns an arbitrary percentile of the slice of int64. func SamplePercentile(values []int64, p float64) float64 { return CalculatePercentiles(values, []float64{p})[0] @@ -181,114 +270,6 @@ func CalculatePercentiles(values []int64, ps []float64) []float64 { return scores } -// sampleSnapshot is a read-only copy of another Sample. -type sampleSnapshot struct { - count int64 - values []int64 - - max int64 - min int64 - mean float64 - sum int64 - variance float64 -} - -// newSampleSnapshotPrecalculated creates a read-only sampleSnapShot, using -// precalculated sums to avoid iterating the values -func newSampleSnapshotPrecalculated(count int64, values []int64, min, max, sum int64) *sampleSnapshot { - if len(values) == 0 { - return &sampleSnapshot{ - count: count, - values: values, - } - } - return &sampleSnapshot{ - count: count, - values: values, - max: max, - min: min, - mean: float64(sum) / float64(len(values)), - sum: sum, - } -} - -// newSampleSnapshot creates a read-only sampleSnapShot, and calculates some -// numbers. -func newSampleSnapshot(count int64, values []int64) *sampleSnapshot { - var ( - max int64 = math.MinInt64 - min int64 = math.MaxInt64 - sum int64 - ) - for _, v := range values { - sum += v - if v > max { - max = v - } - if v < min { - min = v - } - } - return newSampleSnapshotPrecalculated(count, values, min, max, sum) -} - -// Count returns the count of inputs at the time the snapshot was taken. -func (s *sampleSnapshot) Count() int64 { return s.count } - -// Max returns the maximal value at the time the snapshot was taken. -func (s *sampleSnapshot) Max() int64 { return s.max } - -// Mean returns the mean value at the time the snapshot was taken. -func (s *sampleSnapshot) Mean() float64 { return s.mean } - -// Min returns the minimal value at the time the snapshot was taken. -func (s *sampleSnapshot) Min() int64 { return s.min } - -// Percentile returns an arbitrary percentile of values at the time the -// snapshot was taken. -func (s *sampleSnapshot) Percentile(p float64) float64 { - return SamplePercentile(s.values, p) -} - -// Percentiles returns a slice of arbitrary percentiles of values at the time -// the snapshot was taken. -func (s *sampleSnapshot) Percentiles(ps []float64) []float64 { - return CalculatePercentiles(s.values, ps) -} - -// Size returns the size of the sample at the time the snapshot was taken. -func (s *sampleSnapshot) Size() int { return len(s.values) } - -// Snapshot returns the snapshot. -func (s *sampleSnapshot) Snapshot() SampleSnapshot { return s } - -// StdDev returns the standard deviation of values at the time the snapshot was -// taken. -func (s *sampleSnapshot) StdDev() float64 { - if s.variance == 0.0 { - s.variance = SampleVariance(s.mean, s.values) - } - return math.Sqrt(s.variance) -} - -// Sum returns the sum of values at the time the snapshot was taken. -func (s *sampleSnapshot) Sum() int64 { return s.sum } - -// Values returns a copy of the values in the sample. -func (s *sampleSnapshot) Values() []int64 { - values := make([]int64, len(s.values)) - copy(values, s.values) - return values -} - -// Variance returns the variance of values at the time the snapshot was taken. -func (s *sampleSnapshot) Variance() float64 { - if s.variance == 0.0 { - s.variance = SampleVariance(s.mean, s.values) - } - return s.variance -} - // SampleVariance returns the variance of the slice of int64. func SampleVariance(mean float64, values []int64) float64 { if len(values) == 0 { @@ -302,7 +283,7 @@ func SampleVariance(mean float64, values []int64) float64 { return sum / float64(len(values)) } -// A uniform sample using Vitter's Algorithm R. +// UniformSample implements a uniform sample using Vitter's Algorithm R. // // type UniformSample struct { @@ -316,9 +297,6 @@ type UniformSample struct { // NewUniformSample constructs a new uniform sample with the given reservoir // size. func NewUniformSample(reservoirSize int) Sample { - if !Enabled { - return NilSample{} - } return &UniformSample{ reservoirSize: reservoirSize, values: make([]int64, 0, reservoirSize), @@ -336,14 +314,13 @@ func (s *UniformSample) Clear() { s.mutex.Lock() defer s.mutex.Unlock() s.count = 0 - s.values = make([]int64, 0, s.reservoirSize) + clear(s.values) } // Snapshot returns a read-only copy of the sample. -func (s *UniformSample) Snapshot() SampleSnapshot { +func (s *UniformSample) Snapshot() *sampleSnapshot { s.mutex.Lock() - values := make([]int64, len(s.values)) - copy(values, s.values) + values := slices.Clone(s.values) count := s.count s.mutex.Unlock() return newSampleSnapshot(count, values) @@ -351,21 +328,24 @@ func (s *UniformSample) Snapshot() SampleSnapshot { // Update samples a new value. func (s *UniformSample) Update(v int64) { + if !metricsEnabled { + return + } s.mutex.Lock() defer s.mutex.Unlock() s.count++ if len(s.values) < s.reservoirSize { s.values = append(s.values, v) + return + } + var r int64 + if s.rand != nil { + r = s.rand.Int63n(s.count) } else { - var r int64 - if s.rand != nil { - r = s.rand.Int63n(s.count) - } else { - r = rand.Int63n(s.count) - } - if r < int64(len(s.values)) { - s.values[int(r)] = v - } + r = rand.Int63n(s.count) + } + if r < int64(len(s.values)) { + s.values[int(r)] = v } } diff --git a/metrics/sample_test.go b/metrics/sample_test.go index c855671ae2..6619eb1e9e 100644 --- a/metrics/sample_test.go +++ b/metrics/sample_test.go @@ -86,7 +86,7 @@ func TestExpDecaySample(t *testing.T) { if have, want := snap.Size(), min(tc.updates, tc.reservoirSize); have != want { t.Errorf("unexpected size: have %d want %d", have, want) } - values := snap.(*sampleSnapshot).values + values := snap.values if have, want := len(values), min(tc.updates, tc.reservoirSize); have != want { t.Errorf("unexpected values length: have %d want %d", have, want) } @@ -111,8 +111,7 @@ func TestExpDecaySampleNanosecondRegression(t *testing.T) { for i := 0; i < 1000; i++ { sw.Update(20) } - s := sw.Snapshot() - v := s.(*sampleSnapshot).values + v := sw.Snapshot().values avg := float64(0) for i := 0; i < len(v); i++ { avg += float64(v[i]) @@ -166,7 +165,7 @@ func TestUniformSample(t *testing.T) { if size := s.Size(); size != 100 { t.Errorf("s.Size(): 100 != %v\n", size) } - values := s.(*sampleSnapshot).values + values := s.values if l := len(values); l != 100 { t.Errorf("len(s.Values()): 100 != %v\n", l) @@ -184,8 +183,7 @@ func TestUniformSampleIncludesTail(t *testing.T) { for i := 0; i < max; i++ { sw.Update(int64(i)) } - s := sw.Snapshot() - v := s.(*sampleSnapshot).values + v := sw.Snapshot().values sum := 0 exp := (max - 1) * max / 2 for i := 0; i < len(v); i++ { @@ -220,7 +218,7 @@ func benchmarkSample(b *testing.B, s Sample) { } } -func testExpDecaySampleStatistics(t *testing.T, s SampleSnapshot) { +func testExpDecaySampleStatistics(t *testing.T, s *sampleSnapshot) { if sum := s.Sum(); sum != 496598 { t.Errorf("s.Sum(): 496598 != %v\n", sum) } @@ -251,7 +249,7 @@ func testExpDecaySampleStatistics(t *testing.T, s SampleSnapshot) { } } -func testUniformSampleStatistics(t *testing.T, s SampleSnapshot) { +func testUniformSampleStatistics(t *testing.T, s *sampleSnapshot) { if count := s.Count(); count != 10000 { t.Errorf("s.Count(): 10000 != %v\n", count) } diff --git a/metrics/syslog.go b/metrics/syslog.go index fd856d6973..0bc4ed0da5 100644 --- a/metrics/syslog.go +++ b/metrics/syslog.go @@ -15,17 +15,17 @@ func Syslog(r Registry, d time.Duration, w *syslog.Writer) { for range time.Tick(d) { r.Each(func(name string, i interface{}) { switch metric := i.(type) { - case Counter: + case *Counter: w.Info(fmt.Sprintf("counter %s: count: %d", name, metric.Snapshot().Count())) - case CounterFloat64: + case *CounterFloat64: w.Info(fmt.Sprintf("counter %s: count: %f", name, metric.Snapshot().Count())) - case Gauge: + case *Gauge: w.Info(fmt.Sprintf("gauge %s: value: %d", name, metric.Snapshot().Value())) - case GaugeFloat64: + case *GaugeFloat64: w.Info(fmt.Sprintf("gauge %s: value: %f", name, metric.Snapshot().Value())) - case GaugeInfo: + case *GaugeInfo: w.Info(fmt.Sprintf("gauge %s: value: %s", name, metric.Snapshot().Value())) - case Healthcheck: + case *Healthcheck: metric.Check() w.Info(fmt.Sprintf("healthcheck %s: error: %v", name, metric.Error())) case Histogram: @@ -45,7 +45,7 @@ func Syslog(r Registry, d time.Duration, w *syslog.Writer) { ps[3], ps[4], )) - case Meter: + case *Meter: m := metric.Snapshot() w.Info(fmt.Sprintf( "meter %s: count: %d 1-min: %.2f 5-min: %.2f 15-min: %.2f mean: %.2f", @@ -56,7 +56,7 @@ func Syslog(r Registry, d time.Duration, w *syslog.Writer) { m.Rate15(), m.RateMean(), )) - case Timer: + case *Timer: t := metric.Snapshot() ps := t.Percentiles([]float64{0.5, 0.75, 0.95, 0.99, 0.999}) w.Info(fmt.Sprintf( diff --git a/metrics/timer.go b/metrics/timer.go index fc2a88f508..9df15c967a 100644 --- a/metrics/timer.go +++ b/metrics/timer.go @@ -5,47 +5,30 @@ import ( "time" ) -type TimerSnapshot interface { - HistogramSnapshot - MeterSnapshot -} - -// Timer capture the duration and rate of events. -type Timer interface { - Snapshot() TimerSnapshot - Stop() - Time(func()) - UpdateSince(time.Time) - Update(time.Duration) -} - // GetOrRegisterTimer returns an existing Timer or constructs and registers a -// new StandardTimer. +// new Timer. // Be sure to unregister the meter from the registry once it is of no use to // allow for garbage collection. -func GetOrRegisterTimer(name string, r Registry) Timer { +func GetOrRegisterTimer(name string, r Registry) *Timer { if nil == r { r = DefaultRegistry } - return r.GetOrRegister(name, NewTimer).(Timer) + return r.GetOrRegister(name, NewTimer).(*Timer) } -// NewCustomTimer constructs a new StandardTimer from a Histogram and a Meter. +// NewCustomTimer constructs a new Timer from a Histogram and a Meter. // Be sure to call Stop() once the timer is of no use to allow for garbage collection. -func NewCustomTimer(h Histogram, m Meter) Timer { - if !Enabled { - return NilTimer{} - } - return &StandardTimer{ +func NewCustomTimer(h Histogram, m *Meter) *Timer { + return &Timer{ histogram: h, meter: m, } } -// NewRegisteredTimer constructs and registers a new StandardTimer. +// NewRegisteredTimer constructs and registers a new Timer. // Be sure to unregister the meter from the registry once it is of no use to // allow for garbage collection. -func NewRegisteredTimer(name string, r Registry) Timer { +func NewRegisteredTimer(name string, r Registry) *Timer { c := NewTimer() if nil == r { r = DefaultRegistry @@ -54,60 +37,47 @@ func NewRegisteredTimer(name string, r Registry) Timer { return c } -// NewTimer constructs a new StandardTimer using an exponentially-decaying +// NewTimer constructs a new Timer using an exponentially-decaying // sample with the same reservoir size and alpha as UNIX load averages. // Be sure to call Stop() once the timer is of no use to allow for garbage collection. -func NewTimer() Timer { - if !Enabled { - return NilTimer{} - } - return &StandardTimer{ +func NewTimer() *Timer { + return &Timer{ histogram: NewHistogram(NewExpDecaySample(1028, 0.015)), meter: NewMeter(), } } -// NilTimer is a no-op Timer. -type NilTimer struct{} - -func (NilTimer) Snapshot() TimerSnapshot { return (*emptySnapshot)(nil) } -func (NilTimer) Stop() {} -func (NilTimer) Time(f func()) { f() } -func (NilTimer) Update(time.Duration) {} -func (NilTimer) UpdateSince(time.Time) {} - -// StandardTimer is the standard implementation of a Timer and uses a Histogram -// and Meter. -type StandardTimer struct { +// Timer captures the duration and rate of events, using a Histogram and a Meter. +type Timer struct { histogram Histogram - meter Meter + meter *Meter mutex sync.Mutex } // Snapshot returns a read-only copy of the timer. -func (t *StandardTimer) Snapshot() TimerSnapshot { +func (t *Timer) Snapshot() *TimerSnapshot { t.mutex.Lock() defer t.mutex.Unlock() - return &timerSnapshot{ + return &TimerSnapshot{ histogram: t.histogram.Snapshot(), meter: t.meter.Snapshot(), } } // Stop stops the meter. -func (t *StandardTimer) Stop() { +func (t *Timer) Stop() { t.meter.Stop() } // Time record the duration of the execution of the given function. -func (t *StandardTimer) Time(f func()) { +func (t *Timer) Time(f func()) { ts := time.Now() f() t.Update(time.Since(ts)) } // Update the duration of an event, in nanoseconds. -func (t *StandardTimer) Update(d time.Duration) { +func (t *Timer) Update(d time.Duration) { t.mutex.Lock() defer t.mutex.Unlock() t.histogram.Update(d.Nanoseconds()) @@ -116,67 +86,67 @@ func (t *StandardTimer) Update(d time.Duration) { // UpdateSince update the duration of an event that started at a time and ends now. // The record uses nanoseconds. -func (t *StandardTimer) UpdateSince(ts time.Time) { +func (t *Timer) UpdateSince(ts time.Time) { t.Update(time.Since(ts)) } -// timerSnapshot is a read-only copy of another Timer. -type timerSnapshot struct { +// TimerSnapshot is a read-only copy of another Timer. +type TimerSnapshot struct { histogram HistogramSnapshot - meter MeterSnapshot + meter *MeterSnapshot } // Count returns the number of events recorded at the time the snapshot was // taken. -func (t *timerSnapshot) Count() int64 { return t.histogram.Count() } +func (t *TimerSnapshot) Count() int64 { return t.histogram.Count() } // Max returns the maximum value at the time the snapshot was taken. -func (t *timerSnapshot) Max() int64 { return t.histogram.Max() } +func (t *TimerSnapshot) Max() int64 { return t.histogram.Max() } // Size returns the size of the sample at the time the snapshot was taken. -func (t *timerSnapshot) Size() int { return t.histogram.Size() } +func (t *TimerSnapshot) Size() int { return t.histogram.Size() } // Mean returns the mean value at the time the snapshot was taken. -func (t *timerSnapshot) Mean() float64 { return t.histogram.Mean() } +func (t *TimerSnapshot) Mean() float64 { return t.histogram.Mean() } // Min returns the minimum value at the time the snapshot was taken. -func (t *timerSnapshot) Min() int64 { return t.histogram.Min() } +func (t *TimerSnapshot) Min() int64 { return t.histogram.Min() } // Percentile returns an arbitrary percentile of sampled values at the time the // snapshot was taken. -func (t *timerSnapshot) Percentile(p float64) float64 { +func (t *TimerSnapshot) Percentile(p float64) float64 { return t.histogram.Percentile(p) } // Percentiles returns a slice of arbitrary percentiles of sampled values at // the time the snapshot was taken. -func (t *timerSnapshot) Percentiles(ps []float64) []float64 { +func (t *TimerSnapshot) Percentiles(ps []float64) []float64 { return t.histogram.Percentiles(ps) } // Rate1 returns the one-minute moving average rate of events per second at the // time the snapshot was taken. -func (t *timerSnapshot) Rate1() float64 { return t.meter.Rate1() } +func (t *TimerSnapshot) Rate1() float64 { return t.meter.Rate1() } // Rate5 returns the five-minute moving average rate of events per second at // the time the snapshot was taken. -func (t *timerSnapshot) Rate5() float64 { return t.meter.Rate5() } +func (t *TimerSnapshot) Rate5() float64 { return t.meter.Rate5() } // Rate15 returns the fifteen-minute moving average rate of events per second // at the time the snapshot was taken. -func (t *timerSnapshot) Rate15() float64 { return t.meter.Rate15() } +func (t *TimerSnapshot) Rate15() float64 { return t.meter.Rate15() } // RateMean returns the meter's mean rate of events per second at the time the // snapshot was taken. -func (t *timerSnapshot) RateMean() float64 { return t.meter.RateMean() } +func (t *TimerSnapshot) RateMean() float64 { return t.meter.RateMean() } // StdDev returns the standard deviation of the values at the time the snapshot // was taken. -func (t *timerSnapshot) StdDev() float64 { return t.histogram.StdDev() } +func (t *TimerSnapshot) StdDev() float64 { return t.histogram.StdDev() } // Sum returns the sum at the time the snapshot was taken. -func (t *timerSnapshot) Sum() int64 { return t.histogram.Sum() } +func (t *TimerSnapshot) Sum() int64 { return t.histogram.Sum() } // Variance returns the variance of the values at the time the snapshot was // taken. -func (t *timerSnapshot) Variance() float64 { return t.histogram.Variance() } +func (t *TimerSnapshot) Variance() float64 { return t.histogram.Variance() } diff --git a/metrics/writer.go b/metrics/writer.go index c211c5046b..2a41f8e1fe 100644 --- a/metrics/writer.go +++ b/metrics/writer.go @@ -26,22 +26,22 @@ func WriteOnce(r Registry, w io.Writer) { slices.SortFunc(namedMetrics, namedMetric.cmp) for _, namedMetric := range namedMetrics { switch metric := namedMetric.m.(type) { - case Counter: + case *Counter: fmt.Fprintf(w, "counter %s\n", namedMetric.name) fmt.Fprintf(w, " count: %9d\n", metric.Snapshot().Count()) - case CounterFloat64: + case *CounterFloat64: fmt.Fprintf(w, "counter %s\n", namedMetric.name) fmt.Fprintf(w, " count: %f\n", metric.Snapshot().Count()) - case Gauge: + case *Gauge: fmt.Fprintf(w, "gauge %s\n", namedMetric.name) fmt.Fprintf(w, " value: %9d\n", metric.Snapshot().Value()) - case GaugeFloat64: + case *GaugeFloat64: fmt.Fprintf(w, "gauge %s\n", namedMetric.name) fmt.Fprintf(w, " value: %f\n", metric.Snapshot().Value()) - case GaugeInfo: + case *GaugeInfo: fmt.Fprintf(w, "gauge %s\n", namedMetric.name) fmt.Fprintf(w, " value: %s\n", metric.Snapshot().Value().String()) - case Healthcheck: + case *Healthcheck: metric.Check() fmt.Fprintf(w, "healthcheck %s\n", namedMetric.name) fmt.Fprintf(w, " error: %v\n", metric.Error()) @@ -59,7 +59,7 @@ func WriteOnce(r Registry, w io.Writer) { fmt.Fprintf(w, " 95%%: %12.2f\n", ps[2]) fmt.Fprintf(w, " 99%%: %12.2f\n", ps[3]) fmt.Fprintf(w, " 99.9%%: %12.2f\n", ps[4]) - case Meter: + case *Meter: m := metric.Snapshot() fmt.Fprintf(w, "meter %s\n", namedMetric.name) fmt.Fprintf(w, " count: %9d\n", m.Count()) @@ -67,7 +67,7 @@ func WriteOnce(r Registry, w io.Writer) { fmt.Fprintf(w, " 5-min rate: %12.2f\n", m.Rate5()) fmt.Fprintf(w, " 15-min rate: %12.2f\n", m.Rate15()) fmt.Fprintf(w, " mean rate: %12.2f\n", m.RateMean()) - case Timer: + case *Timer: t := metric.Snapshot() ps := t.Percentiles([]float64{0.5, 0.75, 0.95, 0.99, 0.999}) fmt.Fprintf(w, "timer %s\n", namedMetric.name) diff --git a/miner/bid_simulator.go b/miner/bid_simulator.go index dc298ed3e1..afeaeeebda 100644 --- a/miner/bid_simulator.go +++ b/miner/bid_simulator.go @@ -24,6 +24,7 @@ import ( "github.com/ethereum/go-ethereum/log" "github.com/ethereum/go-ethereum/metrics" "github.com/ethereum/go-ethereum/miner/builderclient" + "github.com/ethereum/go-ethereum/miner/minerconfig" "github.com/ethereum/go-ethereum/params" "github.com/ethereum/go-ethereum/rpc" ) @@ -79,7 +80,7 @@ type newBidPackage struct { // bidSimulator is in charge of receiving bid from builders, reporting issue to builders. // And take care of bid simulation, rewards computing, best bid maintaining. type bidSimulator struct { - config *MevConfig + config *minerconfig.MevConfig delayLeftOver time.Duration minGasPrice *big.Int chain *core.BlockChain @@ -117,7 +118,7 @@ type bidSimulator struct { } func newBidSimulator( - config *MevConfig, + config *minerconfig.MevConfig, delayLeftOver time.Duration, minGasPrice *big.Int, eth Backend, @@ -845,8 +846,8 @@ func (r *BidRuntime) commitTransaction(chain *core.BlockChain, chainConfig *para } } - receipt, err := core.ApplyTransaction(chainConfig, chain, &env.coinbase, env.gasPool, env.state, env.header, tx, - &env.header.GasUsed, *chain.GetVMConfig(), core.NewReceiptBloomGenerator()) + receipt, err := core.ApplyTransaction(env.evm, env.gasPool, env.state, env.header, tx, + &env.header.GasUsed, core.NewReceiptBloomGenerator()) if err != nil { return err } else if unRevertible && receipt.Status == types.ReceiptStatusFailed { diff --git a/miner/miner.go b/miner/miner.go index 4b667b3879..c54235f5ff 100644 --- a/miner/miner.go +++ b/miner/miner.go @@ -24,7 +24,6 @@ import ( "time" "github.com/ethereum/go-ethereum/common" - "github.com/ethereum/go-ethereum/common/hexutil" "github.com/ethereum/go-ethereum/consensus" "github.com/ethereum/go-ethereum/core" "github.com/ethereum/go-ethereum/core/state" @@ -33,6 +32,7 @@ import ( "github.com/ethereum/go-ethereum/eth/downloader" "github.com/ethereum/go-ethereum/event" "github.com/ethereum/go-ethereum/log" + "github.com/ethereum/go-ethereum/miner/minerconfig" "github.com/ethereum/go-ethereum/params" ) @@ -43,37 +43,6 @@ type Backend interface { TxPool() *txpool.TxPool } -// Config is the configuration parameters of mining. -type Config struct { - Etherbase common.Address `toml:",omitempty"` // Public address for block mining rewards - ExtraData hexutil.Bytes `toml:",omitempty"` // Block extra data set by the miner - DelayLeftOver time.Duration // Time reserved to finalize a block(calculate root, distribute income...) - GasFloor uint64 // Target gas floor for mined blocks. - GasCeil uint64 // Target gas ceiling for mined blocks. - GasPrice *big.Int // Minimum gas price for mining a transaction - Recommit time.Duration // The time interval for miner to re-create mining work. - VoteEnable bool // Whether to vote when mining - - DisableVoteAttestation bool // Whether to skip assembling vote attestation - - Mev MevConfig // Mev configuration -} - -// DefaultConfig contains default settings for miner. -var DefaultConfig = Config{ - GasCeil: 0, - GasPrice: big.NewInt(params.GWei), - - // The default recommit time is chosen as two seconds since - // consensus-layer usually will wait a half slot of time(6s) - // for payload generation. It should be enough for Geth to - // run 3 rounds. - Recommit: 3 * time.Second, - DelayLeftOver: 50 * time.Millisecond, - - Mev: DefaultMevConfig, -} - // Miner is the main object which takes care of submitting new work to consensus // engine and gathering the sealing result. type Miner struct { @@ -90,7 +59,7 @@ type Miner struct { wg sync.WaitGroup } -func New(eth Backend, config *Config, mux *event.TypeMux, engine consensus.Engine) *Miner { +func New(eth Backend, config *minerconfig.Config, mux *event.TypeMux, engine consensus.Engine) *Miner { miner := &Miner{ mux: mux, eth: eth, diff --git a/miner/miner_mev.go b/miner/miner_mev.go index 38177ea283..dbf0b2d6cb 100644 --- a/miner/miner_mev.go +++ b/miner/miner_mev.go @@ -12,29 +12,6 @@ import ( "github.com/ethereum/go-ethereum/log" ) -type BuilderConfig struct { - Address common.Address - URL string -} - -type MevConfig struct { - Enabled bool // Whether to enable Mev or not - GreedyMergeTx bool // Whether to merge local transactions to the bid - BuilderFeeCeil string // The maximum builder fee of a bid - SentryURL string // The url of Mev sentry - Builders []BuilderConfig // The list of builders - ValidatorCommission uint64 // 100 means the validator claims 1% from block reward - BidSimulationLeftOver time.Duration -} - -var DefaultMevConfig = MevConfig{ - Enabled: false, - SentryURL: "", - Builders: nil, - ValidatorCommission: 100, - BidSimulationLeftOver: 50 * time.Millisecond, -} - // MevRunning return true if mev is running. func (miner *Miner) MevRunning() bool { return miner.bidSimulator.isRunning() && miner.bidSimulator.receivingBid() diff --git a/miner/miner_test.go b/miner/miner_test.go index ce35f9a648..7e5b361678 100644 --- a/miner/miner_test.go +++ b/miner/miner_test.go @@ -35,6 +35,7 @@ import ( "github.com/ethereum/go-ethereum/crypto" "github.com/ethereum/go-ethereum/eth/downloader" "github.com/ethereum/go-ethereum/event" + "github.com/ethereum/go-ethereum/miner/minerconfig" "github.com/ethereum/go-ethereum/params" "github.com/ethereum/go-ethereum/trie" "github.com/ethereum/go-ethereum/triedb" @@ -296,7 +297,7 @@ func minerTestGenesisBlock(period uint64, gasLimit uint64, faucet common.Address } func createMiner(t *testing.T) (*Miner, *event.TypeMux, func(skipMiner bool)) { // Create Ethash config - config := Config{ + config := minerconfig.Config{ Etherbase: common.HexToAddress("123456789"), } // Create chainConfig diff --git a/miner/minerconfig/config.go b/miner/minerconfig/config.go new file mode 100644 index 0000000000..35b0aa97e8 --- /dev/null +++ b/miner/minerconfig/config.go @@ -0,0 +1,81 @@ +// Copyright 2014 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +// Package miner implements Ethereum block creation and mining. +package minerconfig + +import ( + "math/big" + "time" + + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/common/hexutil" + "github.com/ethereum/go-ethereum/params" +) + +// Config is the configuration parameters of mining. +type Config struct { + Etherbase common.Address `toml:",omitempty"` // Public address for block mining rewards + ExtraData hexutil.Bytes `toml:",omitempty"` // Block extra data set by the miner + DelayLeftOver time.Duration // Time reserved to finalize a block(calculate root, distribute income...) + GasFloor uint64 // Target gas floor for mined blocks. + GasCeil uint64 // Target gas ceiling for mined blocks. + GasPrice *big.Int // Minimum gas price for mining a transaction + Recommit time.Duration // The time interval for miner to re-create mining work. + VoteEnable bool // Whether to vote when mining + + DisableVoteAttestation bool // Whether to skip assembling vote attestation + + Mev MevConfig // Mev configuration +} + +// DefaultConfig contains default settings for miner. +var DefaultConfig = Config{ + GasCeil: 0, + GasPrice: big.NewInt(params.GWei), + + // The default recommit time is chosen as two seconds since + // consensus-layer usually will wait a half slot of time(6s) + // for payload generation. It should be enough for Geth to + // run 3 rounds. + Recommit: 3 * time.Second, + DelayLeftOver: 50 * time.Millisecond, + + Mev: DefaultMevConfig, +} + +type BuilderConfig struct { + Address common.Address + URL string +} + +type MevConfig struct { + Enabled bool // Whether to enable Mev or not + GreedyMergeTx bool // Whether to merge local transactions to the bid + BuilderFeeCeil string // The maximum builder fee of a bid + SentryURL string // The url of Mev sentry + Builders []BuilderConfig // The list of builders + ValidatorCommission uint64 // 100 means the validator claims 1% from block reward + BidSimulationLeftOver time.Duration +} + +var DefaultMevConfig = MevConfig{ + Enabled: false, + SentryURL: "", + Builders: nil, + ValidatorCommission: 100, + BidSimulationLeftOver: 50 * time.Millisecond, +} diff --git a/miner/worker.go b/miner/worker.go index 972662c298..068778e49a 100644 --- a/miner/worker.go +++ b/miner/worker.go @@ -43,6 +43,7 @@ import ( "github.com/ethereum/go-ethereum/event" "github.com/ethereum/go-ethereum/log" "github.com/ethereum/go-ethereum/metrics" + "github.com/ethereum/go-ethereum/miner/minerconfig" "github.com/ethereum/go-ethereum/params" "github.com/ethereum/go-ethereum/trie" ) @@ -99,6 +100,7 @@ type environment struct { size uint32 // almost accurate block size, gasPool *core.GasPool // available gas used to pack transactions coinbase common.Address + evm *vm.EVM header *types.Header txs []*types.Transaction @@ -197,7 +199,7 @@ type bidFetcher interface { type worker struct { bidFetcher bidFetcher prefetcher core.Prefetcher - config *Config + config *minerconfig.Config chainConfig *params.ChainConfig engine consensus.Engine eth Backend @@ -253,7 +255,7 @@ type worker struct { recentMinedBlocks *lru.Cache } -func newWorker(config *Config, engine consensus.Engine, eth Backend, mux *event.TypeMux, init bool) *worker { +func newWorker(config *minerconfig.Config, engine consensus.Engine, eth Backend, mux *event.TypeMux, init bool) *worker { recentMinedBlocks, _ := lru.New(recentMinedCacheLimit) chainConfig := eth.BlockChain().Config() worker := &worker{ @@ -693,6 +695,7 @@ func (w *worker) makeEnv(parent *types.Header, header *types.Header, coinbase co coinbase: coinbase, header: header, witness: state.Witness(), + evm: vm.NewEVM(core.NewEVMBlockContext(header, w.chain, &coinbase), state, w.chainConfig, vm.Config{}), } // Keep track of transactions which return errors so they can be removed env.tcount = 0 @@ -765,7 +768,7 @@ func (w *worker) applyTransaction(env *environment, tx *types.Transaction, recei gp = env.gasPool.Gas() ) - receipt, err := core.ApplyTransaction(w.chainConfig, w.chain, &env.coinbase, env.gasPool, env.state, env.header, tx, &env.header.GasUsed, vm.Config{}, receiptProcessors...) + receipt, err := core.ApplyTransaction(env.evm, env.gasPool, env.state, env.header, tx, &env.header.GasUsed, receiptProcessors...) if err != nil { env.state.RevertToSnapshot(snap) env.gasPool.SetGas(gp) @@ -1047,15 +1050,11 @@ func (w *worker) prepareWork(genParams *generateParams, witness bool) (*environm systemcontracts.TryUpdateBuildInSystemContract(w.chainConfig, header.Number, parent.Time, header.Time, env.state, true) if header.ParentBeaconRoot != nil { - context := core.NewEVMBlockContext(header, w.chain, nil) - vmenv := vm.NewEVM(context, vm.TxContext{}, env.state, w.chainConfig, vm.Config{}) - core.ProcessBeaconBlockRoot(*header.ParentBeaconRoot, vmenv, env.state) + core.ProcessBeaconBlockRoot(*header.ParentBeaconRoot, env.evm) } if w.chainConfig.IsPrague(header.Number, header.Time) { - context := core.NewEVMBlockContext(header, w.chain, nil) - vmenv := vm.NewEVM(context, vm.TxContext{}, env.state, w.chainConfig, vm.Config{}) - core.ProcessParentBlockHash(header.ParentHash, vmenv, env.state) + core.ProcessParentBlockHash(header.ParentHash, env.evm) } env.size = uint32(env.header.Size()) @@ -1177,21 +1176,15 @@ func (w *worker) generateWork(params *generateParams, witness bool) *newPayloadR // Collect consensus-layer requests if Prague is enabled. var requests [][]byte if w.chainConfig.IsPrague(work.header.Number, work.header.Time) { + requests = [][]byte{} // EIP-6110 deposits - depositRequests, err := core.ParseDepositLogs(allLogs, w.chainConfig) - if err != nil { + if err := core.ParseDepositLogs(&requests, allLogs, w.chainConfig); err != nil { return &newPayloadResult{err: err} } - requests = append(requests, depositRequests) - // create EVM for system calls - blockContext := core.NewEVMBlockContext(work.header, w.chain, &work.header.Coinbase) - vmenv := vm.NewEVM(blockContext, vm.TxContext{}, work.state, w.chainConfig, vm.Config{}) - // EIP-7002 withdrawals - withdrawalRequests := core.ProcessWithdrawalQueue(vmenv, work.state) - requests = append(requests, withdrawalRequests) + // EIP-7002 + core.ProcessWithdrawalQueue(&requests, work.evm) // EIP-7251 consolidations - consolidationRequests := core.ProcessConsolidationQueue(vmenv, work.state) - requests = append(requests, consolidationRequests) + core.ProcessConsolidationQueue(&requests, work.evm) } if requests != nil { reqHash := types.CalcRequestsHash(requests) diff --git a/miner/worker_test.go b/miner/worker_test.go index 3059387f34..8f056a087e 100644 --- a/miner/worker_test.go +++ b/miner/worker_test.go @@ -35,6 +35,7 @@ import ( "github.com/ethereum/go-ethereum/crypto" "github.com/ethereum/go-ethereum/ethdb" "github.com/ethereum/go-ethereum/event" + "github.com/ethereum/go-ethereum/miner/minerconfig" "github.com/ethereum/go-ethereum/params" "github.com/holiman/uint256" ) @@ -66,7 +67,7 @@ var ( pendingTxs []*types.Transaction newTxs []*types.Transaction - testConfig = &Config{ + testConfig = &minerconfig.Config{ Recommit: time.Second, GasCeil: params.GenesisGasLimit, } diff --git a/oss-fuzz.sh b/oss-fuzz.sh index 5e4aa1c253..1f222c433b 100644 --- a/oss-fuzz.sh +++ b/oss-fuzz.sh @@ -234,10 +234,6 @@ compile_fuzzer github.com/ethereum/go-ethereum/tests/fuzzers/secp256k1 \ compile_fuzzer github.com/ethereum/go-ethereum/eth/protocols/eth \ FuzzEthProtocolHandlers fuzz_eth_protocol_handlers \ - $repo/eth/protocols/eth/handler_test.go + $repo/eth/protocols/eth/handler_test.go,$repo/eth/protocols/eth/peer_test.go -#compile_fuzzer tests/fuzzers/vflux FuzzClientPool fuzzClientPool -#compile_fuzzer tests/fuzzers/difficulty Fuzz fuzzDifficulty -#compile_fuzzer tests/fuzzers/les Fuzz fuzzLes - diff --git a/p2p/discover/metrics.go b/p2p/discover/metrics.go index 9261ae1376..5d4c953c90 100644 --- a/p2p/discover/metrics.go +++ b/p2p/discover/metrics.go @@ -34,7 +34,7 @@ const ( ) var ( - bucketsCounter []metrics.Counter + bucketsCounter []*metrics.Counter ingressTrafficMeter = metrics.NewRegisteredMeter(ingressMeterName, nil) egressTrafficMeter = metrics.NewRegisteredMeter(egressMeterName, nil) ) @@ -53,7 +53,7 @@ type meteredUdpConn struct { func newMeteredConn(conn UDPConn) UDPConn { // Short circuit if metrics are disabled - if !metrics.Enabled { + if !metrics.Enabled() { return conn } return &meteredUdpConn{udpConn: conn} diff --git a/p2p/discover/table.go b/p2p/discover/table.go index 7c95fffd43..224263fe00 100644 --- a/p2p/discover/table.go +++ b/p2p/discover/table.go @@ -625,7 +625,7 @@ func (tab *Table) nodeAdded(b *bucket, n *tableNode) { if tab.nodeAddedHook != nil { tab.nodeAddedHook(b, n) } - if metrics.Enabled { + if metrics.Enabled() { bucketsCounter[b.index].Inc(1) } } @@ -635,7 +635,7 @@ func (tab *Table) nodeRemoved(b *bucket, n *tableNode) { if tab.nodeRemovedHook != nil { tab.nodeRemovedHook(b, n) } - if metrics.Enabled { + if metrics.Enabled() { bucketsCounter[b.index].Dec(1) } } diff --git a/p2p/metrics.go b/p2p/metrics.go index 3b159e842e..d713a2fc8d 100644 --- a/p2p/metrics.go +++ b/p2p/metrics.go @@ -37,19 +37,19 @@ const ( ) var ( - activePeerGauge metrics.Gauge = metrics.NilGauge{} - activeInboundPeerGauge metrics.Gauge = metrics.NilGauge{} - activeOutboundPeerGauge metrics.Gauge = metrics.NilGauge{} + activePeerGauge = metrics.NewRegisteredGauge("p2p/peers", nil) + activeInboundPeerGauge = metrics.NewRegisteredGauge("p2p/peers/inbound", nil) + activeOutboundPeerGauge = metrics.NewRegisteredGauge("p2p/peers/outbound", nil) ingressTrafficMeter = metrics.NewRegisteredMeter("p2p/ingress", nil) egressTrafficMeter = metrics.NewRegisteredMeter("p2p/egress", nil) // general ingress/egress connection meters - serveMeter metrics.Meter = metrics.NilMeter{} - serveSuccessMeter metrics.Meter = metrics.NilMeter{} - dialMeter metrics.Meter = metrics.NilMeter{} - dialSuccessMeter metrics.Meter = metrics.NilMeter{} - dialConnectionError metrics.Meter = metrics.NilMeter{} + serveMeter = metrics.NewRegisteredMeter("p2p/serves", nil) + serveSuccessMeter = metrics.NewRegisteredMeter("p2p/serves/success", nil) + dialMeter = metrics.NewRegisteredMeter("p2p/dials", nil) + dialSuccessMeter = metrics.NewRegisteredMeter("p2p/dials/success", nil) + dialConnectionError = metrics.NewRegisteredMeter("p2p/dials/error/connection", nil) // dial error meters dialTooManyPeers = metrics.NewRegisteredMeter("p2p/dials/error/saturated", nil) @@ -70,25 +70,10 @@ var ( serveProtoHandshakeError = metrics.NewRegisteredMeter("p2p/serves/error/rlpx/proto", nil) ) -func init() { - if !metrics.Enabled { - return - } - - activePeerGauge = metrics.NewRegisteredGauge("p2p/peers", nil) - activeInboundPeerGauge = metrics.NewRegisteredGauge("p2p/peers/inbound", nil) - activeOutboundPeerGauge = metrics.NewRegisteredGauge("p2p/peers/outbound", nil) - serveMeter = metrics.NewRegisteredMeter("p2p/serves", nil) - serveSuccessMeter = metrics.NewRegisteredMeter("p2p/serves/success", nil) - dialMeter = metrics.NewRegisteredMeter("p2p/dials", nil) - dialSuccessMeter = metrics.NewRegisteredMeter("p2p/dials/success", nil) - dialConnectionError = metrics.NewRegisteredMeter("p2p/dials/error/connection", nil) -} - // markDialError matches errors that occur while setting up a dial connection // to the corresponding meter. func markDialError(err error) { - if !metrics.Enabled { + if !metrics.Enabled() { return } if err2 := errors.Unwrap(err); err2 != nil { @@ -115,7 +100,7 @@ func markDialError(err error) { // markServeError matches errors that occur while setting up a serve connection // to the corresponding meter. func markServeError(err error) { - if !metrics.Enabled { + if !metrics.Enabled() { return } if err2 := errors.Unwrap(err); err2 != nil { @@ -149,7 +134,7 @@ type meteredConn struct { // connection meter and also increases the metered peer count. If the metrics // system is disabled, function returns the original connection. func newMeteredConn(conn net.Conn) net.Conn { - if !metrics.Enabled { + if !metrics.Enabled() { return conn } return &meteredConn{Conn: conn} diff --git a/p2p/peer.go b/p2p/peer.go index ab5a93109d..dd13480f21 100644 --- a/p2p/peer.go +++ b/p2p/peer.go @@ -390,7 +390,7 @@ func (p *Peer) handle(msg Msg) error { if err != nil { return fmt.Errorf("msg code out of range: %v", msg.Code) } - if metrics.Enabled { + if metrics.Enabled() { m := fmt.Sprintf("%s/%s/%d/%#02x", ingressMeterName, proto.Name, proto.Version, msg.Code-proto.offset) metrics.GetOrRegisterMeter(m, nil).Mark(int64(msg.meterSize)) metrics.GetOrRegisterMeter(m+"/packets", nil).Mark(1) diff --git a/p2p/tracker/tracker.go b/p2p/tracker/tracker.go index 6a733b9ba5..5b72eb2b88 100644 --- a/p2p/tracker/tracker.go +++ b/p2p/tracker/tracker.go @@ -84,7 +84,7 @@ func New(protocol string, timeout time.Duration) *Tracker { // Track adds a network request to the tracker to wait for a response to arrive // or until the request it cancelled or times out. func (t *Tracker) Track(peer string, version uint, reqCode uint64, resCode uint64, id uint64) { - if !metrics.Enabled { + if !metrics.Enabled() { return } t.lock.Lock() @@ -163,7 +163,7 @@ func (t *Tracker) schedule() { // Fulfil fills a pending request, if any is available, reporting on various metrics. func (t *Tracker) Fulfil(peer string, version uint, code uint64, id uint64) { - if !metrics.Enabled { + if !metrics.Enabled() { return } t.lock.Lock() diff --git a/p2p/transport.go b/p2p/transport.go index c8fc3c92aa..6e11fe8689 100644 --- a/p2p/transport.go +++ b/p2p/transport.go @@ -99,7 +99,7 @@ func (t *rlpxTransport) WriteMsg(msg Msg) error { // Set metrics. msg.meterSize = size - if metrics.Enabled && msg.meterCap.Name != "" { // don't meter non-subprotocol messages + if metrics.Enabled() && msg.meterCap.Name != "" { // don't meter non-subprotocol messages m := fmt.Sprintf("%s/%s/%d/%#02x", egressMeterName, msg.meterCap.Name, msg.meterCap.Version, msg.meterCode) metrics.GetOrRegisterMeter(m, nil).Mark(int64(msg.meterSize)) metrics.GetOrRegisterMeter(m+"/packets", nil).Mark(1) diff --git a/rpc/metrics.go b/rpc/metrics.go index e44e8d76e1..fc04189dd3 100644 --- a/rpc/metrics.go +++ b/rpc/metrics.go @@ -49,7 +49,7 @@ func updateServeTimeHistogram(method string, success bool, elapsed time.Duration metrics.GetOrRegisterHistogramLazy(h, nil, sampler).Update(elapsed.Nanoseconds()) } -func newRPCRequestGauge(method string) metrics.Gauge { +func newRPCRequestGauge(method string) *metrics.Gauge { m := fmt.Sprintf("rpc/count/%s", method) return metrics.GetOrRegisterGauge(m, nil) } diff --git a/signer/core/apitypes/types.go b/signer/core/apitypes/types.go index 6ad052d72e..082e5009e2 100644 --- a/signer/core/apitypes/types.go +++ b/signer/core/apitypes/types.go @@ -680,7 +680,7 @@ func (typedData *TypedData) EncodePrimitiveValue(encType string, encValue interf if err != nil { return nil, err } - return math.U256Bytes(b), nil + return math.U256Bytes(new(big.Int).Set(b)), nil } return nil, fmt.Errorf("unrecognized type '%s'", encType) } diff --git a/tests/state_test.go b/tests/state_test.go index 478dfc098d..ce04a15edd 100644 --- a/tests/state_test.go +++ b/tests/state_test.go @@ -300,7 +300,8 @@ func runBenchmark(b *testing.B, t *StateTest) { context := core.NewEVMBlockContext(block.Header(), nil, &t.json.Env.Coinbase) context.GetHash = vmTestBlockHash context.BaseFee = baseFee - evm := vm.NewEVM(context, txContext, state.StateDB, config, vmconfig) + evm := vm.NewEVM(context, state.StateDB, config, vmconfig) + evm.SetTxContext(txContext) // Create "contract" for sender to cache code analysis. sender := vm.NewContract(vm.AccountRef(msg.From), vm.AccountRef(msg.From), diff --git a/tests/state_test_util.go b/tests/state_test_util.go index 3acda3533f..27eb13eb7b 100644 --- a/tests/state_test_util.go +++ b/tests/state_test_util.go @@ -277,7 +277,6 @@ func (t *StateTest) RunNoVerify(subtest StateSubtest, vmconfig vm.Config, snapsh } // Prepare the EVM. - txContext := core.NewEVMTxContext(msg) context := core.NewEVMBlockContext(block.Header(), nil, &t.json.Env.Coinbase) context.GetHash = vmTestBlockHash context.BaseFee = baseFee @@ -293,7 +292,7 @@ func (t *StateTest) RunNoVerify(subtest StateSubtest, vmconfig vm.Config, snapsh if config.IsCancun(new(big.Int), block.Time()) && t.json.Env.ExcessBlobGas != nil { context.BlobBaseFee = eip4844.CalcBlobFee(*t.json.Env.ExcessBlobGas) } - evm := vm.NewEVM(context, txContext, st.StateDB, config, vmconfig) + evm := vm.NewEVM(context, st.StateDB, config, vmconfig) if tracer := vmconfig.Tracer; tracer != nil && tracer.OnTxStart != nil { tracer.OnTxStart(evm.GetVMContext(), nil, msg.From) diff --git a/trie/proof.go b/trie/proof.go index a39d6b4ea3..2e527348bf 100644 --- a/trie/proof.go +++ b/trie/proof.go @@ -48,7 +48,7 @@ func (t *Trie) Prove(key []byte, proofDb ethdb.KeyValueWriter) error { for len(key) > 0 && tn != nil { switch n := tn.(type) { case *shortNode: - if len(key) < len(n.Key) || !bytes.Equal(n.Key, key[:len(n.Key)]) { + if !bytes.HasPrefix(key, n.Key) { // The trie doesn't contain the key. tn = nil } else { @@ -371,7 +371,7 @@ func unset(parent node, child node, key []byte, pos int, removeLeft bool) error } return unset(cld, cld.Children[key[pos]], key, pos+1, removeLeft) case *shortNode: - if len(key[pos:]) < len(cld.Key) || !bytes.Equal(cld.Key, key[pos:pos+len(cld.Key)]) { + if !bytes.HasPrefix(key[pos:], cld.Key) { // Find the fork point, it's a non-existent branch. if removeLeft { if bytes.Compare(cld.Key, key[pos:]) < 0 { @@ -434,7 +434,7 @@ func hasRightElement(node node, key []byte) bool { } node, pos = rn.Children[key[pos]], pos+1 case *shortNode: - if len(key)-pos < len(rn.Key) || !bytes.Equal(rn.Key, key[pos:pos+len(rn.Key)]) { + if !bytes.HasPrefix(key[pos:], rn.Key) { return bytes.Compare(rn.Key, key[pos:]) > 0 } node, pos = rn.Val, pos+len(rn.Key) @@ -486,13 +486,11 @@ func VerifyRangeProof(rootHash common.Hash, firstKey []byte, keys [][]byte, valu return false, fmt.Errorf("inconsistent proof data, keys: %d, values: %d", len(keys), len(values)) } // Ensure the received batch is monotonic increasing and contains no deletions - for i := 0; i < len(keys)-1; i++ { - if bytes.Compare(keys[i], keys[i+1]) >= 0 { + for i := 0; i < len(keys); i++ { + if i < len(keys)-1 && bytes.Compare(keys[i], keys[i+1]) >= 0 { return false, errors.New("range is not monotonically increasing") } - } - for _, value := range values { - if len(value) == 0 { + if len(values[i]) == 0 { return false, errors.New("range contains deletion") } } @@ -589,7 +587,7 @@ func get(tn node, key []byte, skipResolved bool) ([]byte, node) { for { switch n := tn.(type) { case *shortNode: - if len(key) < len(n.Key) || !bytes.Equal(n.Key, key[:len(n.Key)]) { + if !bytes.HasPrefix(key, n.Key) { return nil, nil } tn = n.Val diff --git a/trie/trie.go b/trie/trie.go index 1a5efc2910..aeb7398899 100644 --- a/trie/trie.go +++ b/trie/trie.go @@ -163,7 +163,7 @@ func (t *Trie) get(origNode node, key []byte, pos int) (value []byte, newnode no case valueNode: return n, n, false, nil case *shortNode: - if len(key)-pos < len(n.Key) || !bytes.Equal(n.Key, key[pos:pos+len(n.Key)]) { + if !bytes.HasPrefix(key[pos:], n.Key) { // key not found in trie return nil, n, false, nil } @@ -219,9 +219,6 @@ func (t *Trie) GetNode(path []byte) ([]byte, int, error) { if resolved > 0 { t.root = newroot } - if item == nil { - return nil, resolved, nil - } return item, resolved, nil } @@ -254,7 +251,7 @@ func (t *Trie) getNode(origNode node, path []byte, pos int) (item []byte, newnod return nil, nil, 0, nil case *shortNode: - if len(path)-pos < len(n.Key) || !bytes.Equal(n.Key, path[pos:pos+len(n.Key)]) { + if !bytes.HasPrefix(path[pos:], n.Key) { // Path branches off from short node return nil, n, 0, nil } diff --git a/trie/utils/verkle.go b/trie/utils/verkle.go index 4294f5e4be..1d33f6c3e5 100644 --- a/trie/utils/verkle.go +++ b/trie/utils/verkle.go @@ -41,10 +41,10 @@ var ( zero = uint256.NewInt(0) verkleNodeWidthLog2 = 8 headerStorageOffset = uint256.NewInt(64) - mainStorageOffsetLshVerkleNodeWidth = new(uint256.Int).Lsh(uint256.NewInt(256), 31-uint(verkleNodeWidthLog2)) codeOffset = uint256.NewInt(128) verkleNodeWidth = uint256.NewInt(256) codeStorageDelta = uint256.NewInt(0).Sub(codeOffset, headerStorageOffset) + mainStorageOffsetLshVerkleNodeWidth = new(uint256.Int).Lsh(uint256.NewInt(1), 248-uint(verkleNodeWidthLog2)) index0Point *verkle.Point // pre-computed commitment of polynomial [2+256*64] @@ -217,7 +217,7 @@ func StorageIndex(storageKey []byte) (*uint256.Int, byte) { // The first MAIN_STORAGE_OFFSET group will see its // first 64 slots unreachable. This is either a typo in the // spec or intended to conserve the 256-u256 - // aligment. If we decide to ever access these 64 + // alignment. If we decide to ever access these 64 // slots, uncomment this. // // Get the new offset since we now know that we are above 64. // pos.Sub(&pos, codeStorageDelta) @@ -273,17 +273,9 @@ func StorageSlotKeyWithEvaluatedAddress(evaluated *verkle.Point, storageKey []by } func pointToHash(evaluated *verkle.Point, suffix byte) []byte { - // The output of Byte() is big endian for banderwagon. This - // introduces an imbalance in the tree, because hashes are - // elements of a 253-bit field. This means more than half the - // tree would be empty. To avoid this problem, use a little - // endian commitment and chop the MSB. - bytes := evaluated.Bytes() - for i := 0; i < 16; i++ { - bytes[31-i], bytes[i] = bytes[i], bytes[31-i] - } - bytes[31] = suffix - return bytes[:] + retb := verkle.HashPointToBytes(evaluated) + retb[31] = suffix + return retb[:] } func evaluateAddressPoint(address []byte) *verkle.Point { diff --git a/trie/verkle_test.go b/trie/verkle_test.go index 4cd1717c0e..84eec2ed30 100644 --- a/trie/verkle_test.go +++ b/trie/verkle_test.go @@ -136,8 +136,8 @@ func TestVerkleRollBack(t *testing.T) { } } - // ensure there is some code in the 2nd group - keyOf2ndGroup := []byte{141, 124, 185, 236, 50, 22, 185, 39, 244, 47, 97, 209, 96, 235, 22, 13, 205, 38, 18, 201, 128, 223, 0, 59, 146, 199, 222, 119, 133, 13, 91, 0} + // ensure there is some code in the 2nd group of the 1st account + keyOf2ndGroup := utils.CodeChunkKeyWithEvaluatedAddress(tr.cache.Get(common.Address{1}.Bytes()), uint256.NewInt(128)) chunk, err := tr.root.Get(keyOf2ndGroup, nil) if err != nil { t.Fatalf("Failed to get account, %v", err) diff --git a/triedb/database.go b/triedb/database.go index 1d317b9854..0126a02cdf 100644 --- a/triedb/database.go +++ b/triedb/database.go @@ -63,6 +63,10 @@ type backend interface { // An error will be returned if the specified state is not available. NodeReader(root common.Hash) (database.NodeReader, error) + // StateReader returns a reader for accessing flat states within the specified + // state. An error will be returned if the specified state is not available. + StateReader(root common.Hash) (database.StateReader, error) + // Initialized returns an indicator if the state data is already initialized // according to the state scheme. Initialized(genesisRoot common.Hash) bool @@ -167,6 +171,13 @@ func (db *Database) NodeReader(blockRoot common.Hash) (database.NodeReader, erro return db.backend.NodeReader(blockRoot) } +// StateReader returns a reader that allows access to the state data associated +// with the specified state. An error will be returned if the specified state is +// not available. +func (db *Database) StateReader(blockRoot common.Hash) (database.StateReader, error) { + return db.backend.StateReader(blockRoot) +} + // Update performs a state transition by committing dirty nodes contained in the // given set in order to update state from the specified parent to the specified // root. The held pre-images accumulated up to this point will be flushed in case diff --git a/triedb/hashdb/database.go b/triedb/hashdb/database.go index 2468f18012..5bdfe582cb 100644 --- a/triedb/hashdb/database.go +++ b/triedb/hashdb/database.go @@ -637,3 +637,9 @@ func (reader *reader) Node(owner common.Hash, path []byte, hash common.Hash) ([] blob, _ := reader.db.node(hash) return blob, nil } + +// StateReader returns a reader that allows access to the state data associated +// with the specified state. +func (db *Database) StateReader(root common.Hash) (database.StateReader, error) { + return nil, errors.New("not implemented") +} diff --git a/triedb/pathdb/asyncnodebuffer.go b/triedb/pathdb/asyncnodebuffer.go index 8a0a8f53c3..4a858f321a 100644 --- a/triedb/pathdb/asyncnodebuffer.go +++ b/triedb/pathdb/asyncnodebuffer.go @@ -1,8 +1,7 @@ package pathdb import ( - "bytes" - "fmt" + "maps" "sync" "sync/atomic" "time" @@ -10,8 +9,6 @@ import ( "github.com/VictoriaMetrics/fastcache" "github.com/ethereum/go-ethereum/common" - "github.com/ethereum/go-ethereum/core/rawdb" - "github.com/ethereum/go-ethereum/crypto" "github.com/ethereum/go-ethereum/ethdb" "github.com/ethereum/go-ethereum/log" "github.com/ethereum/go-ethereum/trie/trienode" @@ -29,16 +26,34 @@ type asyncnodebuffer struct { stopFlushing atomic.Bool } -// newAsyncNodeBuffer initializes the async node buffer with the provided nodes. -func newAsyncNodeBuffer(limit int, nodes *nodeSet, layers uint64) *asyncnodebuffer { - if nodes == nil { - nodes = newNodeSet(nil) +// newAsyncNodeBuffer initializes the async node buffer with the provided nodes and states. +func newAsyncNodeBuffer(limit int, nodes *nodeSet, states *stateSet, layers uint64) *asyncnodebuffer { + return &asyncnodebuffer{ + current: newNodeCache(limit, nodes, states, layers), + background: newNodeCache(limit, nil, nil, 0), } +} - return &asyncnodebuffer{ - current: newNodeCache(uint64(limit), nodes.size, nodes.nodes, layers), - background: newNodeCache(uint64(limit), 0, make(map[common.Hash]map[string]*trienode.Node), 0), +func (a *asyncnodebuffer) account(hash common.Hash) ([]byte, bool) { + a.mux.RLock() + defer a.mux.RUnlock() + + node, found := a.current.account(hash) + if !found { + node, found = a.background.account(hash) + } + return node, found +} + +func (a *asyncnodebuffer) storage(addrHash common.Hash, storageHash common.Hash) ([]byte, bool) { + a.mux.RLock() + defer a.mux.RUnlock() + + node, found := a.current.storage(addrHash, storageHash) + if !found { + node, found = a.background.storage(addrHash, storageHash) } + return node, found } // node retrieves the trie node with given node info. @@ -46,32 +61,32 @@ func (a *asyncnodebuffer) node(owner common.Hash, path []byte) (*trienode.Node, a.mux.RLock() defer a.mux.RUnlock() - node := a.current.node(owner, path) - if node == nil { - node = a.background.node(owner, path) + node, found := a.current.node(owner, path) + if !found { + node, found = a.background.node(owner, path) } - return node, node != nil + return node, found } -// commit merges the dirty nodes into the nodebuffer. This operation won't take +// commit merges the provided states and trie nodesinto the nodebuffer. This operation won't take // the ownership of the nodes map which belongs to the bottom-most diff layer. // It will just hold the node references from the given map which are safe to // copy. -func (a *asyncnodebuffer) commit(nodes *nodeSet) trienodebuffer { +func (a *asyncnodebuffer) commit(nodes *nodeSet, states *stateSet) trienodebuffer { a.mux.Lock() defer a.mux.Unlock() - err := a.current.commit(nodes.nodes) + err := a.current.commit(nodes, states) if err != nil { log.Crit("[BUG] Failed to commit nodes to asyncnodebuffer", "error", err) } return a } -// revert is the reverse operation of commit. It also merges the provided nodes +// revertTo is the reverse operation of commit. It also merges the provided nodes // into the nodebuffer, the difference is that the provided node set should // revert the changes made by the last state transition. -func (a *asyncnodebuffer) revert(db ethdb.KeyValueReader, nodes map[common.Hash]map[string]*trienode.Node) error { +func (a *asyncnodebuffer) revertTo(db ethdb.KeyValueReader, nodes map[common.Hash]map[string]*trienode.Node, accounts map[common.Hash][]byte, storages map[common.Hash]map[common.Hash][]byte) error { a.mux.Lock() defer a.mux.Unlock() @@ -81,7 +96,7 @@ func (a *asyncnodebuffer) revert(db ethdb.KeyValueReader, nodes map[common.Hash] log.Crit("[BUG] Failed to merge node cache under revert async node buffer", "error", err) } a.background.reset() - return a.current.revert(db, nodes) + return a.current.revertTo(db, nodes, accounts, storages) } // reset cleans up the disk cache. @@ -101,6 +116,10 @@ func (a *asyncnodebuffer) empty() bool { return a.current.empty() && a.background.empty() } +func (a *asyncnodebuffer) full() bool { + return a.current.full() +} + // flush persists the in-memory dirty trie node into the disk if the configured // memory threshold is reached. Note, all data must be written atomically. func (a *asyncnodebuffer) flush(db ethdb.KeyValueStore, freezer ethdb.AncientWriter, clean *fastcache.Cache, id uint64, force bool) error { @@ -119,11 +138,11 @@ func (a *asyncnodebuffer) flush(db ethdb.KeyValueStore, freezer ethdb.AncientWri continue } atomic.StoreUint64(&a.current.immutable, 1) - return a.current.flush(db, freezer, clean, id) + return a.current.flush(db, freezer, clean, id, true) } } - if a.current.size < a.current.limit { + if !a.full() { return nil } @@ -139,7 +158,7 @@ func (a *asyncnodebuffer) flush(db ethdb.KeyValueStore, freezer ethdb.AncientWri go func(persistID uint64) { defer a.isFlushing.Store(false) for { - err := a.background.flush(db, freezer, clean, persistID) + err := a.background.flush(db, freezer, clean, persistID, true) if err == nil { log.Debug("Succeed to flush background nodecache to disk", "state_id", persistID) return @@ -158,7 +177,7 @@ func (a *asyncnodebuffer) waitAndStopFlushing() { } } -func (a *asyncnodebuffer) getAllNodes() *nodeSet { +func (a *asyncnodebuffer) getAllNodesAndStates() (*nodeSet, *stateSet) { a.mux.Lock() defer a.mux.Unlock() @@ -166,7 +185,7 @@ func (a *asyncnodebuffer) getAllNodes() *nodeSet { if err != nil { log.Crit("[BUG] Failed to merge node cache under revert async node buffer", "error", err) } - return newNodeSet(cached.nodes) + return cached.nodes, cached.states } func (a *asyncnodebuffer) getLayers() uint64 { @@ -180,153 +199,48 @@ func (a *asyncnodebuffer) getSize() (uint64, uint64) { a.mux.RLock() defer a.mux.RUnlock() - return a.current.size, a.background.size + return a.current.size(), a.background.size() } type nodecache struct { - layers uint64 // The number of diff layers aggregated inside - size uint64 // The size of aggregated writes - limit uint64 // The maximum memory allowance in bytes - nodes map[common.Hash]map[string]*trienode.Node // The dirty node set, mapped by owner and path - immutable uint64 // The flag equal 1, flush nodes to disk background + *buffer + immutable uint64 // The flag equal 1, flush nodes to disk background } -func newNodeCache(limit, size uint64, nodes map[common.Hash]map[string]*trienode.Node, layers uint64) *nodecache { +func newNodeCache(limit int, nodes *nodeSet, states *stateSet, layers uint64) *nodecache { return &nodecache{ - layers: layers, - size: size, - limit: limit, - nodes: nodes, + buffer: newBuffer(limit, nodes, states, layers), immutable: 0, } } -func (nc *nodecache) node(owner common.Hash, path []byte) *trienode.Node { - subset, ok := nc.nodes[owner] - if !ok { - return nil - } - n, ok := subset[string(path)] - if !ok { - return nil - } - return n -} - -func (nc *nodecache) commit(nodes map[common.Hash]map[string]*trienode.Node) error { +func (nc *nodecache) commit(nodes *nodeSet, states *stateSet) error { if atomic.LoadUint64(&nc.immutable) == 1 { return errWriteImmutable } - - var ( - delta int64 - overwrite int64 - overwriteSize int64 - ) - for owner, subset := range nodes { - current, exist := nc.nodes[owner] - if !exist { - // Allocate a new map for the subset instead of claiming it directly - // from the passed map to avoid potential concurrent map read/write. - // The nodes belong to original diff layer are still accessible even - // after merging, thus the ownership of nodes map should still belong - // to original layer and any mutation on it should be prevented. - current = make(map[string]*trienode.Node) - for path, n := range subset { - current[path] = n - delta += int64(len(n.Blob) + len(path)) - } - nc.nodes[owner] = current - continue - } - for path, n := range subset { - if orig, exist := current[path]; !exist { - delta += int64(len(n.Blob) + len(path)) - } else { - delta += int64(len(n.Blob) - len(orig.Blob)) - overwrite++ - overwriteSize += int64(len(orig.Blob) + len(path)) - } - current[path] = n - } - nc.nodes[owner] = current - } - nc.updateSize(delta) - nc.layers++ - gcTrieNodeMeter.Mark(overwrite) - gcTrieNodeBytesMeter.Mark(overwriteSize) + nc.buffer.commit(nodes, states) return nil } -func (nc *nodecache) updateSize(delta int64) { - size := int64(nc.size) + delta - if size >= 0 { - nc.size = uint64(size) - return +func (nc *nodecache) revertTo(db ethdb.KeyValueReader, nodes map[common.Hash]map[string]*trienode.Node, accounts map[common.Hash][]byte, storages map[common.Hash]map[common.Hash][]byte) error { + if atomic.LoadUint64(&nc.immutable) == 1 { + return errRevertImmutable } - s := nc.size - nc.size = 0 - log.Error("Invalid pathdb buffer size", "prev", common.StorageSize(s), "delta", common.StorageSize(delta)) + nc.buffer.revertTo(db, nodes, accounts, storages) + return nil } func (nc *nodecache) reset() { atomic.StoreUint64(&nc.immutable, 0) - nc.layers = 0 - nc.size = 0 - nc.nodes = make(map[common.Hash]map[string]*trienode.Node) -} - -func (nc *nodecache) empty() bool { - return nc.layers == 0 -} - -// allocBatch returns a database batch with pre-allocated buffer. -func (nc *nodecache) allocBatch(db ethdb.KeyValueStore) ethdb.Batch { - var metasize int - for owner, nodes := range nc.nodes { - if owner == (common.Hash{}) { - metasize += len(nodes) * len(rawdb.TrieNodeAccountPrefix) // database key prefix - } else { - metasize += len(nodes) * (len(rawdb.TrieNodeStoragePrefix) + common.HashLength) // database key prefix + owner - } - } - return db.NewBatchWithSize((metasize + int(nc.size)) * 11 / 10) // extra 10% for potential pebble internal stuff + nc.buffer.reset() } -func (nc *nodecache) flush(db ethdb.KeyValueStore, freezer ethdb.AncientWriter, clean *fastcache.Cache, id uint64) error { +func (nc *nodecache) flush(db ethdb.KeyValueStore, freezer ethdb.AncientWriter, nodesCache *fastcache.Cache, id uint64, force bool) error { if atomic.LoadUint64(&nc.immutable) != 1 { return errFlushMutable } - - // Ensure the target state id is aligned with the internal counter. - head := rawdb.ReadPersistentStateID(db) - if head+nc.layers != id { - return fmt.Errorf("buffer layers (%d) cannot be applied on top of persisted state id (%d) to reach requested state id (%d)", nc.layers, head, id) - } - var ( - start = time.Now() - batch = nc.allocBatch(db) - ) - // Explicitly sync the state freezer, ensuring that all written - // data is transferred to disk before updating the key-value store. - if freezer != nil { - if err := freezer.Sync(); err != nil { - return err - } - } - nodes := writeNodes(batch, nc.nodes, clean) - rawdb.WritePersistentStateID(batch, id) - - // Flush all mutations in a single batch - size := batch.ValueSize() - if err := batch.Write(); err != nil { - return err - } - commitBytesMeter.Mark(int64(size)) - commitNodesMeter.Mark(int64(nodes)) - commitTimeTimer.UpdateSince(start) - log.Debug("Persisted pathdb nodes", "nodes", len(nc.nodes), "bytes", common.StorageSize(size), "elapsed", common.PrettyDuration(time.Since(start))) - nc.reset() + nc.buffer.flush(db, freezer, nodesCache, id, force) + atomic.StoreUint64(&nc.immutable, 0) return nil } @@ -351,7 +265,6 @@ func (nc *nodecache) merge(nc1 *nodecache) (*nodecache, error) { var ( immutable *nodecache mutable *nodecache - res = &nodecache{} ) if atomic.LoadUint64(&nc.immutable) == 1 { immutable = nc @@ -360,100 +273,29 @@ func (nc *nodecache) merge(nc1 *nodecache) (*nodecache, error) { immutable = nc1 mutable = nc } - res.size = immutable.size + mutable.size - res.layers = immutable.layers + mutable.layers - res.limit = immutable.limit - res.nodes = make(map[common.Hash]map[string]*trienode.Node) - for acc, subTree := range immutable.nodes { - if _, ok := res.nodes[acc]; !ok { - res.nodes[acc] = make(map[string]*trienode.Node) - } - for path, node := range subTree { - res.nodes[acc][path] = node - } - } - - for acc, subTree := range mutable.nodes { - if _, ok := res.nodes[acc]; !ok { - res.nodes[acc] = make(map[string]*trienode.Node) - } - for path, node := range subTree { - res.nodes[acc][path] = node - } - } + res := copyNodeCache(immutable) + atomic.StoreUint64(&res.immutable, 0) + res.nodes.merge(mutable.nodes) + res.states.merge(mutable.states) return res, nil } -func (nc *nodecache) revert(db ethdb.KeyValueReader, nodes map[common.Hash]map[string]*trienode.Node) error { - if atomic.LoadUint64(&nc.immutable) == 1 { - return errRevertImmutable - } - - // Short circuit if no embedded state transition to revert. - if nc.layers == 0 { - return errStateUnrecoverable - } - nc.layers-- - - // Reset the entire buffer if only a single transition left. - if nc.layers == 0 { - nc.reset() - return nil - } - var delta int64 - for owner, subset := range nodes { - current, ok := nc.nodes[owner] - if !ok { - panic(fmt.Sprintf("non-existent subset (%x)", owner)) - } - for path, n := range subset { - orig, ok := current[path] - if !ok { - // There is a special case in MPT that one child is removed from - // a fullNode which only has two children, and then a new child - // with different position is immediately inserted into the fullNode. - // In this case, the clean child of the fullNode will also be - // marked as dirty because of node collapse and expansion. - // - // In case of database rollback, don't panic if this "clean" - // node occurs which is not present in buffer. - var blob []byte - if owner == (common.Hash{}) { - blob = rawdb.ReadAccountTrieNode(db, []byte(path)) - } else { - blob = rawdb.ReadStorageTrieNode(db, owner, []byte(path)) - } - if bytes.Equal(blob, n.Blob) { - continue - } - panic(fmt.Sprintf("non-existent node (%x %v) blob: %v", owner, path, crypto.Keccak256Hash(n.Blob).Hex())) - } - current[path] = n - delta += int64(len(n.Blob)) - int64(len(orig.Blob)) - } - } - nc.updateSize(delta) - return nil -} - func copyNodeCache(n *nodecache) *nodecache { - if n == nil { + if n == nil || n.buffer == nil { return nil } - nc := &nodecache{ - layers: n.layers, - size: n.size, - limit: n.limit, - immutable: atomic.LoadUint64(&n.immutable), - nodes: make(map[common.Hash]map[string]*trienode.Node), + nc := newNodeCache(int(n.limit), n.nodes, n.states, n.layers) + nc.immutable = atomic.LoadUint64(&n.immutable) + + for acc, subTree := range n.nodes.nodes { + nc.nodes.nodes[acc] = maps.Clone(subTree) } - for acc, subTree := range n.nodes { - if _, ok := nc.nodes[acc]; !ok { - nc.nodes[acc] = make(map[string]*trienode.Node) - } - for path, node := range subTree { - nc.nodes[acc][path] = node - } + nc.nodes.size = n.nodes.size + + storageData := make(map[common.Hash]map[common.Hash][]byte, len(n.states.storageData)) + for accountHash, storage := range n.states.storageData { + storageData[accountHash] = maps.Clone(storage) } + nc.states = newStates(maps.Clone(n.states.accountData), storageData) return nc } diff --git a/triedb/pathdb/buffer.go b/triedb/pathdb/buffer.go index 27554b8abf..6c99760eb8 100644 --- a/triedb/pathdb/buffer.go +++ b/triedb/pathdb/buffer.go @@ -33,40 +33,56 @@ import ( // must be checked before diving into disk (since it basically is not yet written // data). type buffer struct { - layers uint64 // The number of diff layers aggregated inside - limit uint64 // The maximum memory allowance in bytes - nodes *nodeSet // Aggregated trie node set + layers uint64 // The number of diff layers aggregated inside + limit uint64 // The maximum memory allowance in bytes + nodes *nodeSet // Aggregated trie node set + states *stateSet // Aggregated state set } // newBuffer initializes the buffer with the provided states and trie nodes. -func newBuffer(limit int, nodes *nodeSet, layers uint64) *buffer { +func newBuffer(limit int, nodes *nodeSet, states *stateSet, layers uint64) *buffer { // Don't panic for lazy users if any provided set is nil if nodes == nil { nodes = newNodeSet(nil) } + if states == nil { + states = newStates(nil, nil) + } return &buffer{ layers: layers, limit: uint64(limit), nodes: nodes, + states: states, } } +// account retrieves the account blob with account address hash. +func (b *buffer) account(hash common.Hash) ([]byte, bool) { + return b.states.account(hash) +} + +// storage retrieves the storage slot with account address hash and slot key. +func (b *buffer) storage(addrHash common.Hash, storageHash common.Hash) ([]byte, bool) { + return b.states.storage(addrHash, storageHash) +} + // node retrieves the trie node with node path and its trie identifier. func (b *buffer) node(owner common.Hash, path []byte) (*trienode.Node, bool) { return b.nodes.node(owner, path) } // commit merges the provided states and trie nodes into the buffer. -func (b *buffer) commit(nodes *nodeSet) trienodebuffer { +func (b *buffer) commit(nodes *nodeSet, states *stateSet) trienodebuffer { b.layers++ b.nodes.merge(nodes) + b.states.merge(states) return b } -// revert is the reverse operation of commit. It also merges the provided states +// revertTo is the reverse operation of commit. It also merges the provided states // and trie nodes into the buffer. The key difference is that the provided state // set should reverse the changes made by the most recent state transition. -func (b *buffer) revert(db ethdb.KeyValueReader, nodes map[common.Hash]map[string]*trienode.Node) error { +func (b *buffer) revertTo(db ethdb.KeyValueReader, nodes map[common.Hash]map[string]*trienode.Node, accounts map[common.Hash][]byte, storages map[common.Hash]map[common.Hash][]byte) error { // Short circuit if no embedded state transition to revert if b.layers == 0 { return errStateUnrecoverable @@ -78,7 +94,8 @@ func (b *buffer) revert(db ethdb.KeyValueReader, nodes map[common.Hash]map[strin b.reset() return nil } - b.nodes.revert(db, nodes) + b.nodes.revertTo(db, nodes) + b.states.revertTo(accounts, storages) return nil } @@ -86,6 +103,7 @@ func (b *buffer) revert(db ethdb.KeyValueReader, nodes map[common.Hash]map[strin func (b *buffer) reset() { b.layers = 0 b.nodes.reset() + b.states.reset() } // empty returns an indicator if buffer is empty. @@ -101,7 +119,7 @@ func (b *buffer) full() bool { // size returns the approximate memory size of the held content. func (b *buffer) size() uint64 { - return b.nodes.size + return b.states.size + b.nodes.size } // flush persists the in-memory dirty trie node into the disk if the configured @@ -146,17 +164,17 @@ func (b *buffer) flush(db ethdb.KeyValueStore, freezer ethdb.AncientWriter, node func (b *buffer) waitAndStopFlushing() {} -// getSize return the nodebuffer used size. -func (b *buffer) getSize() (uint64, uint64) { - return b.nodes.size, 0 -} - -// getAllNodes return the trie nodes set are cached in nodebuffer. -func (b *buffer) getAllNodes() *nodeSet { - return b.nodes +// getAllNodesAndStates return the trie nodes and states cached in nodebuffer. +func (b *buffer) getAllNodesAndStates() (*nodeSet, *stateSet) { + return b.nodes, b.states } // getLayers return the size of cached difflayers. func (b *buffer) getLayers() uint64 { return b.layers } + +// getSize return the nodebuffer used size. +func (b *buffer) getSize() (uint64, uint64) { + return b.size(), 0 +} diff --git a/triedb/pathdb/database.go b/triedb/pathdb/database.go index 69b6cb340e..6e4a6c82c1 100644 --- a/triedb/pathdb/database.go +++ b/triedb/pathdb/database.go @@ -81,6 +81,24 @@ type layer interface { // Note, the hash parameter can access the diff-layer flat cache to speed up access. node(owner common.Hash, path []byte, hash common.Hash, depth int) ([]byte, common.Hash, *nodeLoc, error) + // account directly retrieves the account RLP associated with a particular + // hash in the slim data format. An error will be returned if the read + // operation exits abnormally. Specifically, if the layer is already stale. + // + // Note: + // - the returned account is not a copy, please don't modify it. + // - no error will be returned if the requested account is not found in database. + account(hash common.Hash, depth int) ([]byte, error) + + // storage directly retrieves the storage data associated with a particular hash, + // within a particular account. An error will be returned if the read operation + // exits abnormally. Specifically, if the layer is already stale. + // + // Note: + // - the returned storage data is not a copy, please don't modify it. + // - no error will be returned if the requested slot is not found in database. + storage(accountHash, storageHash common.Hash, depth int) ([]byte, error) + // rootHash returns the root hash for which this layer was made. rootHash() common.Hash @@ -147,17 +165,18 @@ var Defaults = &Config{ // ReadOnly is the config in order to open database in read only mode. var ReadOnly = &Config{ReadOnly: true} -// Database is a multiple-layered structure for maintaining in-memory trie nodes. -// It consists of one persistent base layer backed by a key-value store, on top -// of which arbitrarily many in-memory diff layers are stacked. The memory diffs -// can form a tree with branching, but the disk layer is singleton and common to -// all. If a reorg goes deeper than the disk layer, a batch of reverse diffs can -// be applied to rollback. The deepest reorg that can be handled depends on the -// amount of state histories tracked in the disk. +// Database is a multiple-layered structure for maintaining in-memory states +// along with its dirty trie nodes. It consists of one persistent base layer +// backed by a key-value store, on top of which arbitrarily many in-memory diff +// layers are stacked. The memory diffs can form a tree with branching, but the +// disk layer is singleton and common to all. If a reorg goes deeper than the +// disk layer, a batch of reverse diffs can be applied to rollback. The deepest +// reorg that can be handled depends on the amount of state histories tracked +// in the disk. // // At most one readable and writable database can be opened at the same time in -// the whole system which ensures that only one database writer can operate disk -// state. Unexpected open operations can cause the system to panic. +// the whole system which ensures that only one database writer can operate the +// persistent state. Unexpected open operations can cause the system to panic. type Database struct { // readOnly is the flag whether the mutation is allowed to be applied. // It will be set automatically when the database is journaled during @@ -379,7 +398,7 @@ func (db *Database) Enable(root common.Hash) error { } // Re-construct a new disk layer backed by persistent state // with **empty clean cache and node buffer**. - db.tree.reset(newDiskLayer(root, 0, db, nil, NewTrieNodeBuffer(db.config.SyncFlush, db.config.WriteBufferSize, nil, 0))) + db.tree.reset(newDiskLayer(root, 0, db, nil, NewTrieNodeBuffer(db.config.SyncFlush, db.config.WriteBufferSize, nil, nil, 0))) // Re-enable the database as the final step. db.waitSync = false diff --git a/triedb/pathdb/database_test.go b/triedb/pathdb/database_test.go index b9341da0d9..279f888a2a 100644 --- a/triedb/pathdb/database_test.go +++ b/triedb/pathdb/database_test.go @@ -309,7 +309,7 @@ func (t *tester) generate(parent common.Hash) (common.Hash, *trienode.MergedNode delete(t.storages, addrHash) } } - return root, ctx.nodes, NewStateSetWithOrigin(ctx.accountOrigin, ctx.storageOrigin) + return root, ctx.nodes, NewStateSetWithOrigin(ctx.accounts, ctx.storages, ctx.accountOrigin, ctx.storageOrigin) } // lastHash returns the latest root hash, or empty if nothing is cached. diff --git a/triedb/pathdb/difflayer.go b/triedb/pathdb/difflayer.go index c16417c8e2..98ab480693 100644 --- a/triedb/pathdb/difflayer.go +++ b/triedb/pathdb/difflayer.go @@ -170,6 +170,7 @@ func newDiffLayer(parent layer, root common.Hash, id uint64, block uint64, nodes } dirtyNodeWriteMeter.Mark(int64(nodes.size)) + dirtyStateWriteMeter.Mark(int64(states.size)) log.Debug("Created new diff layer", "id", id, "block", block, "nodesize", common.StorageSize(nodes.size), "statesize", common.StorageSize(states.size)) return dl } @@ -264,6 +265,58 @@ func (dl *diffLayer) intervalNode(owner common.Hash, path []byte, hash common.Ha return dl.parent.node(owner, path, hash, depth+1) } +// account directly retrieves the account RLP associated with a particular +// hash in the slim data format. +// +// Note the returned account is not a copy, please don't modify it. +func (dl *diffLayer) account(hash common.Hash, depth int) ([]byte, error) { + // Hold the lock, ensure the parent won't be changed during the + // state accessing. + dl.lock.RLock() + defer dl.lock.RUnlock() + + if blob, found := dl.states.account(hash); found { + dirtyStateHitMeter.Mark(1) + dirtyStateHitDepthHist.Update(int64(depth)) + dirtyStateReadMeter.Mark(int64(len(blob))) + + if len(blob) == 0 { + stateAccountInexMeter.Mark(1) + } else { + stateAccountExistMeter.Mark(1) + } + return blob, nil + } + // Account is unknown to this layer, resolve from parent + return dl.parent.account(hash, depth+1) +} + +// storage directly retrieves the storage data associated with a particular hash, +// within a particular account. +// +// Note the returned storage slot is not a copy, please don't modify it. +func (dl *diffLayer) storage(accountHash, storageHash common.Hash, depth int) ([]byte, error) { + // Hold the lock, ensure the parent won't be changed during the + // state accessing. + dl.lock.RLock() + defer dl.lock.RUnlock() + + if blob, found := dl.states.storage(accountHash, storageHash); found { + dirtyStateHitMeter.Mark(1) + dirtyStateHitDepthHist.Update(int64(depth)) + dirtyStateReadMeter.Mark(int64(len(blob))) + + if len(blob) == 0 { + stateStorageInexMeter.Mark(1) + } else { + stateStorageExistMeter.Mark(1) + } + return blob, nil + } + // storage slot is unknown to this layer, resolve from parent + return dl.parent.storage(accountHash, storageHash, depth+1) +} + // update implements the layer interface, creating a new layer on top of the // existing layer tree with the specified data items. func (dl *diffLayer) update(root common.Hash, id uint64, block uint64, nodes *nodeSet, states *StateSetWithOrigin) *diffLayer { diff --git a/triedb/pathdb/difflayer_test.go b/triedb/pathdb/difflayer_test.go index 30b087cca6..4851d88706 100644 --- a/triedb/pathdb/difflayer_test.go +++ b/triedb/pathdb/difflayer_test.go @@ -30,7 +30,7 @@ import ( func emptyLayer() *diskLayer { return &diskLayer{ db: New(rawdb.NewMemoryDatabase(), nil, false), - buffer: newBuffer(defaultDirtyBufferSize, nil, 0), + buffer: newBuffer(defaultDirtyBufferSize, nil, nil, 0), } } @@ -76,7 +76,7 @@ func benchmarkSearch(b *testing.B, depth int, total int) { nblob = common.CopyBytes(blob) } } - return newDiffLayer(parent, common.Hash{}, 0, 0, newNodeSet(nodes), NewStateSetWithOrigin(nil, nil)) + return newDiffLayer(parent, common.Hash{}, 0, 0, newNodeSet(nodes), NewStateSetWithOrigin(nil, nil, nil, nil)) } var layer layer layer = emptyLayer() @@ -118,7 +118,7 @@ func BenchmarkPersist(b *testing.B) { ) nodes[common.Hash{}][string(path)] = node } - return newDiffLayer(parent, common.Hash{}, 0, 0, newNodeSet(nodes), NewStateSetWithOrigin(nil, nil)) + return newDiffLayer(parent, common.Hash{}, 0, 0, newNodeSet(nodes), NewStateSetWithOrigin(nil, nil, nil, nil)) } for i := 0; i < b.N; i++ { b.StopTimer() @@ -156,7 +156,7 @@ func BenchmarkJournal(b *testing.B) { ) nodes[common.Hash{}][string(path)] = node } - return newDiffLayer(parent, common.Hash{}, 0, 0, newNodeSet(nodes), new(StateSetWithOrigin)) + return newDiffLayer(parent, common.Hash{}, 0, 0, newNodeSet(nodes), NewStateSetWithOrigin(nil, nil, nil, nil)) } var layer layer layer = emptyLayer() diff --git a/triedb/pathdb/disklayer.go b/triedb/pathdb/disklayer.go index 8ecfc2bcac..295478089e 100644 --- a/triedb/pathdb/disklayer.go +++ b/triedb/pathdb/disklayer.go @@ -17,6 +17,7 @@ package pathdb import ( + "errors" "fmt" "sync" @@ -33,19 +34,25 @@ import ( // write. The content of the trienodebuffer must be checked before diving into // disk (since it basically is not-yet-written data). type trienodebuffer interface { + // account retrieves the account blob with account address hash. + account(hash common.Hash) ([]byte, bool) + + // storage retrieves the storage slot with account address hash and slot key. + storage(addrHash common.Hash, storageHash common.Hash) ([]byte, bool) + // node retrieves the trie node with given node info. node(owner common.Hash, path []byte) (*trienode.Node, bool) - // commit merges the dirty nodes into the trienodebuffer. This operation won't take + // commit merges the provided states and trie nodes into the buffer. This operation won't take // the ownership of the nodes map which belongs to the bottom-most diff layer. // It will just hold the node references from the given map which are safe to // copy. - commit(*nodeSet) trienodebuffer + commit(nodes *nodeSet, states *stateSet) trienodebuffer - // revert is the reverse operation of commit. It also merges the provided nodes - // into the trienodebuffer, the difference is that the provided node set should - // revert the changes made by the last state transition. - revert(db ethdb.KeyValueReader, nodes map[common.Hash]map[string]*trienode.Node) error + // revertTo is the reverse operation of commit. It also merges the provided states + // and trie nodes into the buffer. The key difference is that the provided state + // set should reverse the changes made by the most recent state transition. + revertTo(db ethdb.KeyValueReader, nodes map[common.Hash]map[string]*trienode.Node, accounts map[common.Hash][]byte, storages map[common.Hash]map[common.Hash][]byte) error // flush persists the in-memory dirty trie node into the disk if the configured // memory threshold is reached. Note, all data must be written atomically. @@ -57,26 +64,26 @@ type trienodebuffer interface { // empty returns an indicator if trienodebuffer contains any state transition inside. empty() bool - // getSize return the trienodebuffer used size. - getSize() (uint64, uint64) + // waitAndStopFlushing will block unit writing the trie nodes of trienodebuffer to disk. + waitAndStopFlushing() - // getAllNodes return the trie nodes set are cached in trienodebuffer. - getAllNodes() *nodeSet + // getAllNodesAndStates return the trie nodes and states cached in nodebuffer. + getAllNodesAndStates() (*nodeSet, *stateSet) // getLayers return the size of cached difflayers. getLayers() uint64 - // waitAndStopFlushing will block unit writing the trie nodes of trienodebuffer to disk. - waitAndStopFlushing() + // getSize return the trienodebuffer used size. + getSize() (uint64, uint64) } -func NewTrieNodeBuffer(sync bool, limit int, nodes *nodeSet, layers uint64) trienodebuffer { +func NewTrieNodeBuffer(sync bool, limit int, nodes *nodeSet, states *stateSet, layers uint64) trienodebuffer { if sync { log.Info("New sync node buffer", "limit", common.StorageSize(limit), "layers", layers) - return newBuffer(limit, nodes, layers) + return newBuffer(limit, nodes, states, layers) } log.Info("New async node buffer", "limit", common.StorageSize(limit), "layers", layers) - return newAsyncNodeBuffer(limit, nodes, layers) + return newAsyncNodeBuffer(limit, nodes, states, layers) } // diskLayer is a low level persistent layer built on top of a key-value store. @@ -85,7 +92,7 @@ type diskLayer struct { id uint64 // Immutable, corresponding state id db *Database // Path-based trie database nodes *fastcache.Cache // GC friendly memory cache of clean nodes - buffer trienodebuffer // Dirty buffer to aggregate writes of nodes + buffer trienodebuffer // Dirty buffer to aggregate writes of nodes and states stale bool // Signals that the layer became stale (state progressed) lock sync.RWMutex // Lock used to protect stale flag } @@ -192,6 +199,75 @@ func (dl *diskLayer) node(owner common.Hash, path []byte, hash common.Hash, dept return blob, h.hash(blob), &nodeLoc{loc: locDiskLayer, depth: depth}, nil } +// account directly retrieves the account RLP associated with a particular +// hash in the slim data format. +// +// Note the returned account is not a copy, please don't modify it. +func (dl *diskLayer) account(hash common.Hash, depth int) ([]byte, error) { + dl.lock.RLock() + defer dl.lock.RUnlock() + + if dl.stale { + return nil, errSnapshotStale + } + // Try to retrieve the account from the not-yet-written + // node buffer first. Note the buffer is lock free since + // it's impossible to mutate the buffer before tagging the + // layer as stale. + blob, found := dl.buffer.account(hash) + if found { + dirtyStateHitMeter.Mark(1) + dirtyStateReadMeter.Mark(int64(len(blob))) + dirtyStateHitDepthHist.Update(int64(depth)) + + if len(blob) == 0 { + stateAccountInexMeter.Mark(1) + } else { + stateAccountExistMeter.Mark(1) + } + return blob, nil + } + dirtyStateMissMeter.Mark(1) + + // TODO(rjl493456442) support persistent state retrieval + return nil, errors.New("not supported") +} + +// storage directly retrieves the storage data associated with a particular hash, +// within a particular account. +// +// Note the returned account is not a copy, please don't modify it. +func (dl *diskLayer) storage(accountHash, storageHash common.Hash, depth int) ([]byte, error) { + // Hold the lock, ensure the parent won't be changed during the + // state accessing. + dl.lock.RLock() + defer dl.lock.RUnlock() + + if dl.stale { + return nil, errSnapshotStale + } + // Try to retrieve the storage slot from the not-yet-written + // node buffer first. Note the buffer is lock free since + // it's impossible to mutate the buffer before tagging the + // layer as stale. + if blob, found := dl.buffer.storage(accountHash, storageHash); found { + dirtyStateHitMeter.Mark(1) + dirtyStateReadMeter.Mark(int64(len(blob))) + dirtyStateHitDepthHist.Update(int64(depth)) + + if len(blob) == 0 { + stateStorageInexMeter.Mark(1) + } else { + stateStorageExistMeter.Mark(1) + } + return blob, nil + } + dirtyStateMissMeter.Mark(1) + + // TODO(rjl493456442) support persistent state retrieval + return nil, errors.New("not supported") +} + // update implements the layer interface, returning a new diff layer on top // with the given state set. func (dl *diskLayer) update(root common.Hash, id uint64, block uint64, nodes *nodeSet, states *StateSetWithOrigin) *diffLayer { @@ -242,14 +318,14 @@ func (dl *diskLayer) commit(bottom *diffLayer, force bool) (*diskLayer, error) { // In a unique scenario where the ID of the oldest history object (after tail // truncation) surpasses the persisted state ID, we take the necessary action - // of forcibly committing the cached dirty nodes to ensure that the persisted + // of forcibly committing the cached dirty states to ensure that the persisted // state ID remains higher. if !force && rawdb.ReadPersistentStateID(dl.db.diskdb) < oldest { force = true } - // Merge the trie nodes of the bottom-most diff layer into the buffer as the - // combined layer. - combined := dl.buffer.commit(bottom.nodes) + // Merge the trie nodes and flat states of the bottom-most diff layer into the + // buffer as the combined layer. + combined := dl.buffer.commit(bottom.nodes, bottom.states.stateSet) if err := combined.flush(dl.db.diskdb, dl.db.freezer, dl.nodes, bottom.stateID(), force); err != nil { return nil, err } @@ -278,6 +354,24 @@ func (dl *diskLayer) revert(h *history) (*diskLayer, error) { if dl.id == 0 { return nil, fmt.Errorf("%w: zero state id", errStateUnrecoverable) } + var ( + buff = crypto.NewKeccakState() + hashes = make(map[common.Address]common.Hash) + accounts = make(map[common.Hash][]byte) + storages = make(map[common.Hash]map[common.Hash][]byte) + ) + for addr, blob := range h.accounts { + hash := crypto.HashData(buff, addr.Bytes()) + hashes[addr] = hash + accounts[hash] = blob + } + for addr, storage := range h.storages { + hash, ok := hashes[addr] + if !ok { + panic(fmt.Errorf("storage history with no account %x", addr)) + } + storages[hash] = storage + } // Apply the reverse state changes upon the current state. This must // be done before holding the lock in order to access state in "this" // layer. @@ -297,7 +391,7 @@ func (dl *diskLayer) revert(h *history) (*diskLayer, error) { // needs to be reverted is not yet flushed and cached in node // buffer, otherwise, manipulate persistent state directly. if !dl.buffer.empty() { - err := dl.buffer.revert(dl.db.diskdb, nodes) + err := dl.buffer.revertTo(dl.db.diskdb, nodes, accounts, storages) if err != nil { return nil, err } diff --git a/triedb/pathdb/journal.go b/triedb/pathdb/journal.go index 1a6901dd01..a98b5e8be5 100644 --- a/triedb/pathdb/journal.go +++ b/triedb/pathdb/journal.go @@ -49,7 +49,8 @@ var ( // // - Version 0: initial version // - Version 1: storage.Incomplete field is removed -const journalVersion uint64 = 1 +// - Version 2: add post-modification state values +const journalVersion uint64 = 2 type JournalWriter interface { io.Writer @@ -238,7 +239,7 @@ func (db *Database) loadLayers() layer { log.Info("Failed to load journal, discard it", "err", err) } // Return single layer with persistent state. - return newDiskLayer(root, rawdb.ReadPersistentStateID(db.diskdb), db, nil, NewTrieNodeBuffer(db.config.SyncFlush, db.config.WriteBufferSize, nil, 0)) + return newDiskLayer(root, rawdb.ReadPersistentStateID(db.diskdb), db, nil, NewTrieNodeBuffer(db.config.SyncFlush, db.config.WriteBufferSize, nil, nil, 0)) } // loadDiskLayer reads the binary blob from the layer journal, reconstructing @@ -278,6 +279,11 @@ func (db *Database) loadDiskLayer(r *rlp.Stream, journalTypeForReader JournalTyp if err := nodes.decode(journalBuf); err != nil { return nil, err } + // Resolve flat state sets in aggregated buffer + var states stateSet + if err := states.decode(journalBuf); err != nil { + return nil, err + } if journalTypeForReader == JournalFileType { var shaSum [32]byte @@ -292,7 +298,7 @@ func (db *Database) loadDiskLayer(r *rlp.Stream, journalTypeForReader JournalTyp } // Calculate the internal state transitions by id difference. - base := newDiskLayer(root, id, db, nil, NewTrieNodeBuffer(db.config.SyncFlush, db.config.WriteBufferSize, &nodes, id-stored)) + base := newDiskLayer(root, id, db, nil, NewTrieNodeBuffer(db.config.SyncFlush, db.config.WriteBufferSize, &nodes, &states, id-stored)) return base, nil } @@ -378,8 +384,13 @@ func (dl *diskLayer) journal(w io.Writer, journalType JournalType) error { if err := rlp.Encode(journalBuf, dl.id); err != nil { return err } + nodes, states := dl.buffer.getAllNodesAndStates() // Step three, write the accumulated trie nodes into the journal - if err := dl.buffer.getAllNodes().encode(journalBuf); err != nil { + if err := nodes.encode(journalBuf); err != nil { + return err + } + // Step four, write the accumulated flat states into the journal + if err := states.encode(journalBuf); err != nil { return err } diff --git a/triedb/pathdb/metrics.go b/triedb/pathdb/metrics.go index 5c4dd3cf59..a53f7388a3 100644 --- a/triedb/pathdb/metrics.go +++ b/triedb/pathdb/metrics.go @@ -30,10 +30,21 @@ var ( dirtyNodeWriteMeter = metrics.NewRegisteredMeter("pathdb/dirty/node/write", nil) dirtyNodeHitDepthHist = metrics.NewRegisteredHistogram("pathdb/dirty/node/depth", nil, metrics.NewExpDecaySample(1028, 0.015)) - cleanFalseMeter = metrics.NewRegisteredMeter("pathdb/clean/false", nil) - dirtyFalseMeter = metrics.NewRegisteredMeter("pathdb/dirty/false", nil) - diskFalseMeter = metrics.NewRegisteredMeter("pathdb/disk/false", nil) - diffFalseMeter = metrics.NewRegisteredMeter("pathdb/diff/false", nil) + stateAccountInexMeter = metrics.NewRegisteredMeter("pathdb/state/account/inex/total", nil) + stateStorageInexMeter = metrics.NewRegisteredMeter("pathdb/state/storage/inex/total", nil) + stateAccountExistMeter = metrics.NewRegisteredMeter("pathdb/state/account/exist/total", nil) + stateStorageExistMeter = metrics.NewRegisteredMeter("pathdb/state/storage/exist/total", nil) + + dirtyStateHitMeter = metrics.NewRegisteredMeter("pathdb/dirty/state/hit", nil) + dirtyStateMissMeter = metrics.NewRegisteredMeter("pathdb/dirty/state/miss", nil) + dirtyStateReadMeter = metrics.NewRegisteredMeter("pathdb/dirty/state/read", nil) + dirtyStateWriteMeter = metrics.NewRegisteredMeter("pathdb/dirty/state/write", nil) + dirtyStateHitDepthHist = metrics.NewRegisteredHistogram("pathdb/dirty/state/depth", nil, metrics.NewExpDecaySample(1028, 0.015)) + + nodeCleanFalseMeter = metrics.NewRegisteredMeter("pathdb/clean/false", nil) + nodeDirtyFalseMeter = metrics.NewRegisteredMeter("pathdb/dirty/false", nil) + nodeDiskFalseMeter = metrics.NewRegisteredMeter("pathdb/disk/false", nil) + nodeDiffFalseMeter = metrics.NewRegisteredMeter("pathdb/diff/false", nil) commitTimeTimer = metrics.NewRegisteredTimer("pathdb/commit/time", nil) commitNodesMeter = metrics.NewRegisteredMeter("pathdb/commit/nodes", nil) @@ -41,6 +52,10 @@ var ( gcTrieNodeMeter = metrics.NewRegisteredMeter("pathdb/gc/node/count", nil) gcTrieNodeBytesMeter = metrics.NewRegisteredMeter("pathdb/gc/node/bytes", nil) + gcAccountMeter = metrics.NewRegisteredMeter("pathdb/gc/account/count", nil) + gcAccountBytesMeter = metrics.NewRegisteredMeter("pathdb/gc/account/bytes", nil) + gcStorageMeter = metrics.NewRegisteredMeter("pathdb/gc/storage/count", nil) + gcStorageBytesMeter = metrics.NewRegisteredMeter("pathdb/gc/storage/bytes", nil) historyBuildTimeMeter = metrics.NewRegisteredTimer("pathdb/history/time", nil) historyDataBytesMeter = metrics.NewRegisteredMeter("pathdb/history/bytes/data", nil) diff --git a/triedb/pathdb/nodes.go b/triedb/pathdb/nodes.go index ade669512e..dee8c872ac 100644 --- a/triedb/pathdb/nodes.go +++ b/triedb/pathdb/nodes.go @@ -131,9 +131,9 @@ func (s *nodeSet) merge(set *nodeSet) { s.updateSize(delta) } -// revert merges the provided trie nodes into the set. This should reverse the +// revertTo merges the provided trie nodes into the set. This should reverse the // changes made by the most recent state transition. -func (s *nodeSet) revert(db ethdb.KeyValueReader, nodes map[common.Hash]map[string]*trienode.Node) { +func (s *nodeSet) revertTo(db ethdb.KeyValueReader, nodes map[common.Hash]map[string]*trienode.Node) { var delta int64 for owner, subset := range nodes { current, ok := s.nodes[owner] diff --git a/triedb/pathdb/reader.go b/triedb/pathdb/reader.go index 6b778b9e24..d6f467c121 100644 --- a/triedb/pathdb/reader.go +++ b/triedb/pathdb/reader.go @@ -21,7 +21,9 @@ import ( "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common/hexutil" + "github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/log" + "github.com/ethereum/go-ethereum/rlp" "github.com/ethereum/go-ethereum/triedb/database" ) @@ -66,13 +68,13 @@ func (r *reader) Node(owner common.Hash, path []byte, hash common.Hash) ([]byte, // is not found. switch loc.loc { case locCleanCache: - cleanFalseMeter.Mark(1) + nodeCleanFalseMeter.Mark(1) case locDirtyCache: - dirtyFalseMeter.Mark(1) + nodeDirtyFalseMeter.Mark(1) case locDiffLayer: - diffFalseMeter.Mark(1) + nodeDiffFalseMeter.Mark(1) case locDiskLayer: - diskFalseMeter.Mark(1) + nodeDiskFalseMeter.Mark(1) } blobHex := "nil" if len(blob) > 0 { @@ -84,6 +86,39 @@ func (r *reader) Node(owner common.Hash, path []byte, hash common.Hash) ([]byte, return blob, nil } +// Account directly retrieves the account associated with a particular hash in +// the slim data format. An error will be returned if the read operation exits +// abnormally. Specifically, if the layer is already stale. +// +// Note: +// - the returned account object is safe to modify +// - no error will be returned if the requested account is not found in database +func (r *reader) Account(hash common.Hash) (*types.SlimAccount, error) { + blob, err := r.layer.account(hash, 0) + if err != nil { + return nil, err + } + if len(blob) == 0 { + return nil, nil + } + account := new(types.SlimAccount) + if err := rlp.DecodeBytes(blob, account); err != nil { + panic(err) + } + return account, nil +} + +// Storage directly retrieves the storage data associated with a particular hash, +// within a particular account. An error will be returned if the read operation +// exits abnormally. Specifically, if the layer is already stale. +// +// Note: +// - the returned storage data is not a copy, please don't modify it +// - no error will be returned if the requested slot is not found in database +func (r *reader) Storage(accountHash, storageHash common.Hash) ([]byte, error) { + return r.layer.storage(accountHash, storageHash, 0) +} + // NodeReader retrieves a layer belonging to the given state root. func (db *Database) NodeReader(root common.Hash) (database.NodeReader, error) { layer := db.tree.get(root) @@ -92,3 +127,13 @@ func (db *Database) NodeReader(root common.Hash) (database.NodeReader, error) { } return &reader{layer: layer, noHashCheck: db.isVerkle}, nil } + +// StateReader returns a reader that allows access to the state data associated +// with the specified state. +func (db *Database) StateReader(root common.Hash) (database.StateReader, error) { + layer := db.tree.get(root) + if layer == nil { + return nil, fmt.Errorf("state %#x is not available", root) + } + return &reader{layer: layer}, nil +} diff --git a/triedb/pathdb/states.go b/triedb/pathdb/states.go index da8befab95..6e633db272 100644 --- a/triedb/pathdb/states.go +++ b/triedb/pathdb/states.go @@ -19,10 +19,15 @@ package pathdb import ( "fmt" "io" + "slices" + "sync" "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/core/rawdb" + "github.com/ethereum/go-ethereum/log" "github.com/ethereum/go-ethereum/metrics" "github.com/ethereum/go-ethereum/rlp" + "golang.org/x/exp/maps" ) // counter helps in tracking items and their corresponding sizes. @@ -38,14 +43,378 @@ func (c *counter) add(size int) { } // report uploads the cached statistics to meters. -func (c *counter) report(count metrics.Meter, size metrics.Meter) { +func (c *counter) report(count, size *metrics.Meter) { count.Mark(int64(c.n)) size.Mark(int64(c.size)) } +// stateSet represents a collection of state modifications associated with a +// transition (e.g., a block execution) or multiple aggregated transitions. +// +// A stateSet can only reside within a diffLayer or the buffer of a diskLayer, +// serving as the envelope for the set. Lock protection is not required for +// accessing or mutating the account set and storage set, as the associated +// envelope is always marked as stale before any mutation is applied. Any +// subsequent state access will be denied due to the stale flag. Therefore, +// state access and mutation won't happen at the same time with guarantee. +type stateSet struct { + accountData map[common.Hash][]byte // Keyed accounts for direct retrieval (nil means deleted) + storageData map[common.Hash]map[common.Hash][]byte // Keyed storage slots for direct retrieval. one per account (nil means deleted) + size uint64 // Memory size of the state data (accountData and storageData) + + accountListSorted []common.Hash // List of account for iteration. If it exists, it's sorted, otherwise it's nil + storageListSorted map[common.Hash][]common.Hash // List of storage slots for iterated retrievals, one per account. Any existing lists are sorted if non-nil + + // Lock for guarding the two lists above. These lists might be accessed + // concurrently and lock protection is essential to avoid concurrent + // slice or map read/write. + listLock sync.RWMutex +} + +// newStates constructs the state set with the provided account and storage data. +func newStates(accounts map[common.Hash][]byte, storages map[common.Hash]map[common.Hash][]byte) *stateSet { + // Don't panic for the lazy callers, initialize the nil maps instead. + if accounts == nil { + accounts = make(map[common.Hash][]byte) + } + if storages == nil { + storages = make(map[common.Hash]map[common.Hash][]byte) + } + s := &stateSet{ + accountData: accounts, + storageData: storages, + storageListSorted: make(map[common.Hash][]common.Hash), + } + s.size = s.check() + return s +} + +// account returns the account data associated with the specified address hash. +func (s *stateSet) account(hash common.Hash) ([]byte, bool) { + // If the account is known locally, return it + if data, ok := s.accountData[hash]; ok { + return data, true + } + return nil, false // account is unknown in this set +} + +// storage returns the storage slot associated with the specified address hash +// and storage key hash. +func (s *stateSet) storage(accountHash, storageHash common.Hash) ([]byte, bool) { + // If the account is known locally, try to resolve the slot locally + if storage, ok := s.storageData[accountHash]; ok { + if data, ok := storage[storageHash]; ok { + return data, true + } + } + return nil, false // storage is unknown in this set +} + +// check sanitizes accounts and storage slots to ensure the data validity. +// Additionally, it computes the total memory size occupied by the maps. +func (s *stateSet) check() uint64 { + var size int + for _, blob := range s.accountData { + size += common.HashLength + len(blob) + } + for accountHash, slots := range s.storageData { + if slots == nil { + panic(fmt.Sprintf("storage %#x nil", accountHash)) // nil slots is not permitted + } + for _, blob := range slots { + size += 2*common.HashLength + len(blob) + } + } + return uint64(size) +} + +// accountList returns a sorted list of all accounts in this state set, including +// the deleted ones. +// +// Note, the returned slice is not a copy, so do not modify it. +// +// nolint:unused +func (s *stateSet) accountList() []common.Hash { + // If an old list already exists, return it + s.listLock.RLock() + list := s.accountListSorted + s.listLock.RUnlock() + + if list != nil { + return list + } + // No old sorted account list exists, generate a new one. It's possible that + // multiple threads waiting for the write lock may regenerate the list + // multiple times, which is acceptable. + s.listLock.Lock() + defer s.listLock.Unlock() + + list = maps.Keys(s.accountData) + slices.SortFunc(list, common.Hash.Cmp) + s.accountListSorted = list + return list +} + +// StorageList returns a sorted list of all storage slot hashes in this state set +// for the given account. The returned list will include the hash of deleted +// storage slot. +// +// Note, the returned slice is not a copy, so do not modify it. +// +// nolint:unused +func (s *stateSet) storageList(accountHash common.Hash) []common.Hash { + s.listLock.RLock() + if _, ok := s.storageData[accountHash]; !ok { + // Account not tracked by this layer + s.listLock.RUnlock() + return nil + } + // If an old list already exists, return it + if list, exist := s.storageListSorted[accountHash]; exist { + s.listLock.RUnlock() + return list // the cached list can't be nil + } + s.listLock.RUnlock() + + // No old sorted account list exists, generate a new one. It's possible that + // multiple threads waiting for the write lock may regenerate the list + // multiple times, which is acceptable. + s.listLock.Lock() + defer s.listLock.Unlock() + + list := maps.Keys(s.storageData[accountHash]) + slices.SortFunc(list, common.Hash.Cmp) + s.storageListSorted[accountHash] = list + return list +} + +// clearLists invalidates the cached account list and storage lists. +func (s *stateSet) clearLists() { + s.listLock.Lock() + defer s.listLock.Unlock() + + s.accountListSorted = nil + s.storageListSorted = make(map[common.Hash][]common.Hash) +} + +// merge integrates the accounts and storages from the external set into the +// local set, ensuring the combined set reflects the combined state of both. +// +// The stateSet supplied as parameter set will not be mutated by this operation, +// as it may still be referenced by other layers. +func (s *stateSet) merge(other *stateSet) { + var ( + delta int + accountOverwrites counter + storageOverwrites counter + ) + // Apply the updated account data + for accountHash, data := range other.accountData { + if origin, ok := s.accountData[accountHash]; ok { + delta += len(data) - len(origin) + accountOverwrites.add(common.HashLength + len(origin)) + } else { + delta += common.HashLength + len(data) + } + s.accountData[accountHash] = data + } + // Apply all the updated storage slots (individually) + for accountHash, storage := range other.storageData { + // If storage didn't exist in the set, overwrite blindly + if _, ok := s.storageData[accountHash]; !ok { + // To prevent potential concurrent map read/write issues, allocate a + // new map for the storage instead of claiming it directly from the + // passed external set. Even after merging, the slots belonging to the + // external state set remain accessible, so ownership of the map should + // not be taken, and any mutation on it should be avoided. + slots := make(map[common.Hash][]byte, len(storage)) + for storageHash, data := range storage { + slots[storageHash] = data + delta += 2*common.HashLength + len(data) + } + s.storageData[accountHash] = slots + continue + } + // Storage exists in both local and external set, merge the slots + slots := s.storageData[accountHash] + for storageHash, data := range storage { + if origin, ok := slots[storageHash]; ok { + delta += len(data) - len(origin) + storageOverwrites.add(2*common.HashLength + len(origin)) + } else { + delta += 2*common.HashLength + len(data) + } + slots[storageHash] = data + } + } + accountOverwrites.report(gcAccountMeter, gcAccountBytesMeter) + storageOverwrites.report(gcStorageMeter, gcStorageBytesMeter) + s.clearLists() + s.updateSize(delta) +} + +// revertTo takes the original value of accounts and storages as input and reverts +// the latest state transition applied on the state set. +// +// Notably, this operation may result in the set containing more entries after a +// revert. For example, if account x did not exist and was created during transition +// w, reverting w will retain an x=nil entry in the set. And also if account x along +// with its storage slots was deleted in the transition w, reverting w will retain +// a list of additional storage slots with their original value. +func (s *stateSet) revertTo(accountOrigin map[common.Hash][]byte, storageOrigin map[common.Hash]map[common.Hash][]byte) { + var delta int // size tracking + for addrHash, blob := range accountOrigin { + data, ok := s.accountData[addrHash] + if !ok { + panic(fmt.Sprintf("non-existent account for reverting, %x", addrHash)) + } + if len(data) == 0 && len(blob) == 0 { + panic(fmt.Sprintf("invalid account mutation (null to null), %x", addrHash)) + } + delta += len(blob) - len(data) + s.accountData[addrHash] = blob + } + // Overwrite the storage data with original value blindly + for addrHash, storage := range storageOrigin { + slots := s.storageData[addrHash] + if len(slots) == 0 { + panic(fmt.Sprintf("non-existent storage set for reverting, %x", addrHash)) + } + for storageHash, blob := range storage { + data, ok := slots[storageHash] + if !ok { + panic(fmt.Sprintf("non-existent storage slot for reverting, %x-%x", addrHash, storageHash)) + } + if len(blob) == 0 && len(data) == 0 { + panic(fmt.Sprintf("invalid storage slot mutation (null to null), %x-%x", addrHash, storageHash)) + } + delta += len(blob) - len(data) + slots[storageHash] = blob + } + } + s.clearLists() + s.updateSize(delta) +} + +// updateSize updates the total cache size by the given delta. +func (s *stateSet) updateSize(delta int) { + size := int64(s.size) + int64(delta) + if size >= 0 { + s.size = uint64(size) + return + } + log.Error("Stateset size underflow", "prev", common.StorageSize(s.size), "delta", common.StorageSize(delta)) + s.size = 0 +} + +// encode serializes the content of state set into the provided writer. +func (s *stateSet) encode(w io.Writer) error { + // Encode accounts + type accounts struct { + AddrHashes []common.Hash + Accounts [][]byte + } + var enc accounts + for addrHash, blob := range s.accountData { + enc.AddrHashes = append(enc.AddrHashes, addrHash) + enc.Accounts = append(enc.Accounts, blob) + } + if err := rlp.Encode(w, enc); err != nil { + return err + } + // Encode storages + type Storage struct { + AddrHash common.Hash + Keys []common.Hash + Vals [][]byte + } + storages := make([]Storage, 0, len(s.storageData)) + for addrHash, slots := range s.storageData { + keys := make([]common.Hash, 0, len(slots)) + vals := make([][]byte, 0, len(slots)) + for key, val := range slots { + keys = append(keys, key) + vals = append(vals, val) + } + storages = append(storages, Storage{ + AddrHash: addrHash, + Keys: keys, + Vals: vals, + }) + } + return rlp.Encode(w, storages) +} + +// decode deserializes the content from the rlp stream into the state set. +func (s *stateSet) decode(r *rlp.Stream) error { + type accounts struct { + AddrHashes []common.Hash + Accounts [][]byte + } + var ( + dec accounts + accountSet = make(map[common.Hash][]byte) + ) + if err := r.Decode(&dec); err != nil { + return fmt.Errorf("load diff accounts: %v", err) + } + for i := 0; i < len(dec.AddrHashes); i++ { + accountSet[dec.AddrHashes[i]] = dec.Accounts[i] + } + s.accountData = accountSet + + // Decode storages + type storage struct { + AddrHash common.Hash + Keys []common.Hash + Vals [][]byte + } + var ( + storages []storage + storageSet = make(map[common.Hash]map[common.Hash][]byte) + ) + if err := r.Decode(&storages); err != nil { + return fmt.Errorf("load diff storage: %v", err) + } + for _, entry := range storages { + storageSet[entry.AddrHash] = make(map[common.Hash][]byte, len(entry.Keys)) + for i := 0; i < len(entry.Keys); i++ { + storageSet[entry.AddrHash][entry.Keys[i]] = entry.Vals[i] + } + } + s.storageData = storageSet + s.storageListSorted = make(map[common.Hash][]common.Hash) + + s.size = s.check() + return nil +} + +// reset clears all cached state data, including any optional sorted lists that +// may have been generated. +func (s *stateSet) reset() { + s.accountData = make(map[common.Hash][]byte) + s.storageData = make(map[common.Hash]map[common.Hash][]byte) + s.size = 0 + s.accountListSorted = nil + s.storageListSorted = make(map[common.Hash][]common.Hash) +} + +// dbsize returns the approximate size for db write. +// +// nolint:unused +func (s *stateSet) dbsize() int { + m := len(s.accountData) * len(rawdb.SnapshotAccountPrefix) + for _, slots := range s.storageData { + m += len(slots) * len(rawdb.SnapshotStoragePrefix) + } + return m + int(s.size) +} + // StateSetWithOrigin wraps the state set with additional original values of the // mutated states. type StateSetWithOrigin struct { + *stateSet + // AccountOrigin represents the account data before the state transition, // corresponding to both the accountData and destructSet. It's keyed by the // account address. The nil value means the account was not present before. @@ -62,7 +431,7 @@ type StateSetWithOrigin struct { } // NewStateSetWithOrigin constructs the state set with the provided data. -func NewStateSetWithOrigin(accountOrigin map[common.Address][]byte, storageOrigin map[common.Address]map[common.Hash][]byte) *StateSetWithOrigin { +func NewStateSetWithOrigin(accounts map[common.Hash][]byte, storages map[common.Hash]map[common.Hash][]byte, accountOrigin map[common.Address][]byte, storageOrigin map[common.Address]map[common.Hash][]byte) *StateSetWithOrigin { // Don't panic for the lazy callers, initialize the nil maps instead. if accountOrigin == nil { accountOrigin = make(map[common.Address][]byte) @@ -82,15 +451,21 @@ func NewStateSetWithOrigin(accountOrigin map[common.Address][]byte, storageOrigi size += 2*common.HashLength + len(data) } } + set := newStates(accounts, storages) return &StateSetWithOrigin{ + stateSet: set, accountOrigin: accountOrigin, storageOrigin: storageOrigin, - size: uint64(size), + size: set.size + uint64(size), } } // encode serializes the content of state set into the provided writer. func (s *StateSetWithOrigin) encode(w io.Writer) error { + // Encode state set + if err := s.stateSet.encode(w); err != nil { + return err + } // Encode accounts type Accounts struct { Addresses []common.Address @@ -108,7 +483,7 @@ func (s *StateSetWithOrigin) encode(w io.Writer) error { type Storage struct { Address common.Address Keys []common.Hash - Blobs [][]byte + Vals [][]byte } storages := make([]Storage, 0, len(s.storageOrigin)) for address, slots := range s.storageOrigin { @@ -118,13 +493,19 @@ func (s *StateSetWithOrigin) encode(w io.Writer) error { keys = append(keys, key) vals = append(vals, val) } - storages = append(storages, Storage{Address: address, Keys: keys, Blobs: vals}) + storages = append(storages, Storage{Address: address, Keys: keys, Vals: vals}) } return rlp.Encode(w, storages) } // decode deserializes the content from the rlp stream into the state set. func (s *StateSetWithOrigin) decode(r *rlp.Stream) error { + if s.stateSet == nil { + s.stateSet = &stateSet{} + } + if err := s.stateSet.decode(r); err != nil { + return err + } // Decode account origin type Accounts struct { Addresses []common.Address @@ -146,7 +527,7 @@ func (s *StateSetWithOrigin) decode(r *rlp.Stream) error { type Storage struct { Address common.Address Keys []common.Hash - Blobs [][]byte + Vals [][]byte } var ( storages []Storage @@ -158,7 +539,7 @@ func (s *StateSetWithOrigin) decode(r *rlp.Stream) error { for _, storage := range storages { storageSet[storage.Address] = make(map[common.Hash][]byte) for i := 0; i < len(storage.Keys); i++ { - storageSet[storage.Address][storage.Keys[i]] = storage.Blobs[i] + storageSet[storage.Address][storage.Keys[i]] = storage.Vals[i] } } s.storageOrigin = storageSet diff --git a/triedb/pathdb/states_test.go b/triedb/pathdb/states_test.go new file mode 100644 index 0000000000..4557fa958d --- /dev/null +++ b/triedb/pathdb/states_test.go @@ -0,0 +1,453 @@ +// Copyright 2024 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see + +package pathdb + +import ( + "bytes" + "reflect" + "testing" + + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/rlp" +) + +func TestStatesMerge(t *testing.T) { + a := newStates( + map[common.Hash][]byte{ + common.Hash{0xa}: {0xa0}, + common.Hash{0xb}: {0xb0}, + common.Hash{0xc}: {0xc0}, + }, + map[common.Hash]map[common.Hash][]byte{ + common.Hash{0xa}: { + common.Hash{0x1}: {0x10}, + common.Hash{0x2}: {0x20}, + }, + common.Hash{0xb}: { + common.Hash{0x1}: {0x10}, + }, + common.Hash{0xc}: { + common.Hash{0x1}: {0x10}, + }, + }, + ) + b := newStates( + map[common.Hash][]byte{ + common.Hash{0xa}: {0xa1}, + common.Hash{0xb}: {0xb1}, + common.Hash{0xc}: nil, // delete account + }, + map[common.Hash]map[common.Hash][]byte{ + common.Hash{0xa}: { + common.Hash{0x1}: {0x11}, + common.Hash{0x2}: nil, // delete slot + common.Hash{0x3}: {0x31}, + }, + common.Hash{0xb}: { + common.Hash{0x1}: {0x11}, + }, + common.Hash{0xc}: { + common.Hash{0x1}: nil, // delete slot + }, + }, + ) + a.merge(b) + + blob, exist := a.account(common.Hash{0xa}) + if !exist || !bytes.Equal(blob, []byte{0xa1}) { + t.Error("Unexpected value for account a") + } + blob, exist = a.account(common.Hash{0xb}) + if !exist || !bytes.Equal(blob, []byte{0xb1}) { + t.Error("Unexpected value for account b") + } + blob, exist = a.account(common.Hash{0xc}) + if !exist || len(blob) != 0 { + t.Error("Unexpected value for account c") + } + // unknown account + blob, exist = a.account(common.Hash{0xd}) + if exist || len(blob) != 0 { + t.Error("Unexpected value for account d") + } + + blob, exist = a.storage(common.Hash{0xa}, common.Hash{0x1}) + if !exist || !bytes.Equal(blob, []byte{0x11}) { + t.Error("Unexpected value for a's storage") + } + blob, exist = a.storage(common.Hash{0xa}, common.Hash{0x2}) + if !exist || len(blob) != 0 { + t.Error("Unexpected value for a's storage") + } + blob, exist = a.storage(common.Hash{0xa}, common.Hash{0x3}) + if !exist || !bytes.Equal(blob, []byte{0x31}) { + t.Error("Unexpected value for a's storage") + } + blob, exist = a.storage(common.Hash{0xb}, common.Hash{0x1}) + if !exist || !bytes.Equal(blob, []byte{0x11}) { + t.Error("Unexpected value for b's storage") + } + blob, exist = a.storage(common.Hash{0xc}, common.Hash{0x1}) + if !exist || len(blob) != 0 { + t.Error("Unexpected value for c's storage") + } + + // unknown storage slots + blob, exist = a.storage(common.Hash{0xd}, common.Hash{0x1}) + if exist || len(blob) != 0 { + t.Error("Unexpected value for d's storage") + } +} + +func TestStatesRevert(t *testing.T) { + a := newStates( + map[common.Hash][]byte{ + common.Hash{0xa}: {0xa0}, + common.Hash{0xb}: {0xb0}, + common.Hash{0xc}: {0xc0}, + }, + map[common.Hash]map[common.Hash][]byte{ + common.Hash{0xa}: { + common.Hash{0x1}: {0x10}, + common.Hash{0x2}: {0x20}, + }, + common.Hash{0xb}: { + common.Hash{0x1}: {0x10}, + }, + common.Hash{0xc}: { + common.Hash{0x1}: {0x10}, + }, + }, + ) + b := newStates( + map[common.Hash][]byte{ + common.Hash{0xa}: {0xa1}, + common.Hash{0xb}: {0xb1}, + common.Hash{0xc}: nil, + }, + map[common.Hash]map[common.Hash][]byte{ + common.Hash{0xa}: { + common.Hash{0x1}: {0x11}, + common.Hash{0x2}: nil, + common.Hash{0x3}: {0x31}, + }, + common.Hash{0xb}: { + common.Hash{0x1}: {0x11}, + }, + common.Hash{0xc}: { + common.Hash{0x1}: nil, + }, + }, + ) + a.merge(b) + a.revertTo( + map[common.Hash][]byte{ + common.Hash{0xa}: {0xa0}, + common.Hash{0xb}: {0xb0}, + common.Hash{0xc}: {0xc0}, + }, + map[common.Hash]map[common.Hash][]byte{ + common.Hash{0xa}: { + common.Hash{0x1}: {0x10}, + common.Hash{0x2}: {0x20}, + common.Hash{0x3}: nil, + }, + common.Hash{0xb}: { + common.Hash{0x1}: {0x10}, + }, + common.Hash{0xc}: { + common.Hash{0x1}: {0x10}, + }, + }, + ) + + blob, exist := a.account(common.Hash{0xa}) + if !exist || !bytes.Equal(blob, []byte{0xa0}) { + t.Error("Unexpected value for account a") + } + blob, exist = a.account(common.Hash{0xb}) + if !exist || !bytes.Equal(blob, []byte{0xb0}) { + t.Error("Unexpected value for account b") + } + blob, exist = a.account(common.Hash{0xc}) + if !exist || !bytes.Equal(blob, []byte{0xc0}) { + t.Error("Unexpected value for account c") + } + // unknown account + blob, exist = a.account(common.Hash{0xd}) + if exist || len(blob) != 0 { + t.Error("Unexpected value for account d") + } + + blob, exist = a.storage(common.Hash{0xa}, common.Hash{0x1}) + if !exist || !bytes.Equal(blob, []byte{0x10}) { + t.Error("Unexpected value for a's storage") + } + blob, exist = a.storage(common.Hash{0xa}, common.Hash{0x2}) + if !exist || !bytes.Equal(blob, []byte{0x20}) { + t.Error("Unexpected value for a's storage") + } + blob, exist = a.storage(common.Hash{0xa}, common.Hash{0x3}) + if !exist || len(blob) != 0 { + t.Error("Unexpected value for a's storage") + } + blob, exist = a.storage(common.Hash{0xb}, common.Hash{0x1}) + if !exist || !bytes.Equal(blob, []byte{0x10}) { + t.Error("Unexpected value for b's storage") + } + blob, exist = a.storage(common.Hash{0xc}, common.Hash{0x1}) + if !exist || !bytes.Equal(blob, []byte{0x10}) { + t.Error("Unexpected value for c's storage") + } + // unknown storage slots + blob, exist = a.storage(common.Hash{0xd}, common.Hash{0x1}) + if exist || len(blob) != 0 { + t.Error("Unexpected value for d's storage") + } +} + +// TestStateRevertAccountNullMarker tests the scenario that account x did not exist +// before and was created during transition w, reverting w will retain an x=nil +// entry in the set. +func TestStateRevertAccountNullMarker(t *testing.T) { + a := newStates(nil, nil) // empty initial state + b := newStates( + map[common.Hash][]byte{ + common.Hash{0xa}: {0xa}, + }, + nil, + ) + a.merge(b) // create account 0xa + a.revertTo( + map[common.Hash][]byte{ + common.Hash{0xa}: nil, + }, + nil, + ) // revert the transition b + + blob, exist := a.account(common.Hash{0xa}) + if !exist { + t.Fatal("null marker is not found") + } + if len(blob) != 0 { + t.Fatalf("Unexpected value for account, %v", blob) + } +} + +// TestStateRevertStorageNullMarker tests the scenario that slot x did not exist +// before and was created during transition w, reverting w will retain an x=nil +// entry in the set. +func TestStateRevertStorageNullMarker(t *testing.T) { + a := newStates(map[common.Hash][]byte{ + common.Hash{0xa}: {0xa}, + }, nil) // initial state with account 0xa + + b := newStates( + nil, + map[common.Hash]map[common.Hash][]byte{ + common.Hash{0xa}: { + common.Hash{0x1}: {0x1}, + }, + }, + ) + a.merge(b) // create slot 0x1 + a.revertTo( + nil, + map[common.Hash]map[common.Hash][]byte{ + common.Hash{0xa}: { + common.Hash{0x1}: nil, + }, + }, + ) // revert the transition b + + blob, exist := a.storage(common.Hash{0xa}, common.Hash{0x1}) + if !exist { + t.Fatal("null marker is not found") + } + if len(blob) != 0 { + t.Fatalf("Unexpected value for storage slot, %v", blob) + } +} + +func TestStatesEncode(t *testing.T) { + s := newStates( + map[common.Hash][]byte{ + common.Hash{0x1}: {0x1}, + }, + map[common.Hash]map[common.Hash][]byte{ + common.Hash{0x1}: { + common.Hash{0x1}: {0x1}, + }, + }, + ) + buf := bytes.NewBuffer(nil) + if err := s.encode(buf); err != nil { + t.Fatalf("Failed to encode states, %v", err) + } + var dec stateSet + if err := dec.decode(rlp.NewStream(buf, 0)); err != nil { + t.Fatalf("Failed to decode states, %v", err) + } + if !reflect.DeepEqual(s.accountData, dec.accountData) { + t.Fatal("Unexpected account data") + } + if !reflect.DeepEqual(s.storageData, dec.storageData) { + t.Fatal("Unexpected storage data") + } +} + +func TestStateWithOriginEncode(t *testing.T) { + s := NewStateSetWithOrigin( + map[common.Hash][]byte{ + common.Hash{0x1}: {0x1}, + }, + map[common.Hash]map[common.Hash][]byte{ + common.Hash{0x1}: { + common.Hash{0x1}: {0x1}, + }, + }, + map[common.Address][]byte{ + common.Address{0x1}: {0x1}, + }, + map[common.Address]map[common.Hash][]byte{ + common.Address{0x1}: { + common.Hash{0x1}: {0x1}, + }, + }, + ) + buf := bytes.NewBuffer(nil) + if err := s.encode(buf); err != nil { + t.Fatalf("Failed to encode states, %v", err) + } + var dec StateSetWithOrigin + if err := dec.decode(rlp.NewStream(buf, 0)); err != nil { + t.Fatalf("Failed to decode states, %v", err) + } + if !reflect.DeepEqual(s.accountData, dec.accountData) { + t.Fatal("Unexpected account data") + } + if !reflect.DeepEqual(s.storageData, dec.storageData) { + t.Fatal("Unexpected storage data") + } + if !reflect.DeepEqual(s.accountOrigin, dec.accountOrigin) { + t.Fatal("Unexpected account origin data") + } + if !reflect.DeepEqual(s.storageOrigin, dec.storageOrigin) { + t.Fatal("Unexpected storage origin data") + } +} + +func TestStateSizeTracking(t *testing.T) { + expSizeA := 3*(common.HashLength+1) + /* account data */ + 2*(2*common.HashLength+1) + /* storage data of 0xa */ + 2*common.HashLength + 3 + /* storage data of 0xb */ + 2*common.HashLength + 1 /* storage data of 0xc */ + + a := newStates( + map[common.Hash][]byte{ + common.Hash{0xa}: {0xa0}, // common.HashLength+1 + common.Hash{0xb}: {0xb0}, // common.HashLength+1 + common.Hash{0xc}: {0xc0}, // common.HashLength+1 + }, + map[common.Hash]map[common.Hash][]byte{ + common.Hash{0xa}: { + common.Hash{0x1}: {0x10}, // 2*common.HashLength+1 + common.Hash{0x2}: {0x20}, // 2*common.HashLength+1 + }, + common.Hash{0xb}: { + common.Hash{0x1}: {0x10, 0x11, 0x12}, // 2*common.HashLength+3 + }, + common.Hash{0xc}: { + common.Hash{0x1}: {0x10}, // 2*common.HashLength+1 + }, + }, + ) + if a.size != uint64(expSizeA) { + t.Fatalf("Unexpected size, want: %d, got: %d", expSizeA, a.size) + } + + expSizeB := common.HashLength + 2 + common.HashLength + 3 + common.HashLength + /* account data */ + 2*common.HashLength + 3 + 2*common.HashLength + 2 + /* storage data of 0xa */ + 2*common.HashLength + 2 + 2*common.HashLength + 2 + /* storage data of 0xb */ + 3*2*common.HashLength /* storage data of 0xc */ + b := newStates( + map[common.Hash][]byte{ + common.Hash{0xa}: {0xa1, 0xa1}, // common.HashLength+2 + common.Hash{0xb}: {0xb1, 0xb1, 0xb1}, // common.HashLength+3 + common.Hash{0xc}: nil, // common.HashLength, account deletion + }, + map[common.Hash]map[common.Hash][]byte{ + common.Hash{0xa}: { + common.Hash{0x1}: {0x11, 0x11, 0x11}, // 2*common.HashLength+3 + common.Hash{0x3}: {0x31, 0x31}, // 2*common.HashLength+2, slot creation + }, + common.Hash{0xb}: { + common.Hash{0x1}: {0x11, 0x11}, // 2*common.HashLength+2 + common.Hash{0x2}: {0x22, 0x22}, // 2*common.HashLength+2, slot creation + }, + // The storage of 0xc is entirely removed + common.Hash{0xc}: { + common.Hash{0x1}: nil, // 2*common.HashLength, slot deletion + common.Hash{0x2}: nil, // 2*common.HashLength, slot deletion + common.Hash{0x3}: nil, // 2*common.HashLength, slot deletion + }, + }, + ) + if b.size != uint64(expSizeB) { + t.Fatalf("Unexpected size, want: %d, got: %d", expSizeB, b.size) + } + + a.merge(b) + mergeSize := expSizeA + 1 /* account a data change */ + 2 /* account b data change */ - 1 /* account c data change */ + mergeSize += 2*common.HashLength + 2 + 2 /* storage a change */ + mergeSize += 2*common.HashLength + 2 - 1 /* storage b change */ + mergeSize += 2*2*common.HashLength - 1 /* storage data removal of 0xc */ + + if a.size != uint64(mergeSize) { + t.Fatalf("Unexpected size, want: %d, got: %d", mergeSize, a.size) + } + + // Revert the set to original status + a.revertTo( + map[common.Hash][]byte{ + common.Hash{0xa}: {0xa0}, + common.Hash{0xb}: {0xb0}, + common.Hash{0xc}: {0xc0}, + }, + map[common.Hash]map[common.Hash][]byte{ + common.Hash{0xa}: { + common.Hash{0x1}: {0x10}, + common.Hash{0x2}: {0x20}, + common.Hash{0x3}: nil, // revert slot creation + }, + common.Hash{0xb}: { + common.Hash{0x1}: {0x10, 0x11, 0x12}, + common.Hash{0x2}: nil, // revert slot creation + }, + common.Hash{0xc}: { + common.Hash{0x1}: {0x10}, + common.Hash{0x2}: {0x20}, // resurrected slot + common.Hash{0x3}: {0x30}, // resurrected slot + }, + }, + ) + revertSize := expSizeA + 2*common.HashLength + 2*common.HashLength // delete-marker of a.3 and b.2 slot + revertSize += 2 * (2*common.HashLength + 1) // resurrected slot, c.2, c.3 + if a.size != uint64(revertSize) { + t.Fatalf("Unexpected size, want: %d, got: %d", revertSize, a.size) + } +} diff --git a/triedb/states.go b/triedb/states.go index 1f9a0de522..fa432e0704 100644 --- a/triedb/states.go +++ b/triedb/states.go @@ -23,7 +23,6 @@ import ( // StateSet represents a collection of mutated states during a state transition. type StateSet struct { - Destructs map[common.Hash]struct{} // Destructed accounts Accounts map[common.Hash][]byte // Mutated accounts in 'slim RLP' encoding AccountsOrigin map[common.Address][]byte // Original values of mutated accounts in 'slim RLP' encoding Storages map[common.Hash]map[common.Hash][]byte // Mutated storage slots in 'prefix-zero-trimmed' RLP format @@ -33,7 +32,6 @@ type StateSet struct { // NewStateSet initializes an empty state set. func NewStateSet() *StateSet { return &StateSet{ - Destructs: make(map[common.Hash]struct{}), Accounts: make(map[common.Hash][]byte), AccountsOrigin: make(map[common.Address][]byte), Storages: make(map[common.Hash]map[common.Hash][]byte), @@ -47,5 +45,5 @@ func (set *StateSet) internal() *pathdb.StateSetWithOrigin { if set == nil { return nil } - return pathdb.NewStateSetWithOrigin(set.AccountsOrigin, set.StoragesOrigin) + return pathdb.NewStateSetWithOrigin(set.Accounts, set.Storages, set.AccountsOrigin, set.StoragesOrigin) }