diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index 5852865f94..b569180852 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -1,25 +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/.gitignore b/.gitignore index a1d48cc72b..7000fedd25 100644 --- a/.gitignore +++ b/.gitignore @@ -4,16 +4,11 @@ # or operating system, you probably want to add a global ignore instead: # git config --global core.excludesfile ~/.gitignore_global -/tmp */**/*un~ */**/*.test *un~ .DS_Store */**/.DS_Store -.ethtest -*/**/*tx_database* -*/**/*dapps* -build/_vendor/pkg #* .#* @@ -42,19 +37,9 @@ profile.cov # IdeaIDE .idea +*.iml # VS Code .vscode -# dashboard -/dashboard/assets/flow-typed -/dashboard/assets/node_modules -/dashboard/assets/stats.json -/dashboard/assets/bundle.js -/dashboard/assets/bundle.js.map -/dashboard/assets/package-lock.json - -**/yarn-error.log -logs/ - tests/spec-tests/ diff --git a/.travis.yml b/.travis.yml index 31c944641f..2ba3af9419 100644 --- a/.travis.yml +++ b/.travis.yml @@ -25,7 +25,7 @@ jobs: before_install: - export DOCKER_CLI_EXPERIMENTAL=enabled script: - - go run build/ci.go dockerx -platform "linux/amd64,linux/arm64" -upload ethereum/client-go + - go run build/ci.go dockerx -platform "linux/amd64,linux/arm64,linux/riscv64" -upload ethereum/client-go # This builder does the Linux Azure uploads - stage: build diff --git a/README.md b/README.md index 182464c639..27842008aa 100644 --- a/README.md +++ b/README.md @@ -40,7 +40,6 @@ directory. | `clef` | Stand-alone signing tool, which can be used as a backend signer for `geth`. | | `devp2p` | Utilities to interact with nodes on the networking layer, without running a full blockchain. | | `abigen` | Source code generator to convert Ethereum contract definitions into easy-to-use, compile-time type-safe Go packages. It operates on plain [Ethereum contract ABIs](https://docs.soliditylang.org/en/develop/abi-spec.html) with expanded functionality if the contract bytecode is also available. However, it also accepts Solidity source files, making development much more streamlined. Please see our [Native DApps](https://geth.ethereum.org/docs/developers/dapp-developer/native-bindings) page for details. | -| `bootnode` | Stripped down version of our Ethereum client implementation that only takes part in the network node discovery protocol, but does not run any of the higher level application protocols. It can be used as a lightweight bootstrap node to aid in finding peers in private networks. | | `evm` | Developer utility version of the EVM (Ethereum Virtual Machine) that is capable of running bytecode snippets within a configurable environment and execution mode. Its purpose is to allow isolated, fine-grained debugging of EVM opcodes (e.g. `evm --code 60ff60ff --debug run`). | | `rlpdump` | Developer utility tool to convert binary RLP ([Recursive Length Prefix](https://ethereum.org/en/developers/docs/data-structures-and-encoding/rlp)) dumps (data encoding used by the Ethereum protocol both network as well as consensus wise) to user-friendlier hierarchical representation (e.g. `rlpdump --hex CE0183FFFFFFC4C304050583616263`). | @@ -270,8 +269,14 @@ start a bootstrap node that others can use to find each other in your network an the internet. The clean way is to configure and run a dedicated bootnode: ```shell -$ bootnode --genkey=boot.key -$ bootnode --nodekey=boot.key +# Use the devp2p tool to create a node file. +# The devp2p tool is also part of the 'alltools' distribution bundle. +$ devp2p key generate node1.key +# file node1.key is created. +$ devp2p key to-enr -ip 10.2.3.4 -udp 30303 -tcp 30303 node1.key +# Prints the ENR for use in --bootnode flag of other nodes. +# Note this method requires knowing the IP/ports ahead of time. +$ geth --nodekey=node1.key ``` With the bootnode online, it will display an [`enode` URL](https://ethereum.org/en/developers/docs/networking-layer/network-addresses/#enode) @@ -279,8 +284,7 @@ that other nodes can use to connect to it and exchange peer information. Make su replace the displayed IP address information (most probably `[::]`) with your externally accessible IP to get the actual `enode` URL. -*Note: You could also use a full-fledged `geth` node as a bootnode, but it's the less -recommended way.* +*Note: You could previously use the `bootnode` utility to start a stripped down version of geth. This is not possible anymore.* #### Starting up your member nodes diff --git a/SECURITY.md b/SECURITY.md index 1c99ab5950..0b497b44ae 100644 --- a/SECURITY.md +++ b/SECURITY.md @@ -29,147 +29,69 @@ Fingerprint: `AE96 ED96 9E47 9B00 84F3 E17F E88D 3334 FA5F 6A0A` ``` -----BEGIN PGP PUBLIC KEY BLOCK----- -Version: SKS 1.1.6 -Comment: Hostname: pgp.mit.edu -mQINBFgl3tgBEAC8A1tUBkD9YV+eLrOmtgy+/JS/H9RoZvkg3K1WZ8IYfj6iIRaYneAk3Bp1 -82GUPVz/zhKr2g0tMXIScDR3EnaDsY+Qg+JqQl8NOG+Cikr1nnkG2on9L8c8yiqry1ZTCmYM -qCa2acTFqnyuXJ482aZNtB4QG2BpzfhW4k8YThpegk/EoRUim+y7buJDtoNf7YILlhDQXN8q -lHB02DWOVUihph9tUIFsPK6BvTr9SIr/eG6j6k0bfUo9pexOn7LS4SojoJmsm/5dp6AoKlac -48cZU5zwR9AYcq/nvkrfmf2WkObg/xRdEvKZzn05jRopmAIwmoC3CiLmqCHPmT5a29vEob/y -PFE335k+ujjZCPOu7OwjzDk7M0zMSfnNfDq8bXh16nn+ueBxJ0NzgD1oC6c2PhM+XRQCXCho -yI8vbfp4dGvCvYqvQAE1bWjqnumZ/7vUPgZN6gDfiAzG2mUxC2SeFBhacgzDvtQls+uuvm+F -nQOUgg2Hh8x2zgoZ7kqV29wjaUPFREuew7e+Th5BxielnzOfVycVXeSuvvIn6cd3g/s8mX1c -2kLSXJR7+KdWDrIrR5Az0kwAqFZt6B6QTlDrPswu3mxsm5TzMbny0PsbL/HBM+GZEZCjMXxB -8bqV2eSaktjnSlUNX1VXxyOxXA+ZG2jwpr51egi57riVRXokrQARAQABtDRFdGhlcmV1bSBG -b3VuZGF0aW9uIEJ1ZyBCb3VudHkgPGJvdW50eUBldGhlcmV1bS5vcmc+iQIcBBEBCAAGBQJa -FCY6AAoJEHoMA3Q0/nfveH8P+gJBPo9BXZL8isUfbUWjwLi81Yi70hZqIJUnz64SWTqBzg5b -mCZ69Ji5637THsxQetS2ARabz0DybQ779FhD/IWnqV9T3KuBM/9RzJtuhLzKCyMrAINPMo28 -rKWdunHHarpuR4m3tL2zWJkle5QVYb+vkZXJJE98PJw+N4IYeKKeCs2ubeqZu636GA0sMzzB -Jn3m/dRRA2va+/zzbr6F6b51ynzbMxWKTsJnstjC8gs8EeI+Zcd6otSyelLtCUkk3h5sTvpV -Wv67BNSU0BYsMkxyFi9PUyy07Wixgeas89K5jG1oOtDva/FkpRHrTE/WA5OXDRcLrHJM+SwD -CwqcLQqJd09NxwUW1iKeBmPptTiOGu1Gv2o7aEyoaWrHRBO7JuYrQrj6q2B3H1Je0zjAd2qt -09ni2bLwLn4LA+VDpprNTO+eZDprv09s2oFSU6NwziHybovu0y7X4pADGkK2evOM7c86PohX -QRQ1M1T16xLj6wP8/Ykwl6v/LUk7iDPXP3GPILnh4YOkwBR3DsCOPn8098xy7FxEELmupRzt -Cj9oC7YAoweeShgUjBPzb+nGY1m6OcFfbUPBgFyMMfwF6joHbiVIO+39+Ut2g2ysZa7KF+yp -XqVDqyEkYXsOLb25OC7brt8IJEPgBPwcHK5GNag6RfLxnQV+iVZ9KNH1yQgSiQI+BBMBAgAo -AhsDBgsJCAcDAgYVCAIJCgsEFgIDAQIeAQIXgAUCWglh+gUJBaNgWAAKCRDojTM0+l9qCgQ2 -D/4udJpV4zGIZW1yNaVvtd3vfKsTLi7GIRJLUBqVb2Yx/uhnN8jTl/tAhCVosCQ1pzvi9kMl -s8qO1vu2kw5EWFFkwK96roI8pTql3VIjwhRVQrCkR7oAk/eUd1U/nt2q6J4UTYeVgqbq4dsI -ZZTRyPJMD667YpuAIcaah+w9j/E5xksYQdMeprnDrQkkBCb4FIMqfDzBPKvEa8DcQr949K85 -kxhr6LDq9i5l4Egxt2JdH8DaR4GLca6+oHy0MyPs/bZOsfmZUObfM2oZgPpqYM96JanhzO1j -dpnItyBii2pc+kNx5nMOf4eikE/MBv+WUJ0TttWzApGGmFUzDhtuEvRH9NBjtJ/pMrYspIGu -O/QNY5KKOKQTvVIlwGcm8dTsSkqtBDSUwZyWbfKfKOI1/RhM9dC3gj5/BOY57DYYV4rdTK01 -ZtYjuhdfs2bhuP1uF/cgnSSZlv8azvf7Egh7tHPnYxvLjfq1bJAhCIX0hNg0a81/ndPAEFky -fSko+JPKvdSvsUcSi2QQ4U2HX//jNBjXRfG4F0utgbJnhXzEckz6gqt7wSDZH2oddVuO8Ssc -T7sK+CdXthSKnRyuI+sGUpG+6glpKWIfYkWFKNZWuQ+YUatY3QEDHXTIioycSmV8p4d/g/0S -V6TegidLxY8bXMkbqz+3n6FArRffv5MH7qt3cYkCPgQTAQIAKAUCWCXhOwIbAwUJAeEzgAYL -CQgHAwIGFQgCCQoLBBYCAwECHgECF4AACgkQ6I0zNPpfagrN/w/+Igp3vtYdNunikw3yHnYf -Jkm0MmaMDUM9mtsaXVN6xb9n25N3Xa3GWCpmdsbYZ8334tI/oQ4/NHq/bEI5WFH5F1aFkMkm -5AJVLuUkipCtmCZ5NkbRPJA9l0uNUUE6uuFXBhf4ddu7jb0jMetRF/kifJHVCCo5fISUNhLp -7bwcWq9qgDQNZNYMOo4s9WX5Tl+5x4gTZdd2/cAYt49h/wnkw+huM+Jm0GojpLqIQ1jZiffm -otf5rF4L+JhIIdW0W4IIh1v9BhHVllXw+z9oj0PALstT5h8/DuKoIiirFJ4DejU85GR1KKAS -DeO19G/lSpWj1rSgFv2N2gAOxq0X+BbQTua2jdcY6JpHR4H1JJ2wzfHsHPgDQcgY1rGlmjVF -aqU73WV4/hzXc/HshK/k4Zd8uD4zypv6rFsZ3UemK0aL2zXLVpV8SPWQ61nS03x675SmDlYr -A80ENfdqvsn00JQuBVIv4Tv0Ub7NfDraDGJCst8rObjBT/0vnBWTBCebb2EsnS2iStIFkWdz -/WXs4L4Yzre1iJwqRjiuqahZR5jHsjAUf2a0O29HVHE7zlFtCFmLPClml2lGQfQOpm5klGZF -rmvus+qZ9rt35UgWHPZezykkwtWrFOwspwuCWaPDto6tgbRJZ4ftitpdYYM3dKW9IGJXBwrt -BQrMsu+lp0vDF+yJAlUEEwEIAD8CGwMGCwkIBwMCBhUIAgkKCwQWAgMBAh4BAheAFiEErpbt -lp5HmwCE8+F/6I0zNPpfagoFAmEAEJwFCQycmLgACgkQ6I0zNPpfagpWoBAAhOcbMAUw6Zt0 -GYzT3sR5/c0iatezPzXEXJf9ebzR8M5uPElXcxcnMx1dvXZmGPXPJKCPa99WCu1NZYy8F+Wj -GTOY9tfIkvSxhys1p/giPAmvid6uQmD+bz7ivktnyzCkDWfMA+l8lsCSEqVlaq6y5T+a6SWB -6TzC2S0MPb/RrC/7DpwyrNYWumvyVJh09adm1Mw/UGgst/sZ8eMaRYEd3X0yyT1CBpX4zp2E -qQj9IEOTizvzv1x2jkHe5ZUeU3+nTBNlhSA+WFHUi0pfBdo2qog3Mv2EC1P2qMKoSdD5tPbA -zql1yKoHHnXOMsqdftGwbiv2sYXWvrYvmaCd3Ys/viOyt3HOy9uV2ZEtBd9Yqo9x/NZj8QMA -nY5k8jjrIXbUC89MqrJsQ6xxWQIg5ikMT7DvY0Ln89ev4oJyVvwIQAwCm4jUzFNm9bZLYDOP -5lGJCV7tF5NYVU7NxNM8vescKc40mVNK/pygS5mxhK9QYOUjZsIv8gddrl1TkqrFMuxFnTyN -WvzE29wFu/n4N1DkF+ZBqS70SlRvB+Hjz5LrDgEzF1Wf1eA/wq1dZbvMjjDVIc2VGlYp8Cp2 -8ob23c1seTtYXTNYgSR5go4EpH+xi+bIWv01bQQ9xGwBbT5sm4WUeWOcmX4QewzLZ3T/wK9+ -N4Ye/hmU9O34FwWJOY58EIe0OUV0aGVyZXVtIEZvdW5kYXRpb24gU2VjdXJpdHkgVGVhbSA8 -c2VjdXJpdHlAZXRoZXJldW0ub3JnPokCHAQRAQgABgUCWhQmOgAKCRB6DAN0NP5372LSEACT -wZk1TASWZj5QF7rmkIM1GEyBxLE+PundNcMgM9Ktj1315ED8SmiukNI4knVS1MY99OIgXhQl -D1foF2GKdTomrwwC4012zTNyUYCY60LnPZ6Z511HG+rZgZtZrbkz0IiUpwAlhGQND77lBqem -J3K+CFX2XpDA/ojui/kqrY4cwMT5P8xPJkwgpRgw/jgdcZyJTsXdHblV9IGU4H1Vd1SgcfAf -Db3YxDUlBtzlp0NkZqxen8irLIXUQvsfuIfRUbUSkWoK/n3U/gOCajAe8ZNF07iX4OWjH4Sw -NDA841WhFWcGE+d8+pfMVfPASU3UPKH72uw86b2VgR46Av6voyMFd1pj+yCA+YAhJuOpV4yL -QaGg2Z0kVOjuNWK/kBzp1F58DWGh4YBatbhE/UyQOqAAtR7lNf0M3QF9AdrHTxX8oZeqVW3V -Fmi2mk0NwCIUv8SSrZr1dTchp04OtyXe5gZBXSfzncCSRQIUDC8OgNWaOzAaUmK299v4bvye -uSCxOysxC7Q1hZtjzFPKdljS81mRlYeUL4fHlJU9R57bg8mriSXLmn7eKrSEDm/EG5T8nRx7 -TgX2MqJs8sWFxD2+bboVEu75yuFmZ//nmCBApAit9Hr2/sCshGIEpa9MQ6xJCYUxyqeJH+Cc -Aja0UfXhnK2uvPClpJLIl4RE3gm4OXeE1IkCPgQTAQIAKAIbAwYLCQgHAwIGFQgCCQoLBBYC -AwECHgECF4AFAloJYfoFCQWjYFgACgkQ6I0zNPpfagr4MQ//cfp3GSbSG8dkqgctW67Fy7cQ -diiTmx3cwxY+tlI3yrNmdjtrIQMzGdqtY6LNz7aN87F8mXNf+DyVHX9+wd1Y8U+E+hVCTzKC -sefUfxTz6unD9TTcGqaoelgIPMn4IiKz1RZE6eKpfDWe6q78W1Y6x1bE0qGNSjqT/QSxpezF -E/OAm/t8RRxVxDtqz8LfH2zLea5zaC+ADj8EqgY9vX9TQa4DyVV8MgOyECCCadJQCD5O5hIA -B2gVDWwrAUw+KBwskXZ7Iq4reJTKLEmt5z9zgtJ/fABwaCFt66ojwg0/RjbO9cNA3ZwHLGwU -C6hkb6bRzIoZoMfYxVS84opiqf/Teq+t/XkBYCxbSXTJDA5MKjcVuw3N6YKWbkGP/EfQThe7 -BfAKFwwIw5YmsWjHK8IQj6R6hBxzTz9rz8y1Lu8EAAFfA7OJKaboI2qbOlauH98OuOUmVtr1 -TczHO+pTcgWVN0ytq2/pX5KBf4vbmULNbg3HFRq+gHx8CW+jyXGkcqjbgU/5FwtDxeqRTdGJ -SyBGNBEU6pBNolyynyaKaaJjJ/biY27pvjymL5rlz95BH3Dn16Z4RRmqwlT6eq/wFYginujg -CCE1icqOSE+Vjl7V8tV8AcgANkXKdbBE+Q8wlKsGI/kS1w4XFAYcaNHFT8qNeS8TSFXFhvU8 -HylYxO79t56JAj4EEwECACgFAlgl3tgCGwMFCQHhM4AGCwkIBwMCBhUIAgkKCwQWAgMBAh4B -AheAAAoJEOiNMzT6X2oKmUMP/0hnaL6bVyepAq2LIdvIUbHfagt/Oo/KVfZs4bkM+xJOitJR -0kwZV9PTihXFdzhL/YNWc2+LtEBtKItqkJZKmWC0E6OPXGVuU6hfFPebuzVccYJfm0Q3Ej19 -VJI9Uomf59Bpak8HYyEED7WVQjoYn7XVPsonwus/9+LDX+c5vutbrUdbjga3KjHbewD93X4O -wVVoXyHEmU2Plyg8qvzFbNDylCWO7N2McO6SN6+7DitGZGr2+jO+P2R4RT1cnl2V3IRVcWZ0 -OTspPSnRGVr2fFiHN/+v8G/wHPLQcJZFvYPfUGNdcYbTmhWdiY0bEYXFiNrgzCCsyad7eKUR -WN9QmxqmyqLDjUEDJCAh19ES6Vg3tqGwXk+uNUCoF30ga0TxQt6UXZJDEQFAGeASQ/RqE/q1 -EAuLv8IGM8o7IqKO2pWfLuqsY6dTbKBwDzz9YOJt7EOGuPPQbHxaYStTushZmJnm7hi8lhVG -jT7qsEJdE95Il+I/mHWnXsCevaXjZugBiyV9yvOq4Hwwe2s1zKfrnQ4u0cadvGAh2eIqum7M -Y3o6nD47aJ3YmEPX/WnhI56bACa2GmWvUwjI4c0/er3esSPYnuHnM9L8Am4qQwMVSmyU80tC -MI7A9e13Mvv+RRkYFLJ7PVPdNpbW5jqX1doklFpKf6/XM+B+ngYneU+zgCUBiQJVBBMBCAA/ -AhsDBgsJCAcDAgYVCAIJCgsEFgIDAQIeAQIXgBYhBK6W7ZaeR5sAhPPhf+iNMzT6X2oKBQJh -ABCQBQkMnJi4AAoJEOiNMzT6X2oKAv0P+gJ3twBp5efNWyVLcIg4h4cOo9uD0NPvz8/fm2gX -FoOJL3MeigtPuSVfE9kuTaTuRbArzuFtdvH6G/kcRQvOlO4zyiIRHCk1gDHoIvvtn6RbRhVm -/Xo4uGIsFHst7n4A7BjicwEK5Op6Ih5Hoq19xz83YSBgBVk2fYEJIRyJiKFbyPjH0eSYe8v+ -Ra5/F85ugLx1P6mMVkW+WPzULns89riW7BGTnZmXFHZp8nO2pkUlcI7F3KRG7l4kmlC50ox6 -DiG/6AJCVulbAClky9C68TmJ/R1RazQxU/9IqVywsydq66tbJQbm5Z7GEti0C5jjbSRJL2oT -1xC7Rilr85PMREkPL3vegJdgj5PKlffZ/MocD/0EohiQ7wFpejFD4iTljeh0exRUwCRb6655 -9ib34JSQgU8Hl4JJu+mEgd9v0ZHD0/1mMD6fnAR84zca+O3cdASbnQmzTOKcGzLIrkE8TEnU -+2UZ8Ol7SAAqmBgzY1gKOilUho6dkyCAwNL+QDpvrITDPLEFPsjyB/M2KudZSVEn+Rletju1 -qkMW31qFMNlsbwzMZw+0USeGcs31Cs0B2/WQsro99CExlhS9auUFkmoVjJmYVTIYOM0zuPa4 -OyGspqPhRu5hEsmMDPDWD7Aad5k4GTqogQNnuKyRliZjXXrDZqFD5nfsJSL8Ky/sJGEMuQIN -BFgl3tgBEACbgq6HTN5gEBi0lkD/MafInmNi+59U5gRGYqk46WlfRjhHudXjDpgD0lolGb4h -YontkMaKRlCg2Rvgjvk3Zve0PKWjKw7gr8YBa9fMFY8BhAXI32OdyI9rFhxEZFfWAfwKVmT1 -9BdeAQRFvcfd+8w8f1XVc+zddULMJFBTr+xKDlIRWwTkdLPQeWbjo0eHl/g4tuLiLrTxVbnj -26bf+2+1DbM/w5VavzPrkviHqvKe/QP/gay4QDViWvFgLb90idfAHIdsPgflp0VDS5rVHFL6 -D73rSRdIRo3I8c8mYoNjSR4XDuvgOkAKW9LR3pvouFHHjp6Fr0GesRbrbb2EG66iPsR99MQ7 -FqIL9VMHPm2mtR+XvbnKkH2rYyEqaMbSdk29jGapkAWle4sIhSKk749A4tGkHl08KZ2N9o6G -rfUehP/V2eJLaph2DioFL1HxRryrKy80QQKLMJRekxigq8greW8xB4zuf9Mkuou+RHNmo8Pe -bHjFstLigiD6/zP2e+4tUmrT0/JTGOShoGMl8Rt0VRxdPImKun+4LOXbfOxArOSkY6i35+gs -gkkSy1gTJE0BY3S9auT6+YrglY/TWPQ9IJxWVOKlT+3WIp5wJu2bBKQ420VLqDYzkoWytel/ -bM1ACUtipMiIVeUs2uFiRjpzA1Wy0QHKPTdSuGlJPRrfcQARAQABiQIlBBgBAgAPAhsMBQJa -CWIIBQkFo2BYAAoJEOiNMzT6X2oKgSwQAKKs7BGF8TyZeIEO2EUK7R2bdQDCdSGZY06tqLFg -3IHMGxDMb/7FVoa2AEsFgv6xpoebxBB5zkhUk7lslgxvKiSLYjxfNjTBltfiFJ+eQnf+OTs8 -KeR51lLa66rvIH2qUzkNDCCTF45H4wIDpV05AXhBjKYkrDCrtey1rQyFp5fxI+0IQ1UKKXvz -ZK4GdxhxDbOUSd38MYy93nqcmclGSGK/gF8XiyuVjeifDCM6+T1NQTX0K9lneidcqtBDvlgg -JTLJtQPO33o5EHzXSiud+dKth1uUhZOFEaYRZoye1YE3yB0TNOOE8fXlvu8iuIAMBSDL9ep6 -sEIaXYwoD60I2gHdWD0lkP0DOjGQpi4ouXM3Edsd5MTi0MDRNTij431kn8T/D0LCgmoUmYYM -BgbwFhXr67axPZlKjrqR0z3F/Elv0ZPPcVg1tNznsALYQ9Ovl6b5M3cJ5GapbbvNWC7yEE1q -Scl9HiMxjt/H6aPastH63/7wcN0TslW+zRBy05VNJvpWGStQXcngsSUeJtI1Gd992YNjUJq4 -/Lih6Z1TlwcFVap+cTcDptoUvXYGg/9mRNNPZwErSfIJ0Ibnx9wPVuRN6NiCLOt2mtKp2F1p -M6AOQPpZ85vEh6I8i6OaO0w/Z0UHBwvpY6jDUliaROsWUQsqz78Z34CVj4cy6vPW2EF4iQIl -BBgBAgAPBQJYJd7YAhsMBQkB4TOAAAoJEOiNMzT6X2oKTjgP/1ojCVyGyvHMLUgnX0zwrR5Q -1M5RKFz6kHwKjODVLR3Isp8I935oTQt3DY7yFDI4t0GqbYRQMtxcNEb7maianhK2trCXfhPs -6/L04igjDf5iTcmzamXN6xnh5xkz06hZJJCMuu4MvKxC9MQHCVKAwjswl/9H9JqIBXAY3E2l -LpX5P+5jDZuPxS86p3+k4Rrdp9KTGXjiuEleM3zGlz5BLWydqovOck7C2aKh27ETFpDYY0z3 -yQ5AsPJyk1rAr0wrH6+ywmwWlzuQewavnrLnJ2M8iMFXpIhyHeEIU/f7o8f+dQk72rZ9CGzd -cqig2za/BS3zawZWgbv2vB2elNsIllYLdir45jxBOxx2yvJvEuu4glz78y4oJTCTAYAbMlle -5gVdPkVcGyvvVS9tinnSaiIzuvWrYHKWll1uYPm2Q1CDs06P5I7bUGAXpgQLUh/XQguy/0sX -GWqW3FS5JzP+XgcR/7UASvwBdHylubKbeqEpB7G1s+m+8C67qOrc7EQv3Jmy1YDOkhEyNig1 -rmjplLuir3tC1X+D7dHpn7NJe7nMwFx2b2MpMkLA9jPPAGPp/ekcu5sxCe+E0J/4UF++K+CR -XIxgtzU2UJfp8p9x+ygbx5qHinR0tVRdIzv3ZnGsXrfxnWfSOaB582cU3VRN9INzHHax8ETa -QVDnGO5uQa+FiQI8BBgBCAAmAhsMFiEErpbtlp5HmwCE8+F/6I0zNPpfagoFAmEAELYFCQyc -mN4ACgkQ6I0zNPpfagoqAQ/+MnDjBx8JWMd/XjeFoYKx/Oo0ntkInV+ME61JTBls4PdVk+TB -8PWZdPQHw9SnTvRmykFeznXIRzuxkowjrZYXdPXBxY2b1WyD5V3Ati1TM9vqpaR4osyPs2xy -I4dzDssh9YvUsIRL99O04/65lGiYeBNuACq+yK/7nD/ErzBkDYJHhMCdadbVWUACxvVIDvro -yQeVLKMsHqMCd8BTGD7VDs79NXskPnN77pAFnkzS4Z2b8SNzrlgTc5pUiuZHIXPIpEYmsYzh -ucTU6uI3dN1PbSFHK5tG2pHb4ZrPxY3L20Dgc2Tfu5/SDApZzwvvKTqjdO891MEJ++H+ssOz -i4O1UeWKs9owWttan9+PI47ozBSKOTxmMqLSQ0f56Np9FJsV0ilGxRKfjhzJ4KniOMUBA7mP -+m+TmXfVtthJred4sHlJMTJNpt+sCcT6wLMmyc3keIEAu33gsJj3LTpkEA2q+V+ZiP6Q8HRB -402ITklABSArrPSE/fQU9L8hZ5qmy0Z96z0iyILgVMLuRCCfQOMWhwl8yQWIIaf1yPI07xur -epy6lH7HmxjjOR7eo0DaSxQGQpThAtFGwkWkFh8yki8j3E42kkrxvEyyYZDXn2YcI3bpqhJx -PtwCMZUJ3kc/skOrs6bOI19iBNaEoNX5Dllm7UHjOgWNDQkcCuOCxucKano= -=arte +mQINBFgl3tgBEAC8A1tUBkD9YV+eLrOmtgy+/JS/H9RoZvkg3K1WZ8IYfj6iIRaY +neAk3Bp182GUPVz/zhKr2g0tMXIScDR3EnaDsY+Qg+JqQl8NOG+Cikr1nnkG2on9 +L8c8yiqry1ZTCmYMqCa2acTFqnyuXJ482aZNtB4QG2BpzfhW4k8YThpegk/EoRUi +m+y7buJDtoNf7YILlhDQXN8qlHB02DWOVUihph9tUIFsPK6BvTr9SIr/eG6j6k0b +fUo9pexOn7LS4SojoJmsm/5dp6AoKlac48cZU5zwR9AYcq/nvkrfmf2WkObg/xRd +EvKZzn05jRopmAIwmoC3CiLmqCHPmT5a29vEob/yPFE335k+ujjZCPOu7OwjzDk7 +M0zMSfnNfDq8bXh16nn+ueBxJ0NzgD1oC6c2PhM+XRQCXChoyI8vbfp4dGvCvYqv +QAE1bWjqnumZ/7vUPgZN6gDfiAzG2mUxC2SeFBhacgzDvtQls+uuvm+FnQOUgg2H +h8x2zgoZ7kqV29wjaUPFREuew7e+Th5BxielnzOfVycVXeSuvvIn6cd3g/s8mX1c +2kLSXJR7+KdWDrIrR5Az0kwAqFZt6B6QTlDrPswu3mxsm5TzMbny0PsbL/HBM+GZ +EZCjMXxB8bqV2eSaktjnSlUNX1VXxyOxXA+ZG2jwpr51egi57riVRXokrQARAQAB +tDRFdGhlcmV1bSBGb3VuZGF0aW9uIEJ1ZyBCb3VudHkgPGJvdW50eUBldGhlcmV1 +bS5vcmc+iQJVBBMBCAA/AhsDBgsJCAcDAgYVCAIJCgsEFgIDAQIeAQIXgBYhBK6W +7ZaeR5sAhPPhf+iNMzT6X2oKBQJl2LD9BQkRdTklAAoJEOiNMzT6X2oKYYYQALkV +wJjWYoVoMuw9D1ybQo4Sqyp6D/XYHXSpqZDO9RlADQisYBfuO7EW75evgZ+54Ajc +8gZ2BUkFcSR9z2t0TEkUyjmPDZsaElTTP2Boa2GG5pyziEM6t1cMMY1sP1aotx9H +DYwCeMmDv0wTMi6v0C6+/in2hBxbGALRbQKWKd/5ss4OEPe37hG9zAJcBYZg2tes +O7ceg7LHZpNC1zvMUrBY6os74FJ437f8bankqvVE83/dvTcCDhMsei9LiWS2uo26 +qiyqeR9lZEj8W5F6UgkQH+UOhamJ9UB3N/h//ipKrwtiv0+jQm9oNG7aIAi3UJgD +CvSod87H0l7/U8RWzyam/r8eh4KFM75hIVtqEy5jFV2z7x2SibXQi7WRfAysjFLp +/li8ff6kLDR9IMATuMSF7Ol0O9JMRfSPjRZRtVOwYVIBla3BhfMrjvMMcZMAy/qS +DWx2iFYDMGsswv7hp3lsFOaa1ju95ClZZk3q/z7u5gH7LFAxR0jPaW48ay3CFylW +sDpQpO1DWb9uXBdhOU+MN18uSjqzocga3Wz2C8jhWRvxyFf3SNIybm3zk6W6IIoy +6KmwSRZ30oxizy6zMYw1qJE89zjjumzlZAm0R/Q4Ui+WJhlSyrYbqzqdxYuLgdEL +lgKfbv9/t8tNXGGSuCe5L7quOv9k7l2+QmLlg+SJtDlFdGhlcmV1bSBGb3VuZGF0 +aW9uIFNlY3VyaXR5IFRlYW0gPHNlY3VyaXR5QGV0aGVyZXVtLm9yZz6JAlUEEwEI +AD8CGwMGCwkIBwMCBhUIAgkKCwQWAgMBAh4BAheAFiEErpbtlp5HmwCE8+F/6I0z +NPpfagoFAmXYsP4FCRF1OSUACgkQ6I0zNPpfagoUGA/+LVzXUJrsfi8+ADMF1hru +wFDcY1r+vM4Ovbk1NhCc/DnV5VG40j5FiQpE81BNiH59sYeZkQm9jFbwevK7Zpuq +RZaG2WGiwU/11xrt5/Qjq7T+vEtd94546kFcBnP8uexZqP4dTi4LHa2on8aRbwzN +7RjCpCQhy1TUuk47dyOR1y3ZHrpTwkHpuhwgffaWtxgSyCMYz7fsd5Ukh3eE+Ani +90CIUieve2U3o+WPxBD9PRaIPg6LmBhfGxGvC/6tqY9W3Z9xEOVDxC4wdYppQzsg +Pg7bNnVmlFWHsEk8FuMfY8nTqY3/ojhJxikWKz2V3Y2AbsLEXCvrEg6b4FvmsS97 +8ifEBbFXU8hvMSpMLtO7vLamWyOHq41IXWH6HLNLhDfDzTfpAJ8iYDKGj72YsMzF +0fIjPa6mniMB2RmREAM0Jas3M/6DUw1EzwK1iQofIBoCRPIkR5mxmzjcRB6tVdQa +on20/9YTKKBUQAdK0OWW8j1euuULDgNdkN2LBXdQLy/JcQiggU8kOCKL/Lmj5HWP +FNT9rYfnjmCuux3UfJGfhPryujEA0CdIfq1Qf4ldOVzpWYjsMn+yQxAQTorAzF3z +iYddP2cw/Nvookay8xywKJnDsaRaWqdQ8Ceox3qSB4LCjQRNR5c3HfvGm3EBdEyI +zEEpjZ6GHa05DCajqKjtjlm5Ag0EWCXe2AEQAJuCrodM3mAQGLSWQP8xp8ieY2L7 +n1TmBEZiqTjpaV9GOEe51eMOmAPSWiUZviFiie2QxopGUKDZG+CO+Tdm97Q8paMr +DuCvxgFr18wVjwGEBcjfY53Ij2sWHERkV9YB/ApWZPX0F14BBEW9x937zDx/VdVz +7N11QswkUFOv7EoOUhFbBOR0s9B5ZuOjR4eX+Di24uIutPFVuePbpt/7b7UNsz/D +lVq/M+uS+Ieq8p79A/+BrLhANWJa8WAtv3SJ18Ach2w+B+WnRUNLmtUcUvoPvetJ +F0hGjcjxzyZig2NJHhcO6+A6QApb0tHem+i4UceOnoWvQZ6xFuttvYQbrqI+xH30 +xDsWogv1Uwc+baa1H5e9ucqQfatjISpoxtJ2Tb2MZqmQBaV7iwiFIqTvj0Di0aQe +XTwpnY32joat9R6E/9XZ4ktqmHYOKgUvUfFGvKsrLzRBAoswlF6TGKCryCt5bzEH +jO5/0yS6i75Ec2ajw95seMWy0uKCIPr/M/Z77i1SatPT8lMY5KGgYyXxG3RVHF08 +iYq6f7gs5dt87ECs5KRjqLfn6CyCSRLLWBMkTQFjdL1q5Pr5iuCVj9NY9D0gnFZU +4qVP7dYinnAm7ZsEpDjbRUuoNjOShbK16X9szUAJS2KkyIhV5Sza4WJGOnMDVbLR +Aco9N1K4aUk9Gt9xABEBAAGJAjwEGAEIACYCGwwWIQSulu2WnkebAITz4X/ojTM0 ++l9qCgUCZdiwoAUJEXU4yAAKCRDojTM0+l9qCj2PD/9pbIPRMZtvKIIE+OhOAl/s +qfZJXByAM40ELpUhDHqwbOplIEyvXtWfQ5c+kWlG/LPJ2CgLkHyFQDn6tuat82rH +/5VoZyxp16CBAwEgYdycOr9hMGSVKNIJDfV9Bu6VtZnn6fa/swBzGE7eVpXsIoNr +jeqsogBtzLecG1oHMXRMq7oUqu9c6VNoCx2uxRUOeWW8YuP7h9j6mxIuKKbcpmQ5 +RSLNEhJZJsMMFLf8RAQPXmshG1ZixY2ZliNe/TTm6eEfFCw0KcQxoX9LmurLWE9w +dIKgn1/nQ04GFnmtcq3hVxY/m9BvzY1jmZXNd4TdpfrPXhi0W/GDn53ERFPJmw5L +F8ogxzD/ekxzyd9nCCgtzkamtBKDJk35x/MoVWMLjD5k6P+yW7YY4xMQliSJHKss +leLnaPpgDBi4KPtLxPswgFqObcy4TNse07rFO4AyHf11FBwMTEfuODCOMnQTpi3z +Zx6KxvS3BEY36abjvwrqsmt8dJ/+/QXT0e82fo2kJ65sXIszez3e0VUZ8KrMp+wd +X0GWYWAfqXws6HrQFYfIpEE0Vz9gXDxEOTFZ2FoVIvIHyRfyDrAIz3wZLmnLGk1h +l3CDjHF0Wigv0CacIQ1V1aYp3NhIVwAvShQ+qS5nFgik6UZnjjWibobOm3yQDzll +6F7hEeTW+gnXEI2gPjfb5w== +=b5eA -----END PGP PUBLIC KEY BLOCK----- ``` 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/bind/util.go b/accounts/abi/bind/util.go index b931fbb04d..c83116e9a1 100644 --- a/accounts/abi/bind/util.go +++ b/accounts/abi/bind/util.go @@ -30,12 +30,18 @@ import ( // WaitMined waits for tx to be mined on the blockchain. // It stops waiting when the context is canceled. func WaitMined(ctx context.Context, b DeployBackend, tx *types.Transaction) (*types.Receipt, error) { + return WaitMinedHash(ctx, b, tx.Hash()) +} + +// WaitMinedHash waits for a transaction with the provided hash to be mined on the blockchain. +// It stops waiting when the context is canceled. +func WaitMinedHash(ctx context.Context, b DeployBackend, hash common.Hash) (*types.Receipt, error) { queryTicker := time.NewTicker(time.Second) defer queryTicker.Stop() - logger := log.New("hash", tx.Hash()) + logger := log.New("hash", hash) for { - receipt, err := b.TransactionReceipt(ctx, tx.Hash()) + receipt, err := b.TransactionReceipt(ctx, hash) if err == nil { return receipt, nil } @@ -61,7 +67,13 @@ func WaitDeployed(ctx context.Context, b DeployBackend, tx *types.Transaction) ( if tx.To() != nil { return common.Address{}, errors.New("tx is not contract creation") } - receipt, err := WaitMined(ctx, b, tx) + return WaitDeployedHash(ctx, b, tx.Hash()) +} + +// WaitDeployedHash waits for a contract deployment transaction with the provided hash and returns the on-chain +// contract address when it is mined. It stops waiting when ctx is canceled. +func WaitDeployedHash(ctx context.Context, b DeployBackend, hash common.Hash) (common.Address, error) { + receipt, err := WaitMinedHash(ctx, b, hash) if err != nil { return common.Address{}, err } 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/accounts/external/backend.go b/accounts/external/backend.go index 62322753da..42eaf661cc 100644 --- a/accounts/external/backend.go +++ b/accounts/external/backend.go @@ -215,7 +215,7 @@ func (api *ExternalSigner) SignTx(account accounts.Account, tx *types.Transactio switch tx.Type() { case types.LegacyTxType, types.AccessListTxType: args.GasPrice = (*hexutil.Big)(tx.GasPrice()) - case types.DynamicFeeTxType, types.BlobTxType: + case types.DynamicFeeTxType, types.BlobTxType, types.SetCodeTxType: args.MaxFeePerGas = (*hexutil.Big)(tx.GasFeeCap()) args.MaxPriorityFeePerGas = (*hexutil.Big)(tx.GasTipCap()) default: diff --git a/accounts/keystore/account_cache.go b/accounts/keystore/account_cache.go index f7cf688e62..d3a98850c7 100644 --- a/accounts/keystore/account_cache.go +++ b/accounts/keystore/account_cache.go @@ -44,8 +44,7 @@ func byURL(a, b accounts.Account) int { return a.URL.Cmp(b.URL) } -// AmbiguousAddrError is returned when attempting to unlock -// an address for which more than one file exists. +// AmbiguousAddrError is returned when an address matches multiple files. type AmbiguousAddrError struct { Addr common.Address Matches []accounts.Account diff --git a/accounts/keystore/testdata/dupes/1 b/accounts/keystore/testdata/dupes/1 deleted file mode 100644 index a3868ec6d5..0000000000 --- a/accounts/keystore/testdata/dupes/1 +++ /dev/null @@ -1 +0,0 @@ -{"address":"f466859ead1932d743d622cb74fc058882e8648a","crypto":{"cipher":"aes-128-ctr","ciphertext":"cb664472deacb41a2e995fa7f96fe29ce744471deb8d146a0e43c7898c9ddd4d","cipherparams":{"iv":"dfd9ee70812add5f4b8f89d0811c9158"},"kdf":"scrypt","kdfparams":{"dklen":32,"n":8,"p":16,"r":8,"salt":"0d6769bf016d45c479213990d6a08d938469c4adad8a02ce507b4a4e7b7739f1"},"mac":"bac9af994b15a45dd39669fc66f9aa8a3b9dd8c22cb16e4d8d7ea089d0f1a1a9"},"id":"472e8b3d-afb6-45b5-8111-72c89895099a","version":3} \ No newline at end of file diff --git a/accounts/keystore/testdata/dupes/2 b/accounts/keystore/testdata/dupes/2 deleted file mode 100644 index a3868ec6d5..0000000000 --- a/accounts/keystore/testdata/dupes/2 +++ /dev/null @@ -1 +0,0 @@ -{"address":"f466859ead1932d743d622cb74fc058882e8648a","crypto":{"cipher":"aes-128-ctr","ciphertext":"cb664472deacb41a2e995fa7f96fe29ce744471deb8d146a0e43c7898c9ddd4d","cipherparams":{"iv":"dfd9ee70812add5f4b8f89d0811c9158"},"kdf":"scrypt","kdfparams":{"dklen":32,"n":8,"p":16,"r":8,"salt":"0d6769bf016d45c479213990d6a08d938469c4adad8a02ce507b4a4e7b7739f1"},"mac":"bac9af994b15a45dd39669fc66f9aa8a3b9dd8c22cb16e4d8d7ea089d0f1a1a9"},"id":"472e8b3d-afb6-45b5-8111-72c89895099a","version":3} \ No newline at end of file diff --git a/accounts/keystore/testdata/dupes/foo b/accounts/keystore/testdata/dupes/foo deleted file mode 100644 index c57060aea0..0000000000 --- a/accounts/keystore/testdata/dupes/foo +++ /dev/null @@ -1 +0,0 @@ -{"address":"7ef5a6135f1fd6a02593eedc869c6d41d934aef8","crypto":{"cipher":"aes-128-ctr","ciphertext":"1d0839166e7a15b9c1333fc865d69858b22df26815ccf601b28219b6192974e1","cipherparams":{"iv":"8df6caa7ff1b00c4e871f002cb7921ed"},"kdf":"scrypt","kdfparams":{"dklen":32,"n":8,"p":16,"r":8,"salt":"e5e6ef3f4ea695f496b643ebd3f75c0aa58ef4070e90c80c5d3fb0241bf1595c"},"mac":"6d16dfde774845e4585357f24bce530528bc69f4f84e1e22880d34fa45c273e5"},"id":"950077c7-71e3-4c44-a4a1-143919141ed4","version":3} \ No newline at end of file diff --git a/accounts/manager.go b/accounts/manager.go index cbe4f7c79d..ac21ecd985 100644 --- a/accounts/manager.go +++ b/accounts/manager.go @@ -29,12 +29,9 @@ import ( // the manager will buffer in its channel. const managerSubBufferSize = 50 -// Config contains the settings of the global account manager. -// -// TODO(rjl493456442, karalabe, holiman): Get rid of this when account management -// is removed in favor of Clef. +// Config is a legacy struct which is not used type Config struct { - InsecureUnlockAllowed bool // Whether account unlocking in insecure environment is allowed + InsecureUnlockAllowed bool // Unused legacy-parameter } // newBackendEvent lets the manager know it should @@ -47,7 +44,6 @@ type newBackendEvent struct { // Manager is an overarching account manager that can communicate with various // backends for signing transactions. type Manager struct { - config *Config // Global account manager configurations backends map[reflect.Type][]Backend // Index of backends currently registered updaters []event.Subscription // Wallet update subscriptions for all backends updates chan WalletEvent // Subscription sink for backend wallet changes @@ -78,7 +74,6 @@ func NewManager(config *Config, backends ...Backend) *Manager { } // Assemble the account manager and return am := &Manager{ - config: config, backends: make(map[reflect.Type][]Backend), updaters: subs, updates: updates, @@ -106,11 +101,6 @@ func (am *Manager) Close() error { return <-errc } -// Config returns the configuration of account manager. -func (am *Manager) Config() *Config { - return am.config -} - // AddBackend starts the tracking of an additional backend for wallet updates. // cmd/geth assumes once this func returns the backends have been already integrated. func (am *Manager) AddBackend(backend Backend) { diff --git a/accounts/usbwallet/ledger.go b/accounts/usbwallet/ledger.go index 81836b3717..2be6edd44f 100644 --- a/accounts/usbwallet/ledger.go +++ b/accounts/usbwallet/ledger.go @@ -338,8 +338,22 @@ func (w *ledgerDriver) ledgerSign(derivationPath []uint32, tx *types.Transaction return common.Address{}, nil, err } } else { - if txrlp, err = rlp.EncodeToBytes([]interface{}{tx.Nonce(), tx.GasPrice(), tx.Gas(), tx.To(), tx.Value(), tx.Data(), chainID, big.NewInt(0), big.NewInt(0)}); err != nil { - return common.Address{}, nil, err + if tx.Type() == types.DynamicFeeTxType { + if txrlp, err = rlp.EncodeToBytes([]interface{}{chainID, tx.Nonce(), tx.GasTipCap(), tx.GasFeeCap(), tx.Gas(), tx.To(), tx.Value(), tx.Data(), tx.AccessList()}); err != nil { + return common.Address{}, nil, err + } + // append type to transaction + txrlp = append([]byte{tx.Type()}, txrlp...) + } else if tx.Type() == types.AccessListTxType { + if txrlp, err = rlp.EncodeToBytes([]interface{}{chainID, tx.Nonce(), tx.GasPrice(), tx.Gas(), tx.To(), tx.Value(), tx.Data(), tx.AccessList()}); err != nil { + return common.Address{}, nil, err + } + // append type to transaction + txrlp = append([]byte{tx.Type()}, txrlp...) + } else if tx.Type() == types.LegacyTxType { + if txrlp, err = rlp.EncodeToBytes([]interface{}{tx.Nonce(), tx.GasPrice(), tx.Gas(), tx.To(), tx.Value(), tx.Data(), chainID, big.NewInt(0), big.NewInt(0)}); err != nil { + return common.Address{}, nil, err + } } } payload := append(path, txrlp...) @@ -353,7 +367,9 @@ func (w *ledgerDriver) ledgerSign(derivationPath []uint32, tx *types.Transaction // Chunk size selection to mitigate an underlying RLP deserialization issue on the ledger app. // https://github.com/LedgerHQ/app-ethereum/issues/409 chunk := 255 - for ; len(payload)%chunk <= ledgerEip155Size; chunk-- { + if tx.Type() == types.LegacyTxType { + for ; len(payload)%chunk <= ledgerEip155Size; chunk-- { + } } for len(payload) > 0 { @@ -381,8 +397,11 @@ func (w *ledgerDriver) ledgerSign(derivationPath []uint32, tx *types.Transaction if chainID == nil { signer = new(types.HomesteadSigner) } else { - signer = types.NewEIP155Signer(chainID) - signature[64] -= byte(chainID.Uint64()*2 + 35) + signer = types.LatestSignerForChainID(chainID) + // For non-legacy transactions, V is 0 or 1, no need to subtract here. + if tx.Type() == types.LegacyTxType { + signature[64] -= byte(chainID.Uint64()*2 + 35) + } } signed, err := tx.WithSignature(signer, signature) if err != nil { diff --git a/appveyor.yml b/appveyor.yml index 92369537cd..1543211edc 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -24,7 +24,9 @@ for: - image: Ubuntu build_script: - go run build/ci.go lint - - go run build/ci.go generate -verify + - go run build/ci.go check_tidy + - go run build/ci.go check_generate + - go run build/ci.go check_baddeps - go run build/ci.go install -dlgo test_script: - go run build/ci.go test -dlgo -short diff --git a/beacon/blsync/client.go b/beacon/blsync/client.go index 39a1c6ea76..3c93754d3d 100644 --- a/beacon/blsync/client.go +++ b/beacon/blsync/client.go @@ -17,25 +17,22 @@ package blsync import ( - "strings" - "github.com/ethereum/go-ethereum/beacon/light" "github.com/ethereum/go-ethereum/beacon/light/api" "github.com/ethereum/go-ethereum/beacon/light/request" "github.com/ethereum/go-ethereum/beacon/light/sync" + "github.com/ethereum/go-ethereum/beacon/params" "github.com/ethereum/go-ethereum/beacon/types" - "github.com/ethereum/go-ethereum/cmd/utils" "github.com/ethereum/go-ethereum/common/mclock" "github.com/ethereum/go-ethereum/ethdb/memorydb" "github.com/ethereum/go-ethereum/event" "github.com/ethereum/go-ethereum/rpc" - "github.com/urfave/cli/v2" ) type Client struct { urls []string customHeader map[string]string - chainConfig *lightClientConfig + config *params.ClientConfig scheduler *request.Scheduler blockSync *beaconBlockSync engineRPC *rpc.Client @@ -44,34 +41,18 @@ type Client struct { engineClient *engineClient } -func NewClient(ctx *cli.Context) *Client { - if !ctx.IsSet(utils.BeaconApiFlag.Name) { - utils.Fatalf("Beacon node light client API URL not specified") - } - var ( - chainConfig = makeChainConfig(ctx) - customHeader = make(map[string]string) - ) - for _, s := range ctx.StringSlice(utils.BeaconApiHeaderFlag.Name) { - kv := strings.Split(s, ":") - if len(kv) != 2 { - utils.Fatalf("Invalid custom API header entry: %s", s) - } - customHeader[strings.TrimSpace(kv[0])] = strings.TrimSpace(kv[1]) - } - +func NewClient(config params.ClientConfig) *Client { // create data structures var ( db = memorydb.New() - threshold = ctx.Int(utils.BeaconThresholdFlag.Name) - committeeChain = light.NewCommitteeChain(db, chainConfig.ChainConfig, threshold, !ctx.Bool(utils.BeaconNoFilterFlag.Name)) - headTracker = light.NewHeadTracker(committeeChain, threshold) + committeeChain = light.NewCommitteeChain(db, &config.ChainConfig, config.Threshold, !config.NoFilter) + headTracker = light.NewHeadTracker(committeeChain, config.Threshold) ) headSync := sync.NewHeadSync(headTracker, committeeChain) // set up scheduler and sync modules scheduler := request.NewScheduler() - checkpointInit := sync.NewCheckpointInit(committeeChain, chainConfig.Checkpoint) + checkpointInit := sync.NewCheckpointInit(committeeChain, config.Checkpoint) forwardSync := sync.NewForwardUpdateSync(committeeChain) beaconBlockSync := newBeaconBlockSync(headTracker) scheduler.RegisterTarget(headTracker) @@ -83,9 +64,9 @@ func NewClient(ctx *cli.Context) *Client { return &Client{ scheduler: scheduler, - urls: ctx.StringSlice(utils.BeaconApiFlag.Name), - customHeader: customHeader, - chainConfig: &chainConfig, + urls: config.Apis, + customHeader: config.CustomHeader, + config: &config, blockSync: beaconBlockSync, } } @@ -97,7 +78,7 @@ func (c *Client) SetEngineRPC(engine *rpc.Client) { func (c *Client) Start() error { headCh := make(chan types.ChainHeadEvent, 16) c.chainHeadSub = c.blockSync.SubscribeChainHead(headCh) - c.engineClient = startEngineClient(c.chainConfig, c.engineRPC, headCh) + c.engineClient = startEngineClient(c.config, c.engineRPC, headCh) c.scheduler.Start() for _, url := range c.urls { diff --git a/beacon/blsync/config.go b/beacon/blsync/config.go deleted file mode 100644 index efc44b47d1..0000000000 --- a/beacon/blsync/config.go +++ /dev/null @@ -1,114 +0,0 @@ -// Copyright 2022 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 blsync - -import ( - "github.com/ethereum/go-ethereum/beacon/types" - "github.com/ethereum/go-ethereum/cmd/utils" - "github.com/ethereum/go-ethereum/common" - "github.com/ethereum/go-ethereum/common/hexutil" - "github.com/urfave/cli/v2" -) - -// lightClientConfig contains beacon light client configuration -type lightClientConfig struct { - *types.ChainConfig - Checkpoint common.Hash -} - -var ( - MainnetConfig = lightClientConfig{ - ChainConfig: (&types.ChainConfig{ - GenesisValidatorsRoot: common.HexToHash("0x4b363db94e286120d76eb905340fdd4e54bfe9f06bf33ff6cf5ad27f511bfe95"), - GenesisTime: 1606824023, - }). - AddFork("GENESIS", 0, []byte{0, 0, 0, 0}). - AddFork("ALTAIR", 74240, []byte{1, 0, 0, 0}). - AddFork("BELLATRIX", 144896, []byte{2, 0, 0, 0}). - AddFork("CAPELLA", 194048, []byte{3, 0, 0, 0}). - AddFork("DENEB", 269568, []byte{4, 0, 0, 0}), - Checkpoint: common.HexToHash("0x388be41594ec7d6a6894f18c73f3469f07e2c19a803de4755d335817ed8e2e5a"), - } - - SepoliaConfig = lightClientConfig{ - ChainConfig: (&types.ChainConfig{ - GenesisValidatorsRoot: common.HexToHash("0xd8ea171f3c94aea21ebc42a1ed61052acf3f9209c00e4efbaaddac09ed9b8078"), - GenesisTime: 1655733600, - }). - AddFork("GENESIS", 0, []byte{144, 0, 0, 105}). - AddFork("ALTAIR", 50, []byte{144, 0, 0, 112}). - AddFork("BELLATRIX", 100, []byte{144, 0, 0, 113}). - AddFork("CAPELLA", 56832, []byte{144, 0, 0, 114}). - AddFork("DENEB", 132608, []byte{144, 0, 0, 115}), - Checkpoint: common.HexToHash("0x1005a6d9175e96bfbce4d35b80f468e9bff0b674e1e861d16e09e10005a58e81"), - } -) - -func makeChainConfig(ctx *cli.Context) lightClientConfig { - var config lightClientConfig - customConfig := ctx.IsSet(utils.BeaconConfigFlag.Name) - utils.CheckExclusive(ctx, utils.MainnetFlag, utils.SepoliaFlag, utils.BeaconConfigFlag) - switch { - case ctx.Bool(utils.MainnetFlag.Name): - config = MainnetConfig - case ctx.Bool(utils.SepoliaFlag.Name): - config = SepoliaConfig - default: - if !customConfig { - config = MainnetConfig - } - } - // Genesis root and time should always be specified together with custom chain config - if customConfig { - if !ctx.IsSet(utils.BeaconGenesisRootFlag.Name) { - utils.Fatalf("Custom beacon chain config is specified but genesis root is missing") - } - if !ctx.IsSet(utils.BeaconGenesisTimeFlag.Name) { - utils.Fatalf("Custom beacon chain config is specified but genesis time is missing") - } - if !ctx.IsSet(utils.BeaconCheckpointFlag.Name) { - utils.Fatalf("Custom beacon chain config is specified but checkpoint is missing") - } - config.ChainConfig = &types.ChainConfig{ - GenesisTime: ctx.Uint64(utils.BeaconGenesisTimeFlag.Name), - } - if c, err := hexutil.Decode(ctx.String(utils.BeaconGenesisRootFlag.Name)); err == nil && len(c) <= 32 { - copy(config.GenesisValidatorsRoot[:len(c)], c) - } else { - utils.Fatalf("Invalid hex string", "beacon.genesis.gvroot", ctx.String(utils.BeaconGenesisRootFlag.Name), "error", err) - } - if err := config.ChainConfig.LoadForks(ctx.String(utils.BeaconConfigFlag.Name)); err != nil { - utils.Fatalf("Could not load beacon chain config file", "file name", ctx.String(utils.BeaconConfigFlag.Name), "error", err) - } - } else { - if ctx.IsSet(utils.BeaconGenesisRootFlag.Name) { - utils.Fatalf("Genesis root is specified but custom beacon chain config is missing") - } - if ctx.IsSet(utils.BeaconGenesisTimeFlag.Name) { - utils.Fatalf("Genesis time is specified but custom beacon chain config is missing") - } - } - // Checkpoint is required with custom chain config and is optional with pre-defined config - if ctx.IsSet(utils.BeaconCheckpointFlag.Name) { - if c, err := hexutil.Decode(ctx.String(utils.BeaconCheckpointFlag.Name)); err == nil && len(c) <= 32 { - copy(config.Checkpoint[:len(c)], c) - } else { - utils.Fatalf("Invalid hex string", "beacon.checkpoint", ctx.String(utils.BeaconCheckpointFlag.Name), "error", err) - } - } - return config -} diff --git a/beacon/blsync/engineclient.go b/beacon/blsync/engineclient.go index fb8f77f32b..c6569fdbde 100644 --- a/beacon/blsync/engineclient.go +++ b/beacon/blsync/engineclient.go @@ -23,6 +23,7 @@ import ( "time" "github.com/ethereum/go-ethereum/beacon/engine" + "github.com/ethereum/go-ethereum/beacon/params" "github.com/ethereum/go-ethereum/beacon/types" "github.com/ethereum/go-ethereum/common" ctypes "github.com/ethereum/go-ethereum/core/types" @@ -31,14 +32,14 @@ import ( ) type engineClient struct { - config *lightClientConfig + config *params.ClientConfig rpc *rpc.Client rootCtx context.Context cancelRoot context.CancelFunc wg sync.WaitGroup } -func startEngineClient(config *lightClientConfig, rpc *rpc.Client, headCh <-chan types.ChainHeadEvent) *engineClient { +func startEngineClient(config *params.ClientConfig, rpc *rpc.Client, headCh <-chan types.ChainHeadEvent) *engineClient { ctx, cancel := context.WithCancel(context.Background()) ec := &engineClient{ config: config, diff --git a/beacon/engine/gen_epe.go b/beacon/engine/gen_epe.go index 039884e842..deada06166 100644 --- a/beacon/engine/gen_epe.go +++ b/beacon/engine/gen_epe.go @@ -20,7 +20,7 @@ func (e ExecutionPayloadEnvelope) MarshalJSON() ([]byte, error) { BlobsBundle *BlobsBundleV1 `json:"blobsBundle"` Requests []hexutil.Bytes `json:"executionRequests"` Override bool `json:"shouldOverrideBuilder"` - Witness *hexutil.Bytes `json:"witness"` + Witness *hexutil.Bytes `json:"witness,omitempty"` } var enc ExecutionPayloadEnvelope enc.ExecutionPayload = e.ExecutionPayload @@ -45,7 +45,7 @@ func (e *ExecutionPayloadEnvelope) UnmarshalJSON(input []byte) error { BlobsBundle *BlobsBundleV1 `json:"blobsBundle"` Requests []hexutil.Bytes `json:"executionRequests"` Override *bool `json:"shouldOverrideBuilder"` - Witness *hexutil.Bytes `json:"witness"` + Witness *hexutil.Bytes `json:"witness,omitempty"` } var dec ExecutionPayloadEnvelope if err := json.Unmarshal(input, &dec); err != nil { diff --git a/beacon/engine/types.go b/beacon/engine/types.go index e80b6e6633..984090ef89 100644 --- a/beacon/engine/types.go +++ b/beacon/engine/types.go @@ -109,7 +109,7 @@ type ExecutionPayloadEnvelope struct { BlobsBundle *BlobsBundleV1 `json:"blobsBundle"` Requests [][]byte `json:"executionRequests"` Override bool `json:"shouldOverrideBuilder"` - Witness *hexutil.Bytes `json:"witness"` + Witness *hexutil.Bytes `json:"witness,omitempty"` } type BlobsBundleV1 struct { @@ -118,6 +118,11 @@ type BlobsBundleV1 struct { Blobs []hexutil.Bytes `json:"blobs"` } +type BlobAndProofV1 struct { + Blob hexutil.Bytes `json:"blob"` + Proof hexutil.Bytes `json:"proof"` +} + // JSON type overrides for ExecutionPayloadEnvelope. type executionPayloadEnvelopeMarshaling struct { BlockValue *hexutil.Big @@ -260,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 } @@ -338,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/beacon/light/committee_chain.go b/beacon/light/committee_chain.go index a8d032bb65..4fa87785c0 100644 --- a/beacon/light/committee_chain.go +++ b/beacon/light/committee_chain.go @@ -76,34 +76,32 @@ type CommitteeChain struct { unixNano func() int64 // system clock (simulated clock in tests) sigVerifier committeeSigVerifier // BLS sig verifier (dummy verifier in tests) - config *types.ChainConfig - signerThreshold int + config *params.ChainConfig minimumUpdateScore types.UpdateScore enforceTime bool // enforceTime specifies whether the age of a signed header should be checked } // NewCommitteeChain creates a new CommitteeChain. -func NewCommitteeChain(db ethdb.KeyValueStore, config *types.ChainConfig, signerThreshold int, enforceTime bool) *CommitteeChain { +func NewCommitteeChain(db ethdb.KeyValueStore, config *params.ChainConfig, signerThreshold int, enforceTime bool) *CommitteeChain { return newCommitteeChain(db, config, signerThreshold, enforceTime, blsVerifier{}, &mclock.System{}, func() int64 { return time.Now().UnixNano() }) } // NewTestCommitteeChain creates a new CommitteeChain for testing. -func NewTestCommitteeChain(db ethdb.KeyValueStore, config *types.ChainConfig, signerThreshold int, enforceTime bool, clock *mclock.Simulated) *CommitteeChain { +func NewTestCommitteeChain(db ethdb.KeyValueStore, config *params.ChainConfig, signerThreshold int, enforceTime bool, clock *mclock.Simulated) *CommitteeChain { return newCommitteeChain(db, config, signerThreshold, enforceTime, dummyVerifier{}, clock, func() int64 { return int64(clock.Now()) }) } // newCommitteeChain creates a new CommitteeChain with the option of replacing the // clock source and signature verification for testing purposes. -func newCommitteeChain(db ethdb.KeyValueStore, config *types.ChainConfig, signerThreshold int, enforceTime bool, sigVerifier committeeSigVerifier, clock mclock.Clock, unixNano func() int64) *CommitteeChain { +func newCommitteeChain(db ethdb.KeyValueStore, config *params.ChainConfig, signerThreshold int, enforceTime bool, sigVerifier committeeSigVerifier, clock mclock.Clock, unixNano func() int64) *CommitteeChain { s := &CommitteeChain{ - committeeCache: lru.NewCache[uint64, syncCommittee](10), - db: db, - sigVerifier: sigVerifier, - clock: clock, - unixNano: unixNano, - config: config, - signerThreshold: signerThreshold, - enforceTime: enforceTime, + committeeCache: lru.NewCache[uint64, syncCommittee](10), + db: db, + sigVerifier: sigVerifier, + clock: clock, + unixNano: unixNano, + config: config, + enforceTime: enforceTime, minimumUpdateScore: types.UpdateScore{ SignerCount: uint32(signerThreshold), SubPeriodIndex: params.SyncPeriodLength / 16, @@ -507,7 +505,7 @@ func (s *CommitteeChain) verifySignedHeader(head types.SignedHeader) (bool, time if committee == nil { return false, age, nil } - if signingRoot, err := s.config.Forks.SigningRoot(head.Header); err == nil { + if signingRoot, err := s.config.Forks.SigningRoot(head.Header.Epoch(), head.Header.Hash()); err == nil { return s.sigVerifier.verifySignature(committee, signingRoot, &head.Signature), age, nil } return false, age, nil diff --git a/beacon/light/committee_chain_test.go b/beacon/light/committee_chain_test.go index 57b6d7175c..17ba135905 100644 --- a/beacon/light/committee_chain_test.go +++ b/beacon/light/committee_chain_test.go @@ -31,15 +31,15 @@ var ( testGenesis = newTestGenesis() testGenesis2 = newTestGenesis() - tfBase = newTestForks(testGenesis, types.Forks{ - &types.Fork{Epoch: 0, Version: []byte{0}}, + tfBase = newTestForks(testGenesis, params.Forks{ + ¶ms.Fork{Epoch: 0, Version: []byte{0}}, }) - tfAlternative = newTestForks(testGenesis, types.Forks{ - &types.Fork{Epoch: 0, Version: []byte{0}}, - &types.Fork{Epoch: 0x700, Version: []byte{1}}, + tfAlternative = newTestForks(testGenesis, params.Forks{ + ¶ms.Fork{Epoch: 0, Version: []byte{0}}, + ¶ms.Fork{Epoch: 0x700, Version: []byte{1}}, }) - tfAnotherGenesis = newTestForks(testGenesis2, types.Forks{ - &types.Fork{Epoch: 0, Version: []byte{0}}, + tfAnotherGenesis = newTestForks(testGenesis2, params.Forks{ + ¶ms.Fork{Epoch: 0, Version: []byte{0}}, }) tcBase = newTestCommitteeChain(nil, tfBase, true, 0, 10, 400, false) @@ -226,13 +226,13 @@ type committeeChainTest struct { t *testing.T db *memorydb.Database clock *mclock.Simulated - config types.ChainConfig + config params.ChainConfig signerThreshold int enforceTime bool chain *CommitteeChain } -func newCommitteeChainTest(t *testing.T, config types.ChainConfig, signerThreshold int, enforceTime bool) *committeeChainTest { +func newCommitteeChainTest(t *testing.T, config params.ChainConfig, signerThreshold int, enforceTime bool) *committeeChainTest { c := &committeeChainTest{ t: t, db: memorydb.New(), @@ -298,20 +298,20 @@ func (c *committeeChainTest) verifyRange(tc *testCommitteeChain, begin, end uint c.verifySignedHeader(tc, float64(end)+1.5, false) } -func newTestGenesis() types.ChainConfig { - var config types.ChainConfig +func newTestGenesis() params.ChainConfig { + var config params.ChainConfig rand.Read(config.GenesisValidatorsRoot[:]) return config } -func newTestForks(config types.ChainConfig, forks types.Forks) types.ChainConfig { +func newTestForks(config params.ChainConfig, forks params.Forks) params.ChainConfig { for _, fork := range forks { config.AddFork(fork.Name, fork.Epoch, fork.Version) } return config } -func newTestCommitteeChain(parent *testCommitteeChain, config types.ChainConfig, newCommittees bool, begin, end int, signerCount int, finalizedHeader bool) *testCommitteeChain { +func newTestCommitteeChain(parent *testCommitteeChain, config params.ChainConfig, newCommittees bool, begin, end int, signerCount int, finalizedHeader bool) *testCommitteeChain { tc := &testCommitteeChain{ config: config, } @@ -337,7 +337,7 @@ type testPeriod struct { type testCommitteeChain struct { periods []testPeriod - config types.ChainConfig + config params.ChainConfig } func (tc *testCommitteeChain) fillCommittees(begin, end int) { diff --git a/beacon/light/test_helpers.go b/beacon/light/test_helpers.go index f537d963a6..7bd19ca0ad 100644 --- a/beacon/light/test_helpers.go +++ b/beacon/light/test_helpers.go @@ -33,7 +33,7 @@ func GenerateTestCommittee() *types.SerializedSyncCommittee { return s } -func GenerateTestUpdate(config *types.ChainConfig, period uint64, committee, nextCommittee *types.SerializedSyncCommittee, signerCount int, finalizedHeader bool) *types.LightClientUpdate { +func GenerateTestUpdate(config *params.ChainConfig, period uint64, committee, nextCommittee *types.SerializedSyncCommittee, signerCount int, finalizedHeader bool) *types.LightClientUpdate { update := new(types.LightClientUpdate) update.NextSyncCommitteeRoot = nextCommittee.Root() var attestedHeader types.Header @@ -48,9 +48,9 @@ func GenerateTestUpdate(config *types.ChainConfig, period uint64, committee, nex return update } -func GenerateTestSignedHeader(header types.Header, config *types.ChainConfig, committee *types.SerializedSyncCommittee, signatureSlot uint64, signerCount int) types.SignedHeader { +func GenerateTestSignedHeader(header types.Header, config *params.ChainConfig, committee *types.SerializedSyncCommittee, signatureSlot uint64, signerCount int) types.SignedHeader { bitmask := makeBitmask(signerCount) - signingRoot, _ := config.Forks.SigningRoot(header) + signingRoot, _ := config.Forks.SigningRoot(header.Epoch(), header.Hash()) c, _ := dummyVerifier{}.deserializeSyncCommittee(committee) return types.SignedHeader{ Header: header, diff --git a/beacon/types/config.go b/beacon/params/config.go similarity index 94% rename from beacon/types/config.go rename to beacon/params/config.go index 7706e85f6c..be2a40f171 100644 --- a/beacon/types/config.go +++ b/beacon/params/config.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 types +package params import ( "crypto/sha256" @@ -39,81 +39,13 @@ const syncCommitteeDomain = 7 var knownForks = []string{"GENESIS", "ALTAIR", "BELLATRIX", "CAPELLA", "DENEB"} -// Fork describes a single beacon chain fork and also stores the calculated -// signature domain used after this fork. -type Fork struct { - // Name of the fork in the chain config (config.yaml) file - Name string - - // Epoch when given fork version is activated - Epoch uint64 - - // Fork version, see https://github.com/ethereum/consensus-specs/blob/dev/specs/phase0/beacon-chain.md#custom-types - Version []byte - - // index in list of known forks or MaxInt if unknown - knownIndex int - - // calculated by computeDomain, based on fork version and genesis validators root - domain merkle.Value -} - -// computeDomain returns the signature domain based on the given fork version -// and genesis validator set root. -func (f *Fork) computeDomain(genesisValidatorsRoot common.Hash) { - var ( - hasher = sha256.New() - forkVersion32 merkle.Value - forkDataRoot merkle.Value - ) - copy(forkVersion32[:], f.Version) - hasher.Write(forkVersion32[:]) - hasher.Write(genesisValidatorsRoot[:]) - hasher.Sum(forkDataRoot[:0]) - - f.domain[0] = syncCommitteeDomain - copy(f.domain[4:], forkDataRoot[:28]) -} - -// Forks is the list of all beacon chain forks in the chain configuration. -type Forks []*Fork - -// domain returns the signature domain for the given epoch (assumes that domains -// have already been calculated). -func (f Forks) domain(epoch uint64) (merkle.Value, error) { - for i := len(f) - 1; i >= 0; i-- { - if epoch >= f[i].Epoch { - return f[i].domain, nil - } - } - return merkle.Value{}, fmt.Errorf("unknown fork for epoch %d", epoch) -} - -// SigningRoot calculates the signing root of the given header. -func (f Forks) SigningRoot(header Header) (common.Hash, error) { - domain, err := f.domain(header.Epoch()) - if err != nil { - return common.Hash{}, err - } - var ( - signingRoot common.Hash - headerHash = header.Hash() - hasher = sha256.New() - ) - hasher.Write(headerHash[:]) - hasher.Write(domain[:]) - hasher.Sum(signingRoot[:0]) - - return signingRoot, nil -} - -func (f Forks) Len() int { return len(f) } -func (f Forks) Swap(i, j int) { f[i], f[j] = f[j], f[i] } -func (f Forks) Less(i, j int) bool { - if f[i].Epoch != f[j].Epoch { - return f[i].Epoch < f[j].Epoch - } - return f[i].knownIndex < f[j].knownIndex +// ClientConfig contains beacon light client configuration. +type ClientConfig struct { + ChainConfig + Apis []string + CustomHeader map[string]string + Threshold int + NoFilter bool } // ChainConfig contains the beacon chain configuration. @@ -121,6 +53,7 @@ type ChainConfig struct { GenesisTime uint64 // Unix timestamp of slot 0 GenesisValidatorsRoot common.Hash // Root hash of the genesis validator set, used for signature domain calculation Forks Forks + Checkpoint common.Hash } // ForkAtEpoch returns the latest active fork at the given epoch. @@ -202,3 +135,79 @@ func (c *ChainConfig) LoadForks(path string) error { } return nil } + +// Fork describes a single beacon chain fork and also stores the calculated +// signature domain used after this fork. +type Fork struct { + // Name of the fork in the chain config (config.yaml) file + Name string + + // Epoch when given fork version is activated + Epoch uint64 + + // Fork version, see https://github.com/ethereum/consensus-specs/blob/dev/specs/phase0/beacon-chain.md#custom-types + Version []byte + + // index in list of known forks or MaxInt if unknown + knownIndex int + + // calculated by computeDomain, based on fork version and genesis validators root + domain merkle.Value +} + +// computeDomain returns the signature domain based on the given fork version +// and genesis validator set root. +func (f *Fork) computeDomain(genesisValidatorsRoot common.Hash) { + var ( + hasher = sha256.New() + forkVersion32 merkle.Value + forkDataRoot merkle.Value + ) + copy(forkVersion32[:], f.Version) + hasher.Write(forkVersion32[:]) + hasher.Write(genesisValidatorsRoot[:]) + hasher.Sum(forkDataRoot[:0]) + + f.domain[0] = syncCommitteeDomain + copy(f.domain[4:], forkDataRoot[:28]) +} + +// Forks is the list of all beacon chain forks in the chain configuration. +type Forks []*Fork + +// domain returns the signature domain for the given epoch (assumes that domains +// have already been calculated). +func (f Forks) domain(epoch uint64) (merkle.Value, error) { + for i := len(f) - 1; i >= 0; i-- { + if epoch >= f[i].Epoch { + return f[i].domain, nil + } + } + return merkle.Value{}, fmt.Errorf("unknown fork for epoch %d", epoch) +} + +// SigningRoot calculates the signing root of the given header. +func (f Forks) SigningRoot(epoch uint64, root common.Hash) (common.Hash, error) { + domain, err := f.domain(epoch) + if err != nil { + return common.Hash{}, err + } + var ( + signingRoot common.Hash + hasher = sha256.New() + ) + hasher.Write(root[:]) + hasher.Write(domain[:]) + hasher.Sum(signingRoot[:0]) + + return signingRoot, nil +} + +func (f Forks) Len() int { return len(f) } +func (f Forks) Swap(i, j int) { f[i], f[j] = f[j], f[i] } +func (f Forks) Less(i, j int) bool { + if f[i].Epoch != f[j].Epoch { + return f[i].Epoch < f[j].Epoch + } + return f[i].knownIndex < f[j].knownIndex +} diff --git a/beacon/params/networks.go b/beacon/params/networks.go new file mode 100644 index 0000000000..5b00b27953 --- /dev/null +++ b/beacon/params/networks.go @@ -0,0 +1,56 @@ +// Copyright 2016 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 params + +import ( + "github.com/ethereum/go-ethereum/common" +) + +var ( + MainnetLightConfig = (&ChainConfig{ + GenesisValidatorsRoot: common.HexToHash("0x4b363db94e286120d76eb905340fdd4e54bfe9f06bf33ff6cf5ad27f511bfe95"), + GenesisTime: 1606824023, + Checkpoint: common.HexToHash("0x6509b691f4de4f7b083f2784938fd52f0e131675432b3fd85ea549af9aebd3d0"), + }). + AddFork("GENESIS", 0, []byte{0, 0, 0, 0}). + AddFork("ALTAIR", 74240, []byte{1, 0, 0, 0}). + AddFork("BELLATRIX", 144896, []byte{2, 0, 0, 0}). + AddFork("CAPELLA", 194048, []byte{3, 0, 0, 0}). + AddFork("DENEB", 269568, []byte{4, 0, 0, 0}) + + SepoliaLightConfig = (&ChainConfig{ + GenesisValidatorsRoot: common.HexToHash("0xd8ea171f3c94aea21ebc42a1ed61052acf3f9209c00e4efbaaddac09ed9b8078"), + GenesisTime: 1655733600, + Checkpoint: common.HexToHash("0x456e85f5608afab3465a0580bff8572255f6d97af0c5f939e3f7536b5edb2d3f"), + }). + AddFork("GENESIS", 0, []byte{144, 0, 0, 105}). + AddFork("ALTAIR", 50, []byte{144, 0, 0, 112}). + AddFork("BELLATRIX", 100, []byte{144, 0, 0, 113}). + AddFork("CAPELLA", 56832, []byte{144, 0, 0, 114}). + AddFork("DENEB", 132608, []byte{144, 0, 0, 115}) + + HoleskyLightConfig = (&ChainConfig{ + GenesisValidatorsRoot: common.HexToHash("0x9143aa7c615a7f7115e2b6aac319c03529df8242ae705fba9df39b79c59fa8b1"), + GenesisTime: 1695902400, + Checkpoint: common.HexToHash("0x6456a1317f54d4b4f2cb5bc9d153b5af0988fe767ef0609f0236cf29030bcff7"), + }). + AddFork("GENESIS", 0, []byte{1, 1, 112, 0}). + AddFork("ALTAIR", 0, []byte{2, 1, 112, 0}). + AddFork("BELLATRIX", 0, []byte{3, 1, 112, 0}). + AddFork("CAPELLA", 256, []byte{4, 1, 112, 0}). + AddFork("DENEB", 29696, []byte{5, 1, 112, 0}) +) diff --git a/build/checksums.txt b/build/checksums.txt index 3da5d00dee..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.2 +# version:golang 1.23.4 # https://go.dev/dl/ -36930162a93df417d90bd22c6e14daff4705baac2b02418edda671cdfa9cd07f go1.23.2.src.tar.gz -025d77f1780906142023a364c31a572afd7d56d3a3be1e4e562e367ca88d3267 go1.23.2.freebsd-amd64.tar.gz -0d50bade977b84e173cb350946087f5de8c75f8df19456c3b60c5d58e186089d go1.23.2.windows-arm64.zip -0edd985dbd6de64d9c88dbc8835bae21203c58444bf26fce0739cbec4eb1b610 go1.23.2.windows-arm64.msi -2283d12dfe7c8c8a46a41bbf7d11fe007434e7590cd1b89e221e478640b7ee3a go1.23.2.linux-mips64le.tar.gz -2293c5c3ffc595418308b4059ce214b99f0383cba83232e47a1a8c3b710c24e8 go1.23.2.linux-loong64.tar.gz -23b93144e754bbcf5eda700e9decbdbd44d29ceedb1bf1de75f95e8a6ea986bb go1.23.2.openbsd-arm64.tar.gz -2734a5b54905cea45f136c28249e626d0241b865b0637fa1db64bf533d9d843e go1.23.2.netbsd-amd64.tar.gz -28af3c40687afdda6b33b300833b6d662716cc2d624fb9fd61a49bdad44cd869 go1.23.2.freebsd-arm.tar.gz -367d522b47c7ce7761a671efcb8b12c8af8f509db1cd6160c91f410ef3201987 go1.23.2.windows-arm.msi -36b7228bae235eee6c8193f5a956e1a9a17874955affb86b3564709b0fab5874 go1.23.2.linux-mipsle.tar.gz -3bd1130a08195d23960b154d2e6eaa80ac7325ebd9d01d74c58b6d12580e6b12 go1.23.2.linux-mips.tar.gz -3bf66879b38a233c5cbb5d2eb982004117f05d6bf06279e886e087d7c504427d go1.23.2.openbsd-riscv64.tar.gz -3e80b943d70c7e1633822b42c1aa7234e61da14f13ff8efff7ee6e1347f37648 go1.23.2.netbsd-arm64.tar.gz -40c0b61971a1a74fd4566c536f682c9d4976fa71d40d9daabc875c06113d0fee go1.23.2.darwin-amd64.pkg -445c0ef19d8692283f4c3a92052cc0568f5a048f4e546105f58e991d4aea54f5 go1.23.2.darwin-amd64.tar.gz -542d3c1705f1c6a1c5a80d5dc62e2e45171af291e755d591c5e6531ef63b454e go1.23.2.linux-amd64.tar.gz -560aff7fe1eeadc32248db35ed5c0a81e190d171b6ecec404cf46d808c13e92f go1.23.2.aix-ppc64.tar.gz -5611cd648f5100b73a7d6fd85589a481af18fdbaf9c153a92de9a8e39a6e061f go1.23.2.darwin-arm64.pkg -695aac64532da8d9a243601ffa0411cd763be891fcf7fd2e857eea4ab10b8bcc go1.23.2.plan9-386.tar.gz -69b31edcd3d4f7d8bbf9aee2b25cafba30b444ef19bc7a033e15026f7d0cc5c2 go1.23.2.netbsd-arm.tar.gz -6ffa4ac1f4368a3121a032917577a4e0a3feaf696c3e98f213b74ac04c318bc4 go1.23.2.plan9-arm.tar.gz -72a6def70300cc804c70073d8b579603d9b39b39b02b3b5d340968d9e7e0e9d4 go1.23.2.windows-386.msi -791ca685ee5ca0f6fe849dc078145cb1323d0ea9dd308e9cca9ba2e7186dbb3d go1.23.2.linux-ppc64.tar.gz -86b5de91fdf7bd9b52c77c62f8762518cf3fc256fe912af9bbff1d073054aa5b go1.23.2.plan9-amd64.tar.gz -8734c7cd464a0620f6605bd3f9256bed062f262d0d58e4f45099c329a08ed966 go1.23.2.openbsd-amd64.tar.gz -980ceb889915695d94b166ca1300250dba76fa37a2d41eca2c5e7727dcb4fb7f go1.23.2.openbsd-arm.tar.gz -a0cf25f236a0fa0a465816fe7f5c930f3b0b90c5c247b09c43a6adeff654e6ae go1.23.2.linux-mips64.tar.gz -a13cc0d621af4f35afd90b886c60b1bf66f771939d226dc36fa61a337d90eb30 go1.23.2.openbsd-ppc64.tar.gz -b29ff163b34cb4943c521fcfc1d956eaa6286561089042051a3fab22e79e9283 go1.23.2.windows-arm.zip -bc28fe3002cd65cec65d0e4f6000584dacb8c71bfaff8801dfb532855ca42513 go1.23.2.windows-amd64.zip -c164ce7d894b10fd861d7d7b96f1dbea3f993663d9f0c30bc4f8ae3915db8b0c go1.23.2.linux-ppc64le.tar.gz -c4ae1087dce4daf45a837f5fca36ac0e29a02ada9addf857f1c426e60bce6f21 go1.23.2.netbsd-386.tar.gz -c80cbc5e66d6fb8b0c3300b0dda1fe925c429e199954d3327da2933d9870b041 go1.23.2.windows-amd64.msi -cb1ed4410f68d8be1156cee0a74fcfbdcd9bca377c83db3a9e1b07eebc6d71ef go1.23.2.linux-386.tar.gz -d1fde255843fec1f7f0611d468effd98e1f4309f589ac13037db07b032f9da35 go1.23.2.openbsd-386.tar.gz -d47e40366cd6c6b6ee14b811554cd7dde0351309f4a8a4569ec5ba2bd7689437 go1.23.2.illumos-amd64.tar.gz -d87031194fe3e01abdcaf3c7302148ade97a7add6eac3fec26765bcb3207b80f go1.23.2.darwin-arm64.tar.gz -de1f94d7dd3548ba3036de1ea97eb8243881c22a88fcc04cc08c704ded769e02 go1.23.2.linux-s390x.tar.gz -e3286bdde186077e65e961cbe18874d42a461e5b9c472c26572b8d4a98d15c40 go1.23.2.linux-armv6l.tar.gz -e4d9a1319dfdaa827407855e406c43e85c878a1f93f4f3984c85dce969c8bf70 go1.23.2.freebsd-386.tar.gz -ea8ab49c5c04c9f94a3f4894d1b030fbce8d10413905fa399f6c39c0a44d5556 go1.23.2.linux-riscv64.tar.gz -eaa3bc377badbdcae144633f8b29bf2680475b72dcd4c135343d3bdc0ba7671e go1.23.2.windows-386.zip -f11b9b4d4a0679909202fc5e88093d6ff720a8a417bfe6a34d502c3862367039 go1.23.2.freebsd-riscv64.tar.gz -f163b99b03e4bbc64cd30363f1694a08fcd44094415db1f092f13f9d1bb7c28e go1.23.2.dragonfly-amd64.tar.gz -f45af3e1434175ff85620a74c07fb41d6844655f1f2cd2389c5fca6de000f58c go1.23.2.freebsd-arm64.tar.gz -f626cdd92fc21a88b31c1251f419c17782933a42903db87a174ce74eeecc66a9 go1.23.2.linux-arm64.tar.gz -fa70d39ddeb6b55241a30b48d7af4e681c6a7d7104e8326c3bc1b12a75e091cc go1.23.2.solaris-amd64.tar.gz +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 1990f2a63d..fd51b90d17 100644 --- a/build/ci.go +++ b/build/ci.go @@ -24,9 +24,14 @@ Usage: go run build/ci.go Available commands are: - install [ -arch architecture ] [ -cc compiler ] [ packages... ] -- builds packages and executables - test [ -coverage ] [ packages... ] -- runs the tests - lint -- runs certain pre-selected linters + lint -- runs certain pre-selected linters + check_tidy -- verifies that everything is 'go mod tidy'-ed + check_generate -- verifies that everything is 'go generate'-ed + check_baddeps -- verifies that certain dependencies are avoided + + install [ -arch architecture ] [ -cc compiler ] [ packages... ] -- builds packages and executables + test [ -coverage ] [ packages... ] -- runs the tests + archive [ -arch architecture ] [ -type zip|tar ] [ -signer key-envvar ] [ -signify key-envvar ] [ -upload dest ] -- archives build artifacts importkeys -- imports signing keys from env debsrc [ -signer key-id ] [ -upload dest ] -- creates a debian source package @@ -39,25 +44,23 @@ package main import ( "bytes" - "crypto/sha256" "encoding/base64" "flag" "fmt" - "io" "log" "os" "os/exec" "path" "path/filepath" "runtime" + "slices" "strings" "time" "github.com/cespare/cp" - "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/crypto/signify" "github.com/ethereum/go-ethereum/internal/build" - "github.com/ethereum/go-ethereum/params" + "github.com/ethereum/go-ethereum/internal/version" ) var ( @@ -71,7 +74,6 @@ var ( allToolsArchiveFiles = []string{ "COPYING", executablePath("abigen"), - executablePath("bootnode"), executablePath("evm"), executablePath("geth"), executablePath("rlpdump"), @@ -84,10 +86,6 @@ var ( BinaryName: "abigen", Description: "Source code generator to convert Ethereum contract definitions into easy to use, compile-time type-safe Go packages.", }, - { - BinaryName: "bootnode", - Description: "Ethereum bootnode.", - }, { BinaryName: "evm", Description: "Developer utility version of the EVM (Ethereum Virtual Machine) that is capable of running bytecode snippets within a configurable environment and execution mode.", @@ -109,7 +107,7 @@ var ( // A debian package is created for all executables listed here. debEthereum = debPackage{ Name: "ethereum", - Version: params.Version, + Version: version.Semantic, Executables: debExecutables, } @@ -125,7 +123,7 @@ var ( "focal", // 20.04, EOL: 04/2030 "jammy", // 22.04, EOL: 04/2032 "noble", // 24.04, EOL: 04/2034 - "oracular", // 24.10, EOL: 07/2025 + "oracular", // 24.10, EOL: 07/2025 } // This is where the tests should be unpacked. @@ -144,7 +142,7 @@ func executablePath(name string) string { func main() { log.SetFlags(log.Lshortfile) - if !common.FileExist(filepath.Join("build", "ci.go")) { + if !build.FileExist(filepath.Join("build", "ci.go")) { log.Fatal("this script must be run from the root of the repository") } if len(os.Args) < 2 { @@ -157,6 +155,12 @@ func main() { doTest(os.Args[2:]) case "lint": doLint(os.Args[2:]) + case "check_tidy": + doCheckTidy() + case "check_generate": + doCheckGenerate() + case "check_baddeps": + doCheckBadDeps() case "archive": doArchive(os.Args[2:]) case "dockerx": @@ -169,8 +173,6 @@ func main() { doPurge(os.Args[2:]) case "sanitycheck": doSanityCheck() - case "generate": - doGenerate() default: log.Fatal("unknown command ", os.Args[1]) } @@ -205,12 +207,6 @@ func doInstall(cmdline []string) { // Configure the build. gobuild := tc.Go("build", buildFlags(env, *staticlink, buildTags)...) - // arm64 CI builders are memory-constrained and can't handle concurrent builds, - // better disable it. This check isn't the best, it should probably - // check for something in env instead. - if env.CI && runtime.GOARCH == "arm64" { - gobuild.Args = append(gobuild.Args, "-p", "1") - } // We use -trimpath to avoid leaking local paths into the built executables. gobuild.Args = append(gobuild.Args, "-trimpath") @@ -226,8 +222,7 @@ func doInstall(cmdline []string) { // Do the build! for _, pkg := range packages { - args := make([]string, len(gobuild.Args)) - copy(args, gobuild.Args) + args := slices.Clone(gobuild.Args) args = append(args, "-o", executablePath(path.Base(pkg))) args = append(args, pkg) build.MustRun(&exec.Cmd{Path: gobuild.Path, Args: args, Env: gobuild.Env}) @@ -256,7 +251,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") @@ -355,128 +350,93 @@ func downloadSpecTestFixtures(csdb *build.ChecksumDB, cachedir string) string { return filepath.Join(cachedir, base) } -// hashAllSourceFiles iterates all files under the top-level project directory -// computing the hash of each file (excluding files within the tests -// subrepo) -func hashAllSourceFiles() (map[string]common.Hash, error) { - res := make(map[string]common.Hash) - err := filepath.WalkDir(".", func(path string, d os.DirEntry, err error) error { - if strings.HasPrefix(path, filepath.FromSlash("tests/testdata")) { - return filepath.SkipDir - } - if !d.Type().IsRegular() { - return nil - } - // open the file and hash it - f, err := os.OpenFile(path, os.O_RDONLY, 0666) - if err != nil { - return err - } - hasher := sha256.New() - if _, err := io.Copy(hasher, f); err != nil { - return err - } - res[path] = common.Hash(hasher.Sum(nil)) - return nil - }) +// doCheckTidy assets that the Go modules files are tidied already. +func doCheckTidy() { + targets := []string{"go.mod", "go.sum"} + + hashes, err := build.HashFiles(targets) if err != nil { - return nil, err + log.Fatalf("failed to hash go.mod/go.sum: %v", err) } - return res, nil -} + build.MustRun(new(build.GoToolchain).Go("mod", "tidy")) -// hashSourceFiles iterates the provided set of filepaths (relative to the top-level geth project directory) -// computing the hash of each file. -func hashSourceFiles(files []string) (map[string]common.Hash, error) { - res := make(map[string]common.Hash) - for _, filePath := range files { - f, err := os.OpenFile(filePath, os.O_RDONLY, 0666) - if err != nil { - return nil, err - } - hasher := sha256.New() - if _, err := io.Copy(hasher, f); err != nil { - return nil, err - } - res[filePath] = common.Hash(hasher.Sum(nil)) - } - return res, nil -} - -// compareHashedFilesets compares two maps (key is relative file path to top-level geth directory, value is its hash) -// and returns the list of file paths whose hashes differed. -func compareHashedFilesets(preHashes map[string]common.Hash, postHashes map[string]common.Hash) []string { - updates := []string{} - for path, postHash := range postHashes { - preHash, ok := preHashes[path] - if !ok || preHash != postHash { - updates = append(updates, path) - } - } - return updates -} - -func doGoModTidy() { - targetFiles := []string{"go.mod", "go.sum"} - preHashes, err := hashSourceFiles(targetFiles) + tidied, err := build.HashFiles(targets) if err != nil { - log.Fatal("failed to hash go.mod/go.sum", "err", err) + log.Fatalf("failed to rehash go.mod/go.sum: %v", err) } - tc := new(build.GoToolchain) - c := tc.Go("mod", "tidy") - build.MustRun(c) - postHashes, err := hashSourceFiles(targetFiles) - updates := compareHashedFilesets(preHashes, postHashes) - for _, updatedFile := range updates { - fmt.Fprintf(os.Stderr, "changed file %s\n", updatedFile) - } - if len(updates) != 0 { - log.Fatal("go.sum and/or go.mod were updated by running 'go mod tidy'") + if updates := build.DiffHashes(hashes, tidied); len(updates) > 0 { + log.Fatalf("files changed on running 'go mod tidy': %v", updates) } + fmt.Println("No untidy module files detected.") } -// doGenerate ensures that re-generating generated files does not cause -// any mutations in the source file tree: i.e. all generated files were -// updated and committed. Any stale generated files are updated. -func doGenerate() { +// doCheckGenerate ensures that re-generating generated files does not cause +// any mutations in the source file tree. +func doCheckGenerate() { var ( - tc = new(build.GoToolchain) cachedir = flag.String("cachedir", "./build/cache", "directory for caching binaries.") - verify = flag.Bool("verify", false, "check whether any files are changed by go generate") ) + // Compute the origin hashes of all the files + var hashes map[string][32]byte - protocPath := downloadProtoc(*cachedir) - protocGenGoPath := downloadProtocGenGo(*cachedir) - - var preHashes map[string]common.Hash - if *verify { - var err error - preHashes, err = hashAllSourceFiles() - if err != nil { - log.Fatal("failed to compute map of source hashes", "err", err) - } + var err error + hashes, err = build.HashFolder(".", []string{"tests/testdata", "build/cache"}) + if err != nil { + log.Fatal("Error computing hashes", "err", err) } - - c := tc.Go("generate", "./...") + // Run any go generate steps we might be missing + var ( + protocPath = downloadProtoc(*cachedir) + protocGenGoPath = downloadProtocGenGo(*cachedir) + ) + c := new(build.GoToolchain).Go("generate", "./...") pathList := []string{filepath.Join(protocPath, "bin"), protocGenGoPath, os.Getenv("PATH")} c.Env = append(c.Env, "PATH="+strings.Join(pathList, string(os.PathListSeparator))) build.MustRun(c) - if !*verify { - return - } - // Check if files were changed. - postHashes, err := hashAllSourceFiles() + // Check if generate file hashes have changed + generated, err := build.HashFolder(".", []string{"tests/testdata", "build/cache"}) if err != nil { - log.Fatal("error computing source tree file hashes", "err", err) + log.Fatalf("Error re-computing hashes: %v", err) } - updates := compareHashedFilesets(preHashes, postHashes) - for _, updatedFile := range updates { - fmt.Fprintf(os.Stderr, "changed file %s\n", updatedFile) + updates := build.DiffHashes(hashes, generated) + for _, file := range updates { + log.Printf("File changed: %s", file) } if len(updates) != 0 { log.Fatal("One or more generated files were updated by running 'go generate ./...'") } + fmt.Println("No stale files detected.") +} + +// doCheckBadDeps verifies whether certain unintended dependencies between some +// packages leak into the codebase due to a refactor. This is not an exhaustive +// list, rather something we build up over time at sensitive places. +func doCheckBadDeps() { + baddeps := [][2]string{ + // Rawdb tends to be a dumping ground for db utils, sometimes leaking the db itself + {"github.com/ethereum/go-ethereum/core/rawdb", "github.com/ethereum/go-ethereum/ethdb/leveldb"}, + {"github.com/ethereum/go-ethereum/core/rawdb", "github.com/ethereum/go-ethereum/ethdb/pebbledb"}, + } + tc := new(build.GoToolchain) + + var failed bool + for _, rule := range baddeps { + out, err := tc.Go("list", "-deps", rule[0]).CombinedOutput() + if err != nil { + log.Fatalf("Failed to list '%s' dependencies: %v", rule[0], err) + } + for _, line := range strings.Split(string(out), "\n") { + if strings.TrimSpace(line) == rule[1] { + log.Printf("Found bad dependency '%s' -> '%s'", rule[0], rule[1]) + failed = true + } + } + } + if failed { + log.Fatalf("Bad dependencies detected.") + } + fmt.Println("No bad dependencies detected.") } // doLint runs golangci-lint on requested packages. @@ -493,8 +453,6 @@ func doLint(cmdline []string) { linter := downloadLinter(*cachedir) lflags := []string{"run", "--config", ".golangci.yml"} build.MustRunCommandWithOutput(linter, append(lflags, packages...)...) - - doGoModTidy() fmt.Println("You have achieved perfection.") } @@ -638,7 +596,7 @@ func doArchive(cmdline []string) { var ( env = build.Env() - basegeth = archiveBasename(*arch, params.ArchiveVersion(env.Commit)) + basegeth = archiveBasename(*arch, version.Archive(env.Commit)) geth = "geth-" + basegeth + ext alltools = "geth-alltools-" + basegeth + ext ) @@ -758,7 +716,7 @@ func doDockerBuildx(cmdline []string) { case env.Branch == "master": tags = []string{"latest"} case strings.HasPrefix(env.Tag, "v1."): - tags = []string{"stable", fmt.Sprintf("release-1.%d", params.VersionMinor), "v" + params.Version} + tags = []string{"stable", fmt.Sprintf("release-%v", version.Family), "v" + version.Semantic} } // Need to create a mult-arch builder build.MustRunCommand("docker", "buildx", "create", "--use", "--name", "multi-arch-builder", "--platform", *platform) @@ -774,7 +732,7 @@ func doDockerBuildx(cmdline []string) { gethImage := fmt.Sprintf("%s%s", spec.base, tag) build.MustRunCommand("docker", "buildx", "build", "--build-arg", "COMMIT="+env.Commit, - "--build-arg", "VERSION="+params.VersionWithMeta, + "--build-arg", "VERSION="+version.WithMeta, "--build-arg", "BUILDNUM="+env.Buildnum, "--tag", gethImage, "--platform", *platform, @@ -921,7 +879,7 @@ func ppaUpload(workdir, ppa, sshUser string, files []string) { var idfile string if sshkey := getenvBase64("PPA_SSH_KEY"); len(sshkey) > 0 { idfile = filepath.Join(workdir, "sshkey") - if !common.FileExist(idfile) { + if !build.FileExist(idfile) { os.WriteFile(idfile, sshkey, 0600) } } @@ -1146,19 +1104,19 @@ func doWindowsInstaller(cmdline []string) { // Build the installer. This assumes that all the needed files have been previously // built (don't mix building and packaging to keep cross compilation complexity to a // minimum). - version := strings.Split(params.Version, ".") + ver := strings.Split(version.Semantic, ".") if env.Commit != "" { - version[2] += "-" + env.Commit[:8] + ver[2] += "-" + env.Commit[:8] } - installer, err := filepath.Abs("geth-" + archiveBasename(*arch, params.ArchiveVersion(env.Commit)) + ".exe") + installer, err := filepath.Abs("geth-" + archiveBasename(*arch, version.Archive(env.Commit)) + ".exe") if err != nil { log.Fatalf("Failed to convert installer file path: %v", err) } build.MustRunCommand("makensis.exe", "/DOUTPUTFILE="+installer, - "/DMAJORVERSION="+version[0], - "/DMINORVERSION="+version[1], - "/DBUILDVERSION="+version[2], + "/DMAJORVERSION="+ver[0], + "/DMINORVERSION="+ver[1], + "/DBUILDVERSION="+ver[2], "/DARCH="+*arch, filepath.Join(*workdir, "geth.nsi"), ) diff --git a/cmd/blsync/main.go b/cmd/blsync/main.go index f9b8575edf..d74e1496cd 100644 --- a/cmd/blsync/main.go +++ b/cmd/blsync/main.go @@ -20,6 +20,7 @@ import ( "context" "fmt" "os" + "slices" "github.com/ethereum/go-ethereum/beacon/blsync" "github.com/ethereum/go-ethereum/cmd/utils" @@ -33,7 +34,7 @@ import ( func main() { app := flags.NewApp("beacon light syncer tool") - app.Flags = flags.Merge([]cli.Flag{ + app.Flags = slices.Concat([]cli.Flag{ utils.BeaconApiFlag, utils.BeaconApiHeaderFlag, utils.BeaconThresholdFlag, @@ -45,6 +46,7 @@ func main() { //TODO datadir for optional permanent database utils.MainnetFlag, utils.SepoliaFlag, + utils.HoleskyFlag, utils.BlsyncApiFlag, utils.BlsyncJWTSecretFlag, }, @@ -68,7 +70,7 @@ func main() { func sync(ctx *cli.Context) error { // set up blsync - client := blsync.NewClient(ctx) + client := blsync.NewClient(utils.MakeBeaconLightConfig(ctx)) client.SetEngineRPC(makeRPCClient(ctx)) client.Start() diff --git a/cmd/bootnode/main.go b/cmd/bootnode/main.go deleted file mode 100644 index 350b85df1e..0000000000 --- a/cmd/bootnode/main.go +++ /dev/null @@ -1,209 +0,0 @@ -// Copyright 2015 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 . - -// bootnode runs a bootstrap node for the Ethereum Discovery Protocol. -package main - -import ( - "crypto/ecdsa" - "flag" - "fmt" - "net" - "os" - "time" - - "github.com/ethereum/go-ethereum/cmd/utils" - "github.com/ethereum/go-ethereum/crypto" - "github.com/ethereum/go-ethereum/log" - "github.com/ethereum/go-ethereum/p2p/discover" - "github.com/ethereum/go-ethereum/p2p/enode" - "github.com/ethereum/go-ethereum/p2p/nat" - "github.com/ethereum/go-ethereum/p2p/netutil" -) - -func main() { - var ( - listenAddr = flag.String("addr", ":30301", "listen address") - genKey = flag.String("genkey", "", "generate a node key") - writeAddr = flag.Bool("writeaddress", false, "write out the node's public key and quit") - nodeKeyFile = flag.String("nodekey", "", "private key filename") - nodeKeyHex = flag.String("nodekeyhex", "", "private key as hex (for testing)") - natdesc = flag.String("nat", "none", "port mapping mechanism (any|none|upnp|pmp|pmp:|extip:)") - netrestrict = flag.String("netrestrict", "", "restrict network communication to the given IP networks (CIDR masks)") - runv5 = flag.Bool("v5", false, "run a v5 topic discovery bootnode") - verbosity = flag.Int("verbosity", 3, "log verbosity (0-5)") - vmodule = flag.String("vmodule", "", "log verbosity pattern") - - nodeKey *ecdsa.PrivateKey - err error - ) - flag.Parse() - - glogger := log.NewGlogHandler(log.NewTerminalHandler(os.Stderr, false)) - slogVerbosity := log.FromLegacyLevel(*verbosity) - glogger.Verbosity(slogVerbosity) - glogger.Vmodule(*vmodule) - log.SetDefault(log.NewLogger(glogger)) - - natm, err := nat.Parse(*natdesc) - if err != nil { - utils.Fatalf("-nat: %v", err) - } - switch { - case *genKey != "": - nodeKey, err = crypto.GenerateKey() - if err != nil { - utils.Fatalf("could not generate key: %v", err) - } - if err = crypto.SaveECDSA(*genKey, nodeKey); err != nil { - utils.Fatalf("%v", err) - } - if !*writeAddr { - return - } - case *nodeKeyFile == "" && *nodeKeyHex == "": - utils.Fatalf("Use -nodekey or -nodekeyhex to specify a private key") - case *nodeKeyFile != "" && *nodeKeyHex != "": - utils.Fatalf("Options -nodekey and -nodekeyhex are mutually exclusive") - case *nodeKeyFile != "": - if nodeKey, err = crypto.LoadECDSA(*nodeKeyFile); err != nil { - utils.Fatalf("-nodekey: %v", err) - } - case *nodeKeyHex != "": - if nodeKey, err = crypto.HexToECDSA(*nodeKeyHex); err != nil { - utils.Fatalf("-nodekeyhex: %v", err) - } - } - - if *writeAddr { - fmt.Printf("%x\n", crypto.FromECDSAPub(&nodeKey.PublicKey)[1:]) - os.Exit(0) - } - - var restrictList *netutil.Netlist - if *netrestrict != "" { - restrictList, err = netutil.ParseNetlist(*netrestrict) - if err != nil { - utils.Fatalf("-netrestrict: %v", err) - } - } - - addr, err := net.ResolveUDPAddr("udp", *listenAddr) - if err != nil { - utils.Fatalf("-ResolveUDPAddr: %v", err) - } - conn, err := net.ListenUDP("udp", addr) - if err != nil { - utils.Fatalf("-ListenUDP: %v", err) - } - defer conn.Close() - - db, _ := enode.OpenDB("") - ln := enode.NewLocalNode(db, nodeKey) - - listenerAddr := conn.LocalAddr().(*net.UDPAddr) - if natm != nil && !listenerAddr.IP.IsLoopback() { - natAddr := doPortMapping(natm, ln, listenerAddr) - if natAddr != nil { - listenerAddr = natAddr - } - } - - printNotice(&nodeKey.PublicKey, *listenerAddr) - cfg := discover.Config{ - PrivateKey: nodeKey, - NetRestrict: restrictList, - } - if *runv5 { - if _, err := discover.ListenV5(conn, ln, cfg); err != nil { - utils.Fatalf("%v", err) - } - } else { - if _, err := discover.ListenUDP(conn, ln, cfg); err != nil { - utils.Fatalf("%v", err) - } - } - - select {} -} - -func printNotice(nodeKey *ecdsa.PublicKey, addr net.UDPAddr) { - if addr.IP.IsUnspecified() { - addr.IP = net.IP{127, 0, 0, 1} - } - n := enode.NewV4(nodeKey, addr.IP, 0, addr.Port) - fmt.Println(n.URLv4()) - fmt.Println("Note: you're using cmd/bootnode, a developer tool.") - fmt.Println("We recommend using a regular node as bootstrap node for production deployments.") -} - -func doPortMapping(natm nat.Interface, ln *enode.LocalNode, addr *net.UDPAddr) *net.UDPAddr { - const ( - protocol = "udp" - name = "ethereum discovery" - ) - newLogger := func(external int, internal int) log.Logger { - return log.New("proto", protocol, "extport", external, "intport", internal, "interface", natm) - } - - var ( - intport = addr.Port - extaddr = &net.UDPAddr{IP: addr.IP, Port: addr.Port} - mapTimeout = nat.DefaultMapTimeout - log = newLogger(addr.Port, intport) - ) - addMapping := func() { - // Get the external address. - var err error - extaddr.IP, err = natm.ExternalIP() - if err != nil { - log.Debug("Couldn't get external IP", "err", err) - return - } - // Create the mapping. - p, err := natm.AddMapping(protocol, extaddr.Port, intport, name, mapTimeout) - if err != nil { - log.Debug("Couldn't add port mapping", "err", err) - return - } - if p != uint16(extaddr.Port) { - extaddr.Port = int(p) - log = newLogger(extaddr.Port, intport) - log.Info("NAT mapped alternative port") - } else { - log.Info("NAT mapped port") - } - // Update IP/port information of the local node. - ln.SetStaticIP(extaddr.IP) - ln.SetFallbackUDP(extaddr.Port) - } - - // Perform mapping once, synchronously. - log.Info("Attempting port mapping") - addMapping() - - // Refresh the mapping periodically. - go func() { - refresh := time.NewTimer(mapTimeout) - defer refresh.Stop() - for range refresh.C { - addMapping() - refresh.Reset(mapTimeout) - } - }() - - return extaddr -} diff --git a/cmd/devp2p/discv4cmd.go b/cmd/devp2p/discv4cmd.go index 3b5400ca3a..8c48b3a557 100644 --- a/cmd/devp2p/discv4cmd.go +++ b/cmd/devp2p/discv4cmd.go @@ -21,6 +21,7 @@ import ( "fmt" "net" "net/http" + "slices" "strconv" "strings" "time" @@ -28,7 +29,6 @@ import ( "github.com/ethereum/go-ethereum/cmd/devp2p/internal/v4test" "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/crypto" - "github.com/ethereum/go-ethereum/internal/flags" "github.com/ethereum/go-ethereum/log" "github.com/ethereum/go-ethereum/p2p/discover" "github.com/ethereum/go-ethereum/p2p/enode" @@ -83,7 +83,7 @@ var ( Name: "listen", Usage: "Runs a discovery node", Action: discv4Listen, - Flags: flags.Merge(discoveryNodeFlags, []cli.Flag{ + Flags: slices.Concat(discoveryNodeFlags, []cli.Flag{ httpAddrFlag, }), } @@ -91,7 +91,7 @@ var ( Name: "crawl", Usage: "Updates a nodes.json file with random nodes found in the DHT", Action: discv4Crawl, - Flags: flags.Merge(discoveryNodeFlags, []cli.Flag{crawlTimeoutFlag, crawlParallelismFlag}), + Flags: slices.Concat(discoveryNodeFlags, []cli.Flag{crawlTimeoutFlag, crawlParallelismFlag}), } discv4TestCommand = &cli.Command{ Name: "test", diff --git a/cmd/devp2p/discv5cmd.go b/cmd/devp2p/discv5cmd.go index 0dac945269..2422ef6644 100644 --- a/cmd/devp2p/discv5cmd.go +++ b/cmd/devp2p/discv5cmd.go @@ -19,11 +19,11 @@ package main import ( "errors" "fmt" + "slices" "time" "github.com/ethereum/go-ethereum/cmd/devp2p/internal/v5test" "github.com/ethereum/go-ethereum/common" - "github.com/ethereum/go-ethereum/internal/flags" "github.com/ethereum/go-ethereum/p2p/discover" "github.com/urfave/cli/v2" ) @@ -56,7 +56,7 @@ var ( Name: "crawl", Usage: "Updates a nodes.json file with random nodes found in the DHT", Action: discv5Crawl, - Flags: flags.Merge(discoveryNodeFlags, []cli.Flag{ + Flags: slices.Concat(discoveryNodeFlags, []cli.Flag{ crawlTimeoutFlag, }), } diff --git a/cmd/devp2p/internal/ethtest/testdata/genesis.json b/cmd/devp2p/internal/ethtest/testdata/genesis.json index e8bb66bb3c..4cfebdcac1 100644 --- a/cmd/devp2p/internal/ethtest/testdata/genesis.json +++ b/cmd/devp2p/internal/ethtest/testdata/genesis.json @@ -18,7 +18,6 @@ "shanghaiTime": 780, "cancunTime": 840, "terminalTotalDifficulty": 9454784, - "terminalTotalDifficultyPassed": true, "ethash": {} }, "nonce": "0x0", diff --git a/cmd/evm/blockrunner.go b/cmd/evm/blockrunner.go index d5cd8d8e3d..9a04807b33 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 ( + collected = collectFiles(path) + results []testResult + ) + for _, fname := range collected { + r, err := runBlockTest(ctx, fname) + if err != nil { + return err + } + results = append(results, r...) + } + report(ctx, results) + return nil +} - 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) - } - // Load the test content from the input file - src, err := os.ReadFile(ctx.Args().First()) +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..92182a53b3 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() + jt = vm.NewEOFInstructionSetForTesting() } 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/eofparse_test.go b/cmd/evm/eofparse_test.go index 9b17039f5b..cda4b38fc9 100644 --- a/cmd/evm/eofparse_test.go +++ b/cmd/evm/eofparse_test.go @@ -43,7 +43,7 @@ func FuzzEofParsing(f *testing.F) { // And do the fuzzing f.Fuzz(func(t *testing.T, data []byte) { var ( - jt = vm.NewPragueEOFInstructionSetForTesting() + jt = vm.NewEOFInstructionSetForTesting() c vm.Container ) cpy := common.CopyBytes(data) 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 9f30d7ba6c..aef497885e 100644 --- a/cmd/evm/internal/t8ntool/execution.go +++ b/cmd/evm/internal/t8ntool/execution.go @@ -70,11 +70,11 @@ type ExecutionResult struct { CurrentExcessBlobGas *math.HexOrDecimal64 `json:"currentExcessBlobGas,omitempty"` CurrentBlobGasUsed *math.HexOrDecimal64 `json:"blobGasUsed,omitempty"` RequestsHash *common.Hash `json:"requestsHash,omitempty"` - Requests [][]byte `json:"requests,omitempty"` + Requests [][]byte `json:"requests"` } type executionResultMarshaling struct { - Requests []hexutil.Bytes `json:"requests,omitempty"` + Requests []hexutil.Bytes `json:"requests"` } type ommer struct { @@ -132,7 +132,7 @@ type rejectedTx struct { // Apply applies a set of transactions to a pre-state func (pre *Prestate) Apply(vmConfig vm.Config, chainConfig *params.ChainConfig, txIt txIterator, miningReward int64, - getTracerFn func(txIndex int, txHash common.Hash) (*tracers.Tracer, io.WriteCloser, error)) (*state.StateDB, *ExecutionResult, []byte, error) { + getTracerFn func(txIndex int, txHash common.Hash, chainConfig *params.ChainConfig) (*tracers.Tracer, io.WriteCloser, error)) (*state.StateDB, *ExecutionResult, []byte, error) { // Capture errors for BLOCKHASH operation, if we haven't been supplied the // required blockhashes var hashError error @@ -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() @@ -242,22 +241,21 @@ func (pre *Prestate) Apply(vmConfig vm.Config, chainConfig *params.ChainConfig, continue } } - tracer, traceOutput, err := getTracerFn(txIndex, tx.Hash()) + tracer, traceOutput, err := getTracerFn(txIndex, tx.Hash(), 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/gen_execresult.go b/cmd/evm/internal/t8ntool/gen_execresult.go index 0da94f5ca2..38310b9f2b 100644 --- a/cmd/evm/internal/t8ntool/gen_execresult.go +++ b/cmd/evm/internal/t8ntool/gen_execresult.go @@ -31,7 +31,7 @@ func (e ExecutionResult) MarshalJSON() ([]byte, error) { CurrentExcessBlobGas *math.HexOrDecimal64 `json:"currentExcessBlobGas,omitempty"` CurrentBlobGasUsed *math.HexOrDecimal64 `json:"blobGasUsed,omitempty"` RequestsHash *common.Hash `json:"requestsHash,omitempty"` - Requests []hexutil.Bytes `json:"requests,omitempty"` + Requests []hexutil.Bytes `json:"requests"` } var enc ExecutionResult enc.StateRoot = e.StateRoot @@ -74,7 +74,7 @@ func (e *ExecutionResult) UnmarshalJSON(input []byte) error { CurrentExcessBlobGas *math.HexOrDecimal64 `json:"currentExcessBlobGas,omitempty"` CurrentBlobGasUsed *math.HexOrDecimal64 `json:"blobGasUsed,omitempty"` RequestsHash *common.Hash `json:"requestsHash,omitempty"` - Requests []hexutil.Bytes `json:"requests,omitempty"` + Requests []hexutil.Bytes `json:"requests"` } var dec ExecutionResult if err := json.Unmarshal(input, &dec); err != nil { diff --git a/cmd/evm/internal/t8ntool/transaction.go b/cmd/evm/internal/t8ntool/transaction.go index 7f66ba4d85..6dac4301dd 100644 --- a/cmd/evm/internal/t8ntool/transaction.go +++ b/cmd/evm/internal/t8ntool/transaction.go @@ -133,7 +133,7 @@ func Transaction(ctx *cli.Context) error { r.Address = sender } // Check intrinsic gas - if gas, err := core.IntrinsicGas(tx.Data(), tx.AccessList(), tx.To() == nil, + if gas, err := core.IntrinsicGas(tx.Data(), tx.AccessList(), tx.SetCodeAuthorizations(), tx.To() == nil, chainConfig.IsHomestead(new(big.Int)), chainConfig.IsIstanbul(new(big.Int)), chainConfig.IsShanghai(new(big.Int), 0)); err != nil { r.Error = err results = append(results, r) diff --git a/cmd/evm/internal/t8ntool/transition.go b/cmd/evm/internal/t8ntool/transition.go index fa052f5954..231576fa42 100644 --- a/cmd/evm/internal/t8ntool/transition.go +++ b/cmd/evm/internal/t8ntool/transition.go @@ -82,7 +82,9 @@ type input struct { } func Transition(ctx *cli.Context) error { - var getTracer = func(txIndex int, txHash common.Hash) (*tracers.Tracer, io.WriteCloser, error) { return nil, nil, nil } + var getTracer = func(txIndex int, txHash common.Hash, chainConfig *params.ChainConfig) (*tracers.Tracer, io.WriteCloser, error) { + return nil, nil, nil + } baseDir, err := createBasedir(ctx) if err != nil { @@ -95,9 +97,8 @@ 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) (*tracers.Tracer, io.WriteCloser, error) { + 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()))) if err != nil { return nil, nil, NewError(ErrorIO, fmt.Errorf("failed creating trace-file: %v", err)) @@ -121,12 +122,12 @@ func Transition(ctx *cli.Context) error { if ctx.IsSet(TraceTracerConfigFlag.Name) { config = []byte(ctx.String(TraceTracerConfigFlag.Name)) } - getTracer = func(txIndex int, txHash common.Hash) (*tracers.Tracer, io.WriteCloser, error) { + getTracer = func(txIndex int, txHash common.Hash, chainConfig *params.ChainConfig) (*tracers.Tracer, io.WriteCloser, error) { traceFile, err := os.Create(filepath.Join(baseDir, fmt.Sprintf("trace-%d-%v.json", txIndex, txHash.String()))) if err != nil { return nil, nil, NewError(ErrorIO, fmt.Errorf("failed creating trace-file: %v", err)) } - tracer, err := tracers.DefaultDirectory.New(ctx.String(TraceTracerFlag.Name), nil, config) + tracer, err := tracers.DefaultDirectory.New(ctx.String(TraceTracerFlag.Name), nil, config, chainConfig) if err != nil { return nil, nil, NewError(ErrorConfig, fmt.Errorf("failed instantiating tracer: %w", err)) } diff --git a/cmd/evm/main.go b/cmd/evm/main.go index 994684ab22..2079425416 100644 --- a/cmd/evm/main.go +++ b/cmd/evm/main.go @@ -19,10 +19,14 @@ package main import ( "fmt" - "math/big" + "io/fs" "os" + "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" @@ -32,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", + } + HumanReadableFlag = &cli.BoolFlag{ + Name: "human", + Usage: "\"Human-readable\" output", + } + StatDumpFlag = &cli.BoolFlag{ + Name: "statdump", + Usage: "displays stack and heap memory information", + } + + // Tracing flags. + TraceFlag = &cli.BoolFlag{ + Name: "trace", + Usage: "Enable tracing and output trace log.", + Category: traceCategory, + } + 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: traceCategory, + } + TraceDisableStackFlag = &cli.BoolFlag{ + Name: "trace.nostack", + Aliases: []string{"nostack"}, + Usage: "disable stack output", + Category: traceCategory, + } + TraceDisableStorageFlag = &cli.BoolFlag{ + Name: "trace.nostorage", + Aliases: []string{"nostorage"}, + Usage: "disable storage output", + Category: traceCategory, + } + TraceDisableReturnDataFlag = &cli.BoolFlag{ + Name: "trace.noreturndata", + Aliases: []string{"noreturndata"}, + Value: true, + Usage: "enable return data output", + Category: traceCategory, + } + + // Deprecated flags. + DebugFlag = &cli.BoolFlag{ + Name: "debug", + Usage: "output full trace logs (deprecated)", + Hidden: true, + Category: traceCategory, } MachineFlag = &cli.BoolFlag{ Name: "json", - Usage: "output trace logs in machine readable format (json)", - Category: flags.VMCategory, - } - SenderFlag = &cli.StringFlag{ - Name: "sender", - Usage: "The transaction origin", - Category: flags.VMCategory, - } - ReceiverFlag = &cli.StringFlag{ - Name: "receiver", - Usage: "The transaction receiver (execution context)", - Category: flags.VMCategory, - } - DisableMemoryFlag = &cli.BoolFlag{ - Name: "nomemory", - Value: true, - Usage: "disable memory output", - Category: flags.VMCategory, - } - DisableStackFlag = &cli.BoolFlag{ - Name: "nostack", - Usage: "disable stack output", - Category: flags.VMCategory, - } - DisableStorageFlag = &cli.BoolFlag{ - Name: "nostorage", - Usage: "disable storage output", - Category: flags.VMCategory, - } - DisableReturnDataFlag = &cli.BoolFlag{ - Name: "noreturndata", - Value: true, - Usage: "enable return data output", - Category: flags.VMCategory, - } - refTestFlag = &cli.StringFlag{ - Name: "test", - Usage: "Path to EOF validation reference test.", - } - hexFlag = &cli.StringFlag{ - Name: "hex", - Usage: "single container data parse and validation", + Usage: "output trace logs in machine readable format, json (deprecated)", + Hidden: true, + Category: traceCategory, } ) +// Command definitions. var ( stateTransitionCommand = &cli.Command{ Name: "transition", @@ -174,7 +156,6 @@ var ( t8ntool.RewardFlag, }, } - transactionCommand = &cli.Command{ Name: "transaction", Aliases: []string{"t9n"}, @@ -202,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 = flags.Merge(vmFlags, traceFlags, debug.Flags) + app.Flags = debug.Flags app.Commands = []*cli.Command{ - compileCommand, - disasmCommand, runCommand, blockTestCommand, stateTestCommand, @@ -279,11 +225,71 @@ 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(code) + 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 + } +} + +// collectFiles walks the given path. If the path is a directory, it will +// return a list of all accumulates all files with json extension. +// Otherwise (if path points to a file), it will return the path. +func collectFiles(path string) []string { + var out []string + if info, err := os.Stat(path); err == nil && !info.IsDir() { + // User explicitly pointed out a file, ignore extension. + return []string{path} + } + 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) + } + 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 235fed6630..c67d3657e2 100644 --- a/cmd/evm/runner.go +++ b/cmd/evm/runner.go @@ -18,25 +18,27 @@ package main import ( "bytes" + "encoding/hex" "encoding/json" "fmt" "io" "math/big" "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" @@ -50,14 +52,83 @@ var runCommand = &cli.Command{ Usage: "Run arbitrary evm binary", ArgsUsage: "", Description: `The run command runs arbitrary EVM code.`, - Flags: flags.Merge(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") } @@ -75,51 +146,60 @@ func readGenesis(genesisPath string) *core.Genesis { } type execStats struct { - time time.Duration // The execution time. - allocs int64 // The number of heap allocations during execution. - bytesAllocated int64 // The cumulative number of bytes allocated during execution. + Time time.Duration `json:"time"` // The execution Time. + Allocs int64 `json:"allocs"` // The number of heap allocations during execution. + BytesAllocated int64 `json:"bytesAllocated"` // The cumulative number of bytes allocated during execution. + GasUsed uint64 `json:"gasUsed"` // the amount of gas used during execution } -func timedExec(bench bool, execFunc func() ([]byte, uint64, error)) (output []byte, gasLeft uint64, stats execStats, err error) { +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++ { - output, gasLeft, err = execFunc() + haveOutput, haveGasUsed, haveErr := execFunc() + if !bytes.Equal(haveOutput, output) { + panic(fmt.Sprintf("output differs\nhave %x\nwant %x\n", haveOutput, output)) + } + if haveGasUsed != gasUsed { + panic(fmt.Sprintf("gas differs, have %v want %v", haveGasUsed, gasUsed)) + } + if haveErr != err { + panic(fmt.Sprintf("err differs, have %v want %v", haveErr, err)) + } } }) - // Get the average execution time from the benchmarking result. // There are other useful stats here that could be reported. - stats.time = time.Duration(result.NsPerOp()) - stats.allocs = result.AllocsPerOp() - stats.bytesAllocated = result.AllocedBytesPerOp() - } else { - var memStatsBefore, memStatsAfter goruntime.MemStats - goruntime.ReadMemStats(&memStatsBefore) - startTime := time.Now() - output, gasLeft, err = execFunc() - stats.time = time.Since(startTime) - goruntime.ReadMemStats(&memStatsAfter) - stats.allocs = int64(memStatsAfter.Mallocs - memStatsBefore.Mallocs) - stats.bytesAllocated = int64(memStatsAfter.TotalAlloc - memStatsBefore.TotalAlloc) + stats := execStats{ + Time: time.Duration(result.NsPerOp()), + Allocs: result.AllocsPerOp(), + BytesAllocated: result.AllocedBytesPerOp(), + GasUsed: gasUsed, + } + return output, stats, err } - - return output, gasLeft, stats, err + var memStatsBefore, memStatsAfter goruntime.MemStats + goruntime.ReadMemStats(&memStatsBefore) + t0 := time.Now() + output, gasUsed, err := execFunc() + duration := time.Since(t0) + goruntime.ReadMemStats(&memStatsAfter) + stats := execStats{ + Time: duration, + Allocs: int64(memStatsAfter.Mallocs - memStatsBefore.Mallocs), + BytesAllocated: int64(memStatsAfter.TotalAlloc - memStatsBefore.TotalAlloc), + GasUsed: gasUsed, + } + return output, stats, err } 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")) @@ -127,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 @@ -156,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) != "" { @@ -169,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)) + // 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 { + fmt.Printf("Could not load code from stdin: %v\n", err) 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) + 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) } - bin, err := compiler.Compile(fn, src, false) - if err != nil { - return err - } - 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), @@ -256,23 +315,28 @@ 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) { - return runtime.Call(receiver, input, &runtimeConfig) + // don't mutate the state! + runtimeConfig.State = prestate.Copy() + output, gasLeft, err := runtime.Call(receiver, input, &runtimeConfig) + return output, initialGas - gasLeft, err } } bench := ctx.Bool(BenchFlag.Name) - output, leftOverGas, stats, err := timedExec(bench, execFunc) + 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 @@ -286,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) { @@ -299,7 +361,7 @@ func runCmd(ctx *cli.Context) error { execution time: %v allocations: %d allocated bytes: %d -`, initialGas-leftOverGas, stats.time, stats.allocs, stats.bytesAllocated) +`, stats.GasUsed, stats.Time, stats.Allocs, stats.BytesAllocated) } if tracer == nil { fmt.Printf("%#x\n", output) @@ -310,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 4514367e8a..4c2dfeabc6 100644 --- a/cmd/evm/staterunner.go +++ b/cmd/evm/staterunner.go @@ -21,106 +21,138 @@ 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" ) +var ( + forkFlag = &cli.StringFlag{ + Name: "statetest.fork", + 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.", + Category: flags.VMCategory, + Value: -1, // default to select all subtest indices + } +) 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: "", -} - -// 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"` + 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() + // If path is provided, run the tests at that path. + if len(path) != 0 { + var ( + collected = collectFiles(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 } - // Load the test content from the input file - if len(ctx.Args().First()) != 0 { - return runStateTest(ctx.Args().First(), cfg, ctx.Bool(DumpFlag.Name)) - } - // 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(fname, cfg, ctx.Bool(DumpFlag.Name)); err != nil { + results, err := runStateTest(ctx, fname) + if err != nil { return err } + report(ctx, results) } return nil } // runStateTest loads the state-test given by fname, and executes the test. -func runStateTest(fname string, cfg vm.Config, dump 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) + } + + 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 - results := make([]StatetestResult, 0, len(testsByName)) + results := make([]testResult, 0, len(testsByName)) for key, test := range testsByName { - for _, st := range test.Subtests() { + 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 fork := ctx.String(forkFlag.Name); fork != "" && st.Fork != fork { + // If specific fork requested, skip all tests that do not match. + continue + } // Run the test and aggregate the result - result := &StatetestResult{Name: key, Fork: st.Fork, Pass: true} - test.Run(st, cfg, false, rawdb.HashScheme, func(err error, tstate *tests.StateTestState) { + 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 tstate.StateDB != nil { - root = tstate.StateDB.IntermediateRoot(false) + if state.StateDB != nil { + root = state.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 + // 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 + // Test failed, mark as so. result.Pass, result.Error = false, err.Error() + return } }) 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 65723694f9..27c6c43164 100644 --- a/cmd/evm/t8n_test.go +++ b/cmd/evm/t8n_test.go @@ -287,6 +287,14 @@ func TestT8n(t *testing.T) { output: t8nOutput{alloc: true, result: true}, expOut: "exp.json", }, + { // Prague test, EIP-7702 transaction + base: "./testdata/33", + input: t8nInput{ + "alloc.json", "txs.json", "env.json", "Prague", "", + }, + output: t8nOutput{alloc: true, result: true}, + expOut: "exp.json", + }, } { args := []string{"t8n"} args = append(args, tc.output.get()...) @@ -341,98 +349,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 @@ -672,6 +588,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{} @@ -683,3 +681,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/1/exp.json b/cmd/evm/testdata/1/exp.json index d1351e5b76..50662f35ea 100644 --- a/cmd/evm/testdata/1/exp.json +++ b/cmd/evm/testdata/1/exp.json @@ -40,6 +40,7 @@ } ], "currentDifficulty": "0x20000", - "gasUsed": "0x5208" + "gasUsed": "0x5208", + "requests": null } } diff --git a/cmd/evm/testdata/13/exp2.json b/cmd/evm/testdata/13/exp2.json index babce35929..6415a4f1f4 100644 --- a/cmd/evm/testdata/13/exp2.json +++ b/cmd/evm/testdata/13/exp2.json @@ -37,6 +37,7 @@ ], "currentDifficulty": "0x20000", "gasUsed": "0x109a0", - "currentBaseFee": "0x36b" + "currentBaseFee": "0x36b", + "requests": null } } diff --git a/cmd/evm/testdata/14/exp.json b/cmd/evm/testdata/14/exp.json index 26d49173ce..300217ee34 100644 --- a/cmd/evm/testdata/14/exp.json +++ b/cmd/evm/testdata/14/exp.json @@ -8,6 +8,7 @@ "currentDifficulty": "0x2000020000000", "receipts": [], "gasUsed": "0x0", - "currentBaseFee": "0x500" + "currentBaseFee": "0x500", + "requests": null } } diff --git a/cmd/evm/testdata/14/exp2.json b/cmd/evm/testdata/14/exp2.json index cd75b47d5a..6d139e371d 100644 --- a/cmd/evm/testdata/14/exp2.json +++ b/cmd/evm/testdata/14/exp2.json @@ -8,6 +8,7 @@ "receipts": [], "currentDifficulty": "0x1ff8020000000", "gasUsed": "0x0", - "currentBaseFee": "0x500" + "currentBaseFee": "0x500", + "requests": null } } diff --git a/cmd/evm/testdata/14/exp_berlin.json b/cmd/evm/testdata/14/exp_berlin.json index 5c00ef130a..c952d0f517 100644 --- a/cmd/evm/testdata/14/exp_berlin.json +++ b/cmd/evm/testdata/14/exp_berlin.json @@ -8,6 +8,7 @@ "receipts": [], "currentDifficulty": "0x1ff9000000000", "gasUsed": "0x0", - "currentBaseFee": "0x500" + "currentBaseFee": "0x500", + "requests": null } } diff --git a/cmd/evm/testdata/19/exp_arrowglacier.json b/cmd/evm/testdata/19/exp_arrowglacier.json index dd49f7d02e..0822fcc290 100644 --- a/cmd/evm/testdata/19/exp_arrowglacier.json +++ b/cmd/evm/testdata/19/exp_arrowglacier.json @@ -8,6 +8,7 @@ "currentDifficulty": "0x2000000200000", "receipts": [], "gasUsed": "0x0", - "currentBaseFee": "0x500" + "currentBaseFee": "0x500", + "requests": null } } diff --git a/cmd/evm/testdata/19/exp_grayglacier.json b/cmd/evm/testdata/19/exp_grayglacier.json index 86fd8e6c13..e80c9eb000 100644 --- a/cmd/evm/testdata/19/exp_grayglacier.json +++ b/cmd/evm/testdata/19/exp_grayglacier.json @@ -1,13 +1,14 @@ { - "result": { - "stateRoot": "0x6f058887ca01549716789c380ede95aecc510e6d1fdc4dbf67d053c7c07f4bdc", - "txRoot": "0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421", - "receiptsRoot": "0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421", - "logsHash": "0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347", - "logsBloom": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", - "receipts": [], - "currentDifficulty": "0x2000000004000", - "gasUsed": "0x0", - "currentBaseFee": "0x500" - } -} \ No newline at end of file + "result": { + "stateRoot": "0x6f058887ca01549716789c380ede95aecc510e6d1fdc4dbf67d053c7c07f4bdc", + "txRoot": "0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421", + "receiptsRoot": "0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421", + "logsHash": "0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347", + "logsBloom": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", + "receipts": [], + "currentDifficulty": "0x2000000004000", + "gasUsed": "0x0", + "currentBaseFee": "0x500", + "requests": null + } +} diff --git a/cmd/evm/testdata/19/exp_london.json b/cmd/evm/testdata/19/exp_london.json index 9e9a17da90..a17f684484 100644 --- a/cmd/evm/testdata/19/exp_london.json +++ b/cmd/evm/testdata/19/exp_london.json @@ -8,6 +8,7 @@ "currentDifficulty": "0x2000080000000", "receipts": [], "gasUsed": "0x0", - "currentBaseFee": "0x500" + "currentBaseFee": "0x500", + "requests": null } } diff --git a/cmd/evm/testdata/23/exp.json b/cmd/evm/testdata/23/exp.json index 22dde0a27c..7f36165e35 100644 --- a/cmd/evm/testdata/23/exp.json +++ b/cmd/evm/testdata/23/exp.json @@ -21,6 +21,7 @@ } ], "currentDifficulty": "0x20000", - "gasUsed": "0x520b" + "gasUsed": "0x520b", + "requests": null } } diff --git a/cmd/evm/testdata/24/exp.json b/cmd/evm/testdata/24/exp.json index ac571d149b..8f380c662b 100644 --- a/cmd/evm/testdata/24/exp.json +++ b/cmd/evm/testdata/24/exp.json @@ -51,6 +51,7 @@ ], "currentDifficulty": null, "gasUsed": "0x10306", - "currentBaseFee": "0x500" + "currentBaseFee": "0x500", + "requests": null } } diff --git a/cmd/evm/testdata/25/exp.json b/cmd/evm/testdata/25/exp.json index 1cb521794c..a674633762 100644 --- a/cmd/evm/testdata/25/exp.json +++ b/cmd/evm/testdata/25/exp.json @@ -34,6 +34,7 @@ ], "currentDifficulty": null, "gasUsed": "0x5208", - "currentBaseFee": "0x460" + "currentBaseFee": "0x460", + "requests": null } } diff --git a/cmd/evm/testdata/26/exp.json b/cmd/evm/testdata/26/exp.json index 4815e5cb65..d6275270f0 100644 --- a/cmd/evm/testdata/26/exp.json +++ b/cmd/evm/testdata/26/exp.json @@ -15,6 +15,7 @@ "currentDifficulty": null, "gasUsed": "0x0", "currentBaseFee": "0x500", - "withdrawalsRoot": "0x4921c0162c359755b2ae714a0978a1dad2eb8edce7ff9b38b9b6fc4cbc547eb5" + "withdrawalsRoot": "0x4921c0162c359755b2ae714a0978a1dad2eb8edce7ff9b38b9b6fc4cbc547eb5", + "requests": null } } diff --git a/cmd/evm/testdata/28/exp.json b/cmd/evm/testdata/28/exp.json index 75c715e972..b86c2d8def 100644 --- a/cmd/evm/testdata/28/exp.json +++ b/cmd/evm/testdata/28/exp.json @@ -42,6 +42,7 @@ "currentBaseFee": "0x9", "withdrawalsRoot": "0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421", "currentExcessBlobGas": "0x0", - "blobGasUsed": "0x20000" + "blobGasUsed": "0x20000", + "requests": null } } diff --git a/cmd/evm/testdata/29/exp.json b/cmd/evm/testdata/29/exp.json index c4c001ec14..7fbdc18283 100644 --- a/cmd/evm/testdata/29/exp.json +++ b/cmd/evm/testdata/29/exp.json @@ -40,6 +40,7 @@ "currentBaseFee": "0x9", "withdrawalsRoot": "0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421", "currentExcessBlobGas": "0x0", - "blobGasUsed": "0x0" + "blobGasUsed": "0x0", + "requests": null } } diff --git a/cmd/evm/testdata/3/exp.json b/cmd/evm/testdata/3/exp.json index 7230dca2cf..831c078591 100644 --- a/cmd/evm/testdata/3/exp.json +++ b/cmd/evm/testdata/3/exp.json @@ -34,6 +34,7 @@ } ], "currentDifficulty": "0x20000", - "gasUsed": "0x521f" + "gasUsed": "0x521f", + "requests": null } } diff --git a/cmd/evm/testdata/30/exp.json b/cmd/evm/testdata/30/exp.json index f0b19c6b3d..a206c3bbdf 100644 --- a/cmd/evm/testdata/30/exp.json +++ b/cmd/evm/testdata/30/exp.json @@ -59,6 +59,7 @@ "currentBaseFee": "0x7", "withdrawalsRoot": "0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421", "currentExcessBlobGas": "0x0", - "blobGasUsed": "0x0" + "blobGasUsed": "0x0", + "requests": null } -} \ No newline at end of file +} diff --git a/cmd/evm/testdata/33/README.md b/cmd/evm/testdata/33/README.md new file mode 100644 index 0000000000..24bea566e5 --- /dev/null +++ b/cmd/evm/testdata/33/README.md @@ -0,0 +1 @@ +This test sets some EIP-7702 delegations and calls them. diff --git a/cmd/evm/testdata/33/alloc.json b/cmd/evm/testdata/33/alloc.json new file mode 100644 index 0000000000..6874a6b339 --- /dev/null +++ b/cmd/evm/testdata/33/alloc.json @@ -0,0 +1,30 @@ +{ + "0x8a0a19589531694250d570040a0c4b74576919b8": { + "nonce": "0x00", + "balance": "0x0de0b6b3a7640000", + "code": "0x600060006000600060007310000000000000000000000000000000000000015af1600155600060006000600060007310000000000000000000000000000000000000025af16002553d600060003e600051600355", + "storage": { + "0x01": "0x0100", + "0x02": "0x0100", + "0x03": "0x0100" + } + }, + "0x000000000000000000000000000000000000aaaa": { + "nonce": "0x00", + "balance": "0x4563918244f40000", + "code": "0x58808080600173703c4b2bd70c169f5717101caee543299fc946c75af100", + "storage": {} + }, + "0x000000000000000000000000000000000000bbbb": { + "nonce": "0x00", + "balance": "0x29a2241af62c0000", + "code": "0x6042805500", + "storage": {} + }, + "0x71562b71999873DB5b286dF957af199Ec94617F7": { + "nonce": "0x00", + "balance": "0x6124fee993bc0000", + "code": "0x", + "storage": {} + } +} diff --git a/cmd/evm/testdata/33/env.json b/cmd/evm/testdata/33/env.json new file mode 100644 index 0000000000..70bb7f9812 --- /dev/null +++ b/cmd/evm/testdata/33/env.json @@ -0,0 +1,14 @@ +{ + "currentCoinbase": "0x2adc25665018aa1fe0e6bc666dac8fc2697ff9ba", + "currentGasLimit": "71794957647893862", + "currentNumber": "1", + "currentTimestamp": "1000", + "currentRandom": "0", + "currentDifficulty": "0", + "blockHashes": {}, + "ommers": [], + "currentBaseFee": "7", + "parentUncleHash": "0x0000000000000000000000000000000000000000000000000000000000000000", + "withdrawals": [], + "parentBeaconBlockRoot": "0x0000000000000000000000000000000000000000000000000000000000000000" +} diff --git a/cmd/evm/testdata/33/exp.json b/cmd/evm/testdata/33/exp.json new file mode 100644 index 0000000000..ae82ef3efa --- /dev/null +++ b/cmd/evm/testdata/33/exp.json @@ -0,0 +1,62 @@ +{ + "alloc": { + "0x000000000000000000000000000000000000aaaa": { + "code": "0x58808080600173703c4b2bd70c169f5717101caee543299fc946c75af100", + "balance": "0x4563918244f40000" + }, + "0x000000000000000000000000000000000000bbbb": { + "code": "0x6042805500", + "balance": "0x29a2241af62c0000" + }, + "0x2adc25665018aa1fe0e6bc666dac8fc2697ff9ba": { + "balance": "0x2bf52" + }, + "0x703c4b2bd70c169f5717101caee543299fc946c7": { + "code": "0xef0100000000000000000000000000000000000000bbbb", + "storage": { + "0x0000000000000000000000000000000000000000000000000000000000000042": "0x0000000000000000000000000000000000000000000000000000000000000042" + }, + "balance": "0x1", + "nonce": "0x1" + }, + "0x71562b71999873db5b286df957af199ec94617f7": { + "code": "0xef0100000000000000000000000000000000000000aaaa", + "balance": "0x6124fee993afa30e", + "nonce": "0x2" + }, + "0x8a0a19589531694250d570040a0c4b74576919b8": { + "code": "0x600060006000600060007310000000000000000000000000000000000000015af1600155600060006000600060007310000000000000000000000000000000000000025af16002553d600060003e600051600355", + "storage": { + "0x0000000000000000000000000000000000000000000000000000000000000001": "0x0000000000000000000000000000000000000000000000000000000000000100", + "0x0000000000000000000000000000000000000000000000000000000000000002": "0x0000000000000000000000000000000000000000000000000000000000000100", + "0x0000000000000000000000000000000000000000000000000000000000000003": "0x0000000000000000000000000000000000000000000000000000000000000100" + }, + "balance": "0xde0b6b3a7640000" + } + }, + "result": { + "stateRoot": "0x9fdcacd4510e93c4488e537dc51578b5c6d505771db64a2610036eeb4be7b26f", + "txRoot": "0x5d13a0b074e80388dc754da92b22922313a63417b3e25a10f324935e09697a53", + "receiptsRoot": "0x504c5d86c34391f70d210e6c482615b391db4bdb9f43479366399d9c5599850a", + "logsHash": "0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347", + "logsBloom": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000","receipts": [{ + "type": "0x4", + "root": "0x", + "status": "0x1", + "cumulativeGasUsed": "0x15fa9", + "logsBloom": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000","logs": null,"transactionHash": "0x0417aab7c1d8a3989190c3167c132876ce9b8afd99262c5a0f9d06802de3d7ef", + "contractAddress": "0x0000000000000000000000000000000000000000", + "gasUsed": "0x15fa9", + "effectiveGasPrice": null, + "blockHash": "0x0000000000000000000000000000000000000000000000000000000000000000", + "transactionIndex": "0x0" + } + ], + "currentDifficulty": null, + "gasUsed": "0x15fa9", + "currentBaseFee": "0x7", + "withdrawalsRoot": "0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421", + "requestsHash": "0xe3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", + "requests": [] +} +} diff --git a/cmd/evm/testdata/33/txs.json b/cmd/evm/testdata/33/txs.json new file mode 100644 index 0000000000..c96cdb03ea --- /dev/null +++ b/cmd/evm/testdata/33/txs.json @@ -0,0 +1,37 @@ +[ + { + "type": "0x4", + "chainId": "0x1", + "nonce": "0x0", + "to": "0x71562b71999873db5b286df957af199ec94617f7", + "gas": "0x7a120", + "gasPrice": null, + "maxPriorityFeePerGas": "0x2", + "maxFeePerGas": "0x12a05f200", + "value": "0x0", + "input": "0x", + "accessList": [], + "authorizationList": [ + { + "chainId": "0x1", + "address": "0x000000000000000000000000000000000000aaaa", + "nonce": "0x1", + "yParity": "0x1", + "r": "0xf7e3e597fc097e71ed6c26b14b25e5395bc8510d58b9136af439e12715f2d721", + "s": "0x6cf7c3d7939bfdb784373effc0ebb0bd7549691a513f395e3cdabf8602724987" + }, + { + "chainId": "0x0", + "address": "0x000000000000000000000000000000000000bbbb", + "nonce": "0x0", + "yParity": "0x1", + "r": "0x5011890f198f0356a887b0779bde5afa1ed04e6acb1e3f37f8f18c7b6f521b98", + "s": "0x56c3fa3456b103f3ef4a0acb4b647b9cab9ec4bc68fbcdf1e10b49fb2bcbcf61" + } + ], + "secretKey": "0xb71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291", + "v": "0x0", + "r": "0x0", + "s": "0x0" + } +] diff --git a/cmd/evm/testdata/5/exp.json b/cmd/evm/testdata/5/exp.json index 7d715672c5..00af8b084d 100644 --- a/cmd/evm/testdata/5/exp.json +++ b/cmd/evm/testdata/5/exp.json @@ -18,6 +18,7 @@ "logsBloom": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", "receipts": [], "currentDifficulty": "0x20000", - "gasUsed": "0x0" + "gasUsed": "0x0", + "requests": null } } 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/accountcmd.go b/cmd/geth/accountcmd.go index cc22684e0b..b564fa3b57 100644 --- a/cmd/geth/accountcmd.go +++ b/cmd/geth/accountcmd.go @@ -17,14 +17,16 @@ package main import ( + "errors" "fmt" "os" + "strings" "github.com/ethereum/go-ethereum/accounts" "github.com/ethereum/go-ethereum/accounts/keystore" "github.com/ethereum/go-ethereum/cmd/utils" + "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/crypto" - "github.com/ethereum/go-ethereum/log" "github.com/urfave/cli/v2" ) @@ -191,7 +193,7 @@ nodes. // makeAccountManager creates an account manager with backends func makeAccountManager(ctx *cli.Context) *accounts.Manager { cfg := loadBaseConfig(ctx) - am := accounts.NewManager(&accounts.Config{InsecureUnlockAllowed: cfg.Node.InsecureUnlockAllowed}) + am := accounts.NewManager(nil) keydir, isEphemeral, err := cfg.Node.GetKeyStoreDir() if err != nil { utils.Fatalf("Failed to get the keystore directory: %v", err) @@ -219,60 +221,22 @@ func accountList(ctx *cli.Context) error { return nil } -// tries unlocking the specified account a few times. -func unlockAccount(ks *keystore.KeyStore, address string, i int, passwords []string) (accounts.Account, string) { - account, err := utils.MakeAddress(ks, address) +// readPasswordFromFile reads the first line of the given file, trims line endings, +// and returns the password and whether the reading was successful. +func readPasswordFromFile(path string) (string, bool) { + if path == "" { + return "", false + } + text, err := os.ReadFile(path) if err != nil { - utils.Fatalf("Could not list accounts: %v", err) + utils.Fatalf("Failed to read password file: %v", err) } - for trials := 0; trials < 3; trials++ { - prompt := fmt.Sprintf("Unlocking account %s | Attempt %d/%d", address, trials+1, 3) - password := utils.GetPassPhraseWithList(prompt, false, i, passwords) - err = ks.Unlock(account, password) - if err == nil { - log.Info("Unlocked account", "address", account.Address.Hex()) - return account, password - } - if err, ok := err.(*keystore.AmbiguousAddrError); ok { - log.Info("Unlocked account", "address", account.Address.Hex()) - return ambiguousAddrRecovery(ks, err, password), password - } - if err != keystore.ErrDecrypt { - // No need to prompt again if the error is not decryption-related. - break - } + lines := strings.Split(string(text), "\n") + if len(lines) == 0 { + return "", false } - // All trials expended to unlock account, bail out - utils.Fatalf("Failed to unlock account %s (%v)", address, err) - - return accounts.Account{}, "" -} - -func ambiguousAddrRecovery(ks *keystore.KeyStore, err *keystore.AmbiguousAddrError, auth string) accounts.Account { - fmt.Printf("Multiple key files exist for address %x:\n", err.Addr) - for _, a := range err.Matches { - fmt.Println(" ", a.URL) - } - fmt.Println("Testing your password against all of them...") - var match *accounts.Account - for i, a := range err.Matches { - if e := ks.Unlock(a, auth); e == nil { - match = &err.Matches[i] - break - } - } - if match == nil { - utils.Fatalf("None of the listed files could be unlocked.") - return accounts.Account{} - } - fmt.Printf("Your password unlocked %s\n", match.URL) - fmt.Println("In order to avoid this warning, you need to remove the following duplicate key files:") - for _, a := range err.Matches { - if a != *match { - fmt.Println(" ", a.URL) - } - } - return *match + // Sanitise DOS line endings. + return strings.TrimRight(lines[0], "\r"), true } // accountCreate creates a new account into the keystore defined by the CLI flags. @@ -292,8 +256,10 @@ func accountCreate(ctx *cli.Context) error { scryptP = keystore.LightScryptP } - password := utils.GetPassPhraseWithList("Your new account is locked with a password. Please give a password. Do not forget this password.", true, 0, utils.MakePasswordList(ctx)) - + password, ok := readPasswordFromFile(ctx.Path(utils.PasswordFileFlag.Name)) + if !ok { + password = utils.GetPassPhrase("Your new account is locked with a password. Please give a password. Do not forget this password.", true) + } account, err := keystore.StoreKey(keydir, password, scryptN, scryptP) if err != nil { @@ -323,10 +289,23 @@ func accountUpdate(ctx *cli.Context) error { ks := backends[0].(*keystore.KeyStore) for _, addr := range ctx.Args().Slice() { - account, oldPassword := unlockAccount(ks, addr, 0, nil) - newPassword := utils.GetPassPhraseWithList("Please give a new password. Do not forget this password.", true, 0, nil) - if err := ks.Update(account, oldPassword, newPassword); err != nil { - utils.Fatalf("Could not update the account: %v", err) + if !common.IsHexAddress(addr) { + return errors.New("address must be specified in hexadecimal form") + } + account := accounts.Account{Address: common.HexToAddress(addr)} + newPassword := utils.GetPassPhrase("Please give a NEW password. Do not forget this password.", true) + updateFn := func(attempt int) error { + prompt := fmt.Sprintf("Please provide the OLD password for account %s | Attempt %d/%d", addr, attempt+1, 3) + password := utils.GetPassPhrase(prompt, false) + return ks.Update(account, password, newPassword) + } + // let user attempt unlock thrice. + err := updateFn(0) + for attempts := 1; attempts < 3 && errors.Is(err, keystore.ErrDecrypt); attempts++ { + err = updateFn(attempts) + } + if err != nil { + return fmt.Errorf("could not update account: %w", err) } } return nil @@ -347,10 +326,12 @@ func importWallet(ctx *cli.Context) error { if len(backends) == 0 { utils.Fatalf("Keystore is not available") } + password, ok := readPasswordFromFile(ctx.Path(utils.PasswordFileFlag.Name)) + if !ok { + password = utils.GetPassPhrase("", false) + } ks := backends[0].(*keystore.KeyStore) - passphrase := utils.GetPassPhraseWithList("", false, 0, utils.MakePasswordList(ctx)) - - acct, err := ks.ImportPreSaleKey(keyJSON, passphrase) + acct, err := ks.ImportPreSaleKey(keyJSON, password) if err != nil { utils.Fatalf("%v", err) } @@ -373,9 +354,11 @@ func accountImport(ctx *cli.Context) error { utils.Fatalf("Keystore is not available") } ks := backends[0].(*keystore.KeyStore) - passphrase := utils.GetPassPhraseWithList("Your new account is locked with a password. Please give a password. Do not forget this password.", true, 0, utils.MakePasswordList(ctx)) - - acct, err := ks.ImportECDSA(key, passphrase) + password, ok := readPasswordFromFile(ctx.Path(utils.PasswordFileFlag.Name)) + if !ok { + password = utils.GetPassPhrase("Your new account is locked with a password. Please give a password. Do not forget this password.", true) + } + acct, err := ks.ImportECDSA(key, password) if err != nil { utils.Fatalf("Could not create the account: %v", err) } diff --git a/cmd/geth/accountcmd_test.go b/cmd/geth/accountcmd_test.go index 8416eb40ef..ab093b54ff 100644 --- a/cmd/geth/accountcmd_test.go +++ b/cmd/geth/accountcmd_test.go @@ -20,7 +20,6 @@ import ( "os" "path/filepath" "runtime" - "strings" "testing" "github.com/cespare/cp" @@ -171,12 +170,12 @@ func TestAccountUpdate(t *testing.T) { "f466859ead1932d743d622cb74fc058882e8648a") defer geth.ExpectExit() geth.Expect(` -Unlocking account f466859ead1932d743d622cb74fc058882e8648a | Attempt 1/3 +Please give a NEW password. Do not forget this password. !! Unsupported terminal, password will be echoed. -Password: {{.InputLine "foobar"}} -Please give a new password. Do not forget this password. Password: {{.InputLine "foobar2"}} Repeat password: {{.InputLine "foobar2"}} +Please provide the OLD password for account f466859ead1932d743d622cb74fc058882e8648a | Attempt 1/3 +Password: {{.InputLine "foobar"}} `) } @@ -206,172 +205,3 @@ Password: {{.InputLine "wrong"}} Fatal: could not decrypt key with given password `) } - -func TestUnlockFlag(t *testing.T) { - t.Parallel() - geth := runMinimalGeth(t, "--port", "0", "--ipcdisable", "--datadir", tmpDatadirWithKeystore(t), - "--unlock", "f466859ead1932d743d622cb74fc058882e8648a", "console", "--exec", "loadScript('testdata/empty.js')") - geth.Expect(` -Unlocking account f466859ead1932d743d622cb74fc058882e8648a | Attempt 1/3 -!! Unsupported terminal, password will be echoed. -Password: {{.InputLine "foobar"}} -undefined -`) - geth.ExpectExit() - - wantMessages := []string{ - "Unlocked account", - "=0xf466859eAD1932D743d622CB74FC058882E8648A", - } - for _, m := range wantMessages { - if !strings.Contains(geth.StderrText(), m) { - t.Errorf("stderr text does not contain %q", m) - } - } -} - -func TestUnlockFlagWrongPassword(t *testing.T) { - t.Parallel() - geth := runMinimalGeth(t, "--port", "0", "--ipcdisable", "--datadir", tmpDatadirWithKeystore(t), - "--unlock", "f466859ead1932d743d622cb74fc058882e8648a", "console", "--exec", "loadScript('testdata/empty.js')") - - defer geth.ExpectExit() - geth.Expect(` -Unlocking account f466859ead1932d743d622cb74fc058882e8648a | Attempt 1/3 -!! Unsupported terminal, password will be echoed. -Password: {{.InputLine "wrong1"}} -Unlocking account f466859ead1932d743d622cb74fc058882e8648a | Attempt 2/3 -Password: {{.InputLine "wrong2"}} -Unlocking account f466859ead1932d743d622cb74fc058882e8648a | Attempt 3/3 -Password: {{.InputLine "wrong3"}} -Fatal: Failed to unlock account f466859ead1932d743d622cb74fc058882e8648a (could not decrypt key with given password) -`) -} - -// https://github.com/ethereum/go-ethereum/issues/1785 -func TestUnlockFlagMultiIndex(t *testing.T) { - t.Parallel() - geth := runMinimalGeth(t, "--port", "0", "--ipcdisable", "--datadir", tmpDatadirWithKeystore(t), - "--unlock", "f466859ead1932d743d622cb74fc058882e8648a", "--unlock", "0,2", "console", "--exec", "loadScript('testdata/empty.js')") - - geth.Expect(` -Unlocking account 0 | Attempt 1/3 -!! Unsupported terminal, password will be echoed. -Password: {{.InputLine "foobar"}} -Unlocking account 2 | Attempt 1/3 -Password: {{.InputLine "foobar"}} -undefined -`) - geth.ExpectExit() - - wantMessages := []string{ - "Unlocked account", - "=0x7EF5A6135f1FD6a02593eEdC869c6D41D934aef8", - "=0x289d485D9771714CCe91D3393D764E1311907ACc", - } - for _, m := range wantMessages { - if !strings.Contains(geth.StderrText(), m) { - t.Errorf("stderr text does not contain %q", m) - } - } -} - -func TestUnlockFlagPasswordFile(t *testing.T) { - t.Parallel() - geth := runMinimalGeth(t, "--port", "0", "--ipcdisable", "--datadir", tmpDatadirWithKeystore(t), - "--unlock", "f466859ead1932d743d622cb74fc058882e8648a", "--password", "testdata/passwords.txt", "--unlock", "0,2", "console", "--exec", "loadScript('testdata/empty.js')") - - geth.Expect(` -undefined -`) - geth.ExpectExit() - - wantMessages := []string{ - "Unlocked account", - "=0x7EF5A6135f1FD6a02593eEdC869c6D41D934aef8", - "=0x289d485D9771714CCe91D3393D764E1311907ACc", - } - for _, m := range wantMessages { - if !strings.Contains(geth.StderrText(), m) { - t.Errorf("stderr text does not contain %q", m) - } - } -} - -func TestUnlockFlagPasswordFileWrongPassword(t *testing.T) { - t.Parallel() - geth := runMinimalGeth(t, "--port", "0", "--ipcdisable", "--datadir", tmpDatadirWithKeystore(t), - "--unlock", "f466859ead1932d743d622cb74fc058882e8648a", "--password", - "testdata/wrong-passwords.txt", "--unlock", "0,2") - defer geth.ExpectExit() - geth.Expect(` -Fatal: Failed to unlock account 0 (could not decrypt key with given password) -`) -} - -func TestUnlockFlagAmbiguous(t *testing.T) { - t.Parallel() - store := filepath.Join("..", "..", "accounts", "keystore", "testdata", "dupes") - geth := runMinimalGeth(t, "--port", "0", "--ipcdisable", "--datadir", tmpDatadirWithKeystore(t), - "--unlock", "f466859ead1932d743d622cb74fc058882e8648a", "--keystore", - store, "--unlock", "f466859ead1932d743d622cb74fc058882e8648a", - "console", "--exec", "loadScript('testdata/empty.js')") - defer geth.ExpectExit() - - // Helper for the expect template, returns absolute keystore path. - geth.SetTemplateFunc("keypath", func(file string) string { - abs, _ := filepath.Abs(filepath.Join(store, file)) - return abs - }) - geth.Expect(` -Unlocking account f466859ead1932d743d622cb74fc058882e8648a | Attempt 1/3 -!! Unsupported terminal, password will be echoed. -Password: {{.InputLine "foobar"}} -Multiple key files exist for address f466859ead1932d743d622cb74fc058882e8648a: - keystore://{{keypath "1"}} - keystore://{{keypath "2"}} -Testing your password against all of them... -Your password unlocked keystore://{{keypath "1"}} -In order to avoid this warning, you need to remove the following duplicate key files: - keystore://{{keypath "2"}} -undefined -`) - geth.ExpectExit() - - wantMessages := []string{ - "Unlocked account", - "=0xf466859eAD1932D743d622CB74FC058882E8648A", - } - for _, m := range wantMessages { - if !strings.Contains(geth.StderrText(), m) { - t.Errorf("stderr text does not contain %q", m) - } - } -} - -func TestUnlockFlagAmbiguousWrongPassword(t *testing.T) { - t.Parallel() - store := filepath.Join("..", "..", "accounts", "keystore", "testdata", "dupes") - geth := runMinimalGeth(t, "--port", "0", "--ipcdisable", "--datadir", tmpDatadirWithKeystore(t), - "--unlock", "f466859ead1932d743d622cb74fc058882e8648a", "--keystore", - store, "--unlock", "f466859ead1932d743d622cb74fc058882e8648a") - - defer geth.ExpectExit() - - // Helper for the expect template, returns absolute keystore path. - geth.SetTemplateFunc("keypath", func(file string) string { - abs, _ := filepath.Abs(filepath.Join(store, file)) - return abs - }) - geth.Expect(` -Unlocking account f466859ead1932d743d622cb74fc058882e8648a | Attempt 1/3 -!! Unsupported terminal, password will be echoed. -Password: {{.InputLine "wrong"}} -Multiple key files exist for address f466859ead1932d743d622cb74fc058882e8648a: - keystore://{{keypath "1"}} - keystore://{{keypath "2"}} -Testing your password against all of them... -Fatal: None of the listed files could be unlocked. -`) - geth.ExpectExit() -} diff --git a/cmd/geth/chaincmd.go b/cmd/geth/chaincmd.go index d85e4a83c8..48564eb5eb 100644 --- a/cmd/geth/chaincmd.go +++ b/cmd/geth/chaincmd.go @@ -22,6 +22,7 @@ import ( "fmt" "os" "runtime" + "slices" "strconv" "sync/atomic" "time" @@ -36,9 +37,7 @@ import ( "github.com/ethereum/go-ethereum/crypto" "github.com/ethereum/go-ethereum/ethdb" "github.com/ethereum/go-ethereum/internal/era" - "github.com/ethereum/go-ethereum/internal/flags" "github.com/ethereum/go-ethereum/log" - "github.com/ethereum/go-ethereum/metrics" "github.com/ethereum/go-ethereum/params" "github.com/urfave/cli/v2" ) @@ -49,7 +48,7 @@ var ( Name: "init", Usage: "Bootstrap and initialize a new genesis block", ArgsUsage: "", - Flags: flags.Merge([]cli.Flag{ + Flags: slices.Concat([]cli.Flag{ utils.CachePreimagesFlag, utils.OverrideCancun, utils.OverrideVerkle, @@ -76,7 +75,7 @@ if one is set. Otherwise it prints the genesis from the datadir.`, Name: "import", Usage: "Import a blockchain file", ArgsUsage: " ( ... ) ", - Flags: flags.Merge([]cli.Flag{ + Flags: slices.Concat([]cli.Flag{ utils.CacheFlag, utils.SyncModeFlag, utils.GCModeFlag, @@ -115,7 +114,7 @@ processing will proceed even if an individual RLP-file import failure occurs.`, Name: "export", Usage: "Export blockchain into file", ArgsUsage: " [ ]", - Flags: flags.Merge([]cli.Flag{ + Flags: slices.Concat([]cli.Flag{ utils.CacheFlag, utils.SyncModeFlag, }, utils.DatabaseFlags), @@ -131,7 +130,7 @@ be gzipped.`, Name: "import-history", Usage: "Import an Era archive", ArgsUsage: "", - Flags: flags.Merge([]cli.Flag{ + Flags: slices.Concat([]cli.Flag{ utils.TxLookupLimitFlag, }, utils.DatabaseFlags, @@ -147,7 +146,7 @@ from Era archives. Name: "export-history", Usage: "Export blockchain history to Era archives", ArgsUsage: " ", - Flags: flags.Merge(utils.DatabaseFlags), + Flags: slices.Concat(utils.DatabaseFlags), Description: ` The export-history command will export blocks and their corresponding receipts into Era archives. Eras are typically packaged in steps of 8192 blocks. @@ -158,7 +157,7 @@ into Era archives. Eras are typically packaged in steps of 8192 blocks. Name: "import-preimages", Usage: "Import the preimage database from an RLP stream", ArgsUsage: "", - Flags: flags.Merge([]cli.Flag{ + Flags: slices.Concat([]cli.Flag{ utils.CacheFlag, utils.SyncModeFlag, }, utils.DatabaseFlags), @@ -173,7 +172,7 @@ It's deprecated, please use "geth db import" instead. Name: "dump", Usage: "Dump a specific block from storage", ArgsUsage: "[? | ]", - Flags: flags.Merge([]cli.Flag{ + Flags: slices.Concat([]cli.Flag{ utils.CacheFlag, utils.IterativeOutputFlag, utils.ExcludeCodeFlag, @@ -282,14 +281,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, _ := makeConfigNode(ctx) + stack, cfg := makeConfigNode(ctx) defer stack.Close() + // Start metrics export if enabled + utils.SetupMetrics(&cfg.Metrics) + chain, db := utils.MakeChain(ctx, stack, false) defer db.Close() diff --git a/cmd/geth/config.go b/cmd/geth/config.go index fd57ff40de..ecee2bfd80 100644 --- a/cmd/geth/config.go +++ b/cmd/geth/config.go @@ -23,6 +23,7 @@ import ( "os" "reflect" "runtime" + "slices" "strings" "unicode" @@ -42,7 +43,6 @@ import ( "github.com/ethereum/go-ethereum/log" "github.com/ethereum/go-ethereum/metrics" "github.com/ethereum/go-ethereum/node" - "github.com/ethereum/go-ethereum/params" "github.com/ethereum/go-ethereum/rpc" "github.com/naoina/toml" "github.com/urfave/cli/v2" @@ -54,7 +54,7 @@ var ( Name: "dumpconfig", Usage: "Export configuration values in a TOML format", ArgsUsage: "", - Flags: flags.Merge(nodeFlags, rpcFlags), + Flags: slices.Concat(nodeFlags, rpcFlags), Description: `Export configuration values in TOML format (to stdout by default).`, } @@ -130,10 +130,10 @@ func defaultNodeConfig() node.Config { git, _ := version.VCS() cfg := node.DefaultConfig cfg.Name = clientIdentifier - cfg.Version = params.VersionWithCommit(git.Commit, git.Date) + cfg.Version = version.WithCommit(git.Commit, git.Date) cfg.HTTPModules = append(cfg.HTTPModules, "eth") cfg.WSModules = append(cfg.WSModules, "eth") - cfg.IPCPath = "geth.ipc" + cfg.IPCPath = clientIdentifier + ".ipc" return cfg } @@ -192,6 +192,9 @@ func makeFullNode(ctx *cli.Context) *node.Node { cfg.Eth.OverrideVerkle = &v } + // Start metrics export if enabled + utils.SetupMetrics(&cfg.Metrics) + backend, eth := utils.RegisterEthService(stack, &cfg.Eth) // Create gauge with geth system and build information @@ -240,7 +243,7 @@ func makeFullNode(ctx *cli.Context) *node.Node { // Start blsync mode. srv := rpc.NewServer() srv.RegisterName("engine", catalyst.NewConsensusAPI(eth)) - blsyncer := blsync.NewClient(ctx) + blsyncer := blsync.NewClient(utils.MakeBeaconLightConfig(ctx)) blsyncer.SetEngineRPC(rpc.DialInProc(srv)) stack.RegisterLifecycle(blsyncer) } else { @@ -325,6 +328,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/geth/consolecmd.go b/cmd/geth/consolecmd.go index 2a59f0052f..bf38c86349 100644 --- a/cmd/geth/consolecmd.go +++ b/cmd/geth/consolecmd.go @@ -18,11 +18,11 @@ package main import ( "fmt" + "slices" "strings" "github.com/ethereum/go-ethereum/cmd/utils" "github.com/ethereum/go-ethereum/console" - "github.com/ethereum/go-ethereum/internal/flags" "github.com/urfave/cli/v2" ) @@ -33,7 +33,7 @@ var ( Action: localConsole, Name: "console", Usage: "Start an interactive JavaScript environment", - Flags: flags.Merge(nodeFlags, rpcFlags, consoleFlags), + Flags: slices.Concat(nodeFlags, rpcFlags, consoleFlags), Description: ` The Geth console is an interactive shell for the JavaScript runtime environment which exposes a node admin interface as well as the Ðapp JavaScript API. @@ -45,7 +45,7 @@ See https://geth.ethereum.org/docs/interacting-with-geth/javascript-console.`, Name: "attach", Usage: "Start an interactive JavaScript environment (connect to node)", ArgsUsage: "[endpoint]", - Flags: flags.Merge([]cli.Flag{utils.DataDirFlag, utils.HttpHeaderFlag}, consoleFlags), + Flags: slices.Concat([]cli.Flag{utils.DataDirFlag, utils.HttpHeaderFlag}, consoleFlags), Description: ` The Geth console is an interactive shell for the JavaScript runtime environment which exposes a node admin interface as well as the Ðapp JavaScript API. @@ -58,7 +58,7 @@ This command allows to open a console on a running geth node.`, Name: "js", Usage: "(DEPRECATED) Execute the specified JavaScript files", ArgsUsage: " [jsfile...]", - Flags: flags.Merge(nodeFlags, consoleFlags), + Flags: slices.Concat(nodeFlags, consoleFlags), Description: ` The JavaScript VM exposes a node admin interface as well as the Ðapp JavaScript API. See https://geth.ethereum.org/docs/interacting-with-geth/javascript-console`, diff --git a/cmd/geth/consolecmd_test.go b/cmd/geth/consolecmd_test.go index ffaf17741f..b8c2c498a6 100644 --- a/cmd/geth/consolecmd_test.go +++ b/cmd/geth/consolecmd_test.go @@ -26,7 +26,7 @@ import ( "testing" "time" - "github.com/ethereum/go-ethereum/params" + "github.com/ethereum/go-ethereum/internal/version" ) const ( @@ -60,7 +60,7 @@ func TestConsoleWelcome(t *testing.T) { geth.SetTemplateFunc("goos", func() string { return runtime.GOOS }) geth.SetTemplateFunc("goarch", func() string { return runtime.GOARCH }) geth.SetTemplateFunc("gover", runtime.Version) - geth.SetTemplateFunc("gethver", func() string { return params.VersionWithCommit("", "") }) + geth.SetTemplateFunc("gethver", func() string { return version.WithCommit("", "") }) geth.SetTemplateFunc("niltime", func() string { return time.Unix(1695902100, 0).Format("Mon Jan 02 2006 15:04:05 GMT-0700 (MST)") }) @@ -129,7 +129,7 @@ func testAttachWelcome(t *testing.T, geth *testgeth, endpoint, apis string) { attach.SetTemplateFunc("goos", func() string { return runtime.GOOS }) attach.SetTemplateFunc("goarch", func() string { return runtime.GOARCH }) attach.SetTemplateFunc("gover", runtime.Version) - attach.SetTemplateFunc("gethver", func() string { return params.VersionWithCommit("", "") }) + attach.SetTemplateFunc("gethver", func() string { return version.WithCommit("", "") }) attach.SetTemplateFunc("niltime", func() string { return time.Unix(1695902100, 0).Format("Mon Jan 02 2006 15:04:05 GMT-0700 (MST)") }) diff --git a/cmd/geth/dbcmd.go b/cmd/geth/dbcmd.go index 052ae0eab2..7622246050 100644 --- a/cmd/geth/dbcmd.go +++ b/cmd/geth/dbcmd.go @@ -22,6 +22,7 @@ import ( "os" "os/signal" "path/filepath" + "slices" "strconv" "strings" "syscall" @@ -36,7 +37,6 @@ import ( "github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/crypto" "github.com/ethereum/go-ethereum/ethdb" - "github.com/ethereum/go-ethereum/internal/flags" "github.com/ethereum/go-ethereum/log" "github.com/ethereum/go-ethereum/rlp" "github.com/ethereum/go-ethereum/trie" @@ -60,7 +60,7 @@ var ( Name: "removedb", Usage: "Remove blockchain and state databases", ArgsUsage: "", - Flags: flags.Merge(utils.DatabaseFlags, + Flags: slices.Concat(utils.DatabaseFlags, []cli.Flag{removeStateDataFlag, removeChainDataFlag}), Description: ` Remove blockchain and state databases`, @@ -89,7 +89,7 @@ Remove blockchain and state databases`, Action: inspect, Name: "inspect", ArgsUsage: " ", - Flags: flags.Merge([]cli.Flag{ + Flags: slices.Concat([]cli.Flag{ utils.SyncModeFlag, }, utils.NetworkFlags, utils.DatabaseFlags), Usage: "Inspect the storage size for each type of data in the database", @@ -99,7 +99,7 @@ Remove blockchain and state databases`, Action: checkStateContent, Name: "check-state-content", ArgsUsage: "", - Flags: flags.Merge(utils.NetworkFlags, utils.DatabaseFlags), + Flags: slices.Concat(utils.NetworkFlags, utils.DatabaseFlags), Usage: "Verify that state data is cryptographically correct", Description: `This command iterates the entire database for 32-byte keys, looking for rlp-encoded trie nodes. For each trie node encountered, it checks that the key corresponds to the keccak256(value). If this is not true, this indicates @@ -109,7 +109,7 @@ a data corruption.`, Action: dbStats, Name: "stats", Usage: "Print leveldb statistics", - Flags: flags.Merge([]cli.Flag{ + Flags: slices.Concat([]cli.Flag{ utils.SyncModeFlag, }, utils.NetworkFlags, utils.DatabaseFlags), } @@ -117,7 +117,7 @@ a data corruption.`, Action: dbCompact, Name: "compact", Usage: "Compact leveldb database. WARNING: May take a very long time", - Flags: flags.Merge([]cli.Flag{ + Flags: slices.Concat([]cli.Flag{ utils.SyncModeFlag, utils.CacheFlag, utils.CacheDatabaseFlag, @@ -131,7 +131,7 @@ corruption if it is aborted during execution'!`, Name: "get", Usage: "Show the value of a database key", ArgsUsage: "", - Flags: flags.Merge([]cli.Flag{ + Flags: slices.Concat([]cli.Flag{ utils.SyncModeFlag, }, utils.NetworkFlags, utils.DatabaseFlags), Description: "This command looks up the specified database key from the database.", @@ -141,7 +141,7 @@ corruption if it is aborted during execution'!`, Name: "delete", Usage: "Delete a database key (WARNING: may corrupt your database)", ArgsUsage: "", - Flags: flags.Merge([]cli.Flag{ + Flags: slices.Concat([]cli.Flag{ utils.SyncModeFlag, }, utils.NetworkFlags, utils.DatabaseFlags), Description: `This command deletes the specified database key from the database. @@ -152,7 +152,7 @@ WARNING: This is a low-level operation which may cause database corruption!`, Name: "put", Usage: "Set the value of a database key (WARNING: may corrupt your database)", ArgsUsage: " ", - Flags: flags.Merge([]cli.Flag{ + Flags: slices.Concat([]cli.Flag{ utils.SyncModeFlag, }, utils.NetworkFlags, utils.DatabaseFlags), Description: `This command sets a given database key to the given value. @@ -163,7 +163,7 @@ WARNING: This is a low-level operation which may cause database corruption!`, Name: "dumptrie", Usage: "Show the storage key/values of a given storage trie", ArgsUsage: " ", - Flags: flags.Merge([]cli.Flag{ + Flags: slices.Concat([]cli.Flag{ utils.SyncModeFlag, }, utils.NetworkFlags, utils.DatabaseFlags), Description: "This command looks up the specified database key from the database.", @@ -173,7 +173,7 @@ WARNING: This is a low-level operation which may cause database corruption!`, Name: "freezer-index", Usage: "Dump out the index of a specific freezer table", ArgsUsage: " ", - Flags: flags.Merge([]cli.Flag{ + Flags: slices.Concat([]cli.Flag{ utils.SyncModeFlag, }, utils.NetworkFlags, utils.DatabaseFlags), Description: "This command displays information about the freezer index.", @@ -183,7 +183,7 @@ WARNING: This is a low-level operation which may cause database corruption!`, Name: "import", Usage: "Imports leveldb-data from an exported RLP dump.", ArgsUsage: " has .gz suffix, gzip compression will be used.", ArgsUsage: " ", - Flags: flags.Merge([]cli.Flag{ + Flags: slices.Concat([]cli.Flag{ utils.SyncModeFlag, }, utils.NetworkFlags, utils.DatabaseFlags), Description: "Exports the specified chain data to an RLP encoded stream, optionally gzip-compressed.", @@ -202,7 +202,7 @@ WARNING: This is a low-level operation which may cause database corruption!`, Action: showMetaData, Name: "metadata", Usage: "Shows metadata about the chain status.", - Flags: flags.Merge([]cli.Flag{ + Flags: slices.Concat([]cli.Flag{ utils.SyncModeFlag, }, utils.NetworkFlags, utils.DatabaseFlags), Description: "Shows metadata about the chain status.", @@ -212,7 +212,7 @@ WARNING: This is a low-level operation which may cause database corruption!`, Name: "inspect-history", Usage: "Inspect the state history within block range", ArgsUsage: "
[OPTIONAL ]", - Flags: flags.Merge([]cli.Flag{ + Flags: slices.Concat([]cli.Flag{ utils.SyncModeFlag, &cli.Uint64Flag{ Name: "start", diff --git a/cmd/geth/genesis_test.go b/cmd/geth/genesis_test.go index ffe8176b01..f5d21cc00b 100644 --- a/cmd/geth/genesis_test.go +++ b/cmd/geth/genesis_test.go @@ -29,7 +29,7 @@ var customGenesisTests = []struct { query string result string }{ - // Genesis file with an empty chain configuration (ensure missing fields work) + // Genesis file with a mostly-empty chain configuration (ensure missing fields work) { genesis: `{ "alloc" : {}, @@ -41,8 +41,8 @@ var customGenesisTests = []struct { "mixhash" : "0x0000000000000000000000000000000000000000000000000000000000000000", "parentHash" : "0x0000000000000000000000000000000000000000000000000000000000000000", "timestamp" : "0x00", - "config" : { - "terminalTotalDifficultyPassed": true + "config": { + "terminalTotalDifficulty": 0 } }`, query: "eth.getBlock(0).nonce", @@ -64,7 +64,7 @@ var customGenesisTests = []struct { "homesteadBlock" : 42, "daoForkBlock" : 141, "daoForkSupport" : true, - "terminalTotalDifficultyPassed" : true + "terminalTotalDifficulty": 0 } }`, query: "eth.getBlock(0).nonce", @@ -114,8 +114,8 @@ func TestCustomBackend(t *testing.T) { "mixhash" : "0x0000000000000000000000000000000000000000000000000000000000000000", "parentHash" : "0x0000000000000000000000000000000000000000000000000000000000000000", "timestamp" : "0x00", - "config" : { - "terminalTotalDifficultyPassed": true + "config": { + "terminalTotalDifficulty": 0 } }` type backendTest struct { diff --git a/cmd/geth/main.go b/cmd/geth/main.go index 2675a61675..9d9256862b 100644 --- a/cmd/geth/main.go +++ b/cmd/geth/main.go @@ -20,13 +20,12 @@ package main import ( "fmt" "os" + "slices" "sort" "strconv" - "strings" "time" "github.com/ethereum/go-ethereum/accounts" - "github.com/ethereum/go-ethereum/accounts/keystore" "github.com/ethereum/go-ethereum/cmd/utils" "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/console/prompt" @@ -35,7 +34,6 @@ import ( "github.com/ethereum/go-ethereum/internal/debug" "github.com/ethereum/go-ethereum/internal/flags" "github.com/ethereum/go-ethereum/log" - "github.com/ethereum/go-ethereum/metrics" "github.com/ethereum/go-ethereum/node" "go.uber.org/automaxprocs/maxprocs" @@ -53,7 +51,7 @@ const ( var ( // flags that configure the node - nodeFlags = flags.Merge([]cli.Flag{ + nodeFlags = slices.Concat([]cli.Flag{ utils.IdentityFlag, utils.UnlockedAccountFlag, utils.PasswordFileFlag, @@ -66,7 +64,7 @@ var ( utils.SmartCardDaemonPathFlag, utils.OverrideCancun, utils.OverrideVerkle, - utils.EnablePersonal, + utils.EnablePersonal, // deprecated utils.TxPoolLocalsFlag, utils.TxPoolNoLocalsFlag, utils.TxPoolJournalFlag, @@ -251,7 +249,7 @@ func init() { } sort.Sort(cli.CommandsByName(app.Commands)) - app.Flags = flags.Merge( + app.Flags = slices.Concat( nodeFlags, rpcFlags, consoleFlags, @@ -326,12 +324,6 @@ func prepare(ctx *cli.Context) { ctx.Set(utils.CacheFlag.Name, strconv.Itoa(4096)) } } - - // Start metrics export if enabled - utils.SetupMetrics(ctx) - - // Start system runtime metrics collection - go metrics.CollectProcessMetrics(3 * time.Second) } // geth is the main entry point into the system if no special subcommand is run. @@ -352,14 +344,14 @@ func geth(ctx *cli.Context) error { } // startNode boots up the system node and all registered protocols, after which -// it unlocks any requested accounts, and starts the RPC/IPC interfaces and the -// miner. +// it starts the RPC/IPC interfaces and the miner. func startNode(ctx *cli.Context, stack *node.Node, isConsole bool) { // Start up the node itself utils.StartNode(ctx, stack, isConsole) - // Unlock any account specifically requested - unlockAccounts(ctx, stack) + if ctx.IsSet(utils.UnlockedAccountFlag.Name) { + log.Warn(`The "unlock" flag has been deprecated and has no effect`) + } // Register wallet event handlers to open and auto-derive wallets events := make(chan accounts.WalletEvent, 16) @@ -426,33 +418,3 @@ func startNode(ctx *cli.Context, stack *node.Node, isConsole bool) { }() } } - -// unlockAccounts unlocks any account specifically requested. -func unlockAccounts(ctx *cli.Context, stack *node.Node) { - var unlocks []string - inputs := strings.Split(ctx.String(utils.UnlockedAccountFlag.Name), ",") - for _, input := range inputs { - if trimmed := strings.TrimSpace(input); trimmed != "" { - unlocks = append(unlocks, trimmed) - } - } - // Short circuit if there is no account to unlock. - if len(unlocks) == 0 { - return - } - // If insecure account unlocking is not allowed if node's APIs are exposed to external. - // Print warning log to user and skip unlocking. - if !stack.Config().InsecureUnlockAllowed && stack.Config().ExtRPCEnabled() { - utils.Fatalf("Account unlock with HTTP access is forbidden!") - } - backends := stack.AccountManager().Backends(keystore.KeyStoreType) - if len(backends) == 0 { - log.Warn("Failed to unlock accounts, keystore is not available") - return - } - ks := backends[0].(*keystore.KeyStore) - passwords := utils.MakePasswordList(ctx) - for i, account := range unlocks { - unlockAccount(ks, account, i, passwords) - } -} diff --git a/cmd/geth/misccmd.go b/cmd/geth/misccmd.go index f3530c30fb..2d31f3abe7 100644 --- a/cmd/geth/misccmd.go +++ b/cmd/geth/misccmd.go @@ -23,7 +23,6 @@ import ( "strings" "github.com/ethereum/go-ethereum/internal/version" - "github.com/ethereum/go-ethereum/params" "github.com/urfave/cli/v2" ) @@ -73,7 +72,7 @@ func printVersion(ctx *cli.Context) error { git, _ := version.VCS() fmt.Println(strings.Title(clientIdentifier)) - fmt.Println("Version:", params.VersionWithMeta) + fmt.Println("Version:", version.WithMeta) if git.Commit != "" { fmt.Println("Git Commit:", git.Commit) } diff --git a/cmd/geth/snapshot.go b/cmd/geth/snapshot.go index 7d713ad110..f0be52a0df 100644 --- a/cmd/geth/snapshot.go +++ b/cmd/geth/snapshot.go @@ -22,6 +22,7 @@ import ( "errors" "fmt" "os" + "slices" "time" "github.com/ethereum/go-ethereum/cmd/utils" @@ -32,7 +33,6 @@ import ( "github.com/ethereum/go-ethereum/core/state/snapshot" "github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/crypto" - "github.com/ethereum/go-ethereum/internal/flags" "github.com/ethereum/go-ethereum/log" "github.com/ethereum/go-ethereum/rlp" "github.com/ethereum/go-ethereum/trie" @@ -50,7 +50,7 @@ var ( Usage: "Prune stale ethereum state data based on the snapshot", ArgsUsage: "", Action: pruneState, - Flags: flags.Merge([]cli.Flag{ + Flags: slices.Concat([]cli.Flag{ utils.BloomFilterSizeFlag, }, utils.NetworkFlags, utils.DatabaseFlags), Description: ` @@ -70,7 +70,7 @@ WARNING: it's only supported in hash mode(--state.scheme=hash)". Usage: "Recalculate state hash based on the snapshot for verification", ArgsUsage: "", Action: verifyState, - Flags: flags.Merge(utils.NetworkFlags, utils.DatabaseFlags), + Flags: slices.Concat(utils.NetworkFlags, utils.DatabaseFlags), Description: ` geth snapshot verify-state will traverse the whole accounts and storages set based on the specified @@ -83,7 +83,7 @@ In other words, this command does the snapshot to trie conversion. Usage: "Check that there is no 'dangling' snap storage", ArgsUsage: "", Action: checkDanglingStorage, - Flags: flags.Merge(utils.NetworkFlags, utils.DatabaseFlags), + Flags: slices.Concat(utils.NetworkFlags, utils.DatabaseFlags), Description: ` geth snapshot check-dangling-storage traverses the snap storage data, and verifies that all snapshot storage data has a corresponding account. @@ -94,7 +94,7 @@ data, and verifies that all snapshot storage data has a corresponding account. Usage: "Check all snapshot layers for the specific account", ArgsUsage: "
", Action: checkAccount, - Flags: flags.Merge(utils.NetworkFlags, utils.DatabaseFlags), + Flags: slices.Concat(utils.NetworkFlags, utils.DatabaseFlags), Description: ` geth snapshot inspect-account
checks all snapshot layers and prints out information about the specified address. @@ -105,7 +105,7 @@ information about the specified address. Usage: "Traverse the state with given root hash and perform quick verification", ArgsUsage: "", Action: traverseState, - Flags: flags.Merge(utils.NetworkFlags, utils.DatabaseFlags), + Flags: slices.Concat(utils.NetworkFlags, utils.DatabaseFlags), Description: ` geth snapshot traverse-state will traverse the whole state from the given state root and will abort if any @@ -120,7 +120,7 @@ It's also usable without snapshot enabled. Usage: "Traverse the state with given root hash and perform detailed verification", ArgsUsage: "", Action: traverseRawState, - Flags: flags.Merge(utils.NetworkFlags, utils.DatabaseFlags), + Flags: slices.Concat(utils.NetworkFlags, utils.DatabaseFlags), Description: ` geth snapshot traverse-rawstate will traverse the whole state from the given root and will abort if any referenced @@ -136,7 +136,7 @@ It's also usable without snapshot enabled. Usage: "Dump a specific block from storage (same as 'geth dump' but using snapshots)", ArgsUsage: "[? | ]", Action: dumpState, - Flags: flags.Merge([]cli.Flag{ + Flags: slices.Concat([]cli.Flag{ utils.ExcludeCodeFlag, utils.ExcludeStorageFlag, utils.StartKeyFlag, @@ -428,7 +428,7 @@ func traverseRawState(ctx *cli.Context) error { log.Error("Failed to open iterator", "root", root, "err", err) return err } - reader, err := triedb.Reader(root) + reader, err := triedb.NodeReader(root) if err != nil { log.Error("State is non-existent", "root", root) return nil diff --git a/cmd/geth/testdata/clique.json b/cmd/geth/testdata/clique.json index 36f5c31057..d318f4c166 100644 --- a/cmd/geth/testdata/clique.json +++ b/cmd/geth/testdata/clique.json @@ -8,7 +8,7 @@ "byzantiumBlock": 0, "constantinopleBlock": 0, "petersburgBlock": 0, - "terminalTotalDifficultyPassed": true, + "terminalTotalDifficulty": 0, "clique": { "period": 5, "epoch": 30000 @@ -22,4 +22,4 @@ "balance": "300000" } } -} \ No newline at end of file +} diff --git a/cmd/geth/verkle.go b/cmd/geth/verkle.go index 9eb37fb5a8..6490f832af 100644 --- a/cmd/geth/verkle.go +++ b/cmd/geth/verkle.go @@ -22,11 +22,11 @@ import ( "errors" "fmt" "os" + "slices" "github.com/ethereum/go-ethereum/cmd/utils" "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/core/rawdb" - "github.com/ethereum/go-ethereum/internal/flags" "github.com/ethereum/go-ethereum/log" "github.com/ethereum/go-verkle" "github.com/urfave/cli/v2" @@ -45,7 +45,7 @@ var ( Usage: "verify the conversion of a MPT into a verkle tree", ArgsUsage: "", Action: verifyVerkle, - Flags: flags.Merge(utils.NetworkFlags, utils.DatabaseFlags), + Flags: slices.Concat(utils.NetworkFlags, utils.DatabaseFlags), Description: ` geth verkle verify This command takes a root commitment and attempts to rebuild the tree. @@ -56,7 +56,7 @@ This command takes a root commitment and attempts to rebuild the tree. Usage: "Dump a verkle tree to a DOT file", ArgsUsage: " [ ...]", Action: expandVerkle, - Flags: flags.Merge(utils.NetworkFlags, utils.DatabaseFlags), + Flags: slices.Concat(utils.NetworkFlags, utils.DatabaseFlags), Description: ` geth verkle dump [ ...] This command will produce a dot file representing the tree, rooted at . diff --git a/cmd/utils/flags.go b/cmd/utils/flags.go index 6db88ff661..8ef39e88de 100644 --- a/cmd/utils/flags.go +++ b/cmd/utils/flags.go @@ -40,6 +40,7 @@ import ( bparams "github.com/ethereum/go-ethereum/beacon/params" "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common/fdlimit" + "github.com/ethereum/go-ethereum/common/hexutil" "github.com/ethereum/go-ethereum/core" "github.com/ethereum/go-ethereum/core/rawdb" "github.com/ethereum/go-ethereum/core/txpool/blobpool" @@ -49,7 +50,6 @@ import ( "github.com/ethereum/go-ethereum/crypto/kzg4844" "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/eth/gasprice" @@ -176,12 +176,6 @@ var ( Usage: "Custom node name", Category: flags.NetworkingCategory, } - DocRootFlag = &flags.DirectoryFlag{ - Name: "docroot", - Usage: "Document Root for HTTPClient file scheme", - Value: flags.DirectoryString(flags.HomeDir()), - Category: flags.APICategory, - } ExitWhenSyncedFlag = &cli.BoolFlag{ Name: "exitwhensynced", Usage: "Exits after block synchronisation completes", @@ -217,8 +211,7 @@ var ( Value: 0, } - defaultSyncMode = ethconfig.Defaults.SyncMode - SnapshotFlag = &cli.BoolFlag{ + SnapshotFlag = &cli.BoolFlag{ Name: "snapshot", Usage: `Enables snapshot-database mode (default = enable)`, Value: true, @@ -250,10 +243,10 @@ var ( Usage: "Manually specify the Verkle fork timestamp, overriding the bundled setting", Category: flags.EthCategory, } - SyncModeFlag = &flags.TextMarshalerFlag{ + SyncModeFlag = &cli.StringFlag{ Name: "syncmode", Usage: `Blockchain sync mode ("snap" or "full")`, - Value: &defaultSyncMode, + Value: ethconfig.Defaults.SyncMode.String(), Category: flags.StateCategory, } GCModeFlag = &cli.StringFlag{ @@ -326,7 +319,7 @@ var ( Usage: "Target EL engine API URL", Category: flags.BeaconCategory, } - BlsyncJWTSecretFlag = &cli.StringFlag{ + BlsyncJWTSecretFlag = &flags.DirectoryFlag{ Name: "blsync.jwtsecret", Usage: "Path to a JWT secret to use for target engine API endpoint", Category: flags.BeaconCategory, @@ -505,12 +498,6 @@ var ( } // Account settings - UnlockedAccountFlag = &cli.StringFlag{ - Name: "unlock", - Usage: "Comma separated list of accounts to unlock", - Value: "", - Category: flags.AccountCategory, - } PasswordFileFlag = &cli.PathFlag{ Name: "password", Usage: "Password file to use for non-interactive password input", @@ -523,12 +510,6 @@ var ( Value: "", Category: flags.AccountCategory, } - InsecureUnlockAllowedFlag = &cli.BoolFlag{ - Name: "allow-insecure-unlock", - Usage: "Allow insecure account unlocking when account-related RPCs are exposed by http", - Category: flags.AccountCategory, - } - // EVM settings VMEnableDebugFlag = &cli.BoolFlag{ Name: "vmdebug", @@ -543,6 +524,7 @@ var ( VMTraceJsonConfigFlag = &cli.StringFlag{ Name: "vmtrace.jsonconfig", Usage: "Tracer configuration (JSON)", + Value: "{}", Category: flags.VMCategory, } // API options. @@ -740,11 +722,6 @@ var ( Value: node.DefaultConfig.BatchResponseMaxSize, Category: flags.APICategory, } - EnablePersonal = &cli.BoolFlag{ - Name: "rpc.enabledeprecatedpersonal", - Usage: "Enables the (deprecated) personal namespace", - Category: flags.APICategory, - } // Network Settings MaxPeersFlag = &cli.IntFlag{ @@ -1278,31 +1255,6 @@ func MakeDatabaseHandles(max int) int { return int(raised / 2) // Leave half for networking and other stuff } -// MakeAddress converts an account specified directly as a hex encoded string or -// a key index in the key store to an internal account representation. -func MakeAddress(ks *keystore.KeyStore, account string) (accounts.Account, error) { - // If the specified account is a valid address, return it - if common.IsHexAddress(account) { - return accounts.Account{Address: common.HexToAddress(account)}, nil - } - // Otherwise try to interpret the account as a keystore index - index, err := strconv.Atoi(account) - if err != nil || index < 0 { - return accounts.Account{}, fmt.Errorf("invalid account address or index %q", account) - } - log.Warn("-------------------------------------------------------------------") - log.Warn("Referring to accounts by order in the keystore folder is dangerous!") - log.Warn("This functionality is deprecated and will be removed in the future!") - log.Warn("Please use explicit addresses! (can search via `geth account list`)") - log.Warn("-------------------------------------------------------------------") - - accs := ks.Accounts() - if len(accs) <= index { - return accounts.Account{}, fmt.Errorf("index %d higher than number of accounts %d", index, len(accs)) - } - return accs[index], nil -} - // setEtherbase retrieves the etherbase from the directly specified command line flags. func setEtherbase(ctx *cli.Context, cfg *ethconfig.Config) { if ctx.IsSet(MinerEtherbaseFlag.Name) { @@ -1323,24 +1275,6 @@ func setEtherbase(ctx *cli.Context, cfg *ethconfig.Config) { cfg.Miner.PendingFeeRecipient = common.BytesToAddress(b) } -// MakePasswordList reads password lines from the file specified by the global --password flag. -func MakePasswordList(ctx *cli.Context) []string { - path := ctx.Path(PasswordFileFlag.Name) - if path == "" { - return nil - } - text, err := os.ReadFile(path) - if err != nil { - Fatalf("Failed to read password file: %v", err) - } - lines := strings.Split(string(text), "\n") - // Sanitise DOS line endings. - for i := range lines { - lines[i] = strings.TrimRight(lines[i], "\r") - } - return lines -} - func SetP2PConfig(ctx *cli.Context, cfg *p2p.Config) { setNodeKey(ctx, cfg) setNAT(ctx, cfg) @@ -1398,9 +1332,8 @@ func SetNodeConfig(ctx *cli.Context, cfg *node.Config) { if ctx.IsSet(JWTSecretFlag.Name) { cfg.JWTSecret = ctx.String(JWTSecretFlag.Name) } - if ctx.IsSet(EnablePersonal.Name) { - cfg.EnablePersonal = true + log.Warn(fmt.Sprintf("Option --%s is deprecated. The 'personal' RPC namespace has been removed.", EnablePersonal.Name)) } if ctx.IsSet(ExternalSignerFlag.Name) { @@ -1423,7 +1356,7 @@ func SetNodeConfig(ctx *cli.Context, cfg *node.Config) { cfg.USB = ctx.Bool(USBFlag.Name) } if ctx.IsSet(InsecureUnlockAllowedFlag.Name) { - cfg.InsecureUnlockAllowed = ctx.Bool(InsecureUnlockAllowedFlag.Name) + log.Warn(fmt.Sprintf("Option %q is deprecated and has no effect", InsecureUnlockAllowedFlag.Name)) } if ctx.IsSet(DBEngineFlag.Name) { dbEngine := ctx.String(DBEngineFlag.Name) @@ -1672,9 +1605,11 @@ 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) { - cfg.SyncMode = *flags.GlobalTextMarshaler(ctx, SyncModeFlag.Name).(*downloader.SyncMode) + if err = cfg.SyncMode.UnmarshalText([]byte(ctx.String(SyncModeFlag.Name))); err != nil { + Fatalf("invalid --syncmode flag: %v", err) + } } if ctx.IsSet(NetworkIdFlag.Name) { cfg.NetworkId = ctx.Uint64(NetworkIdFlag.Name) @@ -1741,7 +1676,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") } @@ -1754,9 +1689,6 @@ func SetEthConfig(ctx *cli.Context, stack *node.Node, cfg *ethconfig.Config) { cfg.SnapshotCache = 0 // Disabled } } - if ctx.IsSet(DocRootFlag.Name) { - cfg.DocRoot = ctx.String(DocRootFlag.Name) - } if ctx.IsSet(VMEnableDebugFlag.Name) { // TODO(fjl): force-enable this in --dev mode cfg.EnablePreimageRecording = ctx.Bool(VMEnableDebugFlag.Name) @@ -1810,20 +1742,22 @@ 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 passphrase string err error ) - if list := MakePasswordList(ctx); len(list) > 0 { - // Just take the first value. Although the function returns a possible multiple values and - // some usages iterate through them as attempts, that doesn't make sense in this setting, - // when we're definitely concerned with only one account. - passphrase = list[0] + if path := ctx.Path(PasswordFileFlag.Name); path != "" { + if text, err := os.ReadFile(path); err != nil { + Fatalf("Failed to read password file: %v", err) + } else { + if lines := strings.Split(string(text), "\n"); len(lines) > 0 { + passphrase = strings.TrimRight(lines[0], "\r") // Sanitise DOS line endings. + } + } } - // Unlock the developer account by local keystore. var ks *keystore.KeyStore if keystores := stack.AccountManager().Backends(keystore.KeyStoreType); len(keystores) > 0 { @@ -1866,9 +1800,6 @@ func SetEthConfig(ctx *cli.Context, stack *node.Node, cfg *ethconfig.Config) { if err != nil { Fatalf("Could not read genesis from database: %v", err) } - if !genesis.Config.TerminalTotalDifficultyPassed { - Fatalf("Bad developer-mode genesis configuration: terminalTotalDifficultyPassed must be true") - } if genesis.Config.TerminalTotalDifficulty == nil { Fatalf("Bad developer-mode genesis configuration: terminalTotalDifficulty must be specified") } else if genesis.Config.TerminalTotalDifficulty.Cmp(big.NewInt(0)) != 0 { @@ -1899,17 +1830,87 @@ func SetEthConfig(ctx *cli.Context, stack *node.Node, cfg *ethconfig.Config) { // VM tracing config. if ctx.IsSet(VMTraceFlag.Name) { if name := ctx.String(VMTraceFlag.Name); name != "" { - var config string - if ctx.IsSet(VMTraceJsonConfigFlag.Name) { - config = ctx.String(VMTraceJsonConfigFlag.Name) - } - cfg.VMTrace = name - cfg.VMTraceJsonConfig = config + cfg.VMTraceJsonConfig = ctx.String(VMTraceJsonConfigFlag.Name) } } } +// MakeBeaconLightConfig constructs a beacon light client config based on the +// related command line flags. +func MakeBeaconLightConfig(ctx *cli.Context) bparams.ClientConfig { + var config bparams.ClientConfig + customConfig := ctx.IsSet(BeaconConfigFlag.Name) + CheckExclusive(ctx, MainnetFlag, SepoliaFlag, HoleskyFlag, BeaconConfigFlag) + switch { + case ctx.Bool(MainnetFlag.Name): + config.ChainConfig = *bparams.MainnetLightConfig + case ctx.Bool(SepoliaFlag.Name): + config.ChainConfig = *bparams.SepoliaLightConfig + case ctx.Bool(HoleskyFlag.Name): + config.ChainConfig = *bparams.HoleskyLightConfig + default: + if !customConfig { + config.ChainConfig = *bparams.MainnetLightConfig + } + } + // Genesis root and time should always be specified together with custom chain config + if customConfig { + if !ctx.IsSet(BeaconGenesisRootFlag.Name) { + Fatalf("Custom beacon chain config is specified but genesis root is missing") + } + if !ctx.IsSet(BeaconGenesisTimeFlag.Name) { + Fatalf("Custom beacon chain config is specified but genesis time is missing") + } + if !ctx.IsSet(BeaconCheckpointFlag.Name) { + Fatalf("Custom beacon chain config is specified but checkpoint is missing") + } + config.ChainConfig = bparams.ChainConfig{ + GenesisTime: ctx.Uint64(BeaconGenesisTimeFlag.Name), + } + if c, err := hexutil.Decode(ctx.String(BeaconGenesisRootFlag.Name)); err == nil && len(c) <= 32 { + copy(config.GenesisValidatorsRoot[:len(c)], c) + } else { + Fatalf("Invalid hex string", "beacon.genesis.gvroot", ctx.String(BeaconGenesisRootFlag.Name), "error", err) + } + configFile := ctx.String(BeaconConfigFlag.Name) + if err := config.ChainConfig.LoadForks(configFile); err != nil { + Fatalf("Could not load beacon chain config", "file", configFile, "error", err) + } + log.Info("Using custom beacon chain config", "file", configFile) + } else { + if ctx.IsSet(BeaconGenesisRootFlag.Name) { + Fatalf("Genesis root is specified but custom beacon chain config is missing") + } + if ctx.IsSet(BeaconGenesisTimeFlag.Name) { + Fatalf("Genesis time is specified but custom beacon chain config is missing") + } + } + // Checkpoint is required with custom chain config and is optional with pre-defined config + if ctx.IsSet(BeaconCheckpointFlag.Name) { + if c, err := hexutil.Decode(ctx.String(BeaconCheckpointFlag.Name)); err == nil && len(c) <= 32 { + copy(config.Checkpoint[:len(c)], c) + } else { + Fatalf("Invalid hex string", "beacon.checkpoint", ctx.String(BeaconCheckpointFlag.Name), "error", err) + } + } + config.Apis = ctx.StringSlice(BeaconApiFlag.Name) + if config.Apis == nil { + Fatalf("Beacon node light client API URL not specified") + } + config.CustomHeader = make(map[string]string) + for _, s := range ctx.StringSlice(BeaconApiHeaderFlag.Name) { + kv := strings.Split(s, ":") + if len(kv) != 2 { + Fatalf("Invalid custom API header entry: %s", s) + } + config.CustomHeader[strings.TrimSpace(kv[0])] = strings.TrimSpace(kv[1]) + } + config.Threshold = ctx.Int(BeaconThresholdFlag.Name) + config.NoFilter = ctx.Bool(BeaconNoFilterFlag.Name) + return config +} + // SetDNSDiscoveryDefaults configures DNS discovery with the given URL if // no URLs are set. func SetDNSDiscoveryDefaults(cfg *ethconfig.Config, genesis common.Hash) { @@ -1967,67 +1968,56 @@ func RegisterFullSyncTester(stack *node.Node, eth *eth.Ethereum, target common.H log.Info("Registered full-sync tester", "hash", target) } -func SetupMetrics(ctx *cli.Context) { - 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) - } - - 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)) - } +// SetupMetrics configures the metrics system. +func SetupMetrics(cfg *metrics.Config) { + if !cfg.Enabled { + return } + log.Info("Enabling metrics collection") + metrics.Enable() + + // 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) + } + + // 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)) + } + + // 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{} @@ -2186,10 +2176,7 @@ func MakeChain(ctx *cli.Context, stack *node.Node, readonly bool) (*core.BlockCh } if ctx.IsSet(VMTraceFlag.Name) { if name := ctx.String(VMTraceFlag.Name); name != "" { - var config json.RawMessage - if ctx.IsSet(VMTraceJsonConfigFlag.Name) { - config = json.RawMessage(ctx.String(VMTraceJsonConfigFlag.Name)) - } + config := json.RawMessage(ctx.String(VMTraceJsonConfigFlag.Name)) t, err := tracers.LiveDirectory.New(name, config) if err != nil { Fatalf("Failed to create tracer %q: %v", name, err) diff --git a/cmd/utils/flags_legacy.go b/cmd/utils/flags_legacy.go index f145f605d6..ff63dd5685 100644 --- a/cmd/utils/flags_legacy.go +++ b/cmd/utils/flags_legacy.go @@ -153,6 +153,23 @@ var ( Usage: "Enable expensive metrics collection and reporting (deprecated)", Category: flags.DeprecatedCategory, } + // Deprecated Oct 2024 + EnablePersonal = &cli.BoolFlag{ + Name: "rpc.enabledeprecatedpersonal", + Usage: "This used to enable the 'personal' namespace.", + Category: flags.DeprecatedCategory, + } + UnlockedAccountFlag = &cli.StringFlag{ + Name: "unlock", + Usage: "Comma separated list of accounts to unlock (deprecated)", + Value: "", + Category: flags.DeprecatedCategory, + } + InsecureUnlockAllowedFlag = &cli.BoolFlag{ + Name: "allow-insecure-unlock", + Usage: "Allow insecure account unlocking when account-related RPCs are exposed by http (deprecated)", + Category: flags.DeprecatedCategory, + } ) // showDeprecated displays deprecated flags that will be soon removed from the codebase. diff --git a/cmd/utils/prompt.go b/cmd/utils/prompt.go index f513e38188..9419b77010 100644 --- a/cmd/utils/prompt.go +++ b/cmd/utils/prompt.go @@ -45,18 +45,3 @@ func GetPassPhrase(text string, confirmation bool) string { } return password } - -// GetPassPhraseWithList retrieves the password associated with an account, either fetched -// from a list of preloaded passphrases, or requested interactively from the user. -func GetPassPhraseWithList(text string, confirmation bool, index int, passwords []string) string { - // If a list of passwords was supplied, retrieve from them - if len(passwords) > 0 { - if index < len(passwords) { - return passwords[index] - } - return passwords[len(passwords)-1] - } - // Otherwise prompt the user for the password - password := GetPassPhrase(text, confirmation) - return password -} diff --git a/cmd/utils/prompt_test.go b/cmd/utils/prompt_test.go deleted file mode 100644 index 236353a7cc..0000000000 --- a/cmd/utils/prompt_test.go +++ /dev/null @@ -1,76 +0,0 @@ -// Copyright 2020 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 utils contains internal helper functions for go-ethereum commands. -package utils - -import ( - "testing" -) - -func TestGetPassPhraseWithList(t *testing.T) { - t.Parallel() - type args struct { - text string - confirmation bool - index int - passwords []string - } - tests := []struct { - name string - args args - want string - }{ - { - "test1", - args{ - "text1", - false, - 0, - []string{"zero", "one", "two"}, - }, - "zero", - }, - { - "test2", - args{ - "text2", - false, - 5, - []string{"zero", "one", "two"}, - }, - "two", - }, - { - "test3", - args{ - "text3", - true, - 1, - []string{"zero", "one", "two"}, - }, - "one", - }, - } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - t.Parallel() - if got := GetPassPhraseWithList(tt.args.text, tt.args.confirmation, tt.args.index, tt.args.passwords); got != tt.want { - t.Errorf("GetPassPhraseWithList() = %v, want %v", got, tt.want) - } - }) - } -} diff --git a/common/math/big.go b/common/math/big.go index d9748d01a3..825f4baec9 100644 --- a/common/math/big.go +++ b/common/math/big.go @@ -24,12 +24,9 @@ import ( // Various big integer limit values. var ( - tt255 = BigPow(2, 255) tt256 = BigPow(2, 256) tt256m1 = new(big.Int).Sub(tt256, big.NewInt(1)) - tt63 = BigPow(2, 63) MaxBig256 = new(big.Int).Set(tt256m1) - MaxBig63 = new(big.Int).Sub(tt63, big.NewInt(1)) ) const ( @@ -146,32 +143,6 @@ func BigPow(a, b int64) *big.Int { return r.Exp(r, big.NewInt(b), nil) } -// BigMax returns the larger of x or y. -func BigMax(x, y *big.Int) *big.Int { - if x.Cmp(y) < 0 { - return y - } - return x -} - -// BigMin returns the smaller of x or y. -func BigMin(x, y *big.Int) *big.Int { - if x.Cmp(y) > 0 { - return y - } - return x -} - -// FirstBitSet returns the index of the first 1 bit in v, counting from LSB. -func FirstBitSet(v *big.Int) int { - for i := 0; i < v.BitLen(); i++ { - if v.Bit(i) > 0 { - return i - } - } - return v.BitLen() -} - // PaddedBigBytes encodes a big integer as a big-endian byte slice. The length // of the slice is at least n bytes. func PaddedBigBytes(bigint *big.Int, n int) []byte { @@ -183,34 +154,6 @@ func PaddedBigBytes(bigint *big.Int, n int) []byte { return ret } -// bigEndianByteAt returns the byte at position n, -// in Big-Endian encoding -// So n==0 returns the least significant byte -func bigEndianByteAt(bigint *big.Int, n int) byte { - words := bigint.Bits() - // Check word-bucket the byte will reside in - i := n / wordBytes - if i >= len(words) { - return byte(0) - } - word := words[i] - // Offset of the byte - shift := 8 * uint(n%wordBytes) - - return byte(word >> shift) -} - -// Byte returns the byte at position n, -// with the supplied padlength in Little-Endian encoding. -// n==0 returns the MSB -// Example: bigint '5', padlength 32, n=31 => 5 -func Byte(bigint *big.Int, padlength, n int) byte { - if n >= padlength { - return byte(0) - } - return bigEndianByteAt(bigint, padlength-1-n) -} - // ReadBits encodes the absolute value of bigint as big-endian bytes. Callers must ensure // that buf has enough space. If buf is too short the result will be incomplete. func ReadBits(bigint *big.Int, buf []byte) { @@ -234,38 +177,3 @@ func U256(x *big.Int) *big.Int { func U256Bytes(n *big.Int) []byte { return PaddedBigBytes(U256(n), 32) } - -// S256 interprets x as a two's complement number. -// x must not exceed 256 bits (the result is undefined if it does) and is not modified. -// -// S256(0) = 0 -// S256(1) = 1 -// S256(2**255) = -2**255 -// S256(2**256-1) = -1 -func S256(x *big.Int) *big.Int { - if x.Cmp(tt255) < 0 { - return x - } - return new(big.Int).Sub(x, tt256) -} - -// Exp implements exponentiation by squaring. -// Exp returns a newly-allocated big integer and does not change -// base or exponent. The result is truncated to 256 bits. -// -// Courtesy @karalabe and @chfast -func Exp(base, exponent *big.Int) *big.Int { - copyBase := new(big.Int).Set(base) - result := big.NewInt(1) - - for _, word := range exponent.Bits() { - for i := 0; i < wordBits; i++ { - if word&1 == 1 { - U256(result.Mul(result, copyBase)) - } - U256(copyBase.Mul(copyBase, copyBase)) - word >>= 1 - } - } - return result -} diff --git a/common/math/big_test.go b/common/math/big_test.go index ee8f09e7b4..6f701b621a 100644 --- a/common/math/big_test.go +++ b/common/math/big_test.go @@ -21,8 +21,6 @@ import ( "encoding/hex" "math/big" "testing" - - "github.com/ethereum/go-ethereum/common" ) func TestHexOrDecimal256(t *testing.T) { @@ -70,53 +68,6 @@ func TestMustParseBig256(t *testing.T) { MustParseBig256("ggg") } -func TestBigMax(t *testing.T) { - a := big.NewInt(10) - b := big.NewInt(5) - - max1 := BigMax(a, b) - if max1 != a { - t.Errorf("Expected %d got %d", a, max1) - } - - max2 := BigMax(b, a) - if max2 != a { - t.Errorf("Expected %d got %d", a, max2) - } -} - -func TestBigMin(t *testing.T) { - a := big.NewInt(10) - b := big.NewInt(5) - - min1 := BigMin(a, b) - if min1 != b { - t.Errorf("Expected %d got %d", b, min1) - } - - min2 := BigMin(b, a) - if min2 != b { - t.Errorf("Expected %d got %d", b, min2) - } -} - -func TestFirstBigSet(t *testing.T) { - tests := []struct { - num *big.Int - ix int - }{ - {big.NewInt(0), 0}, - {big.NewInt(1), 0}, - {big.NewInt(2), 1}, - {big.NewInt(0x100), 8}, - } - for _, test := range tests { - if ix := FirstBitSet(test.num); ix != test.ix { - t.Errorf("FirstBitSet(b%b) = %d, want %d", test.num, ix, test.ix) - } - } -} - func TestPaddedBigBytes(t *testing.T) { tests := []struct { num *big.Int @@ -156,20 +107,6 @@ func BenchmarkPaddedBigBytesSmallOnePadding(b *testing.B) { } } -func BenchmarkByteAtBrandNew(b *testing.B) { - bigint := MustParseBig256("0x18F8F8F1000111000110011100222004330052300000000000000000FEFCF3CC") - for i := 0; i < b.N; i++ { - bigEndianByteAt(bigint, 15) - } -} - -func BenchmarkByteAt(b *testing.B) { - bigint := MustParseBig256("0x18F8F8F1000111000110011100222004330052300000000000000000FEFCF3CC") - for i := 0; i < b.N; i++ { - bigEndianByteAt(bigint, 15) - } -} - func BenchmarkByteAtOld(b *testing.B) { bigint := MustParseBig256("0x18F8F8F1000111000110011100222004330052300000000000000000FEFCF3CC") for i := 0; i < b.N; i++ { @@ -220,105 +157,3 @@ func TestU256Bytes(t *testing.T) { t.Errorf("expected %x got %x", ubytes, unsigned) } } - -func TestBigEndianByteAt(t *testing.T) { - tests := []struct { - x string - y int - exp byte - }{ - {"00", 0, 0x00}, - {"01", 1, 0x00}, - {"00", 1, 0x00}, - {"01", 0, 0x01}, - {"0000000000000000000000000000000000000000000000000000000000102030", 0, 0x30}, - {"0000000000000000000000000000000000000000000000000000000000102030", 1, 0x20}, - {"ABCDEF0908070605040302010000000000000000000000000000000000000000", 31, 0xAB}, - {"ABCDEF0908070605040302010000000000000000000000000000000000000000", 32, 0x00}, - {"ABCDEF0908070605040302010000000000000000000000000000000000000000", 500, 0x00}, - } - for _, test := range tests { - v := new(big.Int).SetBytes(common.Hex2Bytes(test.x)) - actual := bigEndianByteAt(v, test.y) - if actual != test.exp { - t.Fatalf("Expected [%v] %v:th byte to be %v, was %v.", test.x, test.y, test.exp, actual) - } - } -} -func TestLittleEndianByteAt(t *testing.T) { - tests := []struct { - x string - y int - exp byte - }{ - {"00", 0, 0x00}, - {"01", 1, 0x00}, - {"00", 1, 0x00}, - {"01", 0, 0x00}, - {"0000000000000000000000000000000000000000000000000000000000102030", 0, 0x00}, - {"0000000000000000000000000000000000000000000000000000000000102030", 1, 0x00}, - {"ABCDEF0908070605040302010000000000000000000000000000000000000000", 31, 0x00}, - {"ABCDEF0908070605040302010000000000000000000000000000000000000000", 32, 0x00}, - {"ABCDEF0908070605040302010000000000000000000000000000000000000000", 0, 0xAB}, - {"ABCDEF0908070605040302010000000000000000000000000000000000000000", 1, 0xCD}, - {"00CDEF090807060504030201ffffffffffffffffffffffffffffffffffffffff", 0, 0x00}, - {"00CDEF090807060504030201ffffffffffffffffffffffffffffffffffffffff", 1, 0xCD}, - {"0000000000000000000000000000000000000000000000000000000000102030", 31, 0x30}, - {"0000000000000000000000000000000000000000000000000000000000102030", 30, 0x20}, - {"ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", 32, 0x0}, - {"ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", 31, 0xFF}, - {"ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", 0xFFFF, 0x0}, - } - for _, test := range tests { - v := new(big.Int).SetBytes(common.Hex2Bytes(test.x)) - actual := Byte(v, 32, test.y) - if actual != test.exp { - t.Fatalf("Expected [%v] %v:th byte to be %v, was %v.", test.x, test.y, test.exp, actual) - } - } -} - -func TestS256(t *testing.T) { - tests := []struct{ x, y *big.Int }{ - {x: big.NewInt(0), y: big.NewInt(0)}, - {x: big.NewInt(1), y: big.NewInt(1)}, - {x: big.NewInt(2), y: big.NewInt(2)}, - { - x: new(big.Int).Sub(BigPow(2, 255), big.NewInt(1)), - y: new(big.Int).Sub(BigPow(2, 255), big.NewInt(1)), - }, - { - x: BigPow(2, 255), - y: new(big.Int).Neg(BigPow(2, 255)), - }, - { - x: new(big.Int).Sub(BigPow(2, 256), big.NewInt(1)), - y: big.NewInt(-1), - }, - { - x: new(big.Int).Sub(BigPow(2, 256), big.NewInt(2)), - y: big.NewInt(-2), - }, - } - for _, test := range tests { - if y := S256(test.x); y.Cmp(test.y) != 0 { - t.Errorf("S256(%x) = %x, want %x", test.x, y, test.y) - } - } -} - -func TestExp(t *testing.T) { - tests := []struct{ base, exponent, result *big.Int }{ - {base: big.NewInt(0), exponent: big.NewInt(0), result: big.NewInt(1)}, - {base: big.NewInt(1), exponent: big.NewInt(0), result: big.NewInt(1)}, - {base: big.NewInt(1), exponent: big.NewInt(1), result: big.NewInt(1)}, - {base: big.NewInt(1), exponent: big.NewInt(2), result: big.NewInt(1)}, - {base: big.NewInt(3), exponent: big.NewInt(144), result: MustParseBig256("507528786056415600719754159741696356908742250191663887263627442114881")}, - {base: big.NewInt(2), exponent: big.NewInt(255), result: MustParseBig256("57896044618658097711785492504343953926634992332820282019728792003956564819968")}, - } - for _, test := range tests { - if result := Exp(test.base, test.exponent); result.Cmp(test.result) != 0 { - t.Errorf("Exp(%d, %d) = %d, want %d", test.base, test.exponent, result, test.result) - } - } -} diff --git a/common/math/integer.go b/common/math/integer.go index 82de96f927..25ced87053 100644 --- a/common/math/integer.go +++ b/common/math/integer.go @@ -22,22 +22,6 @@ import ( "strconv" ) -// Integer limit values. -const ( - MaxInt8 = 1<<7 - 1 - MinInt8 = -1 << 7 - MaxInt16 = 1<<15 - 1 - MinInt16 = -1 << 15 - MaxInt32 = 1<<31 - 1 - MinInt32 = -1 << 31 - MaxInt64 = 1<<63 - 1 - MinInt64 = -1 << 63 - MaxUint8 = 1<<8 - 1 - MaxUint16 = 1<<16 - 1 - MaxUint32 = 1<<32 - 1 - MaxUint64 = 1<<64 - 1 -) - // HexOrDecimal64 marshals uint64 as hex or decimal. type HexOrDecimal64 uint64 diff --git a/common/math/integer_test.go b/common/math/integer_test.go index b31c7c26c2..4643a43f20 100644 --- a/common/math/integer_test.go +++ b/common/math/integer_test.go @@ -17,6 +17,7 @@ package math import ( + "math" "testing" ) @@ -36,8 +37,8 @@ func TestOverflow(t *testing.T) { op operation }{ // add operations - {MaxUint64, 1, true, add}, - {MaxUint64 - 1, 1, false, add}, + {math.MaxUint64, 1, true, add}, + {math.MaxUint64 - 1, 1, false, add}, // sub operations {0, 1, true, sub}, @@ -46,8 +47,8 @@ func TestOverflow(t *testing.T) { // mul operations {0, 0, false, mul}, {10, 10, false, mul}, - {MaxUint64, 2, true, mul}, - {MaxUint64, 1, false, mul}, + {math.MaxUint64, 2, true, mul}, + {math.MaxUint64, 1, false, mul}, } { var overflows bool switch test.op { diff --git a/common/path.go b/common/path.go index c1e382fd29..49c6a5efc2 100644 --- a/common/path.go +++ b/common/path.go @@ -27,7 +27,6 @@ func FileExist(filePath string) bool { if err != nil && os.IsNotExist(err) { return false } - return true } diff --git a/consensus/beacon/consensus.go b/consensus/beacon/consensus.go index ae4c9f29a1..cdacf354a5 100644 --- a/consensus/beacon/consensus.go +++ b/consensus/beacon/consensus.go @@ -28,6 +28,7 @@ import ( "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/params" "github.com/ethereum/go-ethereum/rpc" "github.com/ethereum/go-ethereum/trie" @@ -115,9 +116,6 @@ func errOut(n int, err error) chan error { func (beacon *Beacon) splitHeaders(chain consensus.ChainHeaderReader, headers []*types.Header) ([]*types.Header, []*types.Header, error) { // TTD is not defined yet, all headers should be in legacy format. ttd := chain.Config().TerminalTotalDifficulty - if ttd == nil { - return headers, nil, nil - } ptd := chain.GetTd(headers[0].ParentHash, headers[0].Number.Uint64()-1) if ptd == nil { return nil, nil, consensus.ErrUnknownAncestor @@ -349,7 +347,7 @@ func (beacon *Beacon) Prepare(chain consensus.ChainHeaderReader, header *types.H } // Finalize implements consensus.Engine and processes withdrawals on top. -func (beacon *Beacon) Finalize(chain consensus.ChainHeaderReader, header *types.Header, state *state.StateDB, body *types.Body) { +func (beacon *Beacon) Finalize(chain consensus.ChainHeaderReader, header *types.Header, state vm.StateDB, body *types.Body) { if !beacon.IsPoSHeader(header) { beacon.ethone.Finalize(chain, header, state, body) return @@ -400,21 +398,25 @@ func (beacon *Beacon) FinalizeAndAssemble(chain consensus.ChainHeaderReader, hea if parent == nil { return nil, fmt.Errorf("nil parent header for block %d", header.Number) } - preTrie, err := state.Database().OpenTrie(parent.Root) if err != nil { return nil, fmt.Errorf("error opening pre-state tree root: %w", err) } - vktPreTrie, okpre := preTrie.(*trie.VerkleTrie) vktPostTrie, okpost := state.GetTrie().(*trie.VerkleTrie) + + // The witness is only attached iff both parent and current block are + // using verkle tree. if okpre && okpost { if len(keys) > 0 { - verkleProof, stateDiff, err := vktPreTrie.Proof(vktPostTrie, keys, vktPreTrie.FlatdbNodeResolver) + verkleProof, stateDiff, err := vktPreTrie.Proof(vktPostTrie, keys) if err != nil { return nil, fmt.Errorf("error generating verkle proof for block %d: %w", header.Number, err) } - block = block.WithWitness(&types.ExecutionWitness{StateDiff: stateDiff, VerkleProof: verkleProof}) + block = block.WithWitness(&types.ExecutionWitness{ + StateDiff: stateDiff, + VerkleProof: verkleProof, + }) } } } @@ -494,9 +496,6 @@ func (beacon *Beacon) SetThreads(threads int) { // It depends on the parentHash already being stored in the database. // If the parentHash is not stored in the database a UnknownAncestor error is returned. func IsTTDReached(chain consensus.ChainHeaderReader, parentHash common.Hash, parentNumber uint64) (bool, error) { - if chain.Config().TerminalTotalDifficulty == nil { - return false, nil - } td := chain.GetTd(parentHash, parentNumber) if td == nil { return false, consensus.ErrUnknownAncestor diff --git a/consensus/clique/clique.go b/consensus/clique/clique.go index c9e9484002..d31efd7445 100644 --- a/consensus/clique/clique.go +++ b/consensus/clique/clique.go @@ -27,7 +27,6 @@ import ( "sync" "time" - "github.com/ethereum/go-ethereum/accounts" "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common/hexutil" "github.com/ethereum/go-ethereum/common/lru" @@ -36,6 +35,7 @@ import ( "github.com/ethereum/go-ethereum/consensus/misc/eip1559" "github.com/ethereum/go-ethereum/core/state" "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/ethdb" "github.com/ethereum/go-ethereum/log" @@ -50,8 +50,6 @@ const ( checkpointInterval = 1024 // Number of blocks after which to save the vote snapshot to the database inmemorySnapshots = 128 // Number of recent vote snapshots to keep in memory inmemorySignatures = 4096 // Number of recent block signatures to keep in memory - - wiggleTime = 500 * time.Millisecond // Random delay (per signer) to allow concurrent signers ) // Clique proof-of-authority protocol constants. @@ -140,9 +138,6 @@ var ( errRecentlySigned = errors.New("recently signed") ) -// SignerFn hashes and signs the data to be signed by a backing account. -type SignerFn func(signer accounts.Account, mimeType string, message []byte) ([]byte, error) - // ecrecover extracts the Ethereum account address from a signed header. func ecrecover(header *types.Header, sigcache *sigLRU) (common.Address, error) { // If the signature's already cached, return that @@ -180,7 +175,6 @@ type Clique struct { proposals map[common.Address]bool // Current list of proposals we are pushing signer common.Address // Ethereum address of the signing key - signFn SignerFn // Signer function to authorize hashes with lock sync.RWMutex // Protects the signer and proposals fields // The fields below are for testing only @@ -580,7 +574,7 @@ func (c *Clique) Prepare(chain consensus.ChainHeaderReader, header *types.Header // Finalize implements consensus.Engine. There is no post-transaction // consensus rules in clique, do nothing here. -func (c *Clique) Finalize(chain consensus.ChainHeaderReader, header *types.Header, state *state.StateDB, body *types.Body) { +func (c *Clique) Finalize(chain consensus.ChainHeaderReader, header *types.Header, state vm.StateDB, body *types.Body) { // No block rewards in PoA, so the state remains as is } @@ -602,82 +596,17 @@ func (c *Clique) FinalizeAndAssemble(chain consensus.ChainHeaderReader, header * // Authorize injects a private key into the consensus engine to mint new blocks // with. -func (c *Clique) Authorize(signer common.Address, signFn SignerFn) { +func (c *Clique) Authorize(signer common.Address) { c.lock.Lock() defer c.lock.Unlock() c.signer = signer - c.signFn = signFn } // Seal implements consensus.Engine, attempting to create a sealed block using // the local signing credentials. func (c *Clique) Seal(chain consensus.ChainHeaderReader, block *types.Block, results chan<- *types.Block, stop <-chan struct{}) error { - header := block.Header() - - // Sealing the genesis block is not supported - number := header.Number.Uint64() - if number == 0 { - return errUnknownBlock - } - // For 0-period chains, refuse to seal empty blocks (no reward but would spin sealing) - if c.config.Period == 0 && len(block.Transactions()) == 0 { - return errors.New("sealing paused while waiting for transactions") - } - // Don't hold the signer fields for the entire sealing procedure - c.lock.RLock() - signer, signFn := c.signer, c.signFn - c.lock.RUnlock() - - // Bail out if we're unauthorized to sign a block - snap, err := c.snapshot(chain, number-1, header.ParentHash, nil) - if err != nil { - return err - } - if _, authorized := snap.Signers[signer]; !authorized { - return errUnauthorizedSigner - } - // If we're amongst the recent signers, wait for the next block - for seen, recent := range snap.Recents { - if recent == signer { - // Signer is among recents, only wait if the current block doesn't shift it out - if limit := uint64(len(snap.Signers)/2 + 1); number < limit || seen > number-limit { - return errors.New("signed recently, must wait for others") - } - } - } - // Sweet, the protocol permits us to sign the block, wait for our time - delay := time.Unix(int64(header.Time), 0).Sub(time.Now()) // nolint: gosimple - if header.Difficulty.Cmp(diffNoTurn) == 0 { - // It's not our turn explicitly to sign, delay it a bit - wiggle := time.Duration(len(snap.Signers)/2+1) * wiggleTime - delay += time.Duration(rand.Int63n(int64(wiggle))) - - log.Trace("Out-of-turn signing requested", "wiggle", common.PrettyDuration(wiggle)) - } - // Sign all the things! - sighash, err := signFn(accounts.Account{Address: signer}, accounts.MimetypeClique, CliqueRLP(header)) - if err != nil { - return err - } - copy(header.Extra[len(header.Extra)-extraSeal:], sighash) - // Wait until sealing is terminated or delay timeout. - log.Trace("Waiting for slot to sign and propagate", "delay", common.PrettyDuration(delay)) - go func() { - select { - case <-stop: - return - case <-time.After(delay): - } - - select { - case results <- block.WithSeal(header): - default: - log.Warn("Sealing result is not read by miner", "sealhash", SealHash(header)) - } - }() - - return nil + panic("clique (poa) sealing not supported any more") } // CalcDifficulty is the difficulty adjustment algorithm. It returns the difficulty diff --git a/consensus/consensus.go b/consensus/consensus.go index 9232f7a2c8..ff76d31f55 100644 --- a/consensus/consensus.go +++ b/consensus/consensus.go @@ -23,6 +23,7 @@ import ( "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/core/state" "github.com/ethereum/go-ethereum/core/types" + "github.com/ethereum/go-ethereum/core/vm" "github.com/ethereum/go-ethereum/params" "github.com/ethereum/go-ethereum/rpc" ) @@ -88,7 +89,7 @@ type Engine interface { // // Note: The state database might be updated to reflect any consensus rules // that happen at finalization (e.g. block rewards). - Finalize(chain ChainHeaderReader, header *types.Header, state *state.StateDB, body *types.Body) + Finalize(chain ChainHeaderReader, header *types.Header, state vm.StateDB, body *types.Body) // FinalizeAndAssemble runs any post-transaction state modifications (e.g. block // rewards or process withdrawals) and assembles the final block. diff --git a/consensus/ethash/consensus.go b/consensus/ethash/consensus.go index 0bd1a56bce..4f92f1282b 100644 --- a/consensus/ethash/consensus.go +++ b/consensus/ethash/consensus.go @@ -24,13 +24,13 @@ import ( mapset "github.com/deckarep/golang-set/v2" "github.com/ethereum/go-ethereum/common" - "github.com/ethereum/go-ethereum/common/math" "github.com/ethereum/go-ethereum/consensus" "github.com/ethereum/go-ethereum/consensus/misc" "github.com/ethereum/go-ethereum/consensus/misc/eip1559" "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/params" "github.com/ethereum/go-ethereum/rlp" "github.com/ethereum/go-ethereum/trie" @@ -480,7 +480,9 @@ func calcDifficultyFrontier(time uint64, parent *types.Header) *big.Int { expDiff := periodCount.Sub(periodCount, big2) expDiff.Exp(big2, expDiff, nil) diff.Add(diff, expDiff) - diff = math.BigMax(diff, params.MinimumDifficulty) + if diff.Cmp(params.MinimumDifficulty) < 0 { + diff = params.MinimumDifficulty + } } return diff } @@ -502,7 +504,7 @@ func (ethash *Ethash) Prepare(chain consensus.ChainHeaderReader, header *types.H } // Finalize implements consensus.Engine, accumulating the block and uncle rewards. -func (ethash *Ethash) Finalize(chain consensus.ChainHeaderReader, header *types.Header, state *state.StateDB, body *types.Body) { +func (ethash *Ethash) Finalize(chain consensus.ChainHeaderReader, header *types.Header, state vm.StateDB, body *types.Body) { // Accumulate any block and uncle rewards accumulateRewards(chain.Config(), state, header, body.Uncles) } @@ -565,7 +567,7 @@ func (ethash *Ethash) SealHash(header *types.Header) (hash common.Hash) { // accumulateRewards credits the coinbase of the given block with the mining // reward. The total reward consists of the static block reward and rewards for // included uncles. The coinbase of each uncle block is also rewarded. -func accumulateRewards(config *params.ChainConfig, stateDB *state.StateDB, header *types.Header, uncles []*types.Header) { +func accumulateRewards(config *params.ChainConfig, stateDB vm.StateDB, header *types.Header, uncles []*types.Header) { // Select the correct block reward based on chain progression blockReward := FrontierBlockReward if config.IsByzantium(header.Number) { diff --git a/consensus/misc/dao.go b/consensus/misc/dao.go index 45669d0bce..b80c1b833a 100644 --- a/consensus/misc/dao.go +++ b/consensus/misc/dao.go @@ -21,11 +21,10 @@ import ( "errors" "math/big" - "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/params" - "github.com/holiman/uint256" ) var ( @@ -74,7 +73,7 @@ func VerifyDAOHeaderExtraData(config *params.ChainConfig, header *types.Header) // ApplyDAOHardFork modifies the state database according to the DAO hard-fork // rules, transferring all balances of a set of DAO accounts to a single refund // contract. -func ApplyDAOHardFork(statedb *state.StateDB) { +func ApplyDAOHardFork(statedb vm.StateDB) { // Retrieve the contract to refund balances into if !statedb.Exist(params.DAORefundContract) { statedb.CreateAccount(params.DAORefundContract) @@ -82,7 +81,8 @@ func ApplyDAOHardFork(statedb *state.StateDB) { // Move every DAO account and extra-balance account funds into the refund contract for _, addr := range params.DAODrainList() { - statedb.AddBalance(params.DAORefundContract, statedb.GetBalance(addr), tracing.BalanceIncreaseDaoContract) - statedb.SetBalance(addr, new(uint256.Int), tracing.BalanceDecreaseDaoAccount) + balance := statedb.GetBalance(addr) + statedb.AddBalance(params.DAORefundContract, balance, tracing.BalanceIncreaseDaoContract) + statedb.SubBalance(addr, balance, tracing.BalanceDecreaseDaoAccount) } } diff --git a/consensus/misc/eip1559/eip1559.go b/consensus/misc/eip1559/eip1559.go index 84b82c4c49..a90bd744b2 100644 --- a/consensus/misc/eip1559/eip1559.go +++ b/consensus/misc/eip1559/eip1559.go @@ -22,7 +22,6 @@ import ( "math/big" "github.com/ethereum/go-ethereum/common" - "github.com/ethereum/go-ethereum/common/math" "github.com/ethereum/go-ethereum/consensus/misc" "github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/params" @@ -78,9 +77,10 @@ func CalcBaseFee(config *params.ChainConfig, parent *types.Header) *big.Int { num.Mul(num, parent.BaseFee) num.Div(num, denom.SetUint64(parentGasTarget)) num.Div(num, denom.SetUint64(config.BaseFeeChangeDenominator())) - baseFeeDelta := math.BigMax(num, common.Big1) - - return num.Add(parent.BaseFee, baseFeeDelta) + if num.Cmp(common.Big1) < 0 { + return num.Add(parent.BaseFee, common.Big1) + } + return num.Add(parent.BaseFee, num) } else { // Otherwise if the parent block used less gas than its target, the baseFee should decrease. // max(0, parentBaseFee * gasUsedDelta / parentGasTarget / baseFeeChangeDenominator) @@ -88,8 +88,11 @@ func CalcBaseFee(config *params.ChainConfig, parent *types.Header) *big.Int { num.Mul(num, parent.BaseFee) num.Div(num, denom.SetUint64(parentGasTarget)) num.Div(num, denom.SetUint64(config.BaseFeeChangeDenominator())) - baseFee := num.Sub(parent.BaseFee, num) - return math.BigMax(baseFee, common.Big0) + baseFee := num.Sub(parent.BaseFee, num) + if baseFee.Cmp(common.Big0) < 0 { + baseFee = common.Big0 + } + return baseFee } } diff --git a/console/bridge.go b/console/bridge.go index 37578041ca..c1d7746c02 100644 --- a/console/bridge.go +++ b/console/bridge.go @@ -19,15 +19,12 @@ package console import ( "encoding/json" "errors" - "fmt" "io" "reflect" "strings" "time" "github.com/dop251/goja" - "github.com/ethereum/go-ethereum/accounts/scwallet" - "github.com/ethereum/go-ethereum/accounts/usbwallet" "github.com/ethereum/go-ethereum/common/hexutil" "github.com/ethereum/go-ethereum/console/prompt" "github.com/ethereum/go-ethereum/internal/jsre" @@ -51,268 +48,6 @@ func newBridge(client *rpc.Client, prompter prompt.UserPrompter, printer io.Writ } } -func getJeth(vm *goja.Runtime) *goja.Object { - jeth := vm.Get("jeth") - if jeth == nil { - panic(vm.ToValue("jeth object does not exist")) - } - return jeth.ToObject(vm) -} - -// NewAccount is a wrapper around the personal.newAccount RPC method that uses a -// non-echoing password prompt to acquire the passphrase and executes the original -// RPC method (saved in jeth.newAccount) with it to actually execute the RPC call. -func (b *bridge) NewAccount(call jsre.Call) (goja.Value, error) { - var ( - password string - confirm string - err error - ) - switch { - // No password was specified, prompt the user for it - case len(call.Arguments) == 0: - if password, err = b.prompter.PromptPassword("Passphrase: "); err != nil { - return nil, err - } - if confirm, err = b.prompter.PromptPassword("Repeat passphrase: "); err != nil { - return nil, err - } - if password != confirm { - return nil, errors.New("passwords don't match") - } - // A single string password was specified, use that - case len(call.Arguments) == 1 && call.Argument(0).ToString() != nil: - password = call.Argument(0).ToString().String() - default: - return nil, errors.New("expected 0 or 1 string argument") - } - // Password acquired, execute the call and return - newAccount, callable := goja.AssertFunction(getJeth(call.VM).Get("newAccount")) - if !callable { - return nil, errors.New("jeth.newAccount is not callable") - } - ret, err := newAccount(goja.Null(), call.VM.ToValue(password)) - if err != nil { - return nil, err - } - return ret, nil -} - -// OpenWallet is a wrapper around personal.openWallet which can interpret and -// react to certain error messages, such as the Trezor PIN matrix request. -func (b *bridge) OpenWallet(call jsre.Call) (goja.Value, error) { - // Make sure we have a wallet specified to open - if call.Argument(0).ToObject(call.VM).ClassName() != "String" { - return nil, errors.New("first argument must be the wallet URL to open") - } - wallet := call.Argument(0) - - var passwd goja.Value - if goja.IsUndefined(call.Argument(1)) || goja.IsNull(call.Argument(1)) { - passwd = call.VM.ToValue("") - } else { - passwd = call.Argument(1) - } - // Open the wallet and return if successful in itself - openWallet, callable := goja.AssertFunction(getJeth(call.VM).Get("openWallet")) - if !callable { - return nil, errors.New("jeth.openWallet is not callable") - } - val, err := openWallet(goja.Null(), wallet, passwd) - if err == nil { - return val, nil - } - - // Wallet open failed, report error unless it's a PIN or PUK entry - switch { - case strings.HasSuffix(err.Error(), usbwallet.ErrTrezorPINNeeded.Error()): - val, err = b.readPinAndReopenWallet(call) - if err == nil { - return val, nil - } - val, err = b.readPassphraseAndReopenWallet(call) - if err != nil { - return nil, err - } - - case strings.HasSuffix(err.Error(), scwallet.ErrPairingPasswordNeeded.Error()): - // PUK input requested, fetch from the user and call open again - input, err := b.prompter.PromptPassword("Please enter the pairing password: ") - if err != nil { - return nil, err - } - passwd = call.VM.ToValue(input) - if val, err = openWallet(goja.Null(), wallet, passwd); err != nil { - if !strings.HasSuffix(err.Error(), scwallet.ErrPINNeeded.Error()) { - return nil, err - } - // PIN input requested, fetch from the user and call open again - input, err := b.prompter.PromptPassword("Please enter current PIN: ") - if err != nil { - return nil, err - } - if val, err = openWallet(goja.Null(), wallet, call.VM.ToValue(input)); err != nil { - return nil, err - } - } - - case strings.HasSuffix(err.Error(), scwallet.ErrPINUnblockNeeded.Error()): - // PIN unblock requested, fetch PUK and new PIN from the user - var pukpin string - input, err := b.prompter.PromptPassword("Please enter current PUK: ") - if err != nil { - return nil, err - } - pukpin = input - input, err = b.prompter.PromptPassword("Please enter new PIN: ") - if err != nil { - return nil, err - } - pukpin += input - - if val, err = openWallet(goja.Null(), wallet, call.VM.ToValue(pukpin)); err != nil { - return nil, err - } - - case strings.HasSuffix(err.Error(), scwallet.ErrPINNeeded.Error()): - // PIN input requested, fetch from the user and call open again - input, err := b.prompter.PromptPassword("Please enter current PIN: ") - if err != nil { - return nil, err - } - if val, err = openWallet(goja.Null(), wallet, call.VM.ToValue(input)); err != nil { - return nil, err - } - - default: - // Unknown error occurred, drop to the user - return nil, err - } - return val, nil -} - -func (b *bridge) readPassphraseAndReopenWallet(call jsre.Call) (goja.Value, error) { - wallet := call.Argument(0) - input, err := b.prompter.PromptPassword("Please enter your passphrase: ") - if err != nil { - return nil, err - } - openWallet, callable := goja.AssertFunction(getJeth(call.VM).Get("openWallet")) - if !callable { - return nil, errors.New("jeth.openWallet is not callable") - } - return openWallet(goja.Null(), wallet, call.VM.ToValue(input)) -} - -func (b *bridge) readPinAndReopenWallet(call jsre.Call) (goja.Value, error) { - wallet := call.Argument(0) - // Trezor PIN matrix input requested, display the matrix to the user and fetch the data - fmt.Fprintf(b.printer, "Look at the device for number positions\n\n") - fmt.Fprintf(b.printer, "7 | 8 | 9\n") - fmt.Fprintf(b.printer, "--+---+--\n") - fmt.Fprintf(b.printer, "4 | 5 | 6\n") - fmt.Fprintf(b.printer, "--+---+--\n") - fmt.Fprintf(b.printer, "1 | 2 | 3\n\n") - - input, err := b.prompter.PromptPassword("Please enter current PIN: ") - if err != nil { - return nil, err - } - openWallet, callable := goja.AssertFunction(getJeth(call.VM).Get("openWallet")) - if !callable { - return nil, errors.New("jeth.openWallet is not callable") - } - return openWallet(goja.Null(), wallet, call.VM.ToValue(input)) -} - -// UnlockAccount is a wrapper around the personal.unlockAccount RPC method that -// uses a non-echoing password prompt to acquire the passphrase and executes the -// original RPC method (saved in jeth.unlockAccount) with it to actually execute -// the RPC call. -func (b *bridge) UnlockAccount(call jsre.Call) (goja.Value, error) { - if len(call.Arguments) < 1 { - return nil, errors.New("usage: unlockAccount(account, [ password, duration ])") - } - - account := call.Argument(0) - // Make sure we have an account specified to unlock. - if goja.IsUndefined(account) || goja.IsNull(account) || account.ExportType().Kind() != reflect.String { - return nil, errors.New("first argument must be the account to unlock") - } - - // If password is not given or is the null value, prompt the user for it. - var passwd goja.Value - if goja.IsUndefined(call.Argument(1)) || goja.IsNull(call.Argument(1)) { - fmt.Fprintf(b.printer, "Unlock account %s\n", account) - input, err := b.prompter.PromptPassword("Passphrase: ") - if err != nil { - return nil, err - } - passwd = call.VM.ToValue(input) - } else { - if call.Argument(1).ExportType().Kind() != reflect.String { - return nil, errors.New("password must be a string") - } - passwd = call.Argument(1) - } - - // Third argument is the duration how long the account should be unlocked. - duration := goja.Null() - if !goja.IsUndefined(call.Argument(2)) && !goja.IsNull(call.Argument(2)) { - if !isNumber(call.Argument(2)) { - return nil, errors.New("unlock duration must be a number") - } - duration = call.Argument(2) - } - - // Send the request to the backend and return. - unlockAccount, callable := goja.AssertFunction(getJeth(call.VM).Get("unlockAccount")) - if !callable { - return nil, errors.New("jeth.unlockAccount is not callable") - } - return unlockAccount(goja.Null(), account, passwd, duration) -} - -// Sign is a wrapper around the personal.sign RPC method that uses a non-echoing password -// prompt to acquire the passphrase and executes the original RPC method (saved in -// jeth.sign) with it to actually execute the RPC call. -func (b *bridge) Sign(call jsre.Call) (goja.Value, error) { - if nArgs := len(call.Arguments); nArgs < 2 { - return nil, errors.New("usage: sign(message, account, [ password ])") - } - var ( - message = call.Argument(0) - account = call.Argument(1) - passwd = call.Argument(2) - ) - - if goja.IsUndefined(message) || message.ExportType().Kind() != reflect.String { - return nil, errors.New("first argument must be the message to sign") - } - if goja.IsUndefined(account) || account.ExportType().Kind() != reflect.String { - return nil, errors.New("second argument must be the account to sign with") - } - - // if the password is not given or null ask the user and ensure password is a string - if goja.IsUndefined(passwd) || goja.IsNull(passwd) { - fmt.Fprintf(b.printer, "Give password for account %s\n", account) - input, err := b.prompter.PromptPassword("Password: ") - if err != nil { - return nil, err - } - passwd = call.VM.ToValue(input) - } else if passwd.ExportType().Kind() != reflect.String { - return nil, errors.New("third argument must be the password to unlock the account") - } - - // Send the request to the backend and return - sign, callable := goja.AssertFunction(getJeth(call.VM).Get("sign")) - if !callable { - return nil, errors.New("jeth.sign is not callable") - } - return sign(goja.Null(), message, account, passwd) -} - // Sleep will block the console for the specified number of seconds. func (b *bridge) Sleep(call jsre.Call) (goja.Value, error) { if nArgs := len(call.Arguments); nArgs < 1 { diff --git a/console/console.go b/console/console.go index 5acb4cdccb..b5c77bd78f 100644 --- a/console/console.go +++ b/console/console.go @@ -142,7 +142,6 @@ func (c *Console) init(preload []string) error { // Add bridge overrides for web3.js functionality. c.jsre.Do(func(vm *goja.Runtime) { c.initAdmin(vm, bridge) - c.initPersonal(vm, bridge) }) // Preload JavaScript files. @@ -249,30 +248,6 @@ func (c *Console) initAdmin(vm *goja.Runtime, bridge *bridge) { } } -// initPersonal redirects account-related API methods through the bridge. -// -// If the console is in interactive mode and the 'personal' API is available, override -// the openWallet, unlockAccount, newAccount and sign methods since these require user -// interaction. The original web3 callbacks are stored in 'jeth'. These will be called -// by the bridge after the prompt and send the original web3 request to the backend. -func (c *Console) initPersonal(vm *goja.Runtime, bridge *bridge) { - personal := getObject(vm, "personal") - if personal == nil || c.prompter == nil { - return - } - log.Warn("Enabling deprecated personal namespace") - jeth := vm.NewObject() - vm.Set("jeth", jeth) - jeth.Set("openWallet", personal.Get("openWallet")) - jeth.Set("unlockAccount", personal.Get("unlockAccount")) - jeth.Set("newAccount", personal.Get("newAccount")) - jeth.Set("sign", personal.Get("sign")) - personal.Set("openWallet", jsre.MakeCallback(vm, bridge.OpenWallet)) - personal.Set("unlockAccount", jsre.MakeCallback(vm, bridge.UnlockAccount)) - personal.Set("newAccount", jsre.MakeCallback(vm, bridge.NewAccount)) - personal.Set("sign", jsre.MakeCallback(vm, bridge.Sign)) -} - func (c *Console) clearHistory() { c.history = nil c.prompter.ClearHistory() diff --git a/core/bench_test.go b/core/bench_test.go index 639d36e9ae..d376830318 100644 --- a/core/bench_test.go +++ b/core/bench_test.go @@ -29,6 +29,7 @@ import ( "github.com/ethereum/go-ethereum/core/vm" "github.com/ethereum/go-ethereum/crypto" "github.com/ethereum/go-ethereum/ethdb" + "github.com/ethereum/go-ethereum/ethdb/pebble" "github.com/ethereum/go-ethereum/params" ) @@ -80,10 +81,16 @@ var ( // value-transfer transaction with n bytes of extra data in each // block. func genValueTx(nbytes int) func(int, *BlockGen) { + // We can reuse the data for all transactions. + // During signing, the method tx.WithSignature(s, sig) + // performs: + // cpy := tx.inner.copy() + // cpy.setSignatureValues(signer.ChainID(), v, r, s) + // After this operation, the data can be reused by the caller. + data := make([]byte, nbytes) return func(i int, gen *BlockGen) { toaddr := common.Address{} - data := make([]byte, nbytes) - gas, _ := IntrinsicGas(data, nil, false, false, false, false) + gas, _ := IntrinsicGas(data, nil, nil, false, false, false, false) signer := gen.Signer() gasPrice := big.NewInt(0) if gen.header.BaseFee != nil { @@ -173,18 +180,16 @@ func genUncles(i int, gen *BlockGen) { func benchInsertChain(b *testing.B, disk bool, gen func(int, *BlockGen)) { // Create the database in memory or in a temporary directory. var db ethdb.Database - var err error if !disk { db = rawdb.NewMemoryDatabase() } else { - dir := b.TempDir() - db, err = rawdb.NewLevelDBDatabase(dir, 128, 128, "", false) + pdb, err := pebble.New(b.TempDir(), 128, 128, "", false) if err != nil { b.Fatalf("cannot create temporary database: %v", err) } + db = rawdb.NewDatabase(pdb) defer db.Close() } - // Generate a chain of b.N blocks using the supplied block // generator function. gspec := &Genesis{ @@ -211,15 +216,27 @@ func BenchmarkChainRead_full_10k(b *testing.B) { benchReadChain(b, true, 10000) } func BenchmarkChainRead_header_100k(b *testing.B) { + if testing.Short() { + b.Skip("Skipping in short-mode") + } benchReadChain(b, false, 100000) } func BenchmarkChainRead_full_100k(b *testing.B) { + if testing.Short() { + b.Skip("Skipping in short-mode") + } benchReadChain(b, true, 100000) } func BenchmarkChainRead_header_500k(b *testing.B) { + if testing.Short() { + b.Skip("Skipping in short-mode") + } benchReadChain(b, false, 500000) } func BenchmarkChainRead_full_500k(b *testing.B) { + if testing.Short() { + b.Skip("Skipping in short-mode") + } benchReadChain(b, true, 500000) } func BenchmarkChainWrite_header_10k(b *testing.B) { @@ -235,9 +252,15 @@ func BenchmarkChainWrite_full_100k(b *testing.B) { benchWriteChain(b, true, 100000) } func BenchmarkChainWrite_header_500k(b *testing.B) { + if testing.Short() { + b.Skip("Skipping in short-mode") + } benchWriteChain(b, false, 500000) } func BenchmarkChainWrite_full_500k(b *testing.B) { + if testing.Short() { + b.Skip("Skipping in short-mode") + } benchWriteChain(b, true, 500000) } @@ -281,11 +304,11 @@ func makeChainForBench(db ethdb.Database, genesis *Genesis, full bool, count uin func benchWriteChain(b *testing.B, full bool, count uint64) { genesis := &Genesis{Config: params.AllEthashProtocolChanges} for i := 0; i < b.N; i++ { - dir := b.TempDir() - db, err := rawdb.NewLevelDBDatabase(dir, 128, 1024, "", false) + pdb, err := pebble.New(b.TempDir(), 1024, 128, "", false) if err != nil { - b.Fatalf("error opening database at %v: %v", dir, err) + b.Fatalf("error opening database: %v", err) } + db := rawdb.NewDatabase(pdb) makeChainForBench(db, genesis, full, count) db.Close() } @@ -294,10 +317,12 @@ func benchWriteChain(b *testing.B, full bool, count uint64) { func benchReadChain(b *testing.B, full bool, count uint64) { dir := b.TempDir() - db, err := rawdb.NewLevelDBDatabase(dir, 128, 1024, "", false) + pdb, err := pebble.New(dir, 1024, 128, "", false) if err != nil { - b.Fatalf("error opening database at %v: %v", dir, err) + b.Fatalf("error opening database: %v", err) } + db := rawdb.NewDatabase(pdb) + genesis := &Genesis{Config: params.AllEthashProtocolChanges} makeChainForBench(db, genesis, full, count) db.Close() @@ -308,15 +333,16 @@ func benchReadChain(b *testing.B, full bool, count uint64) { b.ResetTimer() for i := 0; i < b.N; i++ { - db, err := rawdb.NewLevelDBDatabase(dir, 128, 1024, "", false) + pdb, err = pebble.New(dir, 1024, 128, "", false) if err != nil { - b.Fatalf("error opening database at %v: %v", dir, err) + b.Fatalf("error opening database: %v", err) } + db = rawdb.NewDatabase(pdb) + chain, err := NewBlockChain(db, &cacheConfig, genesis, nil, ethash.NewFaker(), vm.Config{}, nil) if err != nil { b.Fatalf("error creating chain: %v", err) } - for n := uint64(0); n < count; n++ { header := chain.GetHeaderByNumber(n) if full { diff --git a/core/block_validator.go b/core/block_validator.go index 59783a0407..5885df9ee2 100644 --- a/core/block_validator.go +++ b/core/block_validator.go @@ -93,7 +93,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. diff --git a/core/block_validator_test.go b/core/block_validator_test.go index 80e6820661..8af4057693 100644 --- a/core/block_validator_test.go +++ b/core/block_validator_test.go @@ -113,8 +113,12 @@ func testHeaderVerificationForMerging(t *testing.T, isClique bool) { } copy(gspec.ExtraData[32:], addr[:]) + // chain_maker has no blockchain to retrieve the TTD from, setting to nil + // is a hack to signal it to generate pre-merge blocks + gspec.Config.TerminalTotalDifficulty = nil td := 0 genDb, blocks, _ := GenerateChainWithGenesis(gspec, engine, 8, nil) + for i, block := range blocks { header := block.Header() if i > 0 { @@ -145,7 +149,6 @@ func testHeaderVerificationForMerging(t *testing.T, isClique bool) { } preBlocks = blocks gspec.Config.TerminalTotalDifficulty = big.NewInt(int64(td)) - t.Logf("Set ttd to %v\n", gspec.Config.TerminalTotalDifficulty) postBlocks, _ = GenerateChain(gspec.Config, preBlocks[len(preBlocks)-1], engine, genDb, 8, func(i int, gen *BlockGen) { gen.SetPoS() }) diff --git a/core/blockchain.go b/core/blockchain.go index f7c921fe64..0fe4812626 100644 --- a/core/blockchain.go +++ b/core/blockchain.go @@ -23,6 +23,7 @@ import ( "io" "math/big" "runtime" + "slices" "strings" "sync" "sync/atomic" @@ -159,9 +160,9 @@ func (c *CacheConfig) triedbConfig(isVerkle bool) *triedb.Config { } if c.StateScheme == rawdb.PathScheme { config.PathDB = &pathdb.Config{ - StateHistory: c.StateHistory, - CleanCacheSize: c.TrieCleanLimit * 1024 * 1024, - DirtyCacheSize: c.TrieDirtyLimit * 1024 * 1024, + StateHistory: c.StateHistory, + CleanCacheSize: c.TrieCleanLimit * 1024 * 1024, + WriteBufferSize: c.TrieDirtyLimit * 1024 * 1024, } } return config @@ -224,7 +225,6 @@ type BlockChain struct { hc *HeaderChain rmLogsFeed event.Feed chainFeed event.Feed - chainSideFeed event.Feed chainHeadFeed event.Feed logsFeed event.Feed blockProcFeed event.Feed @@ -571,15 +571,14 @@ func (bc *BlockChain) SetHead(head uint64) error { } // Send chain head event to update the transaction pool header := bc.CurrentBlock() - block := bc.GetBlock(header.Hash(), header.Number.Uint64()) - if block == nil { + if block := bc.GetBlock(header.Hash(), header.Number.Uint64()); block == nil { // This should never happen. In practice, previously currentBlock // contained the entire block whereas now only a "marker", so there // is an ever so slight chance for a race we should handle. log.Error("Current block not found in database", "block", header.Number, "hash", header.Hash()) return fmt.Errorf("current block missing: #%d [%x..]", header.Number, header.Hash().Bytes()[:4]) } - bc.chainHeadFeed.Send(ChainHeadEvent{Block: block}) + bc.chainHeadFeed.Send(ChainHeadEvent{Header: header}) return nil } @@ -593,15 +592,14 @@ func (bc *BlockChain) SetHeadWithTimestamp(timestamp uint64) error { } // Send chain head event to update the transaction pool header := bc.CurrentBlock() - block := bc.GetBlock(header.Hash(), header.Number.Uint64()) - if block == nil { + if block := bc.GetBlock(header.Hash(), header.Number.Uint64()); block == nil { // This should never happen. In practice, previously currentBlock // contained the entire block whereas now only a "marker", so there // is an ever so slight chance for a race we should handle. log.Error("Current block not found in database", "block", header.Number, "hash", header.Hash()) return fmt.Errorf("current block missing: #%d [%x..]", header.Number, header.Hash().Bytes()[:4]) } - bc.chainHeadFeed.Send(ChainHeadEvent{Block: block}) + bc.chainHeadFeed.Send(ChainHeadEvent{Header: header}) return nil } @@ -1438,7 +1436,7 @@ func (bc *BlockChain) writeBlockWithoutState(block *types.Block, td *big.Int) (e func (bc *BlockChain) writeKnownBlock(block *types.Block) error { current := bc.CurrentBlock() if block.ParentHash() != current.Hash() { - if err := bc.reorg(current, block); err != nil { + if err := bc.reorg(current, block.Header()); err != nil { return err } } @@ -1544,7 +1542,7 @@ func (bc *BlockChain) writeBlockAndSetHead(block *types.Block, receipts []*types // Reorganise the chain if the parent is not the head block if block.ParentHash() != currentBlock.Hash() { - if err := bc.reorg(currentBlock, block); err != nil { + if err := bc.reorg(currentBlock, block.Header()); err != nil { return NonStatTy, err } } @@ -1552,7 +1550,7 @@ func (bc *BlockChain) writeBlockAndSetHead(block *types.Block, receipts []*types // Set new head. bc.writeHeadBlock(block) - bc.chainFeed.Send(ChainEvent{Block: block, Hash: block.Hash(), Logs: logs}) + bc.chainFeed.Send(ChainEvent{Header: block.Header()}) if len(logs) > 0 { bc.logsFeed.Send(logs) } @@ -1562,7 +1560,7 @@ func (bc *BlockChain) writeBlockAndSetHead(block *types.Block, receipts []*types // we will fire an accumulated ChainHeadEvent and disable fire // event here. if emitHeadEvent { - bc.chainHeadFeed.Send(ChainHeadEvent{Block: block}) + bc.chainHeadFeed.Send(ChainHeadEvent{Header: block.Header()}) } return CanonStatTy, nil } @@ -1627,7 +1625,7 @@ func (bc *BlockChain) insertChain(chain types.Blocks, setHead bool, makeWitness // Fire a single chain head event if we've progressed the chain defer func() { if lastCanon != nil && bc.CurrentBlock().Hash() == lastCanon.Hash() { - bc.chainHeadFeed.Send(ChainHeadEvent{lastCanon}) + bc.chainHeadFeed.Send(ChainHeadEvent{Header: lastCanon.Header()}) } }() // Start the parallel header verifier @@ -1776,7 +1774,6 @@ func (bc *BlockChain) insertChain(chain types.Blocks, setHead bool, makeWitness if err != nil { return nil, it.index, err } - statedb.SetLogger(bc.logger) // If we are past Byzantium, enable prefetching to pull in trie node paths // while processing transactions. Before Byzantium the prefetcher is mostly @@ -1929,7 +1926,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) } @@ -2157,8 +2154,8 @@ func (bc *BlockChain) recoverAncestors(block *types.Block, makeWitness bool) (co return block.Hash(), nil } -// collectLogs collects the logs that were generated or removed during -// the processing of a block. These logs are later announced as deleted or reborn. +// collectLogs collects the logs that were generated or removed during the +// processing of a block. These logs are later announced as deleted or reborn. func (bc *BlockChain) collectLogs(b *types.Block, removed bool) []*types.Log { var blobGasPrice *big.Int excessBlobGas := b.ExcessBlobGas() @@ -2184,70 +2181,55 @@ func (bc *BlockChain) collectLogs(b *types.Block, removed bool) []*types.Log { // reorg takes two blocks, an old chain and a new chain and will reconstruct the // blocks and inserts them to be part of the new canonical chain and accumulates // potential missing transactions and post an event about them. +// // Note the new head block won't be processed here, callers need to handle it // externally. -func (bc *BlockChain) reorg(oldHead *types.Header, newHead *types.Block) error { +func (bc *BlockChain) reorg(oldHead *types.Header, newHead *types.Header) error { var ( - newChain types.Blocks - oldChain types.Blocks - commonBlock *types.Block - - deletedTxs []common.Hash - addedTxs []common.Hash + newChain []*types.Header + oldChain []*types.Header + commonBlock *types.Header ) - oldBlock := bc.GetBlock(oldHead.Hash(), oldHead.Number.Uint64()) - if oldBlock == nil { - return errors.New("current head block missing") - } - newBlock := newHead - // Reduce the longer chain to the same number as the shorter one - if oldBlock.NumberU64() > newBlock.NumberU64() { + if oldHead.Number.Uint64() > newHead.Number.Uint64() { // Old chain is longer, gather all transactions and logs as deleted ones - for ; oldBlock != nil && oldBlock.NumberU64() != newBlock.NumberU64(); oldBlock = bc.GetBlock(oldBlock.ParentHash(), oldBlock.NumberU64()-1) { - oldChain = append(oldChain, oldBlock) - for _, tx := range oldBlock.Transactions() { - deletedTxs = append(deletedTxs, tx.Hash()) - } + for ; oldHead != nil && oldHead.Number.Uint64() != newHead.Number.Uint64(); oldHead = bc.GetHeader(oldHead.ParentHash, oldHead.Number.Uint64()-1) { + oldChain = append(oldChain, oldHead) } } else { // New chain is longer, stash all blocks away for subsequent insertion - for ; newBlock != nil && newBlock.NumberU64() != oldBlock.NumberU64(); newBlock = bc.GetBlock(newBlock.ParentHash(), newBlock.NumberU64()-1) { - newChain = append(newChain, newBlock) + for ; newHead != nil && newHead.Number.Uint64() != oldHead.Number.Uint64(); newHead = bc.GetHeader(newHead.ParentHash, newHead.Number.Uint64()-1) { + newChain = append(newChain, newHead) } } - if oldBlock == nil { + if oldHead == nil { return errInvalidOldChain } - if newBlock == nil { + if newHead == nil { return errInvalidNewChain } // Both sides of the reorg are at the same number, reduce both until the common // ancestor is found for { // If the common ancestor was found, bail out - if oldBlock.Hash() == newBlock.Hash() { - commonBlock = oldBlock + if oldHead.Hash() == newHead.Hash() { + commonBlock = oldHead break } // Remove an old block as well as stash away a new block - oldChain = append(oldChain, oldBlock) - for _, tx := range oldBlock.Transactions() { - deletedTxs = append(deletedTxs, tx.Hash()) - } - newChain = append(newChain, newBlock) + oldChain = append(oldChain, oldHead) + newChain = append(newChain, newHead) // Step back with both chains - oldBlock = bc.GetBlock(oldBlock.ParentHash(), oldBlock.NumberU64()-1) - if oldBlock == nil { + oldHead = bc.GetHeader(oldHead.ParentHash, oldHead.Number.Uint64()-1) + if oldHead == nil { return errInvalidOldChain } - newBlock = bc.GetBlock(newBlock.ParentHash(), newBlock.NumberU64()-1) - if newBlock == nil { + newHead = bc.GetHeader(newHead.ParentHash, newHead.Number.Uint64()-1) + if newHead == nil { return errInvalidNewChain } } - // Ensure the user sees large reorgs if len(oldChain) > 0 && len(newChain) > 0 { logFn := log.Info @@ -2256,7 +2238,7 @@ func (bc *BlockChain) reorg(oldHead *types.Header, newHead *types.Block) error { msg = "Large chain reorg detected" logFn = log.Warn } - logFn(msg, "number", commonBlock.Number(), "hash", commonBlock.Hash(), + logFn(msg, "number", commonBlock.Number, "hash", commonBlock.Hash(), "drop", len(oldChain), "dropfrom", oldChain[0].Hash(), "add", len(newChain), "addfrom", newChain[0].Hash()) blockReorgAddMeter.Mark(int64(len(newChain))) blockReorgDropMeter.Mark(int64(len(oldChain))) @@ -2264,55 +2246,112 @@ func (bc *BlockChain) reorg(oldHead *types.Header, newHead *types.Block) error { } else if len(newChain) > 0 { // Special case happens in the post merge stage that current head is // the ancestor of new head while these two blocks are not consecutive - log.Info("Extend chain", "add", len(newChain), "number", newChain[0].Number(), "hash", newChain[0].Hash()) + log.Info("Extend chain", "add", len(newChain), "number", newChain[0].Number, "hash", newChain[0].Hash()) blockReorgAddMeter.Mark(int64(len(newChain))) } else { // len(newChain) == 0 && len(oldChain) > 0 // rewind the canonical chain to a lower point. - log.Error("Impossible reorg, please file an issue", "oldnum", oldBlock.Number(), "oldhash", oldBlock.Hash(), "oldblocks", len(oldChain), "newnum", newBlock.Number(), "newhash", newBlock.Hash(), "newblocks", len(newChain)) + log.Error("Impossible reorg, please file an issue", "oldnum", oldHead.Number, "oldhash", oldHead.Hash(), "oldblocks", len(oldChain), "newnum", newHead.Number, "newhash", newHead.Hash(), "newblocks", len(newChain)) } // Acquire the tx-lookup lock before mutation. This step is essential // as the txlookups should be changed atomically, and all subsequent // reads should be blocked until the mutation is complete. bc.txLookupLock.Lock() - // Insert the new chain segment in incremental order, from the old - // to the new. The new chain head (newChain[0]) is not inserted here, - // as it will be handled separately outside of this function - for i := len(newChain) - 1; i >= 1; i-- { - // Insert the block in the canonical way, re-writing history - bc.writeHeadBlock(newChain[i]) + // Reorg can be executed, start reducing the chain's old blocks and appending + // the new blocks + var ( + deletedTxs []common.Hash + rebirthTxs []common.Hash - // Collect the new added transactions. - for _, tx := range newChain[i].Transactions() { - addedTxs = append(addedTxs, tx.Hash()) + deletedLogs []*types.Log + rebirthLogs []*types.Log + ) + // Deleted log emission on the API uses forward order, which is borked, but + // we'll leave it in for legacy reasons. + // + // TODO(karalabe): This should be nuked out, no idea how, deprecate some APIs? + { + for i := len(oldChain) - 1; i >= 0; i-- { + block := bc.GetBlock(oldChain[i].Hash(), oldChain[i].Number.Uint64()) + if block == nil { + return errInvalidOldChain // Corrupt database, mostly here to avoid weird panics + } + if logs := bc.collectLogs(block, true); len(logs) > 0 { + deletedLogs = append(deletedLogs, logs...) + } + if len(deletedLogs) > 512 { + bc.rmLogsFeed.Send(RemovedLogsEvent{deletedLogs}) + deletedLogs = nil + } + } + if len(deletedLogs) > 0 { + bc.rmLogsFeed.Send(RemovedLogsEvent{deletedLogs}) } } + // Undo old blocks in reverse order + for i := 0; i < len(oldChain); i++ { + // Collect all the deleted transactions + block := bc.GetBlock(oldChain[i].Hash(), oldChain[i].Number.Uint64()) + if block == nil { + return errInvalidOldChain // Corrupt database, mostly here to avoid weird panics + } + for _, tx := range block.Transactions() { + deletedTxs = append(deletedTxs, tx.Hash()) + } + // Collect deleted logs and emit them for new integrations + if logs := bc.collectLogs(block, true); len(logs) > 0 { + // Emit revertals latest first, older then + slices.Reverse(logs) + // TODO(karalabe): Hook into the reverse emission part + } + } + // Apply new blocks in forward order + for i := len(newChain) - 1; i >= 1; i-- { + // Collect all the included transactions + block := bc.GetBlock(newChain[i].Hash(), newChain[i].Number.Uint64()) + if block == nil { + return errInvalidNewChain // Corrupt database, mostly here to avoid weird panics + } + for _, tx := range block.Transactions() { + rebirthTxs = append(rebirthTxs, tx.Hash()) + } + // Collect inserted logs and emit them + if logs := bc.collectLogs(block, false); len(logs) > 0 { + rebirthLogs = append(rebirthLogs, logs...) + } + if len(rebirthLogs) > 512 { + bc.logsFeed.Send(rebirthLogs) + rebirthLogs = nil + } + // Update the head block + bc.writeHeadBlock(block) + } + if len(rebirthLogs) > 0 { + bc.logsFeed.Send(rebirthLogs) + } // Delete useless indexes right now which includes the non-canonical // transaction indexes, canonical chain indexes which above the head. - var ( - indexesBatch = bc.db.NewBatch() - diffs = types.HashDifference(deletedTxs, addedTxs) - ) - for _, tx := range diffs { - rawdb.DeleteTxLookupEntry(indexesBatch, tx) + batch := bc.db.NewBatch() + for _, tx := range types.HashDifference(deletedTxs, rebirthTxs) { + rawdb.DeleteTxLookupEntry(batch, tx) } // Delete all hash markers that are not part of the new canonical chain. // Because the reorg function does not handle new chain head, all hash // markers greater than or equal to new chain head should be deleted. - number := commonBlock.NumberU64() + number := commonBlock.Number if len(newChain) > 1 { - number = newChain[1].NumberU64() + number = newChain[1].Number } - for i := number + 1; ; i++ { + for i := number.Uint64() + 1; ; i++ { hash := rawdb.ReadCanonicalHash(bc.db, i) if hash == (common.Hash{}) { break } - rawdb.DeleteCanonicalHash(indexesBatch, i) + rawdb.DeleteCanonicalHash(batch, i) } - if err := indexesBatch.Write(); err != nil { + if err := batch.Write(); err != nil { log.Crit("Failed to delete useless indexes", "err", err) } // Reset the tx lookup cache to clear stale txlookup cache. @@ -2321,43 +2360,6 @@ func (bc *BlockChain) reorg(oldHead *types.Header, newHead *types.Block) error { // Release the tx-lookup lock after mutation. bc.txLookupLock.Unlock() - // Send out events for logs from the old canon chain, and 'reborn' - // logs from the new canon chain. The number of logs can be very - // high, so the events are sent in batches of size around 512. - - // Deleted logs + blocks: - var deletedLogs []*types.Log - for i := len(oldChain) - 1; i >= 0; i-- { - // Also send event for blocks removed from the canon chain. - bc.chainSideFeed.Send(ChainSideEvent{Block: oldChain[i]}) - - // Collect deleted logs for notification - if logs := bc.collectLogs(oldChain[i], true); len(logs) > 0 { - deletedLogs = append(deletedLogs, logs...) - } - if len(deletedLogs) > 512 { - bc.rmLogsFeed.Send(RemovedLogsEvent{deletedLogs}) - deletedLogs = nil - } - } - if len(deletedLogs) > 0 { - bc.rmLogsFeed.Send(RemovedLogsEvent{deletedLogs}) - } - - // New logs: - var rebirthLogs []*types.Log - for i := len(newChain) - 1; i >= 1; i-- { - if logs := bc.collectLogs(newChain[i], false); len(logs) > 0 { - rebirthLogs = append(rebirthLogs, logs...) - } - if len(rebirthLogs) > 512 { - bc.logsFeed.Send(rebirthLogs) - rebirthLogs = nil - } - } - if len(rebirthLogs) > 0 { - bc.logsFeed.Send(rebirthLogs) - } return nil } @@ -2395,7 +2397,7 @@ func (bc *BlockChain) SetCanonical(head *types.Block) (common.Hash, error) { // Run the reorg if necessary and set the given block as new head. start := time.Now() if head.ParentHash() != bc.CurrentBlock().Hash() { - if err := bc.reorg(bc.CurrentBlock(), head); err != nil { + if err := bc.reorg(bc.CurrentBlock(), head.Header()); err != nil { return common.Hash{}, err } } @@ -2403,11 +2405,11 @@ func (bc *BlockChain) SetCanonical(head *types.Block) (common.Hash, error) { // Emit events logs := bc.collectLogs(head, false) - bc.chainFeed.Send(ChainEvent{Block: head, Hash: head.Hash(), Logs: logs}) + bc.chainFeed.Send(ChainEvent{Header: head.Header()}) if len(logs) > 0 { bc.logsFeed.Send(logs) } - bc.chainHeadFeed.Send(ChainHeadEvent{Block: head}) + bc.chainHeadFeed.Send(ChainHeadEvent{Header: head.Header()}) context := []interface{}{ "number", head.Number(), diff --git a/core/blockchain_reader.go b/core/blockchain_reader.go index 6b8dffdcdc..cf72e98cb2 100644 --- a/core/blockchain_reader.go +++ b/core/blockchain_reader.go @@ -277,6 +277,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 @@ -337,10 +344,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) @@ -430,11 +434,6 @@ func (bc *BlockChain) SubscribeChainHeadEvent(ch chan<- ChainHeadEvent) event.Su return bc.scope.Track(bc.chainHeadFeed.Subscribe(ch)) } -// SubscribeChainSideEvent registers a subscription of ChainSideEvent. -func (bc *BlockChain) SubscribeChainSideEvent(ch chan<- ChainSideEvent) event.Subscription { - return bc.scope.Track(bc.chainSideFeed.Subscribe(ch)) -} - // SubscribeLogsEvent registers a subscription of []*types.Log. func (bc *BlockChain) SubscribeLogsEvent(ch chan<- []*types.Log) event.Subscription { return bc.scope.Track(bc.logsFeed.Subscribe(ch)) diff --git a/core/blockchain_repair_test.go b/core/blockchain_repair_test.go index aeeb9095d8..6c52d057ad 100644 --- a/core/blockchain_repair_test.go +++ b/core/blockchain_repair_test.go @@ -31,6 +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/ethdb/pebble" "github.com/ethereum/go-ethereum/params" ) @@ -1764,13 +1765,13 @@ func testRepairWithScheme(t *testing.T, tt *rewindTest, snapshots bool, scheme s datadir := t.TempDir() ancient := filepath.Join(datadir, "ancient") - db, err := rawdb.Open(rawdb.OpenOptions{ - Directory: datadir, - AncientsDirectory: ancient, - Ephemeral: true, - }) + pdb, err := pebble.New(datadir, 0, 0, "", false) if err != nil { - t.Fatalf("Failed to create persistent database: %v", err) + t.Fatalf("Failed to create persistent key-value database: %v", err) + } + db, err := rawdb.NewDatabaseWithFreezer(pdb, ancient, "", false) + if err != nil { + t.Fatalf("Failed to create persistent freezer database: %v", err) } defer db.Close() // Might double close, should be fine @@ -1849,13 +1850,13 @@ func testRepairWithScheme(t *testing.T, tt *rewindTest, snapshots bool, scheme s chain.stopWithoutSaving() // Start a new blockchain back up and see where the repair leads us - db, err = rawdb.Open(rawdb.OpenOptions{ - Directory: datadir, - AncientsDirectory: ancient, - Ephemeral: true, - }) + pdb, err = pebble.New(datadir, 0, 0, "", false) if err != nil { - t.Fatalf("Failed to reopen persistent database: %v", err) + t.Fatalf("Failed to reopen persistent key-value database: %v", err) + } + db, err = rawdb.NewDatabaseWithFreezer(pdb, ancient, "", false) + if err != nil { + t.Fatalf("Failed to reopen persistent freezer database: %v", err) } defer db.Close() @@ -1914,12 +1915,13 @@ func testIssue23496(t *testing.T, scheme string) { datadir := t.TempDir() ancient := filepath.Join(datadir, "ancient") - db, err := rawdb.Open(rawdb.OpenOptions{ - Directory: datadir, - AncientsDirectory: ancient, - }) + pdb, err := pebble.New(datadir, 0, 0, "", false) if err != nil { - t.Fatalf("Failed to create persistent database: %v", err) + t.Fatalf("Failed to create persistent key-value database: %v", err) + } + db, err := rawdb.NewDatabaseWithFreezer(pdb, ancient, "", false) + if err != nil { + t.Fatalf("Failed to create persistent freezer database: %v", err) } defer db.Close() // Might double close, should be fine @@ -1971,13 +1973,13 @@ func testIssue23496(t *testing.T, scheme string) { chain.stopWithoutSaving() // Start a new blockchain back up and see where the repair leads us - db, err = rawdb.Open(rawdb.OpenOptions{ - Directory: datadir, - AncientsDirectory: ancient, - Ephemeral: true, - }) + pdb, err = pebble.New(datadir, 0, 0, "", false) if err != nil { - t.Fatalf("Failed to reopen persistent database: %v", err) + t.Fatalf("Failed to reopen persistent key-value database: %v", err) + } + db, err = rawdb.NewDatabaseWithFreezer(pdb, ancient, "", false) + if err != nil { + t.Fatalf("Failed to reopen persistent freezer database: %v", err) } defer db.Close() diff --git a/core/blockchain_sethead_test.go b/core/blockchain_sethead_test.go index 123c2c9af1..424854b2bf 100644 --- a/core/blockchain_sethead_test.go +++ b/core/blockchain_sethead_test.go @@ -33,6 +33,7 @@ import ( "github.com/ethereum/go-ethereum/core/state" "github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/core/vm" + "github.com/ethereum/go-ethereum/ethdb/pebble" "github.com/ethereum/go-ethereum/params" "github.com/ethereum/go-ethereum/triedb" "github.com/ethereum/go-ethereum/triedb/hashdb" @@ -1968,13 +1969,13 @@ func testSetHeadWithScheme(t *testing.T, tt *rewindTest, snapshots bool, scheme datadir := t.TempDir() ancient := filepath.Join(datadir, "ancient") - db, err := rawdb.Open(rawdb.OpenOptions{ - Directory: datadir, - AncientsDirectory: ancient, - Ephemeral: true, - }) + pdb, err := pebble.New(datadir, 0, 0, "", false) if err != nil { - t.Fatalf("Failed to create persistent database: %v", err) + t.Fatalf("Failed to create persistent key-value database: %v", err) + } + db, err := rawdb.NewDatabaseWithFreezer(pdb, ancient, "", false) + if err != nil { + t.Fatalf("Failed to create persistent freezer database: %v", err) } defer db.Close() diff --git a/core/blockchain_snapshot_test.go b/core/blockchain_snapshot_test.go index 3803c153e7..1a6fe38af6 100644 --- a/core/blockchain_snapshot_test.go +++ b/core/blockchain_snapshot_test.go @@ -35,6 +35,7 @@ import ( "github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/core/vm" "github.com/ethereum/go-ethereum/ethdb" + "github.com/ethereum/go-ethereum/ethdb/pebble" "github.com/ethereum/go-ethereum/params" ) @@ -65,13 +66,13 @@ func (basic *snapshotTestBasic) prepare(t *testing.T) (*BlockChain, []*types.Blo datadir := t.TempDir() ancient := filepath.Join(datadir, "ancient") - db, err := rawdb.Open(rawdb.OpenOptions{ - Directory: datadir, - AncientsDirectory: ancient, - Ephemeral: true, - }) + pdb, err := pebble.New(datadir, 0, 0, "", false) if err != nil { - t.Fatalf("Failed to create persistent database: %v", err) + t.Fatalf("Failed to create persistent key-value database: %v", err) + } + db, err := rawdb.NewDatabaseWithFreezer(pdb, ancient, "", false) + if err != nil { + t.Fatalf("Failed to create persistent freezer database: %v", err) } // Initialize a fresh chain var ( @@ -256,13 +257,13 @@ func (snaptest *crashSnapshotTest) test(t *testing.T) { chain.triedb.Close() // Start a new blockchain back up and see where the repair leads us - newdb, err := rawdb.Open(rawdb.OpenOptions{ - Directory: snaptest.datadir, - AncientsDirectory: snaptest.ancient, - Ephemeral: true, - }) + pdb, err := pebble.New(snaptest.datadir, 0, 0, "", false) if err != nil { - t.Fatalf("Failed to reopen persistent database: %v", err) + t.Fatalf("Failed to create persistent key-value database: %v", err) + } + newdb, err := rawdb.NewDatabaseWithFreezer(pdb, snaptest.ancient, "", false) + if err != nil { + t.Fatalf("Failed to create persistent freezer database: %v", err) } defer newdb.Close() diff --git a/core/blockchain_test.go b/core/blockchain_test.go index f107b322b6..fc0e3d8446 100644 --- a/core/blockchain_test.go +++ b/core/blockchain_test.go @@ -17,8 +17,10 @@ package core import ( + "bytes" "errors" "fmt" + gomath "math" "math/big" "math/rand" "os" @@ -28,7 +30,6 @@ import ( "time" "github.com/ethereum/go-ethereum/common" - "github.com/ethereum/go-ethereum/common/math" "github.com/ethereum/go-ethereum/consensus" "github.com/ethereum/go-ethereum/consensus/beacon" "github.com/ethereum/go-ethereum/consensus/ethash" @@ -36,9 +37,11 @@ import ( "github.com/ethereum/go-ethereum/core/state" "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/crypto" "github.com/ethereum/go-ethereum/eth/tracers/logger" "github.com/ethereum/go-ethereum/ethdb" + "github.com/ethereum/go-ethereum/ethdb/pebble" "github.com/ethereum/go-ethereum/params" "github.com/ethereum/go-ethereum/trie" "github.com/holiman/uint256" @@ -1332,85 +1335,6 @@ func checkLogEvents(t *testing.T, logsCh <-chan []*types.Log, rmLogsCh <-chan Re } } -func TestReorgSideEvent(t *testing.T) { - testReorgSideEvent(t, rawdb.HashScheme) - testReorgSideEvent(t, rawdb.PathScheme) -} - -func testReorgSideEvent(t *testing.T, scheme string) { - var ( - key1, _ = crypto.HexToECDSA("b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291") - addr1 = crypto.PubkeyToAddress(key1.PublicKey) - gspec = &Genesis{ - Config: params.TestChainConfig, - Alloc: types.GenesisAlloc{addr1: {Balance: big.NewInt(10000000000000000)}}, - } - signer = types.LatestSigner(gspec.Config) - ) - blockchain, _ := NewBlockChain(rawdb.NewMemoryDatabase(), DefaultCacheConfigWithScheme(scheme), gspec, nil, ethash.NewFaker(), vm.Config{}, nil) - defer blockchain.Stop() - - _, chain, _ := GenerateChainWithGenesis(gspec, ethash.NewFaker(), 3, func(i int, gen *BlockGen) {}) - if _, err := blockchain.InsertChain(chain); err != nil { - t.Fatalf("failed to insert chain: %v", err) - } - - _, replacementBlocks, _ := GenerateChainWithGenesis(gspec, ethash.NewFaker(), 4, func(i int, gen *BlockGen) { - tx, err := types.SignTx(types.NewContractCreation(gen.TxNonce(addr1), new(big.Int), 1000000, gen.header.BaseFee, nil), signer, key1) - if i == 2 { - gen.OffsetTime(-9) - } - if err != nil { - t.Fatalf("failed to create tx: %v", err) - } - gen.AddTx(tx) - }) - chainSideCh := make(chan ChainSideEvent, 64) - blockchain.SubscribeChainSideEvent(chainSideCh) - if _, err := blockchain.InsertChain(replacementBlocks); err != nil { - t.Fatalf("failed to insert chain: %v", err) - } - - expectedSideHashes := map[common.Hash]bool{ - chain[0].Hash(): true, - chain[1].Hash(): true, - chain[2].Hash(): true, - } - - i := 0 - - const timeoutDura = 10 * time.Second - timeout := time.NewTimer(timeoutDura) -done: - for { - select { - case ev := <-chainSideCh: - block := ev.Block - if _, ok := expectedSideHashes[block.Hash()]; !ok { - t.Errorf("%d: didn't expect %x to be in side chain", i, block.Hash()) - } - i++ - - if i == len(expectedSideHashes) { - timeout.Stop() - - break done - } - timeout.Reset(timeoutDura) - - case <-timeout.C: - t.Fatalf("Timeout. Possibly not all blocks were triggered for sideevent: %v", i) - } - } - - // make sure no more events are fired - select { - case e := <-chainSideCh: - t.Errorf("unexpected event fired: %v", e) - case <-time.After(250 * time.Millisecond): - } -} - // Tests if the canonical block can be fetched from the database during chain insertion. func TestCanonicalBlockRetrieval(t *testing.T) { testCanonicalBlockRetrieval(t, rawdb.HashScheme) @@ -2028,11 +1952,11 @@ func testSideImport(t *testing.T, numCanonBlocksInSidechain, blocksBetweenCommon gspec = &Genesis{ Config: &chainConfig, - Alloc: types.GenesisAlloc{addr: {Balance: big.NewInt(math.MaxInt64)}}, + Alloc: types.GenesisAlloc{addr: {Balance: big.NewInt(gomath.MaxInt64)}}, BaseFee: big.NewInt(params.InitialBaseFee), } signer = types.LatestSigner(gspec.Config) - mergeBlock = math.MaxInt32 + mergeBlock = gomath.MaxInt32 ) // Generate and import the canonical chain chain, err := NewBlockChain(rawdb.NewMemoryDatabase(), nil, gspec, nil, engine, vm.Config{}, nil) @@ -2315,7 +2239,7 @@ func testInsertKnownChainDataWithMerging(t *testing.T, typ string, mergeHeight i Config: &chainConfig, } engine = beacon.New(ethash.NewFaker()) - mergeBlock = uint64(math.MaxUint64) + mergeBlock = uint64(gomath.MaxUint64) ) // Apply merging since genesis if mergeHeight == 0 { @@ -2741,13 +2665,13 @@ func testSideImportPrunedBlocks(t *testing.T, scheme string) { datadir := t.TempDir() ancient := path.Join(datadir, "ancient") - db, err := rawdb.Open(rawdb.OpenOptions{ - Directory: datadir, - AncientsDirectory: ancient, - Ephemeral: true, - }) + pdb, err := pebble.New(datadir, 0, 0, "", false) if err != nil { - t.Fatalf("Failed to create persistent database: %v", err) + t.Fatalf("Failed to create persistent key-value database: %v", err) + } + db, err := rawdb.NewDatabaseWithFreezer(pdb, ancient, "", false) + if err != nil { + t.Fatalf("Failed to create persistent freezer database: %v", err) } defer db.Close() @@ -4171,7 +4095,6 @@ func TestEIP3651(t *testing.T) { gspec.Config.BerlinBlock = common.Big0 gspec.Config.LondonBlock = common.Big0 gspec.Config.TerminalTotalDifficulty = common.Big0 - gspec.Config.TerminalTotalDifficultyPassed = true gspec.Config.ShanghaiTime = u64(0) signer := types.LatestSigner(gspec.Config) @@ -4311,3 +4234,95 @@ func TestPragueRequests(t *testing.T) { t.Fatalf("block %d: failed to insert into chain: %v", n, err) } } + +// TestEIP7702 deploys two delegation designations and calls them. It writes one +// value to storage which is verified after. +func TestEIP7702(t *testing.T) { + var ( + config = *params.MergedTestChainConfig + signer = types.LatestSigner(&config) + engine = beacon.NewFaker() + key1, _ = crypto.HexToECDSA("b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291") + key2, _ = crypto.HexToECDSA("8a1f9a8f95be41cd7ccb6168179afb4504aefe388d1e14474d32c45c72ce7b7a") + addr1 = crypto.PubkeyToAddress(key1.PublicKey) + addr2 = crypto.PubkeyToAddress(key2.PublicKey) + aa = common.HexToAddress("0x000000000000000000000000000000000000aaaa") + bb = common.HexToAddress("0x000000000000000000000000000000000000bbbb") + funds = new(big.Int).Mul(common.Big1, big.NewInt(params.Ether)) + ) + gspec := &Genesis{ + Config: &config, + Alloc: types.GenesisAlloc{ + addr1: {Balance: funds}, + addr2: {Balance: funds}, + aa: { // The address 0xAAAA calls into addr2 + Code: program.New().Call(nil, addr2, 1, 0, 0, 0, 0).Bytes(), + Nonce: 0, + Balance: big.NewInt(0), + }, + bb: { // The address 0xBBBB sstores 42 into slot 42. + Code: program.New().Sstore(0x42, 0x42).Bytes(), + Nonce: 0, + Balance: big.NewInt(0), + }, + }, + } + + // Sign authorization tuples. + // The way the auths are combined, it becomes + // 1. tx -> addr1 which is delegated to 0xaaaa + // 2. addr1:0xaaaa calls into addr2:0xbbbb + // 3. addr2:0xbbbb writes to storage + auth1, _ := types.SignSetCode(key1, types.SetCodeAuthorization{ + ChainID: gspec.Config.ChainID.Uint64(), + Address: aa, + Nonce: 1, + }) + auth2, _ := types.SignSetCode(key2, types.SetCodeAuthorization{ + ChainID: 0, + Address: bb, + Nonce: 0, + }) + + _, blocks, _ := GenerateChainWithGenesis(gspec, engine, 1, func(i int, b *BlockGen) { + b.SetCoinbase(aa) + txdata := &types.SetCodeTx{ + ChainID: gspec.Config.ChainID.Uint64(), + Nonce: 0, + To: addr1, + Gas: 500000, + GasFeeCap: uint256.MustFromBig(newGwei(5)), + GasTipCap: uint256.NewInt(2), + AuthList: []types.SetCodeAuthorization{auth1, auth2}, + } + tx := types.MustSignNewTx(key1, signer, txdata) + b.AddTx(tx) + }) + chain, err := NewBlockChain(rawdb.NewMemoryDatabase(), nil, gspec, nil, engine, vm.Config{}, nil) + if err != nil { + t.Fatalf("failed to create tester chain: %v", err) + } + defer chain.Stop() + if n, err := chain.InsertChain(blocks); err != nil { + t.Fatalf("block %d: failed to insert into chain: %v", n, err) + } + + // Verify delegation designations were deployed. + state, _ := chain.State() + code, want := state.GetCode(addr1), types.AddressToDelegation(auth1.Address) + if !bytes.Equal(code, want) { + t.Fatalf("addr1 code incorrect: got %s, want %s", common.Bytes2Hex(code), common.Bytes2Hex(want)) + } + code, want = state.GetCode(addr2), types.AddressToDelegation(auth2.Address) + if !bytes.Equal(code, want) { + t.Fatalf("addr2 code incorrect: got %s, want %s", common.Bytes2Hex(code), common.Bytes2Hex(want)) + } + // Verify delegation executed the correct code. + var ( + fortyTwo = common.BytesToHash([]byte{0x42}) + actual = state.GetState(addr2, fortyTwo) + ) + if actual.Cmp(fortyTwo) != 0 { + t.Fatalf("addr2 storage wrong: expected %d, got %d", fortyTwo, actual) + } +} diff --git a/core/chain_indexer.go b/core/chain_indexer.go index f5fce72588..2865daa1ff 100644 --- a/core/chain_indexer.go +++ b/core/chain_indexer.go @@ -222,20 +222,19 @@ func (c *ChainIndexer) eventLoop(currentHeader *types.Header, events chan ChainH errc <- nil return } - header := ev.Block.Header() - if header.ParentHash != prevHash { + if ev.Header.ParentHash != prevHash { // Reorg to the common ancestor if needed (might not exist in light sync mode, skip reorg then) // TODO(karalabe, zsfelfoldi): This seems a bit brittle, can we detect this case explicitly? if rawdb.ReadCanonicalHash(c.chainDb, prevHeader.Number.Uint64()) != prevHash { - if h := rawdb.FindCommonAncestor(c.chainDb, prevHeader, header); h != nil { + if h := rawdb.FindCommonAncestor(c.chainDb, prevHeader, ev.Header); h != nil { c.newHead(h.Number.Uint64(), true) } } } - c.newHead(header.Number.Uint64(), false) + c.newHead(ev.Header.Number.Uint64(), false) - prevHeader, prevHash = header, header.Hash() + prevHeader, prevHash = ev.Header, ev.Header.Hash() } } } diff --git a/core/chain_makers.go b/core/chain_makers.go index 586979e772..26714845eb 100644 --- a/core/chain_makers.go +++ b/core/chain_makers.go @@ -98,11 +98,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 @@ -116,8 +113,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) + receipt, err := ApplyTransaction(evm, b.gasPool, b.statedb, b.header, tx, &b.header.GasUsed) if err != nil { panic(err) } @@ -292,6 +293,41 @@ func (b *BlockGen) OffsetTime(seconds int64) { b.header.Difficulty = b.engine.CalcDifficulty(b.cm, b.header.Time, b.parent.Header()) } +// ConsensusLayerRequests returns the EIP-7685 requests which have accumulated so far. +func (b *BlockGen) ConsensusLayerRequests() [][]byte { + return b.collectRequests(true) +} + +func (b *BlockGen) collectRequests(readonly bool) (requests [][]byte) { + statedb := b.statedb + if readonly { + // The system contracts clear themselves on a system-initiated read. + // When reading the requests mid-block, we don't want this behavior, so fork + // off the statedb before executing the system calls. + statedb = statedb.Copy() + } + + if b.cm.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...) + } + if err := ParseDepositLogs(&requests, blockLogs, b.cm.config); err != nil { + panic(fmt.Sprintf("failed to parse deposit log: %v", err)) + } + // create EVM for system calls + blockContext := NewEVMBlockContext(b.header, b.cm, &b.header.Coinbase) + evm := vm.NewEVM(blockContext, statedb, b.cm.config, vm.Config{}) + // EIP-7002 + ProcessWithdrawalQueue(&requests, evm) + // EIP-7251 + ProcessConsolidationQueue(&requests, evm) + } + return requests +} + // GenerateChain creates a chain of n blocks. The first block's // parent will be the provided parent. db is used to store // intermediate states and should contain the parent's state trie. @@ -329,6 +365,7 @@ func GenerateChain(config *params.ChainConfig, parent *types.Block, engine conse b.header.Difficulty = big.NewInt(0) } } + // Mutate the state and block according to any hard-fork specs if daoBlock := config.DAOForkBlock; daoBlock != nil { limit := new(big.Int).Add(daoBlock, params.DAOForkExtraRange) @@ -341,33 +378,21 @@ func GenerateChain(config *params.ChainConfig, parent *types.Block, engine conse if config.DAOForkSupport && config.DAOForkBlock != nil && config.DAOForkBlock.Cmp(b.header.Number) == 0 { misc.ApplyDAOHardFork(statedb) } + + if config.IsPrague(b.header.Number, b.header.Time) { + // EIP-2935 + blockContext := NewEVMBlockContext(b.header, cm, &b.header.Coinbase) + blockContext.Random = &common.Hash{} // enable post-merge instruction set + evm := vm.NewEVM(blockContext, statedb, cm.config, vm.Config{}) + ProcessParentBlockHash(b.header.ParentHash, evm) + } + // Execute any user modifications to the block if gen != nil { gen(i, b) } - var requests [][]byte - if config.IsPrague(b.header.Number, b.header.Time) { - // 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 { - 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) - } + requests := b.collectRequests(false) if requests != nil { reqHash := types.CalcRequestsHash(requests) b.header.RequestsHash = &reqHash @@ -466,8 +491,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/chain_makers_test.go b/core/chain_makers_test.go index 61d09117bd..f72e6285df 100644 --- a/core/chain_makers_test.go +++ b/core/chain_makers_test.go @@ -57,7 +57,6 @@ func TestGeneratePOSChain(t *testing.T) { db = rawdb.NewMemoryDatabase() ) - config.TerminalTotalDifficultyPassed = true config.TerminalTotalDifficulty = common.Big0 config.ShanghaiTime = u64(0) config.CancunTime = u64(0) diff --git a/core/error.go b/core/error.go index 161538fe43..82f7ddcf5d 100644 --- a/core/error.go +++ b/core/error.go @@ -103,6 +103,8 @@ var ( // ErrSenderNoEOA is returned if the sender of a transaction is a contract. ErrSenderNoEOA = errors.New("sender not an eoa") + // -- EIP-4844 errors -- + // ErrBlobFeeCapTooLow is returned if the transaction fee cap is less than the // blob gas fee of the block. ErrBlobFeeCapTooLow = errors.New("max fee per blob gas less than block blob gas fee") @@ -112,4 +114,20 @@ var ( // ErrBlobTxCreate is returned if a blob transaction has no explicit to field. ErrBlobTxCreate = errors.New("blob transaction of type create") + + // -- EIP-7702 errors -- + + // Message validation errors: + ErrEmptyAuthList = errors.New("EIP-7702 transaction with empty auth list") + ErrSetCodeTxCreate = errors.New("EIP-7702 transaction cannot be used to create contract") +) + +// EIP-7702 state transition errors. +// Note these are just informational, and do not cause tx execution abort. +var ( + ErrAuthorizationWrongChainID = errors.New("EIP-7702 authorization chain ID mismatch") + ErrAuthorizationNonceOverflow = errors.New("EIP-7702 authorization nonce > 64 bit") + ErrAuthorizationInvalidSignature = errors.New("EIP-7702 authorization has invalid signature") + ErrAuthorizationDestinationHasCode = errors.New("EIP-7702 authorization destination is a contract") + ErrAuthorizationNonceMismatch = errors.New("EIP-7702 authorization nonce does not match current account nonce") ) diff --git a/core/events.go b/core/events.go index ac935a137f..5ad2cb1f7b 100644 --- a/core/events.go +++ b/core/events.go @@ -17,27 +17,19 @@ package core import ( - "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/core/types" ) // NewTxsEvent is posted when a batch of transactions enter the transaction pool. type NewTxsEvent struct{ Txs []*types.Transaction } -// NewMinedBlockEvent is posted when a block has been imported. -type NewMinedBlockEvent struct{ Block *types.Block } - // RemovedLogsEvent is posted when a reorg happens type RemovedLogsEvent struct{ Logs []*types.Log } type ChainEvent struct { - Block *types.Block - Hash common.Hash - Logs []*types.Log + Header *types.Header } -type ChainSideEvent struct { - Block *types.Block +type ChainHeadEvent struct { + Header *types.Header } - -type ChainHeadEvent struct{ Block *types.Block } diff --git a/core/forkid/forkid_test.go b/core/forkid/forkid_test.go index ec6c27b48d..de589c0d44 100644 --- a/core/forkid/forkid_test.go +++ b/core/forkid/forkid_test.go @@ -378,26 +378,25 @@ func TestTimeBasedForkInGenesis(t *testing.T) { forkidHash = checksumToBytes(crc32.ChecksumIEEE(genesis.Hash().Bytes())) config = func(shanghai, cancun uint64) *params.ChainConfig { return ¶ms.ChainConfig{ - ChainID: big.NewInt(1337), - HomesteadBlock: big.NewInt(0), - DAOForkBlock: nil, - DAOForkSupport: true, - EIP150Block: big.NewInt(0), - EIP155Block: big.NewInt(0), - EIP158Block: big.NewInt(0), - ByzantiumBlock: big.NewInt(0), - ConstantinopleBlock: big.NewInt(0), - PetersburgBlock: big.NewInt(0), - IstanbulBlock: big.NewInt(0), - MuirGlacierBlock: big.NewInt(0), - BerlinBlock: big.NewInt(0), - LondonBlock: big.NewInt(0), - TerminalTotalDifficulty: big.NewInt(0), - TerminalTotalDifficultyPassed: true, - MergeNetsplitBlock: big.NewInt(0), - ShanghaiTime: &shanghai, - CancunTime: &cancun, - Ethash: new(params.EthashConfig), + ChainID: big.NewInt(1337), + HomesteadBlock: big.NewInt(0), + DAOForkBlock: nil, + DAOForkSupport: true, + EIP150Block: big.NewInt(0), + EIP155Block: big.NewInt(0), + EIP158Block: big.NewInt(0), + ByzantiumBlock: big.NewInt(0), + ConstantinopleBlock: big.NewInt(0), + PetersburgBlock: big.NewInt(0), + IstanbulBlock: big.NewInt(0), + MuirGlacierBlock: big.NewInt(0), + BerlinBlock: big.NewInt(0), + LondonBlock: big.NewInt(0), + TerminalTotalDifficulty: big.NewInt(0), + MergeNetsplitBlock: big.NewInt(0), + ShanghaiTime: &shanghai, + CancunTime: &cancun, + Ethash: new(params.EthashConfig), } } ) diff --git a/core/genesis.go b/core/genesis.go index eff92084eb..85ef049ba6 100644 --- a/core/genesis.go +++ b/core/genesis.go @@ -472,9 +472,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 0fee874138..3ec87474e5 100644 --- a/core/genesis_test.go +++ b/core/genesis_test.go @@ -257,31 +257,30 @@ func newDbConfig(scheme string) *triedb.Config { func TestVerkleGenesisCommit(t *testing.T) { var verkleTime uint64 = 0 verkleConfig := ¶ms.ChainConfig{ - ChainID: big.NewInt(1), - HomesteadBlock: big.NewInt(0), - DAOForkBlock: nil, - DAOForkSupport: false, - EIP150Block: big.NewInt(0), - EIP155Block: big.NewInt(0), - EIP158Block: big.NewInt(0), - ByzantiumBlock: big.NewInt(0), - ConstantinopleBlock: big.NewInt(0), - PetersburgBlock: big.NewInt(0), - IstanbulBlock: big.NewInt(0), - MuirGlacierBlock: big.NewInt(0), - BerlinBlock: big.NewInt(0), - LondonBlock: big.NewInt(0), - ArrowGlacierBlock: big.NewInt(0), - GrayGlacierBlock: big.NewInt(0), - MergeNetsplitBlock: nil, - ShanghaiTime: &verkleTime, - CancunTime: &verkleTime, - PragueTime: &verkleTime, - VerkleTime: &verkleTime, - TerminalTotalDifficulty: big.NewInt(0), - TerminalTotalDifficultyPassed: true, - Ethash: nil, - Clique: nil, + ChainID: big.NewInt(1), + HomesteadBlock: big.NewInt(0), + DAOForkBlock: nil, + DAOForkSupport: false, + EIP150Block: big.NewInt(0), + EIP155Block: big.NewInt(0), + EIP158Block: big.NewInt(0), + ByzantiumBlock: big.NewInt(0), + ConstantinopleBlock: big.NewInt(0), + PetersburgBlock: big.NewInt(0), + IstanbulBlock: big.NewInt(0), + MuirGlacierBlock: big.NewInt(0), + BerlinBlock: big.NewInt(0), + LondonBlock: big.NewInt(0), + ArrowGlacierBlock: big.NewInt(0), + GrayGlacierBlock: big.NewInt(0), + MergeNetsplitBlock: nil, + ShanghaiTime: &verkleTime, + CancunTime: &verkleTime, + PragueTime: &verkleTime, + VerkleTime: &verkleTime, + TerminalTotalDifficulty: big.NewInt(0), + Ethash: nil, + Clique: nil, } genesis := &Genesis{ @@ -294,7 +293,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/accessors_chain_test.go b/core/rawdb/accessors_chain_test.go index 0b9dbe1335..0e986f66e0 100644 --- a/core/rawdb/accessors_chain_test.go +++ b/core/rawdb/accessors_chain_test.go @@ -649,11 +649,15 @@ func makeTestBlocks(nblock int, txsPerBlock int) []*types.Block { // makeTestReceipts creates fake receipts for the ancient write benchmark. func makeTestReceipts(n int, nPerBlock int) []types.Receipts { receipts := make([]*types.Receipt, nPerBlock) + var logs []*types.Log + for i := 0; i < 5; i++ { + logs = append(logs, new(types.Log)) + } for i := 0; i < len(receipts); i++ { receipts[i] = &types.Receipt{ Status: types.ReceiptStatusSuccessful, CumulativeGasUsed: 0x888888888, - Logs: make([]*types.Log, 5), + Logs: logs, } } allReceipts := make([]types.Receipts, n) diff --git a/core/rawdb/accessors_indexes_test.go b/core/rawdb/accessors_indexes_test.go index 78dba000fc..1bee455503 100644 --- a/core/rawdb/accessors_indexes_test.go +++ b/core/rawdb/accessors_indexes_test.go @@ -35,17 +35,17 @@ var newTestHasher = blocktest.NewHasher func TestLookupStorage(t *testing.T) { tests := []struct { name string - writeTxLookupEntriesByBlock func(ethdb.Writer, *types.Block) + writeTxLookupEntriesByBlock func(ethdb.KeyValueWriter, *types.Block) }{ { "DatabaseV6", - func(db ethdb.Writer, block *types.Block) { + func(db ethdb.KeyValueWriter, block *types.Block) { WriteTxLookupEntriesByBlock(db, block) }, }, { "DatabaseV4-V5", - func(db ethdb.Writer, block *types.Block) { + func(db ethdb.KeyValueWriter, block *types.Block) { for _, tx := range block.Transactions() { db.Put(txLookupKey(tx.Hash()), block.Hash().Bytes()) } @@ -53,7 +53,7 @@ func TestLookupStorage(t *testing.T) { }, { "DatabaseV3", - func(db ethdb.Writer, block *types.Block) { + func(db ethdb.KeyValueWriter, block *types.Block) { for index, tx := range block.Transactions() { entry := LegacyTxLookupEntry{ BlockHash: block.Hash(), diff --git a/core/rawdb/database.go b/core/rawdb/database.go index 13233406fe..53418d1646 100644 --- a/core/rawdb/database.go +++ b/core/rawdb/database.go @@ -27,9 +27,7 @@ import ( "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/ethdb" - "github.com/ethereum/go-ethereum/ethdb/leveldb" "github.com/ethereum/go-ethereum/ethdb/memorydb" - "github.com/ethereum/go-ethereum/ethdb/pebble" "github.com/ethereum/go-ethereum/log" "github.com/olekukonko/tablewriter" ) @@ -299,37 +297,9 @@ func NewMemoryDatabase() ethdb.Database { return NewDatabase(memorydb.New()) } -// NewMemoryDatabaseWithCap creates an ephemeral in-memory key-value database -// with an initial starting capacity, but without a freezer moving immutable -// chain segments into cold storage. -func NewMemoryDatabaseWithCap(size int) ethdb.Database { - return NewDatabase(memorydb.NewWithCap(size)) -} - -// NewLevelDBDatabase creates a persistent key-value database without a freezer -// moving immutable chain segments into cold storage. -func NewLevelDBDatabase(file string, cache int, handles int, namespace string, readonly bool) (ethdb.Database, error) { - db, err := leveldb.New(file, cache, handles, namespace, readonly) - if err != nil { - return nil, err - } - log.Info("Using LevelDB as the backing database") - return NewDatabase(db), nil -} - -// NewPebbleDBDatabase creates a persistent key-value database without a freezer -// moving immutable chain segments into cold storage. -func NewPebbleDBDatabase(file string, cache int, handles int, namespace string, readonly, ephemeral bool) (ethdb.Database, error) { - db, err := pebble.New(file, cache, handles, namespace, readonly, ephemeral) - if err != nil { - return nil, err - } - return NewDatabase(db), nil -} - const ( - dbPebble = "pebble" - dbLeveldb = "leveldb" + DBPebble = "pebble" + DBLeveldb = "leveldb" ) // PreexistingDatabase checks the given data directory whether a database is already @@ -343,75 +313,9 @@ func PreexistingDatabase(path string) string { if err != nil { panic(err) // only possible if the pattern is malformed } - return dbPebble + return DBPebble } - return dbLeveldb -} - -// OpenOptions contains the options to apply when opening a database. -// OBS: If AncientsDirectory is empty, it indicates that no freezer is to be used. -type OpenOptions struct { - Type string // "leveldb" | "pebble" - Directory string // the datadir - AncientsDirectory string // the ancients-dir - Namespace string // the namespace for database relevant metrics - Cache int // the capacity(in megabytes) of the data caching - Handles int // number of files to be open simultaneously - ReadOnly bool - // Ephemeral means that filesystem sync operations should be avoided: data integrity in the face of - // a crash is not important. This option should typically be used in tests. - Ephemeral bool -} - -// openKeyValueDatabase opens a disk-based key-value database, e.g. leveldb or pebble. -// -// type == null type != null -// +---------------------------------------- -// db is non-existent | pebble default | specified type -// db is existent | from db | specified type (if compatible) -func openKeyValueDatabase(o OpenOptions) (ethdb.Database, error) { - // Reject any unsupported database type - if len(o.Type) != 0 && o.Type != dbLeveldb && o.Type != dbPebble { - return nil, fmt.Errorf("unknown db.engine %v", o.Type) - } - // Retrieve any pre-existing database's type and use that or the requested one - // as long as there's no conflict between the two types - existingDb := PreexistingDatabase(o.Directory) - if len(existingDb) != 0 && len(o.Type) != 0 && o.Type != existingDb { - return nil, fmt.Errorf("db.engine choice was %v but found pre-existing %v database in specified data directory", o.Type, existingDb) - } - if o.Type == dbPebble || existingDb == dbPebble { - log.Info("Using pebble as the backing database") - return NewPebbleDBDatabase(o.Directory, o.Cache, o.Handles, o.Namespace, o.ReadOnly, o.Ephemeral) - } - if o.Type == dbLeveldb || existingDb == dbLeveldb { - log.Info("Using leveldb as the backing database") - return NewLevelDBDatabase(o.Directory, o.Cache, o.Handles, o.Namespace, o.ReadOnly) - } - // No pre-existing database, no user-requested one either. Default to Pebble. - log.Info("Defaulting to pebble as the backing database") - return NewPebbleDBDatabase(o.Directory, o.Cache, o.Handles, o.Namespace, o.ReadOnly, o.Ephemeral) -} - -// Open opens both a disk-based key-value database such as leveldb or pebble, but also -// integrates it with a freezer database -- if the AncientDir option has been -// set on the provided OpenOptions. -// The passed o.AncientDir indicates the path of root ancient directory where -// the chain freezer can be opened. -func Open(o OpenOptions) (ethdb.Database, error) { - kvdb, err := openKeyValueDatabase(o) - if err != nil { - return nil, err - } - if len(o.AncientsDirectory) == 0 { - return kvdb, nil - } - frdb, err := NewDatabaseWithFreezer(kvdb, o.AncientsDirectory, o.Namespace, o.ReadOnly) - if err != nil { - kvdb.Close() - return nil, err - } - return frdb, nil + return DBLeveldb } type counter uint64 diff --git a/core/rawdb/freezer.go b/core/rawdb/freezer.go index 1b8df958d1..d6370cee33 100644 --- a/core/rawdb/freezer.go +++ b/core/rawdb/freezer.go @@ -58,8 +58,9 @@ const freezerTableSize = 2 * 1000 * 1000 * 1000 // - The append-only nature ensures that disk writes are minimized. // - The in-order data ensures that disk reads are always optimized. type Freezer struct { - frozen atomic.Uint64 // Number of items already frozen - tail atomic.Uint64 // Number of the first stored item in the freezer + datadir string + frozen atomic.Uint64 // Number of items already frozen + tail atomic.Uint64 // Number of the first stored item in the freezer // This lock synchronizes writers and the truncate operation, as well as // the "atomic" (batched) read operations. @@ -109,6 +110,7 @@ func NewFreezer(datadir string, namespace string, readonly bool, maxTableSize ui } // Open all the supported data tables freezer := &Freezer{ + datadir: datadir, readonly: readonly, tables: make(map[string]*freezerTable), instanceLock: lock, @@ -172,6 +174,11 @@ func (f *Freezer) Close() error { return nil } +// AncientDatadir returns the path of the ancient store. +func (f *Freezer) AncientDatadir() (string, error) { + return f.datadir, nil +} + // HasAncient returns an indicator whether the specified ancient data exists // in the freezer. func (f *Freezer) HasAncient(kind string, number uint64) (bool, error) { diff --git a/core/rawdb/freezer_batch.go b/core/rawdb/freezer_batch.go index 4e1b38df29..013d0b9d13 100644 --- a/core/rawdb/freezer_batch.go +++ b/core/rawdb/freezer_batch.go @@ -18,8 +18,8 @@ package rawdb import ( "fmt" + "math" - "github.com/ethereum/go-ethereum/common/math" "github.com/ethereum/go-ethereum/rlp" "github.com/golang/snappy" ) diff --git a/core/rawdb/freezer_memory.go b/core/rawdb/freezer_memory.go index cd39101514..2d3dbb07dd 100644 --- a/core/rawdb/freezer_memory.go +++ b/core/rawdb/freezer_memory.go @@ -19,10 +19,10 @@ package rawdb import ( "errors" "fmt" + "math" "sync" "github.com/ethereum/go-ethereum/common" - "github.com/ethereum/go-ethereum/common/math" "github.com/ethereum/go-ethereum/ethdb" "github.com/ethereum/go-ethereum/log" "github.com/ethereum/go-ethereum/rlp" @@ -419,3 +419,9 @@ func (f *MemoryFreezer) Reset() error { f.items, f.tail = 0, 0 return nil } + +// AncientDatadir returns the path of the ancient store. +// Since the memory freezer is ephemeral, an empty string is returned. +func (f *MemoryFreezer) AncientDatadir() (string, error) { + return "", nil +} diff --git a/core/rawdb/freezer_resettable.go b/core/rawdb/freezer_resettable.go index b147995066..7c77a06efc 100644 --- a/core/rawdb/freezer_resettable.go +++ b/core/rawdb/freezer_resettable.go @@ -202,6 +202,14 @@ func (f *resettableFreezer) Sync() error { return f.freezer.Sync() } +// AncientDatadir returns the path of the ancient store. +func (f *resettableFreezer) AncientDatadir() (string, error) { + f.lock.RLock() + defer f.lock.RUnlock() + + return f.freezer.AncientDatadir() +} + // cleanup removes the directory located in the specified path // has the name with deletion marker suffix. func cleanup(path string) error { diff --git a/core/rawdb/freezer_table.go b/core/rawdb/freezer_table.go index a8ed17b371..38c47dc223 100644 --- a/core/rawdb/freezer_table.go +++ b/core/rawdb/freezer_table.go @@ -113,10 +113,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 @@ -124,13 +124,13 @@ 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) } // 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/rawdb/table.go b/core/rawdb/table.go index bc1d354d10..1a9060b636 100644 --- a/core/rawdb/table.go +++ b/core/rawdb/table.go @@ -129,6 +129,12 @@ func (t *table) Delete(key []byte) error { return t.db.Delete(append([]byte(t.prefix), key...)) } +// DeleteRange deletes all of the keys (and values) in the range [start,end) +// (inclusive on start, exclusive on end). +func (t *table) DeleteRange(start, end []byte) error { + return t.db.DeleteRange(append([]byte(t.prefix), start...), append([]byte(t.prefix), end...)) +} + // NewIterator creates a binary-alphabetical iterator over a subset // of database content with a particular key prefix, starting at a particular // initial key (or after, if it does not exist). diff --git a/core/state/access_events.go b/core/state/access_events.go index 7f67df64eb..b745c383b1 100644 --- a/core/state/access_events.go +++ b/core/state/access_events.go @@ -117,7 +117,7 @@ func (ae *AccessEvents) ValueTransferGas(callerAddr, targetAddr common.Address) return gas } -// ContractCreateCPreheck charges access costs before +// ContractCreatePreCheckGas charges access costs before // a contract creation is initiated. It is just reads, because the // address collision is done before the transfer, and so no write // are guaranteed to happen at this point. diff --git a/core/state/access_list.go b/core/state/access_list.go index 90e5590748..a58c2b20ea 100644 --- a/core/state/access_list.go +++ b/core/state/access_list.go @@ -139,10 +139,7 @@ func (al *accessList) Equal(other *accessList) bool { if !maps.Equal(al.addresses, other.addresses) { return false } - return slices.EqualFunc(al.slots, other.slots, - func(m map[common.Hash]struct{}, m2 map[common.Hash]struct{}) bool { - return maps.Equal(m, m2) - }) + return slices.EqualFunc(al.slots, other.slots, maps.Equal) } // PrettyPrint prints the contents of the access list in a human-readable form diff --git a/core/state/database.go b/core/state/database.go index de61dee036..faf4954650 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 @@ -180,15 +173,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 { - sr, err := newStateReader(stateRoot, db.snap) + // 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, 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, sr) // snap reader is optional + readers = append(readers, newFlatReader(reader)) // state reader is optional } } // Set up the trie reader, which is expected to always be available @@ -199,7 +201,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. @@ -229,45 +235,20 @@ func (db *CachingDB) OpenStorageTrie(stateRoot common.Hash, address common.Addre return tr, nil } -// 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/journal.go b/core/state/journal.go index a2fea6b6ec..13332dbd79 100644 --- a/core/state/journal.go +++ b/core/state/journal.go @@ -23,7 +23,7 @@ import ( "sort" "github.com/ethereum/go-ethereum/common" - "github.com/ethereum/go-ethereum/core/types" + "github.com/ethereum/go-ethereum/crypto" "github.com/holiman/uint256" ) @@ -192,8 +192,11 @@ func (j *journal) balanceChange(addr common.Address, previous *uint256.Int) { }) } -func (j *journal) setCode(address common.Address) { - j.append(codeChange{account: address}) +func (j *journal) setCode(address common.Address, prevCode []byte) { + j.append(codeChange{ + account: address, + prevCode: prevCode, + }) } func (j *journal) nonceChange(address common.Address, prev uint64) { @@ -256,7 +259,8 @@ type ( origvalue common.Hash } codeChange struct { - account common.Address + account common.Address + prevCode []byte } // Changes to other state values. @@ -377,7 +381,7 @@ func (ch nonceChange) copy() journalEntry { } func (ch codeChange) revert(s *StateDB) { - s.getStateObject(ch.account).setCode(types.EmptyCodeHash, nil) + s.getStateObject(ch.account).setCode(crypto.Keccak256Hash(ch.prevCode), ch.prevCode) } func (ch codeChange) dirtied() *common.Address { @@ -385,7 +389,10 @@ func (ch codeChange) dirtied() *common.Address { } func (ch codeChange) copy() journalEntry { - return codeChange{account: ch.account} + return codeChange{ + account: ch.account, + prevCode: ch.prevCode, + } } func (ch storageChange) revert(s *StateDB) { diff --git a/core/state/reader.go b/core/state/reader.go index 6bddefc2a7..a0f15dfcc8 100644 --- a/core/state/reader.go +++ b/core/state/reader.go @@ -18,21 +18,40 @@ package state import ( "errors" - "maps" "github.com/ethereum/go-ethereum/common" - "github.com/ethereum/go-ethereum/core/state/snapshot" + "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" "github.com/ethereum/go-ethereum/triedb" + "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,49 +66,96 @@ 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) - - // Copy returns a deep-copied state reader. - Copy() Reader } -// stateReader is a wrapper over the state snapshot and implements the Reader -// interface. It provides an efficient way to access flat state. -type stateReader struct { - snap snapshot.Snapshot - buff crypto.KeccakState +// Reader defines the interface for accessing accounts, storage slots and contract +// code associated with a specific state. +type Reader interface { + ContractCodeReader + StateReader } -// newStateReader constructs a flat state reader with on the specified state root. -func newStateReader(root common.Hash, snaps *snapshot.Tree) (*stateReader, error) { - snap := snaps.Snapshot(root) - if snap == nil { - return nil, errors.New("snapshot is not available") +// 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, } - return &stateReader{ - snap: snap, - buff: crypto.NewKeccakState(), - }, nil } -// Account implements Reader, retrieving the account specified by the address. +// 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 +} + +// 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 +} + +// flatReader wraps a database state reader. +type flatReader struct { + reader database.StateReader + buff crypto.KeccakState +} + +// 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 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) { - ret, err := r.snap.Account(crypto.HashData(r.buff, addr.Bytes())) +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 } - if ret == nil { + if account == nil { return nil, nil } acct := &types.StateAccount{ - Nonce: ret.Nonce, - Balance: ret.Balance, - CodeHash: ret.CodeHash, - Root: common.BytesToHash(ret.Root), + Nonce: account.Nonce, + Balance: account.Balance, + CodeHash: account.CodeHash, + Root: common.BytesToHash(account.Root), } if len(acct.CodeHash) == 0 { acct.CodeHash = types.EmptyCodeHash.Bytes() @@ -100,17 +166,17 @@ 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.snap.Storage(addrHash, slotHash) + ret, err := r.reader.Storage(addrHash, slotHash) if err != nil { return common.Hash{}, err } @@ -128,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{ - snap: r.snap, - 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 @@ -172,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. @@ -189,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 @@ -232,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) @@ -285,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) @@ -303,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()) - } - return &multiReader{readers: readers} +// 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, + } } diff --git a/core/state/snapshot/context.go b/core/state/snapshot/context.go index 8a19960501..3d293d27f4 100644 --- a/core/state/snapshot/context.go +++ b/core/state/snapshot/context.go @@ -20,10 +20,10 @@ import ( "bytes" "encoding/binary" "errors" + "math" "time" "github.com/ethereum/go-ethereum/common" - "github.com/ethereum/go-ethereum/common/math" "github.com/ethereum/go-ethereum/core/rawdb" "github.com/ethereum/go-ethereum/ethdb" "github.com/ethereum/go-ethereum/ethdb/memorydb" diff --git a/core/state/snapshot/conversion.go b/core/state/snapshot/conversion.go index 8a0fd1989a..4b0774f2ae 100644 --- a/core/state/snapshot/conversion.go +++ b/core/state/snapshot/conversion.go @@ -50,16 +50,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 779c1ea98c..dce4f79a11 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), @@ -161,10 +139,7 @@ func newDiffLayer(parent snapshot, root common.Hash, destructs map[common.Hash]s panic("unknown parent type") } // 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))) @@ -179,7 +154,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 } @@ -204,10 +178,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)) } @@ -274,11 +244,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 } @@ -310,18 +277,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) @@ -345,11 +308,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 } @@ -391,14 +351,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) @@ -410,8 +362,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 @@ -436,12 +388,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 } @@ -453,17 +399,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), @@ -489,15 +431,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 @@ -512,18 +446,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() @@ -531,13 +464,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 76928edf07..202e6c70ed 100644 --- a/core/state/snapshot/disklayer.go +++ b/core/state/snapshot/disklayer.go @@ -180,8 +180,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.go b/core/state/snapshot/generate.go index 6d9e163075..01fb55ea4c 100644 --- a/core/state/snapshot/generate.go +++ b/core/state/snapshot/generate.go @@ -31,7 +31,6 @@ import ( "github.com/ethereum/go-ethereum/log" "github.com/ethereum/go-ethereum/rlp" "github.com/ethereum/go-ethereum/trie" - "github.com/ethereum/go-ethereum/trie/trienode" "github.com/ethereum/go-ethereum/triedb" ) @@ -353,20 +352,14 @@ func (dl *diskLayer) generateRange(ctx *generatorContext, trieId *trie.ID, prefi // main account trie as a primary lookup when resolving hashes var resolver trie.NodeResolver if len(result.keys) > 0 { - mdb := rawdb.NewMemoryDatabase() - tdb := triedb.NewDatabase(mdb, triedb.HashDefaults) - defer tdb.Close() - snapTrie := trie.NewEmpty(tdb) + tr := trie.NewEmpty(nil) for i, key := range result.keys { - snapTrie.Update(key, result.vals[i]) - } - root, nodes := snapTrie.Commit(false) - if nodes != nil { - tdb.Update(root, types.EmptyRootHash, 0, trienode.NewWithNodeSet(nodes), nil) - tdb.Commit(root, false) + tr.Update(key, result.vals[i]) } + _, nodes := tr.Commit(false) + hashSet := nodes.HashSet() resolver = func(owner common.Hash, path []byte, hash common.Hash) []byte { - return rawdb.ReadTrieNode(mdb, owner, path, hash, tdb.Scheme()) + return hashSet[hash] } } // Construct the trie for state iteration, reuse the trie diff --git a/core/state/snapshot/generate_test.go b/core/state/snapshot/generate_test.go index 891111973a..661610840a 100644 --- a/core/state/snapshot/generate_test.go +++ b/core/state/snapshot/generate_test.go @@ -57,14 +57,14 @@ func testGeneration(t *testing.T, scheme string) { // a fake one manually. We're going with a small account trie of 3 accounts, // two of which also has the same 3-slot storage trie attached. var helper = newHelper(scheme) - stRoot := helper.makeStorageTrie(common.Hash{}, []string{"key-1", "key-2", "key-3"}, []string{"val-1", "val-2", "val-3"}, false) + stRoot := helper.makeStorageTrie("", []string{"key-1", "key-2", "key-3"}, []string{"val-1", "val-2", "val-3"}, false) helper.addTrieAccount("acc-1", &types.StateAccount{Balance: uint256.NewInt(1), Root: stRoot, CodeHash: types.EmptyCodeHash.Bytes()}) helper.addTrieAccount("acc-2", &types.StateAccount{Balance: uint256.NewInt(2), Root: types.EmptyRootHash, CodeHash: types.EmptyCodeHash.Bytes()}) helper.addTrieAccount("acc-3", &types.StateAccount{Balance: uint256.NewInt(3), Root: stRoot, CodeHash: types.EmptyCodeHash.Bytes()}) - helper.makeStorageTrie(hashData([]byte("acc-1")), []string{"key-1", "key-2", "key-3"}, []string{"val-1", "val-2", "val-3"}, true) - helper.makeStorageTrie(hashData([]byte("acc-3")), []string{"key-1", "key-2", "key-3"}, []string{"val-1", "val-2", "val-3"}, true) + helper.makeStorageTrie("acc-1", []string{"key-1", "key-2", "key-3"}, []string{"val-1", "val-2", "val-3"}, true) + helper.makeStorageTrie("acc-3", []string{"key-1", "key-2", "key-3"}, []string{"val-1", "val-2", "val-3"}, true) root, snap := helper.CommitAndGenerate() if have, want := root, common.HexToHash("0xe3712f1a226f3782caca78ca770ccc19ee000552813a9f59d479f8611db9b1fd"); have != want { @@ -97,7 +97,7 @@ func testGenerateExistentState(t *testing.T, scheme string) { // two of which also has the same 3-slot storage trie attached. var helper = newHelper(scheme) - stRoot := helper.makeStorageTrie(hashData([]byte("acc-1")), []string{"key-1", "key-2", "key-3"}, []string{"val-1", "val-2", "val-3"}, true) + stRoot := helper.makeStorageTrie("acc-1", []string{"key-1", "key-2", "key-3"}, []string{"val-1", "val-2", "val-3"}, true) helper.addTrieAccount("acc-1", &types.StateAccount{Balance: uint256.NewInt(1), Root: stRoot, CodeHash: types.EmptyCodeHash.Bytes()}) helper.addSnapAccount("acc-1", &types.StateAccount{Balance: uint256.NewInt(1), Root: stRoot, CodeHash: types.EmptyCodeHash.Bytes()}) helper.addSnapStorage("acc-1", []string{"key-1", "key-2", "key-3"}, []string{"val-1", "val-2", "val-3"}) @@ -105,7 +105,7 @@ func testGenerateExistentState(t *testing.T, scheme string) { helper.addTrieAccount("acc-2", &types.StateAccount{Balance: uint256.NewInt(2), Root: types.EmptyRootHash, CodeHash: types.EmptyCodeHash.Bytes()}) helper.addSnapAccount("acc-2", &types.StateAccount{Balance: uint256.NewInt(2), Root: types.EmptyRootHash, CodeHash: types.EmptyCodeHash.Bytes()}) - stRoot = helper.makeStorageTrie(hashData([]byte("acc-3")), []string{"key-1", "key-2", "key-3"}, []string{"val-1", "val-2", "val-3"}, true) + stRoot = helper.makeStorageTrie("acc-3", []string{"key-1", "key-2", "key-3"}, []string{"val-1", "val-2", "val-3"}, true) helper.addTrieAccount("acc-3", &types.StateAccount{Balance: uint256.NewInt(3), Root: stRoot, CodeHash: types.EmptyCodeHash.Bytes()}) helper.addSnapAccount("acc-3", &types.StateAccount{Balance: uint256.NewInt(3), Root: stRoot, CodeHash: types.EmptyCodeHash.Bytes()}) helper.addSnapStorage("acc-3", []string{"key-1", "key-2", "key-3"}, []string{"val-1", "val-2", "val-3"}) @@ -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) @@ -159,6 +159,7 @@ type testHelper struct { triedb *triedb.Database accTrie *trie.StateTrie nodes *trienode.MergedNodeSet + states *triedb.StateSet } func newHelper(scheme string) *testHelper { @@ -169,19 +170,24 @@ func newHelper(scheme string) *testHelper { } else { config.HashDB = &hashdb.Config{} // disable caching } - triedb := triedb.NewDatabase(diskdb, config) - accTrie, _ := trie.NewStateTrie(trie.StateTrieID(types.EmptyRootHash), triedb) + db := triedb.NewDatabase(diskdb, config) + accTrie, _ := trie.NewStateTrie(trie.StateTrieID(types.EmptyRootHash), db) return &testHelper{ diskdb: diskdb, - triedb: triedb, + triedb: db, accTrie: accTrie, nodes: trienode.NewMergedNodeSet(), + states: triedb.NewStateSet(), } } func (t *testHelper) addTrieAccount(acckey string, acc *types.StateAccount) { val, _ := rlp.EncodeToBytes(acc) t.accTrie.MustUpdate([]byte(acckey), val) + + accHash := hashData([]byte(acckey)) + t.states.Accounts[accHash] = val + t.states.AccountsOrigin[common.BytesToAddress([]byte(acckey))] = nil } func (t *testHelper) addSnapAccount(acckey string, acc *types.StateAccount) { @@ -201,11 +207,21 @@ func (t *testHelper) addSnapStorage(accKey string, keys []string, vals []string) } } -func (t *testHelper) makeStorageTrie(owner common.Hash, keys []string, vals []string, commit bool) common.Hash { +func (t *testHelper) makeStorageTrie(accKey string, keys []string, vals []string, commit bool) common.Hash { + owner := hashData([]byte(accKey)) + addr := common.BytesToAddress([]byte(accKey)) id := trie.StorageTrieID(types.EmptyRootHash, owner, types.EmptyRootHash) stTrie, _ := trie.NewStateTrie(id, t.triedb) for i, k := range keys { stTrie.MustUpdate([]byte(k), []byte(vals[i])) + if t.states.Storages[owner] == nil { + t.states.Storages[owner] = make(map[common.Hash][]byte) + } + if t.states.StoragesOrigin[addr] == nil { + t.states.StoragesOrigin[addr] = make(map[common.Hash][]byte) + } + t.states.Storages[owner][hashData([]byte(k))] = []byte(vals[i]) + t.states.StoragesOrigin[addr][hashData([]byte(k))] = nil } if !commit { return stTrie.Hash() @@ -222,7 +238,7 @@ func (t *testHelper) Commit() common.Hash { if nodes != nil { t.nodes.Merge(nodes) } - t.triedb.Update(root, types.EmptyRootHash, 0, t.nodes, nil) + t.triedb.Update(root, types.EmptyRootHash, 0, t.nodes, t.states) t.triedb.Commit(root, false) return root } @@ -264,23 +280,23 @@ func testGenerateExistentStateWithWrongStorage(t *testing.T, scheme string) { helper.addSnapStorage("acc-1", []string{"key-1", "key-2", "key-3"}, []string{"val-1", "val-2", "val-3"}) // Account two, non empty root but empty database - stRoot := helper.makeStorageTrie(hashData([]byte("acc-2")), []string{"key-1", "key-2", "key-3"}, []string{"val-1", "val-2", "val-3"}, true) + stRoot := helper.makeStorageTrie("acc-2", []string{"key-1", "key-2", "key-3"}, []string{"val-1", "val-2", "val-3"}, true) helper.addAccount("acc-2", &types.StateAccount{Balance: uint256.NewInt(1), Root: stRoot, CodeHash: types.EmptyCodeHash.Bytes()}) // Miss slots { // Account three, non empty root but misses slots in the beginning - helper.makeStorageTrie(hashData([]byte("acc-3")), []string{"key-1", "key-2", "key-3"}, []string{"val-1", "val-2", "val-3"}, true) + helper.makeStorageTrie("acc-3", []string{"key-1", "key-2", "key-3"}, []string{"val-1", "val-2", "val-3"}, true) helper.addAccount("acc-3", &types.StateAccount{Balance: uint256.NewInt(1), Root: stRoot, CodeHash: types.EmptyCodeHash.Bytes()}) helper.addSnapStorage("acc-3", []string{"key-2", "key-3"}, []string{"val-2", "val-3"}) // Account four, non empty root but misses slots in the middle - helper.makeStorageTrie(hashData([]byte("acc-4")), []string{"key-1", "key-2", "key-3"}, []string{"val-1", "val-2", "val-3"}, true) + helper.makeStorageTrie("acc-4", []string{"key-1", "key-2", "key-3"}, []string{"val-1", "val-2", "val-3"}, true) helper.addAccount("acc-4", &types.StateAccount{Balance: uint256.NewInt(1), Root: stRoot, CodeHash: types.EmptyCodeHash.Bytes()}) helper.addSnapStorage("acc-4", []string{"key-1", "key-3"}, []string{"val-1", "val-3"}) // Account five, non empty root but misses slots in the end - helper.makeStorageTrie(hashData([]byte("acc-5")), []string{"key-1", "key-2", "key-3"}, []string{"val-1", "val-2", "val-3"}, true) + helper.makeStorageTrie("acc-5", []string{"key-1", "key-2", "key-3"}, []string{"val-1", "val-2", "val-3"}, true) helper.addAccount("acc-5", &types.StateAccount{Balance: uint256.NewInt(1), Root: stRoot, CodeHash: types.EmptyCodeHash.Bytes()}) helper.addSnapStorage("acc-5", []string{"key-1", "key-2"}, []string{"val-1", "val-2"}) } @@ -288,22 +304,22 @@ func testGenerateExistentStateWithWrongStorage(t *testing.T, scheme string) { // Wrong storage slots { // Account six, non empty root but wrong slots in the beginning - helper.makeStorageTrie(hashData([]byte("acc-6")), []string{"key-1", "key-2", "key-3"}, []string{"val-1", "val-2", "val-3"}, true) + helper.makeStorageTrie("acc-6", []string{"key-1", "key-2", "key-3"}, []string{"val-1", "val-2", "val-3"}, true) helper.addAccount("acc-6", &types.StateAccount{Balance: uint256.NewInt(1), Root: stRoot, CodeHash: types.EmptyCodeHash.Bytes()}) helper.addSnapStorage("acc-6", []string{"key-1", "key-2", "key-3"}, []string{"badval-1", "val-2", "val-3"}) // Account seven, non empty root but wrong slots in the middle - helper.makeStorageTrie(hashData([]byte("acc-7")), []string{"key-1", "key-2", "key-3"}, []string{"val-1", "val-2", "val-3"}, true) + helper.makeStorageTrie("acc-7", []string{"key-1", "key-2", "key-3"}, []string{"val-1", "val-2", "val-3"}, true) helper.addAccount("acc-7", &types.StateAccount{Balance: uint256.NewInt(1), Root: stRoot, CodeHash: types.EmptyCodeHash.Bytes()}) helper.addSnapStorage("acc-7", []string{"key-1", "key-2", "key-3"}, []string{"val-1", "badval-2", "val-3"}) // Account eight, non empty root but wrong slots in the end - helper.makeStorageTrie(hashData([]byte("acc-8")), []string{"key-1", "key-2", "key-3"}, []string{"val-1", "val-2", "val-3"}, true) + helper.makeStorageTrie("acc-8", []string{"key-1", "key-2", "key-3"}, []string{"val-1", "val-2", "val-3"}, true) helper.addAccount("acc-8", &types.StateAccount{Balance: uint256.NewInt(1), Root: stRoot, CodeHash: types.EmptyCodeHash.Bytes()}) helper.addSnapStorage("acc-8", []string{"key-1", "key-2", "key-3"}, []string{"val-1", "val-2", "badval-3"}) // Account 9, non empty root but rotated slots - helper.makeStorageTrie(hashData([]byte("acc-9")), []string{"key-1", "key-2", "key-3"}, []string{"val-1", "val-2", "val-3"}, true) + helper.makeStorageTrie("acc-9", []string{"key-1", "key-2", "key-3"}, []string{"val-1", "val-2", "val-3"}, true) helper.addAccount("acc-9", &types.StateAccount{Balance: uint256.NewInt(1), Root: stRoot, CodeHash: types.EmptyCodeHash.Bytes()}) helper.addSnapStorage("acc-9", []string{"key-1", "key-2", "key-3"}, []string{"val-1", "val-3", "val-2"}) } @@ -311,17 +327,17 @@ func testGenerateExistentStateWithWrongStorage(t *testing.T, scheme string) { // Extra storage slots { // Account 10, non empty root but extra slots in the beginning - helper.makeStorageTrie(hashData([]byte("acc-10")), []string{"key-1", "key-2", "key-3"}, []string{"val-1", "val-2", "val-3"}, true) + helper.makeStorageTrie("acc-10", []string{"key-1", "key-2", "key-3"}, []string{"val-1", "val-2", "val-3"}, true) helper.addAccount("acc-10", &types.StateAccount{Balance: uint256.NewInt(1), Root: stRoot, CodeHash: types.EmptyCodeHash.Bytes()}) helper.addSnapStorage("acc-10", []string{"key-0", "key-1", "key-2", "key-3"}, []string{"val-0", "val-1", "val-2", "val-3"}) // Account 11, non empty root but extra slots in the middle - helper.makeStorageTrie(hashData([]byte("acc-11")), []string{"key-1", "key-2", "key-3"}, []string{"val-1", "val-2", "val-3"}, true) + helper.makeStorageTrie("acc-11", []string{"key-1", "key-2", "key-3"}, []string{"val-1", "val-2", "val-3"}, true) helper.addAccount("acc-11", &types.StateAccount{Balance: uint256.NewInt(1), Root: stRoot, CodeHash: types.EmptyCodeHash.Bytes()}) helper.addSnapStorage("acc-11", []string{"key-1", "key-2", "key-2-1", "key-3"}, []string{"val-1", "val-2", "val-2-1", "val-3"}) // Account 12, non empty root but extra slots in the end - helper.makeStorageTrie(hashData([]byte("acc-12")), []string{"key-1", "key-2", "key-3"}, []string{"val-1", "val-2", "val-3"}, true) + helper.makeStorageTrie("acc-12", []string{"key-1", "key-2", "key-3"}, []string{"val-1", "val-2", "val-3"}, true) helper.addAccount("acc-12", &types.StateAccount{Balance: uint256.NewInt(1), Root: stRoot, CodeHash: types.EmptyCodeHash.Bytes()}) helper.addSnapStorage("acc-12", []string{"key-1", "key-2", "key-3", "key-4"}, []string{"val-1", "val-2", "val-3", "val-4"}) } @@ -356,11 +372,11 @@ func TestGenerateExistentStateWithWrongAccounts(t *testing.T) { func testGenerateExistentStateWithWrongAccounts(t *testing.T, scheme string) { helper := newHelper(scheme) - helper.makeStorageTrie(hashData([]byte("acc-1")), []string{"key-1", "key-2", "key-3"}, []string{"val-1", "val-2", "val-3"}, true) - helper.makeStorageTrie(hashData([]byte("acc-2")), []string{"key-1", "key-2", "key-3"}, []string{"val-1", "val-2", "val-3"}, true) - helper.makeStorageTrie(hashData([]byte("acc-3")), []string{"key-1", "key-2", "key-3"}, []string{"val-1", "val-2", "val-3"}, true) - helper.makeStorageTrie(hashData([]byte("acc-4")), []string{"key-1", "key-2", "key-3"}, []string{"val-1", "val-2", "val-3"}, true) - stRoot := helper.makeStorageTrie(hashData([]byte("acc-6")), []string{"key-1", "key-2", "key-3"}, []string{"val-1", "val-2", "val-3"}, true) + helper.makeStorageTrie("acc-1", []string{"key-1", "key-2", "key-3"}, []string{"val-1", "val-2", "val-3"}, true) + helper.makeStorageTrie("acc-2", []string{"key-1", "key-2", "key-3"}, []string{"val-1", "val-2", "val-3"}, true) + helper.makeStorageTrie("acc-3", []string{"key-1", "key-2", "key-3"}, []string{"val-1", "val-2", "val-3"}, true) + helper.makeStorageTrie("acc-4", []string{"key-1", "key-2", "key-3"}, []string{"val-1", "val-2", "val-3"}, true) + stRoot := helper.makeStorageTrie("acc-6", []string{"key-1", "key-2", "key-3"}, []string{"val-1", "val-2", "val-3"}, true) // Trie accounts [acc-1, acc-2, acc-3, acc-4, acc-6] // Extra accounts [acc-0, acc-5, acc-7] @@ -463,10 +479,10 @@ func testGenerateMissingStorageTrie(t *testing.T, scheme string) { acc3 = hashData([]byte("acc-3")) helper = newHelper(scheme) ) - stRoot := helper.makeStorageTrie(hashData([]byte("acc-1")), []string{"key-1", "key-2", "key-3"}, []string{"val-1", "val-2", "val-3"}, true) // 0xddefcd9376dd029653ef384bd2f0a126bb755fe84fdcc9e7cf421ba454f2bc67 + stRoot := helper.makeStorageTrie("acc-1", []string{"key-1", "key-2", "key-3"}, []string{"val-1", "val-2", "val-3"}, true) // 0xddefcd9376dd029653ef384bd2f0a126bb755fe84fdcc9e7cf421ba454f2bc67 helper.addTrieAccount("acc-1", &types.StateAccount{Balance: uint256.NewInt(1), Root: stRoot, CodeHash: types.EmptyCodeHash.Bytes()}) // 0x9250573b9c18c664139f3b6a7a8081b7d8f8916a8fcc5d94feec6c29f5fd4e9e helper.addTrieAccount("acc-2", &types.StateAccount{Balance: uint256.NewInt(2), Root: types.EmptyRootHash, CodeHash: types.EmptyCodeHash.Bytes()}) // 0x65145f923027566669a1ae5ccac66f945b55ff6eaeb17d2ea8e048b7d381f2d7 - stRoot = helper.makeStorageTrie(hashData([]byte("acc-3")), []string{"key-1", "key-2", "key-3"}, []string{"val-1", "val-2", "val-3"}, true) + stRoot = helper.makeStorageTrie("acc-3", []string{"key-1", "key-2", "key-3"}, []string{"val-1", "val-2", "val-3"}, true) helper.addTrieAccount("acc-3", &types.StateAccount{Balance: uint256.NewInt(3), Root: stRoot, CodeHash: types.EmptyCodeHash.Bytes()}) // 0x50815097425d000edfc8b3a4a13e175fc2bdcfee8bdfbf2d1ff61041d3c235b2 root := helper.Commit() @@ -503,10 +519,10 @@ func testGenerateCorruptStorageTrie(t *testing.T, scheme string) { // two of which also has the same 3-slot storage trie attached. helper := newHelper(scheme) - stRoot := helper.makeStorageTrie(hashData([]byte("acc-1")), []string{"key-1", "key-2", "key-3"}, []string{"val-1", "val-2", "val-3"}, true) // 0xddefcd9376dd029653ef384bd2f0a126bb755fe84fdcc9e7cf421ba454f2bc67 + stRoot := helper.makeStorageTrie("acc-1", []string{"key-1", "key-2", "key-3"}, []string{"val-1", "val-2", "val-3"}, true) // 0xddefcd9376dd029653ef384bd2f0a126bb755fe84fdcc9e7cf421ba454f2bc67 helper.addTrieAccount("acc-1", &types.StateAccount{Balance: uint256.NewInt(1), Root: stRoot, CodeHash: types.EmptyCodeHash.Bytes()}) // 0x9250573b9c18c664139f3b6a7a8081b7d8f8916a8fcc5d94feec6c29f5fd4e9e helper.addTrieAccount("acc-2", &types.StateAccount{Balance: uint256.NewInt(2), Root: types.EmptyRootHash, CodeHash: types.EmptyCodeHash.Bytes()}) // 0x65145f923027566669a1ae5ccac66f945b55ff6eaeb17d2ea8e048b7d381f2d7 - stRoot = helper.makeStorageTrie(hashData([]byte("acc-3")), []string{"key-1", "key-2", "key-3"}, []string{"val-1", "val-2", "val-3"}, true) + stRoot = helper.makeStorageTrie("acc-3", []string{"key-1", "key-2", "key-3"}, []string{"val-1", "val-2", "val-3"}, true) helper.addTrieAccount("acc-3", &types.StateAccount{Balance: uint256.NewInt(3), Root: stRoot, CodeHash: types.EmptyCodeHash.Bytes()}) // 0x50815097425d000edfc8b3a4a13e175fc2bdcfee8bdfbf2d1ff61041d3c235b2 root := helper.Commit() @@ -542,7 +558,7 @@ func testGenerateWithExtraAccounts(t *testing.T, scheme string) { helper := newHelper(scheme) { // Account one in the trie - stRoot := helper.makeStorageTrie(hashData([]byte("acc-1")), + stRoot := helper.makeStorageTrie("acc-1", []string{"key-1", "key-2", "key-3", "key-4", "key-5"}, []string{"val-1", "val-2", "val-3", "val-4", "val-5"}, true, @@ -562,7 +578,7 @@ func testGenerateWithExtraAccounts(t *testing.T, scheme string) { } { // Account two exists only in the snapshot - stRoot := helper.makeStorageTrie(hashData([]byte("acc-2")), + stRoot := helper.makeStorageTrie("acc-2", []string{"key-1", "key-2", "key-3", "key-4", "key-5"}, []string{"val-1", "val-2", "val-3", "val-4", "val-5"}, true, @@ -618,7 +634,7 @@ func testGenerateWithManyExtraAccounts(t *testing.T, scheme string) { helper := newHelper(scheme) { // Account one in the trie - stRoot := helper.makeStorageTrie(hashData([]byte("acc-1")), + stRoot := helper.makeStorageTrie("acc-1", []string{"key-1", "key-2", "key-3"}, []string{"val-1", "val-2", "val-3"}, true, @@ -763,7 +779,7 @@ func testGenerateFromEmptySnap(t *testing.T, scheme string) { helper := newHelper(scheme) // Add 1K accounts to the trie for i := 0; i < 400; i++ { - stRoot := helper.makeStorageTrie(hashData([]byte(fmt.Sprintf("acc-%d", i))), []string{"key-1", "key-2", "key-3"}, []string{"val-1", "val-2", "val-3"}, true) + stRoot := helper.makeStorageTrie(fmt.Sprintf("acc-%d", i), []string{"key-1", "key-2", "key-3"}, []string{"val-1", "val-2", "val-3"}, true) helper.addTrieAccount(fmt.Sprintf("acc-%d", i), &types.StateAccount{Balance: uint256.NewInt(1), Root: stRoot, CodeHash: types.EmptyCodeHash.Bytes()}) } @@ -806,7 +822,7 @@ func testGenerateWithIncompleteStorage(t *testing.T, scheme string) { // on the sensitive spots at the boundaries for i := 0; i < 8; i++ { accKey := fmt.Sprintf("acc-%d", i) - stRoot := helper.makeStorageTrie(hashData([]byte(accKey)), stKeys, stVals, true) + stRoot := helper.makeStorageTrie(accKey, stKeys, stVals, true) helper.addAccount(accKey, &types.StateAccount{Balance: uint256.NewInt(uint64(i)), Root: stRoot, CodeHash: types.EmptyCodeHash.Bytes()}) var moddedKeys []string var moddedVals []string @@ -903,11 +919,11 @@ func TestGenerateCompleteSnapshotWithDanglingStorage(t *testing.T) { func testGenerateCompleteSnapshotWithDanglingStorage(t *testing.T, scheme string) { var helper = newHelper(scheme) - stRoot := helper.makeStorageTrie(hashData([]byte("acc-1")), []string{"key-1", "key-2", "key-3"}, []string{"val-1", "val-2", "val-3"}, true) + stRoot := helper.makeStorageTrie("acc-1", []string{"key-1", "key-2", "key-3"}, []string{"val-1", "val-2", "val-3"}, true) helper.addAccount("acc-1", &types.StateAccount{Balance: uint256.NewInt(1), Root: stRoot, CodeHash: types.EmptyCodeHash.Bytes()}) helper.addAccount("acc-2", &types.StateAccount{Balance: uint256.NewInt(1), Root: types.EmptyRootHash, CodeHash: types.EmptyCodeHash.Bytes()}) - helper.makeStorageTrie(hashData([]byte("acc-3")), []string{"key-1", "key-2", "key-3"}, []string{"val-1", "val-2", "val-3"}, true) + helper.makeStorageTrie("acc-3", []string{"key-1", "key-2", "key-3"}, []string{"val-1", "val-2", "val-3"}, true) helper.addAccount("acc-3", &types.StateAccount{Balance: uint256.NewInt(1), Root: stRoot, CodeHash: types.EmptyCodeHash.Bytes()}) helper.addSnapStorage("acc-1", []string{"key-1", "key-2", "key-3"}, []string{"val-1", "val-2", "val-3"}) @@ -943,11 +959,11 @@ func TestGenerateBrokenSnapshotWithDanglingStorage(t *testing.T) { func testGenerateBrokenSnapshotWithDanglingStorage(t *testing.T, scheme string) { var helper = newHelper(scheme) - stRoot := helper.makeStorageTrie(hashData([]byte("acc-1")), []string{"key-1", "key-2", "key-3"}, []string{"val-1", "val-2", "val-3"}, true) + stRoot := helper.makeStorageTrie("acc-1", []string{"key-1", "key-2", "key-3"}, []string{"val-1", "val-2", "val-3"}, true) helper.addTrieAccount("acc-1", &types.StateAccount{Balance: uint256.NewInt(1), Root: stRoot, CodeHash: types.EmptyCodeHash.Bytes()}) helper.addTrieAccount("acc-2", &types.StateAccount{Balance: uint256.NewInt(2), Root: types.EmptyRootHash, CodeHash: types.EmptyCodeHash.Bytes()}) - helper.makeStorageTrie(hashData([]byte("acc-3")), []string{"key-1", "key-2", "key-3"}, []string{"val-1", "val-2", "val-3"}, true) + helper.makeStorageTrie("acc-3", []string{"key-1", "key-2", "key-3"}, []string{"val-1", "val-2", "val-3"}, true) helper.addTrieAccount("acc-3", &types.StateAccount{Balance: uint256.NewInt(3), Root: stRoot, CodeHash: types.EmptyCodeHash.Bytes()}) populateDangling(helper.diskdb) 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 edf471213f..254c4b860f 100644 --- a/core/state/snapshot/iterator_binary.go +++ b/core/state/snapshot/iterator_binary.go @@ -39,12 +39,12 @@ type binaryIterator struct { // initBinaryAccountIterator creates a simplistic iterator to step over all the // accounts in a slow, but easily verifiable way. Note this function is used for // initialization, use `newBinaryAccountIterator` as the API. -func (dl *diffLayer) initBinaryAccountIterator() Iterator { +func (dl *diffLayer) initBinaryAccountIterator(seek common.Hash) Iterator { parent, ok := dl.parent.(*diffLayer) if !ok { l := &binaryIterator{ - a: dl.AccountIterator(common.Hash{}), - b: dl.Parent().AccountIterator(common.Hash{}), + a: dl.AccountIterator(seek), + b: dl.Parent().AccountIterator(seek), accountIterator: true, } l.aDone = !l.a.Next() @@ -52,8 +52,8 @@ func (dl *diffLayer) initBinaryAccountIterator() Iterator { return l } l := &binaryIterator{ - a: dl.AccountIterator(common.Hash{}), - b: parent.initBinaryAccountIterator(), + a: dl.AccountIterator(seek), + b: parent.initBinaryAccountIterator(seek), accountIterator: true, } l.aDone = !l.a.Next() @@ -64,48 +64,21 @@ func (dl *diffLayer) initBinaryAccountIterator() Iterator { // initBinaryStorageIterator creates a simplistic iterator to step over all the // storage slots in a slow, but easily verifiable way. Note this function is used // for initialization, use `newBinaryStorageIterator` as the API. -func (dl *diffLayer) initBinaryStorageIterator(account 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, common.Hash{}) - 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, common.Hash{}) 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, common.Hash{}) - if destructed { - l := &binaryIterator{ - a: a, - account: account, - } - l.aDone = !l.a.Next() - l.bDone = true - return l - } l := &binaryIterator{ - a: a, - b: parent.initBinaryStorageIterator(account), + a: dl.StorageIterator(account, seek), + b: parent.initBinaryStorageIterator(account, seek), account: account, } l.aDone = !l.a.Next() @@ -117,33 +90,50 @@ func (dl *diffLayer) initBinaryStorageIterator(account common.Hash) Iterator { // or an error if iteration failed for some reason (e.g. root being iterated // becomes stale and garbage collected). func (it *binaryIterator) Next() bool { + for { + if !it.next() { + return false + } + if len(it.Account()) != 0 || len(it.Slot()) != 0 { + return true + } + // it.fail might be set if error occurs by calling + // it.Account() or it.Slot(), stop iteration if so. + if it.fail != nil { + return false + } + } +} + +func (it *binaryIterator) next() bool { if it.aDone && it.bDone { return false } -first: - if it.aDone { - it.k = it.b.Hash() + for { + if it.aDone { + it.k = it.b.Hash() + it.bDone = !it.b.Next() + return true + } + if it.bDone { + it.k = it.a.Hash() + it.aDone = !it.a.Next() + return true + } + nextA, nextB := it.a.Hash(), it.b.Hash() + if diff := bytes.Compare(nextA[:], nextB[:]); diff < 0 { + it.aDone = !it.a.Next() + it.k = nextA + return true + } else if diff == 0 { + // Now we need to advance one of them + it.aDone = !it.a.Next() + continue + } it.bDone = !it.b.Next() + it.k = nextB return true } - if it.bDone { - it.k = it.a.Hash() - it.aDone = !it.a.Next() - return true - } - nextA, nextB := it.a.Hash(), it.b.Hash() - if diff := bytes.Compare(nextA[:], nextB[:]); diff < 0 { - it.aDone = !it.a.Next() - it.k = nextA - return true - } else if diff == 0 { - // Now we need to advance one of them - it.aDone = !it.a.Next() - goto first - } - it.bDone = !it.b.Next() - it.k = nextB - return true } // Error returns any failure that occurred during iteration, which might have @@ -195,19 +185,21 @@ func (it *binaryIterator) Slot() []byte { // Release recursively releases all the iterators in the stack. func (it *binaryIterator) Release() { it.a.Release() - it.b.Release() + if it.b != nil { + it.b.Release() + } } // newBinaryAccountIterator creates a simplistic account iterator to step over // all the accounts in a slow, but easily verifiable way. -func (dl *diffLayer) newBinaryAccountIterator() AccountIterator { - iter := dl.initBinaryAccountIterator() +func (dl *diffLayer) newBinaryAccountIterator(seek common.Hash) AccountIterator { + iter := dl.initBinaryAccountIterator(seek) return iter.(AccountIterator) } // newBinaryStorageIterator creates a simplistic account iterator to step over // all the storage slots in a slow, but easily verifiable way. -func (dl *diffLayer) newBinaryStorageIterator(account common.Hash) StorageIterator { - iter := dl.initBinaryStorageIterator(account) +func (dl *diffLayer) newBinaryStorageIterator(account, seek common.Hash) StorageIterator { + iter := dl.initBinaryStorageIterator(account, seek) return iter.(StorageIterator) } 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 daa8cdcc54..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,10 +50,13 @@ 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 + it = diffLayer.newBinaryAccountIterator(common.Hash{}) + verifyIterator(t, 100, it, verifyNothing) // Nil is allowed for single layer iterator + diskLayer := diffToDisk(diffLayer) it = diskLayer.AccountIterator(common.Hash{}) verifyIterator(t, 100, it, verifyNothing) // Nil is allowed for single layer iterator @@ -92,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 } } @@ -222,20 +221,20 @@ 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 head := snaps.Snapshot(common.HexToHash("0x04")) verifyIterator(t, 3, head.(snapshot).AccountIterator(common.Hash{}), verifyNothing) - verifyIterator(t, 7, head.(*diffLayer).newBinaryAccountIterator(), verifyAccount) + verifyIterator(t, 7, head.(*diffLayer).newBinaryAccountIterator(common.Hash{}), verifyAccount) it, _ := snaps.AccountIterator(common.HexToHash("0x04"), common.Hash{}) verifyIterator(t, 7, it, verifyAccount) @@ -249,7 +248,7 @@ func TestAccountIteratorTraversal(t *testing.T) { }() aggregatorMemoryLimit = 0 // Force pushing the bottom-most layer into disk snaps.Cap(common.HexToHash("0x04"), 2) - verifyIterator(t, 7, head.(*diffLayer).newBinaryAccountIterator(), verifyAccount) + verifyIterator(t, 7, head.(*diffLayer).newBinaryAccountIterator(common.Hash{}), verifyAccount) it, _ = snaps.AccountIterator(common.HexToHash("0x04"), common.Hash{}) verifyIterator(t, 7, it, verifyAccount) @@ -269,21 +268,21 @@ 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")), verifyStorage) + verifyIterator(t, 6, head.(*diffLayer).newBinaryStorageIterator(common.HexToHash("0xaa"), common.Hash{}), verifyStorage) it, _ := snaps.StorageIterator(common.HexToHash("0x04"), common.HexToHash("0xaa"), common.Hash{}) verifyIterator(t, 6, it, verifyStorage) @@ -297,7 +296,7 @@ func TestStorageIteratorTraversal(t *testing.T) { }() aggregatorMemoryLimit = 0 // Force pushing the bottom-most layer into disk snaps.Cap(common.HexToHash("0x04"), 2) - verifyIterator(t, 6, head.(*diffLayer).newBinaryStorageIterator(common.HexToHash("0xaa")), verifyStorage) + verifyIterator(t, 6, head.(*diffLayer).newBinaryStorageIterator(common.HexToHash("0xaa"), common.Hash{}), verifyStorage) it, _ = snaps.StorageIterator(common.HexToHash("0x04"), common.HexToHash("0xaa"), common.Hash{}) verifyIterator(t, 6, it, verifyStorage) @@ -354,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")) @@ -453,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")) @@ -523,12 +522,12 @@ 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")) verifyIterator(t, 200, head.(snapshot).AccountIterator(common.Hash{}), verifyNothing) - verifyIterator(t, 200, head.(*diffLayer).newBinaryAccountIterator(), verifyAccount) + verifyIterator(t, 200, head.(*diffLayer).newBinaryAccountIterator(common.Hash{}), verifyAccount) it, _ := snaps.AccountIterator(common.HexToHash("0x80"), common.Hash{}) verifyIterator(t, 200, it, verifyAccount) @@ -543,7 +542,7 @@ func TestAccountIteratorLargeTraversal(t *testing.T) { aggregatorMemoryLimit = 0 // Force pushing the bottom-most layer into disk snaps.Cap(common.HexToHash("0x80"), 2) - verifyIterator(t, 200, head.(*diffLayer).newBinaryAccountIterator(), verifyAccount) + verifyIterator(t, 200, head.(*diffLayer).newBinaryAccountIterator(common.Hash{}), verifyAccount) it, _ = snaps.AccountIterator(common.HexToHash("0x80"), common.Hash{}) verifyIterator(t, 200, it, verifyAccount) @@ -555,6 +554,20 @@ func TestAccountIteratorLargeTraversal(t *testing.T) { // - flattens C2 all the way into CN // - continues iterating func TestAccountIteratorFlattening(t *testing.T) { + t.Run("fast", func(t *testing.T) { + testAccountIteratorFlattening(t, func(snaps *Tree, root, seek common.Hash) AccountIterator { + it, _ := snaps.AccountIterator(root, seek) + return it + }) + }) + t.Run("binary", func(t *testing.T) { + testAccountIteratorFlattening(t, func(snaps *Tree, root, seek common.Hash) AccountIterator { + return snaps.layers[root].(*diffLayer).newBinaryAccountIterator(seek) + }) + }) +} + +func testAccountIteratorFlattening(t *testing.T, newIterator func(snaps *Tree, root, seek common.Hash) AccountIterator) { // Create an empty base layer and a snapshot tree out of it base := &diskLayer{ diskdb: rawdb.NewMemoryDatabase(), @@ -567,17 +580,17 @@ func TestAccountIteratorFlattening(t *testing.T) { }, } // 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 - it, _ := snaps.AccountIterator(common.HexToHash("0x04"), common.Hash{}) + it := newIterator(snaps, common.HexToHash("0x04"), common.Hash{}) defer it.Release() if err := snaps.Cap(common.HexToHash("0x04"), 1); err != nil { @@ -587,6 +600,21 @@ func TestAccountIteratorFlattening(t *testing.T) { } func TestAccountIteratorSeek(t *testing.T) { + t.Run("fast", func(t *testing.T) { + testAccountIteratorSeek(t, func(snaps *Tree, root, seek common.Hash) AccountIterator { + it, _ := snaps.AccountIterator(root, seek) + return it + }) + }) + t.Run("binary", func(t *testing.T) { + testAccountIteratorSeek(t, func(snaps *Tree, root, seek common.Hash) AccountIterator { + it := snaps.layers[root].(*diffLayer).newBinaryAccountIterator(seek) + return it + }) + }) +} + +func testAccountIteratorSeek(t *testing.T, newIterator func(snaps *Tree, root, seek common.Hash) AccountIterator) { // Create a snapshot stack with some initial data base := &diskLayer{ diskdb: rawdb.NewMemoryDatabase(), @@ -598,13 +626,13 @@ func TestAccountIteratorSeek(t *testing.T) { 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 @@ -612,44 +640,58 @@ func TestAccountIteratorSeek(t *testing.T) { // 03: aa, bb, dd, ee, f0 (, f0), ff // 04: aa, bb, cc, dd, ee, f0 (, f0), ff (, ff) // Construct various iterators and ensure their traversal is correct - it, _ := snaps.AccountIterator(common.HexToHash("0x02"), common.HexToHash("0xdd")) + it := newIterator(snaps, common.HexToHash("0x02"), common.HexToHash("0xdd")) defer it.Release() verifyIterator(t, 3, it, verifyAccount) // expected: ee, f0, ff - it, _ = snaps.AccountIterator(common.HexToHash("0x02"), common.HexToHash("0xaa")) + it = newIterator(snaps, common.HexToHash("0x02"), common.HexToHash("0xaa")) defer it.Release() verifyIterator(t, 4, it, verifyAccount) // expected: aa, ee, f0, ff - it, _ = snaps.AccountIterator(common.HexToHash("0x02"), common.HexToHash("0xff")) + it = newIterator(snaps, common.HexToHash("0x02"), common.HexToHash("0xff")) defer it.Release() verifyIterator(t, 1, it, verifyAccount) // expected: ff - it, _ = snaps.AccountIterator(common.HexToHash("0x02"), common.HexToHash("0xff1")) + it = newIterator(snaps, common.HexToHash("0x02"), common.HexToHash("0xff1")) defer it.Release() verifyIterator(t, 0, it, verifyAccount) // expected: nothing - it, _ = snaps.AccountIterator(common.HexToHash("0x04"), common.HexToHash("0xbb")) + it = newIterator(snaps, common.HexToHash("0x04"), common.HexToHash("0xbb")) defer it.Release() verifyIterator(t, 6, it, verifyAccount) // expected: bb, cc, dd, ee, f0, ff - it, _ = snaps.AccountIterator(common.HexToHash("0x04"), common.HexToHash("0xef")) + it = newIterator(snaps, common.HexToHash("0x04"), common.HexToHash("0xef")) defer it.Release() verifyIterator(t, 2, it, verifyAccount) // expected: f0, ff - it, _ = snaps.AccountIterator(common.HexToHash("0x04"), common.HexToHash("0xf0")) + it = newIterator(snaps, common.HexToHash("0x04"), common.HexToHash("0xf0")) defer it.Release() verifyIterator(t, 2, it, verifyAccount) // expected: f0, ff - it, _ = snaps.AccountIterator(common.HexToHash("0x04"), common.HexToHash("0xff")) + it = newIterator(snaps, common.HexToHash("0x04"), common.HexToHash("0xff")) defer it.Release() verifyIterator(t, 1, it, verifyAccount) // expected: ff - it, _ = snaps.AccountIterator(common.HexToHash("0x04"), common.HexToHash("0xff1")) + it = newIterator(snaps, common.HexToHash("0x04"), common.HexToHash("0xff1")) defer it.Release() verifyIterator(t, 0, it, verifyAccount) // expected: nothing } func TestStorageIteratorSeek(t *testing.T) { + t.Run("fast", func(t *testing.T) { + testStorageIteratorSeek(t, func(snaps *Tree, root, account, seek common.Hash) StorageIterator { + it, _ := snaps.StorageIterator(root, account, seek) + return it + }) + }) + t.Run("binary", func(t *testing.T) { + testStorageIteratorSeek(t, func(snaps *Tree, root, account, seek common.Hash) StorageIterator { + return snaps.layers[root].(*diffLayer).newBinaryStorageIterator(account, seek) + }) + }) +} + +func testStorageIteratorSeek(t *testing.T, newIterator func(snaps *Tree, root, account, seek common.Hash) StorageIterator) { // Create a snapshot stack with some initial data base := &diskLayer{ diskdb: rawdb.NewMemoryDatabase(), @@ -662,13 +704,13 @@ func TestStorageIteratorSeek(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", "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 @@ -676,35 +718,35 @@ func TestStorageIteratorSeek(t *testing.T) { // 03: 01, 02, 03, 05 (, 05), 06 // 04: 01(, 01), 02, 03, 05(, 05, 05), 06, 08 // Construct various iterators and ensure their traversal is correct - it, _ := snaps.StorageIterator(common.HexToHash("0x02"), common.HexToHash("0xaa"), common.HexToHash("0x01")) + it := newIterator(snaps, common.HexToHash("0x02"), common.HexToHash("0xaa"), common.HexToHash("0x01")) defer it.Release() verifyIterator(t, 3, it, verifyStorage) // expected: 01, 03, 05 - it, _ = snaps.StorageIterator(common.HexToHash("0x02"), common.HexToHash("0xaa"), common.HexToHash("0x02")) + it = newIterator(snaps, common.HexToHash("0x02"), common.HexToHash("0xaa"), common.HexToHash("0x02")) defer it.Release() verifyIterator(t, 2, it, verifyStorage) // expected: 03, 05 - it, _ = snaps.StorageIterator(common.HexToHash("0x02"), common.HexToHash("0xaa"), common.HexToHash("0x5")) + it = newIterator(snaps, common.HexToHash("0x02"), common.HexToHash("0xaa"), common.HexToHash("0x5")) defer it.Release() verifyIterator(t, 1, it, verifyStorage) // expected: 05 - it, _ = snaps.StorageIterator(common.HexToHash("0x02"), common.HexToHash("0xaa"), common.HexToHash("0x6")) + it = newIterator(snaps, common.HexToHash("0x02"), common.HexToHash("0xaa"), common.HexToHash("0x6")) defer it.Release() verifyIterator(t, 0, it, verifyStorage) // expected: nothing - it, _ = snaps.StorageIterator(common.HexToHash("0x04"), common.HexToHash("0xaa"), common.HexToHash("0x01")) + it = newIterator(snaps, common.HexToHash("0x04"), common.HexToHash("0xaa"), common.HexToHash("0x01")) defer it.Release() verifyIterator(t, 6, it, verifyStorage) // expected: 01, 02, 03, 05, 06, 08 - it, _ = snaps.StorageIterator(common.HexToHash("0x04"), common.HexToHash("0xaa"), common.HexToHash("0x05")) + it = newIterator(snaps, common.HexToHash("0x04"), common.HexToHash("0xaa"), common.HexToHash("0x05")) defer it.Release() verifyIterator(t, 3, it, verifyStorage) // expected: 05, 06, 08 - it, _ = snaps.StorageIterator(common.HexToHash("0x04"), common.HexToHash("0xaa"), common.HexToHash("0x08")) + it = newIterator(snaps, common.HexToHash("0x04"), common.HexToHash("0xaa"), common.HexToHash("0x08")) defer it.Release() verifyIterator(t, 1, it, verifyStorage) // expected: 08 - it, _ = snaps.StorageIterator(common.HexToHash("0x04"), common.HexToHash("0xaa"), common.HexToHash("0x09")) + it = newIterator(snaps, common.HexToHash("0x04"), common.HexToHash("0xaa"), common.HexToHash("0x09")) defer it.Release() verifyIterator(t, 0, it, verifyStorage) // expected: nothing } @@ -713,6 +755,76 @@ func TestStorageIteratorSeek(t *testing.T) { // deleted accounts (where the Account() value is nil). The iterator // should not output any accounts or nil-values for those cases. func TestAccountIteratorDeletions(t *testing.T) { + t.Run("fast", func(t *testing.T) { + testAccountIteratorDeletions(t, func(snaps *Tree, root, seek common.Hash) AccountIterator { + it, _ := snaps.AccountIterator(root, seek) + return it + }) + }) + t.Run("binary", func(t *testing.T) { + testAccountIteratorDeletions(t, func(snaps *Tree, root, seek common.Hash) AccountIterator { + return snaps.layers[root].(*diffLayer).newBinaryAccountIterator(seek) + }) + }) +} + +func testAccountIteratorDeletions(t *testing.T, newIterator func(snaps *Tree, root, seek common.Hash) AccountIterator) { + // Create an empty base layer and a snapshot tree out of it + base := &diskLayer{ + diskdb: rawdb.NewMemoryDatabase(), + root: common.HexToHash("0x01"), + cache: fastcache.New(1024 * 500), + } + snaps := &Tree{ + layers: map[common.Hash]snapshot{ + base.root: base, + }, + } + // Stack three diff layers on top with various overlaps + 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"), + randomAccountSet("0x33", "0x44", "0x55"), nil) + + // The output should be 11,33,44,55 + it := newIterator(snaps, common.HexToHash("0x04"), common.Hash{}) + + // Do a quick check + verifyIterator(t, 4, it, verifyAccount) + it.Release() + + // And a more detailed verification that we indeed do not see '0x22' + it, _ = snaps.AccountIterator(common.HexToHash("0x04"), common.Hash{}) + defer it.Release() + for it.Next() { + hash := it.Hash() + if it.Account() == nil { + t.Errorf("iterator returned nil-value for hash %x", hash) + } + if hash == common.HexToHash("0x22") { + t.Errorf("expected deleted elem %x to not be returned by iterator", common.HexToHash("0x22")) + } + } +} + +func TestStorageIteratorDeletions(t *testing.T) { + t.Run("fast", func(t *testing.T) { + testStorageIteratorDeletions(t, func(snaps *Tree, root, account, seek common.Hash) StorageIterator { + it, _ := snaps.StorageIterator(root, account, seek) + return it + }) + }) + t.Run("binary", func(t *testing.T) { + testStorageIteratorDeletions(t, func(snaps *Tree, root, account, seek common.Hash) StorageIterator { + return snaps.layers[root].(*diffLayer).newBinaryStorageIterator(account, seek) + }) + }) +} + +func testStorageIteratorDeletions(t *testing.T, newIterator func(snaps *Tree, root, account, seek common.Hash) StorageIterator) { // Create an empty base layer and a snapshot tree out of it base := &diskLayer{ diskdb: rawdb.NewMemoryDatabase(), @@ -726,93 +838,52 @@ func TestAccountIteratorDeletions(t *testing.T) { } // 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("0x04"), common.HexToHash("0x03"), - nil, randomAccountSet("0x33", "0x44", "0x55"), nil) - - // The output should be 11,33,44,55 - it, _ := snaps.AccountIterator(common.HexToHash("0x04"), common.Hash{}) - // Do a quick check - verifyIterator(t, 4, it, verifyAccount) - it.Release() - - // And a more detailed verification that we indeed do not see '0x22' - it, _ = snaps.AccountIterator(common.HexToHash("0x04"), common.Hash{}) - defer it.Release() - for it.Next() { - hash := it.Hash() - 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) - } - } -} - -func TestStorageIteratorDeletions(t *testing.T) { - // Create an empty base layer and a snapshot tree out of it - base := &diskLayer{ - diskdb: rawdb.NewMemoryDatabase(), - root: common.HexToHash("0x01"), - cache: fastcache.New(1024 * 500), - } - snaps := &Tree{ - layers: map[common.Hash]snapshot{ - base.root: base, - }, - } - // Stack three diff layers on top with various overlaps - snaps.Update(common.HexToHash("0x02"), common.HexToHash("0x01"), nil, 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 - it, _ := snaps.StorageIterator(common.HexToHash("0x03"), common.HexToHash("0xaa"), common.Hash{}) + it := newIterator(snaps, common.HexToHash("0x03"), common.HexToHash("0xaa"), common.Hash{}) verifyIterator(t, 4, it, verifyStorage) it.Release() // The output should be 04,05,06 - it, _ = snaps.StorageIterator(common.HexToHash("0x03"), common.HexToHash("0xaa"), common.HexToHash("0x03")) + it = newIterator(snaps, common.HexToHash("0x03"), common.HexToHash("0xaa"), common.HexToHash("0x03")) verifyIterator(t, 3, it, verifyStorage) 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, _ = snaps.StorageIterator(common.HexToHash("0x04"), common.HexToHash("0xaa"), common.Hash{}) + 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 - it, _ = snaps.StorageIterator(common.HexToHash("0x05"), common.HexToHash("0xaa"), common.Hash{}) + + it = newIterator(snaps, common.HexToHash("0x05"), common.HexToHash("0xaa"), common.Hash{}) verifyIterator(t, 3, it, verifyStorage) 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)) - it, _ = snaps.StorageIterator(common.HexToHash("0x06"), common.HexToHash("0xaa"), common.Hash{}) + 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() - verifyIterator(t, 2, snaps.Snapshot(common.HexToHash("0x06")).(*diffLayer).newBinaryStorageIterator(common.HexToHash("0xaa")), verifyStorage) + verifyIterator(t, 2, snaps.Snapshot( + common.HexToHash("0x06")).(*diffLayer). + newBinaryStorageIterator(common.HexToHash("0xaa"), common.Hash{}), + verifyStorage) } // BenchmarkAccountIteratorTraversal is a bit notorious -- all layers contain the @@ -849,17 +920,17 @@ 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. head := snaps.Snapshot(common.HexToHash("0x65")) - head.(*diffLayer).newBinaryAccountIterator() + head.(*diffLayer).newBinaryAccountIterator(common.Hash{}) b.Run("binary iterator keys", func(b *testing.B) { for i := 0; i < b.N; i++ { got := 0 - it := head.(*diffLayer).newBinaryAccountIterator() + it := head.(*diffLayer).newBinaryAccountIterator(common.Hash{}) for it.Next() { got++ } @@ -871,7 +942,7 @@ func BenchmarkAccountIteratorTraversal(b *testing.B) { b.Run("binary iterator values", func(b *testing.B) { for i := 0; i < b.N; i++ { got := 0 - it := head.(*diffLayer).newBinaryAccountIterator() + it := head.(*diffLayer).newBinaryAccountIterator(common.Hash{}) for it.Next() { got++ head.(*diffLayer).accountRLP(it.Hash(), 0) @@ -944,19 +1015,19 @@ 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. head := snaps.Snapshot(common.HexToHash("0x65")) - head.(*diffLayer).newBinaryAccountIterator() + head.(*diffLayer).newBinaryAccountIterator(common.Hash{}) b.Run("binary iterator (keys)", func(b *testing.B) { for i := 0; i < b.N; i++ { got := 0 - it := head.(*diffLayer).newBinaryAccountIterator() + it := head.(*diffLayer).newBinaryAccountIterator(common.Hash{}) for it.Next() { got++ } @@ -968,7 +1039,7 @@ func BenchmarkAccountIteratorLargeBaselayer(b *testing.B) { b.Run("binary iterator (values)", func(b *testing.B) { for i := 0; i < b.N; i++ { got := 0 - it := head.(*diffLayer).newBinaryAccountIterator() + it := head.(*diffLayer).newBinaryAccountIterator(common.Hash{}) for it.Next() { got++ v := it.Hash() @@ -1013,7 +1084,7 @@ func BenchmarkAccountIteratorLargeBaselayer(b *testing.B) { /* func BenchmarkBinaryAccountIteration(b *testing.B) { benchmarkAccountIteration(b, func(snap snapshot) AccountIterator { - return snap.(*diffLayer).newBinaryAccountIterator() + return snap.(*diffLayer).newBinaryAccountIterator(common.Hash{}) }) } diff --git a/core/state/snapshot/journal.go b/core/state/snapshot/journal.go index 8513e73dd0..e4b396b990 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 { @@ -238,16 +242,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 @@ -271,7 +271,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. @@ -292,8 +292,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 @@ -310,10 +310,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) ) @@ -325,8 +323,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) @@ -334,9 +359,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 @@ -355,7 +377,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 d1ffb5cc2d..f0f6296433 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. @@ -335,7 +335,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). @@ -350,7 +350,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() @@ -539,35 +539,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 @@ -575,10 +546,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))) @@ -587,7 +562,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() } @@ -616,6 +591,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 @@ -679,7 +664,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 a9ab3eaea3..34ef61e8d0 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 422badb19b..76a3aba92c 100644 --- a/core/state/state_object.go +++ b/core/state/state_object.go @@ -20,10 +20,10 @@ import ( "bytes" "fmt" "maps" + "slices" "time" "github.com/ethereum/go-ethereum/common" - "github.com/ethereum/go-ethereum/core/tracing" "github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/crypto" "github.com/ethereum/go-ethereum/log" @@ -199,7 +199,7 @@ func (s *stateObject) GetCommittedState(key common.Hash) common.Hash { // Schedule the resolved storage slots for prefetching if it's enabled. if s.db.prefetcher != nil && s.data.Root != types.EmptyRootHash { - if err = s.db.prefetcher.prefetch(s.addrHash, s.origin.Root, s.address, [][]byte{key[:]}, true); err != nil { + if err = s.db.prefetcher.prefetch(s.addrHash, s.origin.Root, s.address, nil, []common.Hash{key}, true); err != nil { log.Error("Failed to prefetch storage slot", "addr", s.address, "key", key, "err", err) } } @@ -208,19 +208,18 @@ func (s *stateObject) GetCommittedState(key common.Hash) common.Hash { } // SetState updates a value in account storage. -func (s *stateObject) SetState(key, value common.Hash) { +// It returns the previous value +func (s *stateObject) SetState(key, value common.Hash) common.Hash { // If the new value is the same as old, don't set. Otherwise, track only the // dirty changes, supporting reverting all of it back to no change. prev, origin := s.getState(key) if prev == value { - return + return prev } // New value is different, update and journal the change s.db.journal.storageChange(s.address, key, prev, origin) s.setState(key, value, origin) - if s.db.logger != nil && s.db.logger.OnStorageChange != nil { - s.db.logger.OnStorageChange(s.address, key, prev, value) - } + return prev } // setState updates a value in account dirty storage. The dirtiness will be @@ -237,7 +236,7 @@ func (s *stateObject) setState(key common.Hash, value common.Hash, origin common // finalise moves all dirty storage slots into the pending area to be hashed or // committed later. It is invoked at the end of every transaction. func (s *stateObject) finalise() { - slotsToPrefetch := make([][]byte, 0, len(s.dirtyStorage)) + slotsToPrefetch := make([]common.Hash, 0, len(s.dirtyStorage)) for key, value := range s.dirtyStorage { if origin, exist := s.uncommittedStorage[key]; exist && origin == value { // The slot is reverted to its original value, delete the entry @@ -250,7 +249,7 @@ func (s *stateObject) finalise() { // The slot is different from its original value and hasn't been // tracked for commit yet. s.uncommittedStorage[key] = s.GetCommittedState(key) - slotsToPrefetch = append(slotsToPrefetch, common.CopyBytes(key[:])) // Copy needed for closure + slotsToPrefetch = append(slotsToPrefetch, key) // Copy needed for closure } // Aggregate the dirty storage slots into the pending area. It might // be possible that the value of tracked slot here is same with the @@ -261,7 +260,7 @@ func (s *stateObject) finalise() { s.pendingStorage[key] = value } if s.db.prefetcher != nil && len(slotsToPrefetch) > 0 && s.data.Root != types.EmptyRootHash { - if err := s.db.prefetcher.prefetch(s.addrHash, s.data.Root, s.address, slotsToPrefetch, false); err != nil { + if err := s.db.prefetcher.prefetch(s.addrHash, s.data.Root, s.address, nil, slotsToPrefetch, false); err != nil { log.Error("Failed to prefetch slots", "addr", s.address, "slots", len(slotsToPrefetch), "err", err) } } @@ -323,7 +322,7 @@ func (s *stateObject) updateTrie() (Trie, error) { // Whereas if the created node is handled first, then the collapse is avoided, and `B` is not resolved. var ( deletions []common.Hash - used = make([][]byte, 0, len(s.uncommittedStorage)) + used = make([]common.Hash, 0, len(s.uncommittedStorage)) ) for key, origin := range s.uncommittedStorage { // Skip noop changes, persist actual changes @@ -346,7 +345,7 @@ func (s *stateObject) updateTrie() (Trie, error) { deletions = append(deletions, key) } // Cache the items for preloading - used = append(used, common.CopyBytes(key[:])) // Copy needed for closure + used = append(used, key) // Copy needed for closure } for _, key := range deletions { if err := tr.DeleteStorage(s.address, key[:]); err != nil { @@ -356,7 +355,7 @@ func (s *stateObject) updateTrie() (Trie, error) { s.db.StorageDeleted.Add(1) } if s.db.prefetcher != nil { - s.db.prefetcher.used(s.addrHash, s.data.Root, used) + s.db.prefetcher.used(s.addrHash, s.data.Root, nil, used) } s.uncommittedStorage = make(Storage) // empties the commit markers return tr, nil @@ -448,33 +447,25 @@ func (s *stateObject) commit() (*accountUpdate, *trienode.NodeSet, error) { // AddBalance adds amount to s's balance. // It is used to add funds to the destination account of a transfer. -func (s *stateObject) AddBalance(amount *uint256.Int, reason tracing.BalanceChangeReason) { +// returns the previous balance +func (s *stateObject) AddBalance(amount *uint256.Int) uint256.Int { // EIP161: We must check emptiness for the objects such that the account // clearing (0,0,0 objects) can take effect. if amount.IsZero() { if s.empty() { s.touch() } - return + return *(s.Balance()) } - s.SetBalance(new(uint256.Int).Add(s.Balance(), amount), reason) + return s.SetBalance(new(uint256.Int).Add(s.Balance(), amount)) } -// SubBalance removes amount from s's balance. -// It is used to remove funds from the origin account of a transfer. -func (s *stateObject) SubBalance(amount *uint256.Int, reason tracing.BalanceChangeReason) { - if amount.IsZero() { - return - } - s.SetBalance(new(uint256.Int).Sub(s.Balance(), amount), reason) -} - -func (s *stateObject) SetBalance(amount *uint256.Int, reason tracing.BalanceChangeReason) { +// SetBalance sets the balance for the object, and returns the previous balance. +func (s *stateObject) SetBalance(amount *uint256.Int) uint256.Int { + prev := *s.data.Balance s.db.journal.balanceChange(s.address, s.data.Balance) - if s.db.logger != nil && s.db.logger.OnBalanceChange != nil { - s.db.logger.OnBalanceChange(s.address, s.Balance().ToBig(), amount.ToBig(), reason) - } s.setBalance(amount) + return prev } func (s *stateObject) setBalance(amount *uint256.Int) { @@ -520,10 +511,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 } @@ -538,20 +532,21 @@ 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 } -func (s *stateObject) SetCode(codeHash common.Hash, code []byte) { - s.db.journal.setCode(s.address) - if s.db.logger != nil && s.db.logger.OnCodeChange != nil { - // TODO remove prevcode from this callback - s.db.logger.OnCodeChange(s.address, common.BytesToHash(s.CodeHash()), nil, codeHash, code) - } +func (s *stateObject) SetCode(codeHash common.Hash, code []byte) (prev []byte) { + prev = slices.Clone(s.code) + s.db.journal.setCode(s.address, prev) s.setCode(codeHash, code) + return prev } func (s *stateObject) setCode(codeHash common.Hash, code []byte) { @@ -562,9 +557,6 @@ func (s *stateObject) setCode(codeHash common.Hash, code []byte) { func (s *stateObject) SetNonce(nonce uint64) { s.db.journal.nonceChange(s.address, s.data.Nonce) - if s.db.logger != nil && s.db.logger.OnNonceChange != nil { - s.db.logger.OnNonceChange(s.address, s.data.Nonce, nonce) - } s.setNonce(nonce) } diff --git a/core/state/state_test.go b/core/state/state_test.go index 9de50beb12..6f54300c37 100644 --- a/core/state/state_test.go +++ b/core/state/state_test.go @@ -23,7 +23,6 @@ import ( "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/core/rawdb" - "github.com/ethereum/go-ethereum/core/tracing" "github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/crypto" "github.com/ethereum/go-ethereum/triedb" @@ -48,11 +47,11 @@ func TestDump(t *testing.T) { // generate a few entries obj1 := s.state.getOrNewStateObject(common.BytesToAddress([]byte{0x01})) - obj1.AddBalance(uint256.NewInt(22), tracing.BalanceChangeUnspecified) + obj1.AddBalance(uint256.NewInt(22)) obj2 := s.state.getOrNewStateObject(common.BytesToAddress([]byte{0x01, 0x02})) obj2.SetCode(crypto.Keccak256Hash([]byte{3, 3, 3, 3, 3, 3, 3}), []byte{3, 3, 3, 3, 3, 3, 3}) obj3 := s.state.getOrNewStateObject(common.BytesToAddress([]byte{0x02})) - obj3.SetBalance(uint256.NewInt(44), tracing.BalanceChangeUnspecified) + obj3.SetBalance(uint256.NewInt(44)) // write some of them to the trie s.state.updateStateObject(obj1) @@ -106,13 +105,13 @@ func TestIterativeDump(t *testing.T) { // generate a few entries obj1 := s.state.getOrNewStateObject(common.BytesToAddress([]byte{0x01})) - obj1.AddBalance(uint256.NewInt(22), tracing.BalanceChangeUnspecified) + obj1.AddBalance(uint256.NewInt(22)) obj2 := s.state.getOrNewStateObject(common.BytesToAddress([]byte{0x01, 0x02})) obj2.SetCode(crypto.Keccak256Hash([]byte{3, 3, 3, 3, 3, 3, 3}), []byte{3, 3, 3, 3, 3, 3, 3}) obj3 := s.state.getOrNewStateObject(common.BytesToAddress([]byte{0x02})) - obj3.SetBalance(uint256.NewInt(44), tracing.BalanceChangeUnspecified) + obj3.SetBalance(uint256.NewInt(44)) obj4 := s.state.getOrNewStateObject(common.BytesToAddress([]byte{0x00})) - obj4.AddBalance(uint256.NewInt(1337), tracing.BalanceChangeUnspecified) + obj4.AddBalance(uint256.NewInt(1337)) // write some of them to the trie s.state.updateStateObject(obj1) @@ -200,7 +199,7 @@ func TestCreateObjectRevert(t *testing.T) { state.CreateAccount(addr) so0 := state.getStateObject(addr) - so0.SetBalance(uint256.NewInt(42), tracing.BalanceChangeUnspecified) + so0.SetBalance(uint256.NewInt(42)) so0.SetNonce(43) so0.SetCode(crypto.Keccak256Hash([]byte{'c', 'a', 'f', 'e'}), []byte{'c', 'a', 'f', 'e'}) state.setStateObject(so0) diff --git a/core/state/statedb.go b/core/state/statedb.go index b2b4f8fb97..d279ccfdfe 100644 --- a/core/state/statedb.go +++ b/core/state/statedb.go @@ -21,7 +21,6 @@ import ( "errors" "fmt" "maps" - "math/big" "slices" "sync" "sync/atomic" @@ -38,7 +37,6 @@ import ( "github.com/ethereum/go-ethereum/params" "github.com/ethereum/go-ethereum/trie" "github.com/ethereum/go-ethereum/trie/trienode" - "github.com/ethereum/go-ethereum/trie/triestate" "github.com/ethereum/go-ethereum/trie/utils" "github.com/holiman/uint256" "golang.org/x/sync/errgroup" @@ -82,7 +80,6 @@ type StateDB struct { db Database prefetcher *triePrefetcher trie Trie - logger *tracing.Hooks reader Reader // originalRoot is the pre-state root, before any changes were made. @@ -190,11 +187,6 @@ func New(root common.Hash, db Database) (*StateDB, error) { return sdb, nil } -// SetLogger sets the logger for account update hooks. -func (s *StateDB) SetLogger(l *tracing.Hooks) { - s.logger = l -} - // StartPrefetcher initializes a new trie prefetcher to pull in nodes from the // state trie concurrently while the state is mutated so that when we reach the // commit phase, most of the needed data is already hot. @@ -215,7 +207,7 @@ func (s *StateDB) StartPrefetcher(namespace string, witness *stateless.Witness) // the prefetcher is constructed. For more details, see: // https://github.com/ethereum/go-ethereum/issues/29880 s.prefetcher = newTriePrefetcher(s.db, s.originalRoot, namespace, witness == nil) - if err := s.prefetcher.prefetch(common.Hash{}, s.originalRoot, common.Address{}, nil, false); err != nil { + if err := s.prefetcher.prefetch(common.Hash{}, s.originalRoot, common.Address{}, nil, nil, false); err != nil { log.Error("Failed to prefetch account trie", "root", s.originalRoot, "err", err) } } @@ -248,9 +240,6 @@ func (s *StateDB) AddLog(log *types.Log) { log.TxHash = s.thash log.TxIndex = uint(s.txIndex) log.Index = s.logSize - if s.logger != nil && s.logger.OnLog != nil { - s.logger.OnLog(log) - } s.logs[s.thash] = append(s.logs[s.thash], log) s.logSize++ } @@ -352,6 +341,9 @@ func (s *StateDB) TxIndex() int { func (s *StateDB) GetCode(addr common.Address) []byte { stateObject := s.getStateObject(addr) if stateObject != nil { + if s.witness != nil { + s.witness.AddCode(stateObject.Code()) + } return stateObject.Code() } return nil @@ -360,6 +352,9 @@ func (s *StateDB) GetCode(addr common.Address) []byte { func (s *StateDB) GetCodeSize(addr common.Address) int { stateObject := s.getStateObject(addr) if stateObject != nil { + if s.witness != nil { + s.witness.AddCode(stateObject.Code()) + } return stateObject.CodeSize() } return 0 @@ -410,25 +405,30 @@ func (s *StateDB) HasSelfDestructed(addr common.Address) bool { */ // AddBalance adds amount to the account associated with addr. -func (s *StateDB) AddBalance(addr common.Address, amount *uint256.Int, reason tracing.BalanceChangeReason) { +func (s *StateDB) AddBalance(addr common.Address, amount *uint256.Int, reason tracing.BalanceChangeReason) uint256.Int { stateObject := s.getOrNewStateObject(addr) - if stateObject != nil { - stateObject.AddBalance(amount, reason) + if stateObject == nil { + return uint256.Int{} } + return stateObject.AddBalance(amount) } // SubBalance subtracts amount from the account associated with addr. -func (s *StateDB) SubBalance(addr common.Address, amount *uint256.Int, reason tracing.BalanceChangeReason) { +func (s *StateDB) SubBalance(addr common.Address, amount *uint256.Int, reason tracing.BalanceChangeReason) uint256.Int { stateObject := s.getOrNewStateObject(addr) - if stateObject != nil { - stateObject.SubBalance(amount, reason) + if stateObject == nil { + return uint256.Int{} } + if amount.IsZero() { + return *(stateObject.Balance()) + } + return stateObject.SetBalance(new(uint256.Int).Sub(stateObject.Balance(), amount)) } func (s *StateDB) SetBalance(addr common.Address, amount *uint256.Int, reason tracing.BalanceChangeReason) { stateObject := s.getOrNewStateObject(addr) if stateObject != nil { - stateObject.SetBalance(amount, reason) + stateObject.SetBalance(amount) } } @@ -439,18 +439,19 @@ func (s *StateDB) SetNonce(addr common.Address, nonce uint64) { } } -func (s *StateDB) SetCode(addr common.Address, code []byte) { +func (s *StateDB) SetCode(addr common.Address, code []byte) (prev []byte) { stateObject := s.getOrNewStateObject(addr) if stateObject != nil { - stateObject.SetCode(crypto.Keccak256Hash(code), code) + return stateObject.SetCode(crypto.Keccak256Hash(code), code) } + return nil } -func (s *StateDB) SetState(addr common.Address, key, value common.Hash) { - stateObject := s.getOrNewStateObject(addr) - if stateObject != nil { - stateObject.SetState(key, value) +func (s *StateDB) SetState(addr common.Address, key, value common.Hash) common.Hash { + if stateObject := s.getOrNewStateObject(addr); stateObject != nil { + return stateObject.SetState(key, value) } + return common.Hash{} } // SetStorage replaces the entire storage for the specified account with given @@ -478,7 +479,7 @@ func (s *StateDB) SetStorage(addr common.Address, storage map[common.Hash]common if obj != nil { newObj.SetCode(common.BytesToHash(obj.CodeHash()), obj.code) newObj.SetNonce(obj.Nonce()) - newObj.SetBalance(obj.Balance(), tracing.BalanceChangeUnspecified) + newObj.SetBalance(obj.Balance()) } } @@ -487,15 +488,17 @@ func (s *StateDB) SetStorage(addr common.Address, storage map[common.Hash]common // // The account's state object is still available until the state is committed, // getStateObject will return a non-nil account after SelfDestruct. -func (s *StateDB) SelfDestruct(addr common.Address) { +func (s *StateDB) SelfDestruct(addr common.Address) uint256.Int { stateObject := s.getStateObject(addr) + var prevBalance uint256.Int if stateObject == nil { - return + return prevBalance } + prevBalance = *(stateObject.Balance()) // Regardless of whether it is already destructed or not, we do have to // journal the balance-change, if we set it to zero here. if !stateObject.Balance().IsZero() { - stateObject.SetBalance(new(uint256.Int), tracing.BalanceDecreaseSelfdestruct) + stateObject.SetBalance(new(uint256.Int)) } // If it is already marked as self-destructed, we do not need to add it // for journalling a second time. @@ -503,16 +506,18 @@ func (s *StateDB) SelfDestruct(addr common.Address) { s.journal.destruct(addr) stateObject.markSelfdestructed() } + return prevBalance } -func (s *StateDB) Selfdestruct6780(addr common.Address) { +func (s *StateDB) SelfDestruct6780(addr common.Address) (uint256.Int, bool) { stateObject := s.getStateObject(addr) if stateObject == nil { - return + return uint256.Int{}, false } if stateObject.newContract { - s.SelfDestruct(addr) + return s.SelfDestruct(addr), true } + return *(stateObject.Balance()), false } // SetTransientState sets transient storage for a given account. It @@ -588,7 +593,7 @@ func (s *StateDB) getStateObject(addr common.Address) *stateObject { } // Schedule the resolved account for prefetching if it's enabled. if s.prefetcher != nil { - if err := s.prefetcher.prefetch(common.Hash{}, s.originalRoot, common.Address{}, [][]byte{addr[:]}, true); err != nil { + if err = s.prefetcher.prefetch(common.Hash{}, s.originalRoot, common.Address{}, []common.Address{addr}, nil, true); err != nil { log.Error("Failed to prefetch account", "addr", addr, "err", err) } } @@ -646,10 +651,11 @@ func (s *StateDB) CreateContract(addr common.Address) { // Snapshots of the copied state cannot be applied to the copy. func (s *StateDB) Copy() *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), - reader: s.reader.Copy(), + reader: reader, originalRoot: s.originalRoot, stateObjects: make(map[common.Address]*stateObject, len(s.stateObjects)), stateObjectsDestruct: make(map[common.Address]*stateObject, len(s.stateObjectsDestruct)), @@ -721,7 +727,7 @@ func (s *StateDB) GetRefund() uint64 { // the journal as well as the refunds. Finalise, however, will not push any updates // into the tries just yet. Only IntermediateRoot or Commit will do that. func (s *StateDB) Finalise(deleteEmptyObjects bool) { - addressesToPrefetch := make([][]byte, 0, len(s.journal.dirties)) + addressesToPrefetch := make([]common.Address, 0, len(s.journal.dirties)) for addr := range s.journal.dirties { obj, exist := s.stateObjects[addr] if !exist { @@ -736,11 +742,6 @@ func (s *StateDB) Finalise(deleteEmptyObjects bool) { if obj.selfDestructed || (deleteEmptyObjects && obj.empty()) { delete(s.stateObjects, obj.address) s.markDelete(addr) - - // If ether was sent to account post-selfdestruct it is burnt. - if bal := obj.Balance(); s.logger != nil && s.logger.OnBalanceChange != nil && obj.selfDestructed && bal.Sign() != 0 { - s.logger.OnBalanceChange(obj.address, bal.ToBig(), new(big.Int), tracing.BalanceDecreaseSelfdestructBurn) - } // We need to maintain account deletions explicitly (will remain // set indefinitely). Note only the first occurred self-destruct // event is tracked. @@ -754,10 +755,10 @@ func (s *StateDB) Finalise(deleteEmptyObjects bool) { // At this point, also ship the address off to the precacher. The precacher // will start loading tries, and when the change is eventually committed, // the commit-phase will be a lot faster - addressesToPrefetch = append(addressesToPrefetch, common.CopyBytes(addr[:])) // Copy needed for closure + addressesToPrefetch = append(addressesToPrefetch, addr) // Copy needed for closure } if s.prefetcher != nil && len(addressesToPrefetch) > 0 { - if err := s.prefetcher.prefetch(common.Hash{}, s.originalRoot, common.Address{}, addressesToPrefetch, false); err != nil { + if err := s.prefetcher.prefetch(common.Hash{}, s.originalRoot, common.Address{}, addressesToPrefetch, nil, false); err != nil { log.Error("Failed to prefetch addresses", "addresses", len(addressesToPrefetch), "err", err) } } @@ -878,7 +879,7 @@ func (s *StateDB) IntermediateRoot(deleteEmptyObjects bool) common.Hash { // into a shortnode. This requires `B` to be resolved from disk. // Whereas if the created node is handled first, then the collapse is avoided, and `B` is not resolved. var ( - usedAddrs [][]byte + usedAddrs []common.Address deletedAddrs []common.Address ) for addr, op := range s.mutations { @@ -893,7 +894,7 @@ func (s *StateDB) IntermediateRoot(deleteEmptyObjects bool) common.Hash { s.updateStateObject(s.stateObjects[addr]) s.AccountUpdated += 1 } - usedAddrs = append(usedAddrs, common.CopyBytes(addr[:])) // Copy needed for closure + usedAddrs = append(usedAddrs, addr) // Copy needed for closure } for _, deletedAddr := range deletedAddrs { s.deleteStateObject(deletedAddr) @@ -902,7 +903,7 @@ func (s *StateDB) IntermediateRoot(deleteEmptyObjects bool) common.Hash { s.AccountUpdates += time.Since(start) if s.prefetcher != nil { - s.prefetcher.used(common.Hash{}, s.originalRoot, usedAddrs) + s.prefetcher.used(common.Hash{}, s.originalRoot, usedAddrs, nil) } // Track the amount of time wasted on hashing the account trie defer func(start time.Time) { s.AccountHashes += time.Since(start) }(time.Now()) @@ -933,16 +934,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()) @@ -950,42 +952,47 @@ 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) } 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{}) { @@ -994,35 +1001,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 @@ -1069,15 +1077,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 { + 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) @@ -1267,7 +1276,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. @@ -1282,8 +1291,7 @@ func (s *StateDB) commitAndFlush(block uint64, deleteEmptyObjects bool) (*stateU // If trie database is enabled, commit the state update as a new layer if db := s.db.TrieDB(); db != nil { start := time.Now() - set := triestate.New(ret.accountsOrigin, ret.storagesOrigin) - if err := db.Update(ret.root, ret.originRoot, block, ret.nodes, set); err != nil { + if err := db.Update(ret.root, ret.originRoot, block, ret.nodes, ret.stateSet()); err != nil { return nil, err } s.TrieDBCommits += time.Since(start) diff --git a/core/state/statedb_fuzz_test.go b/core/state/statedb_fuzz_test.go index 90250819e3..7cbfd9b9d7 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_hooked.go b/core/state/statedb_hooked.go new file mode 100644 index 0000000000..31bdd06b46 --- /dev/null +++ b/core/state/statedb_hooked.go @@ -0,0 +1,271 @@ +// 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 state + +import ( + "math/big" + + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/core/stateless" + "github.com/ethereum/go-ethereum/core/tracing" + "github.com/ethereum/go-ethereum/core/types" + "github.com/ethereum/go-ethereum/crypto" + "github.com/ethereum/go-ethereum/params" + "github.com/ethereum/go-ethereum/trie/utils" + "github.com/holiman/uint256" +) + +// hookedStateDB represents a statedb which emits calls to tracing-hooks +// on state operations. +type hookedStateDB struct { + inner *StateDB + hooks *tracing.Hooks +} + +// NewHookedState wraps the given stateDb with the given hooks +func NewHookedState(stateDb *StateDB, hooks *tracing.Hooks) *hookedStateDB { + s := &hookedStateDB{stateDb, hooks} + if s.hooks == nil { + s.hooks = new(tracing.Hooks) + } + return s +} + +func (s *hookedStateDB) CreateAccount(addr common.Address) { + s.inner.CreateAccount(addr) +} + +func (s *hookedStateDB) CreateContract(addr common.Address) { + s.inner.CreateContract(addr) +} + +func (s *hookedStateDB) GetBalance(addr common.Address) *uint256.Int { + return s.inner.GetBalance(addr) +} + +func (s *hookedStateDB) GetNonce(addr common.Address) uint64 { + return s.inner.GetNonce(addr) +} + +func (s *hookedStateDB) GetCodeHash(addr common.Address) common.Hash { + return s.inner.GetCodeHash(addr) +} + +func (s *hookedStateDB) GetCode(addr common.Address) []byte { + return s.inner.GetCode(addr) +} + +func (s *hookedStateDB) GetCodeSize(addr common.Address) int { + return s.inner.GetCodeSize(addr) +} + +func (s *hookedStateDB) AddRefund(u uint64) { + s.inner.AddRefund(u) +} + +func (s *hookedStateDB) SubRefund(u uint64) { + s.inner.SubRefund(u) +} + +func (s *hookedStateDB) GetRefund() uint64 { + return s.inner.GetRefund() +} + +func (s *hookedStateDB) GetCommittedState(addr common.Address, hash common.Hash) common.Hash { + return s.inner.GetCommittedState(addr, hash) +} + +func (s *hookedStateDB) GetState(addr common.Address, hash common.Hash) common.Hash { + return s.inner.GetState(addr, hash) +} + +func (s *hookedStateDB) GetStorageRoot(addr common.Address) common.Hash { + return s.inner.GetStorageRoot(addr) +} + +func (s *hookedStateDB) GetTransientState(addr common.Address, key common.Hash) common.Hash { + return s.inner.GetTransientState(addr, key) +} + +func (s *hookedStateDB) SetTransientState(addr common.Address, key, value common.Hash) { + s.inner.SetTransientState(addr, key, value) +} + +func (s *hookedStateDB) HasSelfDestructed(addr common.Address) bool { + return s.inner.HasSelfDestructed(addr) +} + +func (s *hookedStateDB) Exist(addr common.Address) bool { + return s.inner.Exist(addr) +} + +func (s *hookedStateDB) Empty(addr common.Address) bool { + return s.inner.Empty(addr) +} + +func (s *hookedStateDB) AddressInAccessList(addr common.Address) bool { + return s.inner.AddressInAccessList(addr) +} + +func (s *hookedStateDB) SlotInAccessList(addr common.Address, slot common.Hash) (addressOk bool, slotOk bool) { + return s.inner.SlotInAccessList(addr, slot) +} + +func (s *hookedStateDB) AddAddressToAccessList(addr common.Address) { + s.inner.AddAddressToAccessList(addr) +} + +func (s *hookedStateDB) AddSlotToAccessList(addr common.Address, slot common.Hash) { + s.inner.AddSlotToAccessList(addr, slot) +} + +func (s *hookedStateDB) PointCache() *utils.PointCache { + return s.inner.PointCache() +} + +func (s *hookedStateDB) Prepare(rules params.Rules, sender, coinbase common.Address, dest *common.Address, precompiles []common.Address, txAccesses types.AccessList) { + s.inner.Prepare(rules, sender, coinbase, dest, precompiles, txAccesses) +} + +func (s *hookedStateDB) RevertToSnapshot(i int) { + s.inner.RevertToSnapshot(i) +} + +func (s *hookedStateDB) Snapshot() int { + return s.inner.Snapshot() +} + +func (s *hookedStateDB) AddPreimage(hash common.Hash, bytes []byte) { + s.inner.AddPreimage(hash, bytes) +} + +func (s *hookedStateDB) Witness() *stateless.Witness { + return s.inner.Witness() +} + +func (s *hookedStateDB) SubBalance(addr common.Address, amount *uint256.Int, reason tracing.BalanceChangeReason) uint256.Int { + prev := s.inner.SubBalance(addr, amount, reason) + if s.hooks.OnBalanceChange != nil && !amount.IsZero() { + newBalance := new(uint256.Int).Sub(&prev, amount) + s.hooks.OnBalanceChange(addr, prev.ToBig(), newBalance.ToBig(), reason) + } + return prev +} + +func (s *hookedStateDB) AddBalance(addr common.Address, amount *uint256.Int, reason tracing.BalanceChangeReason) uint256.Int { + prev := s.inner.AddBalance(addr, amount, reason) + if s.hooks.OnBalanceChange != nil && !amount.IsZero() { + newBalance := new(uint256.Int).Add(&prev, amount) + s.hooks.OnBalanceChange(addr, prev.ToBig(), newBalance.ToBig(), reason) + } + return prev +} + +func (s *hookedStateDB) SetNonce(address common.Address, nonce uint64) { + s.inner.SetNonce(address, nonce) + if s.hooks.OnNonceChange != nil { + s.hooks.OnNonceChange(address, nonce-1, nonce) + } +} + +func (s *hookedStateDB) SetCode(address common.Address, code []byte) []byte { + prev := s.inner.SetCode(address, code) + if s.hooks.OnCodeChange != nil { + prevHash := types.EmptyCodeHash + if len(prev) != 0 { + prevHash = crypto.Keccak256Hash(prev) + } + s.hooks.OnCodeChange(address, prevHash, prev, crypto.Keccak256Hash(code), code) + } + return prev +} + +func (s *hookedStateDB) SetState(address common.Address, key common.Hash, value common.Hash) common.Hash { + prev := s.inner.SetState(address, key, value) + if s.hooks.OnStorageChange != nil && prev != value { + s.hooks.OnStorageChange(address, key, prev, value) + } + return prev +} + +func (s *hookedStateDB) SelfDestruct(address common.Address) uint256.Int { + var prevCode []byte + var prevCodeHash common.Hash + + if s.hooks.OnCodeChange != nil { + prevCode = s.inner.GetCode(address) + prevCodeHash = s.inner.GetCodeHash(address) + } + + prev := s.inner.SelfDestruct(address) + + if s.hooks.OnBalanceChange != nil && !prev.IsZero() { + s.hooks.OnBalanceChange(address, prev.ToBig(), new(big.Int), tracing.BalanceDecreaseSelfdestruct) + } + + if s.hooks.OnCodeChange != nil && len(prevCode) > 0 { + s.hooks.OnCodeChange(address, prevCodeHash, prevCode, types.EmptyCodeHash, nil) + } + + return prev +} + +func (s *hookedStateDB) SelfDestruct6780(address common.Address) (uint256.Int, bool) { + var prevCode []byte + var prevCodeHash common.Hash + + if s.hooks.OnCodeChange != nil { + prevCodeHash = s.inner.GetCodeHash(address) + prevCode = s.inner.GetCode(address) + } + + prev, changed := s.inner.SelfDestruct6780(address) + + if s.hooks.OnBalanceChange != nil && changed && !prev.IsZero() { + s.hooks.OnBalanceChange(address, prev.ToBig(), new(big.Int), tracing.BalanceDecreaseSelfdestruct) + } + + if s.hooks.OnCodeChange != nil && changed && len(prevCode) > 0 { + s.hooks.OnCodeChange(address, prevCodeHash, prevCode, types.EmptyCodeHash, nil) + } + + return prev, changed +} + +func (s *hookedStateDB) AddLog(log *types.Log) { + // The inner will modify the log (add fields), so invoke that first + s.inner.AddLog(log) + if s.hooks.OnLog != nil { + s.hooks.OnLog(log) + } +} + +func (s *hookedStateDB) Finalise(deleteEmptyObjects bool) { + defer s.inner.Finalise(deleteEmptyObjects) + if s.hooks.OnBalanceChange == nil { + return + } + for addr := range s.inner.journal.dirties { + obj := s.inner.stateObjects[addr] + if obj != nil && obj.selfDestructed { + // If ether was sent to account post-selfdestruct it is burnt. + if bal := obj.Balance(); bal.Sign() != 0 { + s.hooks.OnBalanceChange(addr, bal.ToBig(), new(big.Int), tracing.BalanceDecreaseSelfdestructBurn) + } + } + } +} diff --git a/core/state/statedb_hooked_test.go b/core/state/statedb_hooked_test.go new file mode 100644 index 0000000000..5f82ed06d0 --- /dev/null +++ b/core/state/statedb_hooked_test.go @@ -0,0 +1,130 @@ +// 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 state + +import ( + "fmt" + "math/big" + "testing" + + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/core/tracing" + "github.com/ethereum/go-ethereum/core/types" + "github.com/holiman/uint256" +) + +// This method tests that the 'burn' from sending-to-selfdestructed accounts +// is accounted for. +// (There is also a higher-level test in eth/tracers: TestSupplySelfDestruct ) +func TestBurn(t *testing.T) { + // Note: burn can happen even after EIP-6780, if within one single transaction, + // the following occur: + // 1. contract B creates contract A + // 2. contract A is destructed + // 3. contract B sends ether to A + + var burned = new(uint256.Int) + s, _ := New(types.EmptyRootHash, NewDatabaseForTesting()) + hooked := NewHookedState(s, &tracing.Hooks{ + OnBalanceChange: func(addr common.Address, prev, new *big.Int, reason tracing.BalanceChangeReason) { + if reason == tracing.BalanceDecreaseSelfdestructBurn { + burned.Add(burned, uint256.MustFromBig(prev)) + } + }, + }) + createAndDestroy := func(addr common.Address) { + hooked.AddBalance(addr, uint256.NewInt(100), tracing.BalanceChangeUnspecified) + hooked.CreateContract(addr) + hooked.SelfDestruct(addr) + // sanity-check that balance is now 0 + if have, want := hooked.GetBalance(addr), new(uint256.Int); !have.Eq(want) { + t.Fatalf("post-destruct balance wrong: have %v want %v", have, want) + } + } + addA := common.Address{0xaa} + addB := common.Address{0xbb} + addC := common.Address{0xcc} + + // Tx 1: create and destroy address A and B in one tx + createAndDestroy(addA) + createAndDestroy(addB) + hooked.AddBalance(addA, uint256.NewInt(200), tracing.BalanceChangeUnspecified) + hooked.AddBalance(addB, uint256.NewInt(200), tracing.BalanceChangeUnspecified) + hooked.Finalise(true) + + // Tx 2: create and destroy address C, then commit + createAndDestroy(addC) + hooked.AddBalance(addC, uint256.NewInt(200), tracing.BalanceChangeUnspecified) + hooked.Finalise(true) + + s.Commit(0, false) + if have, want := burned, uint256.NewInt(600); !have.Eq(want) { + t.Fatalf("burn-count wrong, have %v want %v", have, want) + } +} + +// TestHooks is a basic sanity-check of all hooks +func TestHooks(t *testing.T) { + inner, _ := New(types.EmptyRootHash, NewDatabaseForTesting()) + inner.SetTxContext(common.Hash{0x11}, 100) // For the log + var result []string + var wants = []string{ + "0xaa00000000000000000000000000000000000000.balance: 0->100 (BalanceChangeUnspecified)", + "0xaa00000000000000000000000000000000000000.balance: 100->50 (BalanceChangeTransfer)", + "0xaa00000000000000000000000000000000000000.nonce: 1336->1337", + "0xaa00000000000000000000000000000000000000.code: (0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470) ->0x1325 (0xa12ae05590de0c93a00bc7ac773c2fdb621e44f814985e72194f921c0050f728)", + "0xaa00000000000000000000000000000000000000.storage slot 0x0000000000000000000000000000000000000000000000000000000000000001: 0x0000000000000000000000000000000000000000000000000000000000000000 ->0x0000000000000000000000000000000000000000000000000000000000000011", + "0xaa00000000000000000000000000000000000000.storage slot 0x0000000000000000000000000000000000000000000000000000000000000001: 0x0000000000000000000000000000000000000000000000000000000000000011 ->0x0000000000000000000000000000000000000000000000000000000000000022", + "log 100", + } + emitF := func(format string, a ...any) { + result = append(result, fmt.Sprintf(format, a...)) + } + sdb := NewHookedState(inner, &tracing.Hooks{ + OnBalanceChange: func(addr common.Address, prev, new *big.Int, reason tracing.BalanceChangeReason) { + emitF("%v.balance: %v->%v (%v)", addr, prev, new, reason) + }, + OnNonceChange: func(addr common.Address, prev, new uint64) { + emitF("%v.nonce: %v->%v", addr, prev, new) + }, + OnCodeChange: func(addr common.Address, prevCodeHash common.Hash, prevCode []byte, codeHash common.Hash, code []byte) { + emitF("%v.code: %#x (%v) ->%#x (%v)", addr, prevCode, prevCodeHash, code, codeHash) + }, + OnStorageChange: func(addr common.Address, slot common.Hash, prev, new common.Hash) { + emitF("%v.storage slot %v: %v ->%v", addr, slot, prev, new) + }, + OnLog: func(log *types.Log) { + emitF("log %v", log.TxIndex) + }, + }) + sdb.AddBalance(common.Address{0xaa}, uint256.NewInt(100), tracing.BalanceChangeUnspecified) + sdb.SubBalance(common.Address{0xaa}, uint256.NewInt(50), tracing.BalanceChangeTransfer) + sdb.SetNonce(common.Address{0xaa}, 1337) + sdb.SetCode(common.Address{0xaa}, []byte{0x13, 37}) + sdb.SetState(common.Address{0xaa}, common.HexToHash("0x01"), common.HexToHash("0x11")) + sdb.SetState(common.Address{0xaa}, common.HexToHash("0x01"), common.HexToHash("0x22")) + sdb.SetTransientState(common.Address{0xaa}, common.HexToHash("0x02"), common.HexToHash("0x01")) + sdb.SetTransientState(common.Address{0xaa}, common.HexToHash("0x02"), common.HexToHash("0x02")) + sdb.AddLog(&types.Log{ + Address: common.Address{0xbb}, + }) + for i, want := range wants { + if have := result[i]; have != want { + t.Fatalf("error event %d, have\n%v\nwant%v\n", i, have, want) + } + } +} diff --git a/core/state/statedb_test.go b/core/state/statedb_test.go index 9441834c6a..37141e90b0 100644 --- a/core/state/statedb_test.go +++ b/core/state/statedb_test.go @@ -170,7 +170,7 @@ func TestCopy(t *testing.T) { for i := byte(0); i < 255; i++ { obj := orig.getOrNewStateObject(common.BytesToAddress([]byte{i})) - obj.AddBalance(uint256.NewInt(uint64(i)), tracing.BalanceChangeUnspecified) + obj.AddBalance(uint256.NewInt(uint64(i))) orig.updateStateObject(obj) } orig.Finalise(false) @@ -187,9 +187,9 @@ func TestCopy(t *testing.T) { copyObj := copy.getOrNewStateObject(common.BytesToAddress([]byte{i})) ccopyObj := ccopy.getOrNewStateObject(common.BytesToAddress([]byte{i})) - origObj.AddBalance(uint256.NewInt(2*uint64(i)), tracing.BalanceChangeUnspecified) - copyObj.AddBalance(uint256.NewInt(3*uint64(i)), tracing.BalanceChangeUnspecified) - ccopyObj.AddBalance(uint256.NewInt(4*uint64(i)), tracing.BalanceChangeUnspecified) + origObj.AddBalance(uint256.NewInt(2 * uint64(i))) + copyObj.AddBalance(uint256.NewInt(3 * uint64(i))) + ccopyObj.AddBalance(uint256.NewInt(4 * uint64(i))) orig.updateStateObject(origObj) copy.updateStateObject(copyObj) @@ -236,7 +236,7 @@ func TestCopyWithDirtyJournal(t *testing.T) { // Fill up the initial states for i := byte(0); i < 255; i++ { obj := orig.getOrNewStateObject(common.BytesToAddress([]byte{i})) - obj.AddBalance(uint256.NewInt(uint64(i)), tracing.BalanceChangeUnspecified) + obj.AddBalance(uint256.NewInt(uint64(i))) obj.data.Root = common.HexToHash("0xdeadbeef") orig.updateStateObject(obj) } @@ -246,7 +246,9 @@ func TestCopyWithDirtyJournal(t *testing.T) { // modify all in memory without finalizing for i := byte(0); i < 255; i++ { obj := orig.getOrNewStateObject(common.BytesToAddress([]byte{i})) - obj.SubBalance(uint256.NewInt(uint64(i)), tracing.BalanceChangeUnspecified) + amount := uint256.NewInt(uint64(i)) + obj.SetBalance(new(uint256.Int).Sub(obj.Balance(), amount)) + orig.updateStateObject(obj) } cpy := orig.Copy() @@ -280,7 +282,7 @@ func TestCopyObjectState(t *testing.T) { // Fill up the initial states for i := byte(0); i < 5; i++ { obj := orig.getOrNewStateObject(common.BytesToAddress([]byte{i})) - obj.AddBalance(uint256.NewInt(uint64(i)), tracing.BalanceChangeUnspecified) + obj.AddBalance(uint256.NewInt(uint64(i))) obj.data.Root = common.HexToHash("0xdeadbeef") orig.updateStateObject(obj) } @@ -648,11 +650,7 @@ func (test *snapshotTest) checkEqual(state, checkstate *StateDB) error { { have := state.transientStorage want := checkstate.transientStorage - eq := maps.EqualFunc(have, want, - func(a Storage, b Storage) bool { - return maps.Equal(a, b) - }) - if !eq { + if !maps.EqualFunc(have, want, maps.Equal) { return fmt.Errorf("transient storage differs ,have\n%v\nwant\n%v", have.PrettyPrint(), want.PrettyPrint()) @@ -981,8 +979,8 @@ func testMissingTrieNodes(t *testing.T, scheme string) { ) if scheme == rawdb.PathScheme { tdb = triedb.NewDatabase(memDb, &triedb.Config{PathDB: &pathdb.Config{ - CleanCacheSize: 0, - DirtyCacheSize: 0, + CleanCacheSize: 0, + WriteBufferSize: 0, }}) // disable caching } else { tdb = triedb.NewDatabase(memDb, &triedb.Config{HashDB: &hashdb.Config{ @@ -1032,12 +1030,8 @@ func testMissingTrieNodes(t *testing.T, scheme string) { func TestStateDBAccessList(t *testing.T) { // Some helpers - addr := func(a string) common.Address { - return common.HexToAddress(a) - } - slot := func(a string) common.Hash { - return common.HexToHash(a) - } + addr := common.HexToAddress + slot := common.HexToHash db := NewDatabaseForTesting() state, _ := New(types.EmptyRootHash, db) @@ -1303,12 +1297,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 f3e6af997e..a320b72f11 100644 --- a/core/state/stateupdate.go +++ b/core/state/stateupdate.go @@ -17,9 +17,12 @@ package state import ( + "maps" + "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/trie/trienode" + "github.com/ethereum/go-ethereum/triedb" ) // contractCode represents a contract code with associated metadata. @@ -32,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. } @@ -51,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 @@ -70,7 +73,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) @@ -81,8 +83,12 @@ 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 accountsOrigin[addr] = op.origin + + if len(op.storages) > 0 { + storages[addrHash] = op.storages + } if len(op.storagesOrigin) > 0 { storagesOrigin[addr] = op.storagesOrigin } @@ -94,35 +100,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 len(op.storagesOrigin) > 0 { - origin := storagesOrigin[addr] - if origin == nil { - storagesOrigin[addr] = op.storagesOrigin - continue + if _, exist := storages[addrHash]; !exist { + storages[addrHash] = op.storages + } else { + maps.Copy(storages[addrHash], op.storages) } - for key, slot := range op.storagesOrigin { - if _, found := origin[key]; !found { - origin[key] = slot + } + // Aggregate the storage original values. If the slot is already present + // in aggregated storagesOrigin set, skip it. + if len(op.storagesOrigin) > 0 { + origin, exist := storagesOrigin[addr] + if !exist { + storagesOrigin[addr] = op.storagesOrigin + } else { + for key, slot := range op.storagesOrigin { + if _, found := origin[key]; !found { + origin[key] = slot + } } } - storagesOrigin[addr] = origin } } return &stateUpdate{ originRoot: types.TrieRootHash(originRoot), root: types.TrieRootHash(root), - destructs: destructs, accounts: accounts, accountsOrigin: accountsOrigin, storages: storages, @@ -131,3 +143,16 @@ func newStateUpdate(originRoot common.Hash, root common.Hash, deletes map[common nodes: nodes, } } + +// stateSet converts the current stateUpdate object into a triedb.StateSet +// object. This function extracts the necessary data from the stateUpdate +// struct and formats it into the StateSet structure consumed by the triedb +// package. +func (sc *stateUpdate) stateSet() *triedb.StateSet { + return &triedb.StateSet{ + Accounts: sc.accounts, + AccountsOrigin: sc.accountsOrigin, + Storages: sc.storages, + StoragesOrigin: sc.storagesOrigin, + } +} diff --git a/core/state/sync_test.go b/core/state/sync_test.go index cc15422c0c..efa56f8860 100644 --- a/core/state/sync_test.go +++ b/core/state/sync_test.go @@ -22,7 +22,6 @@ import ( "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/core/rawdb" - "github.com/ethereum/go-ethereum/core/tracing" "github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/crypto" "github.com/ethereum/go-ethereum/ethdb" @@ -62,7 +61,7 @@ func makeTestState(scheme string) (ethdb.Database, Database, *triedb.Database, c obj := state.getOrNewStateObject(common.BytesToAddress([]byte{i})) acc := &testAccount{address: common.BytesToAddress([]byte{i})} - obj.AddBalance(uint256.NewInt(uint64(11*i)), tracing.BalanceChangeUnspecified) + obj.AddBalance(uint256.NewInt(uint64(11 * i))) acc.balance = uint256.NewInt(uint64(11 * i)) obj.SetNonce(uint64(42 * i)) @@ -207,7 +206,11 @@ func testIterativeStateSync(t *testing.T, count int, commit bool, bypath bool, s for i := 0; i < len(codes); i++ { codeElements = append(codeElements, stateElement{code: codes[i]}) } - reader, err := ndb.Reader(srcRoot) + reader, err := ndb.NodeReader(srcRoot) + 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) } @@ -217,8 +220,8 @@ func testIterativeStateSync(t *testing.T, count int, commit bool, bypath bool, s 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} @@ -326,7 +329,11 @@ func testIterativeDelayedStateSync(t *testing.T, scheme string) { for i := 0; i < len(codes); i++ { codeElements = append(codeElements, stateElement{code: codes[i]}) } - reader, err := ndb.Reader(srcRoot) + reader, err := ndb.NodeReader(srcRoot) + 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) } @@ -337,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} @@ -430,7 +437,11 @@ func testIterativeRandomStateSync(t *testing.T, count int, scheme string) { for _, hash := range codes { codeQueue[hash] = struct{}{} } - reader, err := ndb.Reader(srcRoot) + reader, err := ndb.NodeReader(srcRoot) + 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) } @@ -439,8 +450,8 @@ func testIterativeRandomStateSync(t *testing.T, count int, 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}) @@ -523,7 +534,11 @@ func testIterativeRandomDelayedStateSync(t *testing.T, scheme string) { for _, hash := range codes { codeQueue[hash] = struct{}{} } - reader, err := ndb.Reader(srcRoot) + reader, err := ndb.NodeReader(srcRoot) + 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) } @@ -534,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}) @@ -628,10 +643,14 @@ func testIncompleteStateSync(t *testing.T, scheme string) { addedPaths []string addedHashes []common.Hash ) - reader, err := ndb.Reader(srcRoot) + reader, err := ndb.NodeReader(srcRoot) 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) @@ -650,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}) @@ -714,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 458e965a77..6f492cf9f2 100644 --- a/core/state/trie_prefetcher.go +++ b/core/state/trie_prefetcher.go @@ -47,21 +47,21 @@ type triePrefetcher struct { term chan struct{} // Channel to signal interruption noreads bool // Whether to ignore state-read-only prefetch requests - deliveryMissMeter metrics.Meter + deliveryMissMeter *metrics.Meter - accountLoadReadMeter metrics.Meter - accountLoadWriteMeter metrics.Meter - accountDupReadMeter metrics.Meter - accountDupWriteMeter metrics.Meter - accountDupCrossMeter metrics.Meter - accountWasteMeter metrics.Meter + accountLoadReadMeter *metrics.Meter + accountLoadWriteMeter *metrics.Meter + accountDupReadMeter *metrics.Meter + accountDupWriteMeter *metrics.Meter + accountDupCrossMeter *metrics.Meter + accountWasteMeter *metrics.Meter - storageLoadReadMeter metrics.Meter - storageLoadWriteMeter metrics.Meter - storageDupReadMeter metrics.Meter - storageDupWriteMeter metrics.Meter - storageDupCrossMeter metrics.Meter - storageWasteMeter metrics.Meter + storageLoadReadMeter *metrics.Meter + storageLoadWriteMeter *metrics.Meter + storageDupReadMeter *metrics.Meter + storageDupWriteMeter *metrics.Meter + storageDupCrossMeter *metrics.Meter + storageWasteMeter *metrics.Meter } func newTriePrefetcher(db Database, root common.Hash, namespace string, noreads bool) *triePrefetcher { @@ -111,38 +111,38 @@ func (p *triePrefetcher) terminate(async bool) { // report aggregates the pre-fetching and usage metrics and reports them. func (p *triePrefetcher) report() { - if !metrics.Enabled { + if !metrics.Enabled() { return } for _, fetcher := range p.fetchers { fetcher.wait() // ensure the fetcher's idle before poking in its internals if fetcher.root == p.root { - p.accountLoadReadMeter.Mark(int64(len(fetcher.seenRead))) - p.accountLoadWriteMeter.Mark(int64(len(fetcher.seenWrite))) + p.accountLoadReadMeter.Mark(int64(len(fetcher.seenReadAddr))) + p.accountLoadWriteMeter.Mark(int64(len(fetcher.seenWriteAddr))) p.accountDupReadMeter.Mark(int64(fetcher.dupsRead)) p.accountDupWriteMeter.Mark(int64(fetcher.dupsWrite)) p.accountDupCrossMeter.Mark(int64(fetcher.dupsCross)) - for _, key := range fetcher.used { - delete(fetcher.seenRead, string(key)) - delete(fetcher.seenWrite, string(key)) + for _, key := range fetcher.usedAddr { + delete(fetcher.seenReadAddr, key) + delete(fetcher.seenWriteAddr, key) } - p.accountWasteMeter.Mark(int64(len(fetcher.seenRead) + len(fetcher.seenWrite))) + p.accountWasteMeter.Mark(int64(len(fetcher.seenReadAddr) + len(fetcher.seenWriteAddr))) } else { - p.storageLoadReadMeter.Mark(int64(len(fetcher.seenRead))) - p.storageLoadWriteMeter.Mark(int64(len(fetcher.seenWrite))) + p.storageLoadReadMeter.Mark(int64(len(fetcher.seenReadSlot))) + p.storageLoadWriteMeter.Mark(int64(len(fetcher.seenWriteSlot))) p.storageDupReadMeter.Mark(int64(fetcher.dupsRead)) p.storageDupWriteMeter.Mark(int64(fetcher.dupsWrite)) p.storageDupCrossMeter.Mark(int64(fetcher.dupsCross)) - for _, key := range fetcher.used { - delete(fetcher.seenRead, string(key)) - delete(fetcher.seenWrite, string(key)) + for _, key := range fetcher.usedSlot { + delete(fetcher.seenReadSlot, key) + delete(fetcher.seenWriteSlot, key) } - p.storageWasteMeter.Mark(int64(len(fetcher.seenRead) + len(fetcher.seenWrite))) + p.storageWasteMeter.Mark(int64(len(fetcher.seenReadSlot) + len(fetcher.seenWriteSlot))) } } } @@ -158,7 +158,7 @@ func (p *triePrefetcher) report() { // upon the same contract, the parameters invoking this method may be // repeated. // 2. Finalize of the main account trie. This happens only once per block. -func (p *triePrefetcher) prefetch(owner common.Hash, root common.Hash, addr common.Address, keys [][]byte, read bool) error { +func (p *triePrefetcher) prefetch(owner common.Hash, root common.Hash, addr common.Address, addrs []common.Address, slots []common.Hash, read bool) error { // If the state item is only being read, but reads are disabled, return if read && p.noreads { return nil @@ -175,7 +175,7 @@ func (p *triePrefetcher) prefetch(owner common.Hash, root common.Hash, addr comm fetcher = newSubfetcher(p.db, p.root, owner, root, addr) p.fetchers[id] = fetcher } - return fetcher.schedule(keys, read) + return fetcher.schedule(addrs, slots, read) } // trie returns the trie matching the root hash, blocking until the fetcher of @@ -195,10 +195,12 @@ func (p *triePrefetcher) trie(owner common.Hash, root common.Hash) Trie { // used marks a batch of state items used to allow creating statistics as to // how useful or wasteful the fetcher is. -func (p *triePrefetcher) used(owner common.Hash, root common.Hash, used [][]byte) { +func (p *triePrefetcher) used(owner common.Hash, root common.Hash, usedAddr []common.Address, usedSlot []common.Hash) { if fetcher := p.fetchers[p.trieID(owner, root)]; fetcher != nil { fetcher.wait() // ensure the fetcher's idle before poking in its internals - fetcher.used = append(fetcher.used, used...) + + fetcher.usedAddr = append(fetcher.usedAddr, usedAddr...) + fetcher.usedSlot = append(fetcher.usedSlot, usedSlot...) } } @@ -235,44 +237,50 @@ type subfetcher struct { stop chan struct{} // Channel to interrupt processing term chan struct{} // Channel to signal interruption - seenRead map[string]struct{} // Tracks the entries already loaded via read operations - seenWrite map[string]struct{} // Tracks the entries already loaded via write operations + seenReadAddr map[common.Address]struct{} // Tracks the accounts already loaded via read operations + seenWriteAddr map[common.Address]struct{} // Tracks the accounts already loaded via write operations + seenReadSlot map[common.Hash]struct{} // Tracks the storage already loaded via read operations + seenWriteSlot map[common.Hash]struct{} // Tracks the storage already loaded via write operations dupsRead int // Number of duplicate preload tasks via reads only dupsWrite int // Number of duplicate preload tasks via writes only dupsCross int // Number of duplicate preload tasks via read-write-crosses - used [][]byte // Tracks the entries used in the end + usedAddr []common.Address // Tracks the accounts used in the end + usedSlot []common.Hash // Tracks the storage used in the end } // subfetcherTask is a trie path to prefetch, tagged with whether it originates // from a read or a write request. type subfetcherTask struct { read bool - key []byte + addr *common.Address + slot *common.Hash } // newSubfetcher creates a goroutine to prefetch state items belonging to a // particular root hash. func newSubfetcher(db Database, state common.Hash, owner common.Hash, root common.Hash, addr common.Address) *subfetcher { sf := &subfetcher{ - db: db, - state: state, - owner: owner, - root: root, - addr: addr, - wake: make(chan struct{}, 1), - stop: make(chan struct{}), - term: make(chan struct{}), - seenRead: make(map[string]struct{}), - seenWrite: make(map[string]struct{}), + db: db, + state: state, + owner: owner, + root: root, + addr: addr, + wake: make(chan struct{}, 1), + stop: make(chan struct{}), + term: make(chan struct{}), + seenReadAddr: make(map[common.Address]struct{}), + seenWriteAddr: make(map[common.Address]struct{}), + seenReadSlot: make(map[common.Hash]struct{}), + seenWriteSlot: make(map[common.Hash]struct{}), } go sf.loop() return sf } // schedule adds a batch of trie keys to the queue to prefetch. -func (sf *subfetcher) schedule(keys [][]byte, read bool) error { +func (sf *subfetcher) schedule(addrs []common.Address, slots []common.Hash, read bool) error { // Ensure the subfetcher is still alive select { case <-sf.term: @@ -281,8 +289,11 @@ func (sf *subfetcher) schedule(keys [][]byte, read bool) error { } // Append the tasks to the current queue sf.lock.Lock() - for _, key := range keys { - sf.tasks = append(sf.tasks, &subfetcherTask{read: read, key: key}) + for _, addr := range addrs { + sf.tasks = append(sf.tasks, &subfetcherTask{read: read, addr: &addr}) + } + for _, slot := range slots { + sf.tasks = append(sf.tasks, &subfetcherTask{read: read, slot: &slot}) } sf.lock.Unlock() @@ -378,35 +389,66 @@ func (sf *subfetcher) loop() { sf.lock.Unlock() for _, task := range tasks { - key := string(task.key) - if task.read { - if _, ok := sf.seenRead[key]; ok { - sf.dupsRead++ - continue - } - if _, ok := sf.seenWrite[key]; ok { - sf.dupsCross++ - continue + if task.addr != nil { + key := *task.addr + if task.read { + if _, ok := sf.seenReadAddr[key]; ok { + sf.dupsRead++ + continue + } + if _, ok := sf.seenWriteAddr[key]; ok { + sf.dupsCross++ + continue + } + } else { + if _, ok := sf.seenReadAddr[key]; ok { + sf.dupsCross++ + continue + } + if _, ok := sf.seenWriteAddr[key]; ok { + sf.dupsWrite++ + continue + } } } else { - if _, ok := sf.seenRead[key]; ok { - sf.dupsCross++ - continue - } - if _, ok := sf.seenWrite[key]; ok { - sf.dupsWrite++ - continue + key := *task.slot + if task.read { + if _, ok := sf.seenReadSlot[key]; ok { + sf.dupsRead++ + continue + } + if _, ok := sf.seenWriteSlot[key]; ok { + sf.dupsCross++ + continue + } + } else { + if _, ok := sf.seenReadSlot[key]; ok { + sf.dupsCross++ + continue + } + if _, ok := sf.seenWriteSlot[key]; ok { + sf.dupsWrite++ + continue + } } } - if len(task.key) == common.AddressLength { - sf.trie.GetAccount(common.BytesToAddress(task.key)) + if task.addr != nil { + sf.trie.GetAccount(*task.addr) } else { - sf.trie.GetStorage(sf.addr, task.key) + sf.trie.GetStorage(sf.addr, (*task.slot)[:]) } if task.read { - sf.seenRead[key] = struct{}{} + if task.addr != nil { + sf.seenReadAddr[*task.addr] = struct{}{} + } else { + sf.seenReadSlot[*task.slot] = struct{}{} + } } else { - sf.seenWrite[key] = struct{}{} + if task.addr != nil { + sf.seenWriteAddr[*task.addr] = struct{}{} + } else { + sf.seenWriteSlot[*task.slot] = struct{}{} + } } } diff --git a/core/state/trie_prefetcher_test.go b/core/state/trie_prefetcher_test.go index 529b42d39c..d96727704c 100644 --- a/core/state/trie_prefetcher_test.go +++ b/core/state/trie_prefetcher_test.go @@ -53,12 +53,12 @@ func TestUseAfterTerminate(t *testing.T) { prefetcher := newTriePrefetcher(db.db, db.originalRoot, "", true) skey := common.HexToHash("aaa") - if err := prefetcher.prefetch(common.Hash{}, db.originalRoot, common.Address{}, [][]byte{skey.Bytes()}, false); err != nil { + if err := prefetcher.prefetch(common.Hash{}, db.originalRoot, common.Address{}, nil, []common.Hash{skey}, false); err != nil { t.Errorf("Prefetch failed before terminate: %v", err) } prefetcher.terminate(false) - if err := prefetcher.prefetch(common.Hash{}, db.originalRoot, common.Address{}, [][]byte{skey.Bytes()}, false); err == nil { + if err := prefetcher.prefetch(common.Hash{}, db.originalRoot, common.Address{}, nil, []common.Hash{skey}, false); err == nil { t.Errorf("Prefetch succeeded after terminate: %v", err) } if tr := prefetcher.trie(common.Hash{}, db.originalRoot); tr == nil { @@ -90,14 +90,10 @@ func TestVerklePrefetcher(t *testing.T) { fetcher := newTriePrefetcher(sdb, root, "", false) // Read account - fetcher.prefetch(common.Hash{}, root, common.Address{}, [][]byte{ - addr.Bytes(), - }, false) + fetcher.prefetch(common.Hash{}, root, common.Address{}, []common.Address{addr}, nil, false) // Read storage slot - fetcher.prefetch(crypto.Keccak256Hash(addr.Bytes()), sRoot, addr, [][]byte{ - skey.Bytes(), - }, false) + fetcher.prefetch(crypto.Keccak256Hash(addr.Bytes()), sRoot, addr, nil, []common.Hash{skey}, false) fetcher.terminate(false) accountTrie := fetcher.trie(common.Hash{}, root) diff --git a/core/state_prefetcher.go b/core/state_prefetcher.go index 31405fa078..805df5ef62 100644 --- a/core/state_prefetcher.go +++ b/core/state_prefetcher.go @@ -49,7 +49,7 @@ func (p *statePrefetcher) Prefetch(block *types.Block, statedb *state.StateDB, c header = block.Header() 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, statedb, p.config, cfg) signer = types.MakeSigner(p.config, header.Number, header.Time) ) // Iterate over and process the individual transactions @@ -65,7 +65,10 @@ func (p *statePrefetcher) Prefetch(block *types.Block, statedb *state.StateDB, c return // Also invalid block, bail out } statedb.SetTxContext(tx.Hash(), i) - if err := precacheTransaction(msg, p.config, gaspool, statedb, header, evm); err != nil { + + // We attempt to apply a transaction. The goal is not to execute + // the transaction successfully, rather to warm up touched data slots. + if _, err := ApplyMessage(evm, msg, gaspool); err != nil { return // Ugh, something went horribly wrong, bail out } // If we're pre-byzantium, pre-load trie nodes for the intermediate root @@ -78,14 +81,3 @@ func (p *statePrefetcher) Prefetch(block *types.Block, statedb *state.StateDB, c statedb.IntermediateRoot(true) } } - -// 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) - return err -} diff --git a/core/state_processor.go b/core/state_processor.go index fe0304e81d..3eb83a673a 100644 --- a/core/state_processor.go +++ b/core/state_processor.go @@ -23,6 +23,7 @@ import ( "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/consensus/misc" "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" @@ -74,13 +75,18 @@ func (p *StateProcessor) Process(block *types.Block, statedb *state.StateDB, cfg ) // Apply pre-execution system calls. + var tracingStateDB = vm.StateDB(statedb) + if hooks := cfg.Tracer; hooks != nil { + tracingStateDB = state.NewHookedState(statedb, hooks) + } context = NewEVMBlockContext(header, p.chain, nil) - vmenv := vm.NewEVM(context, vm.TxContext{}, statedb, p.config, cfg) + evm := vm.NewEVM(context, tracingStateDB, p.config, cfg) + if beaconRoot := block.BeaconRoot(); beaconRoot != nil { - ProcessBeaconBlockRoot(*beaconRoot, vmenv, statedb) + ProcessBeaconBlockRoot(*beaconRoot, evm) } if p.config.IsPrague(block.Number(), block.Time()) { - ProcessParentBlockHash(block.ParentHash(), vmenv, statedb) + ProcessParentBlockHash(block.ParentHash(), evm) } // Iterate over and process the individual transactions @@ -91,33 +97,29 @@ 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) + receipt, err := ApplyTransactionWithEVM(msg, gp, statedb, blockNumber, blockHash, tx, usedGas, evm) if err != nil { return nil, fmt.Errorf("could not apply tx %d [%v]: %w", i, tx.Hash().Hex(), err) } receipts = append(receipts, receipt) allLogs = append(allLogs, receipt.Logs...) } - // Read requests if Prague is enabled. var requests [][]byte if p.config.IsPrague(block.Number(), block.Time()) { - // EIP-6110 deposits - depositRequests, err := ParseDepositLogs(allLogs, p.config) - if err != nil { + requests = [][]byte{} + // EIP-6110 + if err := ParseDepositLogs(&requests, allLogs, p.config); err != nil { return nil, err } - requests = append(requests, depositRequests) - // EIP-7002 withdrawals - withdrawalRequests := ProcessWithdrawalQueue(vmenv, statedb) - requests = append(requests, withdrawalRequests) - // EIP-7251 consolidations - consolidationRequests := ProcessConsolidationQueue(vmenv, statedb) - 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) - p.chain.engine.Finalize(p.chain, header, statedb, block.Body()) + p.chain.engine.Finalize(p.chain, header, tracingStateDB, block.Body()) return &ProcessResult{ Receipts: receipts, @@ -130,31 +132,26 @@ 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) (receipt *types.Receipt, err error) { - if evm.Config.Tracer != nil && evm.Config.Tracer.OnTxStart != nil { - evm.Config.Tracer.OnTxStart(evm.GetVMContext(), tx, msg.From) - if evm.Config.Tracer.OnTxEnd != nil { - defer func() { - evm.Config.Tracer.OnTxEnd(receipt, err) - }() +func ApplyTransactionWithEVM(msg *Message, gp *GasPool, statedb *state.StateDB, blockNumber *big.Int, blockHash common.Hash, tx *types.Transaction, usedGas *uint64, evm *vm.EVM) (receipt *types.Receipt, err error) { + if hooks := evm.Config.Tracer; hooks != nil { + if hooks.OnTxStart != nil { + hooks.OnTxStart(evm.GetVMContext(), tx, msg.From) + } + if hooks.OnTxEnd != nil { + defer func() { hooks.OnTxEnd(receipt, err) }() } } - // Create a new context to be used in the EVM environment. - txContext := NewEVMTxContext(msg) - evm.Reset(txContext, statedb) - // 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) { - statedb.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 @@ -203,25 +200,20 @@ 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) (*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) (*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) - return ApplyTransactionWithEVM(msg, config, gp, statedb, header.Number, header.Hash(), tx, usedGas, vmenv) + return ApplyTransactionWithEVM(msg, gp, statedb, header.Number, header.Hash(), tx, usedGas, evm) } // 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 *state.StateDB) { - if tracer := vmenv.Config.Tracer; tracer != nil { - if tracer.OnSystemCallStart != nil { - tracer.OnSystemCallStart() - } +func ProcessBeaconBlockRoot(beaconRoot common.Hash, evm *vm.EVM) { + if tracer := evm.Config.Tracer; tracer != nil { + onSystemCallStart(tracer, evm.GetVMContext()) if tracer.OnSystemCallEnd != nil { defer tracer.OnSystemCallEnd() } @@ -235,19 +227,17 @@ func ProcessBeaconBlockRoot(beaconRoot common.Hash, vmenv *vm.EVM, statedb *stat 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 *state.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() } @@ -261,34 +251,31 @@ func ProcessParentBlockHash(prevHash common.Hash, vmenv *vm.EVM, statedb *state. 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 *state.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 *state.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 *state.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, @@ -297,30 +284,44 @@ func processRequestsSystemCall(vmenv *vm.EVM, statedb *state.StateDB, requestTyp 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_processor_test.go b/core/state_processor_test.go index 9678f2828c..2f841eb64a 100644 --- a/core/state_processor_test.go +++ b/core/state_processor_test.go @@ -18,26 +18,22 @@ package core import ( "crypto/ecdsa" - "encoding/binary" + "math" "math/big" "testing" "github.com/ethereum/go-ethereum/common" - "github.com/ethereum/go-ethereum/common/math" "github.com/ethereum/go-ethereum/consensus" "github.com/ethereum/go-ethereum/consensus/beacon" "github.com/ethereum/go-ethereum/consensus/ethash" "github.com/ethereum/go-ethereum/consensus/misc/eip1559" "github.com/ethereum/go-ethereum/consensus/misc/eip4844" "github.com/ethereum/go-ethereum/core/rawdb" - "github.com/ethereum/go-ethereum/core/state" "github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/core/vm" "github.com/ethereum/go-ethereum/crypto" "github.com/ethereum/go-ethereum/params" "github.com/ethereum/go-ethereum/trie" - "github.com/ethereum/go-ethereum/triedb" - "github.com/ethereum/go-verkle" "github.com/holiman/uint256" "golang.org/x/crypto/sha3" ) @@ -51,23 +47,23 @@ func u64(val uint64) *uint64 { return &val } func TestStateProcessorErrors(t *testing.T) { var ( config = ¶ms.ChainConfig{ - ChainID: big.NewInt(1), - HomesteadBlock: big.NewInt(0), - EIP150Block: big.NewInt(0), - EIP155Block: big.NewInt(0), - EIP158Block: big.NewInt(0), - ByzantiumBlock: big.NewInt(0), - ConstantinopleBlock: big.NewInt(0), - PetersburgBlock: big.NewInt(0), - IstanbulBlock: big.NewInt(0), - MuirGlacierBlock: big.NewInt(0), - BerlinBlock: big.NewInt(0), - LondonBlock: big.NewInt(0), - Ethash: new(params.EthashConfig), - TerminalTotalDifficulty: big.NewInt(0), - TerminalTotalDifficultyPassed: true, - ShanghaiTime: new(uint64), - CancunTime: new(uint64), + ChainID: big.NewInt(1), + HomesteadBlock: big.NewInt(0), + EIP150Block: big.NewInt(0), + EIP155Block: big.NewInt(0), + EIP158Block: big.NewInt(0), + ByzantiumBlock: big.NewInt(0), + ConstantinopleBlock: big.NewInt(0), + PetersburgBlock: big.NewInt(0), + IstanbulBlock: big.NewInt(0), + MuirGlacierBlock: big.NewInt(0), + BerlinBlock: big.NewInt(0), + LondonBlock: big.NewInt(0), + Ethash: new(params.EthashConfig), + TerminalTotalDifficulty: big.NewInt(0), + ShanghaiTime: new(uint64), + CancunTime: new(uint64), + PragueTime: new(uint64), } signer = types.LatestSigner(config) key1, _ = crypto.HexToECDSA("b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291") @@ -115,6 +111,21 @@ func TestStateProcessorErrors(t *testing.T) { } return tx } + var mkSetCodeTx = func(nonce uint64, to common.Address, gasLimit uint64, gasTipCap, gasFeeCap *big.Int, authlist []types.SetCodeAuthorization) *types.Transaction { + tx, err := types.SignTx(types.NewTx(&types.SetCodeTx{ + Nonce: nonce, + GasTipCap: uint256.MustFromBig(gasTipCap), + GasFeeCap: uint256.MustFromBig(gasFeeCap), + Gas: gasLimit, + To: to, + Value: new(uint256.Int), + AuthList: authlist, + }), signer, key1) + if err != nil { + t.Fatal(err) + } + return tx + } { // Tests against a 'recent' chain definition var ( @@ -256,8 +267,15 @@ func TestStateProcessorErrors(t *testing.T) { }, want: "could not apply tx 0 [0x6c11015985ce82db691d7b2d017acda296db88b811c3c60dc71449c76256c716]: max fee per gas less than block base fee: address 0x71562b71999873DB5b286dF957af199Ec94617F7, maxFeePerGas: 1, baseFee: 875000000", }, + { // ErrEmptyAuthList + txs: []*types.Transaction{ + mkSetCodeTx(0, common.Address{}, params.TxGas, big.NewInt(params.InitialBaseFee), big.NewInt(params.InitialBaseFee), nil), + }, + want: "could not apply tx 0 [0xc18d10f4c809dbdfa1a074c3300de9bc4b7f16a20f0ec667f6f67312b71b956a]: EIP-7702 transaction with empty auth list (sender 0x71562b71999873DB5b286dF957af199Ec94617F7)", + }, + // ErrSetCodeTxCreate cannot be tested: it is impossible to create a SetCode-tx with nil `to`. } { - block := GenerateBadBlock(gspec.ToBlock(), beacon.New(ethash.NewFaker()), tt.txs, gspec.Config) + block := GenerateBadBlock(gspec.ToBlock(), beacon.New(ethash.NewFaker()), tt.txs, gspec.Config, false) _, err := blockchain.InsertChain(types.Blocks{block}) if err == nil { t.Fatal("block imported without errors") @@ -306,7 +324,7 @@ func TestStateProcessorErrors(t *testing.T) { want: "could not apply tx 0 [0x88626ac0d53cb65308f2416103c62bb1f18b805573d4f96a3640bbbfff13c14f]: transaction type not supported", }, } { - block := GenerateBadBlock(gspec.ToBlock(), ethash.NewFaker(), tt.txs, gspec.Config) + block := GenerateBadBlock(gspec.ToBlock(), ethash.NewFaker(), tt.txs, gspec.Config, true) _, err := blockchain.InsertChain(types.Blocks{block}) if err == nil { t.Fatal("block imported without errors") @@ -342,10 +360,10 @@ func TestStateProcessorErrors(t *testing.T) { txs: []*types.Transaction{ mkDynamicTx(0, common.Address{}, params.TxGas-1000, big.NewInt(0), big.NewInt(0)), }, - want: "could not apply tx 0 [0x88626ac0d53cb65308f2416103c62bb1f18b805573d4f96a3640bbbfff13c14f]: sender not an eoa: address 0x71562b71999873DB5b286dF957af199Ec94617F7, codehash: 0x9280914443471259d4570a8661015ae4a5b80186dbc619658fb494bebc3da3d1", + want: "could not apply tx 0 [0x88626ac0d53cb65308f2416103c62bb1f18b805573d4f96a3640bbbfff13c14f]: sender not an eoa: address 0x71562b71999873DB5b286dF957af199Ec94617F7, len(code): 4", }, } { - block := GenerateBadBlock(gspec.ToBlock(), beacon.New(ethash.NewFaker()), tt.txs, gspec.Config) + block := GenerateBadBlock(gspec.ToBlock(), beacon.New(ethash.NewFaker()), tt.txs, gspec.Config, false) _, err := blockchain.InsertChain(types.Blocks{block}) if err == nil { t.Fatal("block imported without errors") @@ -361,9 +379,9 @@ func TestStateProcessorErrors(t *testing.T) { // valid, and no proper post-state can be made. But from the perspective of the blockchain, the block is sufficiently // valid to be considered for import: // - valid pow (fake), ancestry, difficulty, gaslimit etc -func GenerateBadBlock(parent *types.Block, engine consensus.Engine, txs types.Transactions, config *params.ChainConfig) *types.Block { +func GenerateBadBlock(parent *types.Block, engine consensus.Engine, txs types.Transactions, config *params.ChainConfig, isPOW bool) *types.Block { difficulty := big.NewInt(0) - if !config.TerminalTotalDifficultyPassed { + if isPOW { fakeChainReader := newChainMaker(nil, config, engine) difficulty = engine.CalcDifficulty(fakeChainReader, parent.Time()+10, &types.Header{ Number: parent.Number(), @@ -427,196 +445,3 @@ func GenerateBadBlock(parent *types.Block, engine consensus.Engine, txs types.Tr } return types.NewBlock(header, body, receipts, trie.NewStackTrie(nil)) } - -var ( - code = common.FromHex(`6060604052600a8060106000396000f360606040526008565b00`) - intrinsicContractCreationGas, _ = IntrinsicGas(code, nil, true, true, true, true) - // A contract creation that calls EXTCODECOPY in the constructor. Used to ensure that the witness - // will not contain that copied data. - // Source: https://gist.github.com/gballet/a23db1e1cb4ed105616b5920feb75985 - codeWithExtCodeCopy = common.FromHex(`0x60806040526040516100109061017b565b604051809103906000f08015801561002c573d6000803e3d6000fd5b506000806101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff16021790555034801561007857600080fd5b5060008067ffffffffffffffff8111156100955761009461024a565b5b6040519080825280601f01601f1916602001820160405280156100c75781602001600182028036833780820191505090505b50905060008060009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1690506020600083833c81610101906101e3565b60405161010d90610187565b61011791906101a3565b604051809103906000f080158015610133573d6000803e3d6000fd5b50600160006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff160217905550505061029b565b60d58061046783390190565b6102068061053c83390190565b61019d816101d9565b82525050565b60006020820190506101b86000830184610194565b92915050565b6000819050602082019050919050565b600081519050919050565b6000819050919050565b60006101ee826101ce565b826101f8846101be565b905061020381610279565b925060208210156102435761023e7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8360200360080261028e565b831692505b5050919050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b600061028582516101d9565b80915050919050565b600082821b905092915050565b6101bd806102aa6000396000f3fe608060405234801561001057600080fd5b506004361061002b5760003560e01c8063f566852414610030575b600080fd5b61003861004e565b6040516100459190610146565b60405180910390f35b6000600160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff166381ca91d36040518163ffffffff1660e01b815260040160206040518083038186803b1580156100b857600080fd5b505afa1580156100cc573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906100f0919061010a565b905090565b60008151905061010481610170565b92915050565b6000602082840312156101205761011f61016b565b5b600061012e848285016100f5565b91505092915050565b61014081610161565b82525050565b600060208201905061015b6000830184610137565b92915050565b6000819050919050565b600080fd5b61017981610161565b811461018457600080fd5b5056fea2646970667358221220a6a0e11af79f176f9c421b7b12f441356b25f6489b83d38cc828a701720b41f164736f6c63430008070033608060405234801561001057600080fd5b5060b68061001f6000396000f3fe6080604052348015600f57600080fd5b506004361060285760003560e01c8063ab5ed15014602d575b600080fd5b60336047565b604051603e9190605d565b60405180910390f35b60006001905090565b6057816076565b82525050565b6000602082019050607060008301846050565b92915050565b600081905091905056fea26469706673582212203a14eb0d5cd07c277d3e24912f110ddda3e553245a99afc4eeefb2fbae5327aa64736f6c63430008070033608060405234801561001057600080fd5b5060405161020638038061020683398181016040528101906100329190610063565b60018160001c6100429190610090565b60008190555050610145565b60008151905061005d8161012e565b92915050565b60006020828403121561007957610078610129565b5b60006100878482850161004e565b91505092915050565b600061009b826100f0565b91506100a6836100f0565b9250827fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff038211156100db576100da6100fa565b5b828201905092915050565b6000819050919050565b6000819050919050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b600080fd5b610137816100e6565b811461014257600080fd5b50565b60b3806101536000396000f3fe6080604052348015600f57600080fd5b506004361060285760003560e01c806381ca91d314602d575b600080fd5b60336047565b604051603e9190605a565b60405180910390f35b60005481565b6054816073565b82525050565b6000602082019050606d6000830184604d565b92915050565b600081905091905056fea26469706673582212209bff7098a2f526de1ad499866f27d6d0d6f17b74a413036d6063ca6a0998ca4264736f6c63430008070033`) - intrinsicCodeWithExtCodeCopyGas, _ = IntrinsicGas(codeWithExtCodeCopy, nil, true, true, true, true) -) - -func TestProcessVerkle(t *testing.T) { - var ( - config = ¶ms.ChainConfig{ - ChainID: big.NewInt(1), - HomesteadBlock: big.NewInt(0), - EIP150Block: big.NewInt(0), - EIP155Block: big.NewInt(0), - EIP158Block: big.NewInt(0), - ByzantiumBlock: big.NewInt(0), - ConstantinopleBlock: big.NewInt(0), - PetersburgBlock: big.NewInt(0), - IstanbulBlock: big.NewInt(0), - MuirGlacierBlock: big.NewInt(0), - BerlinBlock: big.NewInt(0), - LondonBlock: big.NewInt(0), - Ethash: new(params.EthashConfig), - ShanghaiTime: u64(0), - VerkleTime: u64(0), - TerminalTotalDifficulty: common.Big0, - TerminalTotalDifficultyPassed: true, - // TODO uncomment when proof generation is merged - // ProofInBlocks: true, - } - signer = types.LatestSigner(config) - testKey, _ = crypto.HexToECDSA("b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291") - bcdb = rawdb.NewMemoryDatabase() // Database for the blockchain - coinbase = common.HexToAddress("0x71562b71999873DB5b286dF957af199Ec94617F7") - gspec = &Genesis{ - Config: config, - Alloc: GenesisAlloc{ - coinbase: GenesisAccount{ - Balance: big.NewInt(1000000000000000000), // 1 ether - Nonce: 0, - }, - }, - } - ) - // Verkle trees use the snapshot, which must be enabled before the - // data is saved into the tree+database. - // genesis := gspec.MustCommit(bcdb, triedb) - cacheConfig := DefaultCacheConfigWithScheme("path") - cacheConfig.SnapshotLimit = 0 - blockchain, _ := NewBlockChain(bcdb, cacheConfig, gspec, nil, beacon.New(ethash.NewFaker()), vm.Config{}, nil) - defer blockchain.Stop() - - txCost1 := params.TxGas - txCost2 := params.TxGas - contractCreationCost := intrinsicContractCreationGas + - params.WitnessChunkReadCost + params.WitnessChunkWriteCost + params.WitnessBranchReadCost + params.WitnessBranchWriteCost + /* creation */ - params.WitnessChunkReadCost + params.WitnessChunkWriteCost + /* creation with value */ - 739 /* execution costs */ - codeWithExtCodeCopyGas := intrinsicCodeWithExtCodeCopyGas + - params.WitnessChunkReadCost + params.WitnessChunkWriteCost + params.WitnessBranchReadCost + params.WitnessBranchWriteCost + /* creation (tx) */ - params.WitnessChunkReadCost + params.WitnessChunkWriteCost + params.WitnessBranchReadCost + params.WitnessBranchWriteCost + /* creation (CREATE at pc=0x20) */ - params.WitnessChunkReadCost + params.WitnessChunkWriteCost + /* write code hash */ - params.WitnessChunkReadCost + params.WitnessChunkWriteCost + /* code chunk #0 */ - params.WitnessChunkReadCost + params.WitnessChunkWriteCost + /* code chunk #1 */ - params.WitnessChunkReadCost + params.WitnessChunkWriteCost + /* code chunk #2 */ - params.WitnessChunkReadCost + params.WitnessChunkWriteCost + /* code chunk #3 */ - params.WitnessChunkReadCost + params.WitnessChunkWriteCost + /* code chunk #4 */ - params.WitnessChunkReadCost + params.WitnessChunkWriteCost + /* code chunk #5 */ - params.WitnessChunkReadCost + /* SLOAD in constructor */ - params.WitnessChunkWriteCost + /* SSTORE in constructor */ - params.WitnessChunkReadCost + params.WitnessChunkWriteCost + params.WitnessBranchReadCost + params.WitnessBranchWriteCost + /* creation (CREATE at PC=0x121) */ - params.WitnessChunkReadCost + params.WitnessChunkWriteCost + /* write code hash */ - params.WitnessChunkReadCost + params.WitnessChunkWriteCost + /* code chunk #0 */ - params.WitnessChunkReadCost + params.WitnessChunkWriteCost + /* code chunk #1 */ - params.WitnessChunkReadCost + params.WitnessChunkWriteCost + /* code chunk #2 */ - params.WitnessChunkReadCost + params.WitnessChunkWriteCost + /* code chunk #3 */ - params.WitnessChunkReadCost + params.WitnessChunkWriteCost + /* code chunk #4 */ - params.WitnessChunkReadCost + params.WitnessChunkWriteCost + /* code chunk #5 */ - params.WitnessChunkReadCost + /* SLOAD in constructor */ - params.WitnessChunkWriteCost + /* SSTORE in constructor */ - params.WitnessChunkReadCost + params.WitnessChunkWriteCost + /* write code hash for tx creation */ - 15*(params.WitnessChunkReadCost+params.WitnessChunkWriteCost) + /* code chunks #0..#14 */ - 4844 /* execution costs */ - blockGasUsagesExpected := []uint64{ - txCost1*2 + txCost2, - txCost1*2 + txCost2 + contractCreationCost + codeWithExtCodeCopyGas, - } - _, chain, _, proofs, statediffs := GenerateVerkleChainWithGenesis(gspec, beacon.New(ethash.NewFaker()), 2, func(i int, gen *BlockGen) { - gen.SetPoS() - - // TODO need to check that the tx cost provided is the exact amount used (no remaining left-over) - tx, _ := types.SignTx(types.NewTransaction(uint64(i)*3, common.Address{byte(i), 2, 3}, big.NewInt(999), txCost1, big.NewInt(875000000), nil), signer, testKey) - gen.AddTx(tx) - tx, _ = types.SignTx(types.NewTransaction(uint64(i)*3+1, common.Address{}, big.NewInt(999), txCost1, big.NewInt(875000000), nil), signer, testKey) - gen.AddTx(tx) - tx, _ = types.SignTx(types.NewTransaction(uint64(i)*3+2, common.Address{}, big.NewInt(0), txCost2, big.NewInt(875000000), nil), signer, testKey) - gen.AddTx(tx) - - // Add two contract creations in block #2 - if i == 1 { - tx, _ = types.SignTx(types.NewContractCreation(6, big.NewInt(16), 3000000, big.NewInt(875000000), code), signer, testKey) - gen.AddTx(tx) - - tx, _ = types.SignTx(types.NewContractCreation(7, big.NewInt(0), 3000000, big.NewInt(875000000), codeWithExtCodeCopy), signer, testKey) - gen.AddTx(tx) - } - }) - - // Check proof for both blocks - err := verkle.Verify(proofs[0], gspec.ToBlock().Root().Bytes(), chain[0].Root().Bytes(), statediffs[0]) - if err != nil { - t.Fatal(err) - } - err = verkle.Verify(proofs[1], chain[0].Root().Bytes(), chain[1].Root().Bytes(), statediffs[1]) - if err != nil { - t.Fatal(err) - } - - t.Log("verified verkle proof, inserting blocks into the chain") - - endnum, err := blockchain.InsertChain(chain) - if err != nil { - t.Fatalf("block %d imported with error: %v", endnum, err) - } - - for i := 0; i < 2; i++ { - b := blockchain.GetBlockByNumber(uint64(i) + 1) - if b == nil { - t.Fatalf("expected block %d to be present in chain", i+1) - } - if b.Hash() != chain[i].Hash() { - t.Fatalf("block #%d not found at expected height", b.NumberU64()) - } - if b.GasUsed() != blockGasUsagesExpected[i] { - t.Fatalf("expected block #%d txs to use %d, got %d\n", b.NumberU64(), blockGasUsagesExpected[i], b.GasUsed()) - } - } -} - -func TestProcessParentBlockHash(t *testing.T) { - var ( - chainConfig = params.MergedTestChainConfig - hashA = common.Hash{0x01} - hashB = common.Hash{0x02} - header = &types.Header{ParentHash: hashA, Number: big.NewInt(2), Difficulty: big.NewInt(0)} - parent = &types.Header{ParentHash: hashB, Number: big.NewInt(1), Difficulty: big.NewInt(0)} - coinbase = common.Address{} - ) - test := func(statedb *state.StateDB) { - statedb.SetNonce(params.HistoryStorageAddress, 1) - statedb.SetCode(params.HistoryStorageAddress, params.HistoryStorageCode) - statedb.IntermediateRoot(true) - - vmContext := NewEVMBlockContext(header, nil, &coinbase) - evm := vm.NewEVM(vmContext, vm.TxContext{}, statedb, chainConfig, vm.Config{}) - ProcessParentBlockHash(header.ParentHash, evm, statedb) - - vmContext = NewEVMBlockContext(parent, nil, &coinbase) - evm = vm.NewEVM(vmContext, vm.TxContext{}, statedb, chainConfig, vm.Config{}) - ProcessParentBlockHash(parent.ParentHash, evm, statedb) - - // make sure that the state is correct - if have := getParentBlockHash(statedb, 1); have != hashA { - t.Errorf("want parent hash %v, have %v", hashA, have) - } - if have := getParentBlockHash(statedb, 0); have != hashB { - t.Errorf("want parent hash %v, have %v", hashB, have) - } - } - t.Run("MPT", func(t *testing.T) { - statedb, _ := state.New(types.EmptyRootHash, state.NewDatabaseForTesting()) - test(statedb) - }) - t.Run("Verkle", func(t *testing.T) { - db := rawdb.NewMemoryDatabase() - cacheConfig := DefaultCacheConfigWithScheme(rawdb.PathScheme) - cacheConfig.SnapshotLimit = 0 - triedb := triedb.NewDatabase(db, cacheConfig.triedbConfig(true)) - statedb, _ := state.New(types.EmptyVerkleHash, state.NewDatabase(triedb, nil)) - test(statedb) - }) -} - -func getParentBlockHash(statedb *state.StateDB, number uint64) common.Hash { - ringIndex := number % params.HistoryServeWindow - var key common.Hash - binary.BigEndian.PutUint64(key[24:], ringIndex) - return statedb.GetState(params.HistoryStorageAddress, key) -} diff --git a/core/state_transition.go b/core/state_transition.go index d285d03fe2..93d72d16b7 100644 --- a/core/state_transition.go +++ b/core/state_transition.go @@ -22,7 +22,6 @@ import ( "math/big" "github.com/ethereum/go-ethereum/common" - cmath "github.com/ethereum/go-ethereum/common/math" "github.com/ethereum/go-ethereum/core/tracing" "github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/core/vm" @@ -68,7 +67,7 @@ func (result *ExecutionResult) Revert() []byte { } // IntrinsicGas computes the 'intrinsic gas' for a message with the given data. -func IntrinsicGas(data []byte, accessList types.AccessList, isContractCreation, isHomestead, isEIP2028, isEIP3860 bool) (uint64, error) { +func IntrinsicGas(data []byte, accessList types.AccessList, authList []types.SetCodeAuthorization, isContractCreation, isHomestead, isEIP2028, isEIP3860 bool) (uint64, error) { // Set the starting gas for the raw transaction var gas uint64 if isContractCreation && isHomestead { @@ -114,6 +113,9 @@ func IntrinsicGas(data []byte, accessList types.AccessList, isContractCreation, gas += uint64(len(accessList)) * params.TxAccessListAddressGas gas += uint64(accessList.StorageKeys()) * params.TxAccessListStorageKeyGas } + if authList != nil { + gas += uint64(len(authList)) * params.CallNewAccountGas + } return gas, nil } @@ -129,18 +131,19 @@ func toWordSize(size uint64) uint64 { // A Message contains the data derived from a single transaction that is relevant to state // processing. type Message struct { - To *common.Address - From common.Address - Nonce uint64 - Value *big.Int - GasLimit uint64 - GasPrice *big.Int - GasFeeCap *big.Int - GasTipCap *big.Int - Data []byte - AccessList types.AccessList - BlobGasFeeCap *big.Int - BlobHashes []common.Hash + To *common.Address + From common.Address + Nonce uint64 + Value *big.Int + GasLimit uint64 + GasPrice *big.Int + GasFeeCap *big.Int + GasTipCap *big.Int + Data []byte + AccessList types.AccessList + BlobGasFeeCap *big.Int + BlobHashes []common.Hash + SetCodeAuthorizations []types.SetCodeAuthorization // When SkipNonceChecks is true, the message nonce is not checked against the // account nonce in state. @@ -154,23 +157,27 @@ type Message struct { // TransactionToMessage converts a transaction into a Message. func TransactionToMessage(tx *types.Transaction, s types.Signer, baseFee *big.Int) (*Message, error) { msg := &Message{ - Nonce: tx.Nonce(), - GasLimit: tx.Gas(), - GasPrice: new(big.Int).Set(tx.GasPrice()), - GasFeeCap: new(big.Int).Set(tx.GasFeeCap()), - GasTipCap: new(big.Int).Set(tx.GasTipCap()), - To: tx.To(), - Value: tx.Value(), - Data: tx.Data(), - AccessList: tx.AccessList(), - SkipNonceChecks: false, - SkipFromEOACheck: false, - BlobHashes: tx.BlobHashes(), - BlobGasFeeCap: tx.BlobGasFeeCap(), + Nonce: tx.Nonce(), + GasLimit: tx.Gas(), + GasPrice: new(big.Int).Set(tx.GasPrice()), + GasFeeCap: new(big.Int).Set(tx.GasFeeCap()), + GasTipCap: new(big.Int).Set(tx.GasTipCap()), + To: tx.To(), + Value: tx.Value(), + Data: tx.Data(), + AccessList: tx.AccessList(), + SetCodeAuthorizations: tx.SetCodeAuthorizations(), + SkipNonceChecks: false, + SkipFromEOACheck: false, + BlobHashes: tx.BlobHashes(), + BlobGasFeeCap: tx.BlobGasFeeCap(), } // If baseFee provided, set gasPrice to effectiveGasPrice. if baseFee != nil { - msg.GasPrice = cmath.BigMin(msg.GasPrice.Add(msg.GasTipCap, baseFee), msg.GasFeeCap) + msg.GasPrice = msg.GasPrice.Add(msg.GasTipCap, baseFee) + if msg.GasPrice.Cmp(msg.GasFeeCap) > 0 { + msg.GasPrice = msg.GasFeeCap + } } var err error msg.From, err = types.Sender(s, tx) @@ -185,10 +192,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 // @@ -210,7 +218,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 @@ -219,9 +227,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, @@ -230,14 +238,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) @@ -281,7 +289,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 { @@ -300,10 +308,10 @@ func (st *StateTransition) preCheck() error { } if !msg.SkipFromEOACheck { // Make sure the sender is an EOA - codeHash := st.state.GetCodeHash(msg.From) - if codeHash != (common.Hash{}) && codeHash != types.EmptyCodeHash { - return fmt.Errorf("%w: address %v, codehash: %s", ErrSenderNoEOA, - msg.From.Hex(), codeHash) + code := st.state.GetCode(msg.From) + _, delegated := types.ParseDelegation(code) + if len(code) > 0 && !delegated { + return fmt.Errorf("%w: address %v, len(code): %d", ErrSenderNoEOA, msg.From.Hex(), len(code)) } } // Make sure that transaction gasFeeCap is greater than the baseFee (post london) @@ -363,10 +371,19 @@ func (st *StateTransition) preCheck() error { } } } + // Check that EIP-7702 authorization list signatures are well formed. + if msg.SetCodeAuthorizations != nil { + if msg.To == nil { + return fmt.Errorf("%w (sender %v)", ErrSetCodeTxCreate, msg.From) + } + if len(msg.SetCodeAuthorizations) == 0 { + return fmt.Errorf("%w (sender %v)", ErrEmptyAuthList, msg.From) + } + } 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) @@ -376,7 +393,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 // @@ -400,7 +417,7 @@ func (st *StateTransition) TransitionDb() (*ExecutionResult, error) { ) // Check clauses 4-5, subtract intrinsic gas if everything is correct - gas, err := IntrinsicGas(msg.Data, msg.AccessList, contractCreation, rules.IsHomestead, rules.IsIstanbul, rules.IsShanghai) + gas, err := IntrinsicGas(msg.Data, msg.AccessList, msg.SetCodeAuthorizations, contractCreation, rules.IsHomestead, rules.IsIstanbul, rules.IsShanghai) if err != nil { return nil, err } @@ -446,8 +463,27 @@ func (st *StateTransition) TransitionDb() (*ExecutionResult, error) { if contractCreation { ret, _, st.gasRemaining, vmerr = st.evm.Create(sender, msg.Data, st.gasRemaining, value) } else { - // Increment the nonce for the next transaction - st.state.SetNonce(msg.From, st.state.GetNonce(sender.Address())+1) + // Increment the nonce for the next transaction. + st.state.SetNonce(msg.From, st.state.GetNonce(msg.From)+1) + + // Apply EIP-7702 authorizations. + if msg.SetCodeAuthorizations != nil { + for _, auth := range msg.SetCodeAuthorizations { + // Note errors are ignored, we simply skip invalid authorizations here. + st.applyAuthorization(msg, &auth) + } + } + + // Perform convenience warming of sender's delegation target. Although the + // sender is already warmed in Prepare(..), it's possible a delegation to + // the account was deployed during this transaction. To handle correctly, + // simply wait until the final state of delegations is determined before + // performing the resolution and warming. + if addr, ok := types.ParseDelegation(st.state.GetCode(*msg.To)); ok { + st.state.AddAddressToAccessList(addr) + } + + // Execute the transaction's call. ret, st.gasRemaining, vmerr = st.evm.Call(sender, st.to(), msg.Data, st.gasRemaining, value) } @@ -461,7 +497,10 @@ func (st *StateTransition) TransitionDb() (*ExecutionResult, error) { } effectiveTip := msg.GasPrice if rules.IsLondon { - effectiveTip = cmath.BigMin(msg.GasTipCap, new(big.Int).Sub(msg.GasFeeCap, st.evm.Context.BaseFee)) + effectiveTip = new(big.Int).Sub(msg.GasFeeCap, st.evm.Context.BaseFee) + if effectiveTip.Cmp(msg.GasTipCap) > 0 { + effectiveTip = msg.GasTipCap + } } effectiveTipU256, _ := uint256.FromBig(effectiveTip) @@ -488,7 +527,65 @@ func (st *StateTransition) TransitionDb() (*ExecutionResult, error) { }, nil } -func (st *StateTransition) refundGas(refundQuotient uint64) uint64 { +// validateAuthorization validates an EIP-7702 authorization against the state. +func (st *stateTransition) validateAuthorization(auth *types.SetCodeAuthorization) (authority common.Address, err error) { + // Verify chain ID is 0 or equal to current chain ID. + if auth.ChainID != 0 && st.evm.ChainConfig().ChainID.Uint64() != auth.ChainID { + return authority, ErrAuthorizationWrongChainID + } + // Limit nonce to 2^64-1 per EIP-2681. + if auth.Nonce+1 < auth.Nonce { + return authority, ErrAuthorizationNonceOverflow + } + // Validate signature values and recover authority. + authority, err = auth.Authority() + if err != nil { + return authority, fmt.Errorf("%w: %v", ErrAuthorizationInvalidSignature, err) + } + // Check the authority account + // 1) doesn't have code or has exisiting delegation + // 2) matches the auth's nonce + // + // Note it is added to the access list even if the authorization is invalid. + st.state.AddAddressToAccessList(authority) + code := st.state.GetCode(authority) + if _, ok := types.ParseDelegation(code); len(code) != 0 && !ok { + return authority, ErrAuthorizationDestinationHasCode + } + if have := st.state.GetNonce(authority); have != auth.Nonce { + return authority, ErrAuthorizationNonceMismatch + } + return authority, nil +} + +// applyAuthorization applies an EIP-7702 code delegation to the state. +func (st *stateTransition) applyAuthorization(msg *Message, auth *types.SetCodeAuthorization) error { + authority, err := st.validateAuthorization(auth) + if err != nil { + return err + } + + // If the account already exists in state, refund the new account cost + // charged in the intrinsic calculation. + if st.state.Exist(authority) { + st.state.AddRefund(params.CallNewAccountGas - params.TxAuthTupleGas) + } + + // Update nonce and account code. + st.state.SetNonce(authority, auth.Nonce+1) + if auth.Address == (common.Address{}) { + // Delegation to zero address means clear. + st.state.SetCode(authority, nil) + return nil + } + + // Otherwise install delegation to auth.Address. + st.state.SetCode(authority, types.AddressToDelegation(auth.Address)) + + return nil +} + +func (st *stateTransition) refundGas(refundQuotient uint64) uint64 { // Apply refund counter, capped to a refund quotient refund := st.gasUsed() / refundQuotient if refund > st.state.GetRefund() { @@ -518,11 +615,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 f0baf667cb..728f15069b 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,7 @@ type VMContext struct { BlockNumber *big.Int Time uint64 Random *common.Hash - // Effective tx gas price - GasPrice *big.Int - ChainConfig *params.ChainConfig + BaseFee *big.Int StateDB StateDB } @@ -146,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. @@ -181,14 +184,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 // State events OnBalanceChange BalanceChangeHook OnNonceChange NonceChangeHook diff --git a/core/txindexer.go b/core/txindexer.go index 70fe5f3322..b2f2188595 100644 --- a/core/txindexer.go +++ b/core/txindexer.go @@ -151,9 +151,9 @@ func (indexer *txIndexer) loop(chain *BlockChain) { if done == nil { stop = make(chan struct{}) done = make(chan struct{}) - go indexer.run(rawdb.ReadTxIndexTail(indexer.db), head.Block.NumberU64(), stop, done) + go indexer.run(rawdb.ReadTxIndexTail(indexer.db), head.Header.Number.Uint64(), stop, done) } - lastHead = head.Block.NumberU64() + lastHead = head.Header.Number.Uint64() case <-done: stop = nil done = nil diff --git a/core/txpool/blobpool/blobpool.go b/core/txpool/blobpool/blobpool.go index 82df09a4bf..02d339f99c 100644 --- a/core/txpool/blobpool/blobpool.go +++ b/core/txpool/blobpool/blobpool.go @@ -36,6 +36,7 @@ import ( "github.com/ethereum/go-ethereum/core/state" "github.com/ethereum/go-ethereum/core/txpool" "github.com/ethereum/go-ethereum/core/types" + "github.com/ethereum/go-ethereum/crypto/kzg4844" "github.com/ethereum/go-ethereum/event" "github.com/ethereum/go-ethereum/log" "github.com/ethereum/go-ethereum/metrics" @@ -88,9 +89,11 @@ const ( // bare minimum needed fields to keep the size down (and thus number of entries // larger with the same memory consumption). type blobTxMeta struct { - hash common.Hash // Transaction hash to maintain the lookup table - id uint64 // Storage ID in the pool's persistent store - size uint32 // Byte size in the pool's persistent store + hash common.Hash // Transaction hash to maintain the lookup table + vhashes []common.Hash // Blob versioned hashes to maintain the lookup table + + id uint64 // Storage ID in the pool's persistent store + size uint32 // Byte size in the pool's persistent store nonce uint64 // Needed to prioritize inclusion order within an account costCap *uint256.Int // Needed to validate cumulative balance sufficiency @@ -113,6 +116,7 @@ type blobTxMeta struct { func newBlobTxMeta(id uint64, size uint32, tx *types.Transaction) *blobTxMeta { meta := &blobTxMeta{ hash: tx.Hash(), + vhashes: tx.BlobHashes(), id: id, size: size, nonce: tx.Nonce(), @@ -306,7 +310,7 @@ type BlobPool struct { state *state.StateDB // Current state at the head of the chain gasTip *uint256.Int // Currently accepted minimum gas tip - lookup map[common.Hash]uint64 // Lookup table mapping hashes to tx billy entries + lookup *lookup // Lookup table mapping blobs to txs and txs to billy entries index map[common.Address][]*blobTxMeta // Blob transactions grouped by accounts, sorted by nonce spent map[common.Address]*uint256.Int // Expenditure tracking for individual accounts evict *evictHeap // Heap of cheapest accounts for eviction when full @@ -314,6 +318,10 @@ type BlobPool struct { discoverFeed event.Feed // Event feed to send out new tx events on pool discovery (reorg excluded) insertFeed event.Feed // Event feed to send out new tx events on pool inclusion (reorg included) + // txValidationFn defaults to txpool.ValidateTransaction, but can be + // overridden for testing purposes. + txValidationFn txpool.ValidationFunction + lock sync.RWMutex // Mutex protecting the pool during reorg handling } @@ -325,12 +333,13 @@ func New(config Config, chain BlockChain) *BlobPool { // Create the transaction pool with its initial settings return &BlobPool{ - config: config, - signer: types.LatestSigner(chain.Config()), - chain: chain, - lookup: make(map[common.Hash]uint64), - index: make(map[common.Address][]*blobTxMeta), - spent: make(map[common.Address]*uint256.Int), + config: config, + signer: types.LatestSigner(chain.Config()), + chain: chain, + lookup: newLookup(), + index: make(map[common.Address][]*blobTxMeta), + spent: make(map[common.Address]*uint256.Int), + txValidationFn: txpool.ValidateTransaction, } } @@ -471,7 +480,7 @@ func (p *BlobPool) parseTransaction(id uint64, size uint32, blob []byte) error { } meta := newBlobTxMeta(id, size, tx) - if _, exists := p.lookup[meta.hash]; exists { + if p.lookup.exists(meta.hash) { // This path is only possible after a crash, where deleted items are not // removed via the normal shutdown-startup procedure and thus may get // partially resurrected. @@ -496,9 +505,8 @@ func (p *BlobPool) parseTransaction(id uint64, size uint32, blob []byte) error { p.index[sender] = append(p.index[sender], meta) p.spent[sender] = new(uint256.Int).Add(p.spent[sender], meta.costCap) - p.lookup[meta.hash] = meta.id + p.lookup.track(meta) p.stored += uint64(meta.size) - return nil } @@ -531,7 +539,7 @@ func (p *BlobPool) recheck(addr common.Address, inclusions map[common.Hash]uint6 nonces = append(nonces, txs[i].nonce) p.stored -= uint64(txs[i].size) - delete(p.lookup, txs[i].hash) + p.lookup.untrack(txs[i]) // Included transactions blobs need to be moved to the limbo if filled && inclusions != nil { @@ -572,7 +580,7 @@ func (p *BlobPool) recheck(addr common.Address, inclusions map[common.Hash]uint6 p.spent[addr] = new(uint256.Int).Sub(p.spent[addr], txs[0].costCap) p.stored -= uint64(txs[0].size) - delete(p.lookup, txs[0].hash) + p.lookup.untrack(txs[0]) // Included transactions blobs need to be moved to the limbo if inclusions != nil { @@ -621,14 +629,14 @@ func (p *BlobPool) recheck(addr common.Address, inclusions map[common.Hash]uint6 // crash would result in previously deleted entities being resurrected. // That could potentially cause a duplicate nonce to appear. if txs[i].nonce == txs[i-1].nonce { - id := p.lookup[txs[i].hash] + id, _ := p.lookup.storeidOfTx(txs[i].hash) log.Error("Dropping repeat nonce blob transaction", "from", addr, "nonce", txs[i].nonce, "id", id) dropRepeatedMeter.Mark(1) p.spent[addr] = new(uint256.Int).Sub(p.spent[addr], txs[i].costCap) p.stored -= uint64(txs[i].size) - delete(p.lookup, txs[i].hash) + p.lookup.untrack(txs[i]) if err := p.store.Delete(id); err != nil { log.Error("Failed to delete blob transaction", "from", addr, "id", id, "err", err) @@ -650,7 +658,7 @@ func (p *BlobPool) recheck(addr common.Address, inclusions map[common.Hash]uint6 p.spent[addr] = new(uint256.Int).Sub(p.spent[addr], txs[j].costCap) p.stored -= uint64(txs[j].size) - delete(p.lookup, txs[j].hash) + p.lookup.untrack(txs[j]) } txs = txs[:i] @@ -688,7 +696,7 @@ func (p *BlobPool) recheck(addr common.Address, inclusions map[common.Hash]uint6 p.spent[addr] = new(uint256.Int).Sub(p.spent[addr], last.costCap) p.stored -= uint64(last.size) - delete(p.lookup, last.hash) + p.lookup.untrack(last) } if len(txs) == 0 { delete(p.index, addr) @@ -728,7 +736,7 @@ func (p *BlobPool) recheck(addr common.Address, inclusions map[common.Hash]uint6 p.spent[addr] = new(uint256.Int).Sub(p.spent[addr], last.costCap) p.stored -= uint64(last.size) - delete(p.lookup, last.hash) + p.lookup.untrack(last) } p.index[addr] = txs @@ -1006,7 +1014,7 @@ func (p *BlobPool) reinject(addr common.Address, txhash common.Hash) error { p.index[addr] = append(p.index[addr], meta) p.spent[addr] = new(uint256.Int).Add(p.spent[addr], meta.costCap) } - p.lookup[meta.hash] = meta.id + p.lookup.track(meta) p.stored += uint64(meta.size) return nil } @@ -1033,7 +1041,7 @@ func (p *BlobPool) SetGasTip(tip *big.Int) { ) p.spent[addr] = new(uint256.Int).Sub(p.spent[addr], txs[i].costCap) p.stored -= uint64(tx.size) - delete(p.lookup, tx.hash) + p.lookup.untrack(tx) txs[i] = nil // Drop everything afterwards, no gaps allowed @@ -1043,7 +1051,7 @@ func (p *BlobPool) SetGasTip(tip *big.Int) { p.spent[addr] = new(uint256.Int).Sub(p.spent[addr], tx.costCap) p.stored -= uint64(tx.size) - delete(p.lookup, tx.hash) + p.lookup.untrack(tx) txs[i+1+j] = nil } // Clear out the dropped transactions from the index @@ -1087,7 +1095,8 @@ func (p *BlobPool) validateTx(tx *types.Transaction) error { MaxSize: txMaxSize, MinTip: p.gasTip.ToBig(), } - if err := txpool.ValidateTransaction(tx, p.head, p.signer, baseOpts); err != nil { + + if err := p.txValidationFn(tx, p.head, p.signer, baseOpts); err != nil { return err } // Ensure the transaction adheres to the stateful pool filters (nonce, balance) @@ -1171,8 +1180,7 @@ func (p *BlobPool) Has(hash common.Hash) bool { p.lock.RLock() defer p.lock.RUnlock() - _, ok := p.lookup[hash] - return ok + return p.lookup.exists(hash) } // Get returns a transaction if it is contained in the pool, or nil otherwise. @@ -1189,7 +1197,7 @@ func (p *BlobPool) Get(hash common.Hash) *types.Transaction { }(time.Now()) // Pull the blob from disk and return an assembled response - id, ok := p.lookup[hash] + id, ok := p.lookup.storeidOfTx(hash) if !ok { return nil } @@ -1206,6 +1214,58 @@ func (p *BlobPool) Get(hash common.Hash) *types.Transaction { return item } +// GetBlobs returns a number of blobs are proofs for the given versioned hashes. +// This is a utility method for the engine API, enabling consensus clients to +// retrieve blobs from the pools directly instead of the network. +func (p *BlobPool) GetBlobs(vhashes []common.Hash) ([]*kzg4844.Blob, []*kzg4844.Proof) { + // Create a map of the blob hash to indices for faster fills + var ( + blobs = make([]*kzg4844.Blob, len(vhashes)) + proofs = make([]*kzg4844.Proof, len(vhashes)) + ) + index := make(map[common.Hash]int) + for i, vhash := range vhashes { + index[vhash] = i + } + // Iterate over the blob hashes, pulling transactions that fill it. Take care + // to also fill anything else the transaction might include (probably will). + for i, vhash := range vhashes { + // If already filled by a previous fetch, skip + if blobs[i] != nil { + continue + } + // Unfilled, retrieve the datastore item (in a short lock) + p.lock.RLock() + id, exists := p.lookup.storeidOfBlob(vhash) + if !exists { + p.lock.RUnlock() + continue + } + data, err := p.store.Get(id) + p.lock.RUnlock() + + // After releasing the lock, try to fill any blobs requested + if err != nil { + log.Error("Tracked blob transaction missing from store", "id", id, "err", err) + continue + } + item := new(types.Transaction) + if err = rlp.DecodeBytes(data, item); err != nil { + log.Error("Blobs corrupted for traced transaction", "id", id, "err", err) + continue + } + // Fill anything requested, not just the current versioned hash + sidecar := item.BlobTxSidecar() + for j, blobhash := range item.BlobHashes() { + if idx, ok := index[blobhash]; ok { + blobs[idx] = &sidecar.Blobs[j] + proofs[idx] = &sidecar.Proofs[j] + } + } + } + return blobs, proofs +} + // Add inserts a set of blob transactions into the pool if they pass validation (both // consensus validity and pool restrictions). func (p *BlobPool) Add(txs []*types.Transaction, local bool, sync bool) []error { @@ -1319,8 +1379,8 @@ func (p *BlobPool) add(tx *types.Transaction) (err error) { p.spent[from] = new(uint256.Int).Sub(p.spent[from], prev.costCap) p.spent[from] = new(uint256.Int).Add(p.spent[from], meta.costCap) - delete(p.lookup, prev.hash) - p.lookup[meta.hash] = meta.id + p.lookup.untrack(prev) + p.lookup.track(meta) p.stored += uint64(meta.size) - uint64(prev.size) } else { // Transaction extends previously scheduled ones @@ -1330,7 +1390,7 @@ func (p *BlobPool) add(tx *types.Transaction) (err error) { newacc = true } p.spent[from] = new(uint256.Int).Add(p.spent[from], meta.costCap) - p.lookup[meta.hash] = meta.id + p.lookup.track(meta) p.stored += uint64(meta.size) } // Recompute the rolling eviction fields. In case of a replacement, this will @@ -1419,7 +1479,7 @@ func (p *BlobPool) drop() { p.spent[from] = new(uint256.Int).Sub(p.spent[from], drop.costCap) } p.stored -= uint64(drop.size) - delete(p.lookup, drop.hash) + p.lookup.untrack(drop) // Remove the transaction from the pool's eviction heap: // - If the entire account was dropped, pop off the address @@ -1654,3 +1714,53 @@ func (p *BlobPool) Status(hash common.Hash) txpool.TxStatus { } return txpool.TxStatusUnknown } + +// Clear implements txpool.SubPool, removing all tracked transactions +// from the blob pool and persistent store. +func (p *BlobPool) Clear() { + p.lock.Lock() + defer p.lock.Unlock() + + // manually iterating and deleting every entry is super sub-optimal + // However, Clear is not currently used in production so + // performance is not critical at the moment. + for hash := range p.lookup.txIndex { + id, _ := p.lookup.storeidOfTx(hash) + if err := p.store.Delete(id); err != nil { + log.Warn("failed to delete blob tx from backing store", "err", err) + } + } + for hash := range p.lookup.blobIndex { + id, _ := p.lookup.storeidOfBlob(hash) + if err := p.store.Delete(id); err != nil { + log.Warn("failed to delete blob from backing store", "err", err) + } + } + + // unreserve each tracked account. Ideally, we could just clear the + // reservation map in the parent txpool context. However, if we clear in + // parent context, to avoid exposing the subpool lock, we have to lock the + // reservations and then lock each subpool. + // + // This creates the potential for a deadlock situation: + // + // * TxPool.Clear locks the reservations + // * a new transaction is received which locks the subpool mutex + // * TxPool.Clear attempts to lock subpool mutex + // + // The transaction addition may attempt to reserve the sender addr which + // can't happen until Clear releases the reservation lock. Clear cannot + // acquire the subpool lock until the transaction addition is completed. + for acct, _ := range p.index { + p.reserve(acct, false) + } + p.lookup = newLookup() + p.index = make(map[common.Address][]*blobTxMeta) + p.spent = make(map[common.Address]*uint256.Int) + + var ( + basefee = uint256.MustFromBig(eip1559.CalcBaseFee(p.chain.Config(), p.head)) + blobfee = uint256.NewInt(params.BlobTxMinBlobGasprice) + ) + p.evict = newPriceHeap(basefee, blobfee, p.index) +} diff --git a/core/txpool/blobpool/blobpool_test.go b/core/txpool/blobpool/blobpool_test.go index 9c36cf39b3..e4441bec5d 100644 --- a/core/txpool/blobpool/blobpool_test.go +++ b/core/txpool/blobpool/blobpool_test.go @@ -45,12 +45,28 @@ import ( ) var ( - emptyBlob = new(kzg4844.Blob) - emptyBlobCommit, _ = kzg4844.BlobToCommitment(emptyBlob) - emptyBlobProof, _ = kzg4844.ComputeBlobProof(emptyBlob, emptyBlobCommit) - emptyBlobVHash = kzg4844.CalcBlobHashV1(sha256.New(), &emptyBlobCommit) + testBlobs []*kzg4844.Blob + testBlobCommits []kzg4844.Commitment + testBlobProofs []kzg4844.Proof + testBlobVHashes [][32]byte ) +func init() { + for i := 0; i < 10; i++ { + testBlob := &kzg4844.Blob{byte(i)} + testBlobs = append(testBlobs, testBlob) + + testBlobCommit, _ := kzg4844.BlobToCommitment(testBlob) + testBlobCommits = append(testBlobCommits, testBlobCommit) + + testBlobProof, _ := kzg4844.ComputeBlobProof(testBlob, testBlobCommit) + testBlobProofs = append(testBlobProofs, testBlobProof) + + testBlobVHash := kzg4844.CalcBlobHashV1(sha256.New(), &testBlobCommit) + testBlobVHashes = append(testBlobVHashes, testBlobVHash) + } +} + // testBlockChain is a mock of the live chain for testing the pool. type testBlockChain struct { config *params.ChainConfig @@ -181,6 +197,12 @@ func makeTx(nonce uint64, gasTipCap uint64, gasFeeCap uint64, blobFeeCap uint64, // makeUnsignedTx is a utility method to construct a random blob transaction // without signing it. func makeUnsignedTx(nonce uint64, gasTipCap uint64, gasFeeCap uint64, blobFeeCap uint64) *types.BlobTx { + return makeUnsignedTxWithTestBlob(nonce, gasTipCap, gasFeeCap, blobFeeCap, rand.Intn(len(testBlobs))) +} + +// makeUnsignedTx is a utility method to construct a random blob transaction +// without signing it. +func makeUnsignedTxWithTestBlob(nonce uint64, gasTipCap uint64, gasFeeCap uint64, blobFeeCap uint64, blobIdx int) *types.BlobTx { return &types.BlobTx{ ChainID: uint256.MustFromBig(params.MainnetChainConfig.ChainID), Nonce: nonce, @@ -188,12 +210,12 @@ func makeUnsignedTx(nonce uint64, gasTipCap uint64, gasFeeCap uint64, blobFeeCap GasFeeCap: uint256.NewInt(gasFeeCap), Gas: 21000, BlobFeeCap: uint256.NewInt(blobFeeCap), - BlobHashes: []common.Hash{emptyBlobVHash}, + BlobHashes: []common.Hash{testBlobVHashes[blobIdx]}, Value: uint256.NewInt(100), Sidecar: &types.BlobTxSidecar{ - Blobs: []kzg4844.Blob{*emptyBlob}, - Commitments: []kzg4844.Commitment{emptyBlobCommit}, - Proofs: []kzg4844.Proof{emptyBlobProof}, + Blobs: []kzg4844.Blob{*testBlobs[blobIdx]}, + Commitments: []kzg4844.Commitment{testBlobCommits[blobIdx]}, + Proofs: []kzg4844.Proof{testBlobProofs[blobIdx]}, }, } } @@ -204,7 +226,7 @@ func verifyPoolInternals(t *testing.T, pool *BlobPool) { // Mark this method as a helper to remove from stack traces t.Helper() - // Verify that all items in the index are present in the lookup and nothing more + // Verify that all items in the index are present in the tx lookup and nothing more seen := make(map[common.Hash]struct{}) for addr, txs := range pool.index { for _, tx := range txs { @@ -214,14 +236,40 @@ func verifyPoolInternals(t *testing.T, pool *BlobPool) { seen[tx.hash] = struct{}{} } } - for hash, id := range pool.lookup { + for hash, id := range pool.lookup.txIndex { if _, ok := seen[hash]; !ok { - t.Errorf("lookup entry missing from transaction index: hash #%x, id %d", hash, id) + t.Errorf("tx lookup entry missing from transaction index: hash #%x, id %d", hash, id) } delete(seen, hash) } for hash := range seen { - t.Errorf("indexed transaction hash #%x missing from lookup table", hash) + t.Errorf("indexed transaction hash #%x missing from tx lookup table", hash) + } + // Verify that all blobs in the index are present in the blob lookup and nothing more + blobs := make(map[common.Hash]map[common.Hash]struct{}) + for _, txs := range pool.index { + for _, tx := range txs { + for _, vhash := range tx.vhashes { + if blobs[vhash] == nil { + blobs[vhash] = make(map[common.Hash]struct{}) + } + blobs[vhash][tx.hash] = struct{}{} + } + } + } + for vhash, txs := range pool.lookup.blobIndex { + for txhash := range txs { + if _, ok := blobs[vhash][txhash]; !ok { + t.Errorf("blob lookup entry missing from transaction index: blob hash #%x, tx hash #%x", vhash, txhash) + } + delete(blobs[vhash], txhash) + if len(blobs[vhash]) == 0 { + delete(blobs, vhash) + } + } + } + for vhash := range blobs { + t.Errorf("indexed transaction blob hash #%x missing from blob lookup table", vhash) } // Verify that transactions are sorted per account and contain no nonce gaps, // and that the first nonce is the next expected one based on the state. @@ -294,6 +342,53 @@ func verifyPoolInternals(t *testing.T, pool *BlobPool) { } // Verify the price heap internals verifyHeapInternals(t, pool.evict) + + // Verify that all the blobs can be retrieved + verifyBlobRetrievals(t, pool) +} + +// verifyBlobRetrievals attempts to retrieve all testing blobs and checks that +// whatever is in the pool, it can be retrieved correctly. +func verifyBlobRetrievals(t *testing.T, pool *BlobPool) { + // Collect all the blobs tracked by the pool + known := make(map[common.Hash]struct{}) + for _, txs := range pool.index { + for _, tx := range txs { + for _, vhash := range tx.vhashes { + known[vhash] = struct{}{} + } + } + } + // Attempt to retrieve all test blobs + hashes := make([]common.Hash, len(testBlobVHashes)) + for i := range testBlobVHashes { + copy(hashes[i][:], testBlobVHashes[i][:]) + } + blobs, proofs := pool.GetBlobs(hashes) + + // Cross validate what we received vs what we wanted + if len(blobs) != len(hashes) || len(proofs) != len(hashes) { + t.Errorf("retrieved blobs/proofs size mismatch: have %d/%d, want %d", len(blobs), len(proofs), len(hashes)) + return + } + for i, hash := range hashes { + // If an item is missing, but shouldn't, error + if blobs[i] == nil || proofs[i] == nil { + if _, ok := known[hash]; ok { + t.Errorf("tracked blob retrieval failed: item %d, hash %x", i, hash) + } + continue + } + // Item retrieved, make sure it matches the expectation + if *blobs[i] != *testBlobs[i] || *proofs[i] != testBlobProofs[i] { + t.Errorf("retrieved blob or proof mismatch: item %d, hash %x", i, hash) + continue + } + delete(known, hash) + } + for hash := range known { + t.Errorf("indexed blob #%x missing from retrieval", hash) + } } // Tests that transactions can be loaded from disk on startup and that they are @@ -969,21 +1064,21 @@ func TestAdd(t *testing.T) { "alice": { balance: 1000000, txs: []*types.BlobTx{ - makeUnsignedTx(0, 1, 1, 1), + makeUnsignedTxWithTestBlob(0, 1, 1, 1, 0), }, }, "bob": { balance: 1000000, nonce: 1, txs: []*types.BlobTx{ - makeUnsignedTx(1, 1, 1, 1), + makeUnsignedTxWithTestBlob(1, 1, 1, 1, 1), }, }, }, adds: []addtx{ { // New account, 1 tx pending: reject duplicate nonce 0 from: "alice", - tx: makeUnsignedTx(0, 1, 1, 1), + tx: makeUnsignedTxWithTestBlob(0, 1, 1, 1, 0), err: txpool.ErrAlreadyKnown, }, { // New account, 1 tx pending: reject replacement nonce 0 (ignore price for now) @@ -1013,7 +1108,7 @@ func TestAdd(t *testing.T) { }, { // Old account, 1 tx in chain, 1 tx pending: reject duplicate nonce 1 from: "bob", - tx: makeUnsignedTx(1, 1, 1, 1), + tx: makeUnsignedTxWithTestBlob(1, 1, 1, 1, 1), err: txpool.ErrAlreadyKnown, }, { // Old account, 1 tx in chain, 1 tx pending: accept nonce 2 (ignore price for now) @@ -1354,11 +1449,29 @@ func TestAdd(t *testing.T) { } } +// fakeBilly is a billy.Database implementation which just drops data on the floor. +type fakeBilly struct { + billy.Database + count uint64 +} + +func (f *fakeBilly) Put(data []byte) (uint64, error) { + f.count++ + return f.count, nil +} + +var _ billy.Database = (*fakeBilly)(nil) + // Benchmarks the time it takes to assemble the lazy pending transaction list // from the pool contents. func BenchmarkPoolPending100Mb(b *testing.B) { benchmarkPoolPending(b, 100_000_000) } func BenchmarkPoolPending1GB(b *testing.B) { benchmarkPoolPending(b, 1_000_000_000) } -func BenchmarkPoolPending10GB(b *testing.B) { benchmarkPoolPending(b, 10_000_000_000) } +func BenchmarkPoolPending10GB(b *testing.B) { + if testing.Short() { + b.Skip("Skipping in short-mode") + } + benchmarkPoolPending(b, 10_000_000_000) +} func benchmarkPoolPending(b *testing.B, datacap uint64) { // Calculate the maximum number of transaction that would fit into the pool @@ -1382,6 +1495,15 @@ func benchmarkPoolPending(b *testing.B, datacap uint64) { if err := pool.Init(1, chain.CurrentBlock(), makeAddressReserver()); err != nil { b.Fatalf("failed to create blob pool: %v", err) } + // Make the pool not use disk (just drop everything). This test never reads + // back the data, it just iterates over the pool in-memory items + pool.store = &fakeBilly{pool.store, 0} + // Avoid validation - verifying all blob proofs take significant time + // when the capacity is large. The purpose of this bench is to measure assembling + // the lazies, not the kzg verifications. + pool.txValidationFn = func(tx *types.Transaction, head *types.Header, signer types.Signer, opts *txpool.ValidationOptions) error { + return nil // accept all + } // Fill the pool up with one random transaction from each account with the // same price and everything to maximize the worst case scenario for i := 0; i < int(capacity); i++ { diff --git a/core/txpool/blobpool/evictheap_test.go b/core/txpool/blobpool/evictheap_test.go index 1cf577cb00..b03dd83d69 100644 --- a/core/txpool/blobpool/evictheap_test.go +++ b/core/txpool/blobpool/evictheap_test.go @@ -239,9 +239,25 @@ func BenchmarkPriceHeapOverflow10MB(b *testing.B) { benchmarkPriceHeapOverflow( func BenchmarkPriceHeapOverflow100MB(b *testing.B) { benchmarkPriceHeapOverflow(b, 100*1024*1024) } func BenchmarkPriceHeapOverflow1GB(b *testing.B) { benchmarkPriceHeapOverflow(b, 1024*1024*1024) } func BenchmarkPriceHeapOverflow10GB(b *testing.B) { benchmarkPriceHeapOverflow(b, 10*1024*1024*1024) } -func BenchmarkPriceHeapOverflow25GB(b *testing.B) { benchmarkPriceHeapOverflow(b, 25*1024*1024*1024) } -func BenchmarkPriceHeapOverflow50GB(b *testing.B) { benchmarkPriceHeapOverflow(b, 50*1024*1024*1024) } -func BenchmarkPriceHeapOverflow100GB(b *testing.B) { benchmarkPriceHeapOverflow(b, 100*1024*1024*1024) } + +func BenchmarkPriceHeapOverflow25GB(b *testing.B) { + if testing.Short() { + b.Skip("Skipping in short-mode") + } + benchmarkPriceHeapOverflow(b, 25*1024*1024*1024) +} +func BenchmarkPriceHeapOverflow50GB(b *testing.B) { + if testing.Short() { + b.Skip("Skipping in short-mode") + } + benchmarkPriceHeapOverflow(b, 50*1024*1024*1024) +} +func BenchmarkPriceHeapOverflow100GB(b *testing.B) { + if testing.Short() { + b.Skip("Skipping in short-mode") + } + benchmarkPriceHeapOverflow(b, 100*1024*1024*1024) +} func benchmarkPriceHeapOverflow(b *testing.B, datacap uint64) { // Calculate how many unique transactions we can fit into the provided disk diff --git a/core/txpool/blobpool/lookup.go b/core/txpool/blobpool/lookup.go new file mode 100644 index 0000000000..2d8d0fd2bf --- /dev/null +++ b/core/txpool/blobpool/lookup.go @@ -0,0 +1,91 @@ +// Copyright 2022 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 blobpool + +import ( + "github.com/ethereum/go-ethereum/common" +) + +// lookup maps blob versioned hashes to transaction hashes that include them, +// and transaction hashes to billy entries that include them. +type lookup struct { + blobIndex map[common.Hash]map[common.Hash]struct{} + txIndex map[common.Hash]uint64 +} + +// newLookup creates a new index for tracking blob to tx; and tx to billy mappings. +func newLookup() *lookup { + return &lookup{ + blobIndex: make(map[common.Hash]map[common.Hash]struct{}), + txIndex: make(map[common.Hash]uint64), + } +} + +// exists returns whether a transaction is already tracked or not. +func (l *lookup) exists(txhash common.Hash) bool { + _, exists := l.txIndex[txhash] + return exists +} + +// storeidOfTx returns the datastore storage item id of a transaction. +func (l *lookup) storeidOfTx(txhash common.Hash) (uint64, bool) { + id, ok := l.txIndex[txhash] + return id, ok +} + +// storeidOfBlob returns the datastore storage item id of a blob. +func (l *lookup) storeidOfBlob(vhash common.Hash) (uint64, bool) { + // If the blob is unknown, return a miss + txs, ok := l.blobIndex[vhash] + if !ok { + return 0, false + } + // If the blob is known, return any tx for it + for tx := range txs { + return l.storeidOfTx(tx) + } + return 0, false // Weird, don't choke +} + +// track inserts a new set of mappings from blob versioned hashes to transaction +// hashes; and from transaction hashes to datastore storage item ids. +func (l *lookup) track(tx *blobTxMeta) { + // Map all the blobs to the transaction hash + for _, vhash := range tx.vhashes { + if _, ok := l.blobIndex[vhash]; !ok { + l.blobIndex[vhash] = make(map[common.Hash]struct{}) + } + l.blobIndex[vhash][tx.hash] = struct{}{} // may be double mapped if a tx contains the same blob twice + } + // Map the transaction hash to the datastore id + l.txIndex[tx.hash] = tx.id +} + +// untrack removes a set of mappings from blob versioned hashes to transaction +// hashes from the blob index. +func (l *lookup) untrack(tx *blobTxMeta) { + // Unmap the transaction hash from the datastore id + delete(l.txIndex, tx.hash) + + // Unmap all the blobs from the transaction hash + for _, vhash := range tx.vhashes { + delete(l.blobIndex[vhash], tx.hash) // may be double deleted if a tx contains the same blob twice + if len(l.blobIndex[vhash]) == 0 { + delete(l.blobIndex, vhash) + } + } +} diff --git a/core/txpool/legacypool/legacypool.go b/core/txpool/legacypool/legacypool.go index 5506ecc31f..70be7034ee 100644 --- a/core/txpool/legacypool/legacypool.go +++ b/core/txpool/legacypool/legacypool.go @@ -33,6 +33,7 @@ import ( "github.com/ethereum/go-ethereum/core/state" "github.com/ethereum/go-ethereum/core/txpool" "github.com/ethereum/go-ethereum/core/types" + "github.com/ethereum/go-ethereum/crypto/kzg4844" "github.com/ethereum/go-ethereum/event" "github.com/ethereum/go-ethereum/log" "github.com/ethereum/go-ethereum/metrics" @@ -625,7 +626,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 { opts := &txpool.ValidationOptionsWithState{ State: pool.currentState, @@ -681,7 +682,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 @@ -1077,6 +1078,12 @@ func (pool *LegacyPool) get(hash common.Hash) *types.Transaction { return pool.all.Get(hash) } +// GetBlobs is not supported by the legacy transaction pool, it is just here to +// implement the txpool.SubPool interface. +func (pool *LegacyPool) GetBlobs(vhashes []common.Hash) ([]*kzg4844.Blob, []*kzg4844.Proof) { + return nil, nil +} + // Has returns an indicator whether txpool has a transaction cached with the // given hash. func (pool *LegacyPool) Has(hash common.Hash) bool { @@ -1954,3 +1961,44 @@ func (t *lookup) RemotesBelowTip(threshold *big.Int) types.Transactions { func numSlots(tx *types.Transaction) int { return int((tx.Size() + txSlotSize - 1) / txSlotSize) } + +// Clear implements txpool.SubPool, removing all tracked txs from the pool +// and rotating the journal. +func (pool *LegacyPool) Clear() { + pool.mu.Lock() + defer pool.mu.Unlock() + + // unreserve each tracked account. Ideally, we could just clear the + // reservation map in the parent txpool context. However, if we clear in + // parent context, to avoid exposing the subpool lock, we have to lock the + // reservations and then lock each subpool. + // + // This creates the potential for a deadlock situation: + // + // * TxPool.Clear locks the reservations + // * a new transaction is received which locks the subpool mutex + // * TxPool.Clear attempts to lock subpool mutex + // + // The transaction addition may attempt to reserve the sender addr which + // can't happen until Clear releases the reservation lock. Clear cannot + // acquire the subpool lock until the transaction addition is completed. + for _, tx := range pool.all.remotes { + senderAddr, _ := types.Sender(pool.signer, tx) + pool.reserve(senderAddr, false) + } + for localSender, _ := range pool.locals.accounts { + pool.reserve(localSender, false) + } + + pool.all = newLookup() + pool.priced = newPricedList(pool.all) + pool.pending = make(map[common.Address]*list) + pool.queue = make(map[common.Address]*list) + + if !pool.config.NoLocals && pool.config.Journal != "" { + pool.journal = newTxJournal(pool.config.Journal) + if err := pool.journal.rotate(pool.local()); err != nil { + log.Warn("Failed to rotate transaction journal", "err", err) + } + } +} diff --git a/core/txpool/subpool.go b/core/txpool/subpool.go index 9881ed1b8f..9ee0a69c0b 100644 --- a/core/txpool/subpool.go +++ b/core/txpool/subpool.go @@ -23,6 +23,7 @@ import ( "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/core" "github.com/ethereum/go-ethereum/core/types" + "github.com/ethereum/go-ethereum/crypto/kzg4844" "github.com/ethereum/go-ethereum/event" "github.com/holiman/uint256" ) @@ -123,6 +124,11 @@ type SubPool interface { // Get returns a transaction if it is contained in the pool, or nil otherwise. Get(hash common.Hash) *types.Transaction + // GetBlobs returns a number of blobs are proofs for the given versioned hashes. + // This is a utility method for the engine API, enabling consensus clients to + // retrieve blobs from the pools directly instead of the network. + GetBlobs(vhashes []common.Hash) ([]*kzg4844.Blob, []*kzg4844.Proof) + // Add enqueues a batch of transactions into the pool if they are valid. Due // to the large transaction churn, add may postpone fully integrating the tx // to a later point to batch multiple ones together. @@ -162,4 +168,7 @@ type SubPool interface { // Status returns the known status (unknown/pending/queued) of a transaction // identified by their hashes. Status(hash common.Hash) TxStatus + + // Clear removes all tracked transactions from the pool + Clear() } diff --git a/core/txpool/txpool.go b/core/txpool/txpool.go index be7435247d..d54cf4968b 100644 --- a/core/txpool/txpool.go +++ b/core/txpool/txpool.go @@ -25,6 +25,7 @@ import ( "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/core" "github.com/ethereum/go-ethereum/core/types" + "github.com/ethereum/go-ethereum/crypto/kzg4844" "github.com/ethereum/go-ethereum/event" "github.com/ethereum/go-ethereum/log" "github.com/ethereum/go-ethereum/metrics" @@ -125,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) } @@ -142,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) } @@ -243,7 +244,7 @@ func (p *TxPool) loop(head *types.Header, chain BlockChain) { select { case event := <-newHeadCh: // Chain moved forward, store the head for later consumption - newHead = event.Block.Header() + newHead = event.Header case head := <-resetDone: // Previous reset finished, update the old head and allow a new reset @@ -305,6 +306,22 @@ func (p *TxPool) Get(hash common.Hash) *types.Transaction { return nil } +// GetBlobs returns a number of blobs are proofs for the given versioned hashes. +// This is a utility method for the engine API, enabling consensus clients to +// retrieve blobs from the pools directly instead of the network. +func (p *TxPool) GetBlobs(vhashes []common.Hash) ([]*kzg4844.Blob, []*kzg4844.Proof) { + for _, subpool := range p.subpools { + // It's an ugly to assume that only one pool will be capable of returning + // anything meaningful for this call, but anythingh else requires merging + // partial responses and that's too annoying to do until we get a second + // blobpool (probably never). + if blobs, proofs := subpool.GetBlobs(vhashes); blobs != nil { + return blobs, proofs + } + } + return nil, nil +} + // Add enqueues a batch of transactions into the pool if they are valid. Due // to the large transaction churn, add may postpone fully integrating the tx // to a later point to batch multiple ones together. @@ -341,7 +358,7 @@ func (p *TxPool) Add(txs []*types.Transaction, local bool, sync bool) []error { for i, split := range splits { // If the transaction was rejected by all subpools, mark it unsupported if split == -1 { - errs[i] = core.ErrTxTypeNotSupported + errs[i] = fmt.Errorf("%w: received type %d", core.ErrTxTypeNotSupported, txs[i].Type()) continue } // Find which subpool handled it and pull in the corresponding error @@ -480,3 +497,10 @@ func (p *TxPool) Sync() error { return errors.New("pool already terminated") } } + +// Clear removes all tracked txs from the subpools. +func (p *TxPool) Clear() { + for _, subpool := range p.subpools { + subpool.Clear() + } +} diff --git a/core/txpool/validation.go b/core/txpool/validation.go index 7fd5f8bc79..412418dcc9 100644 --- a/core/txpool/validation.go +++ b/core/txpool/validation.go @@ -47,6 +47,11 @@ type ValidationOptions struct { MinTip *big.Int // Minimum gas tip needed to allow a transaction into the caller pool } +// ValidationFunction is an method type which the pools use to perform the tx-validations which do not +// require state access. Production code typically uses ValidateTransaction, whereas testing-code +// might choose to instead use something else, e.g. to always fail or avoid heavy cpu usage. +type ValidationFunction func(tx *types.Transaction, head *types.Header, signer types.Signer, opts *ValidationOptions) error + // ValidateTransaction is a helper method to check whether a transaction is valid // according to the consensus rules, but does not check state-dependent validation // (balance, nonce, etc). @@ -99,11 +104,11 @@ func ValidateTransaction(tx *types.Transaction, head *types.Header, signer types } // Make sure the transaction is signed properly if _, err := types.Sender(signer, tx); err != nil { - return ErrInvalidSender + return fmt.Errorf("%w: %v", ErrInvalidSender, err) } // Ensure the transaction has more gas than the bare minimum needed to cover // the transaction metadata - intrGas, err := core.IntrinsicGas(tx.Data(), tx.AccessList(), tx.To() == nil, true, opts.Config.IsIstanbul(head.Number), opts.Config.IsShanghai(head.Number, head.Time)) + intrGas, err := core.IntrinsicGas(tx.Data(), tx.AccessList(), tx.SetCodeAuthorizations(), tx.To() == nil, true, opts.Config.IsIstanbul(head.Number), opts.Config.IsShanghai(head.Number, head.Time)) if err != nil { return err } diff --git a/core/types/block.go b/core/types/block.go index f20fc7d778..c3db4d89e8 100644 --- a/core/types/block.go +++ b/core/types/block.go @@ -105,7 +105,7 @@ type Header struct { ParentBeaconRoot *common.Hash `json:"parentBeaconBlockRoot" rlp:"optional"` // RequestsHash was added by EIP-7685 and is ignored in legacy headers. - RequestsHash *common.Hash `json:"requestsRoot" rlp:"optional"` + RequestsHash *common.Hash `json:"requestsHash" rlp:"optional"` } // field type overrides for gencodec @@ -463,9 +463,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/block_test.go b/core/types/block_test.go index 1af5b9d7bf..1f61be89a5 100644 --- a/core/types/block_test.go +++ b/core/types/block_test.go @@ -18,6 +18,7 @@ package types import ( "bytes" + gomath "math" "math/big" "reflect" "testing" @@ -277,9 +278,9 @@ func TestRlpDecodeParentHash(t *testing.T) { if rlpData, err := rlp.EncodeToBytes(&Header{ ParentHash: want, Difficulty: mainnetTd, - Number: new(big.Int).SetUint64(math.MaxUint64), + Number: new(big.Int).SetUint64(gomath.MaxUint64), Extra: make([]byte, 65+32), - BaseFee: new(big.Int).SetUint64(math.MaxUint64), + BaseFee: new(big.Int).SetUint64(gomath.MaxUint64), }); err != nil { t.Fatal(err) } else { diff --git a/core/types/gen_authorization.go b/core/types/gen_authorization.go new file mode 100644 index 0000000000..be5467c50d --- /dev/null +++ b/core/types/gen_authorization.go @@ -0,0 +1,75 @@ +// Code generated by github.com/fjl/gencodec. DO NOT EDIT. + +package types + +import ( + "encoding/json" + "errors" + + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/common/hexutil" + "github.com/holiman/uint256" +) + +var _ = (*authorizationMarshaling)(nil) + +// MarshalJSON marshals as JSON. +func (s SetCodeAuthorization) MarshalJSON() ([]byte, error) { + type SetCodeAuthorization struct { + ChainID hexutil.Uint64 `json:"chainId" gencodec:"required"` + Address common.Address `json:"address" gencodec:"required"` + Nonce hexutil.Uint64 `json:"nonce" gencodec:"required"` + V hexutil.Uint64 `json:"yParity" gencodec:"required"` + R hexutil.U256 `json:"r" gencodec:"required"` + S hexutil.U256 `json:"s" gencodec:"required"` + } + var enc SetCodeAuthorization + enc.ChainID = hexutil.Uint64(s.ChainID) + enc.Address = s.Address + enc.Nonce = hexutil.Uint64(s.Nonce) + enc.V = hexutil.Uint64(s.V) + enc.R = hexutil.U256(s.R) + enc.S = hexutil.U256(s.S) + return json.Marshal(&enc) +} + +// UnmarshalJSON unmarshals from JSON. +func (s *SetCodeAuthorization) UnmarshalJSON(input []byte) error { + type SetCodeAuthorization struct { + ChainID *hexutil.Uint64 `json:"chainId" gencodec:"required"` + Address *common.Address `json:"address" gencodec:"required"` + Nonce *hexutil.Uint64 `json:"nonce" gencodec:"required"` + V *hexutil.Uint64 `json:"yParity" gencodec:"required"` + R *hexutil.U256 `json:"r" gencodec:"required"` + S *hexutil.U256 `json:"s" gencodec:"required"` + } + var dec SetCodeAuthorization + if err := json.Unmarshal(input, &dec); err != nil { + return err + } + if dec.ChainID == nil { + return errors.New("missing required field 'chainId' for SetCodeAuthorization") + } + s.ChainID = uint64(*dec.ChainID) + if dec.Address == nil { + return errors.New("missing required field 'address' for SetCodeAuthorization") + } + s.Address = *dec.Address + if dec.Nonce == nil { + return errors.New("missing required field 'nonce' for SetCodeAuthorization") + } + s.Nonce = uint64(*dec.Nonce) + if dec.V == nil { + return errors.New("missing required field 'yParity' for SetCodeAuthorization") + } + s.V = uint8(*dec.V) + if dec.R == nil { + return errors.New("missing required field 'r' for SetCodeAuthorization") + } + s.R = uint256.Int(*dec.R) + if dec.S == nil { + return errors.New("missing required field 's' for SetCodeAuthorization") + } + s.S = uint256.Int(*dec.S) + return nil +} diff --git a/core/types/gen_header_json.go b/core/types/gen_header_json.go index 322c5d5642..0af12500bd 100644 --- a/core/types/gen_header_json.go +++ b/core/types/gen_header_json.go @@ -36,7 +36,7 @@ func (h Header) MarshalJSON() ([]byte, error) { BlobGasUsed *hexutil.Uint64 `json:"blobGasUsed" rlp:"optional"` ExcessBlobGas *hexutil.Uint64 `json:"excessBlobGas" rlp:"optional"` ParentBeaconRoot *common.Hash `json:"parentBeaconBlockRoot" rlp:"optional"` - RequestsHash *common.Hash `json:"requestsRoot" rlp:"optional"` + RequestsHash *common.Hash `json:"requestsHash" rlp:"optional"` Hash common.Hash `json:"hash"` } var enc Header @@ -88,7 +88,7 @@ func (h *Header) UnmarshalJSON(input []byte) error { BlobGasUsed *hexutil.Uint64 `json:"blobGasUsed" rlp:"optional"` ExcessBlobGas *hexutil.Uint64 `json:"excessBlobGas" rlp:"optional"` ParentBeaconRoot *common.Hash `json:"parentBeaconBlockRoot" rlp:"optional"` - RequestsHash *common.Hash `json:"requestsRoot" rlp:"optional"` + RequestsHash *common.Hash `json:"requestsHash" rlp:"optional"` } var dec Header if err := json.Unmarshal(input, &dec); err != nil { 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/types/receipt.go b/core/types/receipt.go index 4f96fde59c..47f16d950a 100644 --- a/core/types/receipt.go +++ b/core/types/receipt.go @@ -204,7 +204,7 @@ func (r *Receipt) decodeTyped(b []byte) error { return errShortTypedReceipt } switch b[0] { - case DynamicFeeTxType, AccessListTxType, BlobTxType: + case DynamicFeeTxType, AccessListTxType, BlobTxType, SetCodeTxType: var data receiptRLP err := rlp.DecodeBytes(b[1:], &data) if err != nil { @@ -312,7 +312,7 @@ func (rs Receipts) EncodeIndex(i int, w *bytes.Buffer) { } w.WriteByte(r.Type) switch r.Type { - case AccessListTxType, DynamicFeeTxType, BlobTxType: + case AccessListTxType, DynamicFeeTxType, BlobTxType, SetCodeTxType: rlp.Encode(w, data) default: // For unsupported types, write nothing. Since this is for diff --git a/core/types/transaction.go b/core/types/transaction.go index 6c8759ee69..35dc1ea3f9 100644 --- a/core/types/transaction.go +++ b/core/types/transaction.go @@ -26,7 +26,6 @@ import ( "time" "github.com/ethereum/go-ethereum/common" - "github.com/ethereum/go-ethereum/common/math" "github.com/ethereum/go-ethereum/crypto" "github.com/ethereum/go-ethereum/rlp" ) @@ -49,6 +48,7 @@ const ( AccessListTxType = 0x01 DynamicFeeTxType = 0x02 BlobTxType = 0x03 + SetCodeTxType = 0x04 ) // Transaction is an Ethereum transaction. @@ -206,6 +206,8 @@ func (tx *Transaction) decodeTyped(b []byte) (TxData, error) { inner = new(DynamicFeeTx) case BlobTxType: inner = new(BlobTx) + case SetCodeTxType: + inner = new(SetCodeTx) default: return nil, ErrTxTypeNotSupported } @@ -355,10 +357,16 @@ func (tx *Transaction) EffectiveGasTip(baseFee *big.Int) (*big.Int, error) { } var err error gasFeeCap := tx.GasFeeCap() - if gasFeeCap.Cmp(baseFee) == -1 { + if gasFeeCap.Cmp(baseFee) < 0 { err = ErrGasFeeCapTooLow } - return math.BigMin(tx.GasTipCap(), gasFeeCap.Sub(gasFeeCap, baseFee)), err + gasFeeCap = gasFeeCap.Sub(gasFeeCap, baseFee) + + gasTipCap := tx.GasTipCap() + if gasTipCap.Cmp(gasFeeCap) < 0 { + return gasTipCap, err + } + return gasFeeCap, err } // EffectiveGasTipValue is identical to EffectiveGasTip, but does not return an @@ -466,6 +474,15 @@ func (tx *Transaction) WithBlobTxSidecar(sideCar *BlobTxSidecar) *Transaction { return cpy } +// SetCodeAuthorizations returns the authorizations list of the transaction. +func (tx *Transaction) SetCodeAuthorizations() []SetCodeAuthorization { + setcodetx, ok := tx.inner.(*SetCodeTx) + if !ok { + return nil + } + return setcodetx.AuthList +} + // SetTime sets the decoding time of a transaction. This is used by tests to set // arbitrary times and by persistent transaction pools when loading old txs from // disk. diff --git a/core/types/transaction_marshalling.go b/core/types/transaction_marshalling.go index 4d5b2bcdd4..993d633c6f 100644 --- a/core/types/transaction_marshalling.go +++ b/core/types/transaction_marshalling.go @@ -31,22 +31,23 @@ import ( type txJSON struct { Type hexutil.Uint64 `json:"type"` - ChainID *hexutil.Big `json:"chainId,omitempty"` - Nonce *hexutil.Uint64 `json:"nonce"` - To *common.Address `json:"to"` - Gas *hexutil.Uint64 `json:"gas"` - GasPrice *hexutil.Big `json:"gasPrice"` - MaxPriorityFeePerGas *hexutil.Big `json:"maxPriorityFeePerGas"` - MaxFeePerGas *hexutil.Big `json:"maxFeePerGas"` - MaxFeePerBlobGas *hexutil.Big `json:"maxFeePerBlobGas,omitempty"` - Value *hexutil.Big `json:"value"` - Input *hexutil.Bytes `json:"input"` - AccessList *AccessList `json:"accessList,omitempty"` - BlobVersionedHashes []common.Hash `json:"blobVersionedHashes,omitempty"` - V *hexutil.Big `json:"v"` - R *hexutil.Big `json:"r"` - S *hexutil.Big `json:"s"` - YParity *hexutil.Uint64 `json:"yParity,omitempty"` + ChainID *hexutil.Big `json:"chainId,omitempty"` + Nonce *hexutil.Uint64 `json:"nonce"` + To *common.Address `json:"to"` + Gas *hexutil.Uint64 `json:"gas"` + GasPrice *hexutil.Big `json:"gasPrice"` + MaxPriorityFeePerGas *hexutil.Big `json:"maxPriorityFeePerGas"` + MaxFeePerGas *hexutil.Big `json:"maxFeePerGas"` + MaxFeePerBlobGas *hexutil.Big `json:"maxFeePerBlobGas,omitempty"` + Value *hexutil.Big `json:"value"` + Input *hexutil.Bytes `json:"input"` + AccessList *AccessList `json:"accessList,omitempty"` + BlobVersionedHashes []common.Hash `json:"blobVersionedHashes,omitempty"` + AuthorizationList []SetCodeAuthorization `json:"authorizationList,omitempty"` + V *hexutil.Big `json:"v"` + R *hexutil.Big `json:"r"` + S *hexutil.Big `json:"s"` + YParity *hexutil.Uint64 `json:"yParity,omitempty"` // Blob transaction sidecar encoding: Blobs []kzg4844.Blob `json:"blobs,omitempty"` @@ -153,6 +154,22 @@ func (tx *Transaction) MarshalJSON() ([]byte, error) { enc.Commitments = itx.Sidecar.Commitments enc.Proofs = itx.Sidecar.Proofs } + case *SetCodeTx: + enc.ChainID = (*hexutil.Big)(new(big.Int).SetUint64(itx.ChainID)) + enc.Nonce = (*hexutil.Uint64)(&itx.Nonce) + enc.To = tx.To() + enc.Gas = (*hexutil.Uint64)(&itx.Gas) + enc.MaxFeePerGas = (*hexutil.Big)(itx.GasFeeCap.ToBig()) + enc.MaxPriorityFeePerGas = (*hexutil.Big)(itx.GasTipCap.ToBig()) + enc.Value = (*hexutil.Big)(itx.Value.ToBig()) + enc.Input = (*hexutil.Bytes)(&itx.Data) + enc.AccessList = &itx.AccessList + enc.AuthorizationList = itx.AuthList + enc.V = (*hexutil.Big)(itx.V.ToBig()) + enc.R = (*hexutil.Big)(itx.R.ToBig()) + enc.S = (*hexutil.Big)(itx.S.ToBig()) + yparity := itx.V.Uint64() + enc.YParity = (*hexutil.Uint64)(&yparity) } return json.Marshal(&enc) } @@ -409,6 +426,81 @@ func (tx *Transaction) UnmarshalJSON(input []byte) error { } } + case SetCodeTxType: + var itx SetCodeTx + inner = &itx + if dec.ChainID == nil { + return errors.New("missing required field 'chainId' in transaction") + } + itx.ChainID = dec.ChainID.ToInt().Uint64() + if dec.Nonce == nil { + return errors.New("missing required field 'nonce' in transaction") + } + itx.Nonce = uint64(*dec.Nonce) + if dec.To == nil { + return errors.New("missing required field 'to' in transaction") + } + itx.To = *dec.To + if dec.Gas == nil { + return errors.New("missing required field 'gas' for txdata") + } + itx.Gas = uint64(*dec.Gas) + if dec.MaxPriorityFeePerGas == nil { + return errors.New("missing required field 'maxPriorityFeePerGas' for txdata") + } + itx.GasTipCap = uint256.MustFromBig((*big.Int)(dec.MaxPriorityFeePerGas)) + if dec.MaxFeePerGas == nil { + return errors.New("missing required field 'maxFeePerGas' for txdata") + } + itx.GasFeeCap = uint256.MustFromBig((*big.Int)(dec.MaxFeePerGas)) + if dec.Value == nil { + return errors.New("missing required field 'value' in transaction") + } + itx.Value = uint256.MustFromBig((*big.Int)(dec.Value)) + if dec.Input == nil { + return errors.New("missing required field 'input' in transaction") + } + itx.Data = *dec.Input + if dec.AccessList != nil { + itx.AccessList = *dec.AccessList + } + if dec.AuthorizationList == nil { + return errors.New("missing required field 'authorizationList' in transaction") + } + itx.AuthList = dec.AuthorizationList + + // signature R + var overflow bool + if dec.R == nil { + return errors.New("missing required field 'r' in transaction") + } + itx.R, overflow = uint256.FromBig((*big.Int)(dec.R)) + if overflow { + return errors.New("'r' value overflows uint256") + } + // signature S + if dec.S == nil { + return errors.New("missing required field 's' in transaction") + } + itx.S, overflow = uint256.FromBig((*big.Int)(dec.S)) + if overflow { + return errors.New("'s' value overflows uint256") + } + // signature V + vbig, err := dec.yParityValue() + if err != nil { + return err + } + itx.V, overflow = uint256.FromBig(vbig) + if overflow { + return errors.New("'v' value overflows uint256") + } + if itx.V.Sign() != 0 || itx.R.Sign() != 0 || itx.S.Sign() != 0 { + if err := sanityCheckSignature(vbig, itx.R.ToBig(), itx.S.ToBig(), false); err != nil { + return err + } + } + default: return ErrTxTypeNotSupported } diff --git a/core/types/transaction_signing.go b/core/types/transaction_signing.go index 73011e238b..d72643b4a8 100644 --- a/core/types/transaction_signing.go +++ b/core/types/transaction_signing.go @@ -40,6 +40,8 @@ type sigCache struct { func MakeSigner(config *params.ChainConfig, blockNumber *big.Int, blockTime uint64) Signer { var signer Signer switch { + case config.IsPrague(blockNumber, blockTime): + signer = NewPragueSigner(config.ChainID) case config.IsCancun(blockNumber, blockTime): signer = NewCancunSigner(config.ChainID) case config.IsLondon(blockNumber): @@ -67,6 +69,8 @@ func LatestSigner(config *params.ChainConfig) Signer { var signer Signer if config.ChainID != nil { switch { + case config.PragueTime != nil: + signer = NewPragueSigner(config.ChainID) case config.CancunTime != nil: signer = NewCancunSigner(config.ChainID) case config.LondonBlock != nil: @@ -94,7 +98,7 @@ func LatestSigner(config *params.ChainConfig) Signer { func LatestSignerForChainID(chainID *big.Int) Signer { var signer Signer if chainID != nil { - signer = NewCancunSigner(chainID) + signer = NewPragueSigner(chainID) } else { signer = HomesteadSigner{} } @@ -174,6 +178,77 @@ type Signer interface { Equal(Signer) bool } +type pragueSigner struct{ cancunSigner } + +// NewPragueSigner returns a signer that accepts +// - EIP-7702 set code transactions +// - EIP-4844 blob transactions +// - EIP-1559 dynamic fee transactions +// - EIP-2930 access list transactions, +// - EIP-155 replay protected transactions, and +// - legacy Homestead transactions. +func NewPragueSigner(chainId *big.Int) Signer { + signer, _ := NewCancunSigner(chainId).(cancunSigner) + return pragueSigner{signer} +} + +func (s pragueSigner) Sender(tx *Transaction) (common.Address, error) { + if tx.Type() != SetCodeTxType { + return s.cancunSigner.Sender(tx) + } + V, R, S := tx.RawSignatureValues() + + // Set code txs are defined to use 0 and 1 as their recovery + // id, add 27 to become equivalent to unprotected Homestead signatures. + V = new(big.Int).Add(V, big.NewInt(27)) + if tx.ChainId().Cmp(s.chainId) != 0 { + return common.Address{}, fmt.Errorf("%w: have %d want %d", ErrInvalidChainId, tx.ChainId(), s.chainId) + } + return recoverPlain(s.Hash(tx), R, S, V, true) +} + +func (s pragueSigner) Equal(s2 Signer) bool { + x, ok := s2.(pragueSigner) + return ok && x.chainId.Cmp(s.chainId) == 0 +} + +func (s pragueSigner) SignatureValues(tx *Transaction, sig []byte) (R, S, V *big.Int, err error) { + txdata, ok := tx.inner.(*SetCodeTx) + if !ok { + return s.cancunSigner.SignatureValues(tx, sig) + } + // Check that chain ID of tx matches the signer. We also accept ID zero here, + // because it indicates that the chain ID was not specified in the tx. + if txdata.ChainID != 0 && new(big.Int).SetUint64(txdata.ChainID).Cmp(s.chainId) != 0 { + return nil, nil, nil, fmt.Errorf("%w: have %d want %d", ErrInvalidChainId, txdata.ChainID, s.chainId) + } + R, S, _ = decodeSignature(sig) + V = big.NewInt(int64(sig[64])) + return R, S, V, nil +} + +// Hash returns the hash to be signed by the sender. +// It does not uniquely identify the transaction. +func (s pragueSigner) Hash(tx *Transaction) common.Hash { + if tx.Type() != SetCodeTxType { + return s.cancunSigner.Hash(tx) + } + return prefixedRlpHash( + tx.Type(), + []interface{}{ + s.chainId, + tx.Nonce(), + tx.GasTipCap(), + tx.GasFeeCap(), + tx.Gas(), + tx.To(), + tx.Value(), + tx.Data(), + tx.AccessList(), + tx.SetCodeAuthorizations(), + }) +} + type cancunSigner struct{ londonSigner } // NewCancunSigner returns a signer that accepts diff --git a/core/types/tx_setcode.go b/core/types/tx_setcode.go new file mode 100644 index 0000000000..f14ae3bc9d --- /dev/null +++ b/core/types/tx_setcode.go @@ -0,0 +1,222 @@ +// 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 types + +import ( + "bytes" + "crypto/ecdsa" + "errors" + "math/big" + + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/common/hexutil" + "github.com/ethereum/go-ethereum/crypto" + "github.com/ethereum/go-ethereum/rlp" + "github.com/holiman/uint256" +) + +// DelegationPrefix is used by code to denote the account is delegating to +// another account. +var DelegationPrefix = []byte{0xef, 0x01, 0x00} + +// ParseDelegation tries to parse the address from a delegation slice. +func ParseDelegation(b []byte) (common.Address, bool) { + if len(b) != 23 || !bytes.HasPrefix(b, DelegationPrefix) { + return common.Address{}, false + } + return common.BytesToAddress(b[len(DelegationPrefix):]), true +} + +// AddressToDelegation adds the delegation prefix to the specified address. +func AddressToDelegation(addr common.Address) []byte { + return append(DelegationPrefix, addr.Bytes()...) +} + +// SetCodeTx implements the EIP-7702 transaction type which temporarily installs +// the code at the signer's address. +type SetCodeTx struct { + ChainID uint64 + Nonce uint64 + GasTipCap *uint256.Int // a.k.a. maxPriorityFeePerGas + GasFeeCap *uint256.Int // a.k.a. maxFeePerGas + Gas uint64 + To common.Address + Value *uint256.Int + Data []byte + AccessList AccessList + AuthList []SetCodeAuthorization + + // Signature values + V *uint256.Int `json:"v" gencodec:"required"` + R *uint256.Int `json:"r" gencodec:"required"` + S *uint256.Int `json:"s" gencodec:"required"` +} + +//go:generate go run github.com/fjl/gencodec -type SetCodeAuthorization -field-override authorizationMarshaling -out gen_authorization.go + +// SetCodeAuthorization is an authorization from an account to deploy code at its address. +type SetCodeAuthorization struct { + ChainID uint64 `json:"chainId" gencodec:"required"` + Address common.Address `json:"address" gencodec:"required"` + Nonce uint64 `json:"nonce" gencodec:"required"` + V uint8 `json:"yParity" gencodec:"required"` + R uint256.Int `json:"r" gencodec:"required"` + S uint256.Int `json:"s" gencodec:"required"` +} + +// field type overrides for gencodec +type authorizationMarshaling struct { + ChainID hexutil.Uint64 + Nonce hexutil.Uint64 + V hexutil.Uint64 + R hexutil.U256 + S hexutil.U256 +} + +// SignSetCode creates a signed the SetCode authorization. +func SignSetCode(prv *ecdsa.PrivateKey, auth SetCodeAuthorization) (SetCodeAuthorization, error) { + sighash := auth.sigHash() + sig, err := crypto.Sign(sighash[:], prv) + if err != nil { + return SetCodeAuthorization{}, err + } + r, s, _ := decodeSignature(sig) + return SetCodeAuthorization{ + ChainID: auth.ChainID, + Address: auth.Address, + Nonce: auth.Nonce, + V: sig[64], + R: *uint256.MustFromBig(r), + S: *uint256.MustFromBig(s), + }, nil +} + +func (a *SetCodeAuthorization) sigHash() common.Hash { + return prefixedRlpHash(0x05, []any{ + a.ChainID, + a.Address, + a.Nonce, + }) +} + +// Authority recovers the the authorizing account of an authorization. +func (a *SetCodeAuthorization) Authority() (common.Address, error) { + sighash := a.sigHash() + if !crypto.ValidateSignatureValues(a.V, a.R.ToBig(), a.S.ToBig(), true) { + return common.Address{}, ErrInvalidSig + } + // encode the signature in uncompressed format + var sig [crypto.SignatureLength]byte + a.R.WriteToSlice(sig[:32]) + a.S.WriteToSlice(sig[32:64]) + sig[64] = a.V + // recover the public key from the signature + pub, err := crypto.Ecrecover(sighash[:], sig[:]) + if err != nil { + return common.Address{}, err + } + if len(pub) == 0 || pub[0] != 4 { + return common.Address{}, errors.New("invalid public key") + } + var addr common.Address + copy(addr[:], crypto.Keccak256(pub[1:])[12:]) + return addr, nil +} + +// copy creates a deep copy of the transaction data and initializes all fields. +func (tx *SetCodeTx) copy() TxData { + cpy := &SetCodeTx{ + Nonce: tx.Nonce, + To: tx.To, + Data: common.CopyBytes(tx.Data), + Gas: tx.Gas, + // These are copied below. + AccessList: make(AccessList, len(tx.AccessList)), + AuthList: make([]SetCodeAuthorization, len(tx.AuthList)), + Value: new(uint256.Int), + ChainID: tx.ChainID, + GasTipCap: new(uint256.Int), + GasFeeCap: new(uint256.Int), + V: new(uint256.Int), + R: new(uint256.Int), + S: new(uint256.Int), + } + copy(cpy.AccessList, tx.AccessList) + copy(cpy.AuthList, tx.AuthList) + if tx.Value != nil { + cpy.Value.Set(tx.Value) + } + if tx.GasTipCap != nil { + cpy.GasTipCap.Set(tx.GasTipCap) + } + if tx.GasFeeCap != nil { + cpy.GasFeeCap.Set(tx.GasFeeCap) + } + if tx.V != nil { + cpy.V.Set(tx.V) + } + if tx.R != nil { + cpy.R.Set(tx.R) + } + if tx.S != nil { + cpy.S.Set(tx.S) + } + return cpy +} + +// accessors for innerTx. +func (tx *SetCodeTx) txType() byte { return SetCodeTxType } +func (tx *SetCodeTx) chainID() *big.Int { return big.NewInt(int64(tx.ChainID)) } +func (tx *SetCodeTx) accessList() AccessList { return tx.AccessList } +func (tx *SetCodeTx) data() []byte { return tx.Data } +func (tx *SetCodeTx) gas() uint64 { return tx.Gas } +func (tx *SetCodeTx) gasFeeCap() *big.Int { return tx.GasFeeCap.ToBig() } +func (tx *SetCodeTx) gasTipCap() *big.Int { return tx.GasTipCap.ToBig() } +func (tx *SetCodeTx) gasPrice() *big.Int { return tx.GasFeeCap.ToBig() } +func (tx *SetCodeTx) value() *big.Int { return tx.Value.ToBig() } +func (tx *SetCodeTx) nonce() uint64 { return tx.Nonce } +func (tx *SetCodeTx) to() *common.Address { tmp := tx.To; return &tmp } + +func (tx *SetCodeTx) effectiveGasPrice(dst *big.Int, baseFee *big.Int) *big.Int { + if baseFee == nil { + return dst.Set(tx.GasFeeCap.ToBig()) + } + tip := dst.Sub(tx.GasFeeCap.ToBig(), baseFee) + if tip.Cmp(tx.GasTipCap.ToBig()) > 0 { + tip.Set(tx.GasTipCap.ToBig()) + } + return tip.Add(tip, baseFee) +} + +func (tx *SetCodeTx) rawSignatureValues() (v, r, s *big.Int) { + return tx.V.ToBig(), tx.R.ToBig(), tx.S.ToBig() +} + +func (tx *SetCodeTx) setSignatureValues(chainID, v, r, s *big.Int) { + tx.ChainID = chainID.Uint64() + tx.V.SetFromBig(v) + tx.R.SetFromBig(r) + tx.S.SetFromBig(s) +} + +func (tx *SetCodeTx) encode(b *bytes.Buffer) error { + return rlp.Encode(b, tx) +} + +func (tx *SetCodeTx) decode(input []byte) error { + return rlp.DecodeBytes(input, tx) +} diff --git a/core/types/tx_setcode_test.go b/core/types/tx_setcode_test.go new file mode 100644 index 0000000000..d0544573cf --- /dev/null +++ b/core/types/tx_setcode_test.go @@ -0,0 +1,70 @@ +// 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 types + +import ( + "testing" + + "github.com/ethereum/go-ethereum/common" +) + +// TestParseDelegation tests a few possible delegation designator values and +// ensures they are parsed correctly. +func TestParseDelegation(t *testing.T) { + addr := common.Address{0x42} + for _, tt := range []struct { + val []byte + want *common.Address + }{ + { // simple correct delegation + val: append(DelegationPrefix, addr.Bytes()...), + want: &addr, + }, + { // wrong address size + val: append(DelegationPrefix, addr.Bytes()[0:19]...), + }, + { // short address + val: append(DelegationPrefix, 0x42), + }, + { // long address + val: append(append(DelegationPrefix, addr.Bytes()...), 0x42), + }, + { // wrong prefix size + val: append(DelegationPrefix[:2], addr.Bytes()...), + }, + { // wrong prefix + val: append([]byte{0xef, 0x01, 0x01}, addr.Bytes()...), + }, + { // wrong prefix + val: append([]byte{0xef, 0x00, 0x00}, addr.Bytes()...), + }, + { // no prefix + val: addr.Bytes(), + }, + { // no address + val: DelegationPrefix, + }, + } { + got, ok := ParseDelegation(tt.val) + if ok && tt.want == nil { + t.Fatalf("expected fail, got %s", got.Hex()) + } + if !ok && tt.want != nil { + t.Fatalf("failed to parse, want %s", tt.want.Hex()) + } + } +} diff --git a/core/verkle_witness_test.go b/core/verkle_witness_test.go new file mode 100644 index 0000000000..5088231207 --- /dev/null +++ b/core/verkle_witness_test.go @@ -0,0 +1,1087 @@ +// 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 core + +import ( + "bytes" + "encoding/binary" + "encoding/hex" + "math/big" + "slices" + "testing" + + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/consensus/beacon" + "github.com/ethereum/go-ethereum/consensus/ethash" + "github.com/ethereum/go-ethereum/core/rawdb" + "github.com/ethereum/go-ethereum/core/state" + "github.com/ethereum/go-ethereum/core/types" + "github.com/ethereum/go-ethereum/core/vm" + "github.com/ethereum/go-ethereum/crypto" + "github.com/ethereum/go-ethereum/params" + "github.com/ethereum/go-ethereum/trie/utils" + "github.com/ethereum/go-ethereum/triedb" + "github.com/ethereum/go-verkle" + "github.com/holiman/uint256" +) + +var ( + testVerkleChainConfig = ¶ms.ChainConfig{ + ChainID: big.NewInt(1), + HomesteadBlock: big.NewInt(0), + EIP150Block: big.NewInt(0), + EIP155Block: big.NewInt(0), + EIP158Block: big.NewInt(0), + ByzantiumBlock: big.NewInt(0), + ConstantinopleBlock: big.NewInt(0), + PetersburgBlock: big.NewInt(0), + IstanbulBlock: big.NewInt(0), + MuirGlacierBlock: big.NewInt(0), + BerlinBlock: big.NewInt(0), + LondonBlock: big.NewInt(0), + Ethash: new(params.EthashConfig), + ShanghaiTime: u64(0), + VerkleTime: u64(0), + TerminalTotalDifficulty: common.Big0, + // TODO uncomment when proof generation is merged + // ProofInBlocks: true, + } + testKaustinenLikeChainConfig = ¶ms.ChainConfig{ + ChainID: big.NewInt(69420), + HomesteadBlock: big.NewInt(0), + EIP150Block: big.NewInt(0), + EIP155Block: big.NewInt(0), + EIP158Block: big.NewInt(0), + ByzantiumBlock: big.NewInt(0), + ConstantinopleBlock: big.NewInt(0), + PetersburgBlock: big.NewInt(0), + IstanbulBlock: big.NewInt(0), + MuirGlacierBlock: big.NewInt(0), + BerlinBlock: big.NewInt(0), + LondonBlock: big.NewInt(0), + Ethash: new(params.EthashConfig), + ShanghaiTime: u64(0), + VerkleTime: u64(0), + TerminalTotalDifficulty: common.Big0, + } +) + +func TestProcessVerkle(t *testing.T) { + var ( + code = common.FromHex(`6060604052600a8060106000396000f360606040526008565b00`) + intrinsicContractCreationGas, _ = IntrinsicGas(code, nil, nil, true, true, true, true) + // A contract creation that calls EXTCODECOPY in the constructor. Used to ensure that the witness + // will not contain that copied data. + // Source: https://gist.github.com/gballet/a23db1e1cb4ed105616b5920feb75985 + codeWithExtCodeCopy = common.FromHex(`0x60806040526040516100109061017b565b604051809103906000f08015801561002c573d6000803e3d6000fd5b506000806101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff16021790555034801561007857600080fd5b5060008067ffffffffffffffff8111156100955761009461024a565b5b6040519080825280601f01601f1916602001820160405280156100c75781602001600182028036833780820191505090505b50905060008060009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1690506020600083833c81610101906101e3565b60405161010d90610187565b61011791906101a3565b604051809103906000f080158015610133573d6000803e3d6000fd5b50600160006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff160217905550505061029b565b60d58061046783390190565b6102068061053c83390190565b61019d816101d9565b82525050565b60006020820190506101b86000830184610194565b92915050565b6000819050602082019050919050565b600081519050919050565b6000819050919050565b60006101ee826101ce565b826101f8846101be565b905061020381610279565b925060208210156102435761023e7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8360200360080261028e565b831692505b5050919050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b600061028582516101d9565b80915050919050565b600082821b905092915050565b6101bd806102aa6000396000f3fe608060405234801561001057600080fd5b506004361061002b5760003560e01c8063f566852414610030575b600080fd5b61003861004e565b6040516100459190610146565b60405180910390f35b6000600160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff166381ca91d36040518163ffffffff1660e01b815260040160206040518083038186803b1580156100b857600080fd5b505afa1580156100cc573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906100f0919061010a565b905090565b60008151905061010481610170565b92915050565b6000602082840312156101205761011f61016b565b5b600061012e848285016100f5565b91505092915050565b61014081610161565b82525050565b600060208201905061015b6000830184610137565b92915050565b6000819050919050565b600080fd5b61017981610161565b811461018457600080fd5b5056fea2646970667358221220a6a0e11af79f176f9c421b7b12f441356b25f6489b83d38cc828a701720b41f164736f6c63430008070033608060405234801561001057600080fd5b5060b68061001f6000396000f3fe6080604052348015600f57600080fd5b506004361060285760003560e01c8063ab5ed15014602d575b600080fd5b60336047565b604051603e9190605d565b60405180910390f35b60006001905090565b6057816076565b82525050565b6000602082019050607060008301846050565b92915050565b600081905091905056fea26469706673582212203a14eb0d5cd07c277d3e24912f110ddda3e553245a99afc4eeefb2fbae5327aa64736f6c63430008070033608060405234801561001057600080fd5b5060405161020638038061020683398181016040528101906100329190610063565b60018160001c6100429190610090565b60008190555050610145565b60008151905061005d8161012e565b92915050565b60006020828403121561007957610078610129565b5b60006100878482850161004e565b91505092915050565b600061009b826100f0565b91506100a6836100f0565b9250827fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff038211156100db576100da6100fa565b5b828201905092915050565b6000819050919050565b6000819050919050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b600080fd5b610137816100e6565b811461014257600080fd5b50565b60b3806101536000396000f3fe6080604052348015600f57600080fd5b506004361060285760003560e01c806381ca91d314602d575b600080fd5b60336047565b604051603e9190605a565b60405180910390f35b60005481565b6054816073565b82525050565b6000602082019050606d6000830184604d565b92915050565b600081905091905056fea26469706673582212209bff7098a2f526de1ad499866f27d6d0d6f17b74a413036d6063ca6a0998ca4264736f6c63430008070033`) + intrinsicCodeWithExtCodeCopyGas, _ = IntrinsicGas(codeWithExtCodeCopy, nil, nil, true, true, true, true) + signer = types.LatestSigner(testVerkleChainConfig) + testKey, _ = crypto.HexToECDSA("b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291") + bcdb = rawdb.NewMemoryDatabase() // Database for the blockchain + coinbase = common.HexToAddress("0x71562b71999873DB5b286dF957af199Ec94617F7") + gspec = &Genesis{ + Config: testVerkleChainConfig, + Alloc: GenesisAlloc{ + coinbase: { + Balance: big.NewInt(1000000000000000000), // 1 ether + Nonce: 0, + }, + params.BeaconRootsAddress: {Nonce: 1, Code: params.BeaconRootsCode, Balance: common.Big0}, + params.HistoryStorageAddress: {Nonce: 1, Code: params.HistoryStorageCode, Balance: common.Big0}, + params.WithdrawalQueueAddress: {Nonce: 1, Code: params.WithdrawalQueueCode, Balance: common.Big0}, + params.ConsolidationQueueAddress: {Nonce: 1, Code: params.ConsolidationQueueCode, Balance: common.Big0}, + }, + } + ) + // Verkle trees use the snapshot, which must be enabled before the + // data is saved into the tree+database. + // genesis := gspec.MustCommit(bcdb, triedb) + cacheConfig := DefaultCacheConfigWithScheme(rawdb.PathScheme) + cacheConfig.SnapshotLimit = 0 + blockchain, _ := NewBlockChain(bcdb, cacheConfig, gspec, nil, beacon.New(ethash.NewFaker()), vm.Config{}, nil) + defer blockchain.Stop() + + txCost1 := params.TxGas + txCost2 := params.TxGas + contractCreationCost := intrinsicContractCreationGas + + params.WitnessChunkReadCost + params.WitnessChunkWriteCost + params.WitnessBranchReadCost + params.WitnessBranchWriteCost + /* creation */ + params.WitnessChunkReadCost + params.WitnessChunkWriteCost + /* creation with value */ + 739 /* execution costs */ + codeWithExtCodeCopyGas := intrinsicCodeWithExtCodeCopyGas + + params.WitnessChunkReadCost + params.WitnessChunkWriteCost + params.WitnessBranchReadCost + params.WitnessBranchWriteCost + /* creation (tx) */ + params.WitnessChunkReadCost + params.WitnessChunkWriteCost + params.WitnessBranchReadCost + params.WitnessBranchWriteCost + /* creation (CREATE at pc=0x20) */ + params.WitnessChunkReadCost + params.WitnessChunkWriteCost + /* write code hash */ + params.WitnessChunkReadCost + params.WitnessChunkWriteCost + /* code chunk #0 */ + params.WitnessChunkReadCost + params.WitnessChunkWriteCost + /* code chunk #1 */ + params.WitnessChunkReadCost + params.WitnessChunkWriteCost + /* code chunk #2 */ + params.WitnessChunkReadCost + params.WitnessChunkWriteCost + /* code chunk #3 */ + params.WitnessChunkReadCost + params.WitnessChunkWriteCost + /* code chunk #4 */ + params.WitnessChunkReadCost + params.WitnessChunkWriteCost + /* code chunk #5 */ + params.WitnessChunkReadCost + /* SLOAD in constructor */ + params.WitnessChunkWriteCost + /* SSTORE in constructor */ + params.WitnessChunkReadCost + params.WitnessChunkWriteCost + params.WitnessBranchReadCost + params.WitnessBranchWriteCost + /* creation (CREATE at PC=0x121) */ + params.WitnessChunkReadCost + params.WitnessChunkWriteCost + /* write code hash */ + params.WitnessChunkReadCost + params.WitnessChunkWriteCost + /* code chunk #0 */ + params.WitnessChunkReadCost + params.WitnessChunkWriteCost + /* code chunk #1 */ + params.WitnessChunkReadCost + params.WitnessChunkWriteCost + /* code chunk #2 */ + params.WitnessChunkReadCost + params.WitnessChunkWriteCost + /* code chunk #3 */ + params.WitnessChunkReadCost + params.WitnessChunkWriteCost + /* code chunk #4 */ + params.WitnessChunkReadCost + params.WitnessChunkWriteCost + /* code chunk #5 */ + params.WitnessChunkReadCost + /* SLOAD in constructor */ + params.WitnessChunkWriteCost + /* SSTORE in constructor */ + params.WitnessChunkReadCost + params.WitnessChunkWriteCost + /* write code hash for tx creation */ + 15*(params.WitnessChunkReadCost+params.WitnessChunkWriteCost) + /* code chunks #0..#14 */ + 4844 /* execution costs */ + blockGasUsagesExpected := []uint64{ + txCost1*2 + txCost2, + txCost1*2 + txCost2 + contractCreationCost + codeWithExtCodeCopyGas, + } + _, chain, _, proofs, statediffs := GenerateVerkleChainWithGenesis(gspec, beacon.New(ethash.NewFaker()), 2, func(i int, gen *BlockGen) { + gen.SetPoS() + + // TODO need to check that the tx cost provided is the exact amount used (no remaining left-over) + tx, _ := types.SignTx(types.NewTransaction(uint64(i)*3, common.Address{byte(i), 2, 3}, big.NewInt(999), txCost1, big.NewInt(875000000), nil), signer, testKey) + gen.AddTx(tx) + tx, _ = types.SignTx(types.NewTransaction(uint64(i)*3+1, common.Address{}, big.NewInt(999), txCost1, big.NewInt(875000000), nil), signer, testKey) + gen.AddTx(tx) + tx, _ = types.SignTx(types.NewTransaction(uint64(i)*3+2, common.Address{}, big.NewInt(0), txCost2, big.NewInt(875000000), nil), signer, testKey) + gen.AddTx(tx) + + // Add two contract creations in block #2 + if i == 1 { + tx, _ = types.SignNewTx(testKey, signer, &types.LegacyTx{Nonce: 6, + Value: big.NewInt(16), + Gas: 3000000, + GasPrice: big.NewInt(875000000), + Data: code, + }) + gen.AddTx(tx) + + tx, _ = types.SignNewTx(testKey, signer, &types.LegacyTx{Nonce: 7, + Value: big.NewInt(0), + Gas: 3000000, + GasPrice: big.NewInt(875000000), + Data: codeWithExtCodeCopy, + }) + gen.AddTx(tx) + } + }) + + // Check proof for both blocks + err := verkle.Verify(proofs[0], gspec.ToBlock().Root().Bytes(), chain[0].Root().Bytes(), statediffs[0]) + if err != nil { + t.Fatal(err) + } + err = verkle.Verify(proofs[1], chain[0].Root().Bytes(), chain[1].Root().Bytes(), statediffs[1]) + if err != nil { + t.Fatal(err) + } + + t.Log("verified verkle proof, inserting blocks into the chain") + + endnum, err := blockchain.InsertChain(chain) + if err != nil { + t.Fatalf("block %d imported with error: %v", endnum, err) + } + + for i := 0; i < 2; i++ { + b := blockchain.GetBlockByNumber(uint64(i) + 1) + if b == nil { + t.Fatalf("expected block %d to be present in chain", i+1) + } + if b.Hash() != chain[i].Hash() { + t.Fatalf("block #%d not found at expected height", b.NumberU64()) + } + if b.GasUsed() != blockGasUsagesExpected[i] { + t.Fatalf("expected block #%d txs to use %d, got %d\n", b.NumberU64(), blockGasUsagesExpected[i], b.GasUsed()) + } + } +} + +func TestProcessParentBlockHash(t *testing.T) { + // This test uses blocks where, + // block 1 parent hash is 0x0100.... + // block 2 parent hash is 0x0200.... + // etc + checkBlockHashes := func(statedb *state.StateDB) { + statedb.SetNonce(params.HistoryStorageAddress, 1) + statedb.SetCode(params.HistoryStorageAddress, params.HistoryStorageCode) + // Process n blocks, from 1 .. num + var num = 2 + 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, statedb, params.MergedTestChainConfig, vm.Config{}) + ProcessParentBlockHash(header.ParentHash, evm) + } + // Read block hashes for block 0 .. num-1 + for i := 0; i < num; i++ { + have, want := getContractStoredBlockHash(statedb, uint64(i)), common.Hash{byte(i + 1)} + if have != want { + t.Errorf("block %d, have parent hash %v, want %v", i, have, want) + } + } + } + t.Run("MPT", func(t *testing.T) { + statedb, _ := state.New(types.EmptyRootHash, state.NewDatabaseForTesting()) + checkBlockHashes(statedb) + }) + t.Run("Verkle", func(t *testing.T) { + db := rawdb.NewMemoryDatabase() + cacheConfig := DefaultCacheConfigWithScheme(rawdb.PathScheme) + cacheConfig.SnapshotLimit = 0 + triedb := triedb.NewDatabase(db, cacheConfig.triedbConfig(true)) + statedb, _ := state.New(types.EmptyVerkleHash, state.NewDatabase(triedb, nil)) + checkBlockHashes(statedb) + }) +} + +// getContractStoredBlockHash is a utility method which reads the stored parent blockhash for block 'number' +func getContractStoredBlockHash(statedb *state.StateDB, number uint64) common.Hash { + ringIndex := number % params.HistoryServeWindow + var key common.Hash + binary.BigEndian.PutUint64(key[24:], ringIndex) + return statedb.GetState(params.HistoryStorageAddress, key) +} + +// TestProcessVerkleInvalidContractCreation checks for several modes of contract creation failures +func TestProcessVerkleInvalidContractCreation(t *testing.T) { + var ( + account1 = common.HexToAddress("0x687704DB07e902e9A8B3754031D168D46E3D586e") + account2 = common.HexToAddress("0x6177843db3138ae69679A54b95cf345ED759450d") + gspec = verkleTestGenesis(testKaustinenLikeChainConfig) + ) + // slightly modify it to suit the live txs from the testnet + gspec.Alloc[account2] = types.Account{ + Balance: big.NewInt(1000000000000000000), // 1 ether + Nonce: 1, + } + + // Create two blocks that reproduce what is happening on kaustinen. + // - The first block contains two failing contract creation transactions, that + // write to storage before they revert. + // + // - The second block contains a single failing contract creation transaction, + // that fails right off the bat. + _, chain, _, _, statediffs := GenerateVerkleChainWithGenesis(gspec, beacon.New(ethash.NewFaker()), 2, func(i int, gen *BlockGen) { + gen.SetPoS() + + if i == 0 { + for _, rlpData := range []string{ + // SSTORE at slot 41 and reverts + "f8d48084479c2c18830186a08080b8806000602955bda3f9600060ca55600060695523b360006039551983576000601255b0620c2fde2c592ac2600060bc55e0ac6000606455a63e22600060e655eb607e605c5360a2605d5360c7605e53601d605f5360eb606053606b606153608e60625360816063536079606453601e60655360fc60665360b7606753608b60685383021e7ca0cc20c65a97d2e526b8ec0f4266e8b01bdcde43b9aeb59d8bfb44e8eb8119c109a07a8e751813ae1b2ce734960dbc39a4f954917d7822a2c5d1dca18b06c584131f", + // SSTORE at slot 133 and reverts + "02f8db83010f2c01843b9aca0084479c2c18830186a08080b88060006085553fad6000600a55600060565555600060b55506600060cf557f1b8b38183e7bd1bdfaa7123c5a4976e54cce0e42049d841411978fd3595e25c66019527f0538943712953cf08900aae40222a40b2d5a4ac8075ad8cf0870e2be307edbb96039527f9f3174ff85024747041ae7a611acffb987c513c088d90ab288aec080a0cd6ac65ce2cb0a912371f6b5a551ba8caffc22ec55ad4d3cb53de41d05eb77b6a02e0dfe8513dfa6ec7bfd7eda6f5c0dac21b39b982436045e128cec46cfd3f960", + // this one is a simple transfer that succeeds, necessary to get the correct nonce in the other block. + "f8e80184479c2c18830186a094bbbbde4ca27f83fc18aa108170547ff57675936a80b8807ff71f7c15faadb969a76a5f54a81a0117e1e743cb7f24e378eda28442ea4c6eb6604a527fb5409e5718d44e23bfffac926e5ea726067f772772e7e19446acba0c853f62f5606a526020608a536088608b536039608c536004608d5360af608e537f7f7675d9f210e0a61564e6d11e7cd75f5bc9009ac9f6b94a0fc63035441a83021e7ba04a4a172d81ebb02847829b76a387ac09749c8b65668083699abe20c887fb9efca07c5b1a990702ec7b31a5e8e3935cd9a77649f8c25a84131229e24ab61aec6093", + } { + var tx = new(types.Transaction) + if err := tx.UnmarshalBinary(common.Hex2Bytes(rlpData)); err != nil { + t.Fatal(err) + } + gen.AddTx(tx) + } + } else { + var tx = new(types.Transaction) + // immediately reverts + if err := tx.UnmarshalBinary(common.Hex2Bytes("01f8d683010f2c028443ad7d0e830186a08080b880b00e7fa3c849dce891cce5fae8a4c46cbb313d6aec0c0ffe7863e05fb7b22d4807674c6055527ffbfcb0938f3e18f7937aa8fa95d880afebd5c4cec0d85186095832d03c85cf8a60755260ab60955360cf6096536066609753606e60985360fa609953609e609a53608e609b536024609c5360f6609d536072609e5360a4609fc080a08fc6f7101f292ff1fb0de8ac69c2d320fbb23bfe61cf327173786ea5daee6e37a044c42d91838ef06646294bf4f9835588aee66243b16a66a2da37641fae4c045f")); err != nil { + t.Fatal(err) + } + gen.AddTx(tx) + } + }) + + tx1ContractAddress := crypto.CreateAddress(account1, 0) + tx1ContractStem := utils.GetTreeKey(tx1ContractAddress[:], uint256.NewInt(0), 105) + tx1ContractStem = tx1ContractStem[:31] + + tx2ContractAddress := crypto.CreateAddress(account2, 1) + tx2SlotKey := [32]byte{} + tx2SlotKey[31] = 133 + tx2ContractStem := utils.StorageSlotKey(tx2ContractAddress[:], tx2SlotKey[:]) + tx2ContractStem = tx2ContractStem[:31] + + eip2935Stem := utils.GetTreeKey(params.HistoryStorageAddress[:], uint256.NewInt(0), 0) + eip2935Stem = eip2935Stem[:31] + + // Check that the witness contains what we expect: a storage entry for each of the two contract + // creations that failed: one at 133 for the 2nd tx, and one at 105 for the first tx. + for _, stemStateDiff := range statediffs[0] { + // Check that the slot number 133, which is overflowing the account header, + // is present. Note that the offset of the 2nd group (first group after the + // header) is skipping the first 64 values, hence we still have an offset + // of 133, and not 133 - 64. + if bytes.Equal(stemStateDiff.Stem[:], tx2ContractStem[:]) { + for _, suffixDiff := range stemStateDiff.SuffixDiffs { + if suffixDiff.Suffix != 133 { + t.Fatalf("invalid suffix diff found for %x in block #1: %d\n", stemStateDiff.Stem, suffixDiff.Suffix) + } + if suffixDiff.CurrentValue != nil { + t.Fatalf("invalid prestate value found for %x in block #1: %v != nil\n", stemStateDiff.Stem, suffixDiff.CurrentValue) + } + if suffixDiff.NewValue != nil { + t.Fatalf("invalid poststate value found for %x in block #1: %v != nil\n", stemStateDiff.Stem, suffixDiff.NewValue) + } + } + } else if bytes.Equal(stemStateDiff.Stem[:], tx1ContractStem) { + // For this contract creation, check that only the account header and storage slot 41 + // are found in the witness. + for _, suffixDiff := range stemStateDiff.SuffixDiffs { + if suffixDiff.Suffix != 105 && suffixDiff.Suffix != 0 && suffixDiff.Suffix != 1 { + t.Fatalf("invalid suffix diff found for %x in block #1: %d\n", stemStateDiff.Stem, suffixDiff.Suffix) + } + } + } else if bytes.Equal(stemStateDiff.Stem[:], eip2935Stem) { + // Check the eip 2935 group of leaves. + // Check that only one leaf was accessed, and is present in the witness. + if len(stemStateDiff.SuffixDiffs) > 1 { + t.Fatalf("invalid suffix diff count found for BLOCKHASH contract: %d != 1", len(stemStateDiff.SuffixDiffs)) + } + // Check that this leaf is the first storage slot + if stemStateDiff.SuffixDiffs[0].Suffix != 64 { + t.Fatalf("invalid suffix diff value found for BLOCKHASH contract: %d != 64", stemStateDiff.SuffixDiffs[0].Suffix) + } + // check that the prestate value is nil and that the poststate value isn't. + if stemStateDiff.SuffixDiffs[0].CurrentValue != nil { + t.Fatalf("non-nil current value in BLOCKHASH contract insert: %x", stemStateDiff.SuffixDiffs[0].CurrentValue) + } + if stemStateDiff.SuffixDiffs[0].NewValue == nil { + t.Fatalf("nil new value in BLOCKHASH contract insert") + } + if *stemStateDiff.SuffixDiffs[0].NewValue != chain[0].Hash() { + t.Fatalf("invalid BLOCKHASH value: %x != %x", *stemStateDiff.SuffixDiffs[0].NewValue, chain[0].Hash()) + } + } else { + // For all other entries present in the witness, check that nothing beyond + // the account header was accessed. + for _, suffixDiff := range stemStateDiff.SuffixDiffs { + if suffixDiff.Suffix > 2 { + t.Fatalf("invalid suffix diff found for %x in block #1: %d\n", stemStateDiff.Stem, suffixDiff.Suffix) + } + } + } + } + + // Check that no account has a value above 4 in the 2nd block as no storage nor + // code should make it to the witness. + for _, stemStateDiff := range statediffs[1] { + for _, suffixDiff := range stemStateDiff.SuffixDiffs { + if bytes.Equal(stemStateDiff.Stem[:], eip2935Stem) { + // BLOCKHASH contract stem + if len(stemStateDiff.SuffixDiffs) > 1 { + t.Fatalf("invalid suffix diff count found for BLOCKHASH contract at block #2: %d != 1", len(stemStateDiff.SuffixDiffs)) + } + if stemStateDiff.SuffixDiffs[0].Suffix != 65 { + t.Fatalf("invalid suffix diff value found for BLOCKHASH contract at block #2: %d != 65", stemStateDiff.SuffixDiffs[0].Suffix) + } + if stemStateDiff.SuffixDiffs[0].NewValue == nil { + t.Fatalf("missing post state value for BLOCKHASH contract at block #2") + } + if *stemStateDiff.SuffixDiffs[0].NewValue != common.HexToHash("0788c2c0f23aa07eb8bf76fe6c1ca9064a4821c1fd0af803913da488a58dba54") { + t.Fatalf("invalid post state value for BLOCKHASH contract at block #2: 0788c2c0f23aa07eb8bf76fe6c1ca9064a4821c1fd0af803913da488a58dba54 != %x", (*stemStateDiff.SuffixDiffs[0].NewValue)[:]) + } + } else if suffixDiff.Suffix > 4 { + t.Fatalf("invalid suffix diff found for %x in block #2: %d\n", stemStateDiff.Stem, suffixDiff.Suffix) + } + } + } +} + +func verkleTestGenesis(config *params.ChainConfig) *Genesis { + var ( + coinbase = common.HexToAddress("0x71562b71999873DB5b286dF957af199Ec94617F7") + account1 = common.HexToAddress("0x687704DB07e902e9A8B3754031D168D46E3D586e") + account2 = common.HexToAddress("0x6177843db3138ae69679A54b95cf345ED759450d") + ) + return &Genesis{ + Config: config, + Alloc: GenesisAlloc{ + coinbase: GenesisAccount{ + Balance: big.NewInt(1000000000000000000), // 1 ether + Nonce: 0, + }, + account1: GenesisAccount{ + Balance: big.NewInt(1000000000000000000), // 1 ether + Nonce: 0, + }, + account2: GenesisAccount{ + Balance: big.NewInt(1000000000000000000), // 1 ether + Nonce: 3, + }, + params.BeaconRootsAddress: {Nonce: 1, Code: params.BeaconRootsCode, Balance: common.Big0}, + params.HistoryStorageAddress: {Nonce: 1, Code: params.HistoryStorageCode, Balance: common.Big0}, + params.WithdrawalQueueAddress: {Nonce: 1, Code: params.WithdrawalQueueCode, Balance: common.Big0}, + params.ConsolidationQueueAddress: {Nonce: 1, Code: params.ConsolidationQueueCode, Balance: common.Big0}, + }, + } +} + +// TestProcessVerkleContractWithEmptyCode checks that the witness contains all valid +// entries, if the initcode returns an empty code. +func TestProcessVerkleContractWithEmptyCode(t *testing.T) { + // The test txs were taken from a secondary testnet with chain id 69421 + config := *testKaustinenLikeChainConfig + config.ChainID.SetUint64(69421) + gspec := verkleTestGenesis(&config) + + _, chain, _, _, statediffs := GenerateVerkleChainWithGenesis(gspec, beacon.New(ethash.NewFaker()), 1, func(i int, gen *BlockGen) { + gen.SetPoS() + var tx types.Transaction + // a transaction that does some PUSH1n but returns a 0-sized contract + txpayload := common.Hex2Bytes("02f8db83010f2d03843b9aca008444cf6a05830186a08080b8807fdfbbb59f2371a76485ce557fd0de00c298d3ede52a3eab56d35af674eb49ec5860335260826053536001605453604c60555360f3605653606060575360446058536096605953600c605a5360df605b5360f3605c5360fb605d53600c605e53609a605f53607f60605360fe606153603d60625360f4606353604b60645360cac001a0486b6dc55b8a311568b7239a2cae1d77e7446dba71df61eaafd53f73820a138fa010bd48a45e56133ac4c5645142c2ea48950d40eb35050e9510b6bad9e15c5865") + if err := tx.UnmarshalBinary(txpayload); err != nil { + t.Fatal(err) + } + gen.AddTx(&tx) + }) + + eip2935Stem := utils.GetTreeKey(params.HistoryStorageAddress[:], uint256.NewInt(0), 0) + eip2935Stem = eip2935Stem[:31] + + for _, stemStateDiff := range statediffs[0] { + // Handle the case of the history contract: make sure only the correct + // slots are added to the witness. + if bytes.Equal(stemStateDiff.Stem[:], eip2935Stem) { + // BLOCKHASH contract stem + if len(stemStateDiff.SuffixDiffs) > 1 { + t.Fatalf("invalid suffix diff count found for BLOCKHASH contract: %d != 1", len(stemStateDiff.SuffixDiffs)) + } + if stemStateDiff.SuffixDiffs[0].Suffix != 64 { + t.Fatalf("invalid suffix diff value found for BLOCKHASH contract: %d != 64", stemStateDiff.SuffixDiffs[0].Suffix) + } + // check that the "current value" is nil and that the new value isn't. + if stemStateDiff.SuffixDiffs[0].CurrentValue != nil { + t.Fatalf("non-nil current value in BLOCKHASH contract insert: %x", stemStateDiff.SuffixDiffs[0].CurrentValue) + } + if stemStateDiff.SuffixDiffs[0].NewValue == nil { + t.Fatalf("nil new value in BLOCKHASH contract insert") + } + if *stemStateDiff.SuffixDiffs[0].NewValue != chain[0].Hash() { + t.Fatalf("invalid BLOCKHASH value: %x != %x", *stemStateDiff.SuffixDiffs[0].NewValue, chain[0].Hash()) + } + } else { + for _, suffixDiff := range stemStateDiff.SuffixDiffs { + if suffixDiff.Suffix > 2 { + // if d8898012c484fb48610ecb7963886339207dab004bce968b007b616ffa18e0 shows up, it means that the PUSHn + // in the transaction above added entries into the witness, when they should not have since they are + // part of a contract deployment. + t.Fatalf("invalid suffix diff found for %x in block #1: %d\n", stemStateDiff.Stem, suffixDiff.Suffix) + } + } + } + } +} + +// TestProcessVerkleExtCodeHashOpcode verifies that calling EXTCODEHASH on another +// deployed contract, creates all the right entries in the witness. +func TestProcessVerkleExtCodeHashOpcode(t *testing.T) { + // The test txs were taken from a secondary testnet with chain id 69421 + config := *testKaustinenLikeChainConfig + config.ChainID.SetUint64(69421) + + var ( + signer = types.LatestSigner(&config) + testKey, _ = crypto.HexToECDSA("b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291") + gspec = verkleTestGenesis(&config) + ) + dummyContract := []byte{ + byte(vm.PUSH1), 2, + byte(vm.PUSH1), 12, + byte(vm.PUSH1), 0x00, + byte(vm.CODECOPY), + + byte(vm.PUSH1), 2, + byte(vm.PUSH1), 0x00, + byte(vm.RETURN), + + byte(vm.PUSH1), 42, + } + deployer := crypto.PubkeyToAddress(testKey.PublicKey) + dummyContractAddr := crypto.CreateAddress(deployer, 0) + + // contract that calls EXTCODEHASH on the dummy contract + extCodeHashContract := []byte{ + byte(vm.PUSH1), 22, + byte(vm.PUSH1), 12, + byte(vm.PUSH1), 0x00, + byte(vm.CODECOPY), + + byte(vm.PUSH1), 22, + byte(vm.PUSH1), 0x00, + byte(vm.RETURN), + + byte(vm.PUSH20), + 0x3a, 0x22, 0x0f, 0x35, 0x12, 0x52, 0x08, 0x9d, 0x38, 0x5b, 0x29, 0xbe, 0xca, 0x14, 0xe2, 0x7f, 0x20, 0x4c, 0x29, 0x6a, + byte(vm.EXTCODEHASH), + } + extCodeHashContractAddr := crypto.CreateAddress(deployer, 1) + + _, _, _, _, statediffs := GenerateVerkleChainWithGenesis(gspec, beacon.New(ethash.NewFaker()), 2, func(i int, gen *BlockGen) { + gen.SetPoS() + + if i == 0 { + // Create dummy contract. + tx, _ := types.SignNewTx(testKey, signer, &types.LegacyTx{Nonce: 0, + Value: big.NewInt(0), + Gas: 100_000, + GasPrice: big.NewInt(875000000), + Data: dummyContract, + }) + gen.AddTx(tx) + + // Create contract with EXTCODEHASH opcode. + tx, _ = types.SignNewTx(testKey, signer, &types.LegacyTx{Nonce: 1, + Value: big.NewInt(0), + Gas: 100_000, + GasPrice: big.NewInt(875000000), + Data: extCodeHashContract}) + gen.AddTx(tx) + } else { + tx, _ := types.SignTx(types.NewTransaction(2, extCodeHashContractAddr, big.NewInt(0), 100_000, big.NewInt(875000000), nil), signer, testKey) + gen.AddTx(tx) + } + }) + + contractKeccakTreeKey := utils.CodeHashKey(dummyContractAddr[:]) + + var stateDiffIdx = -1 + for i, stemStateDiff := range statediffs[1] { + if bytes.Equal(stemStateDiff.Stem[:], contractKeccakTreeKey[:31]) { + stateDiffIdx = i + break + } + } + if stateDiffIdx == -1 { + t.Fatalf("no state diff found for stem") + } + + codeHashStateDiff := statediffs[1][stateDiffIdx].SuffixDiffs[0] + // Check location of code hash was accessed + if codeHashStateDiff.Suffix != utils.CodeHashLeafKey { + t.Fatalf("code hash invalid suffix") + } + // check the code hash wasn't present in the prestate, as + // the contract was deployed in this block. + if codeHashStateDiff.CurrentValue == nil { + t.Fatalf("codeHash.CurrentValue must not be empty") + } + // check the poststate value corresponds to the code hash + // of the deployed contract. + expCodeHash := crypto.Keccak256Hash(dummyContract[12:]) + if *codeHashStateDiff.CurrentValue != expCodeHash { + t.Fatalf("codeHash.CurrentValue unexpected code hash") + } + if codeHashStateDiff.NewValue != nil { + t.Fatalf("codeHash.NewValue must be nil") + } +} + +// TestProcessVerkleBalanceOpcode checks that calling balance +// on another contract will add the correct entries to the witness. +func TestProcessVerkleBalanceOpcode(t *testing.T) { + // The test txs were taken from a secondary testnet with chain id 69421 + config := *testKaustinenLikeChainConfig + config.ChainID.SetUint64(69421) + + var ( + signer = types.LatestSigner(&config) + testKey, _ = crypto.HexToECDSA("b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291") + account2 = common.HexToAddress("0x6177843db3138ae69679A54b95cf345ED759450d") + gspec = verkleTestGenesis(&config) + ) + _, _, _, _, statediffs := GenerateVerkleChainWithGenesis(gspec, beacon.New(ethash.NewFaker()), 1, func(i int, gen *BlockGen) { + gen.SetPoS() + txData := slices.Concat( + []byte{byte(vm.PUSH20)}, + common.HexToAddress("0x6177843db3138ae69679A54b95cf345ED759450d").Bytes(), + []byte{byte(vm.BALANCE)}) + + tx, _ := types.SignNewTx(testKey, signer, &types.LegacyTx{Nonce: 0, + Value: big.NewInt(0), + Gas: 100_000, + GasPrice: big.NewInt(875000000), + Data: txData}) + gen.AddTx(tx) + }) + + account2BalanceTreeKey := utils.BasicDataKey(account2[:]) + + var stateDiffIdx = -1 + for i, stemStateDiff := range statediffs[0] { + if bytes.Equal(stemStateDiff.Stem[:], account2BalanceTreeKey[:31]) { + stateDiffIdx = i + break + } + } + if stateDiffIdx == -1 { + t.Fatalf("no state diff found for stem") + } + + var zero [32]byte + balanceStateDiff := statediffs[0][stateDiffIdx].SuffixDiffs[0] + if balanceStateDiff.Suffix != utils.BasicDataLeafKey { + t.Fatalf("invalid suffix diff") + } + // check the prestate balance wasn't 0 or missing + if balanceStateDiff.CurrentValue == nil || *balanceStateDiff.CurrentValue == zero { + t.Fatalf("invalid current value %v", *balanceStateDiff.CurrentValue) + } + // check that the poststate witness value for the balance is nil, + // meaning that it didn't get updated. + if balanceStateDiff.NewValue != nil { + t.Fatalf("invalid new value") + } +} + +// TestProcessVerkleSelfDestructInSeparateTx controls the contents of the witness after +// a non-eip6780-compliant selfdestruct occurs. +func TestProcessVerkleSelfDestructInSeparateTx(t *testing.T) { + // The test txs were taken from a secondary testnet with chain id 69421 + config := *testKaustinenLikeChainConfig + config.ChainID.SetUint64(69421) + + var ( + signer = types.LatestSigner(&config) + testKey, _ = crypto.HexToECDSA("b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291") + account2 = common.HexToAddress("0x6177843db3138ae69679A54b95cf345ED759450d") + gspec = verkleTestGenesis(&config) + ) + + // runtime code: selfdestruct ( 0x6177843db3138ae69679A54b95cf345ED759450d ) + runtimeCode := slices.Concat( + []byte{byte(vm.PUSH20)}, + account2.Bytes(), + []byte{byte(vm.SELFDESTRUCT)}) + + //The goal of this test is to test SELFDESTRUCT that happens in a contract + // execution which is created in a previous transaction. + selfDestructContract := slices.Concat([]byte{ + byte(vm.PUSH1), byte(len(runtimeCode)), + byte(vm.PUSH1), 12, + byte(vm.PUSH1), 0x00, + byte(vm.CODECOPY), // Codecopy( to-offset: 0, code offset: 12, length: 22 ) + + byte(vm.PUSH1), byte(len(runtimeCode)), + byte(vm.PUSH1), 0x00, + byte(vm.RETURN), // Return ( 0 : len(runtimecode) + }, + runtimeCode) + + deployer := crypto.PubkeyToAddress(testKey.PublicKey) + contract := crypto.CreateAddress(deployer, 0) + + _, _, _, _, statediffs := GenerateVerkleChainWithGenesis(gspec, beacon.New(ethash.NewFaker()), 2, func(i int, gen *BlockGen) { + gen.SetPoS() + + if i == 0 { + // Create selfdestruct contract, sending 42 wei. + tx, _ := types.SignNewTx(testKey, signer, &types.LegacyTx{Nonce: 0, + Value: big.NewInt(42), + Gas: 100_000, + GasPrice: big.NewInt(875000000), + Data: selfDestructContract, + }) + gen.AddTx(tx) + } else { + // Call it. + tx, _ := types.SignTx(types.NewTransaction(1, contract, big.NewInt(0), 100_000, big.NewInt(875000000), nil), signer, testKey) + gen.AddTx(tx) + } + }) + + var zero [32]byte + { // Check self-destructed contract in the witness + selfDestructContractTreeKey := utils.CodeHashKey(contract[:]) + + var stateDiffIdx = -1 + for i, stemStateDiff := range statediffs[1] { + if bytes.Equal(stemStateDiff.Stem[:], selfDestructContractTreeKey[:31]) { + stateDiffIdx = i + break + } + } + if stateDiffIdx == -1 { + t.Fatalf("no state diff found for stem") + } + + balanceStateDiff := statediffs[1][stateDiffIdx].SuffixDiffs[0] + if balanceStateDiff.Suffix != utils.BasicDataLeafKey { + t.Fatalf("balance invalid suffix") + } + + // The original balance was 42. + var oldBalance [16]byte + oldBalance[15] = 42 + if !bytes.Equal((*balanceStateDiff.CurrentValue)[utils.BasicDataBalanceOffset:], oldBalance[:]) { + t.Fatalf("the pre-state balance before self-destruct must be %x, got %x", oldBalance, *balanceStateDiff.CurrentValue) + } + + // The new balance must be 0. + if !bytes.Equal((*balanceStateDiff.NewValue)[utils.BasicDataBalanceOffset:], zero[utils.BasicDataBalanceOffset:]) { + t.Fatalf("the post-state balance after self-destruct must be 0") + } + } + { // Check self-destructed target in the witness. + selfDestructTargetTreeKey := utils.CodeHashKey(account2[:]) + + var stateDiffIdx = -1 + for i, stemStateDiff := range statediffs[1] { + if bytes.Equal(stemStateDiff.Stem[:], selfDestructTargetTreeKey[:31]) { + stateDiffIdx = i + break + } + } + if stateDiffIdx == -1 { + t.Fatalf("no state diff found for stem") + } + + balanceStateDiff := statediffs[1][stateDiffIdx].SuffixDiffs[0] + if balanceStateDiff.Suffix != utils.BasicDataLeafKey { + t.Fatalf("balance invalid suffix") + } + if balanceStateDiff.CurrentValue == nil { + t.Fatalf("codeHash.CurrentValue must not be empty") + } + if balanceStateDiff.NewValue == nil { + t.Fatalf("codeHash.NewValue must not be empty") + } + preStateBalance := binary.BigEndian.Uint64(balanceStateDiff.CurrentValue[utils.BasicDataBalanceOffset+8:]) + postStateBalance := binary.BigEndian.Uint64(balanceStateDiff.NewValue[utils.BasicDataBalanceOffset+8:]) + if postStateBalance-preStateBalance != 42 { + t.Fatalf("the post-state balance after self-destruct must be 42, got %d-%d=%d", postStateBalance, preStateBalance, postStateBalance-preStateBalance) + } + } +} + +// TestProcessVerkleSelfDestructInSeparateTx controls the contents of the witness after +// a eip6780-compliant selfdestruct occurs. +func TestProcessVerkleSelfDestructInSameTx(t *testing.T) { + // The test txs were taken from a secondary testnet with chain id 69421 + config := *testKaustinenLikeChainConfig + config.ChainID.SetUint64(69421) + + var ( + signer = types.LatestSigner(&config) + testKey, _ = crypto.HexToECDSA("b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291") + account2 = common.HexToAddress("0x6177843db3138ae69679A54b95cf345ED759450d") + gspec = verkleTestGenesis(&config) + ) + + // The goal of this test is to test SELFDESTRUCT that happens in a contract + // execution which is created in **the same** transaction sending the remaining + // balance to an external (i.e: not itself) account. + + selfDestructContract := slices.Concat( + []byte{byte(vm.PUSH20)}, + account2.Bytes(), + []byte{byte(vm.SELFDESTRUCT)}) + deployer := crypto.PubkeyToAddress(testKey.PublicKey) + contract := crypto.CreateAddress(deployer, 0) + + _, _, _, _, statediffs := GenerateVerkleChainWithGenesis(gspec, beacon.New(ethash.NewFaker()), 1, func(i int, gen *BlockGen) { + gen.SetPoS() + tx, _ := types.SignNewTx(testKey, signer, &types.LegacyTx{Nonce: 0, + Value: big.NewInt(42), + Gas: 100_000, + GasPrice: big.NewInt(875000000), + Data: selfDestructContract, + }) + gen.AddTx(tx) + }) + + { // Check self-destructed contract in the witness + selfDestructContractTreeKey := utils.CodeHashKey(contract[:]) + + var stateDiffIdx = -1 + for i, stemStateDiff := range statediffs[0] { + if bytes.Equal(stemStateDiff.Stem[:], selfDestructContractTreeKey[:31]) { + stateDiffIdx = i + break + } + } + if stateDiffIdx == -1 { + t.Fatalf("no state diff found for stem") + } + + balanceStateDiff := statediffs[0][stateDiffIdx].SuffixDiffs[0] + if balanceStateDiff.Suffix != utils.BasicDataLeafKey { + t.Fatalf("balance invalid suffix") + } + + if balanceStateDiff.CurrentValue != nil { + t.Fatalf("the pre-state balance before must be nil, since the contract didn't exist") + } + + if balanceStateDiff.NewValue != nil { + t.Fatalf("the post-state balance after self-destruct must be nil since the contract shouldn't be created at all") + } + } + { // Check self-destructed target in the witness. + selfDestructTargetTreeKey := utils.CodeHashKey(account2[:]) + + var stateDiffIdx = -1 + for i, stemStateDiff := range statediffs[0] { + if bytes.Equal(stemStateDiff.Stem[:], selfDestructTargetTreeKey[:31]) { + stateDiffIdx = i + break + } + } + if stateDiffIdx == -1 { + t.Fatalf("no state diff found for stem") + } + + balanceStateDiff := statediffs[0][stateDiffIdx].SuffixDiffs[0] + if balanceStateDiff.Suffix != utils.BasicDataLeafKey { + t.Fatalf("balance invalid suffix") + } + if balanceStateDiff.CurrentValue == nil { + t.Fatalf("codeHash.CurrentValue must not be empty") + } + if balanceStateDiff.NewValue == nil { + t.Fatalf("codeHash.NewValue must not be empty") + } + preStateBalance := binary.BigEndian.Uint64(balanceStateDiff.CurrentValue[utils.BasicDataBalanceOffset+8:]) + postStateBalance := binary.BigEndian.Uint64(balanceStateDiff.NewValue[utils.BasicDataBalanceOffset+8:]) + if postStateBalance-preStateBalance != 42 { + t.Fatalf("the post-state balance after self-destruct must be 42. got %d", postStateBalance) + } + } +} + +// TestProcessVerkleSelfDestructInSeparateTxWithSelfBeneficiary checks the content of the witness +// if a selfdestruct occurs in a different tx than the one that created it, but the beneficiary +// is the selfdestructed account. +func TestProcessVerkleSelfDestructInSeparateTxWithSelfBeneficiary(t *testing.T) { + // The test txs were taken from a secondary testnet with chain id 69421 + config := *testKaustinenLikeChainConfig + config.ChainID.SetUint64(69421) + + var ( + signer = types.LatestSigner(&config) + testKey, _ = crypto.HexToECDSA("b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291") + gspec = verkleTestGenesis(&config) + ) + // The goal of this test is to test SELFDESTRUCT that happens in a contract + // execution which is created in a *previous* transaction sending the remaining + // balance to itself. + selfDestructContract := []byte{ + byte(vm.PUSH1), 2, // PUSH1 2 + byte(vm.PUSH1), 10, // PUSH1 12 + byte(vm.PUSH0), // PUSH0 + byte(vm.CODECOPY), // Codecopy ( to offset 0, code@offset: 10, length: 2) + + byte(vm.PUSH1), 22, + byte(vm.PUSH0), + byte(vm.RETURN), // RETURN( memory[0:2] ) + + // Deployed code + byte(vm.ADDRESS), + byte(vm.SELFDESTRUCT), + } + deployer := crypto.PubkeyToAddress(testKey.PublicKey) + contract := crypto.CreateAddress(deployer, 0) + + _, _, _, _, statediffs := GenerateVerkleChainWithGenesis(gspec, beacon.New(ethash.NewFaker()), 2, func(i int, gen *BlockGen) { + gen.SetPoS() + if i == 0 { + // Create self-destruct contract, sending 42 wei. + tx, _ := types.SignNewTx(testKey, signer, &types.LegacyTx{Nonce: 0, + Value: big.NewInt(42), + Gas: 100_000, + GasPrice: big.NewInt(875000000), + Data: selfDestructContract, + }) + gen.AddTx(tx) + } else { + // Call it. + tx, _ := types.SignTx(types.NewTransaction(1, contract, big.NewInt(0), 100_000, big.NewInt(875000000), nil), signer, testKey) + gen.AddTx(tx) + } + }) + + { + // Check self-destructed contract in the witness. + // The way 6780 is implemented today, it always SubBalance from the self-destructed contract, and AddBalance + // to the beneficiary. In this case both addresses are the same, thus this might be optimizable from a gas + // perspective. But until that happens, we need to honor this "balance reading" adding it to the witness. + + selfDestructContractTreeKey := utils.CodeHashKey(contract[:]) + + var stateDiffIdx = -1 + for i, stemStateDiff := range statediffs[1] { + if bytes.Equal(stemStateDiff.Stem[:], selfDestructContractTreeKey[:31]) { + stateDiffIdx = i + break + } + } + if stateDiffIdx == -1 { + t.Fatal("no state diff found for stem") + } + + balanceStateDiff := statediffs[1][stateDiffIdx].SuffixDiffs[0] + if balanceStateDiff.Suffix != utils.BasicDataLeafKey { + t.Fatal("balance invalid suffix") + } + + // The original balance was 42. + var oldBalance [16]byte + oldBalance[15] = 42 + if !bytes.Equal((*balanceStateDiff.CurrentValue)[utils.BasicDataBalanceOffset:], oldBalance[:]) { + t.Fatal("the pre-state balance before self-destruct must be 42") + } + + // Note that the SubBalance+AddBalance net effect is a 0 change, so NewValue + // must be nil. + if balanceStateDiff.NewValue != nil { + t.Fatal("the post-state balance after self-destruct must be empty") + } + } +} + +// TestProcessVerkleSelfDestructInSameTxWithSelfBeneficiary checks the content of the witness +// if a selfdestruct occurs in the same tx as the one that created it, but the beneficiary +// is the selfdestructed account. +func TestProcessVerkleSelfDestructInSameTxWithSelfBeneficiary(t *testing.T) { + // The test txs were taken from a secondary testnet with chain id 69421 + config := *testKaustinenLikeChainConfig + config.ChainID.SetUint64(69421) + + var ( + signer = types.LatestSigner(&config) + testKey, _ = crypto.HexToECDSA("b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291") + gspec = verkleTestGenesis(&config) + deployer = crypto.PubkeyToAddress(testKey.PublicKey) + contract = crypto.CreateAddress(deployer, 0) + ) + + // The goal of this test is to test SELFDESTRUCT that happens while executing + // the init code of a contract creation, that occurs in **the same** transaction. + // The balance is sent to itself. + t.Logf("Contract: %v", contract.String()) + + selfDestructContract := []byte{byte(vm.ADDRESS), byte(vm.SELFDESTRUCT)} + + _, _, _, _, stateDiffs := GenerateVerkleChainWithGenesis(gspec, beacon.New(ethash.NewFaker()), 1, func(i int, gen *BlockGen) { + gen.SetPoS() + tx, _ := types.SignNewTx(testKey, signer, &types.LegacyTx{Nonce: 0, + Value: big.NewInt(42), + Gas: 100_000, + GasPrice: big.NewInt(875000000), + Data: selfDestructContract, + }) + gen.AddTx(tx) + }) + stateDiff := stateDiffs[0] // state difference of block 1 + + { // Check self-destructed contract in the witness + selfDestructContractTreeKey := utils.CodeHashKey(contract[:]) + + var stateDiffIdx = -1 + for i, stemStateDiff := range stateDiff { + if bytes.Equal(stemStateDiff.Stem[:], selfDestructContractTreeKey[:31]) { + stateDiffIdx = i + break + } + } + if stateDiffIdx == -1 { + t.Fatal("no state diff found for stem") + } + balanceStateDiff := stateDiff[stateDiffIdx].SuffixDiffs[0] + if balanceStateDiff.Suffix != utils.BasicDataLeafKey { + t.Fatal("balance invalid suffix") + } + if balanceStateDiff.CurrentValue != nil { + t.Fatal("the pre-state balance before must be nil, since the contract didn't exist") + } + // Ensure that the value is burnt, and therefore that the balance of the self-destructed + // contract isn't modified (it should remain missing from the state) + if balanceStateDiff.NewValue != nil { + t.Fatal("the post-state balance after self-destruct must be nil since the contract shouldn't be created at all") + } + } +} + +// TestProcessVerkleSelfDestructInSameTxWithSelfBeneficiaryAndPrefundedAccount checks the +// content of the witness if a selfdestruct occurs in the same tx as the one that created it, +// it, but the beneficiary is the selfdestructed account. The difference with the test above, +// is that the created account is prefunded and so the final value should be 0. +func TestProcessVerkleSelfDestructInSameTxWithSelfBeneficiaryAndPrefundedAccount(t *testing.T) { + // The test txs were taken from a secondary testnet with chain id 69421 + config := *testKaustinenLikeChainConfig + config.ChainID.SetUint64(69421) + + var ( + signer = types.LatestSigner(&config) + testKey, _ = crypto.HexToECDSA("b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291") + gspec = verkleTestGenesis(&config) + deployer = crypto.PubkeyToAddress(testKey.PublicKey) + contract = crypto.CreateAddress(deployer, 0) + ) + // 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 current version of the spec requires. + // TODO(gballet) revisit after the spec has been modified. + gspec.Alloc[contract] = types.Account{ + Balance: big.NewInt(100), + } + + selfDestructContract := []byte{byte(vm.ADDRESS), byte(vm.SELFDESTRUCT)} + + _, _, _, _, stateDiffs := GenerateVerkleChainWithGenesis(gspec, beacon.New(ethash.NewFaker()), 1, func(i int, gen *BlockGen) { + gen.SetPoS() + tx, _ := types.SignNewTx(testKey, signer, &types.LegacyTx{Nonce: 0, + Value: big.NewInt(42), + Gas: 100_000, + GasPrice: big.NewInt(875000000), + Data: selfDestructContract, + }) + gen.AddTx(tx) + }) + stateDiff := stateDiffs[0] // state difference of block 1 + + { // Check self-destructed contract in the witness + selfDestructContractTreeKey := utils.CodeHashKey(contract[:]) + + var stateDiffIdx = -1 + for i, stemStateDiff := range stateDiff { + if bytes.Equal(stemStateDiff.Stem[:], selfDestructContractTreeKey[:31]) { + stateDiffIdx = i + break + } + } + if stateDiffIdx == -1 { + t.Fatal("no state diff found for stem") + } + balanceStateDiff := stateDiff[stateDiffIdx].SuffixDiffs[0] + if balanceStateDiff.Suffix != utils.BasicDataLeafKey { + t.Fatal("balance invalid suffix") + } + expected, _ := hex.DecodeString("0000000000000000000000000000000000000000000000000000000000000064") + if balanceStateDiff.CurrentValue == nil || !bytes.Equal(balanceStateDiff.CurrentValue[:], expected) { + t.Fatalf("incorrect prestate balance: %x != %x", *balanceStateDiff.CurrentValue, expected) + } + // Ensure that the value is burnt, and therefore that the balance of the self-destructed + // contract isn't modified (it should remain missing from the state) + expected = make([]byte, 32) + if balanceStateDiff.NewValue == nil { + t.Fatal("incorrect nil poststate balance") + } + if !bytes.Equal(balanceStateDiff.NewValue[:], expected[:]) { + t.Fatalf("incorrect poststate balance: %x != %x", *balanceStateDiff.NewValue, expected[:]) + } + } +} diff --git a/core/vm/common.go b/core/vm/common.go index ba75950e37..658803b820 100644 --- a/core/vm/common.go +++ b/core/vm/common.go @@ -17,8 +17,9 @@ package vm import ( + "math" + "github.com/ethereum/go-ethereum/common" - "github.com/ethereum/go-ethereum/common/math" "github.com/holiman/uint256" ) diff --git a/core/vm/contracts.go b/core/vm/contracts.go index 104d2ba814..f54d5ab86e 100644 --- a/core/vm/contracts.go +++ b/core/vm/contracts.go @@ -22,6 +22,7 @@ import ( "errors" "fmt" "maps" + "math" "math/big" "github.com/consensys/gnark-crypto/ecc" @@ -29,7 +30,6 @@ import ( "github.com/consensys/gnark-crypto/ecc/bls12-381/fp" "github.com/consensys/gnark-crypto/ecc/bls12-381/fr" "github.com/ethereum/go-ethereum/common" - "github.com/ethereum/go-ethereum/common/math" "github.com/ethereum/go-ethereum/core/tracing" "github.com/ethereum/go-ethereum/crypto" "github.com/ethereum/go-ethereum/crypto/blake2b" @@ -398,7 +398,12 @@ func (c *bigModExp) RequiredGas(input []byte) uint64 { } adjExpLen.Add(adjExpLen, big.NewInt(int64(msb))) // Calculate the gas cost of the operation - gas := new(big.Int).Set(math.BigMax(modLen, baseLen)) + gas := new(big.Int) + if modLen.Cmp(baseLen) < 0 { + gas.Set(baseLen) + } else { + gas.Set(modLen) + } if c.eip2565 { // EIP-2565 has three changes // 1. Different multComplexity (inlined here) @@ -412,7 +417,9 @@ func (c *bigModExp) RequiredGas(input []byte) uint64 { gas.Rsh(gas, 3) gas.Mul(gas, gas) - gas.Mul(gas, math.BigMax(adjExpLen, big1)) + if adjExpLen.Cmp(big1) > 0 { + gas.Mul(gas, adjExpLen) + } // 2. Different divisor (`GQUADDIVISOR`) (3) gas.Div(gas, big3) if gas.BitLen() > 64 { @@ -425,7 +432,9 @@ func (c *bigModExp) RequiredGas(input []byte) uint64 { return gas.Uint64() } gas = modexpMultComplexity(gas) - gas.Mul(gas, math.BigMax(adjExpLen, big1)) + if adjExpLen.Cmp(big1) > 0 { + gas.Mul(gas, adjExpLen) + } gas.Div(gas, big20) if gas.BitLen() > 64 { diff --git a/core/vm/eips.go b/core/vm/eips.go index 71d51f81ef..a51a18dc60 100644 --- a/core/vm/eips.go +++ b/core/vm/eips.go @@ -23,6 +23,8 @@ import ( "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/core/tracing" + "github.com/ethereum/go-ethereum/core/types" + "github.com/ethereum/go-ethereum/crypto" "github.com/ethereum/go-ethereum/params" "github.com/holiman/uint256" ) @@ -40,6 +42,7 @@ var activators = map[int]func(*JumpTable){ 1344: enable1344, 1153: enable1153, 4762: enable4762, + 7702: enable7702, } // EnableEIP enables the given EIP on the config. @@ -703,3 +706,68 @@ func enableEOF(jt *JumpTable) { memorySize: memoryExtCall, } } + +// opExtCodeCopyEIP7702 implements the EIP-7702 variation of opExtCodeCopy. +func opExtCodeCopyEIP7702(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) { + var ( + stack = scope.Stack + a = stack.pop() + memOffset = stack.pop() + codeOffset = stack.pop() + length = stack.pop() + ) + uint64CodeOffset, overflow := codeOffset.Uint64WithOverflow() + if overflow { + uint64CodeOffset = math.MaxUint64 + } + code := interpreter.evm.StateDB.GetCode(common.Address(a.Bytes20())) + if _, ok := types.ParseDelegation(code); ok { + code = types.DelegationPrefix[:2] + } + codeCopy := getData(code, uint64CodeOffset, length.Uint64()) + scope.Memory.Set(memOffset.Uint64(), length.Uint64(), codeCopy) + + return nil, nil +} + +// opExtCodeSizeEIP7702 implements the EIP-7702 variation of opExtCodeSize. +func opExtCodeSizeEIP7702(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) { + slot := scope.Stack.peek() + code := interpreter.evm.StateDB.GetCode(common.Address(slot.Bytes20())) + if _, ok := types.ParseDelegation(code); ok { + code = types.DelegationPrefix[:2] + } + slot.SetUint64(uint64(len(code))) + return nil, nil +} + +// opExtCodeHashEIP7702 implements the EIP-7702 variation of opExtCodeHash. +func opExtCodeHashEIP7702(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) { + slot := scope.Stack.peek() + addr := common.Address(slot.Bytes20()) + if interpreter.evm.StateDB.Empty(addr) { + slot.Clear() + return nil, nil + } + code := interpreter.evm.StateDB.GetCode(addr) + if _, ok := types.ParseDelegation(code); ok { + // If the code is a delegation, return the prefix without version. + slot.SetBytes(crypto.Keccak256(types.DelegationPrefix[:2])) + } else { + // Otherwise, return normal code hash. + slot.SetBytes(interpreter.evm.StateDB.GetCodeHash(addr).Bytes()) + } + return nil, nil +} + +// enable7702 the EIP-7702 changes to support delegation designators. +func enable7702(jt *JumpTable) { + jt[EXTCODECOPY].execute = opExtCodeCopyEIP7702 + jt[EXTCODESIZE].execute = opExtCodeSizeEIP7702 + jt[EXTCODEHASH].execute = opExtCodeHashEIP7702 + + jt[CALL].dynamicGas = gasCallEIP7702 + jt[CALLCODE].dynamicGas = gasCallCodeEIP7702 + jt[STATICCALL].dynamicGas = gasStaticCallEIP7702 + jt[DELEGATECALL].dynamicGas = gasDelegateCallEIP7702 +} 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/eof_test.go b/core/vm/eof_test.go index 6c9925dec3..8106a29728 100644 --- a/core/vm/eof_test.go +++ b/core/vm/eof_test.go @@ -18,7 +18,6 @@ package vm import ( "encoding/hex" - "fmt" "reflect" "testing" @@ -95,7 +94,6 @@ func TestEOFSubcontainer(t *testing.T) { if err := got.UnmarshalBinary(b, true); err != nil { t.Fatal(err) } - fmt.Print(got) if res := got.MarshalBinary(); !reflect.DeepEqual(res, b) { t.Fatalf("invalid marshalling, want %v got %v", b, res) } diff --git a/core/vm/eof_validation_test.go b/core/vm/eof_validation_test.go index 6680ca3a5d..afb856a32c 100644 --- a/core/vm/eof_validation_test.go +++ b/core/vm/eof_validation_test.go @@ -251,7 +251,7 @@ func TestValidateCode(t *testing.T) { data: make([]byte, 0), subContainers: make([]*Container, 0), } - _, err := validateCode(test.code, test.section, container, &pragueEOFInstructionSet, false) + _, err := validateCode(test.code, test.section, container, &eofInstructionSet, false) if !errors.Is(err, test.err) { t.Errorf("test %d (%s): unexpected error (want: %v, got: %v)", i, common.Bytes2Hex(test.code), test.err, err) } @@ -277,7 +277,7 @@ func BenchmarkRJUMPI(b *testing.B) { } b.ResetTimer() for i := 0; i < b.N; i++ { - _, err := validateCode(code, 0, container, &pragueEOFInstructionSet, false) + _, err := validateCode(code, 0, container, &eofInstructionSet, false) if err != nil { b.Fatal(err) } @@ -309,7 +309,7 @@ func BenchmarkRJUMPV(b *testing.B) { } b.ResetTimer() for i := 0; i < b.N; i++ { - _, err := validateCode(code, 0, container, &pragueEOFInstructionSet, false) + _, err := validateCode(code, 0, container, &pragueInstructionSet, false) if err != nil { b.Fatal(err) } @@ -357,7 +357,7 @@ func BenchmarkEOFValidation(b *testing.B) { if err := container2.UnmarshalBinary(bin, true); err != nil { b.Fatal(err) } - if err := container2.ValidateCode(&pragueEOFInstructionSet, false); err != nil { + if err := container2.ValidateCode(&pragueInstructionSet, false); err != nil { b.Fatal(err) } } @@ -412,7 +412,7 @@ func BenchmarkEOFValidation2(b *testing.B) { if err := container2.UnmarshalBinary(bin, true); err != nil { b.Fatal(err) } - if err := container2.ValidateCode(&pragueEOFInstructionSet, false); err != nil { + if err := container2.ValidateCode(&pragueInstructionSet, false); err != nil { b.Fatal(err) } } @@ -468,7 +468,7 @@ func BenchmarkEOFValidation3(b *testing.B) { if err := container2.UnmarshalBinary(bin, true); err != nil { b.Fatal(err) } - if err := container2.ValidateCode(&pragueEOFInstructionSet, false); err != nil { + if err := container2.ValidateCode(&pragueInstructionSet, false); err != nil { b.Fatal(err) } } @@ -494,7 +494,7 @@ func BenchmarkRJUMPI_2(b *testing.B) { } b.ResetTimer() for i := 0; i < b.N; i++ { - _, err := validateCode(code, 0, container, &pragueEOFInstructionSet, false) + _, err := validateCode(code, 0, container, &pragueInstructionSet, false) if err != nil { b.Fatal(err) } @@ -512,6 +512,6 @@ func FuzzValidate(f *testing.F) { f.Fuzz(func(_ *testing.T, code []byte, maxStack uint16) { var container Container container.types = append(container.types, &functionMetadata{inputs: 0, outputs: 0x80, maxStackHeight: maxStack}) - validateCode(code, 0, &container, &pragueEOFInstructionSet, true) + validateCode(code, 0, &container, &pragueInstructionSet, true) }) } diff --git a/core/vm/evm.go b/core/vm/evm.go index 616668d565..1a0215459c 100644 --- a/core/vm/evm.go +++ b/core/vm/evm.go @@ -116,12 +116,13 @@ 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 { +// 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, - TxContext: txCtx, StateDB: statedb, Config: config, chainConfig: chainConfig, @@ -132,6 +133,11 @@ func NewEVM(blockCtx BlockContext, txCtx TxContext, statedb StateDB, chainConfig 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. @@ -139,14 +145,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 @@ -212,10 +217,7 @@ func (evm *EVM) Call(caller ContractRef, addr common.Address, input []byte, gas } else { // Initialise a new contract and set the code that is to be used by the EVM. // The contract is a scoped environment for this execution context only. - code := evm.StateDB.GetCode(addr) - if witness := evm.StateDB.Witness(); witness != nil { - witness.AddCode(code) - } + code := evm.resolveCode(addr) if len(code) == 0 { ret, err = nil, nil // gas is unchanged } else { @@ -223,7 +225,7 @@ func (evm *EVM) Call(caller ContractRef, addr common.Address, input []byte, gas // If the account has no code, we can abort here // The depth-check is already done, and precompiles handled above contract := NewContract(caller, AccountRef(addrCopy), value, gas) - contract.SetCallCode(&addrCopy, evm.StateDB.GetCodeHash(addrCopy), code) + contract.SetCallCode(&addrCopy, evm.resolveCodeHash(addrCopy), code) ret, err = evm.interpreter.Run(contract, input, false) gas = contract.Gas } @@ -283,10 +285,7 @@ func (evm *EVM) CallCode(caller ContractRef, addr common.Address, input []byte, // Initialise a new contract and set the code that is to be used by the EVM. // The contract is a scoped environment for this execution context only. contract := NewContract(caller, AccountRef(caller.Address()), value, gas) - if witness := evm.StateDB.Witness(); witness != nil { - witness.AddCode(evm.StateDB.GetCode(addrCopy)) - } - contract.SetCallCode(&addrCopy, evm.StateDB.GetCodeHash(addrCopy), evm.StateDB.GetCode(addrCopy)) + contract.SetCallCode(&addrCopy, evm.resolveCodeHash(addrCopy), evm.resolveCode(addrCopy)) ret, err = evm.interpreter.Run(contract, input, false) gas = contract.Gas } @@ -333,10 +332,7 @@ func (evm *EVM) DelegateCall(caller ContractRef, addr common.Address, input []by addrCopy := addr // Initialise a new contract and make initialise the delegate values contract := NewContract(caller, AccountRef(caller.Address()), nil, gas).AsDelegate() - if witness := evm.StateDB.Witness(); witness != nil { - witness.AddCode(evm.StateDB.GetCode(addrCopy)) - } - contract.SetCallCode(&addrCopy, evm.StateDB.GetCodeHash(addrCopy), evm.StateDB.GetCode(addrCopy)) + contract.SetCallCode(&addrCopy, evm.resolveCodeHash(addrCopy), evm.resolveCode(addrCopy)) ret, err = evm.interpreter.Run(contract, input, false) gas = contract.Gas } @@ -391,10 +387,7 @@ func (evm *EVM) StaticCall(caller ContractRef, addr common.Address, input []byte // Initialise a new contract and set the code that is to be used by the EVM. // The contract is a scoped environment for this execution context only. contract := NewContract(caller, AccountRef(addrCopy), new(uint256.Int), gas) - if witness := evm.StateDB.Witness(); witness != nil { - witness.AddCode(evm.StateDB.GetCode(addrCopy)) - } - contract.SetCallCode(&addrCopy, evm.StateDB.GetCodeHash(addrCopy), evm.StateDB.GetCode(addrCopy)) + contract.SetCallCode(&addrCopy, evm.resolveCodeHash(addrCopy), evm.resolveCode(addrCopy)) // When an error was returned by the EVM or when setting the creation code // above we revert to the snapshot and consume any gas remaining. Additionally // when we're in Homestead this also counts for code storage gas errors. @@ -574,6 +567,35 @@ func (evm *EVM) Create2(caller ContractRef, code []byte, gas uint64, endowment * return evm.create(caller, codeAndHash, gas, endowment, contractAddr, CREATE2) } +// resolveCode returns the code associated with the provided account. After +// Prague, it can also resolve code pointed to by a delegation designator. +func (evm *EVM) resolveCode(addr common.Address) []byte { + code := evm.StateDB.GetCode(addr) + if !evm.chainRules.IsPrague { + return code + } + if target, ok := types.ParseDelegation(code); ok { + // Note we only follow one level of delegation. + return evm.StateDB.GetCode(target) + } + return code +} + +// resolveCodeHash returns the code hash associated with the provided address. +// After Prague, it can also resolve code hash of the account pointed to by a +// delegation designator. Although this is not accessible in the EVM it is used +// internally to associate jumpdest analysis to code. +func (evm *EVM) resolveCodeHash(addr common.Address) common.Hash { + if evm.chainRules.IsPrague { + code := evm.StateDB.GetCode(addr) + if target, ok := types.ParseDelegation(code); ok { + // Note we only follow one level of delegation. + return evm.StateDB.GetCodeHash(target) + } + } + return evm.StateDB.GetCodeHash(addr) +} + // ChainConfig returns the environment's chain configuration func (evm *EVM) ChainConfig() *params.ChainConfig { return evm.chainConfig } @@ -612,8 +634,7 @@ func (evm *EVM) GetVMContext() *tracing.VMContext { BlockNumber: evm.Context.BlockNumber, Time: evm.Context.Time, Random: evm.Context.Random, - GasPrice: evm.TxContext.GasPrice, - ChainConfig: evm.ChainConfig(), + 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 35d6393fba..9b9a31a855 100644 --- a/core/vm/instructions.go +++ b/core/vm/instructions.go @@ -340,10 +340,6 @@ func opReturnDataCopy(pc *uint64, interpreter *EVMInterpreter, scope *ScopeConte func opExtCodeSize(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) { slot := scope.Stack.peek() - address := slot.Bytes20() - if witness := interpreter.evm.StateDB.Witness(); witness != nil { - witness.AddCode(interpreter.evm.StateDB.GetCode(address)) - } slot.SetUint64(uint64(interpreter.evm.StateDB.GetCodeSize(slot.Bytes20()))) return nil, nil } @@ -383,9 +379,6 @@ func opExtCodeCopy(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) } addr := common.Address(a.Bytes20()) code := interpreter.evm.StateDB.GetCode(addr) - if witness := interpreter.evm.StateDB.Witness(); witness != nil { - witness.AddCode(code) - } codeCopy := getData(code, uint64CodeOffset, length.Uint64()) scope.Memory.Set(memOffset.Uint64(), length.Uint64(), codeCopy) @@ -508,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 @@ -919,7 +911,7 @@ func opSelfdestruct6780(pc *uint64, interpreter *EVMInterpreter, scope *ScopeCon balance := interpreter.evm.StateDB.GetBalance(scope.Contract.Address()) interpreter.evm.StateDB.SubBalance(scope.Contract.Address(), balance, tracing.BalanceDecreaseSelfdestruct) interpreter.evm.StateDB.AddBalance(beneficiary.Bytes20(), balance, tracing.BalanceIncreaseSelfdestruct) - interpreter.evm.StateDB.Selfdestruct6780(scope.Contract.Address()) + interpreter.evm.StateDB.SelfDestruct6780(scope.Contract.Address()) if tracer := interpreter.evm.Config.Tracer; tracer != nil { if tracer.OnEnter != nil { tracer.OnEnter(interpreter.evm.depth, byte(SELFDESTRUCT), scope.Contract.Address(), beneficiary.Bytes20(), []byte{}, 0, balance.ToBig()) @@ -984,13 +976,13 @@ func makePush(size uint64, pushByteSize int) executionFunc { start = min(codeLen, int(*pc+1)) end = min(codeLen, start+pushByteSize) ) - scope.Stack.push(new(uint256.Int).SetBytes( - common.RightPadBytes( - scope.Contract.Code[start:end], - pushByteSize, - )), - ) + a := new(uint256.Int).SetBytes(scope.Contract.Code[start:end]) + // Missing bytes: pushByteSize - len(pushData) + if missing := pushByteSize - (end - start); missing > 0 { + a.Lsh(a, uint(8*missing)) + } + scope.Stack.push(a) *pc += size return nil, nil } diff --git a/core/vm/instructions_test.go b/core/vm/instructions_test.go index a3f9ee81d1..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) @@ -927,3 +909,75 @@ func TestOpMCopy(t *testing.T) { } } } + +// TestPush sanity-checks how code with immediates are handled when the code size is +// smaller than the size of the immediate. +func TestPush(t *testing.T) { + code := common.FromHex("0011223344556677889900aabbccddeeff0102030405060708090a0b0c0d0e0ff1e1d1c1b1a19181716151413121") + + push32 := makePush(32, 32) + + scope := &ScopeContext{ + Memory: nil, + Stack: newstack(), + Contract: &Contract{ + Code: code, + }, + } + for i, want := range []string{ + "0x11223344556677889900aabbccddeeff0102030405060708090a0b0c0d0e0ff1", + "0x223344556677889900aabbccddeeff0102030405060708090a0b0c0d0e0ff1e1", + "0x3344556677889900aabbccddeeff0102030405060708090a0b0c0d0e0ff1e1d1", + "0x44556677889900aabbccddeeff0102030405060708090a0b0c0d0e0ff1e1d1c1", + "0x556677889900aabbccddeeff0102030405060708090a0b0c0d0e0ff1e1d1c1b1", + "0x6677889900aabbccddeeff0102030405060708090a0b0c0d0e0ff1e1d1c1b1a1", + "0x77889900aabbccddeeff0102030405060708090a0b0c0d0e0ff1e1d1c1b1a191", + "0x889900aabbccddeeff0102030405060708090a0b0c0d0e0ff1e1d1c1b1a19181", + "0x9900aabbccddeeff0102030405060708090a0b0c0d0e0ff1e1d1c1b1a1918171", + "0xaabbccddeeff0102030405060708090a0b0c0d0e0ff1e1d1c1b1a191817161", + "0xaabbccddeeff0102030405060708090a0b0c0d0e0ff1e1d1c1b1a19181716151", + "0xbbccddeeff0102030405060708090a0b0c0d0e0ff1e1d1c1b1a1918171615141", + "0xccddeeff0102030405060708090a0b0c0d0e0ff1e1d1c1b1a191817161514131", + "0xddeeff0102030405060708090a0b0c0d0e0ff1e1d1c1b1a19181716151413121", + "0xeeff0102030405060708090a0b0c0d0e0ff1e1d1c1b1a1918171615141312100", + "0xff0102030405060708090a0b0c0d0e0ff1e1d1c1b1a191817161514131210000", + "0x102030405060708090a0b0c0d0e0ff1e1d1c1b1a19181716151413121000000", + "0x2030405060708090a0b0c0d0e0ff1e1d1c1b1a1918171615141312100000000", + "0x30405060708090a0b0c0d0e0ff1e1d1c1b1a191817161514131210000000000", + "0x405060708090a0b0c0d0e0ff1e1d1c1b1a19181716151413121000000000000", + "0x5060708090a0b0c0d0e0ff1e1d1c1b1a1918171615141312100000000000000", + "0x60708090a0b0c0d0e0ff1e1d1c1b1a191817161514131210000000000000000", + "0x708090a0b0c0d0e0ff1e1d1c1b1a19181716151413121000000000000000000", + "0x8090a0b0c0d0e0ff1e1d1c1b1a1918171615141312100000000000000000000", + "0x90a0b0c0d0e0ff1e1d1c1b1a191817161514131210000000000000000000000", + "0xa0b0c0d0e0ff1e1d1c1b1a19181716151413121000000000000000000000000", + "0xb0c0d0e0ff1e1d1c1b1a1918171615141312100000000000000000000000000", + "0xc0d0e0ff1e1d1c1b1a191817161514131210000000000000000000000000000", + "0xd0e0ff1e1d1c1b1a19181716151413121000000000000000000000000000000", + "0xe0ff1e1d1c1b1a1918171615141312100000000000000000000000000000000", + "0xff1e1d1c1b1a191817161514131210000000000000000000000000000000000", + "0xf1e1d1c1b1a19181716151413121000000000000000000000000000000000000", + "0xe1d1c1b1a1918171615141312100000000000000000000000000000000000000", + "0xd1c1b1a191817161514131210000000000000000000000000000000000000000", + "0xc1b1a19181716151413121000000000000000000000000000000000000000000", + "0xb1a1918171615141312100000000000000000000000000000000000000000000", + "0xa191817161514131210000000000000000000000000000000000000000000000", + "0x9181716151413121000000000000000000000000000000000000000000000000", + "0x8171615141312100000000000000000000000000000000000000000000000000", + "0x7161514131210000000000000000000000000000000000000000000000000000", + "0x6151413121000000000000000000000000000000000000000000000000000000", + "0x5141312100000000000000000000000000000000000000000000000000000000", + "0x4131210000000000000000000000000000000000000000000000000000000000", + "0x3121000000000000000000000000000000000000000000000000000000000000", + "0x2100000000000000000000000000000000000000000000000000000000000000", + "0x0", + } { + pc := new(uint64) + *pc = uint64(i) + push32(pc, nil, scope) + res := scope.Stack.pop() + if have := res.Hex(); have != want { + t.Fatalf("case %d, have %v want %v", i, have, want) + } + } +} diff --git a/core/vm/interface.go b/core/vm/interface.go index 5f42643565..011541dde3 100644 --- a/core/vm/interface.go +++ b/core/vm/interface.go @@ -33,8 +33,8 @@ type StateDB interface { CreateAccount(common.Address) CreateContract(common.Address) - SubBalance(common.Address, *uint256.Int, tracing.BalanceChangeReason) - AddBalance(common.Address, *uint256.Int, tracing.BalanceChangeReason) + SubBalance(common.Address, *uint256.Int, tracing.BalanceChangeReason) uint256.Int + AddBalance(common.Address, *uint256.Int, tracing.BalanceChangeReason) uint256.Int GetBalance(common.Address) *uint256.Int GetNonce(common.Address) uint64 @@ -42,7 +42,9 @@ type StateDB interface { GetCodeHash(common.Address) common.Hash GetCode(common.Address) []byte - SetCode(common.Address, []byte) + + // SetCode sets the new code for the address, and returns the previous code, if any. + SetCode(common.Address, []byte) []byte GetCodeSize(common.Address) int AddRefund(uint64) @@ -51,16 +53,21 @@ type StateDB interface { GetCommittedState(common.Address, common.Hash) common.Hash GetState(common.Address, common.Hash) common.Hash - SetState(common.Address, common.Hash, common.Hash) + SetState(common.Address, common.Hash, common.Hash) common.Hash GetStorageRoot(addr common.Address) common.Hash GetTransientState(addr common.Address, key common.Hash) common.Hash SetTransientState(addr common.Address, key, value common.Hash) - SelfDestruct(common.Address) + SelfDestruct(common.Address) uint256.Int HasSelfDestructed(common.Address) bool - Selfdestruct6780(common.Address) + // SelfDestruct6780 is post-EIP6780 selfdestruct, which means that it's a + // send-all-to-beneficiary, unless the contract was created in this same + // transaction, in which case it will be destructed. + // This method returns the prior balance, along with a boolean which is + // true iff the object was indeed destructed. + SelfDestruct6780(common.Address) (uint256.Int, bool) // Exist reports whether the given account exists in state. // Notably this should also return true for self-destructed accounts. @@ -90,6 +97,9 @@ type StateDB interface { AddPreimage(common.Hash, []byte) Witness() *stateless.Witness + + // Finalise must be invoked at the end of a transaction + Finalise(bool) } // CallContext provides a basic interface for the EVM calling conventions. The EVM diff --git a/core/vm/interpreter.go b/core/vm/interpreter.go index 793f398367..996ed6e56a 100644 --- a/core/vm/interpreter.go +++ b/core/vm/interpreter.go @@ -109,6 +109,8 @@ func NewEVMInterpreter(evm *EVM) *EVMInterpreter { case evm.chainRules.IsVerkle: // TODO replace with proper instruction set when fork is specified table = &verkleInstructionSet + case evm.chainRules.IsPrague: + table = &pragueInstructionSet case evm.chainRules.IsCancun: table = &cancunInstructionSet case evm.chainRules.IsShanghai: @@ -249,8 +251,11 @@ func (in *EVMInterpreter) Run(contract *Contract, input []byte, readOnly bool) ( } else if sLen > operation.maxStack { return nil, &ErrStackOverflow{stackLen: sLen, limit: operation.maxStack} } - if !contract.UseGas(cost, in.evm.Config.Tracer, tracing.GasChangeIgnored) { + // for tracing: this gas consumption event is emitted below in the debug section. + if contract.Gas < cost { return nil, ErrOutOfGas + } else { + contract.Gas -= cost } if operation.dynamicGas != nil { @@ -279,8 +284,11 @@ func (in *EVMInterpreter) Run(contract *Contract, input []byte, readOnly bool) ( if err != nil { return nil, fmt.Errorf("%w: %v", ErrOutOfGas, err) } - if !contract.UseGas(dynamicCost, in.evm.Config.Tracer, tracing.GasChangeIgnored) { + // for tracing: this gas consumption event is emitted below in the debug section. + if contract.Gas < dynamicCost { return nil, ErrOutOfGas + } else { + contract.Gas -= dynamicCost } // Do tracing before memory expansion diff --git a/core/vm/interpreter_test.go b/core/vm/interpreter_test.go index a1369648da..cacad8f813 100644 --- a/core/vm/interpreter_test.go +++ b/core/vm/interpreter_test.go @@ -17,11 +17,11 @@ package vm import ( + "math" "testing" "time" "github.com/ethereum/go-ethereum/common" - "github.com/ethereum/go-ethereum/common/math" "github.com/ethereum/go-ethereum/core/state" "github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/params" @@ -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/jump_table.go b/core/vm/jump_table.go index 658014f24c..6610fa7f9a 100644 --- a/core/vm/jump_table.go +++ b/core/vm/jump_table.go @@ -61,7 +61,8 @@ var ( shanghaiInstructionSet = newShanghaiInstructionSet() cancunInstructionSet = newCancunInstructionSet() verkleInstructionSet = newVerkleInstructionSet() - pragueEOFInstructionSet = newPragueEOFInstructionSet() + pragueInstructionSet = newPragueInstructionSet() + eofInstructionSet = newEOFInstructionSetForTesting() ) // JumpTable contains the EVM opcodes supported at a given fork. @@ -91,16 +92,22 @@ func newVerkleInstructionSet() JumpTable { return validate(instructionSet) } -func NewPragueEOFInstructionSetForTesting() JumpTable { - return newPragueEOFInstructionSet() +func NewEOFInstructionSetForTesting() JumpTable { + return newEOFInstructionSetForTesting() } -func newPragueEOFInstructionSet() JumpTable { - instructionSet := newCancunInstructionSet() +func newEOFInstructionSetForTesting() JumpTable { + instructionSet := newPragueInstructionSet() enableEOF(&instructionSet) return validate(instructionSet) } +func newPragueInstructionSet() JumpTable { + instructionSet := newCancunInstructionSet() + enable7702(&instructionSet) // EIP-7702 Setcode transaction type + return validate(instructionSet) +} + func newCancunInstructionSet() JumpTable { instructionSet := newShanghaiInstructionSet() enable4844(&instructionSet) // EIP-4844 (BLOBHASH opcode) diff --git a/core/vm/memory.go b/core/vm/memory.go index 1ddd8d1ead..5e11e83748 100644 --- a/core/vm/memory.go +++ b/core/vm/memory.go @@ -73,8 +73,7 @@ func (m *Memory) Set32(offset uint64, val *uint256.Int) { panic("invalid memory: store empty") } // Fill in relevant bits - b32 := val.Bytes32() - copy(m.store[offset:], b32[:]) + val.PutUint256(m.store[offset:]) } // Resize resizes the memory to size diff --git a/core/vm/opcodes.go b/core/vm/opcodes.go index 162be8f6c8..0820b20fb1 100644 --- a/core/vm/opcodes.go +++ b/core/vm/opcodes.go @@ -164,7 +164,7 @@ const ( // 0x80 range - dups. const ( - DUP1 = 0x80 + iota + DUP1 OpCode = 0x80 + iota DUP2 DUP3 DUP4 @@ -184,7 +184,7 @@ const ( // 0x90 range - swaps. const ( - SWAP1 = 0x90 + iota + SWAP1 OpCode = 0x90 + iota SWAP2 SWAP3 SWAP4 diff --git a/core/vm/operations_acl.go b/core/vm/operations_acl.go index b993b651ff..ff3875868f 100644 --- a/core/vm/operations_acl.go +++ b/core/vm/operations_acl.go @@ -22,6 +22,7 @@ import ( "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common/math" "github.com/ethereum/go-ethereum/core/tracing" + "github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/params" ) @@ -242,3 +243,70 @@ func makeSelfdestructGasFn(refundsEnabled bool) gasFunc { } return gasFunc } + +var ( + gasCallEIP7702 = makeCallVariantGasCallEIP7702(gasCall) + gasDelegateCallEIP7702 = makeCallVariantGasCallEIP7702(gasDelegateCall) + gasStaticCallEIP7702 = makeCallVariantGasCallEIP7702(gasStaticCall) + gasCallCodeEIP7702 = makeCallVariantGasCallEIP7702(gasCallCode) +) + +func makeCallVariantGasCallEIP7702(oldCalculator gasFunc) gasFunc { + return func(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) { + var ( + total uint64 // total dynamic gas used + addr = common.Address(stack.Back(1).Bytes20()) + ) + + // Check slot presence in the access list + if !evm.StateDB.AddressInAccessList(addr) { + evm.StateDB.AddAddressToAccessList(addr) + // The WarmStorageReadCostEIP2929 (100) is already deducted in the form of a constant cost, so + // the cost to charge for cold access, if any, is Cold - Warm + coldCost := params.ColdAccountAccessCostEIP2929 - params.WarmStorageReadCostEIP2929 + // Charge the remaining difference here already, to correctly calculate available + // gas for call + if !contract.UseGas(coldCost, evm.Config.Tracer, tracing.GasChangeCallStorageColdAccess) { + return 0, ErrOutOfGas + } + total += coldCost + } + + // Check if code is a delegation and if so, charge for resolution. + if target, ok := types.ParseDelegation(evm.StateDB.GetCode(addr)); ok { + var cost uint64 + if evm.StateDB.AddressInAccessList(target) { + cost = params.WarmStorageReadCostEIP2929 + } else { + evm.StateDB.AddAddressToAccessList(target) + cost = params.ColdAccountAccessCostEIP2929 + } + if !contract.UseGas(cost, evm.Config.Tracer, tracing.GasChangeCallStorageColdAccess) { + return 0, ErrOutOfGas + } + total += cost + } + + // Now call the old calculator, which takes into account + // - create new account + // - transfer value + // - memory expansion + // - 63/64ths rule + old, err := oldCalculator(evm, contract, stack, mem, memorySize) + if err != nil { + return old, err + } + + // Temporarily add the gas charge back to the contract and return value. By + // adding it to the return, it will be charged outside of this function, as + // part of the dynamic gas. This will ensure it is correctly reported to + // tracers. + contract.Gas += total + + var overflow bool + if total, overflow = math.SafeAdd(old, total); overflow { + return 0, ErrGasUintOverflow + } + return total, nil + } +} diff --git a/core/vm/operations_verkle.go b/core/vm/operations_verkle.go index 722d5ed2ce..3492994778 100644 --- a/core/vm/operations_verkle.go +++ b/core/vm/operations_verkle.go @@ -17,6 +17,8 @@ package vm import ( + gomath "math" + "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common/math" "github.com/ethereum/go-ethereum/params" @@ -126,7 +128,7 @@ func gasCodeCopyEip4762(evm *EVM, contract *Contract, stack *Stack, mem *Memory, ) uint64CodeOffset, overflow := codeOffset.Uint64WithOverflow() if overflow { - uint64CodeOffset = math.MaxUint64 + uint64CodeOffset = gomath.MaxUint64 } _, copyOffset, nonPaddedCopyLength := getDataAndAdjustedBounds(contract.Code, uint64CodeOffset, length.Uint64()) if !contract.IsDeployment { 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.go b/core/vm/runtime/runtime.go index f83ed682cd..2243e14b65 100644 --- a/core/vm/runtime/runtime.go +++ b/core/vm/runtime/runtime.go @@ -61,27 +61,26 @@ func setDefaults(cfg *Config) { cancunTime = uint64(0) ) cfg.ChainConfig = ¶ms.ChainConfig{ - ChainID: big.NewInt(1), - HomesteadBlock: new(big.Int), - DAOForkBlock: new(big.Int), - DAOForkSupport: false, - EIP150Block: new(big.Int), - EIP155Block: new(big.Int), - EIP158Block: new(big.Int), - ByzantiumBlock: new(big.Int), - ConstantinopleBlock: new(big.Int), - PetersburgBlock: new(big.Int), - IstanbulBlock: new(big.Int), - MuirGlacierBlock: new(big.Int), - BerlinBlock: new(big.Int), - LondonBlock: new(big.Int), - ArrowGlacierBlock: nil, - GrayGlacierBlock: nil, - TerminalTotalDifficulty: big.NewInt(0), - TerminalTotalDifficultyPassed: true, - MergeNetsplitBlock: nil, - ShanghaiTime: &shanghaiTime, - CancunTime: &cancunTime} + ChainID: big.NewInt(1), + HomesteadBlock: new(big.Int), + DAOForkBlock: new(big.Int), + DAOForkSupport: false, + EIP150Block: new(big.Int), + EIP155Block: new(big.Int), + EIP158Block: new(big.Int), + ByzantiumBlock: new(big.Int), + ConstantinopleBlock: new(big.Int), + PetersburgBlock: new(big.Int), + IstanbulBlock: new(big.Int), + MuirGlacierBlock: new(big.Int), + BerlinBlock: new(big.Int), + LondonBlock: new(big.Int), + ArrowGlacierBlock: nil, + GrayGlacierBlock: nil, + TerminalTotalDifficulty: big.NewInt(0), + MergeNetsplitBlock: nil, + ShanghaiTime: &shanghaiTime, + CancunTime: &cancunTime} } if cfg.Difficulty == nil { cfg.Difficulty = new(big.Int) @@ -109,10 +108,7 @@ func setDefaults(cfg *Config) { if cfg.BlobBaseFee == nil { cfg.BlobBaseFee = big.NewInt(params.BlobTxMinBlobGasprice) } - // Merge indicators - if t := cfg.ChainConfig.ShanghaiTime; cfg.ChainConfig.TerminalTotalDifficultyPassed || (t != nil && *t == 0) { - cfg.Random = &(common.Hash{}) - } + cfg.Random = &(common.Hash{}) } // Execute executes the code using the input as call data during the execution. @@ -146,13 +142,16 @@ func Execute(code, input []byte, cfg *Config) ([]byte, *state.StateDB, error) { // set the receiver's (the executing contract) code for execution. cfg.State.SetCode(address, code) // Call the code with the given configuration. - ret, _, err := vmenv.Call( + ret, leftOverGas, err := vmenv.Call( sender, common.BytesToAddress([]byte("contract")), input, cfg.GasLimit, uint256.MustFromBig(cfg.Value), ) + if cfg.EVMConfig.Tracer != nil && cfg.EVMConfig.Tracer.OnTxEnd != nil { + cfg.EVMConfig.Tracer.OnTxEnd(&types.Receipt{GasUsed: cfg.GasLimit - leftOverGas}, err) + } return ret, cfg.State, err } @@ -185,6 +184,9 @@ func Create(input []byte, cfg *Config) ([]byte, common.Address, uint64, error) { cfg.GasLimit, uint256.MustFromBig(cfg.Value), ) + if cfg.EVMConfig.Tracer != nil && cfg.EVMConfig.Tracer.OnTxEnd != nil { + cfg.EVMConfig.Tracer.OnTxEnd(&types.Receipt{GasUsed: cfg.GasLimit - leftOverGas}, err) + } return code, address, leftOverGas, err } @@ -218,5 +220,8 @@ func Call(address common.Address, input []byte, cfg *Config) ([]byte, uint64, er cfg.GasLimit, uint256.MustFromBig(cfg.Value), ) + if cfg.EVMConfig.Tracer != nil && cfg.EVMConfig.Tracer.OnTxEnd != nil { + cfg.EVMConfig.Tracer.OnTxEnd(&types.Receipt{GasUsed: cfg.GasLimit - leftOverGas}, err) + } return ret, leftOverGas, err } diff --git a/core/vm/runtime/runtime_test.go b/core/vm/runtime/runtime_test.go index 9046dad5fe..6074e9a096 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" @@ -395,7 +397,7 @@ func benchmarkNonModifyingCode(gas uint64, code []byte, name string, tracerCode cfg.State, _ = state.New(types.EmptyRootHash, state.NewDatabaseForTesting()) cfg.GasLimit = gas if len(tracerCode) > 0 { - tracer, err := tracers.DefaultDirectory.New(tracerCode, new(tracers.Context), nil) + tracer, err := tracers.DefaultDirectory.New(tracerCode, new(tracers.Context), nil, cfg.ChainConfig) if err != nil { b.Fatal(err) } @@ -436,99 +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), - } + 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 - 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), - } + 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 - 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), - } + 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 - 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), - } + 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 - 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 + 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() - 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), - } + 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() - 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() + 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{ @@ -540,6 +489,7 @@ func BenchmarkSimpleLoop(b *testing.B) { benchmarkNonModifyingCode(100000000, staticCallIdentity, "staticcall-identity-100M", "", b) benchmarkNonModifyingCode(100000000, callIdentity, "call-identity-100M", "", b) benchmarkNonModifyingCode(100000000, loopingCode, "loop-100M", "", b) + benchmarkNonModifyingCode(100000000, loopingCode2, "loop2-100M", "", b) benchmarkNonModifyingCode(100000000, callInexistant, "call-nonexist-100M", "", b) benchmarkNonModifyingCode(100000000, callEOA, "call-EOA-100M", "", b) benchmarkNonModifyingCode(100000000, callRevertingContractWithInput, "call-reverting-100M", "", b) @@ -721,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) } } @@ -766,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"`}, }, } @@ -887,7 +788,7 @@ func TestRuntimeJSTracer(t *testing.T) { statedb.SetCode(common.HexToAddress("0xee"), calleeCode) statedb.SetCode(common.HexToAddress("0xff"), suicideCode) - tracer, err := tracers.DefaultDirectory.New(jsTracer, new(tracers.Context), nil) + tracer, err := tracers.DefaultDirectory.New(jsTracer, new(tracers.Context), nil, params.MergedTestChainConfig) if err != nil { t.Fatal(err) } @@ -922,7 +823,7 @@ func TestJSTracerCreateTx(t *testing.T) { code := []byte{byte(vm.PUSH1), 0, byte(vm.PUSH1), 0, byte(vm.RETURN)} statedb, _ := state.New(types.EmptyRootHash, state.NewDatabaseForTesting()) - tracer, err := tracers.DefaultDirectory.New(jsTracer, new(tracers.Context), nil) + tracer, err := tracers.DefaultDirectory.New(jsTracer, new(tracers.Context), nil, params.MergedTestChainConfig) if err != nil { t.Fatal(err) } @@ -946,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() {}, @@ -973,3 +866,83 @@ func BenchmarkTracerStepVsCallFrame(b *testing.B) { benchmarkNonModifyingCode(10000000, code, "tracer-step-10M", stepTracer, b) benchmarkNonModifyingCode(10000000, code, "tracer-call-frame-10M", callFrameTracer, b) } + +// TestDelegatedAccountAccessCost tests that calling an account with an EIP-7702 +// delegation designator incurs the correct amount of gas based on the tracer. +func TestDelegatedAccountAccessCost(t *testing.T) { + statedb, _ := state.New(types.EmptyRootHash, state.NewDatabaseForTesting()) + statedb.SetCode(common.HexToAddress("0xff"), types.AddressToDelegation(common.HexToAddress("0xaa"))) + statedb.SetCode(common.HexToAddress("0xaa"), program.New().Return(0, 0).Bytes()) + + for i, tc := range []struct { + code []byte + step int + want uint64 + }{ + { // CALL(0xff) + code: []byte{ + byte(vm.PUSH1), 0x0, + byte(vm.DUP1), byte(vm.DUP1), byte(vm.DUP1), byte(vm.DUP1), + byte(vm.PUSH1), 0xff, byte(vm.DUP1), byte(vm.CALL), byte(vm.POP), + }, + step: 7, + want: 5455, + }, + { // CALLCODE(0xff) + code: []byte{ + byte(vm.PUSH1), 0x0, + byte(vm.DUP1), byte(vm.DUP1), byte(vm.DUP1), byte(vm.DUP1), + byte(vm.PUSH1), 0xff, byte(vm.DUP1), byte(vm.CALLCODE), byte(vm.POP), + }, + step: 7, + want: 5455, + }, + { // DELEGATECALL(0xff) + code: []byte{ + byte(vm.PUSH1), 0x0, + byte(vm.DUP1), byte(vm.DUP1), byte(vm.DUP1), + byte(vm.PUSH1), 0xff, byte(vm.DUP1), byte(vm.DELEGATECALL), byte(vm.POP), + }, + step: 6, + want: 5455, + }, + { // STATICCALL(0xff) + code: []byte{ + byte(vm.PUSH1), 0x0, + byte(vm.DUP1), byte(vm.DUP1), byte(vm.DUP1), + byte(vm.PUSH1), 0xff, byte(vm.DUP1), byte(vm.STATICCALL), byte(vm.POP), + }, + step: 6, + want: 5455, + }, + { // SELFDESTRUCT(0xff): should not be affected by resolution + code: []byte{ + byte(vm.PUSH1), 0xff, byte(vm.SELFDESTRUCT), + }, + step: 1, + want: 7600, + }, + } { + var step = 0 + var have = uint64(0) + Execute(tc.code, nil, &Config{ + ChainConfig: params.MergedTestChainConfig, + State: statedb, + EVMConfig: vm.Config{ + 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++ + }, + }, + }, + }) + if want := tc.want; have != want { + t.Fatalf("testcase %d, gas report wrong, step %d, have %d want %d", i, tc.step, have, want) + } + } +} diff --git a/crypto/bn256/gnark/g1.go b/crypto/bn256/gnark/g1.go new file mode 100644 index 0000000000..2f933dd536 --- /dev/null +++ b/crypto/bn256/gnark/g1.go @@ -0,0 +1,51 @@ +package bn256 + +import ( + "math/big" + + "github.com/consensys/gnark-crypto/ecc/bn254" +) + +// G1 is the affine representation of a G1 group element. +// +// Since this code is used for precompiles, using Jacobian +// points are not beneficial because there are no intermediate +// points to allow us to save on inversions. +// +// Note: We also use this struct so that we can conform to the existing API +// that the precompiles want. +type G1 struct { + inner bn254.G1Affine +} + +// Add adds `a` and `b` together, storing the result in `g` +func (g *G1) Add(a, b *G1) { + g.inner.Add(&a.inner, &b.inner) +} + +// ScalarMult computes the scalar multiplication between `a` and +// `scalar`, storing the result in `g` +func (g *G1) ScalarMult(a *G1, scalar *big.Int) { + g.inner.ScalarMultiplication(&a.inner, scalar) +} + +// Unmarshal deserializes `buf` into `g` +// +// Note: whether the deserialization is of a compressed +// or an uncompressed point, is encoded in the bytes. +// +// For our purpose, the point will always be serialized +// as uncompressed, ie 64 bytes. +// +// This method also checks whether the point is on the +// curve and in the prime order subgroup. +func (g *G1) Unmarshal(buf []byte) (int, error) { + return g.inner.SetBytes(buf) +} + +// Marshal serializes the point into a byte slice. +// +// Note: The point is serialized as uncompressed. +func (p *G1) Marshal() []byte { + return p.inner.Marshal() +} diff --git a/crypto/bn256/gnark/g2.go b/crypto/bn256/gnark/g2.go new file mode 100644 index 0000000000..205373a591 --- /dev/null +++ b/crypto/bn256/gnark/g2.go @@ -0,0 +1,38 @@ +package bn256 + +import ( + "github.com/consensys/gnark-crypto/ecc/bn254" +) + +// G2 is the affine representation of a G2 group element. +// +// Since this code is used for precompiles, using Jacobian +// points are not beneficial because there are no intermediate +// points and G2 in particular is only used for the pairing input. +// +// Note: We also use this struct so that we can conform to the existing API +// that the precompiles want. +type G2 struct { + inner bn254.G2Affine +} + +// Unmarshal deserializes `buf` into `g` +// +// Note: whether the deserialization is of a compressed +// or an uncompressed point, is encoded in the bytes. +// +// For our purpose, the point will always be serialized +// as uncompressed, ie 128 bytes. +// +// This method also checks whether the point is on the +// curve and in the prime order subgroup. +func (g *G2) Unmarshal(buf []byte) (int, error) { + return g.inner.SetBytes(buf) +} + +// Marshal serializes the point into a byte slice. +// +// Note: The point is serialized as uncompressed. +func (g *G2) Marshal() []byte { + return g.inner.Marshal() +} diff --git a/crypto/bn256/gnark/gt.go b/crypto/bn256/gnark/gt.go new file mode 100644 index 0000000000..c30022c5f8 --- /dev/null +++ b/crypto/bn256/gnark/gt.go @@ -0,0 +1,65 @@ +package bn256 + +import ( + "fmt" + "math/big" + + "github.com/consensys/gnark-crypto/ecc/bn254" +) + +// GT is the affine representation of a GT field element. +// +// Note: GT is not explicitly used in mainline code. +// It is needed for fuzzing. +type GT struct { + inner bn254.GT +} + +// Pair compute the optimal Ate pairing between a G1 and +// G2 element. +// +// Note: This method is not explicitly used in mainline code. +// It is needed for fuzzing. It should also be noted, +// that the output of this function may not match other +func Pair(a_ *G1, b_ *G2) *GT { + a := a_.inner + b := b_.inner + + pairingOutput, err := bn254.Pair([]bn254.G1Affine{a}, []bn254.G2Affine{b}) + + if err != nil { + // Since this method is only called during fuzzing, it is okay to panic here. + // We do not return an error to match the interface of the other bn256 libraries. + panic(fmt.Sprintf("gnark/bn254 encountered error: %v", err)) + } + + return >{ + inner: pairingOutput, + } +} + +// Unmarshal deserializes `buf` into `g` +// +// Note: This method is not explicitly used in mainline code. +// It is needed for fuzzing. +func (g *GT) Unmarshal(buf []byte) error { + return g.inner.SetBytes(buf) +} + +// Marshal serializes the point into a byte slice. +// +// Note: This method is not explicitly used in mainline code. +// It is needed for fuzzing. +func (g *GT) Marshal() []byte { + bytes := g.inner.Bytes() + return bytes[:] +} + +// Exp raises `base` to the power of `exponent` +// +// Note: This method is not explicitly used in mainline code. +// It is needed for fuzzing. +func (g *GT) Exp(base GT, exponent *big.Int) *GT { + g.inner.Exp(base.inner, exponent) + return g +} diff --git a/crypto/bn256/gnark/pairing.go b/crypto/bn256/gnark/pairing.go new file mode 100644 index 0000000000..39e8a657f4 --- /dev/null +++ b/crypto/bn256/gnark/pairing.go @@ -0,0 +1,73 @@ +package bn256 + +import ( + "github.com/consensys/gnark-crypto/ecc/bn254" +) + +// Computes the following relation: ∏ᵢ e(Pᵢ, Qᵢ) =? 1 +// +// To explain why gnark returns a (bool, error): +// +// - If the function `e` does not return a result then internally +// an error is returned. +// - If `e` returns a result, then error will be nil, +// but if this value is not `1` then the boolean value will be false +// +// We therefore check for an error, and return false if its non-nil and +// then return the value of the boolean if not. +func PairingCheck(a_ []*G1, b_ []*G2) bool { + a := getInnerG1s(a_) + b := getInnerG2s(b_) + + // Assume that len(a) == len(b) + // + // The pairing function will return + // false, if this is not the case. + size := len(a) + + // Check if input is empty -- gnark will + // return false on an empty input, however + // the ossified behavior is to return true + // on an empty input, so we add this if statement. + if size == 0 { + return true + } + + ok, err := bn254.PairingCheck(a, b) + if err != nil { + return false + } + return ok +} + +// getInnerG1s gets the inner gnark G1 elements. +// +// These methods are used for two reasons: +// +// - We use a new type `G1`, so we need to convert from +// []*G1 to []*bn254.G1Affine +// - The gnark API accepts slices of values and not slices of +// pointers to values, so we need to return []bn254.G1Affine +// instead of []*bn254.G1Affine. +func getInnerG1s(pointerSlice []*G1) []bn254.G1Affine { + gnarkValues := make([]bn254.G1Affine, 0, len(pointerSlice)) + for _, ptr := range pointerSlice { + if ptr != nil { + gnarkValues = append(gnarkValues, ptr.inner) + } + } + return gnarkValues +} + +// getInnerG2s gets the inner gnark G2 elements. +// +// The rationale for this method is the same as `getInnerG1s`. +func getInnerG2s(pointerSlice []*G2) []bn254.G2Affine { + gnarkValues := make([]bn254.G2Affine, 0, len(pointerSlice)) + for _, ptr := range pointerSlice { + if ptr != nil { + gnarkValues = append(gnarkValues, ptr.inner) + } + } + return gnarkValues +} diff --git a/crypto/signature_nocgo.go b/crypto/signature_nocgo.go index 5ac3765c71..16a785a186 100644 --- a/crypto/signature_nocgo.go +++ b/crypto/signature_nocgo.go @@ -25,8 +25,8 @@ import ( "fmt" "math/big" - "github.com/btcsuite/btcd/btcec/v2" - btc_ecdsa "github.com/btcsuite/btcd/btcec/v2/ecdsa" + "github.com/decred/dcrd/dcrec/secp256k1/v4" + decred_ecdsa "github.com/decred/dcrd/dcrec/secp256k1/v4/ecdsa" ) // Ecrecover returns the uncompressed public key that created the given signature. @@ -39,16 +39,16 @@ func Ecrecover(hash, sig []byte) ([]byte, error) { return bytes, err } -func sigToPub(hash, sig []byte) (*btcec.PublicKey, error) { +func sigToPub(hash, sig []byte) (*secp256k1.PublicKey, error) { if len(sig) != SignatureLength { return nil, errors.New("invalid signature") } - // Convert to btcec input format with 'recovery id' v at the beginning. + // Convert to secp256k1 input format with 'recovery id' v at the beginning. btcsig := make([]byte, SignatureLength) btcsig[0] = sig[RecoveryIDOffset] + 27 copy(btcsig[1:], sig) - pub, _, err := btc_ecdsa.RecoverCompact(btcsig, hash) + pub, _, err := decred_ecdsa.RecoverCompact(btcsig, hash) return pub, err } @@ -82,13 +82,13 @@ func Sign(hash []byte, prv *ecdsa.PrivateKey) ([]byte, error) { if prv.Curve != S256() { return nil, errors.New("private key curve is not secp256k1") } - // ecdsa.PrivateKey -> btcec.PrivateKey - var priv btcec.PrivateKey + // ecdsa.PrivateKey -> secp256k1.PrivateKey + var priv secp256k1.PrivateKey if overflow := priv.Key.SetByteSlice(prv.D.Bytes()); overflow || priv.Key.IsZero() { return nil, errors.New("invalid private key") } defer priv.Zero() - sig := btc_ecdsa.SignCompact(&priv, hash, false) // ref uncompressed pubkey + sig := decred_ecdsa.SignCompact(&priv, hash, false) // ref uncompressed pubkey // Convert to Ethereum signature format with 'recovery id' v at the end. v := sig[0] - 27 copy(sig, sig[1:]) @@ -103,19 +103,19 @@ func VerifySignature(pubkey, hash, signature []byte) bool { if len(signature) != 64 { return false } - var r, s btcec.ModNScalar + var r, s secp256k1.ModNScalar if r.SetByteSlice(signature[:32]) { return false // overflow } if s.SetByteSlice(signature[32:]) { return false } - sig := btc_ecdsa.NewSignature(&r, &s) - key, err := btcec.ParsePubKey(pubkey) + sig := decred_ecdsa.NewSignature(&r, &s) + key, err := secp256k1.ParsePubKey(pubkey) if err != nil { return false } - // Reject malleable signatures. libsecp256k1 does this check but btcec doesn't. + // Reject malleable signatures. libsecp256k1 does this check but decred doesn't. if s.IsOverHalfOrder() { return false } @@ -127,7 +127,7 @@ func DecompressPubkey(pubkey []byte) (*ecdsa.PublicKey, error) { if len(pubkey) != 33 { return nil, errors.New("invalid compressed public key length") } - key, err := btcec.ParsePubKey(pubkey) + key, err := secp256k1.ParsePubKey(pubkey) if err != nil { return nil, err } @@ -148,20 +148,20 @@ func DecompressPubkey(pubkey []byte) (*ecdsa.PublicKey, error) { // when constructing a PrivateKey. func CompressPubkey(pubkey *ecdsa.PublicKey) []byte { // NOTE: the coordinates may be validated with - // btcec.ParsePubKey(FromECDSAPub(pubkey)) - var x, y btcec.FieldVal + // secp256k1.ParsePubKey(FromECDSAPub(pubkey)) + var x, y secp256k1.FieldVal x.SetByteSlice(pubkey.X.Bytes()) y.SetByteSlice(pubkey.Y.Bytes()) - return btcec.NewPublicKey(&x, &y).SerializeCompressed() + return secp256k1.NewPublicKey(&x, &y).SerializeCompressed() } // S256 returns an instance of the secp256k1 curve. func S256() EllipticCurve { - return btCurve{btcec.S256()} + return btCurve{secp256k1.S256()} } type btCurve struct { - *btcec.KoblitzCurve + *secp256k1.KoblitzCurve } // Marshal converts a point given as (x, y) into a byte slice. diff --git a/docs/postmortems/2021-08-22-split-postmortem.md b/docs/postmortems/2021-08-22-split-postmortem.md index 5ec4f37e87..2e5c41c764 100644 --- a/docs/postmortems/2021-08-22-split-postmortem.md +++ b/docs/postmortems/2021-08-22-split-postmortem.md @@ -56,7 +56,7 @@ On the evening of 17th, we discussed options on how to handle it. We made a stat It was decided that in this specific instance, it would be possible to make a public announcement and a patch release: - The fix can be made pretty 'generically', e.g. always copying data on input to precompiles. -- The flaw is pretty difficult to find, given a generic fix in the call. The attacker needs to figure out that it concerns the precompiles, specifically the datcopy, and that it concerns the `RETURNDATA` buffer rather than the regular memory, and lastly the special circumstances to trigger it (overlapping but shifted input/output). +- The flaw is pretty difficult to find, given a generic fix in the call. The attacker needs to figure out that it concerns the precompiles, specifically the datacopy, and that it concerns the `RETURNDATA` buffer rather than the regular memory, and lastly the special circumstances to trigger it (overlapping but shifted input/output). Since we had merged the removal of `ETH65`, if the entire network were to upgrade, then nodes which have not yet implemented `ETH66` would be cut off from the network. After further discussions, we decided to: diff --git a/eth/api_backend.go b/eth/api_backend.go index 8a9898b956..be2101c6ec 100644 --- a/eth/api_backend.go +++ b/eth/api_backend.go @@ -249,18 +249,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 { @@ -275,10 +274,6 @@ func (b *EthAPIBackend) SubscribeChainHeadEvent(ch chan<- core.ChainHeadEvent) e return b.eth.BlockChain().SubscribeChainHeadEvent(ch) } -func (b *EthAPIBackend) SubscribeChainSideEvent(ch chan<- core.ChainSideEvent) event.Subscription { - return b.eth.BlockChain().SubscribeChainSideEvent(ch) -} - func (b *EthAPIBackend) SubscribeLogsEvent(ch chan<- []*types.Log) event.Subscription { return b.eth.BlockChain().SubscribeLogsEvent(ch) } diff --git a/eth/backend.go b/eth/backend.go index f10d99c3a7..a3aa0a7b9b 100644 --- a/eth/backend.go +++ b/eth/backend.go @@ -47,6 +47,7 @@ import ( "github.com/ethereum/go-ethereum/event" "github.com/ethereum/go-ethereum/internal/ethapi" "github.com/ethereum/go-ethereum/internal/shutdowncheck" + "github.com/ethereum/go-ethereum/internal/version" "github.com/ethereum/go-ethereum/log" "github.com/ethereum/go-ethereum/miner" "github.com/ethereum/go-ethereum/node" @@ -56,6 +57,7 @@ import ( "github.com/ethereum/go-ethereum/params" "github.com/ethereum/go-ethereum/rlp" "github.com/ethereum/go-ethereum/rpc" + gethversion "github.com/ethereum/go-ethereum/version" ) // Config contains the configuration options of the ETH protocol. @@ -172,7 +174,7 @@ func New(stack *node.Node, config *ethconfig.Config) (*Ethereum, error) { if !config.SkipBcVersionCheck { if bcVersion != nil && *bcVersion > core.BlockChainVersion { - return nil, fmt.Errorf("database version is v%d, Geth %s only supports v%d", *bcVersion, params.VersionWithMeta, core.BlockChainVersion) + return nil, fmt.Errorf("database version is v%d, Geth %s only supports v%d", *bcVersion, version.WithMeta, core.BlockChainVersion) } else if bcVersion == nil || *bcVersion < core.BlockChainVersion { if bcVersion != nil { // only print warning on upgrade, not on init log.Warn("Upgrade blockchain database version", "from", dbVer, "to", core.BlockChainVersion) @@ -197,7 +199,7 @@ func New(stack *node.Node, config *ethconfig.Config) (*Ethereum, error) { } ) if config.VMTrace != "" { - var traceConfig json.RawMessage + traceConfig := json.RawMessage("{}") if config.VMTraceJsonConfig != "" { traceConfig = json.RawMessage(config.VMTraceJsonConfig) } @@ -278,7 +280,7 @@ func makeExtraData(extra []byte) []byte { if len(extra) == 0 { // create default extradata extra, _ = rlp.EncodeToBytes([]interface{}{ - uint(params.VersionMajor<<16 | params.VersionMinor<<8 | params.VersionPatch), + uint(gethversion.Major<<16 | gethversion.Minor<<8 | gethversion.Patch), "geth", runtime.Version(), runtime.GOOS, @@ -422,17 +424,17 @@ func (s *Ethereum) Stop() error { // SyncMode retrieves the current sync mode, either explicitly set, or derived // from the chain status. -func (s *Ethereum) SyncMode() downloader.SyncMode { +func (s *Ethereum) SyncMode() ethconfig.SyncMode { // If we're in snap sync mode, return that directly if s.handler.snapSync.Load() { - return downloader.SnapSync + return ethconfig.SnapSync } // 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. head := s.blockchain.CurrentBlock() if pivot := rawdb.ReadLastPivotNumber(s.chainDb); pivot != nil { if head.Number.Uint64() < *pivot { - return downloader.SnapSync + return ethconfig.SnapSync } } // We are in a full sync, but the associated head state is missing. To complete @@ -440,8 +442,8 @@ func (s *Ethereum) SyncMode() downloader.SyncMode { // persistent state is corrupted, just mismatch with the head block. if !s.blockchain.HasState(head.Root) { log.Info("Reenabled snap sync as chain is stateless") - return downloader.SnapSync + return ethconfig.SnapSync } // Nope, we're really full syncing - return downloader.FullSync + return ethconfig.FullSync } diff --git a/eth/catalyst/api.go b/eth/catalyst/api.go index 79034fd00a..3e45ad9e4f 100644 --- a/eth/catalyst/api.go +++ b/eth/catalyst/api.go @@ -31,13 +31,13 @@ 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" "github.com/ethereum/go-ethereum/node" - "github.com/ethereum/go-ethereum/params" "github.com/ethereum/go-ethereum/params/forks" "github.com/ethereum/go-ethereum/rlp" "github.com/ethereum/go-ethereum/rpc" @@ -93,6 +93,7 @@ var caps = []string{ "engine_getPayloadV2", "engine_getPayloadV3", "engine_getPayloadV4", + "engine_getBlobsV1", "engine_newPayloadV1", "engine_newPayloadV2", "engine_newPayloadV3", @@ -536,6 +537,25 @@ func (api *ConsensusAPI) getPayload(payloadID engine.PayloadID, full bool) (*eng return data, nil } +// GetBlobsV1 returns a blob from the transaction pool. +func (api *ConsensusAPI) GetBlobsV1(hashes []common.Hash) ([]*engine.BlobAndProofV1, error) { + if len(hashes) > 128 { + return nil, engine.TooLargeRequest.With(fmt.Errorf("requested blob count too large: %v", len(hashes))) + } + res := make([]*engine.BlobAndProofV1, len(hashes)) + + blobs, proofs := api.eth.TxPool().GetBlobs(hashes) + for i := 0; i < len(blobs); i++ { + if blobs[i] != nil { + res[i] = &engine.BlobAndProofV1{ + Blob: (*blobs[i])[:], + Proof: (*proofs[i])[:], + } + } + } + return res, nil +} + // NewPayloadV1 creates an Eth1 block, inserts it in the chain, and returns the status of the chain. func (api *ConsensusAPI) NewPayloadV1(params engine.ExecutableData) (engine.PayloadStatusV1, error) { if params.Withdrawals != nil { @@ -593,8 +613,7 @@ func (api *ConsensusAPI) NewPayloadV3(params engine.ExecutableData, versionedHas } // NewPayloadV4 creates an Eth1 block, inserts it in the chain, and returns the status of the chain. -// NewPayloadV4 creates an Eth1 block, inserts it in the chain, and returns the status of the chain. -func (api *ConsensusAPI) NewPayloadV4(params engine.ExecutableData, versionedHashes []common.Hash, beaconRoot *common.Hash, requests [][]byte) (engine.PayloadStatusV1, error) { +func (api *ConsensusAPI) NewPayloadV4(params engine.ExecutableData, versionedHashes []common.Hash, beaconRoot *common.Hash, executionRequests []hexutil.Bytes) (engine.PayloadStatusV1, error) { if params.Withdrawals == nil { return engine.PayloadStatusV1{Status: engine.INVALID}, engine.InvalidParams.With(errors.New("nil withdrawals post-shanghai")) } @@ -611,13 +630,14 @@ func (api *ConsensusAPI) NewPayloadV4(params engine.ExecutableData, versionedHas if beaconRoot == nil { return engine.PayloadStatusV1{Status: engine.INVALID}, engine.InvalidParams.With(errors.New("nil beaconRoot post-cancun")) } - if requests == nil { + if executionRequests == nil { return engine.PayloadStatusV1{Status: engine.INVALID}, engine.InvalidParams.With(errors.New("nil executionRequests post-prague")) } if api.eth.BlockChain().Config().LatestFork(params.Timestamp) != forks.Prague { return engine.PayloadStatusV1{Status: engine.INVALID}, engine.UnsupportedFork.With(errors.New("newPayloadV4 must only be called for prague payloads")) } + requests := convertRequests(executionRequests) return api.newPayload(params, versionedHashes, beaconRoot, requests, false) } @@ -682,7 +702,7 @@ func (api *ConsensusAPI) NewPayloadWithWitnessV3(params engine.ExecutableData, v // NewPayloadWithWitnessV4 is analogous to NewPayloadV4, only it also generates // and returns a stateless witness after running the payload. -func (api *ConsensusAPI) NewPayloadWithWitnessV4(params engine.ExecutableData, versionedHashes []common.Hash, beaconRoot *common.Hash, requests [][]byte) (engine.PayloadStatusV1, error) { +func (api *ConsensusAPI) NewPayloadWithWitnessV4(params engine.ExecutableData, versionedHashes []common.Hash, beaconRoot *common.Hash, executionRequests []hexutil.Bytes) (engine.PayloadStatusV1, error) { if params.Withdrawals == nil { return engine.PayloadStatusV1{Status: engine.INVALID}, engine.InvalidParams.With(errors.New("nil withdrawals post-shanghai")) } @@ -699,13 +719,14 @@ func (api *ConsensusAPI) NewPayloadWithWitnessV4(params engine.ExecutableData, v if beaconRoot == nil { return engine.PayloadStatusV1{Status: engine.INVALID}, engine.InvalidParams.With(errors.New("nil beaconRoot post-cancun")) } - if requests == nil { + if executionRequests == nil { return engine.PayloadStatusV1{Status: engine.INVALID}, engine.InvalidParams.With(errors.New("nil executionRequests post-prague")) } if api.eth.BlockChain().Config().LatestFork(params.Timestamp) != forks.Prague { return engine.PayloadStatusV1{Status: engine.INVALID}, engine.UnsupportedFork.With(errors.New("newPayloadWithWitnessV4 must only be called for prague payloads")) } + requests := convertRequests(executionRequests) return api.newPayload(params, versionedHashes, beaconRoot, requests, true) } @@ -770,7 +791,7 @@ func (api *ConsensusAPI) ExecuteStatelessPayloadV3(params engine.ExecutableData, // ExecuteStatelessPayloadV4 is analogous to NewPayloadV4, only it operates in // a stateless mode on top of a provided witness instead of the local database. -func (api *ConsensusAPI) ExecuteStatelessPayloadV4(params engine.ExecutableData, versionedHashes []common.Hash, beaconRoot *common.Hash, requests [][]byte, opaqueWitness hexutil.Bytes) (engine.StatelessPayloadStatusV1, error) { +func (api *ConsensusAPI) ExecuteStatelessPayloadV4(params engine.ExecutableData, versionedHashes []common.Hash, beaconRoot *common.Hash, executionRequests []hexutil.Bytes, opaqueWitness hexutil.Bytes) (engine.StatelessPayloadStatusV1, error) { if params.Withdrawals == nil { return engine.StatelessPayloadStatusV1{Status: engine.INVALID}, engine.InvalidParams.With(errors.New("nil withdrawals post-shanghai")) } @@ -787,13 +808,14 @@ func (api *ConsensusAPI) ExecuteStatelessPayloadV4(params engine.ExecutableData, if beaconRoot == nil { return engine.StatelessPayloadStatusV1{Status: engine.INVALID}, engine.InvalidParams.With(errors.New("nil beaconRoot post-cancun")) } - if requests == nil { + if executionRequests == nil { return engine.StatelessPayloadStatusV1{Status: engine.INVALID}, engine.InvalidParams.With(errors.New("nil executionRequests post-prague")) } if api.eth.BlockChain().Config().LatestFork(params.Timestamp) != forks.Prague { return engine.StatelessPayloadStatusV1{Status: engine.INVALID}, engine.UnsupportedFork.With(errors.New("executeStatelessPayloadV4 must only be called for prague payloads")) } + requests := convertRequests(executionRequests) return api.executeStatelessPayload(params, versionedHashes, beaconRoot, requests, opaqueWitness) } @@ -896,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) { @@ -929,7 +951,6 @@ func (api *ConsensusAPI) newPayload(params engine.ExecutableData, versionedHashe func (api *ConsensusAPI) executeStatelessPayload(params engine.ExecutableData, versionedHashes []common.Hash, beaconRoot *common.Hash, requests [][]byte, opaqueWitness hexutil.Bytes) (engine.StatelessPayloadStatusV1, error) { log.Trace("Engine API request received", "method", "ExecuteStatelessPayload", "number", params.Number, "hash", params.BlockHash) - block, err := engine.ExecutableDataToBlockNoHash(params, versionedHashes, beaconRoot, requests) if err != nil { bgu := "nil" @@ -975,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() @@ -1010,7 +1031,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. @@ -1173,7 +1194,7 @@ func (api *ConsensusAPI) GetClientVersionV1(info engine.ClientVersionV1) []engin { Code: engine.ClientCode, Name: engine.ClientName, - Version: params.VersionWithMeta, + Version: version.WithMeta, Commit: hexutil.Encode(commit), }, } @@ -1254,3 +1275,15 @@ func getBody(block *types.Block) *engine.ExecutionPayloadBody { return &result } + +// convertRequests converts a hex requests slice to plain [][]byte. +func convertRequests(hex []hexutil.Bytes) [][]byte { + if hex == nil { + return nil + } + req := make([][]byte, len(hex)) + for i := range hex { + req[i] = hex[i] + } + return req +} diff --git a/eth/catalyst/api_test.go b/eth/catalyst/api_test.go index d4069e50e6..e0a155f12b 100644 --- a/eth/catalyst/api_test.go +++ b/eth/catalyst/api_test.go @@ -40,8 +40,8 @@ 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/internal/version" "github.com/ethereum/go-ethereum/miner" "github.com/ethereum/go-ethereum/node" "github.com/ethereum/go-ethereum/p2p" @@ -65,7 +65,6 @@ func generateMergeChain(n int, merged bool) (*core.Genesis, []*types.Block) { engine := consensus.Engine(beaconConsensus.New(ethash.NewFaker())) if merged { config.TerminalTotalDifficulty = common.Big0 - config.TerminalTotalDifficultyPassed = true engine = beaconConsensus.NewFaker() } genesis := &core.Genesis{ @@ -208,7 +207,6 @@ func TestEth2PrepareAndGetPayload(t *testing.T) { t.Fatalf("error preparing payload, err=%v", err) } // give the payload some time to be built - time.Sleep(100 * time.Millisecond) payloadID := (&miner.BuildPayloadArgs{ Parent: fcState.HeadBlockHash, Timestamp: blockParams.Timestamp, @@ -217,12 +215,12 @@ func TestEth2PrepareAndGetPayload(t *testing.T) { BeaconRoot: blockParams.BeaconRoot, Version: engine.PayloadV1, }).Id() - execData, err := api.GetPayloadV1(payloadID) + execData, err := api.getPayload(payloadID, true) if err != nil { t.Fatalf("error getting payload, err=%v", err) } - if len(execData.Transactions) != blocks[9].Transactions().Len() { - t.Fatalf("invalid number of transactions %d != 1", len(execData.Transactions)) + if len(execData.ExecutionPayload.Transactions) != blocks[9].Transactions().Len() { + t.Fatalf("invalid number of transactions %d != 1", len(execData.ExecutionPayload.Transactions)) } // Test invalid payloadID var invPayload engine.PayloadID @@ -453,8 +451,7 @@ func startEthService(t *testing.T, genesis *core.Genesis, blocks []*types.Block) } mcfg := miner.DefaultConfig - mcfg.PendingFeeRecipient = testAddr - ethcfg := ðconfig.Config{Genesis: genesis, SyncMode: downloader.FullSync, TrieTimeout: time.Minute, TrieDirtyCache: 256, TrieCleanCache: 256, Miner: mcfg} + ethcfg := ðconfig.Config{Genesis: genesis, SyncMode: ethconfig.FullSync, TrieTimeout: time.Minute, TrieDirtyCache: 256, TrieCleanCache: 256, Miner: mcfg} ethservice, err := eth.New(n, ethcfg) if err != nil { t.Fatal("can't create eth service:", err) @@ -628,7 +625,7 @@ func TestNewPayloadOnInvalidChain(t *testing.T) { SafeBlockHash: common.Hash{}, FinalizedBlockHash: common.Hash{}, } - payload *engine.ExecutableData + payload *engine.ExecutionPayloadEnvelope resp engine.ForkChoiceResponse err error ) @@ -640,11 +637,10 @@ func TestNewPayloadOnInvalidChain(t *testing.T) { t.Fatalf("error preparing payload, invalid status: %v", resp.PayloadStatus.Status) } // give the payload some time to be built - time.Sleep(50 * time.Millisecond) - if payload, err = api.GetPayloadV1(*resp.PayloadID); err != nil { + if payload, err = api.getPayload(*resp.PayloadID, true); err != nil { t.Fatalf("can't get payload: %v", err) } - if len(payload.Transactions) > 0 { + if len(payload.ExecutionPayload.Transactions) > 0 { break } // No luck this time we need to update the params and try again. @@ -653,7 +649,7 @@ func TestNewPayloadOnInvalidChain(t *testing.T) { t.Fatalf("payload should not be empty") } } - execResp, err := api.NewPayloadV1(*payload) + execResp, err := api.NewPayloadV1(*payload.ExecutionPayload) if err != nil { t.Fatalf("can't execute payload: %v", err) } @@ -661,14 +657,14 @@ func TestNewPayloadOnInvalidChain(t *testing.T) { t.Fatalf("invalid status: %v", execResp.Status) } fcState = engine.ForkchoiceStateV1{ - HeadBlockHash: payload.BlockHash, - SafeBlockHash: payload.ParentHash, - FinalizedBlockHash: payload.ParentHash, + HeadBlockHash: payload.ExecutionPayload.BlockHash, + SafeBlockHash: payload.ExecutionPayload.ParentHash, + FinalizedBlockHash: payload.ExecutionPayload.ParentHash, } if _, err := api.ForkchoiceUpdatedV1(fcState, nil); err != nil { t.Fatalf("Failed to insert block: %v", err) } - if ethservice.BlockChain().CurrentBlock().Number.Uint64() != payload.Number { + if ethservice.BlockChain().CurrentBlock().Number.Uint64() != payload.ExecutionPayload.Number { t.Fatalf("Chain head should be updated") } parent = ethservice.BlockChain().CurrentBlock() @@ -1736,9 +1732,6 @@ func TestWitnessCreationAndConsumption(t *testing.T) { if err != nil { t.Fatalf("error preparing payload, err=%v", err) } - // Give the payload some time to be built - time.Sleep(100 * time.Millisecond) - payloadID := (&miner.BuildPayloadArgs{ Parent: fcState.HeadBlockHash, Timestamp: blockParams.Timestamp, @@ -1748,7 +1741,7 @@ func TestWitnessCreationAndConsumption(t *testing.T) { BeaconRoot: blockParams.BeaconRoot, Version: engine.PayloadV3, }).Id() - envelope, err := api.GetPayloadV3(payloadID) + envelope, err := api.getPayload(payloadID, true) if err != nil { t.Fatalf("error getting payload, err=%v", err) } @@ -1823,7 +1816,7 @@ func TestGetClientVersion(t *testing.T) { t.Fatalf("expected only one returned client version, got %d", len(infos)) } info = infos[0] - if info.Code != engine.ClientCode || info.Name != engine.ClientName || info.Version != params.VersionWithMeta { + if info.Code != engine.ClientCode || info.Name != engine.ClientName || info.Version != version.WithMeta { t.Fatalf("client info does match expected, got %s", info.String()) } } diff --git a/eth/catalyst/simulated_beacon.go b/eth/catalyst/simulated_beacon.go index 9f14f3f949..a24ff52101 100644 --- a/eth/catalyst/simulated_beacon.go +++ b/eth/catalyst/simulated_beacon.go @@ -21,7 +21,6 @@ import ( "crypto/sha256" "errors" "fmt" - "math/big" "sync" "time" @@ -34,7 +33,6 @@ import ( "github.com/ethereum/go-ethereum/event" "github.com/ethereum/go-ethereum/log" "github.com/ethereum/go-ethereum/node" - "github.com/ethereum/go-ethereum/params" "github.com/ethereum/go-ethereum/rpc" ) @@ -220,7 +218,8 @@ func (c *SimulatedBeacon) sealBlock(withdrawals []*types.Withdrawal, timestamp u } } // Mark the payload as canon - if _, err = c.engineAPI.NewPayloadV4(*payload, blobHashes, &common.Hash{}, envelope.Requests); err != nil { + _, err = c.engineAPI.newPayload(*payload, blobHashes, &common.Hash{}, envelope.Requests, false) + if err != nil { return err } c.setCurrentState(payload.BlockHash, finalizedHash) @@ -286,12 +285,7 @@ func (c *SimulatedBeacon) Commit() common.Hash { // Rollback un-sends previously added transactions. func (c *SimulatedBeacon) Rollback() { - // Flush all transactions from the transaction pools - maxUint256 := new(big.Int).Sub(new(big.Int).Lsh(common.Big1, 256), common.Big1) - c.eth.TxPool().SetGasTip(maxUint256) - // Set the gas tip back to accept new transactions - // TODO (Marius van der Wijden): set gas tip to parameter passed by config - c.eth.TxPool().SetGasTip(big.NewInt(params.GWei)) + c.eth.TxPool().Clear() } // Fork sets the head to the provided hash. diff --git a/eth/catalyst/simulated_beacon_test.go b/eth/catalyst/simulated_beacon_test.go index 7db1769234..79d9ba738e 100644 --- a/eth/catalyst/simulated_beacon_test.go +++ b/eth/catalyst/simulated_beacon_test.go @@ -27,7 +27,6 @@ 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/node" @@ -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: miner.DefaultConfig} ethservice, err := eth.New(n, ethcfg) if err != nil { t.Fatal("can't create eth service:", err) @@ -123,16 +122,16 @@ func TestSimulatedBeaconSendWithdrawals(t *testing.T) { timer := time.NewTimer(12 * time.Second) for { select { - case evt := <-chainHeadCh: - for _, includedTx := range evt.Block.Transactions() { + case ev := <-chainHeadCh: + block := ethService.BlockChain().GetBlock(ev.Header.Hash(), ev.Header.Number.Uint64()) + for _, includedTx := range block.Transactions() { includedTxs[includedTx.Hash()] = struct{}{} } - for _, includedWithdrawal := range evt.Block.Withdrawals() { + for _, includedWithdrawal := range block.Withdrawals() { includedWithdrawals = append(includedWithdrawals, includedWithdrawal.Index) } - // ensure all withdrawals/txs included. this will take two blocks b/c number of withdrawals > 10 - if len(includedTxs) == len(txs) && len(includedWithdrawals) == len(withdrawals) && evt.Block.Number().Cmp(big.NewInt(2)) == 0 { + if len(includedTxs) == len(txs) && len(includedWithdrawals) == len(withdrawals) && ev.Header.Number.Cmp(big.NewInt(2)) == 0 { return } case <-timer.C: @@ -186,11 +185,12 @@ func TestOnDemandSpam(t *testing.T) { ) for { select { - case evt := <-chainHeadCh: - for _, itx := range evt.Block.Transactions() { + case ev := <-chainHeadCh: + block := eth.BlockChain().GetBlock(ev.Header.Hash(), ev.Header.Number.Uint64()) + for _, itx := range block.Transactions() { includedTxs[itx.Hash()] = struct{}{} } - for _, iwx := range evt.Block.Withdrawals() { + for _, iwx := range block.Withdrawals() { includedWxs = append(includedWxs, iwx.Index) } // ensure all withdrawals/txs included. this will take two blocks b/c number of withdrawals > 10 diff --git a/eth/catalyst/tester.go b/eth/catalyst/tester.go index 0922ac0ba6..db2d638701 100644 --- a/eth/catalyst/tester.go +++ b/eth/catalyst/tester.go @@ -22,7 +22,7 @@ import ( "github.com/ethereum/go-ethereum/common" "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/log" "github.com/ethereum/go-ethereum/node" ) @@ -62,7 +62,7 @@ func (tester *FullSyncTester) Start() error { // Trigger beacon sync with the provided block hash as trusted // chain head. - err := tester.backend.Downloader().BeaconDevSync(downloader.FullSync, tester.target, tester.closed) + err := tester.backend.Downloader().BeaconDevSync(ethconfig.FullSync, tester.target, tester.closed) if err != nil { log.Info("Failed to trigger beacon sync", "err", err) } diff --git a/eth/downloader/beaconsync.go b/eth/downloader/beaconsync.go index e682536e07..c142ea7435 100644 --- a/eth/downloader/beaconsync.go +++ b/eth/downloader/beaconsync.go @@ -24,6 +24,7 @@ import ( "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/core/rawdb" "github.com/ethereum/go-ethereum/core/types" + "github.com/ethereum/go-ethereum/eth/ethconfig" "github.com/ethereum/go-ethereum/log" ) @@ -198,9 +199,9 @@ func (d *Downloader) findBeaconAncestor() (uint64, error) { var chainHead *types.Header switch d.getMode() { - case FullSync: + case ethconfig.FullSync: chainHead = d.blockchain.CurrentBlock() - case SnapSync: + case ethconfig.SnapSync: chainHead = d.blockchain.CurrentSnapBlock() default: panic("unknown sync mode") @@ -218,9 +219,9 @@ func (d *Downloader) findBeaconAncestor() (uint64, error) { } var linked bool switch d.getMode() { - case FullSync: + case ethconfig.FullSync: linked = d.blockchain.HasBlock(beaconTail.ParentHash, beaconTail.Number.Uint64()-1) - case SnapSync: + case ethconfig.SnapSync: linked = d.blockchain.HasFastBlock(beaconTail.ParentHash, beaconTail.Number.Uint64()-1) default: panic("unknown sync mode") @@ -253,9 +254,9 @@ func (d *Downloader) findBeaconAncestor() (uint64, error) { var known bool switch d.getMode() { - case FullSync: + case ethconfig.FullSync: known = d.blockchain.HasBlock(h.Hash(), n) - case SnapSync: + case ethconfig.SnapSync: known = d.blockchain.HasFastBlock(h.Hash(), n) default: panic("unknown sync mode") diff --git a/eth/downloader/downloader.go b/eth/downloader/downloader.go index fadb68ef03..8ac5d2eb31 100644 --- a/eth/downloader/downloader.go +++ b/eth/downloader/downloader.go @@ -30,6 +30,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" @@ -69,6 +70,17 @@ var ( errNoPivotHeader = errors.New("pivot header is not 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) @@ -230,9 +242,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) @@ -326,7 +338,7 @@ func (d *Downloader) synchronise(mode SyncMode, beaconPing chan struct{}) error 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 @@ -434,7 +446,7 @@ func (d *Downloader) syncToHead() (err error) { // 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() } height := latest.Number.Uint64() @@ -452,7 +464,7 @@ func (d *Downloader) syncToHead() (err error) { d.syncStatsLock.Unlock() // Ensure our origin point is below any snap sync pivot point - if mode == SnapSync { + if mode == ethconfig.SnapSync { if height <= uint64(fsMinFullBlocks) { origin = 0 } else { @@ -466,10 +478,10 @@ func (d *Downloader) syncToHead() (err error) { } } 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 @@ -523,13 +535,13 @@ func (d *Downloader) syncToHead() (err error) { func() error { return d.fetchReceipts(origin + 1) }, // Receipts are retrieved during snap sync func() error { return d.processHeaders(origin + 1) }, } - 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() }) } return d.spawnSync(fetchers) @@ -676,7 +688,7 @@ func (d *Downloader) processHeaders(origin uint64) error { chunkHashes := hashes[:limit] // In case of header only syncing, validate the chunk immediately - if mode == SnapSync { + if mode == ethconfig.SnapSync { // Although the received headers might be all valid, a legacy // PoW/PoA sync must not accept post-merge headers. Make sure // that any transition is rejected at this point. diff --git a/eth/downloader/queue.go b/eth/downloader/queue.go index a2f916ebbc..a1c114f057 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" @@ -180,7 +181,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) @@ -328,7 +329,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 { @@ -523,7 +524,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) + stale, throttle, item, err := q.resultCache.AddFetch(header, q.mode == ethconfig.SnapSync) if stale { // Don't put back in the task queue, this item has already been // delivered upstream @@ -881,7 +882,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 c781a63940..6b75ab816f 100644 --- a/eth/ethconfig/config.go +++ b/eth/ethconfig/config.go @@ -18,7 +18,7 @@ package ethconfig import ( - "errors" + "fmt" "time" "github.com/ethereum/go-ethereum/common" @@ -29,9 +29,9 @@ 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/log" "github.com/ethereum/go-ethereum/miner" "github.com/ethereum/go-ethereum/params" ) @@ -48,7 +48,7 @@ var FullNodeGPO = gasprice.Config{ // Defaults contains default settings for use on the Ethereum main net. var Defaults = Config{ - SyncMode: downloader.SnapSync, + SyncMode: SnapSync, NetworkId: 0, // enable auto configuration of networkID == chainID TxLookupLimit: 2350000, TransactionHistory: 2350000, @@ -79,7 +79,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 // This can be set to list of enrtree:// URLs which will be queried for // nodes to connect to. @@ -138,9 +138,6 @@ type Config struct { VMTrace string VMTraceJsonConfig string - // Miscellaneous options - DocRoot string `toml:"-"` - // RPCGasCap is the global gas cap for eth-call variants. RPCGasCap uint64 @@ -162,10 +159,9 @@ type Config struct { // Clique is allowed for now to live standalone, but ethash is forbidden and can // only exist on already merged networks. func CreateConsensusEngine(config *params.ChainConfig, db ethdb.Database) (consensus.Engine, error) { - // Geth v1.14.0 dropped support for non-merged networks in any consensus - // mode. If such a network is requested, reject startup. - if !config.TerminalTotalDifficultyPassed { - return nil, errors.New("only PoS networks are supported, please transition old ones with Geth v1.13.x") + if config.TerminalTotalDifficulty == nil { + 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") } // Wrap previously supported consensus engines into their post-merge counterpart if config.Clique != nil { diff --git a/eth/ethconfig/gen_config.go b/eth/ethconfig/gen_config.go index d96ba0ccb7..8e954eaefb 100644 --- a/eth/ethconfig/gen_config.go +++ b/eth/ethconfig/gen_config.go @@ -9,7 +9,6 @@ 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" ) @@ -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 EthDiscoveryURLs []string SnapDiscoveryURLs []string NoPruning bool @@ -46,7 +45,6 @@ func (c Config) MarshalTOML() (interface{}, error) { EnablePreimageRecording bool VMTrace string VMTraceJsonConfig string - DocRoot string `toml:"-"` RPCGasCap uint64 RPCEVMTimeout time.Duration RPCTxFeeCap float64 @@ -83,7 +81,6 @@ func (c Config) MarshalTOML() (interface{}, error) { enc.EnablePreimageRecording = c.EnablePreimageRecording enc.VMTrace = c.VMTrace enc.VMTraceJsonConfig = c.VMTraceJsonConfig - enc.DocRoot = c.DocRoot enc.RPCGasCap = c.RPCGasCap enc.RPCEVMTimeout = c.RPCEVMTimeout enc.RPCTxFeeCap = c.RPCTxFeeCap @@ -97,7 +94,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 EthDiscoveryURLs []string SnapDiscoveryURLs []string NoPruning *bool @@ -124,7 +121,6 @@ func (c *Config) UnmarshalTOML(unmarshal func(interface{}) error) error { EnablePreimageRecording *bool VMTrace *string VMTraceJsonConfig *string - DocRoot *string `toml:"-"` RPCGasCap *uint64 RPCEVMTimeout *time.Duration RPCTxFeeCap *float64 @@ -222,9 +218,6 @@ func (c *Config) UnmarshalTOML(unmarshal func(interface{}) error) error { if dec.VMTraceJsonConfig != nil { c.VMTraceJsonConfig = *dec.VMTraceJsonConfig } - if dec.DocRoot != nil { - c.DocRoot = *dec.DocRoot - } if dec.RPCGasCap != nil { c.RPCGasCap = *dec.RPCGasCap } 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/filters/bench_test.go b/eth/filters/bench_test.go deleted file mode 100644 index 73b96b77af..0000000000 --- a/eth/filters/bench_test.go +++ /dev/null @@ -1,189 +0,0 @@ -// Copyright 2017 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 filters - -import ( - "context" - "fmt" - "testing" - "time" - - "github.com/ethereum/go-ethereum/common" - "github.com/ethereum/go-ethereum/common/bitutil" - "github.com/ethereum/go-ethereum/core/bloombits" - "github.com/ethereum/go-ethereum/core/rawdb" - "github.com/ethereum/go-ethereum/core/types" - "github.com/ethereum/go-ethereum/ethdb" - "github.com/ethereum/go-ethereum/node" -) - -func BenchmarkBloomBits512(b *testing.B) { - benchmarkBloomBits(b, 512) -} - -func BenchmarkBloomBits1k(b *testing.B) { - benchmarkBloomBits(b, 1024) -} - -func BenchmarkBloomBits2k(b *testing.B) { - benchmarkBloomBits(b, 2048) -} - -func BenchmarkBloomBits4k(b *testing.B) { - benchmarkBloomBits(b, 4096) -} - -func BenchmarkBloomBits8k(b *testing.B) { - benchmarkBloomBits(b, 8192) -} - -func BenchmarkBloomBits16k(b *testing.B) { - benchmarkBloomBits(b, 16384) -} - -func BenchmarkBloomBits32k(b *testing.B) { - benchmarkBloomBits(b, 32768) -} - -const benchFilterCnt = 2000 - -func benchmarkBloomBits(b *testing.B, sectionSize uint64) { - b.Skip("test disabled: this tests presume (and modify) an existing datadir.") - benchDataDir := node.DefaultDataDir() + "/geth/chaindata" - b.Log("Running bloombits benchmark section size:", sectionSize) - - db, err := rawdb.NewLevelDBDatabase(benchDataDir, 128, 1024, "", false) - if err != nil { - b.Fatalf("error opening database at %v: %v", benchDataDir, err) - } - head := rawdb.ReadHeadBlockHash(db) - if head == (common.Hash{}) { - b.Fatalf("chain data not found at %v", benchDataDir) - } - - clearBloomBits(db) - b.Log("Generating bloombits data...") - headNum := rawdb.ReadHeaderNumber(db, head) - if headNum == nil || *headNum < sectionSize+512 { - b.Fatalf("not enough blocks for running a benchmark") - } - - start := time.Now() - cnt := (*headNum - 512) / sectionSize - var dataSize, compSize uint64 - for sectionIdx := uint64(0); sectionIdx < cnt; sectionIdx++ { - bc, err := bloombits.NewGenerator(uint(sectionSize)) - if err != nil { - b.Fatalf("failed to create generator: %v", err) - } - var header *types.Header - for i := sectionIdx * sectionSize; i < (sectionIdx+1)*sectionSize; i++ { - hash := rawdb.ReadCanonicalHash(db, i) - if header = rawdb.ReadHeader(db, hash, i); header == nil { - b.Fatalf("Error creating bloomBits data") - return - } - bc.AddBloom(uint(i-sectionIdx*sectionSize), header.Bloom) - } - sectionHead := rawdb.ReadCanonicalHash(db, (sectionIdx+1)*sectionSize-1) - for i := 0; i < types.BloomBitLength; i++ { - data, err := bc.Bitset(uint(i)) - if err != nil { - b.Fatalf("failed to retrieve bitset: %v", err) - } - comp := bitutil.CompressBytes(data) - dataSize += uint64(len(data)) - compSize += uint64(len(comp)) - rawdb.WriteBloomBits(db, uint(i), sectionIdx, sectionHead, comp) - } - //if sectionIdx%50 == 0 { - // b.Log(" section", sectionIdx, "/", cnt) - //} - } - - d := time.Since(start) - b.Log("Finished generating bloombits data") - b.Log(" ", d, "total ", d/time.Duration(cnt*sectionSize), "per block") - b.Log(" data size:", dataSize, " compressed size:", compSize, " compression ratio:", float64(compSize)/float64(dataSize)) - - b.Log("Running filter benchmarks...") - start = time.Now() - - var ( - backend *testBackend - sys *FilterSystem - ) - for i := 0; i < benchFilterCnt; i++ { - if i%20 == 0 { - db.Close() - db, _ = rawdb.NewLevelDBDatabase(benchDataDir, 128, 1024, "", false) - backend = &testBackend{db: db, sections: cnt} - sys = NewFilterSystem(backend, Config{}) - } - var addr common.Address - addr[0] = byte(i) - addr[1] = byte(i / 256) - filter := sys.NewRangeFilter(0, int64(cnt*sectionSize-1), []common.Address{addr}, nil) - if _, err := filter.Logs(context.Background()); err != nil { - b.Error("filter.Logs error:", err) - } - } - - d = time.Since(start) - b.Log("Finished running filter benchmarks") - b.Log(" ", d, "total ", d/time.Duration(benchFilterCnt), "per address", d*time.Duration(1000000)/time.Duration(benchFilterCnt*cnt*sectionSize), "per million blocks") - db.Close() -} - -//nolint:unused -func clearBloomBits(db ethdb.Database) { - var bloomBitsPrefix = []byte("bloomBits-") - fmt.Println("Clearing bloombits data...") - it := db.NewIterator(bloomBitsPrefix, nil) - for it.Next() { - db.Delete(it.Key()) - } - it.Release() -} - -func BenchmarkNoBloomBits(b *testing.B) { - b.Skip("test disabled: this tests presume (and modify) an existing datadir.") - benchDataDir := node.DefaultDataDir() + "/geth/chaindata" - b.Log("Running benchmark without bloombits") - db, err := rawdb.NewLevelDBDatabase(benchDataDir, 128, 1024, "", false) - if err != nil { - b.Fatalf("error opening database at %v: %v", benchDataDir, err) - } - head := rawdb.ReadHeadBlockHash(db) - if head == (common.Hash{}) { - b.Fatalf("chain data not found at %v", benchDataDir) - } - headNum := rawdb.ReadHeaderNumber(db, head) - - clearBloomBits(db) - - _, sys := newTestFilterSystem(b, db, Config{}) - - b.Log("Running filter benchmarks...") - start := time.Now() - filter := sys.NewRangeFilter(0, int64(*headNum), []common.Address{{}}, nil) - filter.Logs(context.Background()) - d := time.Since(start) - b.Log("Finished running filter benchmarks") - b.Log(" ", d, "total ", d*time.Duration(1000000)/time.Duration(*headNum+1), "per million blocks") - db.Close() -} diff --git a/eth/filters/filter_system.go b/eth/filters/filter_system.go index a3a2787a41..86012b3f9a 100644 --- a/eth/filters/filter_system.go +++ b/eth/filters/filter_system.go @@ -391,7 +391,7 @@ func (es *EventSystem) handleTxsEvent(filters filterIndex, ev core.NewTxsEvent) func (es *EventSystem) handleChainEvent(filters filterIndex, ev core.ChainEvent) { for _, f := range filters[BlocksSubscription] { - f.headers <- ev.Block.Header() + f.headers <- ev.Header } } diff --git a/eth/filters/filter_system_test.go b/eth/filters/filter_system_test.go index 1d52afb282..aec5ee4166 100644 --- a/eth/filters/filter_system_test.go +++ b/eth/filters/filter_system_test.go @@ -200,7 +200,7 @@ func TestBlockSubscription(t *testing.T) { ) for _, blk := range chain { - chainEvents = append(chainEvents, core.ChainEvent{Hash: blk.Hash(), Block: blk}) + chainEvents = append(chainEvents, core.ChainEvent{Header: blk.Header()}) } chan0 := make(chan *types.Header) @@ -213,13 +213,13 @@ func TestBlockSubscription(t *testing.T) { for i1 != len(chainEvents) || i2 != len(chainEvents) { select { case header := <-chan0: - if chainEvents[i1].Hash != header.Hash() { - t.Errorf("sub0 received invalid hash on index %d, want %x, got %x", i1, chainEvents[i1].Hash, header.Hash()) + if chainEvents[i1].Header.Hash() != header.Hash() { + t.Errorf("sub0 received invalid hash on index %d, want %x, got %x", i1, chainEvents[i1].Header.Hash(), header.Hash()) } i1++ case header := <-chan1: - if chainEvents[i2].Hash != header.Hash() { - t.Errorf("sub1 received invalid hash on index %d, want %x, got %x", i2, chainEvents[i2].Hash, header.Hash()) + if chainEvents[i2].Header.Hash() != header.Hash() { + t.Errorf("sub1 received invalid hash on index %d, want %x, got %x", i2, chainEvents[i2].Header.Hash(), header.Hash()) } i2++ } diff --git a/eth/filters/filter_test.go b/eth/filters/filter_test.go index d8b703fee4..6a3057326d 100644 --- a/eth/filters/filter_test.go +++ b/eth/filters/filter_test.go @@ -48,7 +48,7 @@ func makeReceipt(addr common.Address) *types.Receipt { func BenchmarkFilters(b *testing.B) { var ( - db, _ = rawdb.NewLevelDBDatabase(b.TempDir(), 0, 0, "", false) + db = rawdb.NewMemoryDatabase() _, sys = newTestFilterSystem(b, db, Config{}) key1, _ = crypto.HexToECDSA("b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291") addr1 = crypto.PubkeyToAddress(key1.PublicKey) diff --git a/eth/gasestimator/gasestimator.go b/eth/gasestimator/gasestimator.go index d43057dda2..a6c4718cf4 100644 --- a/eth/gasestimator/gasestimator.go +++ b/eth/gasestimator/gasestimator.go @@ -28,6 +28,7 @@ import ( "github.com/ethereum/go-ethereum/core/state" "github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/core/vm" + "github.com/ethereum/go-ethereum/internal/ethapi/override" "github.com/ethereum/go-ethereum/log" "github.com/ethereum/go-ethereum/params" ) @@ -38,10 +39,11 @@ import ( // these together, it would be excessively hard to test. Splitting the parts out // allows testing without needing a proper live chain. type Options struct { - Config *params.ChainConfig // Chain configuration for hard fork selection - Chain core.ChainContext // Chain context to access past block hashes - Header *types.Header // Header defining the block context to execute in - State *state.StateDB // Pre-state on top of which to estimate the gas + Config *params.ChainConfig // Chain configuration for hard fork selection + Chain core.ChainContext // Chain context to access past block hashes + Header *types.Header // Header defining the block context to execute in + State *state.StateDB // Pre-state on top of which to estimate the gas + BlockOverrides *override.BlockOverrides // Block overrides to apply during the estimation ErrorRatio float64 // Allowed overestimation ratio for faster estimation termination } @@ -217,20 +219,22 @@ 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() ) + if opts.BlockOverrides != nil { + opts.BlockOverrides.Apply(&evmContext) + } // 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/gasprice/gasprice.go b/eth/gasprice/gasprice.go index 19a6c0010a..fe2e4d408a 100644 --- a/eth/gasprice/gasprice.go +++ b/eth/gasprice/gasprice.go @@ -124,10 +124,10 @@ func NewOracle(backend OracleBackend, params Config, startPrice *big.Int) *Oracl go func() { var lastHead common.Hash for ev := range headEvent { - if ev.Block.ParentHash() != lastHead { + if ev.Header.ParentHash != lastHead { cache.Purge() } - lastHead = ev.Block.Hash() + lastHead = ev.Header.Hash() } }() diff --git a/eth/handler.go b/eth/handler.go index d5117584c0..9820118173 100644 --- a/eth/handler.go +++ b/eth/handler.go @@ -27,11 +27,11 @@ import ( "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/core" "github.com/ethereum/go-ethereum/core/forkid" - "github.com/ethereum/go-ethereum/core/rawdb" "github.com/ethereum/go-ethereum/core/txpool" "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/eth" "github.com/ethereum/go-ethereum/eth/protocols/snap" @@ -41,7 +41,6 @@ import ( "github.com/ethereum/go-ethereum/metrics" "github.com/ethereum/go-ethereum/p2p" "github.com/ethereum/go-ethereum/p2p/enode" - "github.com/ethereum/go-ethereum/triedb/pathdb" ) const ( @@ -89,7 +88,7 @@ type handlerConfig struct { Chain *core.BlockChain // Blockchain to serve data from TxPool txPool // Transaction pool to propagate from Network uint64 // Network identifier to advertise - 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 @@ -147,7 +146,7 @@ func newHandler(config *handlerConfig) (*handler, error) { handlerDoneCh: make(chan struct{}), handlerStartCh: 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 @@ -367,7 +366,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 { @@ -558,7 +557,4 @@ func (h *handler) enableSyncedFeatures() { log.Info("Snap sync complete, auto disabling") h.snapSync.Store(false) } - if h.chain.TrieDB().Scheme() == rawdb.PathScheme { - h.chain.TrieDB().SetBufferSize(pathdb.DefaultBufferSize) - } } diff --git a/eth/handler_eth_test.go b/eth/handler_eth_test.go index 55f7da87dd..ce17345358 100644 --- a/eth/handler_eth_test.go +++ b/eth/handler_eth_test.go @@ -29,7 +29,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/eth" "github.com/ethereum/go-ethereum/event" "github.com/ethereum/go-ethereum/p2p" @@ -109,7 +109,7 @@ func testForkIDSplit(t *testing.T, protocol uint) { Chain: chainNoFork, TxPool: newTestTxPool(), Network: 1, - Sync: downloader.FullSync, + Sync: ethconfig.FullSync, BloomCache: 1, }) ethProFork, _ = newHandler(&handlerConfig{ @@ -117,7 +117,7 @@ func testForkIDSplit(t *testing.T, protocol uint) { Chain: chainProFork, TxPool: newTestTxPool(), Network: 1, - Sync: downloader.FullSync, + Sync: ethconfig.FullSync, BloomCache: 1, }) ) diff --git a/eth/handler_test.go b/eth/handler_test.go index 7b250df2e9..b63d3e8592 100644 --- a/eth/handler_test.go +++ b/eth/handler_test.go @@ -29,7 +29,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" @@ -164,7 +164,7 @@ func newTestHandlerWithBlocks(blocks int) *testHandler { Chain: chain, TxPool: txpool, Network: 1, - Sync: downloader.SnapSync, + Sync: ethconfig.SnapSync, BloomCache: 1, }) handler.Start(1000) 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/handler_test.go b/eth/protocols/eth/handler_test.go index fc82b42947..d0509f2f3d 100644 --- a/eth/protocols/eth/handler_test.go +++ b/eth/protocols/eth/handler_test.go @@ -17,10 +17,12 @@ package eth import ( + "bytes" "math" "math/big" "math/rand" "testing" + "time" "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/consensus" @@ -37,6 +39,7 @@ import ( "github.com/ethereum/go-ethereum/p2p" "github.com/ethereum/go-ethereum/p2p/enode" "github.com/ethereum/go-ethereum/params" + "github.com/ethereum/go-ethereum/rlp" ) var ( @@ -75,27 +78,26 @@ func newTestBackendWithGenerator(blocks int, shanghai bool, generator func(int, if shanghai { config = ¶ms.ChainConfig{ - ChainID: big.NewInt(1), - HomesteadBlock: big.NewInt(0), - DAOForkBlock: nil, - DAOForkSupport: true, - EIP150Block: big.NewInt(0), - EIP155Block: big.NewInt(0), - EIP158Block: big.NewInt(0), - ByzantiumBlock: big.NewInt(0), - ConstantinopleBlock: big.NewInt(0), - PetersburgBlock: big.NewInt(0), - IstanbulBlock: big.NewInt(0), - MuirGlacierBlock: big.NewInt(0), - BerlinBlock: big.NewInt(0), - LondonBlock: big.NewInt(0), - ArrowGlacierBlock: big.NewInt(0), - GrayGlacierBlock: big.NewInt(0), - MergeNetsplitBlock: big.NewInt(0), - ShanghaiTime: u64(0), - TerminalTotalDifficulty: big.NewInt(0), - TerminalTotalDifficultyPassed: true, - Ethash: new(params.EthashConfig), + ChainID: big.NewInt(1), + HomesteadBlock: big.NewInt(0), + DAOForkBlock: nil, + DAOForkSupport: true, + EIP150Block: big.NewInt(0), + EIP155Block: big.NewInt(0), + EIP158Block: big.NewInt(0), + ByzantiumBlock: big.NewInt(0), + ConstantinopleBlock: big.NewInt(0), + PetersburgBlock: big.NewInt(0), + IstanbulBlock: big.NewInt(0), + MuirGlacierBlock: big.NewInt(0), + BerlinBlock: big.NewInt(0), + LondonBlock: big.NewInt(0), + ArrowGlacierBlock: big.NewInt(0), + GrayGlacierBlock: big.NewInt(0), + MergeNetsplitBlock: big.NewInt(0), + ShanghaiTime: u64(0), + TerminalTotalDifficulty: big.NewInt(0), + Ethash: new(params.EthashConfig), } engine = beacon.NewFaker() } @@ -143,10 +145,12 @@ func (b *testBackend) RunPeer(peer *Peer, handler Handler) error { func (b *testBackend) PeerInfo(enode.ID) interface{} { panic("not implemented") } func (b *testBackend) AcceptTxs() bool { - panic("data processing tests should be done in the handler package") + return true + //panic("data processing tests should be done in the handler package") } func (b *testBackend) Handle(*Peer, Packet) error { - panic("data processing tests should be done in the handler package") + return nil + //panic("data processing tests should be done in the handler package") } // Tests that block headers can be retrieved from a remote chain based on user queries. @@ -499,3 +503,76 @@ func testGetBlockReceipts(t *testing.T, protocol uint) { t.Errorf("receipts mismatch: %v", err) } } + +type decoder struct { + msg []byte +} + +func (d decoder) Decode(val interface{}) error { + buffer := bytes.NewBuffer(d.msg) + s := rlp.NewStream(buffer, uint64(len(d.msg))) + return s.Decode(val) +} + +func (d decoder) Time() time.Time { + return time.Now() +} + +func setup() (*testBackend, *testPeer) { + // Generate some transactions etc. + acc1Key, _ := crypto.HexToECDSA("8a1f9a8f95be41cd7ccb6168179afb4504aefe388d1e14474d32c45c72ce7b7a") + acc2Key, _ := crypto.HexToECDSA("49a7b37aa6f6645917e7b807e9d1c00d4fa71f18343b0d4122a4d2df64dd6fee") + acc1Addr := crypto.PubkeyToAddress(acc1Key.PublicKey) + acc2Addr := crypto.PubkeyToAddress(acc2Key.PublicKey) + signer := types.HomesteadSigner{} + gen := func(n int, block *core.BlockGen) { + if n%2 == 0 { + w := &types.Withdrawal{ + Address: common.Address{0xaa}, + Amount: 42, + } + block.AddWithdrawal(w) + } + switch n { + case 0: + // In block 1, the test bank sends account #1 some ether. + tx, _ := types.SignTx(types.NewTransaction(block.TxNonce(testAddr), acc1Addr, big.NewInt(10_000_000_000_000_000), params.TxGas, block.BaseFee(), nil), signer, testKey) + block.AddTx(tx) + case 1: + // In block 2, the test bank sends some more ether to account #1. + // acc1Addr passes it on to account #2. + tx1, _ := types.SignTx(types.NewTransaction(block.TxNonce(testAddr), acc1Addr, big.NewInt(1_000_000_000_000_000), params.TxGas, block.BaseFee(), nil), signer, testKey) + tx2, _ := types.SignTx(types.NewTransaction(block.TxNonce(acc1Addr), acc2Addr, big.NewInt(1_000_000_000_000_000), params.TxGas, block.BaseFee(), nil), signer, acc1Key) + block.AddTx(tx1) + block.AddTx(tx2) + case 2: + // Block 3 is empty but was mined by account #2. + block.SetCoinbase(acc2Addr) + block.SetExtra([]byte("yeehaw")) + } + } + backend := newTestBackendWithGenerator(maxBodiesServe+15, true, gen) + peer, _ := newTestPeer("peer", ETH68, backend) + // Discard all messages + go func() { + for { + msg, err := peer.app.ReadMsg() + if err == nil { + msg.Discard() + } + } + }() + return backend, peer +} + +func FuzzEthProtocolHandlers(f *testing.F) { + handlers := eth68 + backend, peer := setup() + f.Fuzz(func(t *testing.T, code byte, msg []byte) { + handler := handlers[uint64(code)%protocolLengths[ETH68]] + if handler == nil { + return + } + handler(backend, decoder{msg: msg}, peer.Peer) + }) +} diff --git a/eth/protocols/eth/handshake.go b/eth/protocols/eth/handshake.go index ea16a85b1e..68cf846925 100644 --- a/eth/protocols/eth/handshake.go +++ b/eth/protocols/eth/handshake.go @@ -112,7 +112,7 @@ func (p *Peer) readStatus(network uint64, status *StatusPacket, genesis common.H // 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/snap/sync_test.go b/eth/protocols/snap/sync_test.go index c97c3b99b3..d318077d99 100644 --- a/eth/protocols/snap/sync_test.go +++ b/eth/protocols/snap/sync_test.go @@ -1515,7 +1515,7 @@ func makeAccountTrieNoStorage(n int, scheme string) (string, *trie.Trie, []*kv) // Commit the state changes into db and re-create the trie // for accessing later. root, nodes := accTrie.Commit(false) - db.Update(root, types.EmptyRootHash, 0, trienode.NewWithNodeSet(nodes), nil) + db.Update(root, types.EmptyRootHash, 0, trienode.NewWithNodeSet(nodes), triedb.NewStateSet()) accTrie, _ = trie.New(trie.StateTrieID(root), db) return db.Scheme(), accTrie, entries @@ -1577,7 +1577,7 @@ func makeBoundaryAccountTrie(scheme string, n int) (string, *trie.Trie, []*kv) { // Commit the state changes into db and re-create the trie // for accessing later. root, nodes := accTrie.Commit(false) - db.Update(root, types.EmptyRootHash, 0, trienode.NewWithNodeSet(nodes), nil) + db.Update(root, types.EmptyRootHash, 0, trienode.NewWithNodeSet(nodes), triedb.NewStateSet()) accTrie, _ = trie.New(trie.StateTrieID(root), db) return db.Scheme(), accTrie, entries @@ -1626,7 +1626,7 @@ func makeAccountTrieWithStorageWithUniqueStorage(scheme string, accounts, slots nodes.Merge(set) // Commit gathered dirty nodes into database - db.Update(root, types.EmptyRootHash, 0, nodes, nil) + db.Update(root, types.EmptyRootHash, 0, nodes, triedb.NewStateSet()) // Re-create tries with new root accTrie, _ = trie.New(trie.StateTrieID(root), db) @@ -1693,7 +1693,7 @@ func makeAccountTrieWithStorage(scheme string, accounts, slots int, code, bounda nodes.Merge(set) // Commit gathered dirty nodes into database - db.Update(root, types.EmptyRootHash, 0, nodes, nil) + db.Update(root, types.EmptyRootHash, 0, nodes, triedb.NewStateSet()) // Re-create tries with new root accTrie, err := trie.New(trie.StateTrieID(root), db) diff --git a/eth/state_accessor.go b/eth/state_accessor.go index cb5a233a83..0749d73791 100644 --- a/eth/state_accessor.go +++ b/eth/state_accessor.go @@ -235,16 +235,14 @@ func (eth *Ethereum) stateAtTransaction(ctx context.Context, block *types.Block, return nil, vm.BlockContext{}, nil, nil, err } // 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 @@ -252,22 +250,20 @@ func (eth *Ethereum) stateAtTransaction(ctx context.Context, block *types.Block, // Recompute transactions up to the target index. signer := types.MakeSigner(eth.blockchain.Config(), block.Number(), block.Time()) for idx, tx := range block.Transactions() { - // 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_test.go b/eth/sync_test.go index 7ede0a82c5..57eea73790 100644 --- a/eth/sync_test.go +++ b/eth/sync_test.go @@ -20,7 +20,7 @@ import ( "testing" "time" - "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" @@ -85,7 +85,7 @@ func testSnapSyncDisabling(t *testing.T, ethVer uint, snapVer uint) { time.Sleep(250 * time.Millisecond) // Check that snap sync was disabled - if err := empty.handler.downloader.BeaconSync(downloader.SnapSync, full.chain.CurrentBlock(), nil); err != nil { + if err := empty.handler.downloader.BeaconSync(ethconfig.SnapSync, full.chain.CurrentBlock(), nil); err != nil { t.Fatal("sync failed:", err) } empty.handler.enableSyncedFeatures() diff --git a/eth/tracers/api.go b/eth/tracers/api.go index 189afa48d4..22163030de 100644 --- a/eth/tracers/api.go +++ b/eth/tracers/api.go @@ -39,6 +39,7 @@ import ( "github.com/ethereum/go-ethereum/eth/tracers/logger" "github.com/ethereum/go-ethereum/ethdb" "github.com/ethereum/go-ethereum/internal/ethapi" + "github.com/ethereum/go-ethereum/internal/ethapi/override" "github.com/ethereum/go-ethereum/log" "github.com/ethereum/go-ethereum/params" "github.com/ethereum/go-ethereum/rlp" @@ -163,8 +164,8 @@ type TraceConfig struct { // field to override the state for tracing. type TraceCallConfig struct { TraceConfig - StateOverrides *ethapi.StateOverride - BlockOverrides *ethapi.BlockOverrides + StateOverrides *override.StateOverride + BlockOverrides *override.BlockOverrides TxIndex *hexutil.Uint } @@ -378,16 +379,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 @@ -537,24 +536,20 @@ func (api *API) IntermediateRoots(ctx context.Context, hash common.Hash, config vmctx = core.NewEVMBlockContext(block.Header(), api.chainContext(ctx), nil) deleteEmptyObjects = chainConfig.IsEIP158(block.Number()) ) + 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{}) - ) + 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 @@ -605,13 +600,12 @@ func (api *API) traceBlock(ctx context.Context, block *types.Block, config *Trac defer release() 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 @@ -695,6 +689,8 @@ func (api *API) traceBlockParallel(ctx context.Context, block *types.Block, stat // Feed the transactions into the tracers and return var failed error 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 { // Send the trace task over for execution @@ -709,14 +705,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) @@ -764,7 +759,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 ( @@ -783,23 +777,21 @@ 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() { // 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{}) { @@ -822,12 +814,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) } @@ -843,7 +834,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 { @@ -1011,14 +1002,13 @@ func (api *API) traceTx(ctx context.Context, tx *types.Transaction, message *cor Stop: logger.Stop, } } else { - tracer, err = DefaultDirectory.New(*config.Tracer, txctx, config.TracerConfig) + tracer, err = DefaultDirectory.New(*config.Tracer, txctx, config.TracerConfig, api.backend.ChainConfig()) if err != nil { 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}) - statedb.SetLogger(tracer.Hooks) + 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 { @@ -1032,14 +1022,14 @@ 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() // 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 47e3693495..13a7b0aaae 100644 --- a/eth/tracers/api_test.go +++ b/eth/tracers/api_test.go @@ -37,12 +37,14 @@ 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" "github.com/ethereum/go-ethereum/eth/tracers/logger" "github.com/ethereum/go-ethereum/ethdb" "github.com/ethereum/go-ethereum/internal/ethapi" + "github.com/ethereum/go-ethereum/internal/ethapi/override" "github.com/ethereum/go-ethereum/params" "github.com/ethereum/go-ethereum/rpc" ) @@ -170,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() @@ -366,7 +455,7 @@ func TestTraceCall(t *testing.T) { Input: &hexutil.Bytes{0x43}, // blocknumber }, config: &TraceCallConfig{ - BlockOverrides: ðapi.BlockOverrides{Number: (*hexutil.Big)(big.NewInt(0x1337))}, + BlockOverrides: &override.BlockOverrides{Number: (*hexutil.Big)(big.NewInt(0x1337))}, }, expectErr: nil, expect: ` {"gas":53018,"failed":false,"returnValue":"","structLogs":[ @@ -447,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") } @@ -610,8 +699,8 @@ func TestTracingWithOverrides(t *testing.T) { Value: (*hexutil.Big)(big.NewInt(1000)), }, config: &TraceCallConfig{ - StateOverrides: ðapi.StateOverride{ - randomAccounts[0].addr: ethapi.OverrideAccount{Balance: newRPCBalance(new(big.Int).Mul(big.NewInt(1), big.NewInt(params.Ether)))}, + StateOverrides: &override.StateOverride{ + randomAccounts[0].addr: override.OverrideAccount{Balance: newRPCBalance(new(big.Int).Mul(big.NewInt(1), big.NewInt(params.Ether)))}, }, }, want: `{"gas":21000,"failed":false,"returnValue":""}`, @@ -652,8 +741,8 @@ func TestTracingWithOverrides(t *testing.T) { }, config: &TraceCallConfig{ //Tracer: &tracer, - StateOverrides: ðapi.StateOverride{ - randomAccounts[2].addr: ethapi.OverrideAccount{ + StateOverrides: &override.StateOverride{ + randomAccounts[2].addr: override.OverrideAccount{ Code: newRPCBytes(common.Hex2Bytes("6080604052348015600f57600080fd5b506004361060285760003560e01c80638381f58a14602d575b600080fd5b60336049565b6040518082815260200191505060405180910390f35b6000548156fea2646970667358221220eab35ffa6ab2adfe380772a48b8ba78e82a1b820a18fcb6f59aa4efb20a5f60064736f6c63430007040033")), StateDiff: newStates([]common.Hash{{}}, []common.Hash{common.BigToHash(big.NewInt(123))}), }, @@ -669,7 +758,7 @@ func TestTracingWithOverrides(t *testing.T) { Input: newRPCBytes(common.Hex2Bytes("4360005260206000f3")), }, config: &TraceCallConfig{ - BlockOverrides: ðapi.BlockOverrides{Number: (*hexutil.Big)(big.NewInt(0x1337))}, + BlockOverrides: &override.BlockOverrides{Number: (*hexutil.Big)(big.NewInt(0x1337))}, }, want: `{"gas":59537,"failed":false,"returnValue":"0000000000000000000000000000000000000000000000000000000000001337"}`, }, @@ -689,7 +778,7 @@ func TestTracingWithOverrides(t *testing.T) { }, // blocknumber }, config: &TraceCallConfig{ - BlockOverrides: ðapi.BlockOverrides{Number: (*hexutil.Big)(big.NewInt(0x1337))}, + BlockOverrides: &override.BlockOverrides{Number: (*hexutil.Big)(big.NewInt(0x1337))}, }, want: `{"gas":72666,"failed":false,"returnValue":"000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"}`, }, @@ -719,8 +808,8 @@ func TestTracingWithOverrides(t *testing.T) { Data: newRPCBytes(common.Hex2Bytes("f8a8fd6d")), // }, config: &TraceCallConfig{ - StateOverrides: ðapi.StateOverride{ - randomAccounts[2].addr: ethapi.OverrideAccount{ + StateOverrides: &override.StateOverride{ + randomAccounts[2].addr: override.OverrideAccount{ Code: newRPCBytes(common.Hex2Bytes("6080604052348015600f57600080fd5b506004361060325760003560e01c806366e41cb7146037578063f8a8fd6d14603f575b600080fd5b603d6057565b005b60456062565b60405190815260200160405180910390f35b610539600090815580fd5b60006001600081905550306001600160a01b03166366e41cb76040518163ffffffff1660e01b8152600401600060405180830381600087803b15801560a657600080fd5b505af192505050801560b6575060015b60e9573d80801560e1576040519150601f19603f3d011682016040523d82523d6000602084013e60e6565b606091505b50505b506000549056fea26469706673582212205ce45de745a5308f713cb2f448589177ba5a442d1a2eff945afaa8915961b4d064736f6c634300080c0033")), }, }, @@ -735,8 +824,8 @@ func TestTracingWithOverrides(t *testing.T) { Data: newRPCBytes(common.Hex2Bytes("f8a8fd6d")), // }, config: &TraceCallConfig{ - StateOverrides: ðapi.StateOverride{ - randomAccounts[2].addr: ethapi.OverrideAccount{ + StateOverrides: &override.StateOverride{ + randomAccounts[2].addr: override.OverrideAccount{ Code: newRPCBytes(common.Hex2Bytes("6080604052348015600f57600080fd5b506004361060325760003560e01c806366e41cb7146037578063f8a8fd6d14603f575b600080fd5b603d6057565b005b60456062565b60405190815260200160405180910390f35b610539600090815580fd5b60006001600081905550306001600160a01b03166366e41cb76040518163ffffffff1660e01b8152600401600060405180830381600087803b15801560a657600080fd5b505af192505050801560b6575060015b60e9573d80801560e1576040519150601f19603f3d011682016040523d82523d6000602084013e60e6565b606091505b50505b506000549056fea26469706673582212205ce45de745a5308f713cb2f448589177ba5a442d1a2eff945afaa8915961b4d064736f6c634300080c0033")), State: newStates([]common.Hash{{}}, []common.Hash{{}}), }, @@ -753,8 +842,8 @@ func TestTracingWithOverrides(t *testing.T) { Data: newRPCBytes(common.Hex2Bytes("f8a8fd6d")), // }, config: &TraceCallConfig{ - StateOverrides: ðapi.StateOverride{ - storageAccount: ethapi.OverrideAccount{ + StateOverrides: &override.StateOverride{ + storageAccount: override.OverrideAccount{ Code: newRPCBytes([]byte{ // SLOAD(3) + SLOAD(4) (which is 0x77) byte(vm.PUSH1), 0x04, @@ -788,8 +877,8 @@ func TestTracingWithOverrides(t *testing.T) { Data: newRPCBytes(common.Hex2Bytes("f8a8fd6d")), // }, config: &TraceCallConfig{ - StateOverrides: ðapi.StateOverride{ - storageAccount: ethapi.OverrideAccount{ + StateOverrides: &override.StateOverride{ + storageAccount: override.OverrideAccount{ Code: newRPCBytes([]byte{ // SLOAD(3) + SLOAD(4) (which is now 0x11 + 0x00) byte(vm.PUSH1), 0x04, @@ -826,8 +915,8 @@ func TestTracingWithOverrides(t *testing.T) { Data: newRPCBytes(common.Hex2Bytes("f8a8fd6d")), // }, config: &TraceCallConfig{ - StateOverrides: ðapi.StateOverride{ - storageAccount: ethapi.OverrideAccount{ + StateOverrides: &override.StateOverride{ + storageAccount: override.OverrideAccount{ Code: newRPCBytes([]byte{ // SLOAD(3) + SLOAD(4) (which is now 0x11 + 0x44) byte(vm.PUSH1), 0x04, diff --git a/eth/tracers/dir.go b/eth/tracers/dir.go index 650815350b..55bcb44d23 100644 --- a/eth/tracers/dir.go +++ b/eth/tracers/dir.go @@ -22,6 +22,7 @@ import ( "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/core/tracing" + "github.com/ethereum/go-ethereum/params" ) // Context contains some contextual infos for a transaction execution that is not @@ -44,8 +45,8 @@ type Tracer struct { Stop func(err error) } -type ctorFn func(*Context, json.RawMessage) (*Tracer, error) -type jsCtorFn func(string, *Context, json.RawMessage) (*Tracer, error) +type ctorFn func(*Context, json.RawMessage, *params.ChainConfig) (*Tracer, error) +type jsCtorFn func(string, *Context, json.RawMessage, *params.ChainConfig) (*Tracer, error) type elem struct { ctor ctorFn @@ -78,12 +79,15 @@ func (d *directory) RegisterJSEval(f jsCtorFn) { // New returns a new instance of a tracer, by iterating through the // registered lookups. Name is either name of an existing tracer // or an arbitrary JS code. -func (d *directory) New(name string, ctx *Context, cfg json.RawMessage) (*Tracer, error) { +func (d *directory) New(name string, ctx *Context, cfg json.RawMessage, chainConfig *params.ChainConfig) (*Tracer, error) { + if len(cfg) == 0 { + cfg = json.RawMessage("{}") + } if elem, ok := d.elems[name]; ok { - return elem.ctor(ctx, cfg) + return elem.ctor(ctx, cfg, chainConfig) } // Assume JS code - return d.jsEval(name, ctx, cfg) + return d.jsEval(name, ctx, cfg, chainConfig) } // IsJS will return true if the given tracer will evaluate diff --git a/eth/tracers/internal/tracetest/calltrace_test.go b/eth/tracers/internal/tracetest/calltrace_test.go index 19934575b6..999ab211c0 100644 --- a/eth/tracers/internal/tracetest/calltrace_test.go +++ b/eth/tracers/internal/tracetest/calltrace_test.go @@ -29,12 +29,12 @@ import ( "github.com/ethereum/go-ethereum/common/hexutil" "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/types" "github.com/ethereum/go-ethereum/core/vm" "github.com/ethereum/go-ethereum/crypto" "github.com/ethereum/go-ethereum/eth/tracers" "github.com/ethereum/go-ethereum/params" - "github.com/ethereum/go-ethereum/rlp" "github.com/ethereum/go-ethereum/tests" ) @@ -116,21 +116,23 @@ func testCallTracer(tracerName string, dirPath string, t *testing.T) { var ( signer = types.MakeSigner(test.Genesis.Config, new(big.Int).SetUint64(uint64(test.Context.Number)), uint64(test.Context.Time)) context = test.Context.toBlockContext(test.Genesis) - state = tests.MakePreState(rawdb.NewMemoryDatabase(), test.Genesis.Alloc, false, rawdb.HashScheme) + st = tests.MakePreState(rawdb.NewMemoryDatabase(), test.Genesis.Alloc, false, rawdb.HashScheme) ) - state.Close() + st.Close() - tracer, err := tracers.DefaultDirectory.New(tracerName, new(tracers.Context), test.TracerConfig) + tracer, err := tracers.DefaultDirectory.New(tracerName, new(tracers.Context), test.TracerConfig, test.Genesis.Config) if err != nil { t.Fatalf("failed to create call tracer: %v", err) } - - state.StateDB.SetLogger(tracer.Hooks) + logState := vm.StateDB(st.StateDB) + if tracer.Hooks != nil { + logState = state.NewHookedState(st.StateDB, tracer.Hooks) + } msg, err := core.TransactionToMessage(tx, signer, context.BaseFee) 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, 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 { @@ -199,24 +201,11 @@ func BenchmarkTracers(b *testing.B) { func benchTracer(tracerName string, test *callTracerTest, b *testing.B) { // Configure a blockchain with the given prestate tx := new(types.Transaction) - if err := rlp.DecodeBytes(common.FromHex(test.Input), tx); err != nil { + if err := tx.UnmarshalBinary(common.FromHex(test.Input)); err != nil { 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 := vm.BlockContext{ - CanTransfer: core.CanTransfer, - Transfer: core.Transfer, - Coinbase: test.Context.Miner, - BlockNumber: new(big.Int).SetUint64(uint64(test.Context.Number)), - Time: uint64(test.Context.Time), - Difficulty: (*big.Int)(test.Context.Difficulty), - GasLimit: uint64(test.Context.GasLimit), - } + context := test.Context.toBlockContext(test.Genesis) msg, err := core.TransactionToMessage(tx, signer, context.BaseFee) if err != nil { b.Fatalf("failed to prepare transaction for tracing: %v", err) @@ -226,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++ { - tracer, err := tracers.DefaultDirectory.New(tracerName, new(tracers.Context), nil) + 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) } @@ -264,7 +262,7 @@ func TestInternals(t *testing.T) { } ) mkTracer := func(name string, cfg json.RawMessage) *tracers.Tracer { - tr, err := tracers.DefaultDirectory.New(name, nil, cfg) + tr, err := tracers.DefaultDirectory.New(name, nil, cfg, config) if err != nil { t.Fatalf("failed to create call tracer: %v", err) } @@ -349,7 +347,7 @@ func TestInternals(t *testing.T) { }, } { t.Run(tc.name, func(t *testing.T) { - state := tests.MakePreState(rawdb.NewMemoryDatabase(), + st := tests.MakePreState(rawdb.NewMemoryDatabase(), types.GenesisAlloc{ to: types.Account{ Code: tc.code, @@ -358,8 +356,13 @@ func TestInternals(t *testing.T) { Balance: big.NewInt(500000000000000), }, }, false, rawdb.HashScheme) - defer state.Close() - state.StateDB.SetLogger(tc.tracer.Hooks) + defer st.Close() + + logState := vm.StateDB(st.StateDB) + if hooks := tc.tracer.Hooks; hooks != nil { + logState = state.NewHookedState(st.StateDB, hooks) + } + tx, err := types.SignNewTx(key, signer, &types.LegacyTx{ To: &to, Value: big.NewInt(0), @@ -369,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, state.StateDB, 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 f20bbd8e71..553eaf1b57 100644 --- a/eth/tracers/internal/tracetest/flat_calltrace_test.go +++ b/eth/tracers/internal/tracetest/flat_calltrace_test.go @@ -89,17 +89,16 @@ func flatCallTracerTestRunner(tracerName string, filename string, dirPath string defer state.Close() // Create the tracer, the EVM environment and run it - tracer, err := tracers.DefaultDirectory.New(tracerName, new(tracers.Context), test.TracerConfig) + tracer, err := tracers.DefaultDirectory.New(tracerName, new(tracers.Context), test.TracerConfig, test.Genesis.Config) if err != nil { return fmt.Errorf("failed to create call tracer: %v", err) } - state.StateDB.SetLogger(tracer.Hooks) msg, err := core.TransactionToMessage(tx, signer, context.BaseFee) 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 f7a8116e7e..ad3d75d8de 100644 --- a/eth/tracers/internal/tracetest/prestate_test.go +++ b/eth/tracers/internal/tracetest/prestate_test.go @@ -97,17 +97,16 @@ func testPrestateDiffTracer(tracerName string, dirPath string, t *testing.T) { ) defer state.Close() - tracer, err := tracers.DefaultDirectory.New(tracerName, new(tracers.Context), test.TracerConfig) + tracer, err := tracers.DefaultDirectory.New(tracerName, new(tracers.Context), test.TracerConfig, test.Genesis.Config) if err != nil { t.Fatalf("failed to create call tracer: %v", err) } - state.StateDB.SetLogger(tracer.Hooks) msg, err := core.TransactionToMessage(tx, signer, context.BaseFee) 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 2cddcae67d..6f06b7c0d5 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] @@ -597,6 +597,7 @@ func testSupplyTracer(t *testing.T, genesis *core.Genesis, gen func(*core.BlockG } func compareAsJSON(t *testing.T, expected interface{}, actual interface{}) { + t.Helper() want, err := json.Marshal(expected) if err != nil { t.Fatalf("failed to marshal expected value to JSON: %v", err) @@ -608,6 +609,6 @@ func compareAsJSON(t *testing.T, expected interface{}, actual interface{}) { } if !bytes.Equal(want, have) { - t.Fatalf("incorrect supply info: expected %s, got %s", string(want), string(have)) + t.Fatalf("incorrect supply info:\nwant %s\nhave %s", string(want), string(have)) } } diff --git a/eth/tracers/internal/tracetest/testdata/call_tracer/blob_tx.json b/eth/tracers/internal/tracetest/testdata/call_tracer/blob_tx.json index b974151c1b..05da3b42e1 100644 --- a/eth/tracers/internal/tracetest/testdata/call_tracer/blob_tx.json +++ b/eth/tracers/internal/tracetest/testdata/call_tracer/blob_tx.json @@ -41,8 +41,7 @@ "grayGlacierBlock": 0, "shanghaiTime": 0, "cancunTime": 0, - "terminalTotalDifficulty": 0, - "terminalTotalDifficultyPassed": true + "terminalTotalDifficulty": 0 } }, "context": { diff --git a/eth/tracers/internal/tracetest/testdata/call_tracer_flat/big_slow.json b/eth/tracers/internal/tracetest/testdata/call_tracer_flat/big_slow.json index 26495dfbce..7acb221666 100644 --- a/eth/tracers/internal/tracetest/testdata/call_tracer_flat/big_slow.json +++ b/eth/tracers/internal/tracetest/testdata/call_tracer_flat/big_slow.json @@ -44,6 +44,7 @@ "result": [ { "action": { + "creationMethod": "create", "from": "0xf8bda96b67036ee48107f2a0695ea673479dda56", "gas": "0x231860", "init": "0x5b620186a05a131560135760016020526000565b600080601f600039601f565b6000f3", diff --git a/eth/tracers/internal/tracetest/testdata/call_tracer_flat/callcode_precompiled_fail_hide.json b/eth/tracers/internal/tracetest/testdata/call_tracer_flat/callcode_precompiled_fail_hide.json index 72a4db6901..a098b57029 100644 --- a/eth/tracers/internal/tracetest/testdata/call_tracer_flat/callcode_precompiled_fail_hide.json +++ b/eth/tracers/internal/tracetest/testdata/call_tracer_flat/callcode_precompiled_fail_hide.json @@ -51,6 +51,7 @@ { "type": "create", "action": { + "creationMethod": "create", "from": "0x877bd459c9b7d8576b44e59e09d076c25946f443", "value": "0x0", "gas": "0x19f78", @@ -66,8 +67,7 @@ "transactionPosition": 74, "transactionHash": "0x5ef60b27ac971c22a7d484e546e50093ca62300c8986d165154e47773764b6a4", "blockNumber": 1555279, - "blockHash": "0xd6c98d1b87dfa92a210d99bad2873adaf0c9e51fe43addc63fd9cca03a5c6f46", - "time": "209.346µs" + "blockHash": "0xd6c98d1b87dfa92a210d99bad2873adaf0c9e51fe43addc63fd9cca03a5c6f46" }, { "action": { diff --git a/eth/tracers/internal/tracetest/testdata/call_tracer_flat/callcode_precompiled_oog.json b/eth/tracers/internal/tracetest/testdata/call_tracer_flat/callcode_precompiled_oog.json index 0c4d29dd5b..6baa57689f 100644 --- a/eth/tracers/internal/tracetest/testdata/call_tracer_flat/callcode_precompiled_oog.json +++ b/eth/tracers/internal/tracetest/testdata/call_tracer_flat/callcode_precompiled_oog.json @@ -51,6 +51,7 @@ { "type": "create", "action": { + "creationMethod": "create", "from": "0x877bd459c9b7d8576b44e59e09d076c25946f443", "value": "0x0", "gas": "0x1a758", @@ -66,8 +67,7 @@ "transactionPosition": 141, "transactionHash": "0x1592cbda0d928b8d18eed98857942b91ade32d088e55b8bf63418917cb0231f1", "blockNumber": 1555278, - "blockHash": "0x755bd54de4b2f5a7a589a10d69888b4ead48a6311d5d69f2f69ca85ec35fbe0b", - "time": "300.9µs" + "blockHash": "0x755bd54de4b2f5a7a589a10d69888b4ead48a6311d5d69f2f69ca85ec35fbe0b" }, { "type": "call", @@ -80,9 +80,7 @@ "callType": "callcode" }, "error": "out of gas", - "traceAddress": [ - 0 - ], + "traceAddress": [0], "subtraces": 0, "transactionPosition": 141, "transactionHash": "0x1592cbda0d928b8d18eed98857942b91ade32d088e55b8bf63418917cb0231f1", @@ -90,4 +88,4 @@ "blockHash": "0x755bd54de4b2f5a7a589a10d69888b4ead48a6311d5d69f2f69ca85ec35fbe0b" } ] -} \ No newline at end of file +} diff --git a/eth/tracers/internal/tracetest/testdata/call_tracer_flat/callcode_precompiled_throw.json b/eth/tracers/internal/tracetest/testdata/call_tracer_flat/callcode_precompiled_throw.json index 5538a708ea..8949dcfe57 100644 --- a/eth/tracers/internal/tracetest/testdata/call_tracer_flat/callcode_precompiled_throw.json +++ b/eth/tracers/internal/tracetest/testdata/call_tracer_flat/callcode_precompiled_throw.json @@ -51,6 +51,7 @@ { "type": "create", "action": { + "creationMethod": "create", "from": "0x877bd459c9b7d8576b44e59e09d076c25946f443", "value": "0x0", "gas": "0x1a034", @@ -62,8 +63,7 @@ "transactionPosition": 117, "transactionHash": "0x7fe4dec901e1a62c1a1d96b8267bb9ff9dc1f75def43aa45b998743455eff8f9", "blockNumber": 1555275, - "blockHash": "0x80945caaff2fc67253cbb0217d2e5a307afde943929e97d8b36e58b88cbb02fd", - "time": "332.877µs" + "blockHash": "0x80945caaff2fc67253cbb0217d2e5a307afde943929e97d8b36e58b88cbb02fd" }, { "type": "call", @@ -76,9 +76,7 @@ "callType": "callcode" }, "error": "invalid input length", - "traceAddress": [ - 0 - ], + "traceAddress": [0], "subtraces": 0, "transactionPosition": 117, "transactionHash": "0x7fe4dec901e1a62c1a1d96b8267bb9ff9dc1f75def43aa45b998743455eff8f9", @@ -86,4 +84,4 @@ "blockHash": "0x80945caaff2fc67253cbb0217d2e5a307afde943929e97d8b36e58b88cbb02fd" } ] -} \ No newline at end of file +} diff --git a/eth/tracers/internal/tracetest/testdata/call_tracer_flat/create.json b/eth/tracers/internal/tracetest/testdata/call_tracer_flat/create.json index 83d20fb078..2de8f8d4d5 100644 --- a/eth/tracers/internal/tracetest/testdata/call_tracer_flat/create.json +++ b/eth/tracers/internal/tracetest/testdata/call_tracer_flat/create.json @@ -47,6 +47,7 @@ "result": [ { "action": { + "creationMethod": "create", "from": "0x13e4acefe6a6700604929946e70e6443e4e73447", "gas": "0x897be", "init": "0x606060405260405160208061077c83398101604052808051906020019091905050600160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff161415151561007d57600080fd5b336000806101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff16021790555080600160006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055506001600460006101000a81548160ff02191690831515021790555050610653806101296000396000f300606060405260043610610083576000357c0100000000000000000000000000000000000000000000000000000000900463ffffffff16806305e4382a146100855780631c02708d146100ae5780632e1a7d4d146100c35780635114cb52146100e6578063a37dda2c146100fe578063ae200e7914610153578063b5769f70146101a8575b005b341561009057600080fd5b6100986101d1565b6040518082815260200191505060405180910390f35b34156100b957600080fd5b6100c16101d7565b005b34156100ce57600080fd5b6100e460048080359060200190919050506102eb565b005b6100fc6004808035906020019091905050610513565b005b341561010957600080fd5b6101116105d6565b604051808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060405180910390f35b341561015e57600080fd5b6101666105fc565b604051808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060405180910390f35b34156101b357600080fd5b6101bb610621565b6040518082815260200191505060405180910390f35b60025481565b60011515600460009054906101000a900460ff1615151415156101f957600080fd5b6000809054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff1614806102a15750600160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff16145b15156102ac57600080fd5b6000600460006101000a81548160ff0219169083151502179055506003543073ffffffffffffffffffffffffffffffffffffffff163103600281905550565b6000809054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff1614806103935750600160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff16145b151561039e57600080fd5b6000809054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff16141561048357600060025411801561040757506002548111155b151561041257600080fd5b80600254036002819055506000809054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff166108fc829081150290604051600060405180830381858888f19350505050151561047e57600080fd5b610510565b600060035411801561049757506003548111155b15156104a257600080fd5b8060035403600381905550600160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff166108fc829081150290604051600060405180830381858888f19350505050151561050f57600080fd5b5b50565b60011515600460009054906101000a900460ff16151514151561053557600080fd5b6000809054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff1614801561059657506003548160035401115b80156105bd575080600354013073ffffffffffffffffffffffffffffffffffffffff163110155b15156105c857600080fd5b806003540160038190555050565b600160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1681565b6000809054906101000a900473ffffffffffffffffffffffffffffffffffffffff1681565b600354815600a165627a7a72305820c3b849e8440987ce43eae3097b77672a69234d516351368b03fe5b7de03807910029000000000000000000000000c65e620a3a55451316168d57e268f5702ef56a11", diff --git a/eth/tracers/internal/tracetest/testdata/call_tracer_flat/create_oog_parity.json b/eth/tracers/internal/tracetest/testdata/call_tracer_flat/create_oog_parity.json new file mode 100644 index 0000000000..5e79e6ad45 --- /dev/null +++ b/eth/tracers/internal/tracetest/testdata/call_tracer_flat/create_oog_parity.json @@ -0,0 +1,94 @@ +{ + "genesis": { + "difficulty": "4639933", + "extraData": "0xd883010b05846765746888676f312e31342e33856c696e7578", + "gasLimit": "9280188", + "hash": "0x9a5f3a98eb1c60f6e3f450658a9cea190157e7021d04f927b752ad6482cf9194", + "miner": "0x73f26d124436b0791169d63a3af29c2ae47765a3", + "mixHash": "0x6b6f8fcaa54b8565c4c1ae7cf0a020e938a53007f4561e758b17bc05c9044d78", + "nonce": "0x773aba50dc51b462", + "number": "1555169", + "stateRoot": "0xc4b9703de3e59ff795baae2c3afa010cf039c37244a7a6af7f3f491a10601348", + "timestamp": "1590794111", + "totalDifficulty": "2242105342155", + "alloc": { + "0x5ac5599fc9df172c89ee7ec55ad9104ccbfed40d": { + "balance": "0x0", + "nonce": "0", + "code": "0x", + "storage": {} + }, + "0x877bd459c9b7d8576b44e59e09d076c25946f443": { + "balance": "0x62325b40cbbd0915c4b9", + "nonce": "260875", + "code": "0x", + "storage": {} + } + }, + "config": { + "chainId": 63, + "daoForkSupport": true, + "eip150Block": 0, + "eip150Hash": "0x41941023680923e0fe4d74a34bdac8141f2540e3ae90623718e47d66d1ca4a2d", + "eip155Block": 0, + "eip158Block": 0, + "ethash": {}, + "homesteadBlock": 0, + "byzantiumBlock": 0, + "constantinopleBlock": 301243, + "petersburgBlock": 999983, + "istanbulBlock": 999983 + } + }, + "context": { + "number": "1555170", + "difficulty": "4642198", + "timestamp": "1590794112", + "gasLimit": "9289249", + "miner": "0x877bd459c9b7d8576b44e59e09d076c25946f443" + }, + "input": "0xf8658303fb0b843b9aca0083019ee48080915a600055600060006000f0505a6001550081a2a01a7deb3a16d967b766459ef486b00656c6581e5ad58968184a33701e27e0eb8aa07162ccdfe2018d64360a605310a62c399dd586c7282dd42a88c54f02f51d451f", + "tracerConfig": { + "convertParityErrors": true + }, + "result": [ + { + "type": "create", + "action": { + "from": "0x877bd459c9b7d8576b44e59e09d076c25946f443", + "value": "0x0", + "gas": "0x19ee4", + "init": "0x5a600055600060006000f0505a60015500", + "creationMethod": "create" + }, + "error": "Out of gas", + "traceAddress": [], + "subtraces": 1, + "transactionPosition": 63, + "transactionHash": "0x60e881fae3884657b5430925c5d0053535b45cce0b8188f2a6be1feee8bcc650", + "blockNumber": 1555170, + "blockHash": "0xea46fbf941d51bf1e4180fbf26d22fda3896f49c7f371d109c226de95dd7b02e" + }, + { + "type": "create", + "action": { + "from": "0x9c5cfe45b15eaff4ad617af4250189e26024a4f8", + "value": "0x0", + "gas": "0x3cb", + "init": "0x", + "creationMethod": "create" + }, + "result": { + "gasUsed": "0x0", + "code": "0x", + "address": "0x5ac5599fc9df172c89ee7ec55ad9104ccbfed40d" + }, + "traceAddress": [0], + "subtraces": 0, + "transactionPosition": 63, + "transactionHash": "0x60e881fae3884657b5430925c5d0053535b45cce0b8188f2a6be1feee8bcc650", + "blockNumber": 1555170, + "blockHash": "0xea46fbf941d51bf1e4180fbf26d22fda3896f49c7f371d109c226de95dd7b02e" + } + ] +} diff --git a/eth/tracers/internal/tracetest/testdata/call_tracer_flat/gas.json b/eth/tracers/internal/tracetest/testdata/call_tracer_flat/gas.json index 250c402808..2fad32afe2 100644 --- a/eth/tracers/internal/tracetest/testdata/call_tracer_flat/gas.json +++ b/eth/tracers/internal/tracetest/testdata/call_tracer_flat/gas.json @@ -51,6 +51,7 @@ { "type": "create", "action": { + "creationMethod": "create", "from": "0x877bd459c9b7d8576b44e59e09d076c25946f443", "value": "0x0", "gas": "0x1a9c8", @@ -66,8 +67,7 @@ "transactionPosition": 18, "transactionHash": "0xc1c42a325856d513523aec464811923b2e2926f54015c7ba37877064cf889803", "blockNumber": 1555275, - "blockHash": "0x80945caaff2fc67253cbb0217d2e5a307afde943929e97d8b36e58b88cbb02fd", - "time": "453.925µs" + "blockHash": "0x80945caaff2fc67253cbb0217d2e5a307afde943929e97d8b36e58b88cbb02fd" }, { "type": "call", diff --git a/eth/tracers/internal/tracetest/testdata/call_tracer_flat/inner_create_oog_outer_throw.json b/eth/tracers/internal/tracetest/testdata/call_tracer_flat/inner_create_oog_outer_throw.json index 3747d7f0f8..0af816e683 100644 --- a/eth/tracers/internal/tracetest/testdata/call_tracer_flat/inner_create_oog_outer_throw.json +++ b/eth/tracers/internal/tracetest/testdata/call_tracer_flat/inner_create_oog_outer_throw.json @@ -71,6 +71,7 @@ }, { "action": { + "creationMethod": "create", "from": "0x1d3ddf7caf024f253487e18bc4a15b1a360c170a", "gas": "0x39ff0", "init": "0x606060405234620000005760405160208062001fd283398101604052515b805b600a8054600160a060020a031916600160a060020a0383161790555b506001600d819055600e81905560408051808201909152600c8082527f566f74696e672053746f636b00000000000000000000000000000000000000006020928301908152600b805460008290528251601860ff1990911617825590947f0175b7a638427703f0dbe7bb9bbf987a2551717b34e79f33b5b1008d1fa01db9600291831615610100026000190190921604601f0193909304830192906200010c565b828001600101855582156200010c579182015b828111156200010c578251825591602001919060010190620000ef565b5b50620001309291505b808211156200012c576000815560010162000116565b5090565b50506040805180820190915260038082527f43565300000000000000000000000000000000000000000000000000000000006020928301908152600c805460008290528251600660ff1990911617825590937fdf6966c971051c3d54ec59162606531493a51404a002842f56009d7e5cf4a8c760026001841615610100026000190190931692909204601f010481019291620001f7565b82800160010185558215620001f7579182015b82811115620001f7578251825591602001919060010190620001da565b5b506200021b9291505b808211156200012c576000815560010162000116565b5090565b50505b505b611da280620002306000396000f3006060604052361561019a5763ffffffff60e060020a600035041662e1986d811461019f57806302a72a4c146101d657806306eb4e421461020157806306fdde0314610220578063095ea7b3146102ad578063158ccb99146102dd57806318160ddd146102f85780631cf65a781461031757806323b872dd146103365780632c71e60a1461036c57806333148fd6146103ca578063435ebc2c146103f55780635eeb6e451461041e578063600e85b71461043c5780636103d70b146104a157806362c1e46a146104b05780636c182e99146104ba578063706dc87c146104f057806370a082311461052557806377174f851461055057806395d89b411461056f578063a7771ee3146105fc578063a9059cbb14610629578063ab377daa14610659578063b25dbb5e14610685578063b89a73cb14610699578063ca5eb5e1146106c6578063cbcf2e5a146106e1578063d21f05ba1461070e578063d347c2051461072d578063d96831e114610765578063dd62ed3e14610777578063df3c211b146107a8578063e2982c21146107d6578063eb944e4c14610801575b610000565b34610000576101d4600160a060020a036004351660243567ffffffffffffffff6044358116906064358116906084351661081f565b005b34610000576101ef600160a060020a0360043516610a30565b60408051918252519081900360200190f35b34610000576101ef610a4f565b60408051918252519081900360200190f35b346100005761022d610a55565b604080516020808252835181830152835191928392908301918501908083838215610273575b80518252602083111561027357601f199092019160209182019101610253565b505050905090810190601f16801561029f5780820380516001836020036101000a031916815260200191505b509250505060405180910390f35b34610000576102c9600160a060020a0360043516602435610ae3565b604080519115158252519081900360200190f35b34610000576101d4600160a060020a0360043516610b4e565b005b34610000576101ef610b89565b60408051918252519081900360200190f35b34610000576101ef610b8f565b60408051918252519081900360200190f35b34610000576102c9600160a060020a0360043581169060243516604435610b95565b604080519115158252519081900360200190f35b3461000057610388600160a060020a0360043516602435610bb7565b60408051600160a060020a039096168652602086019490945267ffffffffffffffff928316858501529082166060850152166080830152519081900360a00190f35b34610000576101ef600160a060020a0360043516610c21565b60408051918252519081900360200190f35b3461000057610402610c40565b60408051600160a060020a039092168252519081900360200190f35b34610000576101d4600160a060020a0360043516602435610c4f565b005b3461000057610458600160a060020a0360043516602435610cc9565b60408051600160a060020a03909716875260208701959095528585019390935267ffffffffffffffff9182166060860152811660808501521660a0830152519081900360c00190f35b34610000576101d4610d9e565b005b6101d4610e1e565b005b34610000576104d3600160a060020a0360043516610e21565b6040805167ffffffffffffffff9092168252519081900360200190f35b3461000057610402600160a060020a0360043516610ead565b60408051600160a060020a039092168252519081900360200190f35b34610000576101ef600160a060020a0360043516610ef9565b60408051918252519081900360200190f35b34610000576101ef610f18565b60408051918252519081900360200190f35b346100005761022d610f1e565b604080516020808252835181830152835191928392908301918501908083838215610273575b80518252602083111561027357601f199092019160209182019101610253565b505050905090810190601f16801561029f5780820380516001836020036101000a031916815260200191505b509250505060405180910390f35b34610000576102c9600160a060020a0360043516610fac565b604080519115158252519081900360200190f35b34610000576102c9600160a060020a0360043516602435610fc2565b604080519115158252519081900360200190f35b3461000057610402600435610fe2565b60408051600160a060020a039092168252519081900360200190f35b34610000576101d46004351515610ffd565b005b34610000576102c9600160a060020a036004351661104c565b604080519115158252519081900360200190f35b34610000576101d4600160a060020a0360043516611062565b005b34610000576102c9600160a060020a0360043516611070565b604080519115158252519081900360200190f35b34610000576101ef6110f4565b60408051918252519081900360200190f35b34610000576101ef600160a060020a036004351667ffffffffffffffff602435166110fa565b60408051918252519081900360200190f35b34610000576101d4600435611121565b005b34610000576101ef600160a060020a03600435811690602435166111c6565b60408051918252519081900360200190f35b34610000576101ef6004356024356044356064356084356111f3565b60408051918252519081900360200190f35b34610000576101ef600160a060020a036004351661128c565b60408051918252519081900360200190f35b34610000576101d4600160a060020a036004351660243561129e565b005b6040805160a08101825260008082526020820181905291810182905260608101829052608081019190915267ffffffffffffffff848116908416101561086457610000565b8367ffffffffffffffff168267ffffffffffffffff16101561088557610000565b8267ffffffffffffffff168267ffffffffffffffff1610156108a657610000565b506040805160a081018252600160a060020a033381168252602080830188905267ffffffffffffffff80871684860152858116606085015287166080840152908816600090815260039091529190912080546001810180835582818380158290116109615760030281600302836000526020600020918201910161096191905b8082111561095d578054600160a060020a031916815560006001820155600281018054600160c060020a0319169055600301610926565b5090565b5b505050916000526020600020906003020160005b5082518154600160a060020a031916600160a060020a03909116178155602083015160018201556040830151600290910180546060850151608086015167ffffffffffffffff1990921667ffffffffffffffff948516176fffffffffffffffff00000000000000001916604060020a918516919091021777ffffffffffffffff000000000000000000000000000000001916608060020a939091169290920291909117905550610a268686610fc2565b505b505050505050565b600160a060020a0381166000908152600360205260409020545b919050565b60055481565b600b805460408051602060026001851615610100026000190190941693909304601f81018490048402820184019092528181529291830182828015610adb5780601f10610ab057610100808354040283529160200191610adb565b820191906000526020600020905b815481529060010190602001808311610abe57829003601f168201915b505050505081565b600160a060020a03338116600081815260026020908152604080832094871680845294825280832086905580518681529051929493927f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925929181900390910190a35060015b92915050565b600a5433600160a060020a03908116911614610b6957610000565b600a8054600160a060020a031916600160a060020a0383161790555b5b50565b60005481565b60005b90565b6000610ba2848484611600565b610bad8484846116e2565b90505b9392505050565b600360205281600052604060002081815481101561000057906000526020600020906003020160005b5080546001820154600290920154600160a060020a03909116935090915067ffffffffffffffff80821691604060020a8104821691608060020a9091041685565b600160a060020a0381166000908152600860205260409020545b919050565b600a54600160a060020a031681565b600a5433600160a060020a03908116911614610c6a57610000565b610c7660005482611714565b6000908155600160a060020a038316815260016020526040902054610c9b9082611714565b600160a060020a038316600090815260016020526040812091909155610cc390839083611600565b5b5b5050565b6000600060006000600060006000600360008a600160a060020a0316600160a060020a0316815260200190815260200160002088815481101561000057906000526020600020906003020160005b508054600182015460028301546040805160a081018252600160a060020a039094168085526020850184905267ffffffffffffffff808416928601839052604060020a8404811660608701819052608060020a9094041660808601819052909c50929a509197509095509350909150610d90904261172d565b94505b509295509295509295565b33600160a060020a038116600090815260066020526040902054801515610dc457610000565b8030600160a060020a0316311015610ddb57610000565b600160a060020a0382166000818152600660205260408082208290555183156108fc0291849190818181858888f193505050501515610cc357610000565b5b5050565b5b565b600160a060020a03811660009081526003602052604081205442915b81811015610ea557600160a060020a03841660009081526003602052604090208054610e9a9190839081101561000057906000526020600020906003020160005b5060020154604060020a900467ffffffffffffffff168461177d565b92505b600101610e3d565b5b5050919050565b600160a060020a0380821660009081526007602052604081205490911615610eef57600160a060020a0380831660009081526007602052604090205416610ef1565b815b90505b919050565b600160a060020a0381166000908152600160205260409020545b919050565b600d5481565b600c805460408051602060026001851615610100026000190190941693909304601f81018490048402820184019092528181529291830182828015610adb5780601f10610ab057610100808354040283529160200191610adb565b820191906000526020600020905b815481529060010190602001808311610abe57829003601f168201915b505050505081565b60006000610fb983610c21565b1190505b919050565b6000610fcf338484611600565b610fd983836117ac565b90505b92915050565b600460205260009081526040902054600160a060020a031681565b8015801561101a575061100f33610ef9565b61101833610c21565b115b1561102457610000565b33600160a060020a03166000908152600960205260409020805460ff19168215151790555b50565b60006000610fb983610ef9565b1190505b919050565b610b8533826117dc565b5b50565b600a54604080516000602091820181905282517fcbcf2e5a000000000000000000000000000000000000000000000000000000008152600160a060020a03868116600483015293519194939093169263cbcf2e5a92602480830193919282900301818787803b156100005760325a03f115610000575050604051519150505b919050565b600e5481565b6000610fd961110984846118b2565b61111385856119b6565b611a05565b90505b92915050565b600a5433600160a060020a0390811691161461113c57610000565b61114860005482611a1f565b600055600554600190101561116c57600a5461116c90600160a060020a0316611a47565b5b600a54600160a060020a03166000908152600160205260409020546111929082611a1f565b600a8054600160a060020a039081166000908152600160205260408120939093559054610b8592911683611600565b5b5b50565b600160a060020a038083166000908152600260209081526040808320938516835292905220545b92915050565b6000600060008487101561120a5760009250611281565b8387111561121a57879250611281565b61123f6112308961122b888a611714565b611a90565b61123a8689611714565b611abc565b915081925061124e8883611714565b905061127e8361127961126a8461122b8c8b611714565b611a90565b61123a888b611714565b611abc565b611a1f565b92505b505095945050505050565b60066020526000908152604090205481565b600160a060020a03821660009081526003602052604081208054829190849081101561000057906000526020600020906003020160005b50805490925033600160a060020a039081169116146112f357610000565b6040805160a0810182528354600160a060020a0316815260018401546020820152600284015467ffffffffffffffff80821693830193909352604060020a810483166060830152608060020a900490911660808201526113539042611af9565b600160a060020a0385166000908152600360205260409020805491925090849081101561000057906000526020600020906003020160005b508054600160a060020a031916815560006001820181905560029091018054600160c060020a0319169055600160a060020a0385168152600360205260409020805460001981019081101561000057906000526020600020906003020160005b50600160a060020a03851660009081526003602052604090208054859081101561000057906000526020600020906003020160005b5081548154600160a060020a031916600160a060020a03918216178255600180840154908301556002928301805493909201805467ffffffffffffffff191667ffffffffffffffff948516178082558354604060020a908190048616026fffffffffffffffff000000000000000019909116178082559254608060020a9081900490941690930277ffffffffffffffff00000000000000000000000000000000199092169190911790915584166000908152600360205260409020805460001981018083559190829080158290116115485760030281600302836000526020600020918201910161154891905b8082111561095d578054600160a060020a031916815560006001820155600281018054600160c060020a0319169055600301610926565b5090565b5b505050600160a060020a033316600090815260016020526040902054611570915082611a1f565b600160a060020a03338116600090815260016020526040808220939093559086168152205461159f9082611714565b600160a060020a038086166000818152600160209081526040918290209490945580518581529051339093169391927fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef929181900390910190a35b50505050565b600160a060020a0383161561166e576116466008600061161f86610ead565b600160a060020a0316600160a060020a031681526020019081526020016000205482611714565b6008600061165386610ead565b600160a060020a031681526020810191909152604001600020555b600160a060020a038216156116dc576116b46008600061168d85610ead565b600160a060020a0316600160a060020a031681526020019081526020016000205482611a1f565b600860006116c185610ead565b600160a060020a031681526020810191909152604001600020555b5b505050565b600083826116f082426110fa565b8111156116fc57610000565b611707868686611b1b565b92505b5b50509392505050565b600061172283831115611b4d565b508082035b92915050565b6000610fd983602001518367ffffffffffffffff16856080015167ffffffffffffffff16866040015167ffffffffffffffff16876060015167ffffffffffffffff166111f3565b90505b92915050565b60008167ffffffffffffffff168367ffffffffffffffff1610156117a15781610fd9565b825b90505b92915050565b600033826117ba82426110fa565b8111156117c657610000565b6117d08585611b5d565b92505b5b505092915050565b6117e582610ef9565b6117ee83610c21565b11156117f957610000565b600160a060020a03811660009081526009602052604090205460ff16158015611834575081600160a060020a031681600160a060020a031614155b1561183e57610000565b61184782611070565b1561185157610000565b611864828261185f85610ef9565b611600565b600160a060020a0382811660009081526007602052604090208054600160a060020a031916918316918217905561189a82610ead565b600160a060020a031614610cc357610000565b5b5050565b600160a060020a038216600090815260036020526040812054815b818110156119885761197d836112796003600089600160a060020a0316600160a060020a0316815260200190815260200160002084815481101561000057906000526020600020906003020160005b506040805160a0810182528254600160a060020a031681526001830154602082015260029092015467ffffffffffffffff80821692840192909252604060020a810482166060840152608060020a900416608082015287611af9565b611a1f565b92505b6001016118cd565b600160a060020a0385166000908152600160205260409020546117d09084611714565b92505b505092915050565b600060006119c384611070565b80156119d157506000600d54115b90506119fb816119e9576119e485610ef9565b6119ec565b60005b6111138686611b7b565b611a05565b91505b5092915050565b60008183106117a15781610fd9565b825b90505b92915050565b6000828201611a3c848210801590611a375750838210155b611b4d565b8091505b5092915050565b611a508161104c565b15611a5a57610b85565b6005805460009081526004602052604090208054600160a060020a031916600160a060020a038416179055805460010190555b50565b6000828202611a3c841580611a37575083858381156100005704145b611b4d565b8091505b5092915050565b60006000611acc60008411611b4d565b8284811561000057049050611a3c838581156100005706828502018514611b4d565b8091505b5092915050565b6000610fd98360200151611b0d858561172d565b611714565b90505b92915050565b60008382611b2982426110fa565b811115611b3557610000565b611707868686611b8f565b92505b5b50509392505050565b801515610b8557610000565b5b50565b6000611b6883611a47565b610fd98383611c92565b90505b92915050565b6000610fd983610ef9565b90505b92915050565b600160a060020a038084166000908152600260209081526040808320338516845282528083205493861683526001909152812054909190611bd09084611a1f565b600160a060020a038086166000908152600160205260408082209390935590871681522054611bff9084611714565b600160a060020a038616600090815260016020526040902055611c228184611714565b600160a060020a038087166000818152600260209081526040808320338616845282529182902094909455805187815290519288169391927fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef929181900390910190a3600191505b509392505050565b60003382611ca082426110fa565b811115611cac57610000565b6117d08585611cc2565b92505b5b505092915050565b600160a060020a033316600090815260016020526040812054611ce59083611714565b600160a060020a033381166000908152600160205260408082209390935590851681522054611d149083611a1f565b600160a060020a038085166000818152600160209081526040918290209490945580518681529051919333909316927fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef92918290030190a35060015b929150505600a165627a7a72305820bfa5ddd3fecf3f43aed25385ec7ec3ef79638c2e58d99f85d9a3cc494183bf160029000000000000000000000000a14bdd7e5666d784dcce98ad24d383a6b1cd4182", diff --git a/eth/tracers/internal/tracetest/testdata/call_tracer_flat/nested_create.json b/eth/tracers/internal/tracetest/testdata/call_tracer_flat/nested_create.json index 7dc6cc8098..4383af2d8a 100644 --- a/eth/tracers/internal/tracetest/testdata/call_tracer_flat/nested_create.json +++ b/eth/tracers/internal/tracetest/testdata/call_tracer_flat/nested_create.json @@ -51,6 +51,7 @@ { "type": "create", "action": { + "creationMethod": "create", "from": "0x877bd459c9b7d8576b44e59e09d076c25946f443", "value": "0x0", "gas": "0x53e90", @@ -66,12 +67,12 @@ "transactionPosition": 23, "transactionHash": "0xe267552ce8437a5bc7081385c99f912de5723ad34b958db215dbc41abd5f6c03", "blockNumber": 555462, - "blockHash": "0x38bba9e3965b57205097ea5ec53fc403cf3941bec2e4c933faae244de5ca4ba1", - "time": "1.147715ms" + "blockHash": "0x38bba9e3965b57205097ea5ec53fc403cf3941bec2e4c933faae244de5ca4ba1" }, { "type": "create", "action": { + "creationMethod": "create", "from": "0x9db7a1baf185a865ffee3824946ccd8958191e5e", "value": "0x0", "gas": "0x30b34", diff --git a/eth/tracers/internal/tracetest/testdata/call_tracer_flat/nested_create2_action_gas.json b/eth/tracers/internal/tracetest/testdata/call_tracer_flat/nested_create2_action_gas.json index 82692d181a..9fe9ba2b9d 100644 --- a/eth/tracers/internal/tracetest/testdata/call_tracer_flat/nested_create2_action_gas.json +++ b/eth/tracers/internal/tracetest/testdata/call_tracer_flat/nested_create2_action_gas.json @@ -51,6 +51,7 @@ { "type": "create", "action": { + "creationMethod": "create", "from": "0x877bd459c9b7d8576b44e59e09d076c25946f443", "value": "0x0", "gas": "0x19ed8", @@ -66,12 +67,12 @@ "transactionPosition": 31, "transactionHash": "0x1257b698c5833c54ce786734087002b097275abc3877af082b5c2a538e894a41", "blockNumber": 1555161, - "blockHash": "0xb0793dd508dd106a19794b8ce1dfc0ff8d98c76aab61bf32a11799854149a171", - "time": "889.048µs" + "blockHash": "0xb0793dd508dd106a19794b8ce1dfc0ff8d98c76aab61bf32a11799854149a171" }, { "type": "create", "action": { + "creationMethod": "create2", "from": "0x2e8eded627eead210cb6143eb39ef7a3e44e4f00", "value": "0x0", "gas": "0x5117", diff --git a/eth/tracers/internal/tracetest/testdata/call_tracer_flat/nested_create_action_gas.json b/eth/tracers/internal/tracetest/testdata/call_tracer_flat/nested_create_action_gas.json index d9595a7210..f8ad3b5b0e 100644 --- a/eth/tracers/internal/tracetest/testdata/call_tracer_flat/nested_create_action_gas.json +++ b/eth/tracers/internal/tracetest/testdata/call_tracer_flat/nested_create_action_gas.json @@ -51,6 +51,7 @@ { "type": "create", "action": { + "creationMethod": "create", "from": "0x877bd459c9b7d8576b44e59e09d076c25946f443", "value": "0x0", "gas": "0x19ee4", @@ -62,12 +63,12 @@ "transactionPosition": 63, "transactionHash": "0x60e881fae3884657b5430925c5d0053535b45cce0b8188f2a6be1feee8bcc650", "blockNumber": 1555170, - "blockHash": "0xea46fbf941d51bf1e4180fbf26d22fda3896f49c7f371d109c226de95dd7b02e", - "time": "952.736µs" + "blockHash": "0xea46fbf941d51bf1e4180fbf26d22fda3896f49c7f371d109c226de95dd7b02e" }, { "type": "create", "action": { + "creationMethod": "create", "from": "0x9c5cfe45b15eaff4ad617af4250189e26024a4f8", "value": "0x0", "gas": "0x3cb", diff --git a/eth/tracers/internal/tracetest/testdata/call_tracer_flat/nested_create_inerror.json b/eth/tracers/internal/tracetest/testdata/call_tracer_flat/nested_create_inerror.json index cb4954d9b7..41199e90e3 100644 --- a/eth/tracers/internal/tracetest/testdata/call_tracer_flat/nested_create_inerror.json +++ b/eth/tracers/internal/tracetest/testdata/call_tracer_flat/nested_create_inerror.json @@ -73,11 +73,11 @@ "transactionPosition": 26, "transactionHash": "0xcb1090fa85d2a3da8326b75333e92b3dca89963c895d9c981bfdaa64643135e4", "blockNumber": 839247, - "blockHash": "0xce7ff7d84ca97f0f89d6065e2c12409a795c9f607cdb14aef0713cad5d7e311c", - "time": "182.267µs" + "blockHash": "0xce7ff7d84ca97f0f89d6065e2c12409a795c9f607cdb14aef0713cad5d7e311c" }, { "action": { + "creationMethod": "create", "from": "0x76554b33410b6d90b7dc889bfed0451ad195f27e", "gas": "0x25a18", "init": "0x0000000000000000000000000000000000000000000000000000000000000000", @@ -90,4 +90,4 @@ "type": "create" } ] -} \ No newline at end of file +} diff --git a/eth/tracers/internal/tracetest/testdata/call_tracer_flat/nested_pointer_issue.json b/eth/tracers/internal/tracetest/testdata/call_tracer_flat/nested_pointer_issue.json index 89f642db20..a7421d294d 100644 --- a/eth/tracers/internal/tracetest/testdata/call_tracer_flat/nested_pointer_issue.json +++ b/eth/tracers/internal/tracetest/testdata/call_tracer_flat/nested_pointer_issue.json @@ -61,7 +61,6 @@ "berlinBlock": 4460644, "londonBlock": 5062605, "terminalTotalDifficulty": 10790000, - "terminalTotalDifficultyPassed": true, "clique": { "period": 15, "epoch": 30000 @@ -97,6 +96,7 @@ "result": [ { "action": { + "creationMethod": "create", "from": "0x5409ed021d9299bf6814279a6a1411a7e866a631", "gas": "0x2c8c7f", "init": "0x60806040523480156200001157600080fd5b5060405162002d2c38038062002d2c83398101806040526200003791908101906200051d565b6000805433600160a060020a031991821617825560018054909116600160a060020a0386161790558251849084908490849081906200007e906004906020870190620003d0565b50825162000094906005906020860190620003d0565b50620000b0836010640100000000620019476200036f82021704565b9150620000cd846010640100000000620019476200036f82021704565b60028054600160a060020a03948516600160a060020a031991821617909155600380549285169290911691909117905550600154604080517f4552433230546f6b656e28616464726573732900000000000000000000000000815290519081900360130181207f6070410800000000000000000000000000000000000000000000000000000000825291909216945063607041089350620001739250906004016200068e565b602060405180830381600087803b1580156200018e57600080fd5b505af1158015620001a3573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250620001c99190810190620004f4565b9050600160a060020a038116151562000219576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016200021090620006b0565b60405180910390fd5b6002546040517f095ea7b3000000000000000000000000000000000000000000000000000000008152600160a060020a039091169063095ea7b39062000268908490600019906004016200066f565b602060405180830381600087803b1580156200028357600080fd5b505af115801562000298573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250620002be9190810190620005a1565b506003546040517f095ea7b3000000000000000000000000000000000000000000000000000000008152600160a060020a039091169063095ea7b3906200030e908490600019906004016200066f565b602060405180830381600087803b1580156200032957600080fd5b505af11580156200033e573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250620003649190810190620005a1565b50505050506200077a565b600081601401835110151515620003b4576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040162000210906200069e565b506014818301810151910190600160a060020a03165b92915050565b828054600181600116156101000203166002900490600052602060002090601f016020900481019282601f106200041357805160ff191683800117855562000443565b8280016001018555821562000443579182015b828111156200044357825182559160200191906001019062000426565b506200045192915062000455565b5090565b6200047291905b808211156200045157600081556001016200045c565b90565b600062000483825162000711565b9392505050565b600062000483825162000742565b6000601f82018313620004aa57600080fd5b8151620004c1620004bb82620006e9565b620006c2565b91508082526020830160208301858383011115620004de57600080fd5b620004eb83828462000747565b50505092915050565b6000602082840312156200050757600080fd5b600062000515848462000475565b949350505050565b6000806000606084860312156200053357600080fd5b600062000541868662000475565b93505060208401516001604060020a038111156200055e57600080fd5b6200056c8682870162000498565b92505060408401516001604060020a038111156200058957600080fd5b620005978682870162000498565b9150509250925092565b600060208284031215620005b457600080fd5b60006200051584846200048a565b620005cd8162000711565b82525050565b620005cd816200071d565b602681527f475245415445525f4f525f455155414c5f544f5f32305f4c454e4754485f524560208201527f5155495245440000000000000000000000000000000000000000000000000000604082015260600190565b601881527f554e524547495354455245445f41535345545f50524f58590000000000000000602082015260400190565b620005cd8162000472565b604081016200067f8285620005c2565b62000483602083018462000664565b60208101620003ca8284620005d3565b60208082528101620003ca81620005de565b60208082528101620003ca8162000634565b6040518181016001604060020a0381118282101715620006e157600080fd5b604052919050565b60006001604060020a038211156200070057600080fd5b506020601f91909101601f19160190565b600160a060020a031690565b7fffffffff000000000000000000000000000000000000000000000000000000001690565b151590565b60005b83811015620007645781810151838201526020016200074a565b8381111562000774576000848401525b50505050565b6125a2806200078a6000396000f30060806040526004361061006c5763ffffffff7c010000000000000000000000000000000000000000000000000000000060003504166318978e8281146100c8578063630f1e6c146100f25780638da5cb5b146101125780639395525c14610134578063f2fde38b14610147575b60025473ffffffffffffffffffffffffffffffffffffffff1633146100c6576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016100bd90612388565b60405180910390fd5b005b6100db6100d6366004611df1565b610167565b6040516100e9929190612488565b60405180910390f35b3480156100fe57600080fd5b506100c661010d366004611eec565b6102f7565b34801561011e57600080fd5b50610127610388565b6040516100e99190612337565b6100db610142366004611d0b565b6103a4565b34801561015357600080fd5b506100c6610162366004611ce5565b61050a565b61016f6119fa565b6101776119fa565b6000806101826105bb565b60048054604080516020601f60027fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff610100600188161502019095169490940493840181900481028201810190925282815261025c939092909183018282801561022d5780601f106102025761010080835404028352916020019161022d565b820191906000526020600020905b81548152906001019060200180831161021057829003601f168201915b50505050508c600081518110151561024157fe5b6020908102909101015161014001519063ffffffff61069616565b156102875761026c8b8b8b6107c3565b935061028084600001518560600151610ac1565b90506102ae565b6102928b8b8b610b03565b9350836060015191506102a68883896107c3565b845190935090505b6102c2846020015184602001518888610d15565b6102e98b60008151811015156102d457fe5b90602001906020020151610140015182610f29565b505097509795505050505050565b60005473ffffffffffffffffffffffffffffffffffffffff163314610348576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016100bd90612438565b61038383838080601f01602080910402602001604051908101604052809392919081815260200183838082843750879450610f299350505050565b505050565b60005473ffffffffffffffffffffffffffffffffffffffff1681565b6103ac6119fa565b6103b46119fa565b60008060006103c16105bb565b60048054604080516020601f60027fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff6101006001881615020190951694909404938401819004810282018101909252828152610441939092909183018282801561022d5780601f106102025761010080835404028352916020019161022d565b156104925761046a670de0b6b3a7640000610464670de0b6b3a76400008a611045565b3461108f565b92506104778b848c6110e7565b945061048b85600001518660600151610ac1565b90506104d6565b6104ad670d2f13f7789f0000670de0b6b3a76400003461108f565b92506104ba8b848c6110e7565b9450846060015191506104ce89838a6107c3565b855190945090505b6104ea856020015185602001518989610d15565b6104fc8b60008151811015156102d457fe5b505050965096945050505050565b60005473ffffffffffffffffffffffffffffffffffffffff16331461055b576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016100bd90612438565b73ffffffffffffffffffffffffffffffffffffffff8116156105b857600080547fffffffffffffffffffffffff00000000000000000000000000000000000000001673ffffffffffffffffffffffffffffffffffffffff83161790555b50565b600034116105f5576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016100bd90612398565b600260009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1663d0e30db0346040518263ffffffff167c01000000000000000000000000000000000000000000000000000000000281526004016000604051808303818588803b15801561067b57600080fd5b505af115801561068f573d6000803e3d6000fd5b5050505050565b6000815183511480156107ba5750816040518082805190602001908083835b602083106106f257805182527fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe090920191602091820191016106b5565b51815160209384036101000a7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0180199092169116179052604051919093018190038120885190955088945090928392508401908083835b6020831061078757805182527fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0909201916020918201910161074a565b6001836020036101000a038019825116818451168082178552505050505050905001915050604051809103902060001916145b90505b92915050565b6107cb6119fa565b60608060008060008060006107de6119fa565b8a15156107ea57610ab2565b6004805460408051602060026001851615610100027fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0190941693909304601f8101849004840282018401909252818152929183018282801561088e5780601f106108635761010080835404028352916020019161088e565b820191906000526020600020905b81548152906001019060200180831161087157829003601f168201915b505060058054604080516020601f60027fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff6101006001881615020190951694909404938401819004810282018101909252828152969e509194509250840190508282801561093d5780601f106109125761010080835404028352916020019161093d565b820191906000526020600020905b81548152906001019060200180831161092057829003601f168201915b50505050509650600095508b519450600093505b838514610a7857878c8581518110151561096757fe5b6020908102909101015161014001528b5187908d908690811061098657fe5b60209081029091010151610160015261099f8b87610ac1565b9250610a068c858151811015156109b257fe5b9060200190602002015160a00151610a008e878151811015156109d157fe5b90602001906020020151608001518f888151811015156109ed57fe5b9060200190602002015160e00151610ac1565b8561128b565b9150610a418c85815181101515610a1957fe5b90602001906020020151838c87815181101515610a3257fe5b906020019060200201516112e6565b9050610a4d898261135e565b610a5f89600001518a60600151610ac1565b95508a8610610a6d57610a78565b600190930192610951565b8a861015610ab2576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016100bd90612418565b50505050505050509392505050565b600082821115610afd576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016100bd906123b8565b50900390565b610b0b6119fa565b606080600080600080610b1c6119fa565b60008b6000815181101515610b2d57fe5b6020908102919091018101516101400151600580546040805160026001841615610100027fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0190931692909204601f8101869004860283018601909152808252929b5092909190830182828015610be55780601f10610bba57610100808354040283529160200191610be5565b820191906000526020600020905b815481529060010190602001808311610bc857829003601f168201915b505050505096508b519550600094505b848614610cdb57878c86815181101515610c0b57fe5b6020908102909101015161014001528b5187908d9087908110610c2a57fe5b6020908102909101015161016001528851610c46908c90610ac1565b9350610c898c86815181101515610c5957fe5b9060200190602002015160a001518d87815181101515610c7557fe5b90602001906020020151608001518661128b565b9250610cb58c86815181101515610c9c57fe5b90602001906020020151848c88815181101515610a3257fe5b9150610cc1898361135e565b5087518a8110610cd057610cdb565b600190940193610bf5565b8a811015610ab2576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016100bd90612418565b600080808066b1a2bc2ec50000861115610d5b576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016100bd90612448565b610d658888611045565b935034841115610da1576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016100bd906123a8565b610dab3485610ac1565b9250610dc086670de0b6b3a76400008a61108f565b915082821115610dfc576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016100bd90612428565b6000831115610f1f576002546040517f2e1a7d4d00000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff90911690632e1a7d4d90610e5b9086906004016124a4565b600060405180830381600087803b158015610e7557600080fd5b505af1158015610e89573d6000803e3d6000fd5b505050506000821115610edb5760405173ffffffffffffffffffffffffffffffffffffffff86169083156108fc029084906000818181858888f19350505050158015610ed9573d6000803e3d6000fd5b505b610ee58383610ac1565b90506000811115610f1f57604051339082156108fc029083906000818181858888f19350505050158015610f1d573d6000803e3d6000fd5b505b5050505050505050565b6000610f3b838263ffffffff6113c016565b604080517f4552433230546f6b656e28616464726573732900000000000000000000000000815290519081900360130190209091507fffffffff0000000000000000000000000000000000000000000000000000000080831691161415610fab57610fa6838361142d565b610383565b604080517f455243373231546f6b656e28616464726573732c75696e7432353629000000008152905190819003601c0190207fffffffff000000000000000000000000000000000000000000000000000000008281169116141561101357610fa6838361161b565b6040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016100bd906123f8565b600082820183811015611084576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016100bd906123e8565b8091505b5092915050565b60008083116110ca576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016100bd906123d8565b6110dd6110d78584611703565b8461175e565b90505b9392505050565b6110ef6119fa565b60608060008060006110ff6119fa565b89600081518110151561110e57fe5b6020908102919091018101516101400151600580546040805160026001841615610100027fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0190931692909204601f8101869004860283018601909152808252929950929091908301828280156111c65780601f1061119b576101008083540402835291602001916111c6565b820191906000526020600020905b8154815290600101906020018083116111a957829003601f168201915b5050505050945089519350600092505b82841461127e57858a848151811015156111ec57fe5b602090810290910101516101400152895185908b908590811061120b57fe5b90602001906020020151610160018190525061122b898860200151610ac1565b91506112578a8481518110151561123e57fe5b90602001906020020151838a86815181101515610a3257fe5b9050611263878261135e565b602087015189116112735761127e565b6001909201916111d6565b5050505050509392505050565b60008083116112c6576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016100bd906123d8565b6110dd6110d76112d68685611703565b6112e1866001610ac1565b611045565b6112ee6119fa565b606060006112fd868686611775565b600154815191935073ffffffffffffffffffffffffffffffffffffffff1691506080908390602082016000855af1801561135457825184526020830151602085015260408301516040850152606083015160608501525b5050509392505050565b8151815161136c9190611045565b8252602080830151908201516113829190611045565b60208301526040808301519082015161139b9190611045565b6040830152606080830151908201516113b49190611045565b60609092019190915250565b600081600401835110151515611402576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016100bd90612468565b5001602001517fffffffff000000000000000000000000000000000000000000000000000000001690565b60008061144184601063ffffffff61194716565b604080517f7472616e7366657228616464726573732c75696e7432353629000000000000008152905190819003601901812091935073ffffffffffffffffffffffffffffffffffffffff8416919061149f903390879060240161236d565b604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08184030181529181526020820180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167fffffffff000000000000000000000000000000000000000000000000000000009094169390931783525181519192909182919080838360005b8381101561154357818101518382015260200161152b565b50505050905090810190601f1680156115705780820380516001836020036101000a031916815260200191505b509150506000604051808303816000865af1925050508015156115bf576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016100bd90612408565b3d156115dc575060003d602014156115dc5760206000803e506000515b801515611615576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016100bd90612408565b50505050565b60008060018314611658576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016100bd90612478565b61166984601063ffffffff61194716565b915061167c84602463ffffffff6119a816565b6040517f23b872dd00000000000000000000000000000000000000000000000000000000815290915073ffffffffffffffffffffffffffffffffffffffff8316906323b872dd906116d590309033908690600401612345565b600060405180830381600087803b1580156116ef57600080fd5b505af1158015610f1f573d6000803e3d6000fd5b6000808315156117165760009150611088565b5082820282848281151561172657fe5b0414611084576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016100bd906123e8565b600080828481151561176c57fe5b04949350505050565b604080517fb4be83d5000000000000000000000000000000000000000000000000000000006020808301919091526060602483018181528751608485019081528884015160a48601529488015160c48501529087015160e4840152608087015161010484015260a087015161012484015260c087015161014484015260e08701516101648401526101008701516101848401526101208701516101a4840152610140870180516101c485019081526101608901516101e4860152610180905251805161020485018190529394919384936044870192849261022489019291820191601f82010460005b8181101561187c57835185526020948501949093019260010161185e565b50505050818103610160808401919091528a0151805180835260209283019291820191601f82010460005b818110156118c55783518552602094850194909301926001016118a7565b50505089845250848103602093840190815288518083529093918201918981019190601f82010460005b8181101561190d5783518552602094850194909301926001016118ef565b5050507fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08883030188525060405250505050509392505050565b600081601401835110151515611989576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016100bd90612458565b50016014015173ffffffffffffffffffffffffffffffffffffffff1690565b60006107ba83836000816020018351101515156119f1576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016100bd906123c8565b50016020015190565b608060405190810160405280600081526020016000815260200160008152602001600081525090565b60006107ba8235612540565b6000601f82018313611a4057600080fd5b8135611a53611a4e826124d9565b6124b2565b81815260209384019390925082018360005b83811015611a915781358601611a7b8882611b41565b8452506020928301929190910190600101611a65565b5050505092915050565b6000601f82018313611aac57600080fd5b8135611aba611a4e826124d9565b81815260209384019390925082018360005b83811015611a915781358601611ae28882611b90565b8452506020928301929190910190600101611acc565b600080601f83018413611b0a57600080fd5b50813567ffffffffffffffff811115611b2257600080fd5b602083019150836001820283011115611b3a57600080fd5b9250929050565b6000601f82018313611b5257600080fd5b8135611b60611a4e826124fa565b91508082526020830160208301858383011115611b7c57600080fd5b611b8783828461255c565b50505092915050565b60006101808284031215611ba357600080fd5b611bae6101806124b2565b90506000611bbc8484611a23565b8252506020611bcd84848301611a23565b6020830152506040611be184828501611a23565b6040830152506060611bf584828501611a23565b6060830152506080611c0984828501611cd9565b60808301525060a0611c1d84828501611cd9565b60a08301525060c0611c3184828501611cd9565b60c08301525060e0611c4584828501611cd9565b60e083015250610100611c5a84828501611cd9565b61010083015250610120611c7084828501611cd9565b6101208301525061014082013567ffffffffffffffff811115611c9257600080fd5b611c9e84828501611b41565b6101408301525061016082013567ffffffffffffffff811115611cc057600080fd5b611ccc84828501611b41565b6101608301525092915050565b60006107ba8235612559565b600060208284031215611cf757600080fd5b6000611d038484611a23565b949350505050565b60008060008060008060c08789031215611d2457600080fd5b863567ffffffffffffffff811115611d3b57600080fd5b611d4789828a01611a9b565b965050602087013567ffffffffffffffff811115611d6457600080fd5b611d7089828a01611a2f565b955050604087013567ffffffffffffffff811115611d8d57600080fd5b611d9989828a01611a9b565b945050606087013567ffffffffffffffff811115611db657600080fd5b611dc289828a01611a2f565b9350506080611dd389828a01611cd9565b92505060a0611de489828a01611a23565b9150509295509295509295565b600080600080600080600060e0888a031215611e0c57600080fd5b873567ffffffffffffffff811115611e2357600080fd5b611e2f8a828b01611a9b565b9750506020611e408a828b01611cd9565b965050604088013567ffffffffffffffff811115611e5d57600080fd5b611e698a828b01611a2f565b955050606088013567ffffffffffffffff811115611e8657600080fd5b611e928a828b01611a9b565b945050608088013567ffffffffffffffff811115611eaf57600080fd5b611ebb8a828b01611a2f565b93505060a0611ecc8a828b01611cd9565b92505060c0611edd8a828b01611a23565b91505092959891949750929550565b600080600060408486031215611f0157600080fd5b833567ffffffffffffffff811115611f1857600080fd5b611f2486828701611af8565b93509350506020611f3786828701611cd9565b9150509250925092565b611f4a81612540565b82525050565b602381527f44454641554c545f46554e4354494f4e5f574554485f434f4e54524143545f4f60208201527f4e4c590000000000000000000000000000000000000000000000000000000000604082015260600190565b601181527f494e56414c49445f4d53475f56414c5545000000000000000000000000000000602082015260400190565b600d81527f4f564552534f4c445f5745544800000000000000000000000000000000000000602082015260400190565b601181527f55494e543235365f554e444552464c4f57000000000000000000000000000000602082015260400190565b602681527f475245415445525f4f525f455155414c5f544f5f33325f4c454e4754485f524560208201527f5155495245440000000000000000000000000000000000000000000000000000604082015260600190565b601081527f4449564953494f4e5f42595f5a45524f00000000000000000000000000000000602082015260400190565b601081527f55494e543235365f4f564552464c4f5700000000000000000000000000000000602082015260400190565b601781527f554e535550504f525445445f41535345545f50524f5859000000000000000000602082015260400190565b600f81527f5452414e534645525f4641494c45440000000000000000000000000000000000602082015260400190565b601481527f434f4d504c4554455f46494c4c5f4641494c4544000000000000000000000000602082015260400190565b601a81527f494e53554646494349454e545f4554485f52454d41494e494e47000000000000602082015260400190565b601381527f4f4e4c595f434f4e54524143545f4f574e455200000000000000000000000000602082015260400190565b601881527f4645455f50455243454e544147455f544f4f5f4c415247450000000000000000602082015260400190565b602681527f475245415445525f4f525f455155414c5f544f5f32305f4c454e4754485f524560208201527f5155495245440000000000000000000000000000000000000000000000000000604082015260600190565b602581527f475245415445525f4f525f455155414c5f544f5f345f4c454e4754485f52455160208201527f5549524544000000000000000000000000000000000000000000000000000000604082015260600190565b600e81527f494e56414c49445f414d4f554e54000000000000000000000000000000000000602082015260400190565b805160808301906122f9848261232e565b50602082015161230c602085018261232e565b50604082015161231f604085018261232e565b50606082015161161560608501825b611f4a81612559565b602081016107bd8284611f41565b606081016123538286611f41565b6123606020830185611f41565b611d03604083018461232e565b6040810161237b8285611f41565b6110e0602083018461232e565b602080825281016107bd81611f50565b602080825281016107bd81611fa6565b602080825281016107bd81611fd6565b602080825281016107bd81612006565b602080825281016107bd81612036565b602080825281016107bd8161208c565b602080825281016107bd816120bc565b602080825281016107bd816120ec565b602080825281016107bd8161211c565b602080825281016107bd8161214c565b602080825281016107bd8161217c565b602080825281016107bd816121ac565b602080825281016107bd816121dc565b602080825281016107bd8161220c565b602080825281016107bd81612262565b602080825281016107bd816122b8565b610100810161249782856122e8565b6110e060808301846122e8565b602081016107bd828461232e565b60405181810167ffffffffffffffff811182821017156124d157600080fd5b604052919050565b600067ffffffffffffffff8211156124f057600080fd5b5060209081020190565b600067ffffffffffffffff82111561251157600080fd5b506020601f919091017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0160190565b73ffffffffffffffffffffffffffffffffffffffff1690565b90565b828183375060009101525600a265627a7a72305820d9f418f11e0f91f06f6f9d22924be0add925495eeb76a6388b5417adb505eeb36c6578706572696d656e74616cf5003700000000000000000000000048bacb9266a570d521063ef5dd96e61686dbe788000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000c00000000000000000000000000000000000000000000000000000000000000024f47261b0000000000000000000000000871dd7c2b4b25e1aa18728e9d5f2af4c4e431f5c000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000024f47261b00000000000000000000000000b1ba0af832d7c05fd64161e0db78e85978e808200000000000000000000000000000000000000000000000000000000", @@ -185,4 +185,4 @@ "type": "call" } ] -} \ No newline at end of file +} diff --git a/eth/tracers/internal/tracetest/testdata/call_tracer_flat/result_output.json b/eth/tracers/internal/tracetest/testdata/call_tracer_flat/result_output.json index f6215a2b3f..544fff9dd3 100644 --- a/eth/tracers/internal/tracetest/testdata/call_tracer_flat/result_output.json +++ b/eth/tracers/internal/tracetest/testdata/call_tracer_flat/result_output.json @@ -80,8 +80,7 @@ "transactionPosition": 5, "transactionHash": "0x04d2029a5cbbed30969cdc0a2ca9e9fc6b719e323af0802b52466f07ee0ecada", "blockNumber": 553416, - "blockHash": "0x8df024322173d225a09681d35edeaa528aca60743a11a70f854c158862bf5282", - "time": "617.42µs" + "blockHash": "0x8df024322173d225a09681d35edeaa528aca60743a11a70f854c158862bf5282" }, { "type": "call", @@ -97,9 +96,7 @@ "gasUsed": "0x0", "output": "0x" }, - "traceAddress": [ - 0 - ], + "traceAddress": [0], "subtraces": 0, "transactionPosition": 5, "transactionHash": "0x04d2029a5cbbed30969cdc0a2ca9e9fc6b719e323af0802b52466f07ee0ecada", @@ -107,4 +104,4 @@ "blockHash": "0x8df024322173d225a09681d35edeaa528aca60743a11a70f854c158862bf5282" } ] -} \ No newline at end of file +} diff --git a/eth/tracers/internal/tracetest/testdata/call_tracer_flat/selfdestruct.json b/eth/tracers/internal/tracetest/testdata/call_tracer_flat/selfdestruct.json index c8c6dc65c1..d7b4a22cf5 100644 --- a/eth/tracers/internal/tracetest/testdata/call_tracer_flat/selfdestruct.json +++ b/eth/tracers/internal/tracetest/testdata/call_tracer_flat/selfdestruct.json @@ -51,6 +51,7 @@ { "type": "create", "action": { + "creationMethod": "create", "from": "0x877bd459c9b7d8576b44e59e09d076c25946f443", "value": "0x0", "gas": "0x19ecc", @@ -66,11 +67,11 @@ "transactionPosition": 14, "transactionHash": "0xdd76f02407e2f8329303ba688e111cae4f7008ad0d14d6e42c5698424ea36d79", "blockNumber": 1555146, - "blockHash": "0xafb4f1dd27b9054c805acb81a88ed04384788cb31d84164c21874935c81e5c7e", - "time": "187.145µs" + "blockHash": "0xafb4f1dd27b9054c805acb81a88ed04384788cb31d84164c21874935c81e5c7e" }, { "action": { + "creationMethod": "create", "from": "0x1d99a1a3efa9181f540f9e24fa6e4e08eb7844ca", "gas": "0x50ac", "init": "0x5a", @@ -90,9 +91,7 @@ "balance": "0x0" }, "result": null, - "traceAddress": [ - 1 - ], + "traceAddress": [1], "subtraces": 0, "transactionPosition": 14, "transactionHash": "0xdd76f02407e2f8329303ba688e111cae4f7008ad0d14d6e42c5698424ea36d79", @@ -100,4 +99,4 @@ "blockHash": "0xafb4f1dd27b9054c805acb81a88ed04384788cb31d84164c21874935c81e5c7e" } ] -} \ No newline at end of file +} diff --git a/eth/tracers/internal/tracetest/testdata/call_tracer_flat/skip_no_balance_error.json b/eth/tracers/internal/tracetest/testdata/call_tracer_flat/skip_no_balance_error.json index a25f2383d6..6e020fe2b7 100644 --- a/eth/tracers/internal/tracetest/testdata/call_tracer_flat/skip_no_balance_error.json +++ b/eth/tracers/internal/tracetest/testdata/call_tracer_flat/skip_no_balance_error.json @@ -51,6 +51,7 @@ { "type": "create", "action": { + "creationMethod": "create", "from": "0x877bd459c9b7d8576b44e59e09d076c25946f443", "value": "0x0", "gas": "0x1aab0", @@ -62,11 +63,11 @@ "transactionPosition": 16, "transactionHash": "0x384487e5ae8d2997aece8e28403d393cb9752425e6de358891bed981c5af1c05", "blockNumber": 1555285, - "blockHash": "0x93231d8e9662adb4c5c703583a92c7b3112cd5448f43ab4fa1f0f00a0183ed3f", - "time": "665.278µs" + "blockHash": "0x93231d8e9662adb4c5c703583a92c7b3112cd5448f43ab4fa1f0f00a0183ed3f" }, { "action": { + "creationMethod": "create", "from": "0xf84bf5189ccd19f5897739756d214fa0dc099e0d", "gas": "0x1d5c", "init": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", @@ -79,4 +80,4 @@ "type": "create" } ] -} \ No newline at end of file +} diff --git a/eth/tracers/internal/tracetest/testdata/call_tracer_withLog/calldata.json b/eth/tracers/internal/tracetest/testdata/call_tracer_withLog/calldata.json index cef0c1c5a8..30991edafb 100644 --- a/eth/tracers/internal/tracetest/testdata/call_tracer_withLog/calldata.json +++ b/eth/tracers/internal/tracetest/testdata/call_tracer_withLog/calldata.json @@ -59,7 +59,6 @@ "londonBlock": 12965000, "arrowGlacierBlock": 13773000, "grayGlacierBlock": 15050000, - "terminalTotalDifficultyPassed": true, "ethash": {} } }, diff --git a/eth/tracers/internal/tracetest/testdata/call_tracer_withLog/delegatecall.json b/eth/tracers/internal/tracetest/testdata/call_tracer_withLog/delegatecall.json index 0ae76e92f9..cdb0dda5f8 100644 --- a/eth/tracers/internal/tracetest/testdata/call_tracer_withLog/delegatecall.json +++ b/eth/tracers/internal/tracetest/testdata/call_tracer_withLog/delegatecall.json @@ -116,7 +116,6 @@ "londonBlock": 12965000, "arrowGlacierBlock": 13773000, "grayGlacierBlock": 15050000, - "terminalTotalDifficultyPassed": true, "ethash": {} } }, diff --git a/eth/tracers/internal/tracetest/testdata/call_tracer_withLog/frontier_create_outofstorage.json b/eth/tracers/internal/tracetest/testdata/call_tracer_withLog/frontier_create_outofstorage.json index 049f24d932..e562affb15 100644 --- a/eth/tracers/internal/tracetest/testdata/call_tracer_withLog/frontier_create_outofstorage.json +++ b/eth/tracers/internal/tracetest/testdata/call_tracer_withLog/frontier_create_outofstorage.json @@ -60,7 +60,6 @@ "grayGlacierBlock": 15050000, "shanghaiTime": 1681338455, "terminalTotalDifficulty": 7797655526461000, - "terminalTotalDifficultyPassed": true, "ethash": {} } }, @@ -186,4 +185,4 @@ "value": "0x0", "type": "CREATE" } -} \ No newline at end of file +} diff --git a/eth/tracers/internal/tracetest/testdata/call_tracer_withLog/multi_contracts.json b/eth/tracers/internal/tracetest/testdata/call_tracer_withLog/multi_contracts.json index 5cc3013f80..43d6be2be9 100644 --- a/eth/tracers/internal/tracetest/testdata/call_tracer_withLog/multi_contracts.json +++ b/eth/tracers/internal/tracetest/testdata/call_tracer_withLog/multi_contracts.json @@ -281,7 +281,6 @@ "londonBlock": 12965000, "arrowGlacierBlock": 13773000, "grayGlacierBlock": 15050000, - "terminalTotalDifficultyPassed": true, "ethash": {} } }, diff --git a/eth/tracers/internal/tracetest/testdata/call_tracer_withLog/multilogs.json b/eth/tracers/internal/tracetest/testdata/call_tracer_withLog/multilogs.json index c931ed63fc..3434dd3103 100644 --- a/eth/tracers/internal/tracetest/testdata/call_tracer_withLog/multilogs.json +++ b/eth/tracers/internal/tracetest/testdata/call_tracer_withLog/multilogs.json @@ -149,7 +149,6 @@ "londonBlock": 12965000, "arrowGlacierBlock": 13773000, "grayGlacierBlock": 15050000, - "terminalTotalDifficultyPassed": true, "ethash": {} } }, diff --git a/eth/tracers/internal/tracetest/testdata/call_tracer_withLog/notopic.json b/eth/tracers/internal/tracetest/testdata/call_tracer_withLog/notopic.json index 42fc872554..814189dc6b 100644 --- a/eth/tracers/internal/tracetest/testdata/call_tracer_withLog/notopic.json +++ b/eth/tracers/internal/tracetest/testdata/call_tracer_withLog/notopic.json @@ -84,7 +84,6 @@ "londonBlock": 12965000, "arrowGlacierBlock": 13773000, "grayGlacierBlock": 15050000, - "terminalTotalDifficultyPassed": true, "ethash": {} } }, diff --git a/eth/tracers/internal/tracetest/testdata/call_tracer_withLog/simple.json b/eth/tracers/internal/tracetest/testdata/call_tracer_withLog/simple.json index 5300d341b8..c00f083159 100644 --- a/eth/tracers/internal/tracetest/testdata/call_tracer_withLog/simple.json +++ b/eth/tracers/internal/tracetest/testdata/call_tracer_withLog/simple.json @@ -45,7 +45,6 @@ "londonBlock": 12965000, "arrowGlacierBlock": 13773000, "grayGlacierBlock": 15050000, - "terminalTotalDifficultyPassed": true, "ethash": {} } }, diff --git a/eth/tracers/internal/tracetest/testdata/call_tracer_withLog/tx_failed.json b/eth/tracers/internal/tracetest/testdata/call_tracer_withLog/tx_failed.json index d5c2f864c1..293575a989 100644 --- a/eth/tracers/internal/tracetest/testdata/call_tracer_withLog/tx_failed.json +++ b/eth/tracers/internal/tracetest/testdata/call_tracer_withLog/tx_failed.json @@ -119,7 +119,6 @@ "londonBlock": 12965000, "arrowGlacierBlock": 13773000, "grayGlacierBlock": 15050000, - "terminalTotalDifficultyPassed": true, "ethash": {} } }, diff --git a/eth/tracers/internal/tracetest/testdata/call_tracer_withLog/tx_partial_failed.json b/eth/tracers/internal/tracetest/testdata/call_tracer_withLog/tx_partial_failed.json index 1267160d13..aba652ede6 100644 --- a/eth/tracers/internal/tracetest/testdata/call_tracer_withLog/tx_partial_failed.json +++ b/eth/tracers/internal/tracetest/testdata/call_tracer_withLog/tx_partial_failed.json @@ -57,7 +57,6 @@ "londonBlock": 12965000, "arrowGlacierBlock": 13773000, "grayGlacierBlock": 15050000, - "terminalTotalDifficultyPassed": true, "ethash": {} } }, diff --git a/eth/tracers/internal/tracetest/testdata/call_tracer_withLog/with_onlyTopCall.json b/eth/tracers/internal/tracetest/testdata/call_tracer_withLog/with_onlyTopCall.json index 38d96a6bd0..ed518faf5e 100644 --- a/eth/tracers/internal/tracetest/testdata/call_tracer_withLog/with_onlyTopCall.json +++ b/eth/tracers/internal/tracetest/testdata/call_tracer_withLog/with_onlyTopCall.json @@ -59,7 +59,6 @@ "londonBlock": 12965000, "arrowGlacierBlock": 13773000, "grayGlacierBlock": 15050000, - "terminalTotalDifficultyPassed": true, "ethash": {} } }, diff --git a/eth/tracers/internal/tracetest/testdata/prestate_tracer/blob_tx.json b/eth/tracers/internal/tracetest/testdata/prestate_tracer/blob_tx.json index 444eba450b..f8adbabf63 100644 --- a/eth/tracers/internal/tracetest/testdata/prestate_tracer/blob_tx.json +++ b/eth/tracers/internal/tracetest/testdata/prestate_tracer/blob_tx.json @@ -41,8 +41,7 @@ "grayGlacierBlock": 0, "shanghaiTime": 0, "cancunTime": 0, - "terminalTotalDifficulty": 0, - "terminalTotalDifficultyPassed": true + "terminalTotalDifficulty": 0 } }, "context": { diff --git a/eth/tracers/internal/tracetest/testdata/prestate_tracer/create_create.json b/eth/tracers/internal/tracetest/testdata/prestate_tracer/create_create.json index b013d520c1..489a1ae6b5 100644 --- a/eth/tracers/internal/tracetest/testdata/prestate_tracer/create_create.json +++ b/eth/tracers/internal/tracetest/testdata/prestate_tracer/create_create.json @@ -38,7 +38,6 @@ "grayGlacierBlock": 0, "shanghaiTime": 0, "terminalTotalDifficulty": 0, - "terminalTotalDifficultyPassed": true, "isDev": true } }, @@ -59,4 +58,4 @@ "balance": "0x8ac7230489e80000" } } -} \ No newline at end of file +} diff --git a/eth/tracers/internal/tracetest/testdata/prestate_tracer/create_existing_contract.json b/eth/tracers/internal/tracetest/testdata/prestate_tracer/create_existing_contract.json index 64a3b16cb1..abbc104f32 100644 --- a/eth/tracers/internal/tracetest/testdata/prestate_tracer/create_existing_contract.json +++ b/eth/tracers/internal/tracetest/testdata/prestate_tracer/create_existing_contract.json @@ -49,7 +49,6 @@ "londonBlock": 12965000, "arrowGlacierBlock": 13773000, "grayGlacierBlock": 15050000, - "terminalTotalDifficultyPassed": true, "ethash": {} } }, diff --git a/eth/tracers/internal/tracetest/testdata/prestate_tracer/disable_code.json b/eth/tracers/internal/tracetest/testdata/prestate_tracer/disable_code.json new file mode 100644 index 0000000000..d60c3d7385 --- /dev/null +++ b/eth/tracers/internal/tracetest/testdata/prestate_tracer/disable_code.json @@ -0,0 +1,83 @@ +{ + "context": { + "difficulty": "3502894804", + "gasLimit": "4722976", + "miner": "0x1585936b53834b021f68cc13eeefdec2efc8e724", + "number": "2289806", + "timestamp": "1513601314" + }, + "genesis": { + "alloc": { + "0x0024f658a46fbb89d8ac105e98d7ac7cbbaf27c5": { + "balance": "0x0", + "code": "0x", + "nonce": "22", + "storage": {} + }, + "0x3b873a919aa0512d5a0f09e6dcceaa4a6727fafe": { + "balance": "0x4d87094125a369d9bd5", + "code": "0x606060405236156100935763ffffffff60e060020a60003504166311ee8382811461009c57806313af4035146100be5780631f5e8f4c146100ee57806324daddc5146101125780634921a91a1461013b57806363e4bff414610157578063764978f91461017f578063893d20e8146101a1578063ba40aaa1146101cd578063cebc9a82146101f4578063e177246e14610216575b61009a5b5b565b005b34156100a457fe5b6100ac61023d565b60408051918252519081900360200190f35b34156100c657fe5b6100da600160a060020a0360043516610244565b604080519115158252519081900360200190f35b34156100f657fe5b6100da610307565b604080519115158252519081900360200190f35b341561011a57fe5b6100da6004351515610318565b604080519115158252519081900360200190f35b6100da6103d6565b604080519115158252519081900360200190f35b6100da600160a060020a0360043516610420565b604080519115158252519081900360200190f35b341561018757fe5b6100ac61046c565b60408051918252519081900360200190f35b34156101a957fe5b6101b1610473565b60408051600160a060020a039092168252519081900360200190f35b34156101d557fe5b6100da600435610483565b604080519115158252519081900360200190f35b34156101fc57fe5b6100ac61050d565b60408051918252519081900360200190f35b341561021e57fe5b6100da600435610514565b604080519115158252519081900360200190f35b6003545b90565b60006000610250610473565b600160a060020a031633600160a060020a03161415156102705760006000fd5b600160a060020a03831615156102865760006000fd5b50600054600160a060020a0390811690831681146102fb57604051600160a060020a0380851691908316907ffcf23a92150d56e85e3a3d33b357493246e55783095eb6a733eb8439ffc752c890600090a360008054600160a060020a031916600160a060020a03851617905560019150610300565b600091505b5b50919050565b60005460a060020a900460ff165b90565b60006000610324610473565b600160a060020a031633600160a060020a03161415156103445760006000fd5b5060005460a060020a900460ff16801515831515146102fb576000546040805160a060020a90920460ff1615158252841515602083015280517fe6cd46a119083b86efc6884b970bfa30c1708f53ba57b86716f15b2f4551a9539281900390910190a16000805460a060020a60ff02191660a060020a8515150217905560019150610300565b600091505b5b50919050565b60006103e0610307565b801561040557506103ef610473565b600160a060020a031633600160a060020a031614155b156104105760006000fd5b610419336105a0565b90505b5b90565b600061042a610307565b801561044f5750610439610473565b600160a060020a031633600160a060020a031614155b1561045a5760006000fd5b610463826105a0565b90505b5b919050565b6001545b90565b600054600160a060020a03165b90565b6000600061048f610473565b600160a060020a031633600160a060020a03161415156104af5760006000fd5b506001548281146102fb57604080518281526020810185905281517f79a3746dde45672c9e8ab3644b8bb9c399a103da2dc94b56ba09777330a83509929181900390910190a160018381559150610300565b600091505b5b50919050565b6002545b90565b60006000610520610473565b600160a060020a031633600160a060020a03161415156105405760006000fd5b506002548281146102fb57604080518281526020810185905281517ff6991a728965fedd6e927fdf16bdad42d8995970b4b31b8a2bf88767516e2494929181900390910190a1600283905560019150610300565b600091505b5b50919050565b60006000426105ad61023d565b116102fb576105c46105bd61050d565b4201610652565b6105cc61046c565b604051909150600160a060020a038416908290600081818185876187965a03f1925050501561063d57604080518281529051600160a060020a038516917f9bca65ce52fdef8a470977b51f247a2295123a4807dfa9e502edf0d30722da3b919081900360200190a260019150610300565b6102fb42610652565b5b600091505b50919050565b60038190555b505600a165627a7a72305820f3c973c8b7ed1f62000b6701bd5b708469e19d0f1d73fde378a56c07fd0b19090029", + "nonce": "1", + "storage": { + "0x0000000000000000000000000000000000000000000000000000000000000000": "0x000000000000000000000001b436ba50d378d4bbc8660d312a13df6af6e89dfb", + "0x0000000000000000000000000000000000000000000000000000000000000001": "0x00000000000000000000000000000000000000000000000006f05b59d3b20000", + "0x0000000000000000000000000000000000000000000000000000000000000002": "0x000000000000000000000000000000000000000000000000000000000000003c", + "0x0000000000000000000000000000000000000000000000000000000000000003": "0x000000000000000000000000000000000000000000000000000000005a37b834" + } + }, + "0xb436ba50d378d4bbc8660d312a13df6af6e89dfb": { + "balance": "0x1780d77678137ac1b775", + "code": "0x", + "nonce": "29072", + "storage": {} + } + }, + "config": { + "byzantiumBlock": 1700000, + "chainId": 3, + "eip150Block": 0, + "eip155Block": 10, + "eip158Block": 10, + "ethash": {}, + "homesteadBlock": 0 + }, + "difficulty": "3509749784", + "extraData": "0x4554482e45544846414e532e4f52472d4641313738394444", + "gasLimit": "4727564", + "hash": "0x609948ac3bd3c00b7736b933248891d6c901ee28f066241bddb28f4e00a9f440", + "miner": "0xbbf5029fd710d227630c8b7d338051b8e76d50b3", + "mixHash": "0xb131e4507c93c7377de00e7c271bf409ec7492767142ff0f45c882f8068c2ada", + "nonce": "0x4eb12e19c16d43da", + "number": "2289805", + "stateRoot": "0xc7f10f352bff82fac3c2999d3085093d12652e19c7fd32591de49dc5d91b4f1f", + "timestamp": "1513601261", + "totalDifficulty": "7143276353481064" + }, + "input": "0xf88b8271908506fc23ac0083015f90943b873a919aa0512d5a0f09e6dcceaa4a6727fafe80a463e4bff40000000000000000000000000024f658a46fbb89d8ac105e98d7ac7cbbaf27c52aa0bdce0b59e8761854e857fe64015f06dd08a4fbb7624f6094893a79a72e6ad6bea01d9dde033cff7bb235a3163f348a6d7ab8d6b52bc0963a95b91612e40ca766a4", + "tracerConfig": { + "disableCode": true + }, + "result": { + "0x0024f658a46fbb89d8ac105e98d7ac7cbbaf27c5": { + "balance": "0x0", + "nonce": 22 + }, + "0x3b873a919aa0512d5a0f09e6dcceaa4a6727fafe": { + "balance": "0x4d87094125a369d9bd5", + "nonce": 1, + "storage": { + "0x0000000000000000000000000000000000000000000000000000000000000000": "0x000000000000000000000001b436ba50d378d4bbc8660d312a13df6af6e89dfb", + "0x0000000000000000000000000000000000000000000000000000000000000001": "0x00000000000000000000000000000000000000000000000006f05b59d3b20000", + "0x0000000000000000000000000000000000000000000000000000000000000002": "0x000000000000000000000000000000000000000000000000000000000000003c", + "0x0000000000000000000000000000000000000000000000000000000000000003": "0x000000000000000000000000000000000000000000000000000000005a37b834" + } + }, + "0xb436ba50d378d4bbc8660d312a13df6af6e89dfb": { + "balance": "0x1780d77678137ac1b775", + "nonce": 29072 + }, + "0x1585936b53834b021f68cc13eeefdec2efc8e724": { + "balance": "0x0" + } + } +} diff --git a/eth/tracers/internal/tracetest/testdata/prestate_tracer/disable_code_and_storage.json b/eth/tracers/internal/tracetest/testdata/prestate_tracer/disable_code_and_storage.json new file mode 100644 index 0000000000..b37dfa90a1 --- /dev/null +++ b/eth/tracers/internal/tracetest/testdata/prestate_tracer/disable_code_and_storage.json @@ -0,0 +1,78 @@ +{ + "context": { + "difficulty": "3502894804", + "gasLimit": "4722976", + "miner": "0x1585936b53834b021f68cc13eeefdec2efc8e724", + "number": "2289806", + "timestamp": "1513601314" + }, + "genesis": { + "alloc": { + "0x0024f658a46fbb89d8ac105e98d7ac7cbbaf27c5": { + "balance": "0x0", + "code": "0x", + "nonce": "22", + "storage": {} + }, + "0x3b873a919aa0512d5a0f09e6dcceaa4a6727fafe": { + "balance": "0x4d87094125a369d9bd5", + "code": "0x606060405236156100935763ffffffff60e060020a60003504166311ee8382811461009c57806313af4035146100be5780631f5e8f4c146100ee57806324daddc5146101125780634921a91a1461013b57806363e4bff414610157578063764978f91461017f578063893d20e8146101a1578063ba40aaa1146101cd578063cebc9a82146101f4578063e177246e14610216575b61009a5b5b565b005b34156100a457fe5b6100ac61023d565b60408051918252519081900360200190f35b34156100c657fe5b6100da600160a060020a0360043516610244565b604080519115158252519081900360200190f35b34156100f657fe5b6100da610307565b604080519115158252519081900360200190f35b341561011a57fe5b6100da6004351515610318565b604080519115158252519081900360200190f35b6100da6103d6565b604080519115158252519081900360200190f35b6100da600160a060020a0360043516610420565b604080519115158252519081900360200190f35b341561018757fe5b6100ac61046c565b60408051918252519081900360200190f35b34156101a957fe5b6101b1610473565b60408051600160a060020a039092168252519081900360200190f35b34156101d557fe5b6100da600435610483565b604080519115158252519081900360200190f35b34156101fc57fe5b6100ac61050d565b60408051918252519081900360200190f35b341561021e57fe5b6100da600435610514565b604080519115158252519081900360200190f35b6003545b90565b60006000610250610473565b600160a060020a031633600160a060020a03161415156102705760006000fd5b600160a060020a03831615156102865760006000fd5b50600054600160a060020a0390811690831681146102fb57604051600160a060020a0380851691908316907ffcf23a92150d56e85e3a3d33b357493246e55783095eb6a733eb8439ffc752c890600090a360008054600160a060020a031916600160a060020a03851617905560019150610300565b600091505b5b50919050565b60005460a060020a900460ff165b90565b60006000610324610473565b600160a060020a031633600160a060020a03161415156103445760006000fd5b5060005460a060020a900460ff16801515831515146102fb576000546040805160a060020a90920460ff1615158252841515602083015280517fe6cd46a119083b86efc6884b970bfa30c1708f53ba57b86716f15b2f4551a9539281900390910190a16000805460a060020a60ff02191660a060020a8515150217905560019150610300565b600091505b5b50919050565b60006103e0610307565b801561040557506103ef610473565b600160a060020a031633600160a060020a031614155b156104105760006000fd5b610419336105a0565b90505b5b90565b600061042a610307565b801561044f5750610439610473565b600160a060020a031633600160a060020a031614155b1561045a5760006000fd5b610463826105a0565b90505b5b919050565b6001545b90565b600054600160a060020a03165b90565b6000600061048f610473565b600160a060020a031633600160a060020a03161415156104af5760006000fd5b506001548281146102fb57604080518281526020810185905281517f79a3746dde45672c9e8ab3644b8bb9c399a103da2dc94b56ba09777330a83509929181900390910190a160018381559150610300565b600091505b5b50919050565b6002545b90565b60006000610520610473565b600160a060020a031633600160a060020a03161415156105405760006000fd5b506002548281146102fb57604080518281526020810185905281517ff6991a728965fedd6e927fdf16bdad42d8995970b4b31b8a2bf88767516e2494929181900390910190a1600283905560019150610300565b600091505b5b50919050565b60006000426105ad61023d565b116102fb576105c46105bd61050d565b4201610652565b6105cc61046c565b604051909150600160a060020a038416908290600081818185876187965a03f1925050501561063d57604080518281529051600160a060020a038516917f9bca65ce52fdef8a470977b51f247a2295123a4807dfa9e502edf0d30722da3b919081900360200190a260019150610300565b6102fb42610652565b5b600091505b50919050565b60038190555b505600a165627a7a72305820f3c973c8b7ed1f62000b6701bd5b708469e19d0f1d73fde378a56c07fd0b19090029", + "nonce": "1", + "storage": { + "0x0000000000000000000000000000000000000000000000000000000000000000": "0x000000000000000000000001b436ba50d378d4bbc8660d312a13df6af6e89dfb", + "0x0000000000000000000000000000000000000000000000000000000000000001": "0x00000000000000000000000000000000000000000000000006f05b59d3b20000", + "0x0000000000000000000000000000000000000000000000000000000000000002": "0x000000000000000000000000000000000000000000000000000000000000003c", + "0x0000000000000000000000000000000000000000000000000000000000000003": "0x000000000000000000000000000000000000000000000000000000005a37b834" + } + }, + "0xb436ba50d378d4bbc8660d312a13df6af6e89dfb": { + "balance": "0x1780d77678137ac1b775", + "code": "0x", + "nonce": "29072", + "storage": {} + } + }, + "config": { + "byzantiumBlock": 1700000, + "chainId": 3, + "eip150Block": 0, + "eip155Block": 10, + "eip158Block": 10, + "ethash": {}, + "homesteadBlock": 0 + }, + "difficulty": "3509749784", + "extraData": "0x4554482e45544846414e532e4f52472d4641313738394444", + "gasLimit": "4727564", + "hash": "0x609948ac3bd3c00b7736b933248891d6c901ee28f066241bddb28f4e00a9f440", + "miner": "0xbbf5029fd710d227630c8b7d338051b8e76d50b3", + "mixHash": "0xb131e4507c93c7377de00e7c271bf409ec7492767142ff0f45c882f8068c2ada", + "nonce": "0x4eb12e19c16d43da", + "number": "2289805", + "stateRoot": "0xc7f10f352bff82fac3c2999d3085093d12652e19c7fd32591de49dc5d91b4f1f", + "timestamp": "1513601261", + "totalDifficulty": "7143276353481064" + }, + "input": "0xf88b8271908506fc23ac0083015f90943b873a919aa0512d5a0f09e6dcceaa4a6727fafe80a463e4bff40000000000000000000000000024f658a46fbb89d8ac105e98d7ac7cbbaf27c52aa0bdce0b59e8761854e857fe64015f06dd08a4fbb7624f6094893a79a72e6ad6bea01d9dde033cff7bb235a3163f348a6d7ab8d6b52bc0963a95b91612e40ca766a4", + "tracerConfig": { + "disableCode": true, + "disableStorage": true + }, + "result": { + "0x0024f658a46fbb89d8ac105e98d7ac7cbbaf27c5": { + "balance": "0x0", + "nonce": 22 + }, + "0x3b873a919aa0512d5a0f09e6dcceaa4a6727fafe": { + "balance": "0x4d87094125a369d9bd5", + "nonce": 1 + }, + "0xb436ba50d378d4bbc8660d312a13df6af6e89dfb": { + "balance": "0x1780d77678137ac1b775", + "nonce": 29072 + }, + "0x1585936b53834b021f68cc13eeefdec2efc8e724": { + "balance": "0x0" + } + } +} diff --git a/eth/tracers/internal/tracetest/testdata/prestate_tracer/disable_storage.json b/eth/tracers/internal/tracetest/testdata/prestate_tracer/disable_storage.json new file mode 100644 index 0000000000..43d6e03b44 --- /dev/null +++ b/eth/tracers/internal/tracetest/testdata/prestate_tracer/disable_storage.json @@ -0,0 +1,78 @@ +{ + "context": { + "difficulty": "3502894804", + "gasLimit": "4722976", + "miner": "0x1585936b53834b021f68cc13eeefdec2efc8e724", + "number": "2289806", + "timestamp": "1513601314" + }, + "genesis": { + "alloc": { + "0x0024f658a46fbb89d8ac105e98d7ac7cbbaf27c5": { + "balance": "0x0", + "code": "0x", + "nonce": "22", + "storage": {} + }, + "0x3b873a919aa0512d5a0f09e6dcceaa4a6727fafe": { + "balance": "0x4d87094125a369d9bd5", + "code": "0x606060405236156100935763ffffffff60e060020a60003504166311ee8382811461009c57806313af4035146100be5780631f5e8f4c146100ee57806324daddc5146101125780634921a91a1461013b57806363e4bff414610157578063764978f91461017f578063893d20e8146101a1578063ba40aaa1146101cd578063cebc9a82146101f4578063e177246e14610216575b61009a5b5b565b005b34156100a457fe5b6100ac61023d565b60408051918252519081900360200190f35b34156100c657fe5b6100da600160a060020a0360043516610244565b604080519115158252519081900360200190f35b34156100f657fe5b6100da610307565b604080519115158252519081900360200190f35b341561011a57fe5b6100da6004351515610318565b604080519115158252519081900360200190f35b6100da6103d6565b604080519115158252519081900360200190f35b6100da600160a060020a0360043516610420565b604080519115158252519081900360200190f35b341561018757fe5b6100ac61046c565b60408051918252519081900360200190f35b34156101a957fe5b6101b1610473565b60408051600160a060020a039092168252519081900360200190f35b34156101d557fe5b6100da600435610483565b604080519115158252519081900360200190f35b34156101fc57fe5b6100ac61050d565b60408051918252519081900360200190f35b341561021e57fe5b6100da600435610514565b604080519115158252519081900360200190f35b6003545b90565b60006000610250610473565b600160a060020a031633600160a060020a03161415156102705760006000fd5b600160a060020a03831615156102865760006000fd5b50600054600160a060020a0390811690831681146102fb57604051600160a060020a0380851691908316907ffcf23a92150d56e85e3a3d33b357493246e55783095eb6a733eb8439ffc752c890600090a360008054600160a060020a031916600160a060020a03851617905560019150610300565b600091505b5b50919050565b60005460a060020a900460ff165b90565b60006000610324610473565b600160a060020a031633600160a060020a03161415156103445760006000fd5b5060005460a060020a900460ff16801515831515146102fb576000546040805160a060020a90920460ff1615158252841515602083015280517fe6cd46a119083b86efc6884b970bfa30c1708f53ba57b86716f15b2f4551a9539281900390910190a16000805460a060020a60ff02191660a060020a8515150217905560019150610300565b600091505b5b50919050565b60006103e0610307565b801561040557506103ef610473565b600160a060020a031633600160a060020a031614155b156104105760006000fd5b610419336105a0565b90505b5b90565b600061042a610307565b801561044f5750610439610473565b600160a060020a031633600160a060020a031614155b1561045a5760006000fd5b610463826105a0565b90505b5b919050565b6001545b90565b600054600160a060020a03165b90565b6000600061048f610473565b600160a060020a031633600160a060020a03161415156104af5760006000fd5b506001548281146102fb57604080518281526020810185905281517f79a3746dde45672c9e8ab3644b8bb9c399a103da2dc94b56ba09777330a83509929181900390910190a160018381559150610300565b600091505b5b50919050565b6002545b90565b60006000610520610473565b600160a060020a031633600160a060020a03161415156105405760006000fd5b506002548281146102fb57604080518281526020810185905281517ff6991a728965fedd6e927fdf16bdad42d8995970b4b31b8a2bf88767516e2494929181900390910190a1600283905560019150610300565b600091505b5b50919050565b60006000426105ad61023d565b116102fb576105c46105bd61050d565b4201610652565b6105cc61046c565b604051909150600160a060020a038416908290600081818185876187965a03f1925050501561063d57604080518281529051600160a060020a038516917f9bca65ce52fdef8a470977b51f247a2295123a4807dfa9e502edf0d30722da3b919081900360200190a260019150610300565b6102fb42610652565b5b600091505b50919050565b60038190555b505600a165627a7a72305820f3c973c8b7ed1f62000b6701bd5b708469e19d0f1d73fde378a56c07fd0b19090029", + "nonce": "1", + "storage": { + "0x0000000000000000000000000000000000000000000000000000000000000000": "0x000000000000000000000001b436ba50d378d4bbc8660d312a13df6af6e89dfb", + "0x0000000000000000000000000000000000000000000000000000000000000001": "0x00000000000000000000000000000000000000000000000006f05b59d3b20000", + "0x0000000000000000000000000000000000000000000000000000000000000002": "0x000000000000000000000000000000000000000000000000000000000000003c", + "0x0000000000000000000000000000000000000000000000000000000000000003": "0x000000000000000000000000000000000000000000000000000000005a37b834" + } + }, + "0xb436ba50d378d4bbc8660d312a13df6af6e89dfb": { + "balance": "0x1780d77678137ac1b775", + "code": "0x", + "nonce": "29072", + "storage": {} + } + }, + "config": { + "byzantiumBlock": 1700000, + "chainId": 3, + "eip150Block": 0, + "eip155Block": 10, + "eip158Block": 10, + "ethash": {}, + "homesteadBlock": 0 + }, + "difficulty": "3509749784", + "extraData": "0x4554482e45544846414e532e4f52472d4641313738394444", + "gasLimit": "4727564", + "hash": "0x609948ac3bd3c00b7736b933248891d6c901ee28f066241bddb28f4e00a9f440", + "miner": "0xbbf5029fd710d227630c8b7d338051b8e76d50b3", + "mixHash": "0xb131e4507c93c7377de00e7c271bf409ec7492767142ff0f45c882f8068c2ada", + "nonce": "0x4eb12e19c16d43da", + "number": "2289805", + "stateRoot": "0xc7f10f352bff82fac3c2999d3085093d12652e19c7fd32591de49dc5d91b4f1f", + "timestamp": "1513601261", + "totalDifficulty": "7143276353481064" + }, + "input": "0xf88b8271908506fc23ac0083015f90943b873a919aa0512d5a0f09e6dcceaa4a6727fafe80a463e4bff40000000000000000000000000024f658a46fbb89d8ac105e98d7ac7cbbaf27c52aa0bdce0b59e8761854e857fe64015f06dd08a4fbb7624f6094893a79a72e6ad6bea01d9dde033cff7bb235a3163f348a6d7ab8d6b52bc0963a95b91612e40ca766a4", + "tracerConfig": { + "disableStorage": true + }, + "result": { + "0x0024f658a46fbb89d8ac105e98d7ac7cbbaf27c5": { + "balance": "0x0", + "nonce": 22 + }, + "0x3b873a919aa0512d5a0f09e6dcceaa4a6727fafe": { + "balance": "0x4d87094125a369d9bd5", + "nonce": 1, + "code": "0x606060405236156100935763ffffffff60e060020a60003504166311ee8382811461009c57806313af4035146100be5780631f5e8f4c146100ee57806324daddc5146101125780634921a91a1461013b57806363e4bff414610157578063764978f91461017f578063893d20e8146101a1578063ba40aaa1146101cd578063cebc9a82146101f4578063e177246e14610216575b61009a5b5b565b005b34156100a457fe5b6100ac61023d565b60408051918252519081900360200190f35b34156100c657fe5b6100da600160a060020a0360043516610244565b604080519115158252519081900360200190f35b34156100f657fe5b6100da610307565b604080519115158252519081900360200190f35b341561011a57fe5b6100da6004351515610318565b604080519115158252519081900360200190f35b6100da6103d6565b604080519115158252519081900360200190f35b6100da600160a060020a0360043516610420565b604080519115158252519081900360200190f35b341561018757fe5b6100ac61046c565b60408051918252519081900360200190f35b34156101a957fe5b6101b1610473565b60408051600160a060020a039092168252519081900360200190f35b34156101d557fe5b6100da600435610483565b604080519115158252519081900360200190f35b34156101fc57fe5b6100ac61050d565b60408051918252519081900360200190f35b341561021e57fe5b6100da600435610514565b604080519115158252519081900360200190f35b6003545b90565b60006000610250610473565b600160a060020a031633600160a060020a03161415156102705760006000fd5b600160a060020a03831615156102865760006000fd5b50600054600160a060020a0390811690831681146102fb57604051600160a060020a0380851691908316907ffcf23a92150d56e85e3a3d33b357493246e55783095eb6a733eb8439ffc752c890600090a360008054600160a060020a031916600160a060020a03851617905560019150610300565b600091505b5b50919050565b60005460a060020a900460ff165b90565b60006000610324610473565b600160a060020a031633600160a060020a03161415156103445760006000fd5b5060005460a060020a900460ff16801515831515146102fb576000546040805160a060020a90920460ff1615158252841515602083015280517fe6cd46a119083b86efc6884b970bfa30c1708f53ba57b86716f15b2f4551a9539281900390910190a16000805460a060020a60ff02191660a060020a8515150217905560019150610300565b600091505b5b50919050565b60006103e0610307565b801561040557506103ef610473565b600160a060020a031633600160a060020a031614155b156104105760006000fd5b610419336105a0565b90505b5b90565b600061042a610307565b801561044f5750610439610473565b600160a060020a031633600160a060020a031614155b1561045a5760006000fd5b610463826105a0565b90505b5b919050565b6001545b90565b600054600160a060020a03165b90565b6000600061048f610473565b600160a060020a031633600160a060020a03161415156104af5760006000fd5b506001548281146102fb57604080518281526020810185905281517f79a3746dde45672c9e8ab3644b8bb9c399a103da2dc94b56ba09777330a83509929181900390910190a160018381559150610300565b600091505b5b50919050565b6002545b90565b60006000610520610473565b600160a060020a031633600160a060020a03161415156105405760006000fd5b506002548281146102fb57604080518281526020810185905281517ff6991a728965fedd6e927fdf16bdad42d8995970b4b31b8a2bf88767516e2494929181900390910190a1600283905560019150610300565b600091505b5b50919050565b60006000426105ad61023d565b116102fb576105c46105bd61050d565b4201610652565b6105cc61046c565b604051909150600160a060020a038416908290600081818185876187965a03f1925050501561063d57604080518281529051600160a060020a038516917f9bca65ce52fdef8a470977b51f247a2295123a4807dfa9e502edf0d30722da3b919081900360200190a260019150610300565b6102fb42610652565b5b600091505b50919050565b60038190555b505600a165627a7a72305820f3c973c8b7ed1f62000b6701bd5b708469e19d0f1d73fde378a56c07fd0b19090029" + }, + "0xb436ba50d378d4bbc8660d312a13df6af6e89dfb": { + "balance": "0x1780d77678137ac1b775", + "nonce": 29072 + }, + "0x1585936b53834b021f68cc13eeefdec2efc8e724": { + "balance": "0x0" + } + } +} diff --git a/eth/tracers/internal/tracetest/testdata/prestate_tracer/setcode_tx.json b/eth/tracers/internal/tracetest/testdata/prestate_tracer/setcode_tx.json new file mode 100644 index 0000000000..b7d5ee1c54 --- /dev/null +++ b/eth/tracers/internal/tracetest/testdata/prestate_tracer/setcode_tx.json @@ -0,0 +1,82 @@ +{ + "genesis": { + "baseFeePerGas": "7", + "blobGasUsed": "0", + "difficulty": "0", + "excessBlobGas": "36306944", + "extraData": "0xd983010e00846765746888676f312e32312e308664617277696e", + "gasLimit": "15639172", + "hash": "0xc682259fda061bb9ce8ccb491d5b2d436cb73daf04e1025dd116d045ce4ad28c", + "miner": "0x0000000000000000000000000000000000000000", + "mixHash": "0xae1a5ba939a4c9ac38aabeff361169fb55a6fc2c9511457e0be6eff9514faec0", + "nonce": "0x0000000000000000", + "number": "315", + "parentBeaconBlockRoot": "0x0000000000000000000000000000000000000000000000000000000000000000", + "stateRoot": "0x577f42ab21ccfd946511c57869ace0bdf7c217c36f02b7cd3459df0ed1cffc1a", + "timestamp": "1709626771", + "withdrawals": [], + "withdrawalsRoot": "0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421", + "alloc": { + "0x0000000000000000000000000000000000000000": { + "balance": "0x272e0528" + }, + "0x000000000000000000000000000000000000aaaa": { + "code": "0x6000600060006000600173703c4b2bd70c169f5717101caee543299fc946c75af1", + "balance": "0x0" + }, + "0x000000000000000000000000000000000000bbbb": { + "code": "0x6042604255", + "balance": "0x0" + }, + "0x703c4b2bd70c169f5717101caee543299fc946c7": { + "balance": "0xde0b6b3a7640000" + }, + "0x71562b71999873db5b286df957af199ec94617f7": { + "balance": "0xde0b6b3a7640000" + } + }, + "config": { + "chainId": 1337, + "homesteadBlock": 0, + "eip150Block": 0, + "eip155Block": 0, + "eip158Block": 0, + "byzantiumBlock": 0, + "constantinopleBlock": 0, + "petersburgBlock": 0, + "istanbulBlock": 0, + "muirGlacierBlock": 0, + "berlinBlock": 0, + "londonBlock": 0, + "arrowGlacierBlock": 0, + "grayGlacierBlock": 0, + "shanghaiTime": 0, + "cancunTime": 0, + "pragueTime": 0, + "terminalTotalDifficulty": 0 + } + }, + "context": { + "number": "316", + "difficulty": "0", + "timestamp": "1709626785", + "gasLimit": "15654443", + "miner": "0x0000000000000000000000000000000000000000", + "baseFeePerGas": "7" + }, + "input": "04f90126820539800285012a05f2008307a1209471562b71999873db5b286df957af199ec94617f78080c0f8baf85c82053994000000000000000000000000000000000000aaaa0101a07ed17af7d2d2b9ba7d797a202125bf505b9a0f962a67b3b61b56783d8faf7461a001b73b6e586edc706dce6c074eaec28692fa6359fb3446a2442f36777e1c0669f85a8094000000000000000000000000000000000000bbbb8001a05011890f198f0356a887b0779bde5afa1ed04e6acb1e3f37f8f18c7b6f521b98a056c3fa3456b103f3ef4a0acb4b647b9cab9ec4bc68fbcdf1e10b49fb2bcbcf6101a0167b0ecfc343a497095c22ee4270d3cc3b971cc3599fc73bbff727e0d2ed432da01c003c72306807492bf1150e39b2f79da23b49a4e83eb6e9209ae30d3572368f", + "result": { + "0x0000000000000000000000000000000000000000": { + "balance": "0x272e0528" + }, + "0x703c4b2bd70c169f5717101caee543299fc946c7": { + "balance": "0xde0b6b3a7640000", + "storage": { + "0x0000000000000000000000000000000000000000000000000000000000000042": "0x0000000000000000000000000000000000000000000000000000000000000000" + } + }, + "0x71562b71999873db5b286df957af199ec94617f7": { + "balance": "0xde0b6b3a7640000" + } + } +} diff --git a/eth/tracers/internal/tracetest/testdata/prestate_tracer_with_diff_mode/create_disable_code.json b/eth/tracers/internal/tracetest/testdata/prestate_tracer_with_diff_mode/create_disable_code.json new file mode 100644 index 0000000000..5d7c024a5e --- /dev/null +++ b/eth/tracers/internal/tracetest/testdata/prestate_tracer_with_diff_mode/create_disable_code.json @@ -0,0 +1,100 @@ +{ + "genesis": { + "difficulty": "13756228101629", + "extraData": "0xd983010302844765746887676f312e342e328777696e646f7773", + "gasLimit": "3141592", + "hash": "0x58b7a87b6ba10b46b4e251d64ebc3d9822dd82218eaf24dff6796f6f1f687251", + "miner": "0xf8b483dba2c3b7176a3da549ad41a48bb3121069", + "mixHash": "0x5984b9a316116bd890e6e5f4c52d655184b0d7aa74821e1382d7760f9803c1dd", + "nonce": "0xea4bb4997242c681", + "number": "1061221", + "stateRoot": "0x5402c04d481414248d824c3b61e924e0c9307adbc9fbaae774a74cce30a4163d", + "timestamp": "1456458069", + "totalDifficulty": "7930751135586064334", + "alloc": { + "0x2a65aca4d5fc5b5c859090a6c34d164135398226": { + "balance": "0x9fb6b81e112638b886", + "nonce": "217865", + "code": "0x" + }, + "0xf0c5cef39b17c213cfe090a46b8c7760ffb7928a": { + "balance": "0x15b6828e22bb12188", + "nonce": "747", + "code": "0x" + } + }, + "config": { + "chainId": 1, + "homesteadBlock": 1150000, + "daoForkBlock": 1920000, + "eip150Block": 2463000, + "eip155Block": 2675000, + "eip158Block": 2675000, + "byzantiumBlock": 4370000, + "constantinopleBlock": 7280000, + "petersburgBlock": 7280000, + "istanbulBlock": 9069000, + "muirGlacierBlock": 9200000, + "berlinBlock": 12244000, + "londonBlock": 12965000, + "arrowGlacierBlock": 13773000, + "grayGlacierBlock": 15050000, + "ethash": {} + } + }, + "context": { + "number": "1061222", + "difficulty": "13749511193633", + "timestamp": "1456458097", + "gasLimit": "3141592", + "miner": "0x2a65aca4d5fc5b5c859090a6c34d164135398226" + }, + "input": "0xf905498202eb850ba43b7400830f42408080b904f460606040526040516102b43803806102b48339016040526060805160600190602001505b5b33600060006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908302179055505b806001600050908051906020019082805482825590600052602060002090601f01602090048101928215609e579182015b82811115609d5782518260005055916020019190600101906081565b5b50905060c5919060a9565b8082111560c1576000818150600090555060010160a9565b5090565b50505b506101dc806100d86000396000f30060606040526000357c01000000000000000000000000000000000000000000000000000000009004806341c0e1b514610044578063cfae32171461005157610042565b005b61004f6004506100ca565b005b61005c60045061015e565b60405180806020018281038252838181518152602001915080519060200190808383829060006004602084601f0104600302600f01f150905090810190601f1680156100bc5780820380516001836020036101000a031916815260200191505b509250505060405180910390f35b600060009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff16141561015b57600060009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16ff5b5b565b60206040519081016040528060008152602001506001600050805480601f016020809104026020016040519081016040528092919081815260200182805480156101cd57820191906000526020600020905b8154815290600101906020018083116101b057829003601f168201915b505050505090506101d9565b9056000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000001ee7b225f6964223a225a473466784a7245323639384866623839222c22666f726d5f736f75726365223a22434c54523031222c22636f6d6d69746d656e745f64617465223a22222c22626f72726f7765725f6e616d65223a22222c22626f72726f7765725f616464726573735f6c696e6531223a22222c22626f72726f7765725f616464726573735f6c696e6532223a22222c22626f72726f7765725f636f6e74616374223a22222c22626f72726f7765725f7374617465223a22222c22626f72726f7765725f74797065223a22222c2270726f70657274795f61646472657373223a22222c226c6f616e5f616d6f756e745f7772697474656e223a22222c226c6f616e5f616d6f756e74223a22222c224c54565f7772697474656e223a22222c224c5456223a22222c2244534352223a22222c2270726f70657274795f74797065223a22222c2270726f70657274795f6465736372697074696f6e223a22222c226c656e646572223a22222c2267756172616e746f7273223a22222c226c696d69746564223a22222c226361705f616d6f756e74223a22222c226361705f70657263656e745f7772697474656e223a22222c226361705f70657263656e74616765223a22222c227465726d5f7772697474656e223a22222c227465726d223a22222c22657874656e64223a22227d0000000000000000000000000000000000001ba027d54712289af34f0ec0f06092745104d68e5801cd17097bc1104111f855258da070ec9f1c942d9bedf89f9660a684d3bb8cd9c2ac7f6dd883cb3e26a193180244", + "tracerConfig": { + "diffMode": true, + "disableCode": true + }, + "result": { + "pre": { + "0x2a65aca4d5fc5b5c859090a6c34d164135398226": { + "balance": "0x9fb6b81e112638b886", + "nonce": 217865 + }, + "0xf0c5cef39b17c213cfe090a46b8c7760ffb7928a": { + "balance": "0x15b6828e22bb12188", + "nonce": 747 + } + }, + "post": { + "0x2a65aca4d5fc5b5c859090a6c34d164135398226": { + "balance": "0x9fb71abdd2621d8886" + }, + "0x40f2f445da6c9047554683fb382fba6769717116": { + "storage": { + "0x0000000000000000000000000000000000000000000000000000000000000000": "0x000000000000000000000000f0c5cef39b17c213cfe090a46b8c7760ffb7928a", + "0x0000000000000000000000000000000000000000000000000000000000000001": "0x00000000000000000000000000000000000000000000000000000000000001ee", + "0xb10e2d527612073b26eecdfd717e6a320cf44b4afac2b0732d9fcbe2b7fa0cf6": "0x7b225f6964223a225a473466784a7245323639384866623839222c22666f726d", + "0xb10e2d527612073b26eecdfd717e6a320cf44b4afac2b0732d9fcbe2b7fa0cf7": "0x5f736f75726365223a22434c54523031222c22636f6d6d69746d656e745f6461", + "0xb10e2d527612073b26eecdfd717e6a320cf44b4afac2b0732d9fcbe2b7fa0cf8": "0x7465223a22222c22626f72726f7765725f6e616d65223a22222c22626f72726f", + "0xb10e2d527612073b26eecdfd717e6a320cf44b4afac2b0732d9fcbe2b7fa0cf9": "0x7765725f616464726573735f6c696e6531223a22222c22626f72726f7765725f", + "0xb10e2d527612073b26eecdfd717e6a320cf44b4afac2b0732d9fcbe2b7fa0cfa": "0x616464726573735f6c696e6532223a22222c22626f72726f7765725f636f6e74", + "0xb10e2d527612073b26eecdfd717e6a320cf44b4afac2b0732d9fcbe2b7fa0cfb": "0x616374223a22222c22626f72726f7765725f7374617465223a22222c22626f72", + "0xb10e2d527612073b26eecdfd717e6a320cf44b4afac2b0732d9fcbe2b7fa0cfc": "0x726f7765725f74797065223a22222c2270726f70657274795f61646472657373", + "0xb10e2d527612073b26eecdfd717e6a320cf44b4afac2b0732d9fcbe2b7fa0cfd": "0x223a22222c226c6f616e5f616d6f756e745f7772697474656e223a22222c226c", + "0xb10e2d527612073b26eecdfd717e6a320cf44b4afac2b0732d9fcbe2b7fa0cfe": "0x6f616e5f616d6f756e74223a22222c224c54565f7772697474656e223a22222c", + "0xb10e2d527612073b26eecdfd717e6a320cf44b4afac2b0732d9fcbe2b7fa0cff": "0x224c5456223a22222c2244534352223a22222c2270726f70657274795f747970", + "0xb10e2d527612073b26eecdfd717e6a320cf44b4afac2b0732d9fcbe2b7fa0d00": "0x65223a22222c2270726f70657274795f6465736372697074696f6e223a22222c", + "0xb10e2d527612073b26eecdfd717e6a320cf44b4afac2b0732d9fcbe2b7fa0d01": "0x226c656e646572223a22222c2267756172616e746f7273223a22222c226c696d", + "0xb10e2d527612073b26eecdfd717e6a320cf44b4afac2b0732d9fcbe2b7fa0d02": "0x69746564223a22222c226361705f616d6f756e74223a22222c226361705f7065", + "0xb10e2d527612073b26eecdfd717e6a320cf44b4afac2b0732d9fcbe2b7fa0d03": "0x7263656e745f7772697474656e223a22222c226361705f70657263656e746167", + "0xb10e2d527612073b26eecdfd717e6a320cf44b4afac2b0732d9fcbe2b7fa0d04": "0x65223a22222c227465726d5f7772697474656e223a22222c227465726d223a22", + "0xb10e2d527612073b26eecdfd717e6a320cf44b4afac2b0732d9fcbe2b7fa0d05": "0x222c22657874656e64223a22227d000000000000000000000000000000000000" + } + }, + "0xf0c5cef39b17c213cfe090a46b8c7760ffb7928a": { + "balance": "0x15b058920efcc5188", + "nonce": 748 + } + } + } +} diff --git a/eth/tracers/internal/tracetest/testdata/prestate_tracer_with_diff_mode/create_disable_storage.json b/eth/tracers/internal/tracetest/testdata/prestate_tracer_with_diff_mode/create_disable_storage.json new file mode 100644 index 0000000000..65594feb44 --- /dev/null +++ b/eth/tracers/internal/tracetest/testdata/prestate_tracer_with_diff_mode/create_disable_storage.json @@ -0,0 +1,81 @@ +{ + "genesis": { + "difficulty": "13756228101629", + "extraData": "0xd983010302844765746887676f312e342e328777696e646f7773", + "gasLimit": "3141592", + "hash": "0x58b7a87b6ba10b46b4e251d64ebc3d9822dd82218eaf24dff6796f6f1f687251", + "miner": "0xf8b483dba2c3b7176a3da549ad41a48bb3121069", + "mixHash": "0x5984b9a316116bd890e6e5f4c52d655184b0d7aa74821e1382d7760f9803c1dd", + "nonce": "0xea4bb4997242c681", + "number": "1061221", + "stateRoot": "0x5402c04d481414248d824c3b61e924e0c9307adbc9fbaae774a74cce30a4163d", + "timestamp": "1456458069", + "totalDifficulty": "7930751135586064334", + "alloc": { + "0x2a65aca4d5fc5b5c859090a6c34d164135398226": { + "balance": "0x9fb6b81e112638b886", + "nonce": "217865", + "code": "0x" + }, + "0xf0c5cef39b17c213cfe090a46b8c7760ffb7928a": { + "balance": "0x15b6828e22bb12188", + "nonce": "747", + "code": "0x" + } + }, + "config": { + "chainId": 1, + "homesteadBlock": 1150000, + "daoForkBlock": 1920000, + "eip150Block": 2463000, + "eip155Block": 2675000, + "eip158Block": 2675000, + "byzantiumBlock": 4370000, + "constantinopleBlock": 7280000, + "petersburgBlock": 7280000, + "istanbulBlock": 9069000, + "muirGlacierBlock": 9200000, + "berlinBlock": 12244000, + "londonBlock": 12965000, + "arrowGlacierBlock": 13773000, + "grayGlacierBlock": 15050000, + "ethash": {} + } + }, + "context": { + "number": "1061222", + "difficulty": "13749511193633", + "timestamp": "1456458097", + "gasLimit": "3141592", + "miner": "0x2a65aca4d5fc5b5c859090a6c34d164135398226" + }, + "input": "0xf905498202eb850ba43b7400830f42408080b904f460606040526040516102b43803806102b48339016040526060805160600190602001505b5b33600060006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908302179055505b806001600050908051906020019082805482825590600052602060002090601f01602090048101928215609e579182015b82811115609d5782518260005055916020019190600101906081565b5b50905060c5919060a9565b8082111560c1576000818150600090555060010160a9565b5090565b50505b506101dc806100d86000396000f30060606040526000357c01000000000000000000000000000000000000000000000000000000009004806341c0e1b514610044578063cfae32171461005157610042565b005b61004f6004506100ca565b005b61005c60045061015e565b60405180806020018281038252838181518152602001915080519060200190808383829060006004602084601f0104600302600f01f150905090810190601f1680156100bc5780820380516001836020036101000a031916815260200191505b509250505060405180910390f35b600060009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff16141561015b57600060009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16ff5b5b565b60206040519081016040528060008152602001506001600050805480601f016020809104026020016040519081016040528092919081815260200182805480156101cd57820191906000526020600020905b8154815290600101906020018083116101b057829003601f168201915b505050505090506101d9565b9056000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000001ee7b225f6964223a225a473466784a7245323639384866623839222c22666f726d5f736f75726365223a22434c54523031222c22636f6d6d69746d656e745f64617465223a22222c22626f72726f7765725f6e616d65223a22222c22626f72726f7765725f616464726573735f6c696e6531223a22222c22626f72726f7765725f616464726573735f6c696e6532223a22222c22626f72726f7765725f636f6e74616374223a22222c22626f72726f7765725f7374617465223a22222c22626f72726f7765725f74797065223a22222c2270726f70657274795f61646472657373223a22222c226c6f616e5f616d6f756e745f7772697474656e223a22222c226c6f616e5f616d6f756e74223a22222c224c54565f7772697474656e223a22222c224c5456223a22222c2244534352223a22222c2270726f70657274795f74797065223a22222c2270726f70657274795f6465736372697074696f6e223a22222c226c656e646572223a22222c2267756172616e746f7273223a22222c226c696d69746564223a22222c226361705f616d6f756e74223a22222c226361705f70657263656e745f7772697474656e223a22222c226361705f70657263656e74616765223a22222c227465726d5f7772697474656e223a22222c227465726d223a22222c22657874656e64223a22227d0000000000000000000000000000000000001ba027d54712289af34f0ec0f06092745104d68e5801cd17097bc1104111f855258da070ec9f1c942d9bedf89f9660a684d3bb8cd9c2ac7f6dd883cb3e26a193180244", + "tracerConfig": { + "diffMode": true, + "disableStorage": true + }, + "result": { + "pre": { + "0x2a65aca4d5fc5b5c859090a6c34d164135398226": { + "balance": "0x9fb6b81e112638b886", + "nonce": 217865 + }, + "0xf0c5cef39b17c213cfe090a46b8c7760ffb7928a": { + "balance": "0x15b6828e22bb12188", + "nonce": 747 + } + }, + "post": { + "0x2a65aca4d5fc5b5c859090a6c34d164135398226": { + "balance": "0x9fb71abdd2621d8886" + }, + "0x40f2f445da6c9047554683fb382fba6769717116": { + "code": "0x60606040526000357c01000000000000000000000000000000000000000000000000000000009004806341c0e1b514610044578063cfae32171461005157610042565b005b61004f6004506100ca565b005b61005c60045061015e565b60405180806020018281038252838181518152602001915080519060200190808383829060006004602084601f0104600302600f01f150905090810190601f1680156100bc5780820380516001836020036101000a031916815260200191505b509250505060405180910390f35b600060009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff16141561015b57600060009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16ff5b5b565b60206040519081016040528060008152602001506001600050805480601f016020809104026020016040519081016040528092919081815260200182805480156101cd57820191906000526020600020905b8154815290600101906020018083116101b057829003601f168201915b505050505090506101d9565b9056" + }, + "0xf0c5cef39b17c213cfe090a46b8c7760ffb7928a": { + "balance": "0x15b058920efcc5188", + "nonce": 748 + } + } + } +} diff --git a/eth/tracers/internal/tracetest/testdata/prestate_tracer_with_diff_mode/create_failed.json b/eth/tracers/internal/tracetest/testdata/prestate_tracer_with_diff_mode/create_failed.json index ae7f7e97f5..b5bccf7bd0 100644 --- a/eth/tracers/internal/tracetest/testdata/prestate_tracer_with_diff_mode/create_failed.json +++ b/eth/tracers/internal/tracetest/testdata/prestate_tracer_with_diff_mode/create_failed.json @@ -55,7 +55,6 @@ "londonBlock": 12965000, "arrowGlacierBlock": 13773000, "grayGlacierBlock": 15050000, - "terminalTotalDifficultyPassed": true, "ethash": {} } }, diff --git a/eth/tracers/internal/tracetest/testdata/prestate_tracer_with_diff_mode/inner_create_disable_code_and_storage.json b/eth/tracers/internal/tracetest/testdata/prestate_tracer_with_diff_mode/inner_create_disable_code_and_storage.json new file mode 100644 index 0000000000..96c93e7cf8 --- /dev/null +++ b/eth/tracers/internal/tracetest/testdata/prestate_tracer_with_diff_mode/inner_create_disable_code_and_storage.json @@ -0,0 +1,256 @@ +{ + "genesis": { + "difficulty": "13707196986889", + "extraData": "0xd983010302844765746887676f312e342e328777696e646f7773", + "gasLimit": "3141592", + "hash": "0x607b38fe7e94427ee8f3b9a62375c67f953f8d49e05dbfd0145f9d3bac142193", + "miner": "0xf8b483dba2c3b7176a3da549ad41a48bb3121069", + "mixHash": "0x98c74c9e76fd0078157e1696e4334a7e787396459693a84536d8b96414dafd5d", + "nonce": "0x77a5a0a73ad8745e", + "number": "1062502", + "stateRoot": "0x1df615df5fdbc8d5397bf3574f462f6d9696428eb8796d8e9252bccc8e3a8996", + "timestamp": "1456480432", + "totalDifficulty": "7948153536501153741", + "alloc": { + "0x0000000000000000000000000000000000000004": { + "balance": "0x0", + "code": "0x" + }, + "0x1deeda36e15ec9e80f3d7414d67a4803ae45fc80": { + "balance": "0x0", + "code": "0x650200d2f18c7350606060405236156100c15760e060020a60003504630bd295e681146100c65780630fd1f94e1461017d5780630fee183d1461018c578063349501b7146101ad5780635054d98a146101c75780637c0278fc146101ef5780637e92656214610287578063a0943154146102f6578063a1873db61461030e578063a9d2293d14610355578063b5d0f16e146103ad578063c17e6817146103ce578063cc3471af1461046a578063da46be0a1461047a578063f55627531461052a575b610007565b6105d36004356024356044355b60006000600030915081600160a060020a0316630a16697a6040518160e060020a0281526004018090506020604051808303816000876161da5a03f1156100075750506040515191505080841080610173575081600160a060020a031663a06db7dc6040518160e060020a0281526004018090506020604051808303816000876161da5a03f115610007575050506040518051906020015060ff16810184115b1561100d57610007565b6105d3600060f0610f6d61046e565b6105d3600435602435604435606435600081600202831015610ff657610fee565b6105d36004355b600081600014156109115750600161098f565b6105d36004355b6008810154600090819062010000900460ff16156105f257600691506105ec565b60408051602060248035600481810135601f81018590048502860185019096528585526105e5958135959194604494929390920191819084018382808284375094965050505050505060006004825103836001016000508181546001816001161561010002031660029004825481601f106108005782601f1061083a575b826008026101000360020a80910402828001178355610851565b6105e5600435602435604051600090600160a060020a038316907f398bd6b21ae4164ec322fb0eb8c2eb6277f36fd41903fbbed594dfe125591281908390a26007830154819010610e415760078301546005840154610e3f9162010000909104600160a060020a0316906103d8565b6105d3600435602435600060006000611064856101ce565b6105d36004356024356044356004830154600090819030908410156110e4577f4e4f545f454e4f5547485f47415300000000000000000000000000000000000091506112dd565b6105d35b60006000309050600a81600160a060020a0316630a16697a6040518160e060020a0281526004018090506020604051808303816000876161da5a03f1156100075750506040515160091901935050505b5090565b6105d36004356024355b60008282111561099e578183606402049050610998565b6105d36004356024355b600030600160a060020a0316318211156103fa57600160a060020a0330163191505b6000821115610994577389efe605e9ecbe22849cd85d5449cc946c26f8f36312c82bcc84846040518360e060020a0281526004018083600160a060020a031681526020018281526020019250505060206040518083038160008760325a03f2156100075750839250610998915050565b6105d35b6000600f610f6d610359565b6105e560043560243560443560643560843560088501805461ff00191661010017905584543090600090819081908190819060a060020a900460e060020a02811480156104db575060018b8101546002918116156101000260001901160481145b156109b3578a5460028c0154600160a060020a039190911690895a60405191900391906000818181858888f193505050508b60080160006101000a81548160ff02191690830217905550610bfa565b6105d36004355b6000600060006000309250600a83600160a060020a0316630a16697a6040518160e060020a0281526004018090506020604051808303816000876161da5a03f11561000757505060405151600919019350505081851115610eb05782600160a060020a031663c6502da86040518160e060020a0281526004018090506020604051808303816000876161da5a03f115610007575050604051519450610ea89050565b60408051918252519081900360200190f35b005b600291505b50919050565b6008830154610100900460ff161561060d57600591506105ec565b30905080600160a060020a0316630a16697a6040518160e060020a0281526004018090506020604051808303816000876161da5a03f1156100075750506040515143610109011015905061066457600091506105ec565b80600160a060020a0316630a16697a6040518160e060020a0281526004018090506020604051808303816000876161da5a03f1156100075750506040515143600a01101590506106d3576005830154620100009004600160a060020a0316600014156105e757600191506105ec565b80600160a060020a0316630a16697a6040518160e060020a0281526004018090506020604051808303816000876161da5a03f11561000757505060405151431015905061072357600391506105ec565b80600160a060020a031663a06db7dc6040518160e060020a0281526004018090506020604051808303816000876161da5a03f115610007575050506040518051906020015060ff1681600160a060020a0316630a16697a6040518160e060020a0281526004018090506020604051808303816000876161da5a03f115610007575050604051519190910143101590506107bf57600491506105ec565b600791506105ec565b5081800160010183558181151161085157601f016020900481601f016020900483600052602060002091820191016108519190610826565b82601f106107c8575082600052602060002080549082601f016020900481019061090691905b808211156103a95760008155600101610826565b60ff19168360005260206000205581800160010183555b5050505060048251111561090c575060005b6001838101546002918116156101000260001901160481101561090c57818160040181518110156100075790602001015160f860020a900460f860020a02836001016000508281546001816001161561010002031660029004811015610007578154600116156108e25790600052602060002090602091828204019190065b601f036101000a81548160ff0219169060f860020a84040217905550600101610863565b5061026d565b505050565b604080517f5f5f6469672875696e74323536290000000000000000000000000000000000008152815190819003600e01812060e060020a9081900481028190049081028252600019850160048301529151600160a060020a03301692916102bc86029160248281019260009291908290030181838887f19450505050505b919050565b5060005b92915050565b818360020203836064020460c8039050610998565b8a5460a060020a900460e060020a0260001415610a23578a5460028c0154600160a060020a039190911690895a03908d6001016000506040518082805460018160011615610100020316600290048015610ae55780601f10610aba57610100808354040283529160200191610ae5565b60018b8101546002918116156101000260001901160460001415610b1a578a5460028c0154600160a060020a039190911690895a03908d60000160149054906101000a900460e060020a0260e060020a900491906040518360e060020a028152600401809050600060405180830381858988f19450505050508b60080160006101000a81548160ff02191690830217905550610bfa565b820191906000526020600020905b815481529060010190602001808311610ac857829003601f168201915b5050915050600060405180830381858888f193505050508b60080160006101000a81548160ff02191690830217905550610bfa565b8a5460028c0154600160a060020a039190911690895a03908d60000160149054906101000a900460e060020a0260e060020a900491908e6001016000506040518460e060020a0281526004018082805460018160011615610100020316600290048015610bc85780601f10610b9d57610100808354040283529160200191610bc8565b820191906000526020600020905b815481529060010190602001808311610bab57829003601f168201915b5050915050600060405180830381858988f19450505050508b60080160006101000a81548160ff021916908302179055505b85600160a060020a031663938b5f326040518160e060020a0281526004018090506020604051808303816000876161da5a03f1156100075750505060405180519060200150600160a060020a031660405180807f75706461746544656661756c745061796d656e742829000000000000000000008152602001506016019050604051809103902060e060020a8091040260e060020a90046040518160e060020a0281526004018090506000604051808303816000876161da5a03f15050505060038b0154610cc8903a6103b7565b60058c0154909550620100009004600160a060020a03908116908a161415610cf65760068b01549350610d38565b85600160a060020a031663c6502da86040518160e060020a0281526004018090506020604051808303816000876161da5a03f115610007575050604051519450505b6064858502048b6007016000505401925060648587600160a060020a031663625cc4656040518160e060020a0281526004018090506020604051808303816000876161da5a03f11561000757505050604051805190602001500204915060008b60070160005081905550865a8b03013a029050610db7898285016103d8565b9250610dd773d3cda913deb6f67967b99d67acdfa1712c293601836103d8565b6040805160088e01548482526020820187905281830184905260ff1660608201529051919350600160a060020a038b16917f4538b7ec91dae8fada01e66a052482086d3e690c3db5a80457fbcd55457b4ae19181900360800190a25050505050505050505050565b505b309050610e8c81600160a060020a031663ae45850b6040518160e060020a0281526004018090506020604051808303816000876161da5a03f1156100075750506040515190316103d8565b505050600801805462ff0000191662010000179055565b600093505b505050919050565b600e19919091019081851115610f075782600160a060020a031663c6502da86040518160e060020a0281526004018090506020604051808303816000876161da5a03f115610007575050604051519450610ea89050565b60ef19919091019081851115610ea357818503905060f08184600160a060020a031663c6502da86040518160e060020a0281526004018090506020604051808303816000876161da5a03f115610007575050506040518051906020015002049350610ea8565b03905090565b6006860181905560058601805475ffffffffffffffffffffffffffffffffffffffff000019166201000087021790556007860184905560408051600160a060020a0387168152602081019290925280517fd8138f8a3f377c5259ca548e70e4c2de94f129f5a11036a15b69513cba2b426a9281900390910190a15b949350505050565b610f7343610531565b600192505b50509392505050565b60108185031015610fff576005860154620100009004600160a060020a03166000148061105057506005860154620100009004600160a060020a03908116908616145b9250611004565b600092505b505092915050565b91503090506000821480156110c4575080600160a060020a031663ae45850b6040518160e060020a0281526004018090506020604051808303816000876161da5a03f11561000757505060405151600160a060020a039081169086161490505b156110d2576001925061105c565b6007821415611057576001925061105c565b6008860154610100900460ff161561111e577f414c52454144595f43414c4c454400000000000000000000000000000000000091506112dd565b80600160a060020a0316630a16697a6040518160e060020a0281526004018090506020604051808303816000876161da5a03f115610007575050604051514310905080611206575080600160a060020a031663a06db7dc6040518160e060020a0281526004018090506020604051808303816000876161da5a03f1156100075750506040805180517f0a16697a000000000000000000000000000000000000000000000000000000008252915160ff9092169291630a16697a9160048181019260209290919082900301816000876161da5a03f1156100075750506040515191909101431190505b15611233577f4e4f545f494e5f43414c4c5f57494e444f57000000000000000000000000000091506112dd565b61123e8686436100d3565b151561126c577f4e4f545f415554484f52495a454400000000000000000000000000000000000091506112dd565b6005860154600061ffff91909116118015611299575032600160a060020a031685600160a060020a031614155b80156112b4575060058601546112b29061ffff166101b4565b155b156112dd577f535441434b5f544f4f5f4445455000000000000000000000000000000000000091505b60008214610fff5760408051600160a060020a03871681526020810184905281517fdcb278834ca505ad219cf8e4b5d11f026080abef6ec68e249ea5e4d9bb3dc7b2929181900390910190a16000925061100456" + }, + "0x2a65aca4d5fc5b5c859090a6c34d164135398226": { + "balance": "0x98e1c608601c2496b2", + "nonce": "218916", + "code": "0x" + }, + "0x651913977e8140c323997fce5e03c19e0015eebf": { + "balance": "0x0", + "code": "0x", + "storage": { + "0x0000000000000000000000000000000000000000000000000000000000000000": "0x0000000000000000000000000000000000000000000000000000000000000000", + "0x0000000000000000000000000000000000000000000000000000000000000001": "0x0000000000000000000000000000000000000000000000000000000000000000", + "0x0000000000000000000000000000000000000000000000000000000000000002": "0x0000000000000000000000000000000000000000000000000000000000000000", + "0x0000000000000000000000000000000000000000000000000000000000000003": "0x0000000000000000000000000000000000000000000000000000000000000000", + "0x0000000000000000000000000000000000000000000000000000000000000004": "0x0000000000000000000000000000000000000000000000000000000000000000", + "0x0000000000000000000000000000000000000000000000000000000000000005": "0x0000000000000000000000000000000000000000000000000000000000000000", + "0x0000000000000000000000000000000000000000000000000000000000000006": "0x0000000000000000000000000000000000000000000000000000000000000000", + "0x0000000000000000000000000000000000000000000000000000000000000007": "0x0000000000000000000000000000000000000000000000000000000000000000", + "0x0000000000000000000000000000000000000000000000000000000000000008": "0x0000000000000000000000000000000000000000000000000000000000000000", + "0x000000000000000000000000000000000000000000000000000000000000000c": "0x0000000000000000000000000000000000000000000000000000000000000000", + "0x000000000000000000000000000000000000000000000000000000000000000d": "0x0000000000000000000000000000000000000000000000000000000000000000", + "0x000000000000000000000000000000000000000000000000000000000000000e": "0x0000000000000000000000000000000000000000000000000000000000000000" + } + }, + "0x6c8f2a135f6ed072de4503bd7c4999a1a17f824b": { + "balance": "0x0", + "nonce": "237", + "code": "0x6060604052361561027c5760e060020a600035046301991313811461027e57806303d22885146102ca5780630450991814610323578063049ae734146103705780630ce46c43146103c35780630e85023914610602578063112e39a8146106755780631b4fa6ab146106c25780631e74a2d3146106d057806326a7985a146106fd5780633017fe2414610753578063346cabbc1461075c578063373a1bc3146107d55780633a9e74331461081e5780633c2c21a01461086e5780633d9ce89b146108ba578063480b70bd1461092f578063481078431461097e57806348f0518714610a0e5780634c471cde14610a865780634db3da8314610b09578063523ccfa814610b4f578063586a69fa14610be05780635a9f2def14610c3657806364ee49fe14610caf57806367beaccb14610d055780636840246014610d74578063795b9a6f14610dca5780637b55c8b514610e415780637c73f84614610ee15780638c0e156d14610f145780638c1d01c814610f605780638e46afa914610f69578063938c430714610fc0578063971c803f146111555780639772c982146111ac57806398c9cdf41461122857806398e00e541461127f5780639f927be7146112d5578063a00aede914611383578063a1c0539d146113d3578063aff21c6514611449578063b152f19e14611474578063b549793d146114cb578063b5b33eda1461154b578063bbc6eb1f1461159b578063c0f68859146115ab578063c3a2c0c314611601578063c43d05751461164b578063d8e5c04814611694578063dbfef71014611228578063e29fb547146116e7578063e6470fbe1461173a578063ea27a8811461174c578063ee77fe86146117d1578063f158458c14611851575b005b611882600435602435604435600073e54d323f9ef17c1f0dede47ecc86a9718fe5ea346350d4e41160016000503387876020604051908101604052806000815260200150612225610f6d565b61188260043560243560443560643560843560a43560c435600073e54d323f9ef17c1f0dede47ecc86a9718fe5ea346350d4e4116001600050338b8a6020604051908101604052806000815260200150896125196106c6565b611882600435600073e54d323f9ef17c1f0dede47ecc86a9718fe5ea346350d4e41160016000503385600060e060020a026020604051908101604052806000815260200150611e4a610f6d565b611882600435602435604435606435608435600073e54d323f9ef17c1f0dede47ecc86a9718fe5ea346350d4e41160016000503389896020604051908101604052806000815260200150886124e86106c6565b604080516020604435600481810135601f8101849004840285018401909552848452611882948135946024803595939460649492939101918190840183828082843750506040805160a08082019092529597963596608435969095506101449450925060a491506005908390839080828437509095505050505050604080518082018252600160a060020a03338116825288166020820152815160c0810190925260009173e54d323f9ef17c1f0dede47ecc86a9718fe5ea349163e3042c0f91600191908a908a9089908b90808b8b9090602002015181526020018b60016005811015610002579090602002015181526020018b60026005811015610002579090602002015181526020018b60036005811015610002579090602002015181526020018b6004600581101561000257909060200201518152602001348152602001506040518860e060020a02815260040180888152602001876002602002808383829060006004602084601f0104600f02600301f150905001868152602001806020018560ff1681526020018461ffff168152602001836006602002808383829060006004602084601f0104600f02600301f1509050018281038252868181518152602001915080519060200190808383829060006004602084601f0104600f02600301f150905090810190601f1680156105d25780820380516001836020036101000a031916815260200191505b509850505050505050505060206040518083038160008760325a03f2156100025750506040515191506124cd9050565b60408051602060248035600481810135601f81018590048502860185019096528585526118829581359591946044949293909201918190840183828082843750949650505050505050600073e54d323f9ef17c1f0dede47ecc86a9718fe5ea346350d4e411600133808787611e64610f6d565b611882600435600073e54d323f9ef17c1f0dede47ecc86a9718fe5ea346350d4e41160016000503333600060e060020a026020604051908101604052806000815260200150611d28610f6d565b61189f5b6000611bf8611159565b6118b7600073e54d323f9ef17c1f0dede47ecc86a9718fe5ea3463ea27a881600060005054611a9561159f565b6118b7600073e54d323f9ef17c1f0dede47ecc86a9718fe5ea346326a7985a6040518160e060020a02815260040180905060206040518083038160008760325a03f2156100025750506040515191506107599050565b6118b760075b90565b604080516020606435600481810135601f8101849004840285018401909552848452611882948135946024803595604435956084949201919081908401838280828437509496505093359350505050600073e54d323f9ef17c1f0dede47ecc86a9718fe5ea346350d4e41160013389898861224b610f6d565b611882600435602435600073e54d323f9ef17c1f0dede47ecc86a9718fe5ea346350d4e41160016000503386866020604051908101604052806000815260200150611e64610f6d565b611882600435602435604435606435600073e54d323f9ef17c1f0dede47ecc86a9718fe5ea346350d4e41160016000503333896020604051908101604052806000815260200150886123bc6106c6565b611882600435602435604435600073e54d323f9ef17c1f0dede47ecc86a9718fe5ea346350d4e41160016000503387866020604051908101604052806000815260200150611f8d610f6d565b60408051602060248035600481810135601f810185900485028601850190965285855261188295813595919460449492939092019181908401838280828437509496505093359350505050600073e54d323f9ef17c1f0dede47ecc86a9718fe5ea346350d4e411600133808888612225610f6d565b611882600435602435604435606435600073e54d323f9ef17c1f0dede47ecc86a9718fe5ea346350d4e41160016000503388886020604051908101604052806000815260200150612388610f6d565b611882600435604080517fc4144b2600000000000000000000000000000000000000000000000000000000815260016004820152600160a060020a03831660248201529051600091737c1eb207c07e7ab13cf245585bd03d0fa478d0349163c4144b26916044818101926020929091908290030181878760325a03f215610002575050604051519150611b409050565b604080516020604435600481810135601f81018490048402850184019095528484526118829481359460248035959394606494929391019181908401838280828437509496505093359350505050600073e54d323f9ef17c1f0dede47ecc86a9718fe5ea346350d4e411600133888888612238610f6d565b604080516020604435600481810135601f810184900484028501840190955284845261188294813594602480359593946064949293910191819084018382808284375094965050933593505060843591505060a43560c435600073e54d323f9ef17c1f0dede47ecc86a9718fe5ea346350d4e4116001338b8b8b896126536106c6565b611882600435600073e54d323f9ef17c1f0dede47ecc86a9718fe5ea346350d4e41160016000503333866020604051908101604052806000815260200150611e4a610f6d565b6118b76004355b604080517fed5bd7ea00000000000000000000000000000000000000000000000000000000815260016004820152600160a060020a03831660248201529051600091737c1eb207c07e7ab13cf245585bd03d0fa478d0349163ed5bd7ea916044818101926020929091908290030181878760325a03f215610002575050604051519150611b409050565b61189f600073e54d323f9ef17c1f0dede47ecc86a9718fe5ea3463586a69fa6040518160e060020a02815260040180905060206040518083038160008760325a03f2156100025750506040515191506107599050565b60408051602060248035600481810135601f81018590048502860185019096528585526118829581359591946044949293909201918190840183828082843750949650509335935050606435915050600073e54d323f9ef17c1f0dede47ecc86a9718fe5ea346350d4e411600133808989612388610f6d565b61188260043560243560443560643560843560a435600073e54d323f9ef17c1f0dede47ecc86a9718fe5ea346350d4e4116001600050338a896020604051908101604052806000815260200150886124d76106c6565b6040805160206004803580820135601f8101849004840285018401909552848452611882949193602493909291840191908190840183828082843750949650505050505050600073e54d323f9ef17c1f0dede47ecc86a9718fe5ea346350d4e411600133808587611e4a610f6d565b61188260043560243560443560643560843560a435600073e54d323f9ef17c1f0dede47ecc86a9718fe5ea346350d4e4116001600050338a8a60206040519081016040528060008152602001508961262d6106c6565b604080516020606435600481810135601f810184900484028501840190955284845261188294813594602480359560443595608494920191908190840183828082843750949650505050505050600073e54d323f9ef17c1f0dede47ecc86a9718fe5ea346350d4e4116001338888876120c7610f6d565b604080516020604435600481810135601f81018490048402850184019095528484526118829481359460248035959394606494929391019181908401838280828437505060408051608080820190925295979635969561010495509350608492508591508390839080828437509095505050505050600073e54d323f9ef17c1f0dede47ecc86a9718fe5ea346350d4e4116001338989898961263a6106c6565b6118b7600435602435604435600073e54d323f9ef17c1f0dede47ecc86a9718fe5ea3463ea27a881858585611ba361122c565b611882600435602435604435600073e54d323f9ef17c1f0dede47ecc86a9718fe5ea346350d4e4116001600050333388602060405190810160405280600081526020015061236e610f6d565b6118b760005481565b6118c95b600073e54d323f9ef17c1f0dede47ecc86a9718fe5ea34638e46afa96040518160e060020a02815260040180905060206040518083038160008760325a03f2156100025750506040515191506107599050565b60408051602060248035600481810135601f8101859004850286018501909652858552611882958135959194604494929390920191819084018382808284375094965050933593505060643591505060843560a43560c43560e43561010435600073e54d323f9ef17c1f0dede47ecc86a9718fe5ea346350d4e411600160005033338e8e8d8f8e8e8e8e8e346040518e60e060020a028152600401808e81526020018d600160a060020a031681526020018c600160a060020a031681526020018b8152602001806020018a60ff1681526020018961ffff16815260200188815260200187815260200186815260200185815260200184815260200183815260200182810382528b8181518152602001915080519060200190808383829060006004602084601f0104600f02600301f150905090810190601f1680156111195780820380516001836020036101000a031916815260200191505b509e50505050505050505050505050505060206040518083038160008760325a03f215610002575050604051519b9a5050505050505050505050565b61189f5b600073e54d323f9ef17c1f0dede47ecc86a9718fe5ea3463971c803f6040518160e060020a02815260040180905060206040518083038160008760325a03f2156100025750506040515191506107599050565b604080516020604435600481810135601f8101849004840285018401909552848452611882948135946024803595939460649492939101918190840183828082843750949650509335935050608435915050600073e54d323f9ef17c1f0dede47ecc86a9718fe5ea346350d4e4116001338989896123a2610f6d565b6118b75b600073e54d323f9ef17c1f0dede47ecc86a9718fe5ea346398c9cdf46040518160e060020a02815260040180905060206040518083038160008760325a03f2156100025750506040515191506107599050565b6118b7600073e54d323f9ef17c1f0dede47ecc86a9718fe5ea346398e00e546040518160e060020a02815260040180905060206040518083038160008760325a03f2156100025750506040515191506107599050565b611882600435604080517fe6ce3a6a000000000000000000000000000000000000000000000000000000008152600160048201527f3e3d0000000000000000000000000000000000000000000000000000000000006024820152604481018390529051600091737c1eb207c07e7ab13cf245585bd03d0fa478d0349163e6ce3a6a916064818101926020929091908290030181878760325a03f215610002575050604051519150611b409050565b611882600435602435600073e54d323f9ef17c1f0dede47ecc86a9718fe5ea346350d4e41160016000503385600060e060020a0260206040519081016040528060008152602001506121ef610f6d565b604080516020604435600481810135601f8101849004840285018401909552848452611882948135946024803595939460649492939101918190840183828082843750949650505050505050600073e54d323f9ef17c1f0dede47ecc86a9718fe5ea346350d4e4116001338787876120b5610f6d565b6118b7600435600073e54d323f9ef17c1f0dede47ecc86a9718fe5ea3463ea27a88183611b4561159f565b6118b75b600073e54d323f9ef17c1f0dede47ecc86a9718fe5ea3463b152f19e6040518160e060020a02815260040180905060206040518083038160008760325a03f2156100025750506040515191506107599050565b60408051602060248035600481810135601f8101859004850286018501909652858552611882958135959194604494929390920191819084018382808284375094965050933593505060643591505060843560a435600073e54d323f9ef17c1f0dede47ecc86a9718fe5ea346350d4e411600133808b8b8961262d6106c6565b611882600435602435600073e54d323f9ef17c1f0dede47ecc86a9718fe5ea346350d4e41160016000503386600060e060020a026020604051908101604052806000815260200150612200610f6d565b6118b75b60005460649004610759565b6118b7600073e54d323f9ef17c1f0dede47ecc86a9718fe5ea3463c0f688596040518160e060020a02815260040180905060206040518083038160008760325a03f2156100025750506040515191506107599050565b611882600073e54d323f9ef17c1f0dede47ecc86a9718fe5ea346350d4e41160016000503333600060e060020a026020604051908101604052806000815260200150611bff610f6d565b611882600435602435600073e54d323f9ef17c1f0dede47ecc86a9718fe5ea346350d4e41160016000503333876020604051908101604052806000815260200150612200610f6d565b611882600435602435604435600073e54d323f9ef17c1f0dede47ecc86a9718fe5ea346350d4e41160016000503387600060e060020a026020604051908101604052806000815260200150612213610f6d565b611882600435602435604435606435608435600073e54d323f9ef17c1f0dede47ecc86a9718fe5ea346350d4e411600160005033338a60206040519081016040528060008152602001508961250c6106c6565b61027c6000600060006118e033610b56565b6118b7600435602435604435606435600073e54d323f9ef17c1f0dede47ecc86a9718fe5ea3463ea27a881868686866040518560e060020a0281526004018085815260200184815260200183815260200182815260200194505050505060206040518083038160008760325a03f215610002575050604051519150505b949350505050565b604080516020604435600481810135601f810184900484028501840190955284845261188294813594602480359593946064949293910191819084018382808284375094965050933593505060843591505060a435600073e54d323f9ef17c1f0dede47ecc86a9718fe5ea346350d4e4116001338a8a8a886124fa6106c6565b6118b7600435602435600073e54d323f9ef17c1f0dede47ecc86a9718fe5ea3463ea27a88184846000611b4f61122c565b60408051600160a060020a03929092168252519081900360200190f35b6040805161ffff929092168252519081900360200190f35b60408051918252519081900360200190f35b6040805160ff929092168252519081900360200190f35b15611a905733925082600160a060020a031663c6502da86040518160e060020a0281526004018090506020604051808303816000876161da5a03f1156100025750506040805180517fc6803622000000000000000000000000000000000000000000000000000000008252915191945063c680362291600482810192602092919082900301816000876161da5a03f11561000257505060405151905080156119d1575082600160a060020a031663d379be236040518160e060020a0281526004018090506020604051808303816000876161da5a03f11561000257505060405151600160a060020a03166000141590505b80156119dd5750600082115b80156119ec5750600054600190115b15611a90578183600160a060020a031663830953ab6040518160e060020a0281526004018090506020604051808303816000876161da5a03f1156100025750506040515160640291909104915050604281118015611a4d5750600054829011155b15611a675760008054612710612711909102049055611a90565b602181108015611a7a5750600054829010155b15611a90576000805461271061270f9091020490555b505050565b6000611a9f61122c565b6040518560e060020a0281526004018085815260200184815260200183815260200182815260200194505050505060206040518083038160008760325a03f2156100025750506040515191506107599050565b6040518560e060020a0281526004018085815260200184815260200183815260200182815260200194505050505060206040518083038160008760325a03f215610002575050604051519150505b919050565b6000611af261122c565b6040518560e060020a0281526004018085815260200184815260200183815260200182815260200194505050505060206040518083038160008760325a03f215610002575050604051519150505b92915050565b6040518560e060020a0281526004018085815260200184815260200183815260200182815260200194505050505060206040518083038160008760325a03f215610002575050604051519150505b9392505050565b9050610759565b611c076106c6565b6000611c11611478565b611c1961122c565b600054611c2461159f565b346040518e60e060020a028152600401808e81526020018d600160a060020a031681526020018c600160a060020a031681526020018b8152602001806020018a60ff1681526020018961ffff16815260200188815260200187815260200186815260200185815260200184815260200183815260200182810382528b8181518152602001915080519060200190808383829060006004602084601f0104600f02600301f150905090810190601f168015611cf25780820380516001836020036101000a031916815260200191505b509e50505050505050505050505050505060206040518083038160008760325a03f2156100025750506040515191506107599050565b611d306106c6565b60008b611d3b61122c565b600054611d4661159f565b346040518e60e060020a028152600401808e81526020018d600160a060020a031681526020018c600160a060020a031681526020018b8152602001806020018a60ff1681526020018961ffff16815260200188815260200187815260200186815260200185815260200184815260200183815260200182810382528b8181518152602001915080519060200190808383829060006004602084601f0104600f02600301f150905090810190601f168015611e145780820380516001836020036101000a031916815260200191505b509e50505050505050505050505050505060206040518083038160008760325a03f215610002575050604051519150611b409050565b611e526106c6565b6000611e5c611478565b611d3b61122c565b611e6c6106c6565b6000611e76611478565b611e7e61122c565b600054611e8961159f565b346040518e60e060020a028152600401808e81526020018d600160a060020a031681526020018c600160a060020a031681526020018b8152602001806020018a60ff1681526020018961ffff16815260200188815260200187815260200186815260200185815260200184815260200183815260200182810382528b8181518152602001915080519060200190808383829060006004602084601f0104600f02600301f150905090810190601f168015611f575780820380516001836020036101000a031916815260200191505b509e50505050505050505050505050505060206040518083038160008760325a03f215610002575050604051519150611b9d9050565b611f956106c6565b8b611f9e611478565b611fa661122c565b600054611fb161159f565b346040518e60e060020a028152600401808e81526020018d600160a060020a031681526020018c600160a060020a031681526020018b8152602001806020018a60ff1681526020018961ffff16815260200188815260200187815260200186815260200185815260200184815260200183815260200182810382528b8181518152602001915080519060200190808383829060006004602084601f0104600f02600301f150905090810190601f16801561207f5780820380516001836020036101000a031916815260200191505b509e50505050505050505050505050505060206040518083038160008760325a03f215610002575050604051519150611bf19050565b6120bd6106c6565b6000611f9e611478565b6120cf6106c6565b8b6120d8611478565b6120e061122c565b6000546120eb61159f565b346040518e60e060020a028152600401808e81526020018d600160a060020a031681526020018c600160a060020a031681526020018b8152602001806020018a60ff1681526020018961ffff16815260200188815260200187815260200186815260200185815260200184815260200183815260200182810382528b8181518152602001915080519060200190808383829060006004602084601f0104600f02600301f150905090810190601f1680156121b95780820380516001836020036101000a031916815260200191505b509e50505050505050505050505050505060206040518083038160008760325a03f2156100025750506040515191506117c99050565b6121f76106c6565b8b611e76611478565b6122086106c6565b60008b611e7e61122c565b61221b6106c6565b8a8c611fa661122c565b61222d6106c6565b60008b611fa661122c565b6122406106c6565b60008b6120e061122c565b6122536106c6565b8c8b61225d61122c565b60005461226861159f565b346040518e60e060020a028152600401808e81526020018d600160a060020a031681526020018c600160a060020a031681526020018b8152602001806020018a60ff1681526020018961ffff16815260200188815260200187815260200186815260200185815260200184815260200183815260200182810382528b8181518152602001915080519060200190808383829060006004602084601f0104600f02600301f150905090810190601f1680156123365780820380516001836020036101000a031916815260200191505b509e50505050505050505050505050505060206040518083038160008760325a03f21561000257505060405151979650505050505050565b6123766106c6565b60008c8c600060005054611fb161159f565b6123906106c6565b60008c8c6000600050546120eb61159f565b6123aa6106c6565b60008c8c60006000505461226861159f565b60008d8d6000600050546120eb61159f565b346040518e60e060020a028152600401808e81526020018d600160a060020a031681526020018c600160a060020a031681526020018b8152602001806020018a60ff1681526020018961ffff16815260200188815260200187815260200186815260200185815260200184815260200183815260200182810382528b8181518152602001915080519060200190808383829060006004602084601f0104600f02600301f150905090810190601f16801561249c5780820380516001836020036101000a031916815260200191505b509e50505050505050505050505050505060206040518083038160008760325a03f215610002575050604051519150505b9695505050505050565b8e8d8d6000600050546123ce61159f565b60008d8d60006000505461226861159f565b60008d8d6000600050546123ce61159f565b60008e8e8d61226861159f565b8f8e8e8d61252561159f565b346040518e60e060020a028152600401808e81526020018d600160a060020a031681526020018c600160a060020a031681526020018b8152602001806020018a60ff1681526020018961ffff16815260200188815260200187815260200186815260200185815260200184815260200183815260200182810382528b8181518152602001915080519060200190808383829060006004602084601f0104600f02600301f150905090810190601f1680156125f35780820380516001836020036101000a031916815260200191505b509e50505050505050505050505050505060206040518083038160008760325a03f215610002575050604051519998505050505050505050565b60008e8e8d6123ce61159f565b8a5160208c015160408d015160608e015161226861159f565b60008e8e8d61252561159f56", + "storage": { + "0x0000000000000000000000000000000000000000000000000000000000000000": "0x0000000000000000000000000000000000000000000000000011f8119429ed3a", + "0x0000000000000000000000000000000000000000000000000000000000000001": "0x000000000000000000000000f5d861791e76fa01433e0d7421aee565290e4afe", + "0x031b9ec274101cc3ccff4d6d98ef4513742dadbaadba538bff48b88403253234": "0x0000000000000000000000000000000000000000000000000000000000000001", + "0x20ef51bb8ea9e8e8d5e2c17d28e47285698893c1017db4b4e40b792358a3dbc7": "0x0000000000000000000000000000000000000000000000000000000000000004", + "0x26cba0705aade77fa0f9275b68d01fb71206a44abd3a4f5a838f7241efbc8abd": "0x000000000000000000000000c9a2bfd279fe57e7651e5d9f29bb1793c9a1cf01", + "0x26cba0705aade77fa0f9275b68d01fb71206a44abd3a4f5a838f7241efbc8abf": "0x00000000000000000000000042e69cd0a17ae9992f9ad93d136c4bb0d95e3230", + "0x26cba0705aade77fa0f9275b68d01fb71206a44abd3a4f5a838f7241efbc8ac2": "0x0000000000000000000000000000000000000000000000000000000000000001", + "0x37a551428681c06e6f97b79bb6c8c325935dc1a51b31a982594f40f2dd794dfb": "0x000000000000000000000000f5d861791e76fa01433e0d7421aee565290e4afe", + "0x37a551428681c06e6f97b79bb6c8c325935dc1a51b31a982594f40f2dd794dfc": "0x00000000000000000000000000000000000000000000000000000000000f6897", + "0x37a551428681c06e6f97b79bb6c8c325935dc1a51b31a982594f40f2dd794dfd": "0x0000000000000000000000000000000000000000000000000000000000000000", + "0x37a551428681c06e6f97b79bb6c8c325935dc1a51b31a982594f40f2dd794dfe": "0x0000000000000000000000002859ddf2877c46d54e67b6becdb1cafb8ef4a458", + "0x37a551428681c06e6f97b79bb6c8c325935dc1a51b31a982594f40f2dd794dff": "0x000000000000000000000000b7df3c43a8b13ecf45777c267404e15c7cdb04c9", + "0x37a551428681c06e6f97b79bb6c8c325935dc1a51b31a982594f40f2dd794e00": "0x0000000000000000000000000000000000000000000000000000000000000008", + "0x3b20a4b931bc4ae9450774ee52b8f5da1b248d23e61cd20c09b25662f73894fd": "0x0000000000000000000000000000000000000000000000000000000000000006", + "0x3b99aee1e3090227401ac2055c861246ca6ec62f426b4b4d74df88510f841b89": "0x0000000000000000000000000000000000000000000000000000000000000007", + "0x49f03a2c2f4fd666a32141fb324283b6f84a1d07b5fa435669fdb55766aef711": "0x000000000000000000000000a4d91b341f0e9a7000be916a668408b463f4c38c", + "0x49f03a2c2f4fd666a32141fb324283b6f84a1d07b5fa435669fdb55766aef712": "0x0000000000000000000000000000000000000000000000000000000000102ce9", + "0x49f03a2c2f4fd666a32141fb324283b6f84a1d07b5fa435669fdb55766aef713": "0x000000000000000000000000fd97a0d81cc92eecd52452831930b27889925ef0", + "0x49f03a2c2f4fd666a32141fb324283b6f84a1d07b5fa435669fdb55766aef714": "0x00000000000000000000000016917c151bb1399852a0741eb7b317b443e2cfa3", + "0x49f03a2c2f4fd666a32141fb324283b6f84a1d07b5fa435669fdb55766aef715": "0x000000000000000000000000d7b0e93fa8386b17fb5d1cf934076203dcc122f3", + "0x49f03a2c2f4fd666a32141fb324283b6f84a1d07b5fa435669fdb55766aef716": "0x0000000000000000000000000000000000000000000000000000000000000004", + "0x5d866e5ddc53cb4c50f232302c51f03204d70c867baf663c9211cc229676a3fe": "0x000000000000000000000000c5ef24ec3bf0e3522cfc8e53f3e076b043547ce1", + "0x5d866e5ddc53cb4c50f232302c51f03204d70c867baf663c9211cc229676a3ff": "0x00000000000000000000000000000000000000000000000000000000000fff67", + "0x5d866e5ddc53cb4c50f232302c51f03204d70c867baf663c9211cc229676a400": "0x000000000000000000000000b7df3c43a8b13ecf45777c267404e15c7cdb04c9", + "0x5d866e5ddc53cb4c50f232302c51f03204d70c867baf663c9211cc229676a401": "0x00000000000000000000000010fc2e8ba5f40336c3576ffaa25177f1cdedf836", + "0x5d866e5ddc53cb4c50f232302c51f03204d70c867baf663c9211cc229676a402": "0x000000000000000000000000fd97a0d81cc92eecd52452831930b27889925ef0", + "0x5d866e5ddc53cb4c50f232302c51f03204d70c867baf663c9211cc229676a403": "0x0000000000000000000000000000000000000000000000000000000000000006", + "0x95e05d02b91af970cb4998107e8613455258880676e00b819c12d675e60de5ba": "0x00000000000000000000000042e69cd0a17ae9992f9ad93d136c4bb0d95e3230", + "0x95e05d02b91af970cb4998107e8613455258880676e00b819c12d675e60de5bb": "0x000000000000000000000000000000000000000000000000000000000010347b", + "0x95e05d02b91af970cb4998107e8613455258880676e00b819c12d675e60de5bc": "0x000000000000000000000000d7b0e93fa8386b17fb5d1cf934076203dcc122f3", + "0x95e05d02b91af970cb4998107e8613455258880676e00b819c12d675e60de5bd": "0x000000000000000000000000c9a2bfd279fe57e7651e5d9f29bb1793c9a1cf01", + "0x95e05d02b91af970cb4998107e8613455258880676e00b819c12d675e60de5be": "0x000000000000000000000000741467b251fca923d6229c4b439078b55dca233b", + "0x95e05d02b91af970cb4998107e8613455258880676e00b819c12d675e60de5bf": "0x0000000000000000000000000000000000000000000000000000000000000002", + "0x99d5294a34e2d6d560a223237786adc8b5651c09094b9ecd56e6ae7abc2a2751": "0x0000000000000000000000000000000000000000000000000000000000000000", + "0x99d5294a34e2d6d560a223237786adc8b5651c09094b9ecd56e6ae7abc2a2752": "0x0000000000000000000000000000000000000000000000000000000000000000", + "0x99d5294a34e2d6d560a223237786adc8b5651c09094b9ecd56e6ae7abc2a2753": "0x0000000000000000000000000000000000000000000000000000000000000000", + "0x99d5294a34e2d6d560a223237786adc8b5651c09094b9ecd56e6ae7abc2a2754": "0x0000000000000000000000000000000000000000000000000000000000000000", + "0x99d5294a34e2d6d560a223237786adc8b5651c09094b9ecd56e6ae7abc2a2755": "0x0000000000000000000000000000000000000000000000000000000000000000", + "0x99d5294a34e2d6d560a223237786adc8b5651c09094b9ecd56e6ae7abc2a2756": "0x0000000000000000000000000000000000000000000000000000000000000000", + "0xa9e249fecbfa0518be95c32972ad551c71206081844335006bb2a349490826a7": "0x000000000000000000000000b7df3c43a8b13ecf45777c267404e15c7cdb04c9", + "0xa9e249fecbfa0518be95c32972ad551c71206081844335006bb2a349490826a8": "0x00000000000000000000000000000000000000000000000000000000000fe13d", + "0xa9e249fecbfa0518be95c32972ad551c71206081844335006bb2a349490826a9": "0x000000000000000000000000f5d861791e76fa01433e0d7421aee565290e4afe", + "0xa9e249fecbfa0518be95c32972ad551c71206081844335006bb2a349490826aa": "0x00000000000000000000000063110531142fb314118164ff579ba52746504408", + "0xa9e249fecbfa0518be95c32972ad551c71206081844335006bb2a349490826ab": "0x000000000000000000000000c5ef24ec3bf0e3522cfc8e53f3e076b043547ce1", + "0xa9e249fecbfa0518be95c32972ad551c71206081844335006bb2a349490826ac": "0x0000000000000000000000000000000000000000000000000000000000000007", + "0xac33ff75c19e70fe83507db0d683fd3465c996598dc972688b7ace676c890780": "0x0000000000000000000000000000000000000000000000000000000000000000", + "0xccd2cbc946692be8ade97db99353304e3af0fa6202f93649d4e185ad8b1f385c": "0x0000000000000000000000000000000000000000000000000000000000000004", + "0xd3a5582b3eff6ef8ee90f3962e9d598a3f4b7d07840356c9b8fd7b494879b4ef": "0x000000000000000000000000d7b0e93fa8386b17fb5d1cf934076203dcc122f3", + "0xd3a5582b3eff6ef8ee90f3962e9d598a3f4b7d07840356c9b8fd7b494879b4f0": "0x00000000000000000000000000000000000000000000000000000000001030b3", + "0xd3a5582b3eff6ef8ee90f3962e9d598a3f4b7d07840356c9b8fd7b494879b4f1": "0x000000000000000000000000a4d91b341f0e9a7000be916a668408b463f4c38c", + "0xd3a5582b3eff6ef8ee90f3962e9d598a3f4b7d07840356c9b8fd7b494879b4f2": "0x000000000000000000000000dd87a67740c2acf48a31829783a095a81c3628d9", + "0xd3a5582b3eff6ef8ee90f3962e9d598a3f4b7d07840356c9b8fd7b494879b4f3": "0x00000000000000000000000042e69cd0a17ae9992f9ad93d136c4bb0d95e3230", + "0xd3a5582b3eff6ef8ee90f3962e9d598a3f4b7d07840356c9b8fd7b494879b4f4": "0x0000000000000000000000000000000000000000000000000000000000000003", + "0xdabde47554d6a6cfcff3c968abb145f298585fafa9e24c10fc526269794bd626": "0x0000000000000000000000000000000000000000000000000000000000000003", + "0xf7518490c515b9fc8e7fe713b647fe88eacefc92d616fa9454e61fe9aab64db7": "0x000000000000000000000000741467b251fca923d6229c4b439078b55dca233b", + "0xf7518490c515b9fc8e7fe713b647fe88eacefc92d616fa9454e61fe9aab64db8": "0x000000000000000000000000000000000000000000000000000000000010365c", + "0xf7518490c515b9fc8e7fe713b647fe88eacefc92d616fa9454e61fe9aab64db9": "0x00000000000000000000000042e69cd0a17ae9992f9ad93d136c4bb0d95e3230", + "0xf7518490c515b9fc8e7fe713b647fe88eacefc92d616fa9454e61fe9aab64dba": "0x0000000000000000000000000000000000000000000000000000000000000000", + "0xf7518490c515b9fc8e7fe713b647fe88eacefc92d616fa9454e61fe9aab64dbb": "0x0000000000000000000000000000000000000000000000000000000000000000", + "0xf7518490c515b9fc8e7fe713b647fe88eacefc92d616fa9454e61fe9aab64dbc": "0x0000000000000000000000000000000000000000000000000000000000000001", + "0xfbba286dd5525a6ed3322411df4f261c98e43b123fef71777adc2b44d705bdec": "0x000000000000000000000000fd97a0d81cc92eecd52452831930b27889925ef0", + "0xfbba286dd5525a6ed3322411df4f261c98e43b123fef71777adc2b44d705bded": "0x0000000000000000000000000000000000000000000000000000000000101dc2", + "0xfbba286dd5525a6ed3322411df4f261c98e43b123fef71777adc2b44d705bdee": "0x000000000000000000000000c5ef24ec3bf0e3522cfc8e53f3e076b043547ce1", + "0xfbba286dd5525a6ed3322411df4f261c98e43b123fef71777adc2b44d705bdef": "0x000000000000000000000000173243e117a6382211b1ac91eeb262f4a7021c16", + "0xfbba286dd5525a6ed3322411df4f261c98e43b123fef71777adc2b44d705bdf0": "0x000000000000000000000000a4d91b341f0e9a7000be916a668408b463f4c38c", + "0xfbba286dd5525a6ed3322411df4f261c98e43b123fef71777adc2b44d705bdf1": "0x0000000000000000000000000000000000000000000000000000000000000005" + } + }, + "0x741467b251fca923d6229c4b439078b55dca233b": { + "balance": "0x29c613529e8218f8", + "code": "0x606060405236156101a05760e060020a60003504630924120081146101c25780630a16697a146101cf5780630fd1f94e146101d8578063137c638b1461022e57806321835af61461023b57806324032866146102545780632f95b833146102d65780633017fe24146102e55780633233c686146102ef57806337f4c00e146102fa5780634500054f146103055780634e417a98146103785780634e71d92d146103e15780634f059a43146103f35780636146195414610451578063625cc4651461046157806367ce940d1461046a5780637d298ee314610477578063830953ab146104f9578063938b5f321461050457806395ee122114610516578063974654f41461052a578063a06db7dc14610535578063a9d2293d14610541578063ae45850b14610597578063b0f07e44146105a9578063c19d93fb146105cb578063c6502da81461062e578063c680362214610637578063ca94692d1461064a578063cc3471af14610673578063d379be23146106c9578063d62457f6146106e3578063ea8a1af0146106ee578063f5562753146107f3578063f6b4dfb414610854575b610868600080548190600160a060020a03908116339091161461087a57610994565b610868600b5460ff165b90565b610868600d5481565b6108686000731deeda36e15ec9e80f3d7414d67a4803ae45fc80630fd1f94e6040518160e060020a02815260040180905060206040518083038160008760325a03f2156100025750506040515191506101cc9050565b6108685b62012cc86101cc565b61086860043560008160001415610dc65750600161084f565b6108686004356024356000731deeda36e15ec9e80f3d7414d67a4803ae45fc80630bd295e6600360005085856040518460e060020a0281526004018084815260200183600160a060020a03168152602001828152602001935050505060206040518083038160008760325a03f215610002575050604051519150505b92915050565b61099860085461ffff166101cc565b61086860026101cc565b610868600a546101cc565b6108686006546101cc565b6108686000731deeda36e15ec9e80f3d7414d67a4803ae45fc8063a09431546003600050336040518360e060020a0281526004018083815260200182600160a060020a031681526020019250505060206040518083038160008760325a03f2156100025750506040515191506101cc9050565b6109af60408051602081810183526000825282516004805460026001821615610100026000190190911604601f81018490048402830184019095528482529293909291830182828015610a7d5780601f10610a5257610100808354040283529160200191610a7d565b61086860006000600180610b7b6105cf565b6108686000731deeda36e15ec9e80f3d7414d67a4803ae45fc8063f5562753436040518260e060020a0281526004018082815260200191505060206040518083038160008760325a03f2156100025750506040515191506101cc9050565b610a1d6000600480610c986105cf565b61086860025481565b6108685b620186a06101cc565b6108686004356024355b6000731deeda36e15ec9e80f3d7414d67a4803ae45fc8063a1873db6600360005085856040518460e060020a0281526004018084815260200183600160a060020a03168152602001828152602001935050505060206040518083038160008760325a03f2156100025750506040515191506102d09050565b6108686009546101cc565b610a1f600c54600160a060020a031681565b610868600b5462010000900460ff166101cc565b6108686007546101cc565b610a3c600e5460ff1681565b6108686000731deeda36e15ec9e80f3d7414d67a4803ae45fc8063a9d2293d6040518160e060020a02815260040180905060206040518083038160008760325a03f2156100025750506040515191506101cc9050565b610a1f600054600160a060020a031681565b610868600080548190600160a060020a039081163390911614610a8957610994565b6108685b6000731deeda36e15ec9e80f3d7414d67a4803ae45fc80635054d98a60036000506040518260e060020a0281526004018082815260200191505060206040518083038160008760325a03f2156100025750506040515191506101cc9050565b61086860015481565b610868600b54610100900460ff166101cc565b61086860035474010000000000000000000000000000000000000000900460e060020a026101cc565b6108686000731deeda36e15ec9e80f3d7414d67a4803ae45fc8063cc3471af6040518160e060020a02815260040180905060206040518083038160008760325a03f2156100025750506040515191506101cc9050565b610a1f600854620100009004600160a060020a03166101cc565b6108686005546101cc565b610a1d604080517fa09431540000000000000000000000000000000000000000000000000000000081526003600482015233600160a060020a031660248201529051731deeda36e15ec9e80f3d7414d67a4803ae45fc809163a0943154916044808301926020929190829003018160008760325a03f215610002575050604051511590506107f157604080517f7e9265620000000000000000000000000000000000000000000000000000000081526003600482015233600160a060020a031660248201529051731deeda36e15ec9e80f3d7414d67a4803ae45fc8091637e9265629160448083019260009291908290030181838760325a03f215610002575050505b565b6108686004356000731deeda36e15ec9e80f3d7414d67a4803ae45fc8063f5562753836040518260e060020a0281526004018082815260200191505060206040518083038160008760325a03f215610002575050604051519150505b919050565b610a1f600354600160a060020a03166101cc565b60408051918252519081900360200190f35b60045460006002600183161561010002600019019092169190910411156108a45760009150610994565b6108ac6105cf565b9050600081141580156108c0575060018114155b80156108cd575060028114155b156108db5760009150610994565b600480546000828152602060026001841615610100026000190190931692909204601f908101929092047f8a35acfbc15ff81a39ae7d344fd709f28e8600b4aa8c65c6b64bfe7fe36bd19b9081019236929083901061095d5782800160ff198235161785555b5061098d9291505b808211156109945760008155600101610949565b82800160010185558215610941579182015b8281111561094157823582600050559160200191906001019061096f565b5050600191505b5090565b6040805161ffff9092168252519081900360200190f35b60405180806020018281038252838181518152602001915080519060200190808383829060006004602084601f0104600f02600301f150905090810190601f168015610a0f5780820380516001836020036101000a031916815260200191505b509250505060405180910390f35b005b60408051600160a060020a03929092168252519081900360200190f35b6040805160ff9092168252519081900360200190f35b820191906000526020600020905b815481529060010190602001808311610a6057829003601f168201915b505050505090506101cc565b6004546000600260018316156101000260001901909216919091041115610ab35760009150610994565b610abb6105cf565b905060008114158015610acf575060018114155b8015610adc575060028114155b15610aea5760009150610994565b604080517f7c0278fc000000000000000000000000000000000000000000000000000000008152600360048201818152602483019384523660448401819052731deeda36e15ec9e80f3d7414d67a4803ae45fc8094637c0278fc946000939190606401848480828437820191505094505050505060006040518083038160008760325a03f215610002575050505090565b1415610c8557604080516001547f0fee183d0000000000000000000000000000000000000000000000000000000082526003600483015233600160a060020a0316602483015234604483015260648201529051731deeda36e15ec9e80f3d7414d67a4803ae45fc8091630fee183d916084828101926020929190829003018160008760325a03f21561000257505060405151925050811515610c8a577389efe605e9ecbe22849cd85d5449cc946c26f8f36312c82bcc33346040518360e060020a0281526004018083600160a060020a031681526020018281526020019250505060206040518083038160008760325a03f2156100025750506040515115159050610c8a57610002565b505090565b81925050610994565b505b50565b1415610c93575a9150610cab3383610481565b1515610cb75750610c95565b731deeda36e15ec9e80f3d7414d67a4803ae45fc8063da46be0a60038433610cdd61046e565b610ce5610232565b6040518660e060020a0281526004018086815260200185815260200184600160a060020a031681526020018381526020018281526020019550505050505060006040518083038160008760325a03f21561000257505050610c933360408051600080547fc17e6817000000000000000000000000000000000000000000000000000000008352600160a060020a03908116600484015230163160248301529151731deeda36e15ec9e80f3d7414d67a4803ae45fc809263c17e68179260448082019360209390928390039091019082908760325a03f2156100025750505050565b30600160a060020a031660405180807f5f5f6469672875696e7432353629000000000000000000000000000000000000815260200150600e019050604051809103902060e060020a8091040260e060020a9004600184036040518260e060020a0281526004018082815260200191505060006040518083038160008760325a03f292505050151561084f5761000256", + "storage": { + "0x0000000000000000000000000000000000000000000000000000000000000000": "0x0000000000000000000000007dd677b54fc954824a7bc49bd26cbdfa12c75adf", + "0x0000000000000000000000000000000000000000000000000000000000000001": "0x0000000000000000000000000000000000000000000000000011f79bd42b0c7c", + "0x0000000000000000000000000000000000000000000000000000000000000002": "0x00000000000000000000000000000000000000000000000000002dfeff8fca5d", + "0x0000000000000000000000000000000000000000000000000000000000000003": "0x00000000000000003defb9627dd677b54fc954824a7bc49bd26cbdfa12c75adf", + "0x0000000000000000000000000000000000000000000000000000000000000004": "0x0000000000000000000000000000000000000000000000000000000000000000", + "0x0000000000000000000000000000000000000000000000000000000000000005": "0x0000000000000000000000000000000000000000000000000000000000000000", + "0x0000000000000000000000000000000000000000000000000000000000000006": "0x0000000000000000000000000000000000000000000000000000000ba43b7400", + "0x0000000000000000000000000000000000000000000000000000000000000007": "0x00000000000000000000000000000000000000000000000000000000001e8480", + "0x0000000000000000000000000000000000000000000000000000000000000008": "0x000000000000000000000000000000000000000000000000000000000000000a", + "0x000000000000000000000000000000000000000000000000000000000000000a": "0x0000000000000000000000000000000000000000000000000000000000000000", + "0x000000000000000000000000000000000000000000000000000000000000000b": "0x0000000000000000000000000000000000000000000000000000000000000000", + "0x000000000000000000000000000000000000000000000000000000000000000c": "0x0000000000000000000000006c8f2a135f6ed072de4503bd7c4999a1a17f824b", + "0x000000000000000000000000000000000000000000000000000000000000000d": "0x000000000000000000000000000000000000000000000000000000000010365c", + "0x000000000000000000000000000000000000000000000000000000000000000e": "0x00000000000000000000000000000000000000000000000000000000000000ff" + } + }, + "0x7c1eb207c07e7ab13cf245585bd03d0fa478d034": { + "balance": "0x0", + "code": "0x650200d2f18c7350606060405236156100a05760e060020a60003504630e9f1a3c81146100a55780632b4096b4146100c95780636ec13982146100eb578063a3119e571461010d578063a749f19b1461012f578063ab7366f714610151578063bacd69581461017f578063bfdf87c0146101c2578063c4144b26146101e1578063caa46c9c1461023c578063e6ce3a6a14610297578063ed5bd7ea146102b6575b610007565b6102d960043560243560008181526001830160205260409020600401545b92915050565b6102d960043560243560008181526001830160205260409020600301546100c3565b6102d960043560243560008181526001830160205260409020600201546100c3565b6102d960043560243560008181526001838101602052604090912001546100c3565b6102d960043560243560008181526001830160205260409020600501546100c3565b6102eb6004356024355b600081815260018301602052604081208054829182918291908614610790576101b9565b6102eb600435602435604435600082815260018401602052604081205481908190819086141561068a576040812060010154851415610680575b50505050505050565b6102d960043560243560008181526001830160205260409020546100c3565b6102d96004356024355b6040805160c08101825260008082526020828101829052828401829052606083018290526080830182905260a08301829052848252600186019052918220805490919083908114156102fb576102f2565b6102d96004356024355b6040805160c08101825260008082526020828101829052828401829052606083018290526080830182905260a08301829052848252600186019052918220805490919083908114156104c0576102f2565b6102d960043560243560443582546000908181811415610a6557610a8c565b6102d96004356024356000818152600183016020526040812060050154116100c3565b60408051918252519081900360200190f35b005b815193505b50505092915050565b60048301546000146103d257600483810154600090815260018881016020908152604092839020835160c081018552815481529281015491830191909152600281015492820192909252600382015460608201529181015460808301526005015460a082015291505b60608201516000146102ed57606091820151600090815260018781016020908152604092839020835160c081018552815481529281015491830191909152600281015492820192909252600382015493810193909352600481015460808401526005015460a0830152610364565b600283015460001461045b5750506002810154600081815260018681016020908152604092839020835160c081018552865481529286015491830191909152918101929092526003830154606083015260048301546080830152600583015460a08301525b81516003820154141561044d57805493506102f2565b600281015460001415610464575b600093506102f2565b6040805160c08101825282548152600183810154602083810191909152600285015483850181905260038601546060850152600486015460808501526005959095015460a0840152600094855290890190529120909150610437565b600383015460001461059757600383810154600090815260018881016020908152604092839020835160c081018552815481529281015491830191909152600281015492820192909252918101546060830152600481015460808301526005015460a082015291505b60808201516000146102ed57608091820151600090815260018781016020908152604092839020835160c081018552815481529281015491830191909152600281015492820192909252600382015460608201526004820154938101939093526005015460a0830152610529565b600283015460001461045b5750506002810154600081815260018681016020908152604092839020835160c081018552865481529286015491830191909152918101929092526003830154606083015260048301546080830152600583015460a08301525b81516004820154141561061257805493506102f2565b6002810154600014156106245761045b565b6040805160c08101825282548152600183810154602083810191909152600285015483850181905260038601546060850152600486015460808501526005959095015460a08401526000948552908901905291209091506105fc565b61068a878761015b565b86546000925082141561069b578587555b508554600090815260018701602052604090205b8054600014156107255785815560028101829055600181018590556101b987875b60008181526001830160205260408120905b8154610d8e9085905b60008181526001830160205260408082206004810154835281832060059081015460038301548552929093209092015403905b5092915050565b60018101548154925085126107625760048101546000141561074957600481018690555b60040154600090815260018701602052604090206106af565b60038101546000141561077757600381018690555b60030154600090815260018701602052604090206106af565b600381015460001415806107a957506004810154600014155b156107cf576003810154600014610826578054600188019060009061083b908a90610246565b6002810154600014610a285760028101546000908152600188016020526040902060038101548254919550141561080857600060038501555b60048401548154141561081d57600060048501555b83549150610a2d565b80546001880190600090610852908a906101eb565b815260208101919091526040016000209450610865565b8152602081019190915260400160002094505b600285015460009081526001880160205260409020600381015486549195509092508214156108b9576004850154600385018190556000146108b95760048501546000908152604090208454600282015592505b60048401548554141561091357600385015460048501819055600014610913578660010160005060008660030160005054815260200190815260200160002060005092508250836000016000505483600201600050819055505b60028082015490860181905560001461098457866001016000506000826002016000505481526020019081526020016000206000509350835080600001600050548460030160005054141561096a57845460038501555b60048401548154141561097f57845460048501555b610989565b845487555b6003818101549086018190556000146109d6578660010160005060008260030160005054815260200190815260200160002060005092508250846000016000505483600201600050819055505b600481810154908601819055600014610a23578660010160005060008260040160005054815260200190815260200160002060005092508250846000016000505483600201600050819055505b610a2d565b600087555b6000808255600182018190556002820181905560038201819055600482018190556005820181905582146101b9576101b987836106d0565b50600081815260018601602052604090205b6001810154610a95908686610ad4565b805492505b50509392505050565b15610b915760fa60020a600f02851480610ab6575060f060020a613c3d0285145b15610af157600481015460001415610b3a5780549250610a8c565b86865b600060f960020a601f02831415610ce357508083135b9392505050565b60f960020a601f02851480610b0d575060f060020a613e3d0285145b80610b1f575060f060020a613d3d0285145b15610b9157600381015460001415610bc85780549250610a8c565b610b73610ad1878360040160005054600081815260018301602052604081205b600381015460001415610d61576001810154915061071e565b15610a87576004015460009081526001860160205260409020610a77565b60fa60020a600f02851480610bad575060f060020a613c3d0285145b15610c1f57600381015460001415610c565760009250610a8c565b610c01610ad1878360030160005054600081815260018301602052604081205b600481015460001415610d48576001810154915061071e565b15610a87576003015460009081526001860160205260409020610a77565b60f960020a601f02851480610c3b575060f060020a613e3d0285145b15610c6f57600481015460001415610ca25760009250610a8c565b6003015460009081526001860160205260409020610a77565b60f060020a613d3d02851415610cde57600181015484901215610cbb57600481015460001415610ca25760009250610a8c565b6004015460009081526001860160205260409020610a77565b600181015484901315610cde57600381015460001415610c565760009250610a8c565b610a77565b60fa60020a600f02831415610cfb5750808312610aea565b60f060020a613e3d02831415610d15575080831215610aea565b60f060020a613c3d02831415610d2f575080831315610aea565b60f060020a613d3d028314156100a05750828114610aea565b6004015460009081526001840160205260409020610be8565b6003015460009081526001840160205260409020610b5a565b600282015460001415610fbd575b50505050565b90508060021415610e2657610daa8483600301600050546106eb565b6000191415610dc457610dc4848360030160005054610dfe565b8154610e269085905b60008181526001830160205260408120600381015490919081908190811415610ffb57610007565b8154610e5a9085905b60008181526001830160205260408120600481015490919081908190811415610e7f57610007565b806001191415610e5a57610e418483600401600050546106eb565b60011415610df557610df5848360040160005054610dcd565b8060001913158015610e6d575060018113155b15610d7a578154610d7a908590610f7a565b6004840180546000908152600188016020526040812060028088015490820181905592829055945014610f0f57856001016000506000856002016000505481526020019081526020016000206000509150836000016000505482600301600050541415610efa57826000016000505482600301600050819055505b835460048301541415610f0f57825460048301555b6003830154600014610f40575060038201546000908152600186016020526040902080546004850155835460028201555b82546002808601919091558454600385015583015460001415610f7157826000016000505486600001600050819055505b8354610fe69087905b6000818152600183016020526040808220600381015483528183206005908101546004830154855292842001549092610fd99291908183106110fa5750816100c3565b60029091015460009081526001840160205260409020906106e2565b6001016005820155505050565b8254610ff3908790610f7a565b505050505050565b600384018054600090815260018801602052604081206002808801549082018190559282905594501461108b5785600101600050600085600201600050548152602001908152602001600020600050915083600001600050548260030160005054141561107657826000016000505482600301600050819055505b83546004830154141561108b57825460048301555b60048301546000146110bd57506004820154600081815260018701602052604090206003850191909155835460028201555b82546002808601919091558454600485015583015460001415610f7157826000016000505486600001600050819055508354610fe6908790610f7a565b50806100c356" + }, + "0x7dd677b54fc954824a7bc49bd26cbdfa12c75adf": { + "balance": "0xd7a58f5b73b4b6c4", + "code": "0x606060405236156100985760e060020a60003504633896002781146100e15780633defb962146100ea5780633f4be8891461010c5780634136aa351461011f5780634a420138146101a057806369c1a7121461028c5780638129fc1c146102955780638da5cb5b146102a6578063ae45850b146102b8578063af3309d8146102cc578063ea8a1af0146102d5578063ead50da3146102f4575b610308671bc16d674ec8000030600160a060020a03163110156100df57600554604051600160a060020a03918216916000913091909116319082818181858883f150505050505b565b61030a60005481565b610308671bc16d674ec8000030600160a060020a031631101561040f576100df565b61031c600454600160a060020a03165b90565b61030a5b600080548190118015610199575060408051600480547f0a16697a0000000000000000000000000000000000000000000000000000000083529251600160a060020a039390931692630a16697a928083019260209291829003018187876161da5a03f1156100025750506040515160ff01431090505b905061011c565b6103085b600354600554604080517f8c0e156d0000000000000000000000000000000000000000000000000000000081527f3defb96200000000000000000000000000000000000000000000000000000000600482015260a060020a90920461ffff1643016024830152621e8480604483015251600092600160a060020a031691638c0e156d916729a2241af62c000091606481810192602092909190829003018185886185025a03f1156100025750506040515192600160a060020a0384161491506102899050576004805473ffffffffffffffffffffffffffffffffffffffff1916821790555b50565b61030a60015481565b61030860008054146103f2576100df565b61031c600554600160a060020a031681565b61031c600354600160a060020a031661011c565b61030a60025481565b610308600554600160a060020a03908116339091161461035157610002565b61033960055460a060020a900461ffff1681565b005b60408051918252519081900360200190f35b60408051600160a060020a03929092168252519081900360200190f35b6040805161ffff929092168252519081900360200190f35b6004546000600160a060020a03919091163111156103c75760408051600480547fea8a1af00000000000000000000000000000000000000000000000000000000083529251600160a060020a03939093169263ea8a1af0928083019260009291829003018183876161da5a03f115610002575050505b600554604051600160a060020a03918216916000913091909116319082818181858883f15050505050565b426000556100df6101a4565b600280546001908101909155429055565b600454600160a060020a03908116339091161461042b576100df565b610433610123565b151561043e576100df565b6103fe6101a456", + "storage": { + "0x0000000000000000000000000000000000000000000000000000000000000000": "0x0000000000000000000000000000000000000000000000000000000056be5b99", + "0x0000000000000000000000000000000000000000000000000000000000000001": "0x0000000000000000000000000000000000000000000000000000000056d0009b", + "0x0000000000000000000000000000000000000000000000000000000000000002": "0x000000000000000000000000000000000000000000000000000000000000008b", + "0x0000000000000000000000000000000000000000000000000000000000000003": "0x0000000000000000000000006c8f2a135f6ed072de4503bd7c4999a1a17f824b", + "0x0000000000000000000000000000000000000000000000000000000000000004": "0x000000000000000000000000741467b251fca923d6229c4b439078b55dca233b", + "0x0000000000000000000000000000000000000000000000000000000000000005": "0x0000000000000000000001e0d3cda913deb6f67967b99d67acdfa1712c293601" + } + }, + "0x89efe605e9ecbe22849cd85d5449cc946c26f8f3": { + "balance": "0x0", + "code": "0x650200d2f18c73506060604052361561007f5760e060020a600035046312c82bcc81146100845780635548c837146100a55780635c54305e146101015780636b103966146101555780637fcf532c14610189578063b1df3d80146101d5578063b5bc6dbb146101ee578063c6ab451414610225578063e62af6c114610293575b610007565b6102c56004356024356000620186a05a10156103855761030083835a610232565b6102d760043560243560443581600160a060020a031683600160a060020a03167f47a08955ce2b7f21ea62ff0024e1ea0ad87430953554a87e6bc65d777f18e639836040518082815260200191505060405180910390a3505050565b6102d760043560243560443560408051838152602081018390528151600160a060020a038616927f9b24879829bed3003de08d5c5d7e18dcbb8dc76faebd95cafc5d4dec8c61a3a5928290030190a2505050565b6102d76004356024356044355b600160a060020a03821660009081526020849052604090205480820110156102d957610007565b6102d7600435602435604080518281529051600160a060020a038416917fd0c5cf41ee8ebf084ad0bce53de7cbc6e4693d9b53a4019ca36a2f91cdc20b3a919081900360200190a25050565b6102c560043560243560443560006102fc848484610162565b6102c5600435602435604435600160a060020a03821660009081526020849052604081205482901061032b576103338484846102a0565b6102c56004356024356044355b60006000831180156102605750604051600160a060020a038516908290859082818181858883f19350505050155b156102fc57604051600160a060020a03851690839085906000818181858888f1935050505015156102fc57506000610300565b6102d76004356024356044355b600160a060020a03821660009081526020849052604090205481111561030757610007565b60408051918252519081900360200190f35b005b600160a060020a0382166000908152602084905260409020805482019055505050565b5060015b9392505050565b600160a060020a038216600090815260208490526040902080548290039055505050565b506000610300565b604051600160a060020a03841690600090849082818181858883f1935050505015156102fc57604051600160a060020a038416908390600081818185876185025a03f19250505015156102fc57610007565b6103008383620186a061023256" + }, + "0xb834e3edfc1a927bdcecb67a9d0eccbd752a5bb3": { + "balance": "0xffe9b09a5c474dca", + "nonce": "975", + "code": "0x" + }, + "0xd3cda913deb6f67967b99d67acdfa1712c293601": { + "balance": "0x4f5807198e238f13e", + "nonce": "283", + "code": "0x" + }, + "0xe54d323f9ef17c1f0dede47ecc86a9718fe5ea34": { + "balance": "0x0", + "code": "0x650200d2f18c7350606060405236156100ab5760e060020a600035046326a7985a81146100b057806350d4e411146100be57806354fd4d501461023d578063586a69fa1461025d5780638e46afa91461026857806396cff3df14610272578063971c803f1461029657806398c9cdf4146102a157806398e00e54146102ae578063b152f19e146102b8578063c0f68859146102c4578063e3042c0f146102cf578063ea27a88114610461575b610007565b6102845b60006104cb6102a5565b604080516020601f60843560048181013592830184900484028501840190955281845261047f948035946024803595604435956064359560a494930191819084018382808284375094965050933593505060c43591505060e435610104356101243561014435610164356101843560006101806040519081016040528060008152602001600081526020016000815260200160206040519081016040528060008152602001508152602001600081526020016000815260200160008152602001600081526020016000815260200160008152602001600081526020016000815260200150610180604051908101604052808f81526020018e81526020018d81526020018c81526020018981526020018b81526020018a81526020018881526020018781526020018681526020018581526020018481526020015090506104d48f825b600060006000600a43018460e0015110156105de577f544f4f5f534f4f4e0000000000000000000000000000000000000000000000009150610524565b604080516000808252600760208301528183015290519081900360600190f35b61049c5b6103e85b90565b6104b460ff610265565b62030d403a0260026024356004350102015b60408051918252519081900360200190f35b61049c5b600a610265565b6102845b62030d40610265565b6102846010610265565b61028443600a01610265565b6102845b6020610265565b60408051808201825261047f916004803592909160649190602490600290839083908082843780516020601f608435808c01359182018390048302840183019094528083529499983598975060a49650909450910191908190840183828082843750506040805160c0818101909252959796359660c435969095506101a49450925060e491506006908390839080828437509095505050505050604080516101808181018352600080835260208381018290528385018290528451908101855281815260608401526080830181905260a0830181905260c0830181905260e0830181905261010083018190526101208301819052610140830181905261016083018190528351918201909352808984505181526020018960015060209081015182528101899052604081018890526060018484505181526020810187905260408101869052606001846001506020908101518252018460025060400151815260200184600350606001518152602001846004506080015181526020018460055060a00151905290506104e78982610200565b6102846004356024356044356064355b3a0291909201600202010190565b60408051600160a060020a03929092168252519081900360200190f35b6040805161ffff929092168252519081900360200190f35b6040805160ff929092168252519081900360200190f35b45039050610265565b9f9e505050505050505050505050505050565b9998505050505050505050565b8461016001511015610524577f494e53554646494349454e545f46554e4453000000000000000000000000000091505b600082146106ed576040805185518482529151600160a060020a0392909216917f513485fc54ef019ef1bc1ea683ef7d5d522f2865224ae10871ff992749c0ba4f9181900360200190a27389efe605e9ecbe22849cd85d5449cc946c26f8f36312c82bcc85600001518661016001516040518360e060020a0281526004018083600160a060020a031681526020018281526020019250505060206040518083038160008760325a03f215610007575050505b505092915050565b8360c0015161ffff166105ef61029a565b61ffff1611806106115750610602610261565b61ffff168460c0015161ffff16115b1561063e577f535441434b5f434845434b5f4f55545f4f465f52414e474500000000000000009150610524565b6106466102c8565b8460a0015160ff16101561067c577f47524143455f544f4f5f53484f525400000000000000000000000000000000009150610524565b6106846102a5565b84610100015110806106a157506106996100b4565b846101000151115b156106ce577f52455155495245445f4741535f4f55545f4f465f52414e4745000000000000009150610524565b6104f48461012001518561014001518660800151876101000151610471565b83610160015184600001518560e001518660a001518760200151886040015189606001518a608001518b61010001518c60c001518d61012001518e6101400151604051611078806108fa833901808c600160a060020a031681526020018b81526020018a60ff16815260200189600160a060020a03168152602001888152602001806020018781526020018681526020018561ffff1681526020018481526020018381526020018281038252888181518152602001915080519060200190808383829060006004602084601f0104600f02600301f150905090810190601f1680156107ec5780820380516001836020036101000a031916815260200191505b509c505050505050505050505050506040518091039082f090509050737c1eb207c07e7ab13cf245585bd03d0fa478d03463bacd69588683600160a060020a031660010284600160a060020a0316630a16697a6040518160e060020a0281526004018090506020604051808303816000876161da5a03f11561000757505050604051805190602001506040518460e060020a02815260040180848152602001838152602001828152602001935050505060006040518083038160008760325a03f21561000757505060408051600160a060020a038416815290517f2b05d346f0b0b9fd470024751c52d3b5dac5c37796f077c1a66241f2eada44b792509081900360200190a18092506105d656606060405260405161107838038061107883398101604052805160805160a05160c05160e05161010051610120516101405161016051610180516101a051999a98999798969795969490940194929391929091908a84848a8a8a8a88886101008051600c8054600160a060020a031990811633179091556000805482168d1781556001868155600286815560078e90556008805461ffff19168e1790553a600655600380547c01000000000000000000000000000000000000000000000000000000008d04740100000000000000000000000000000000000000000260a060020a63ffffffff0219919096168e17169490941790935588516004805493819052956020601f9385161590910260001901909316939093048101919091047f8a35acfbc15ff81a39ae7d344fd709f28e8600b4aa8c65c6b64bfe7fe36bd19b908101939091608091909101908390106101ee57805160ff19168380011785555b5061017a9291505b8082111561021e5760008155600101610166565b5050826003600050600201600050819055505050505050505050508a600060006101000a815481600160a060020a030219169083021790555089600d6000508190555088600e60006101000a81548160ff021916908302179055505050505050505050505050610e56806102226000396000f35b8280016001018555821561015e579182015b8281111561015e578251826000505591602001919060010190610200565b509056606060405236156101a05760e060020a60003504630924120081146101c25780630a16697a146101cf5780630fd1f94e146101d8578063137c638b1461022e57806321835af61461023b57806324032866146102545780632f95b833146102d65780633017fe24146102e55780633233c686146102ef57806337f4c00e146102fa5780634500054f146103055780634e417a98146103785780634e71d92d146103e15780634f059a43146103f35780636146195414610451578063625cc4651461046157806367ce940d1461046a5780637d298ee314610477578063830953ab146104f9578063938b5f321461050457806395ee122114610516578063974654f41461052a578063a06db7dc14610535578063a9d2293d14610541578063ae45850b14610597578063b0f07e44146105a9578063c19d93fb146105cb578063c6502da81461062e578063c680362214610637578063ca94692d1461064a578063cc3471af14610673578063d379be23146106c9578063d62457f6146106e3578063ea8a1af0146106ee578063f5562753146107f3578063f6b4dfb414610854575b610868600080548190600160a060020a03908116339091161461087a57610994565b610868600b5460ff165b90565b610868600d5481565b6108686000731deeda36e15ec9e80f3d7414d67a4803ae45fc80630fd1f94e6040518160e060020a02815260040180905060206040518083038160008760325a03f2156100025750506040515191506101cc9050565b6108685b62012cc86101cc565b61086860043560008160001415610dc65750600161084f565b6108686004356024356000731deeda36e15ec9e80f3d7414d67a4803ae45fc80630bd295e6600360005085856040518460e060020a0281526004018084815260200183600160a060020a03168152602001828152602001935050505060206040518083038160008760325a03f215610002575050604051519150505b92915050565b61099860085461ffff166101cc565b61086860026101cc565b610868600a546101cc565b6108686006546101cc565b6108686000731deeda36e15ec9e80f3d7414d67a4803ae45fc8063a09431546003600050336040518360e060020a0281526004018083815260200182600160a060020a031681526020019250505060206040518083038160008760325a03f2156100025750506040515191506101cc9050565b6109af60408051602081810183526000825282516004805460026001821615610100026000190190911604601f81018490048402830184019095528482529293909291830182828015610a7d5780601f10610a5257610100808354040283529160200191610a7d565b61086860006000600180610b7b6105cf565b6108686000731deeda36e15ec9e80f3d7414d67a4803ae45fc8063f5562753436040518260e060020a0281526004018082815260200191505060206040518083038160008760325a03f2156100025750506040515191506101cc9050565b610a1d6000600480610c986105cf565b61086860025481565b6108685b620186a06101cc565b6108686004356024355b6000731deeda36e15ec9e80f3d7414d67a4803ae45fc8063a1873db6600360005085856040518460e060020a0281526004018084815260200183600160a060020a03168152602001828152602001935050505060206040518083038160008760325a03f2156100025750506040515191506102d09050565b6108686009546101cc565b610a1f600c54600160a060020a031681565b610868600b5462010000900460ff166101cc565b6108686007546101cc565b610a3c600e5460ff1681565b6108686000731deeda36e15ec9e80f3d7414d67a4803ae45fc8063a9d2293d6040518160e060020a02815260040180905060206040518083038160008760325a03f2156100025750506040515191506101cc9050565b610a1f600054600160a060020a031681565b610868600080548190600160a060020a039081163390911614610a8957610994565b6108685b6000731deeda36e15ec9e80f3d7414d67a4803ae45fc80635054d98a60036000506040518260e060020a0281526004018082815260200191505060206040518083038160008760325a03f2156100025750506040515191506101cc9050565b61086860015481565b610868600b54610100900460ff166101cc565b61086860035474010000000000000000000000000000000000000000900460e060020a026101cc565b6108686000731deeda36e15ec9e80f3d7414d67a4803ae45fc8063cc3471af6040518160e060020a02815260040180905060206040518083038160008760325a03f2156100025750506040515191506101cc9050565b610a1f600854620100009004600160a060020a03166101cc565b6108686005546101cc565b610a1d604080517fa09431540000000000000000000000000000000000000000000000000000000081526003600482015233600160a060020a031660248201529051731deeda36e15ec9e80f3d7414d67a4803ae45fc809163a0943154916044808301926020929190829003018160008760325a03f215610002575050604051511590506107f157604080517f7e9265620000000000000000000000000000000000000000000000000000000081526003600482015233600160a060020a031660248201529051731deeda36e15ec9e80f3d7414d67a4803ae45fc8091637e9265629160448083019260009291908290030181838760325a03f215610002575050505b565b6108686004356000731deeda36e15ec9e80f3d7414d67a4803ae45fc8063f5562753836040518260e060020a0281526004018082815260200191505060206040518083038160008760325a03f215610002575050604051519150505b919050565b610a1f600354600160a060020a03166101cc565b60408051918252519081900360200190f35b60045460006002600183161561010002600019019092169190910411156108a45760009150610994565b6108ac6105cf565b9050600081141580156108c0575060018114155b80156108cd575060028114155b156108db5760009150610994565b600480546000828152602060026001841615610100026000190190931692909204601f908101929092047f8a35acfbc15ff81a39ae7d344fd709f28e8600b4aa8c65c6b64bfe7fe36bd19b9081019236929083901061095d5782800160ff198235161785555b5061098d9291505b808211156109945760008155600101610949565b82800160010185558215610941579182015b8281111561094157823582600050559160200191906001019061096f565b5050600191505b5090565b6040805161ffff9092168252519081900360200190f35b60405180806020018281038252838181518152602001915080519060200190808383829060006004602084601f0104600f02600301f150905090810190601f168015610a0f5780820380516001836020036101000a031916815260200191505b509250505060405180910390f35b005b60408051600160a060020a03929092168252519081900360200190f35b6040805160ff9092168252519081900360200190f35b820191906000526020600020905b815481529060010190602001808311610a6057829003601f168201915b505050505090506101cc565b6004546000600260018316156101000260001901909216919091041115610ab35760009150610994565b610abb6105cf565b905060008114158015610acf575060018114155b8015610adc575060028114155b15610aea5760009150610994565b604080517f7c0278fc000000000000000000000000000000000000000000000000000000008152600360048201818152602483019384523660448401819052731deeda36e15ec9e80f3d7414d67a4803ae45fc8094637c0278fc946000939190606401848480828437820191505094505050505060006040518083038160008760325a03f215610002575050505090565b1415610c8557604080516001547f0fee183d0000000000000000000000000000000000000000000000000000000082526003600483015233600160a060020a0316602483015234604483015260648201529051731deeda36e15ec9e80f3d7414d67a4803ae45fc8091630fee183d916084828101926020929190829003018160008760325a03f21561000257505060405151925050811515610c8a577389efe605e9ecbe22849cd85d5449cc946c26f8f36312c82bcc33346040518360e060020a0281526004018083600160a060020a031681526020018281526020019250505060206040518083038160008760325a03f2156100025750506040515115159050610c8a57610002565b505090565b81925050610994565b505b50565b1415610c93575a9150610cab3383610481565b1515610cb75750610c95565b731deeda36e15ec9e80f3d7414d67a4803ae45fc8063da46be0a60038433610cdd61046e565b610ce5610232565b6040518660e060020a0281526004018086815260200185815260200184600160a060020a031681526020018381526020018281526020019550505050505060006040518083038160008760325a03f21561000257505050610c933360408051600080547fc17e6817000000000000000000000000000000000000000000000000000000008352600160a060020a03908116600484015230163160248301529151731deeda36e15ec9e80f3d7414d67a4803ae45fc809263c17e68179260448082019360209390928390039091019082908760325a03f2156100025750505050565b30600160a060020a031660405180807f5f5f6469672875696e7432353629000000000000000000000000000000000000815260200150600e019050604051809103902060e060020a8091040260e060020a9004600184036040518260e060020a0281526004018082815260200191505060006040518083038160008760325a03f292505050151561084f5761000256" + } + }, + "config": { + "chainId": 1, + "homesteadBlock": 1150000, + "daoForkBlock": 1920000, + "eip150Block": 2463000, + "eip155Block": 2675000, + "eip158Block": 2675000, + "byzantiumBlock": 4370000, + "constantinopleBlock": 7280000, + "petersburgBlock": 7280000, + "istanbulBlock": 9069000, + "muirGlacierBlock": 9200000, + "berlinBlock": 12244000, + "londonBlock": 12965000, + "arrowGlacierBlock": 13773000, + "grayGlacierBlock": 15050000, + "ethash": {} + } + }, + "context": { + "number": "1062503", + "difficulty": "13700504019867", + "timestamp": "1456480446", + "gasLimit": "3141592", + "miner": "0x2a65aca4d5fc5b5c859090a6c34d164135398226" + }, + "input": "0xf86b8203cf850ba43b740083200b2094741467b251fca923d6229c4b439078b55dca233b8084614619541ca078293714f69a810356f1ee29dc686ec2ca3a0e5448e1ef6322c77369ebdd26c2a01c3836fa363548959554ee5360361be9db4aea9eb7c31f61550f0e9a10138adf", + "tracerConfig": { + "diffMode": true, + "disableCode": true, + "disableStorage": true + }, + "result": { + "pre": { + "0x2a65aca4d5fc5b5c859090a6c34d164135398226": { + "balance": "0x98e1c608601c2496b2", + "nonce": 218916 + }, + "0x6c8f2a135f6ed072de4503bd7c4999a1a17f824b": { + "balance": "0x0", + "nonce": 237 + }, + "0x741467b251fca923d6229c4b439078b55dca233b": { + "balance": "0x29c613529e8218f8" + }, + "0x7dd677b54fc954824a7bc49bd26cbdfa12c75adf": { + "balance": "0xd7a58f5b73b4b6c4" + }, + "0xb834e3edfc1a927bdcecb67a9d0eccbd752a5bb3": { + "balance": "0xffe9b09a5c474dca", + "nonce": 975 + }, + "0xd3cda913deb6f67967b99d67acdfa1712c293601": { + "balance": "0x4f5807198e238f13e", + "nonce": 283 + } + }, + "post": { + "0x2a65aca4d5fc5b5c859090a6c34d164135398226": { + "balance": "0x98e2b02f14529b1eb2" + }, + "0x651913977e8140c323997fce5e03c19e0015eebf": { + "balance": "0x29a2241af62c0000" + }, + "0x6c8f2a135f6ed072de4503bd7c4999a1a17f824b": { + "nonce": 238 + }, + "0x741467b251fca923d6229c4b439078b55dca233b": { + "balance": "0x0" + }, + "0x7dd677b54fc954824a7bc49bd26cbdfa12c75adf": { + "balance": "0xd6c5f42b8502a0e3" + }, + "0xb834e3edfc1a927bdcecb67a9d0eccbd752a5bb3": { + "balance": "0x10002e64ebd492a46", + "nonce": 976 + }, + "0xd3cda913deb6f67967b99d67acdfa1712c293601": { + "balance": "0x4f5809f97e1c8bb9b" + } + } + } +} diff --git a/eth/tracers/internal/tracetest/testdata/prestate_tracer_with_diff_mode/simple_disable_code_and_storage.json b/eth/tracers/internal/tracetest/testdata/prestate_tracer_with_diff_mode/simple_disable_code_and_storage.json new file mode 100644 index 0000000000..5f939ba2df --- /dev/null +++ b/eth/tracers/internal/tracetest/testdata/prestate_tracer_with_diff_mode/simple_disable_code_and_storage.json @@ -0,0 +1,101 @@ +{ + "context": { + "difficulty": "3502894804", + "gasLimit": "4722976", + "miner": "0x1585936b53834b021f68cc13eeefdec2efc8e724", + "number": "2289806", + "timestamp": "1513601314" + }, + "genesis": { + "alloc": { + "0x0024f658a46fbb89d8ac105e98d7ac7cbbaf27c5": { + "balance": "0x0", + "code": "0x", + "nonce": "22", + "storage": {} + }, + "0x3b873a919aa0512d5a0f09e6dcceaa4a6727fafe": { + "balance": "0x4d87094125a369d9bd5", + "code": "0x606060405236156100935763ffffffff60e060020a60003504166311ee8382811461009c57806313af4035146100be5780631f5e8f4c146100ee57806324daddc5146101125780634921a91a1461013b57806363e4bff414610157578063764978f91461017f578063893d20e8146101a1578063ba40aaa1146101cd578063cebc9a82146101f4578063e177246e14610216575b61009a5b5b565b005b34156100a457fe5b6100ac61023d565b60408051918252519081900360200190f35b34156100c657fe5b6100da600160a060020a0360043516610244565b604080519115158252519081900360200190f35b34156100f657fe5b6100da610307565b604080519115158252519081900360200190f35b341561011a57fe5b6100da6004351515610318565b604080519115158252519081900360200190f35b6100da6103d6565b604080519115158252519081900360200190f35b6100da600160a060020a0360043516610420565b604080519115158252519081900360200190f35b341561018757fe5b6100ac61046c565b60408051918252519081900360200190f35b34156101a957fe5b6101b1610473565b60408051600160a060020a039092168252519081900360200190f35b34156101d557fe5b6100da600435610483565b604080519115158252519081900360200190f35b34156101fc57fe5b6100ac61050d565b60408051918252519081900360200190f35b341561021e57fe5b6100da600435610514565b604080519115158252519081900360200190f35b6003545b90565b60006000610250610473565b600160a060020a031633600160a060020a03161415156102705760006000fd5b600160a060020a03831615156102865760006000fd5b50600054600160a060020a0390811690831681146102fb57604051600160a060020a0380851691908316907ffcf23a92150d56e85e3a3d33b357493246e55783095eb6a733eb8439ffc752c890600090a360008054600160a060020a031916600160a060020a03851617905560019150610300565b600091505b5b50919050565b60005460a060020a900460ff165b90565b60006000610324610473565b600160a060020a031633600160a060020a03161415156103445760006000fd5b5060005460a060020a900460ff16801515831515146102fb576000546040805160a060020a90920460ff1615158252841515602083015280517fe6cd46a119083b86efc6884b970bfa30c1708f53ba57b86716f15b2f4551a9539281900390910190a16000805460a060020a60ff02191660a060020a8515150217905560019150610300565b600091505b5b50919050565b60006103e0610307565b801561040557506103ef610473565b600160a060020a031633600160a060020a031614155b156104105760006000fd5b610419336105a0565b90505b5b90565b600061042a610307565b801561044f5750610439610473565b600160a060020a031633600160a060020a031614155b1561045a5760006000fd5b610463826105a0565b90505b5b919050565b6001545b90565b600054600160a060020a03165b90565b6000600061048f610473565b600160a060020a031633600160a060020a03161415156104af5760006000fd5b506001548281146102fb57604080518281526020810185905281517f79a3746dde45672c9e8ab3644b8bb9c399a103da2dc94b56ba09777330a83509929181900390910190a160018381559150610300565b600091505b5b50919050565b6002545b90565b60006000610520610473565b600160a060020a031633600160a060020a03161415156105405760006000fd5b506002548281146102fb57604080518281526020810185905281517ff6991a728965fedd6e927fdf16bdad42d8995970b4b31b8a2bf88767516e2494929181900390910190a1600283905560019150610300565b600091505b5b50919050565b60006000426105ad61023d565b116102fb576105c46105bd61050d565b4201610652565b6105cc61046c565b604051909150600160a060020a038416908290600081818185876187965a03f1925050501561063d57604080518281529051600160a060020a038516917f9bca65ce52fdef8a470977b51f247a2295123a4807dfa9e502edf0d30722da3b919081900360200190a260019150610300565b6102fb42610652565b5b600091505b50919050565b60038190555b505600a165627a7a72305820f3c973c8b7ed1f62000b6701bd5b708469e19d0f1d73fde378a56c07fd0b19090029", + "nonce": "1", + "storage": { + "0x0000000000000000000000000000000000000000000000000000000000000000": "0x000000000000000000000001b436ba50d378d4bbc8660d312a13df6af6e89dfb", + "0x0000000000000000000000000000000000000000000000000000000000000001": "0x00000000000000000000000000000000000000000000000006f05b59d3b20000", + "0x0000000000000000000000000000000000000000000000000000000000000002": "0x000000000000000000000000000000000000000000000000000000000000003c", + "0x0000000000000000000000000000000000000000000000000000000000000003": "0x000000000000000000000000000000000000000000000000000000005a37b834" + } + }, + "0xb436ba50d378d4bbc8660d312a13df6af6e89dfb": { + "balance": "0x1780d77678137ac1b775", + "code": "0x", + "nonce": "29072", + "storage": {} + } + }, + "config": { + "byzantiumBlock": 1700000, + "chainId": 3, + "eip150Block": 0, + "eip155Block": 10, + "eip158Block": 10, + "ethash": {}, + "homesteadBlock": 0 + }, + "difficulty": "3509749784", + "extraData": "0x4554482e45544846414e532e4f52472d4641313738394444", + "gasLimit": "4727564", + "hash": "0x609948ac3bd3c00b7736b933248891d6c901ee28f066241bddb28f4e00a9f440", + "miner": "0xbbf5029fd710d227630c8b7d338051b8e76d50b3", + "mixHash": "0xb131e4507c93c7377de00e7c271bf409ec7492767142ff0f45c882f8068c2ada", + "nonce": "0x4eb12e19c16d43da", + "number": "2289805", + "stateRoot": "0xc7f10f352bff82fac3c2999d3085093d12652e19c7fd32591de49dc5d91b4f1f", + "timestamp": "1513601261", + "totalDifficulty": "7143276353481064" + }, + "input": "0xf88b8271908506fc23ac0083015f90943b873a919aa0512d5a0f09e6dcceaa4a6727fafe80a463e4bff40000000000000000000000000024f658a46fbb89d8ac105e98d7ac7cbbaf27c52aa0bdce0b59e8761854e857fe64015f06dd08a4fbb7624f6094893a79a72e6ad6bea01d9dde033cff7bb235a3163f348a6d7ab8d6b52bc0963a95b91612e40ca766a4", + "tracerConfig": { + "diffMode": true + }, + "result": { + "pre": { + "0x0024f658a46fbb89d8ac105e98d7ac7cbbaf27c5": { + "balance": "0x0", + "nonce": 22 + }, + "0x1585936b53834b021f68cc13eeefdec2efc8e724": { + "balance": "0x0" + }, + "0x3b873a919aa0512d5a0f09e6dcceaa4a6727fafe": { + "balance": "0x4d87094125a369d9bd5", + "nonce": 1, + "code": "0x606060405236156100935763ffffffff60e060020a60003504166311ee8382811461009c57806313af4035146100be5780631f5e8f4c146100ee57806324daddc5146101125780634921a91a1461013b57806363e4bff414610157578063764978f91461017f578063893d20e8146101a1578063ba40aaa1146101cd578063cebc9a82146101f4578063e177246e14610216575b61009a5b5b565b005b34156100a457fe5b6100ac61023d565b60408051918252519081900360200190f35b34156100c657fe5b6100da600160a060020a0360043516610244565b604080519115158252519081900360200190f35b34156100f657fe5b6100da610307565b604080519115158252519081900360200190f35b341561011a57fe5b6100da6004351515610318565b604080519115158252519081900360200190f35b6100da6103d6565b604080519115158252519081900360200190f35b6100da600160a060020a0360043516610420565b604080519115158252519081900360200190f35b341561018757fe5b6100ac61046c565b60408051918252519081900360200190f35b34156101a957fe5b6101b1610473565b60408051600160a060020a039092168252519081900360200190f35b34156101d557fe5b6100da600435610483565b604080519115158252519081900360200190f35b34156101fc57fe5b6100ac61050d565b60408051918252519081900360200190f35b341561021e57fe5b6100da600435610514565b604080519115158252519081900360200190f35b6003545b90565b60006000610250610473565b600160a060020a031633600160a060020a03161415156102705760006000fd5b600160a060020a03831615156102865760006000fd5b50600054600160a060020a0390811690831681146102fb57604051600160a060020a0380851691908316907ffcf23a92150d56e85e3a3d33b357493246e55783095eb6a733eb8439ffc752c890600090a360008054600160a060020a031916600160a060020a03851617905560019150610300565b600091505b5b50919050565b60005460a060020a900460ff165b90565b60006000610324610473565b600160a060020a031633600160a060020a03161415156103445760006000fd5b5060005460a060020a900460ff16801515831515146102fb576000546040805160a060020a90920460ff1615158252841515602083015280517fe6cd46a119083b86efc6884b970bfa30c1708f53ba57b86716f15b2f4551a9539281900390910190a16000805460a060020a60ff02191660a060020a8515150217905560019150610300565b600091505b5b50919050565b60006103e0610307565b801561040557506103ef610473565b600160a060020a031633600160a060020a031614155b156104105760006000fd5b610419336105a0565b90505b5b90565b600061042a610307565b801561044f5750610439610473565b600160a060020a031633600160a060020a031614155b1561045a5760006000fd5b610463826105a0565b90505b5b919050565b6001545b90565b600054600160a060020a03165b90565b6000600061048f610473565b600160a060020a031633600160a060020a03161415156104af5760006000fd5b506001548281146102fb57604080518281526020810185905281517f79a3746dde45672c9e8ab3644b8bb9c399a103da2dc94b56ba09777330a83509929181900390910190a160018381559150610300565b600091505b5b50919050565b6002545b90565b60006000610520610473565b600160a060020a031633600160a060020a03161415156105405760006000fd5b506002548281146102fb57604080518281526020810185905281517ff6991a728965fedd6e927fdf16bdad42d8995970b4b31b8a2bf88767516e2494929181900390910190a1600283905560019150610300565b600091505b5b50919050565b60006000426105ad61023d565b116102fb576105c46105bd61050d565b4201610652565b6105cc61046c565b604051909150600160a060020a038416908290600081818185876187965a03f1925050501561063d57604080518281529051600160a060020a038516917f9bca65ce52fdef8a470977b51f247a2295123a4807dfa9e502edf0d30722da3b919081900360200190a260019150610300565b6102fb42610652565b5b600091505b50919050565b60038190555b505600a165627a7a72305820f3c973c8b7ed1f62000b6701bd5b708469e19d0f1d73fde378a56c07fd0b19090029", + "storage": { + "0x0000000000000000000000000000000000000000000000000000000000000003": "0x000000000000000000000000000000000000000000000000000000005a37b834" + } + }, + "0xb436ba50d378d4bbc8660d312a13df6af6e89dfb": { + "balance": "0x1780d77678137ac1b775", + "nonce": 29072 + } + }, + "post": { + "0x0024f658a46fbb89d8ac105e98d7ac7cbbaf27c5": { + "balance": "0x6f05b59d3b20000" + }, + "0x1585936b53834b021f68cc13eeefdec2efc8e724": { + "balance": "0x420eed1bd6c00" + }, + "0x3b873a919aa0512d5a0f09e6dcceaa4a6727fafe": { + "balance": "0x4d869a3b70062eb9bd5", + "storage": { + "0x0000000000000000000000000000000000000000000000000000000000000003": "0x000000000000000000000000000000000000000000000000000000005a37b95e" + } + }, + "0xb436ba50d378d4bbc8660d312a13df6af6e89dfb": { + "balance": "0x1780d7725724a9044b75", + "nonce": 29073 + } + } + } +} diff --git a/eth/tracers/internal/tracetest/util.go b/eth/tracers/internal/tracetest/util.go index a74a96f8a4..abc2699498 100644 --- a/eth/tracers/internal/tracetest/util.go +++ b/eth/tracers/internal/tracetest/util.go @@ -47,6 +47,11 @@ func (c *callContext) toBlockContext(genesis *core.Genesis) vm.BlockContext { if genesis.Config.IsLondon(context.BlockNumber) { context.BaseFee = (*big.Int)(c.BaseFee) } + + if genesis.Config.TerminalTotalDifficulty != nil && genesis.Config.TerminalTotalDifficulty.Sign() == 0 { + context.Random = &genesis.Mixhash + } + if genesis.ExcessBlobGas != nil && genesis.BlobGasUsed != nil { excessBlobGas := eip4844.CalcExcessBlobGas(*genesis.ExcessBlobGas, *genesis.BlobGasUsed) context.BlobBaseFee = eip4844.CalcBlobFee(excessBlobGas) diff --git a/eth/tracers/js/goja.go b/eth/tracers/js/goja.go index b823ef740a..227ea57226 100644 --- a/eth/tracers/js/goja.go +++ b/eth/tracers/js/goja.go @@ -22,12 +22,14 @@ import ( "fmt" "math/big" "slices" + "sync" "github.com/dop251/goja" "github.com/ethereum/go-ethereum/core/tracing" "github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/eth/tracers" "github.com/ethereum/go-ethereum/eth/tracers/internal" + "github.com/ethereum/go-ethereum/params" "github.com/holiman/uint256" "github.com/ethereum/go-ethereum/common" @@ -46,10 +48,10 @@ func init() { if err != nil { panic(err) } - type ctorFn = func(*tracers.Context, json.RawMessage) (*tracers.Tracer, error) + type ctorFn = func(*tracers.Context, json.RawMessage, *params.ChainConfig) (*tracers.Tracer, error) lookup := func(code string) ctorFn { - return func(ctx *tracers.Context, cfg json.RawMessage) (*tracers.Tracer, error) { - return newJsTracer(code, ctx, cfg) + return func(ctx *tracers.Context, cfg json.RawMessage, chainConfig *params.ChainConfig) (*tracers.Tracer, error) { + return newJsTracer(code, ctx, cfg, chainConfig) } } for name, code := range assetTracers { @@ -58,9 +60,17 @@ func init() { tracers.DefaultDirectory.RegisterJSEval(newJsTracer) } -// bigIntProgram is compiled once and the exported function mostly invoked to convert -// hex strings into big ints. -var bigIntProgram = goja.MustCompile("bigInt", bigIntegerJS, false) +var compiledBigInt *goja.Program +var compileOnce sync.Once + +// getBigIntProgram compiles the bigint library, if needed, and returns the compiled +// goja program. +func getBigIntProgram() *goja.Program { + compileOnce.Do(func() { + compiledBigInt = goja.MustCompile("bigInt", bigIntegerJS, false) + }) + return compiledBigInt +} type toBigFn = func(vm *goja.Runtime, val string) (goja.Value, error) type toBufFn = func(vm *goja.Runtime, val []byte) (goja.Value, error) @@ -102,6 +112,7 @@ func fromBuf(vm *goja.Runtime, bufType goja.Value, buf goja.Value, allowString b type jsTracer struct { vm *goja.Runtime env *tracing.VMContext + chainConfig *params.ChainConfig toBig toBigFn // Converts a hex string into a JS bigint toBuf toBufFn // Converts a []byte into a JS buffer fromBuf fromBufFn // Converts an array, hex string or Uint8Array to a []byte @@ -138,13 +149,14 @@ type jsTracer struct { // The methods `result` and `fault` are required to be present. // The methods `step`, `enter`, and `exit` are optional, but note that // `enter` and `exit` always go together. -func newJsTracer(code string, ctx *tracers.Context, cfg json.RawMessage) (*tracers.Tracer, error) { +func newJsTracer(code string, ctx *tracers.Context, cfg json.RawMessage, chainConfig *params.ChainConfig) (*tracers.Tracer, error) { vm := goja.New() // By default field names are exported to JS as is, i.e. capitalized. vm.SetFieldNameMapper(goja.UncapFieldNameMapper()) t := &jsTracer{ - vm: vm, - ctx: make(map[string]goja.Value), + vm: vm, + ctx: make(map[string]goja.Value), + chainConfig: chainConfig, } t.setTypeConverters() @@ -244,11 +256,11 @@ func (t *jsTracer) OnTxStart(env *tracing.VMContext, tx *types.Transaction, from db := &dbObj{db: env.StateDB, vm: t.vm, toBig: t.toBig, toBuf: t.toBuf, fromBuf: t.fromBuf} t.dbValue = db.setupObject() // Update list of precompiles based on current block - rules := env.ChainConfig.Rules(env.BlockNumber, env.Random != nil, env.Time) + rules := t.chainConfig.Rules(env.BlockNumber, env.Random != nil, env.Time) 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 @@ -564,7 +576,7 @@ func (t *jsTracer) setBuiltinFunctions() { func (t *jsTracer) setTypeConverters() error { // Inject bigint logic. // TODO: To be replaced after goja adds support for native JS bigint. - toBigCode, err := t.vm.RunProgram(bigIntProgram) + toBigCode, err := t.vm.RunProgram(getBigIntProgram()) if err != nil { return err } diff --git a/eth/tracers/js/tracer_test.go b/eth/tracers/js/tracer_test.go index 7122b3c90e..faad1e7194 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) @@ -90,11 +91,12 @@ func runTrace(tracer *tracers.Tracer, vmctx *vmContext, chaincfg *params.ChainCo func TestTracer(t *testing.T) { execTracer := func(code string, contract []byte) ([]byte, string) { t.Helper() - tracer, err := newJsTracer(code, nil, nil) + chainConfig := params.TestChainConfig + tracer, err := newJsTracer(code, nil, nil, chainConfig) if err != nil { t.Fatal(err) } - ret, err := runTrace(tracer, testCtx(), params.TestChainConfig, contract) + ret, err := runTrace(tracer, testCtx(), chainConfig, contract) if err != nil { return nil, err.Error() // Stringify to allow comparison without nil checks } @@ -167,7 +169,8 @@ func TestTracer(t *testing.T) { func TestHalt(t *testing.T) { timeout := errors.New("stahp") - tracer, err := newJsTracer("{step: function() { while(1); }, result: function() { return null; }, fault: function(){}}", nil, nil) + chainConfig := params.TestChainConfig + tracer, err := newJsTracer("{step: function() { while(1); }, result: function() { return null; }, fault: function(){}}", nil, nil, chainConfig) if err != nil { t.Fatal(err) } @@ -175,21 +178,23 @@ func TestHalt(t *testing.T) { time.Sleep(1 * time.Second) tracer.Stop(timeout) }() - if _, err = runTrace(tracer, testCtx(), params.TestChainConfig, nil); !strings.Contains(err.Error(), "stahp") { + if _, err = runTrace(tracer, testCtx(), chainConfig, nil); !strings.Contains(err.Error(), "stahp") { t.Errorf("Expected timeout error, got %v", err) } } func TestHaltBetweenSteps(t *testing.T) { - tracer, err := newJsTracer("{step: function() {}, fault: function() {}, result: function() { return null; }}", nil, nil) + chainConfig := params.TestChainConfig + tracer, err := newJsTracer("{step: function() {}, fault: function() {}, result: function() { return null; }}", nil, nil, chainConfig) if err != nil { t.Fatal(err) } 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{}, params.TestChainConfig, 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") @@ -206,12 +211,14 @@ func TestHaltBetweenSteps(t *testing.T) { func TestNoStepExec(t *testing.T) { execTracer := func(code string) []byte { t.Helper() - tracer, err := newJsTracer(code, nil, nil) + chainConfig := params.TestChainConfig + tracer, err := newJsTracer(code, nil, nil, chainConfig) if err != nil { t.Fatal(err) } - env := vm.NewEVM(vm.BlockContext{BlockNumber: big.NewInt(1)}, vm.TxContext{GasPrice: big.NewInt(100)}, &dummyStatedb{}, params.TestChainConfig, 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() @@ -241,7 +248,7 @@ func TestIsPrecompile(t *testing.T) { chaincfg.IstanbulBlock = big.NewInt(200) chaincfg.BerlinBlock = big.NewInt(300) txCtx := vm.TxContext{GasPrice: big.NewInt(100000)} - tracer, err := newJsTracer("{addr: toAddress('0000000000000000000000000000000000000009'), res: null, step: function() { this.res = isPrecompiled(this.addr); }, fault: function() {}, result: function() { return this.res; }}", nil, nil) + tracer, err := newJsTracer("{addr: toAddress('0000000000000000000000000000000000000009'), res: null, step: function() { this.res = isPrecompiled(this.addr); }, fault: function() {}, result: function() { return this.res; }}", nil, nil, chaincfg) if err != nil { t.Fatal(err) } @@ -255,7 +262,7 @@ func TestIsPrecompile(t *testing.T) { t.Errorf("tracer should not consider blake2f as precompile in byzantium") } - tracer, _ = newJsTracer("{addr: toAddress('0000000000000000000000000000000000000009'), res: null, step: function() { this.res = isPrecompiled(this.addr); }, fault: function() {}, result: function() { return this.res; }}", nil, nil) + tracer, _ = newJsTracer("{addr: toAddress('0000000000000000000000000000000000000009'), res: null, step: function() { this.res = isPrecompiled(this.addr); }, fault: function() {}, result: function() { return this.res; }}", nil, nil, chaincfg) blockCtx = vm.BlockContext{BlockNumber: big.NewInt(250)} res, err = runTrace(tracer, &vmContext{blockCtx, txCtx}, chaincfg, nil) if err != nil { @@ -267,15 +274,16 @@ func TestIsPrecompile(t *testing.T) { } func TestEnterExit(t *testing.T) { + chainConfig := params.TestChainConfig // test that either both or none of enter() and exit() are defined - if _, err := newJsTracer("{step: function() {}, fault: function() {}, result: function() { return null; }, enter: function() {}}", new(tracers.Context), nil); err == nil { + if _, err := newJsTracer("{step: function() {}, fault: function() {}, result: function() { return null; }, enter: function() {}}", new(tracers.Context), nil, chainConfig); err == nil { t.Fatal("tracer creation should've failed without exit() definition") } - if _, err := newJsTracer("{step: function() {}, fault: function() {}, result: function() { return null; }, enter: function() {}, exit: function() {}}", new(tracers.Context), nil); err != nil { + if _, err := newJsTracer("{step: function() {}, fault: function() {}, result: function() { return null; }, enter: function() {}, exit: function() {}}", new(tracers.Context), nil, chainConfig); err != nil { t.Fatal(err) } // test that the enter and exit method are correctly invoked and the values passed - tracer, err := newJsTracer("{enters: 0, exits: 0, enterGas: 0, gasUsed: 0, step: function() {}, fault: function() {}, result: function() { return {enters: this.enters, exits: this.exits, enterGas: this.enterGas, gasUsed: this.gasUsed} }, enter: function(frame) { this.enters++; this.enterGas = frame.getGas(); }, exit: function(res) { this.exits++; this.gasUsed = res.getGasUsed(); }}", new(tracers.Context), nil) + tracer, err := newJsTracer("{enters: 0, exits: 0, enterGas: 0, gasUsed: 0, step: function() {}, fault: function() {}, result: function() { return {enters: this.enters, exits: this.exits, enterGas: this.enterGas, gasUsed: this.gasUsed} }, enter: function(frame) { this.enters++; this.enterGas = frame.getGas(); }, exit: function(res) { this.exits++; this.gasUsed = res.getGasUsed(); }}", new(tracers.Context), nil, chainConfig) if err != nil { t.Fatal(err) } @@ -297,7 +305,8 @@ func TestEnterExit(t *testing.T) { func TestSetup(t *testing.T) { // Test empty config - _, err := newJsTracer(`{setup: function(cfg) { if (cfg !== "{}") { throw("invalid empty config") } }, fault: function() {}, result: function() {}}`, new(tracers.Context), nil) + chainConfig := params.TestChainConfig + _, err := newJsTracer(`{setup: function(cfg) { if (cfg !== "{}") { throw("invalid empty config") } }, fault: function() {}, result: function() {}}`, new(tracers.Context), nil, chainConfig) if err != nil { t.Error(err) } @@ -307,12 +316,12 @@ func TestSetup(t *testing.T) { t.Fatal(err) } // Test no setup func - _, err = newJsTracer(`{fault: function() {}, result: function() {}}`, new(tracers.Context), cfg) + _, err = newJsTracer(`{fault: function() {}, result: function() {}}`, new(tracers.Context), cfg, chainConfig) if err != nil { t.Fatal(err) } // Test config value - tracer, err := newJsTracer("{config: null, setup: function(cfg) { this.config = JSON.parse(cfg) }, step: function() {}, fault: function() {}, result: function() { return this.config.foo }}", new(tracers.Context), cfg) + tracer, err := newJsTracer("{config: null, setup: function(cfg) { this.config = JSON.parse(cfg) }, step: function() {}, fault: function() {}, result: function() { return this.config.foo }}", new(tracers.Context), cfg, chainConfig) if err != nil { t.Fatal(err) } diff --git a/eth/tracers/live.go b/eth/tracers/live.go index ffb2303af4..8b222d2e6c 100644 --- a/eth/tracers/live.go +++ b/eth/tracers/live.go @@ -1,3 +1,19 @@ +// 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 tracers import ( @@ -24,6 +40,9 @@ func (d *liveDirectory) Register(name string, f ctorFunc) { // New instantiates a tracer by name. func (d *liveDirectory) New(name string, config json.RawMessage) (*tracing.Hooks, error) { + if len(config) == 0 { + config = json.RawMessage("{}") + } if f, ok := d.elems[name]; ok { return f(config) } diff --git a/eth/tracers/live/noop.go b/eth/tracers/live/noop.go index 7433c28840..46c5700d25 100644 --- a/eth/tracers/live/noop.go +++ b/eth/tracers/live/noop.go @@ -1,3 +1,19 @@ +// 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 live import ( diff --git a/eth/tracers/live/supply.go b/eth/tracers/live/supply.go index 96f7059454..fa4e5b1904 100644 --- a/eth/tracers/live/supply.go +++ b/eth/tracers/live/supply.go @@ -1,3 +1,19 @@ +// 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 live import ( @@ -19,7 +35,7 @@ import ( ) func init() { - tracers.LiveDirectory.Register("supply", newSupply) + tracers.LiveDirectory.Register("supply", newSupplyTracer) } type supplyInfoIssuance struct { @@ -63,7 +79,7 @@ type supplyTxCallstack struct { burn *big.Int } -type supply struct { +type supplyTracer struct { delta supplyInfo txCallstack []supplyTxCallstack // Callstack for current transaction logger *lumberjack.Logger @@ -74,12 +90,10 @@ type supplyTracerConfig struct { MaxSize int `json:"maxSize"` // MaxSize is the maximum size in megabytes of the tracer log file before it gets rotated. It defaults to 100 megabytes. } -func newSupply(cfg json.RawMessage) (*tracing.Hooks, error) { +func newSupplyTracer(cfg json.RawMessage) (*tracing.Hooks, error) { var config supplyTracerConfig - if cfg != nil { - if err := json.Unmarshal(cfg, &config); err != nil { - return nil, fmt.Errorf("failed to parse config: %v", err) - } + if err := json.Unmarshal(cfg, &config); err != nil { + return nil, fmt.Errorf("failed to parse config: %v", err) } if config.Path == "" { return nil, errors.New("supply tracer output path is required") @@ -93,19 +107,19 @@ func newSupply(cfg json.RawMessage) (*tracing.Hooks, error) { logger.MaxSize = config.MaxSize } - t := &supply{ + t := &supplyTracer{ delta: newSupplyInfo(), logger: logger, } return &tracing.Hooks{ - OnBlockStart: t.OnBlockStart, - OnBlockEnd: t.OnBlockEnd, - OnGenesisBlock: t.OnGenesisBlock, - OnTxStart: t.OnTxStart, - OnBalanceChange: t.OnBalanceChange, - OnEnter: t.OnEnter, - OnExit: t.OnExit, - OnClose: t.OnClose, + OnBlockStart: t.onBlockStart, + OnBlockEnd: t.onBlockEnd, + OnGenesisBlock: t.onGenesisBlock, + OnTxStart: t.onTxStart, + OnBalanceChange: t.onBalanceChange, + OnEnter: t.onEnter, + OnExit: t.onExit, + OnClose: t.onClose, }, nil } @@ -128,11 +142,11 @@ func newSupplyInfo() supplyInfo { } } -func (s *supply) resetDelta() { +func (s *supplyTracer) resetDelta() { s.delta = newSupplyInfo() } -func (s *supply) OnBlockStart(ev tracing.BlockEvent) { +func (s *supplyTracer) onBlockStart(ev tracing.BlockEvent) { s.resetDelta() s.delta.Number = ev.Block.NumberU64() @@ -155,11 +169,11 @@ func (s *supply) OnBlockStart(ev tracing.BlockEvent) { } } -func (s *supply) OnBlockEnd(err error) { +func (s *supplyTracer) onBlockEnd(err error) { s.write(s.delta) } -func (s *supply) OnGenesisBlock(b *types.Block, alloc types.GenesisAlloc) { +func (s *supplyTracer) onGenesisBlock(b *types.Block, alloc types.GenesisAlloc) { s.resetDelta() s.delta.Number = b.NumberU64() @@ -174,7 +188,7 @@ func (s *supply) OnGenesisBlock(b *types.Block, alloc types.GenesisAlloc) { s.write(s.delta) } -func (s *supply) OnBalanceChange(a common.Address, prevBalance, newBalance *big.Int, reason tracing.BalanceChangeReason) { +func (s *supplyTracer) onBalanceChange(a common.Address, prevBalance, newBalance *big.Int, reason tracing.BalanceChangeReason) { diff := new(big.Int).Sub(newBalance, prevBalance) // NOTE: don't handle "BalanceIncreaseGenesisBalance" because it is handled in OnGenesisBlock @@ -193,12 +207,12 @@ func (s *supply) OnBalanceChange(a common.Address, prevBalance, newBalance *big. } } -func (s *supply) OnTxStart(vm *tracing.VMContext, tx *types.Transaction, from common.Address) { +func (s *supplyTracer) onTxStart(vm *tracing.VMContext, tx *types.Transaction, from common.Address) { s.txCallstack = make([]supplyTxCallstack, 0, 1) } // internalTxsHandler handles internal transactions burned amount -func (s *supply) internalTxsHandler(call *supplyTxCallstack) { +func (s *supplyTracer) internalTxsHandler(call *supplyTxCallstack) { // Handle Burned amount if call.burn != nil { s.delta.Burn.Misc.Add(s.delta.Burn.Misc, call.burn) @@ -211,7 +225,7 @@ func (s *supply) internalTxsHandler(call *supplyTxCallstack) { } } -func (s *supply) OnEnter(depth int, typ byte, from common.Address, to common.Address, input []byte, gas uint64, value *big.Int) { +func (s *supplyTracer) onEnter(depth int, typ byte, from common.Address, to common.Address, input []byte, gas uint64, value *big.Int) { call := supplyTxCallstack{ calls: make([]supplyTxCallstack, 0), } @@ -226,7 +240,7 @@ func (s *supply) OnEnter(depth int, typ byte, from common.Address, to common.Add s.txCallstack = append(s.txCallstack, call) } -func (s *supply) OnExit(depth int, output []byte, gasUsed uint64, err error, reverted bool) { +func (s *supplyTracer) onExit(depth int, output []byte, gasUsed uint64, err error, reverted bool) { if depth == 0 { // No need to handle Burned amount if transaction is reverted if !reverted { @@ -252,13 +266,13 @@ func (s *supply) OnExit(depth int, output []byte, gasUsed uint64, err error, rev s.txCallstack[size-1].calls = append(s.txCallstack[size-1].calls, call) } -func (s *supply) OnClose() { +func (s *supplyTracer) onClose() { if err := s.logger.Close(); err != nil { log.Warn("failed to close supply tracer log file", "error", err) } } -func (s *supply) write(data any) { +func (s *supplyTracer) write(data any) { supply, ok := data.(supplyInfo) if !ok { log.Warn("failed to cast supply tracer data on write to log file") diff --git a/eth/tracers/logger/logger.go b/eth/tracers/logger/logger.go index f918ce154b..dc9e6e62b7 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 @@ -141,44 +247,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) { @@ -194,7 +292,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 ( @@ -202,17 +300,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. @@ -222,12 +322,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) { @@ -246,7 +347,7 @@ func (l *StructLogger) GetResult() (json.RawMessage, error) { Gas: l.usedGas, Failed: failed, ReturnValue: returnVal, - StructLogs: formatLogs(l.StructLogs()), + StructLogs: l.logs, }) } @@ -273,9 +374,6 @@ func (l *StructLogger) OnTxEnd(receipt *types.Receipt, err error) { } } -// 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 } @@ -283,49 +381,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) } } @@ -363,26 +422,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) } } @@ -390,7 +458,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 @@ -401,7 +470,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) @@ -416,65 +484,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 137608f884..e7799dde35 100644 --- a/eth/tracers/logger/logger_test.go +++ b/eth/tracers/logger/logger_test.go @@ -49,20 +49,22 @@ type dummyStatedb struct { state.StateDB } -func (*dummyStatedb) GetRefund() uint64 { return 1337 } -func (*dummyStatedb) GetState(_ common.Address, _ common.Hash) common.Hash { return common.Hash{} } -func (*dummyStatedb) SetState(_ common.Address, _ common.Hash, _ common.Hash) {} +func (*dummyStatedb) GetRefund() uint64 { return 1337 } +func (*dummyStatedb) GetState(_ common.Address, _ common.Hash) common.Hash { return common.Hash{} } +func (*dummyStatedb) SetState(_ common.Address, _ common.Hash, _ common.Hash) common.Hash { + return common.Hash{} +} 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/native/4byte.go b/eth/tracers/native/4byte.go index 6cb0e433d2..cec45a1e7a 100644 --- a/eth/tracers/native/4byte.go +++ b/eth/tracers/native/4byte.go @@ -27,6 +27,7 @@ import ( "github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/core/vm" "github.com/ethereum/go-ethereum/eth/tracers" + "github.com/ethereum/go-ethereum/params" ) func init() { @@ -48,17 +49,19 @@ func init() { // 0xc281d19e-0: 1 // } type fourByteTracer struct { - ids map[string]int // ids aggregates the 4byte ids found - interrupt atomic.Bool // Atomic flag to signal execution interruption - reason error // Textual reason for the interruption + ids map[string]int // ids aggregates the 4byte ids found + interrupt atomic.Bool // Atomic flag to signal execution interruption + reason error // Textual reason for the interruption + chainConfig *params.ChainConfig activePrecompiles []common.Address // Updated on tx start based on given rules } // newFourByteTracer returns a native go tracer which collects // 4 byte-identifiers of a tx, and implements vm.EVMLogger. -func newFourByteTracer(ctx *tracers.Context, _ json.RawMessage) (*tracers.Tracer, error) { +func newFourByteTracer(ctx *tracers.Context, cfg json.RawMessage, chainConfig *params.ChainConfig) (*tracers.Tracer, error) { t := &fourByteTracer{ - ids: make(map[string]int), + ids: make(map[string]int), + chainConfig: chainConfig, } return &tracers.Tracer{ Hooks: &tracing.Hooks{ @@ -88,7 +91,7 @@ func (t *fourByteTracer) store(id []byte, size int) { func (t *fourByteTracer) OnTxStart(env *tracing.VMContext, tx *types.Transaction, from common.Address) { // Update list of precompiles based on current block - rules := env.ChainConfig.Rules(env.BlockNumber, env.Random != nil, env.Time) + rules := t.chainConfig.Rules(env.BlockNumber, env.Random != nil, env.Time) t.activePrecompiles = vm.ActivePrecompiles(rules) } diff --git a/eth/tracers/native/call.go b/eth/tracers/native/call.go index 1b94dd7b67..c2247d1ce4 100644 --- a/eth/tracers/native/call.go +++ b/eth/tracers/native/call.go @@ -29,6 +29,7 @@ import ( "github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/core/vm" "github.com/ethereum/go-ethereum/eth/tracers" + "github.com/ethereum/go-ethereum/params" ) //go:generate go run github.com/fjl/gencodec -type callFrame -field-override callFrameMarshaling -out gen_callframe_json.go @@ -125,7 +126,7 @@ type callTracerConfig struct { // newCallTracer returns a native go tracer which tracks // call frames of a tx, and implements vm.EVMLogger. -func newCallTracer(ctx *tracers.Context, cfg json.RawMessage) (*tracers.Tracer, error) { +func newCallTracer(ctx *tracers.Context, cfg json.RawMessage, chainConfig *params.ChainConfig) (*tracers.Tracer, error) { t, err := newCallTracerObject(ctx, cfg) if err != nil { return nil, err @@ -145,10 +146,8 @@ func newCallTracer(ctx *tracers.Context, cfg json.RawMessage) (*tracers.Tracer, func newCallTracerObject(ctx *tracers.Context, cfg json.RawMessage) (*callTracer, error) { var config callTracerConfig - if cfg != nil { - if err := json.Unmarshal(cfg, &config); err != nil { - return nil, err - } + if err := json.Unmarshal(cfg, &config); err != nil { + return nil, err } // First callframe contains tx context info // and is populated on start and end. diff --git a/eth/tracers/native/call_flat.go b/eth/tracers/native/call_flat.go index a47b79f8df..e56d011139 100644 --- a/eth/tracers/native/call_flat.go +++ b/eth/tracers/native/call_flat.go @@ -31,6 +31,7 @@ import ( "github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/core/vm" "github.com/ethereum/go-ethereum/eth/tracers" + "github.com/ethereum/go-ethereum/params" ) //go:generate go run github.com/fjl/gencodec -type flatCallAction -field-override flatCallActionMarshaling -out gen_flatcallaction_json.go @@ -54,6 +55,7 @@ var parityErrorMapping = map[string]string{ } var parityErrorMappingStartingWith = map[string]string{ + "out of gas:": "Out of gas", // convert OOG wrapped errors, eg `out of gas: not enough gas for reentrancy sentry` "invalid opcode:": "Bad instruction", "stack underflow": "Stack underflow", } @@ -114,6 +116,7 @@ type flatCallResultMarshaling struct { type flatCallTracer struct { tracer *callTracer config flatCallTracerConfig + chainConfig *params.ChainConfig ctx *tracers.Context // Holds tracer context data interrupt atomic.Bool // Atomic flag to signal execution interruption activePrecompiles []common.Address // Updated on tx start based on given rules @@ -125,22 +128,20 @@ type flatCallTracerConfig struct { } // newFlatCallTracer returns a new flatCallTracer. -func newFlatCallTracer(ctx *tracers.Context, cfg json.RawMessage) (*tracers.Tracer, error) { +func newFlatCallTracer(ctx *tracers.Context, cfg json.RawMessage, chainConfig *params.ChainConfig) (*tracers.Tracer, error) { var config flatCallTracerConfig - if cfg != nil { - if err := json.Unmarshal(cfg, &config); err != nil { - return nil, err - } + if err := json.Unmarshal(cfg, &config); err != nil { + return nil, err } // Create inner call tracer with default configuration, don't forward // the OnlyTopCall or WithLog to inner for now - t, err := newCallTracerObject(ctx, nil) + t, err := newCallTracerObject(ctx, json.RawMessage("{}")) if err != nil { return nil, err } - ft := &flatCallTracer{tracer: t, ctx: ctx, config: config} + ft := &flatCallTracer{tracer: t, ctx: ctx, config: config, chainConfig: chainConfig} return &tracers.Tracer{ Hooks: &tracing.Hooks{ OnTxStart: ft.OnTxStart, @@ -206,7 +207,7 @@ func (t *flatCallTracer) OnTxStart(env *tracing.VMContext, tx *types.Transaction } t.tracer.OnTxStart(env, tx, from) // Update list of precompiles based on current block - rules := env.ChainConfig.Rules(env.BlockNumber, env.Random != nil, env.Time) + rules := t.chainConfig.Rules(env.BlockNumber, env.Random != nil, env.Time) t.activePrecompiles = vm.ActivePrecompiles(rules) } @@ -296,10 +297,11 @@ func newFlatCreate(input *callFrame) *flatCallFrame { return &flatCallFrame{ Type: strings.ToLower(vm.CREATE.String()), Action: flatCallAction{ - From: &input.From, - Gas: &input.Gas, - Value: input.Value, - Init: &actionInit, + CreationMethod: strings.ToLower(input.Type.String()), + From: &input.From, + Gas: &input.Gas, + Value: input.Value, + Init: &actionInit, }, Result: &flatCallResult{ GasUsed: &input.GasUsed, @@ -370,6 +372,7 @@ func convertErrorToParity(call *flatCallFrame) { for gethError, parityError := range parityErrorMappingStartingWith { if strings.HasPrefix(call.Error, gethError) { call.Error = parityError + break } } } diff --git a/eth/tracers/native/call_flat_test.go b/eth/tracers/native/call_flat_test.go index d5481b868b..a81af6d6bc 100644 --- a/eth/tracers/native/call_flat_test.go +++ b/eth/tracers/native/call_flat_test.go @@ -31,7 +31,7 @@ import ( ) func TestCallFlatStop(t *testing.T) { - tracer, err := tracers.DefaultDirectory.New("flatCallTracer", &tracers.Context{}, nil) + tracer, err := tracers.DefaultDirectory.New("flatCallTracer", &tracers.Context{}, nil, params.MainnetChainConfig) require.NoError(t, err) // this error should be returned by GetResult @@ -47,9 +47,7 @@ func TestCallFlatStop(t *testing.T) { Data: nil, }) - tracer.OnTxStart(&tracing.VMContext{ - ChainConfig: params.MainnetChainConfig, - }, tx, common.Address{}) + tracer.OnTxStart(&tracing.VMContext{}, tx, common.Address{}) tracer.OnEnter(0, byte(vm.CALL), common.Address{}, common.Address{}, nil, 0, big.NewInt(0)) diff --git a/eth/tracers/native/mux.go b/eth/tracers/native/mux.go index c3b1d9f8ca..77ab254568 100644 --- a/eth/tracers/native/mux.go +++ b/eth/tracers/native/mux.go @@ -24,6 +24,7 @@ import ( "github.com/ethereum/go-ethereum/core/tracing" "github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/eth/tracers" + "github.com/ethereum/go-ethereum/params" ) func init() { @@ -38,17 +39,15 @@ type muxTracer struct { } // newMuxTracer returns a new mux tracer. -func newMuxTracer(ctx *tracers.Context, cfg json.RawMessage) (*tracers.Tracer, error) { +func newMuxTracer(ctx *tracers.Context, cfg json.RawMessage, chainConfig *params.ChainConfig) (*tracers.Tracer, error) { var config map[string]json.RawMessage - if cfg != nil { - if err := json.Unmarshal(cfg, &config); err != nil { - return nil, err - } + if err := json.Unmarshal(cfg, &config); err != nil { + return nil, err } objects := make([]*tracers.Tracer, 0, len(config)) names := make([]string, 0, len(config)) for k, v := range config { - t, err := tracers.DefaultDirectory.New(k, ctx, v) + t, err := tracers.DefaultDirectory.New(k, ctx, v, chainConfig) if err != nil { return nil, err } diff --git a/eth/tracers/native/noop.go b/eth/tracers/native/noop.go index f147134610..ac174cc25e 100644 --- a/eth/tracers/native/noop.go +++ b/eth/tracers/native/noop.go @@ -24,6 +24,7 @@ import ( "github.com/ethereum/go-ethereum/core/tracing" "github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/eth/tracers" + "github.com/ethereum/go-ethereum/params" ) func init() { @@ -35,7 +36,7 @@ func init() { type noopTracer struct{} // newNoopTracer returns a new noop tracer. -func newNoopTracer(ctx *tracers.Context, _ json.RawMessage) (*tracers.Tracer, error) { +func newNoopTracer(ctx *tracers.Context, cfg json.RawMessage, chainConfig *params.ChainConfig) (*tracers.Tracer, error) { t := &noopTracer{} return &tracers.Tracer{ Hooks: &tracing.Hooks{ diff --git a/eth/tracers/native/prestate.go b/eth/tracers/native/prestate.go index b353c06960..e04b77f61f 100644 --- a/eth/tracers/native/prestate.go +++ b/eth/tracers/native/prestate.go @@ -31,6 +31,7 @@ import ( "github.com/ethereum/go-ethereum/eth/tracers" "github.com/ethereum/go-ethereum/eth/tracers/internal" "github.com/ethereum/go-ethereum/log" + "github.com/ethereum/go-ethereum/params" ) //go:generate go run github.com/fjl/gencodec -type account -field-override accountMarshaling -out gen_account_json.go @@ -71,15 +72,15 @@ type prestateTracer struct { } type prestateTracerConfig struct { - DiffMode bool `json:"diffMode"` // If true, this tracer will return state modifications + DiffMode bool `json:"diffMode"` // If true, this tracer will return state modifications + DisableCode bool `json:"disableCode"` // If true, this tracer will not return the contract code + DisableStorage bool `json:"disableStorage"` // If true, this tracer will not return the contract storage } -func newPrestateTracer(ctx *tracers.Context, cfg json.RawMessage) (*tracers.Tracer, error) { +func newPrestateTracer(ctx *tracers.Context, cfg json.RawMessage, chainConfig *params.ChainConfig) (*tracers.Tracer, error) { var config prestateTracerConfig - if cfg != nil { - if err := json.Unmarshal(cfg, &config); err != nil { - return nil, err - } + if err := json.Unmarshal(cfg, &config); err != nil { + return nil, err } t := &prestateTracer{ pre: stateMap{}, @@ -158,6 +159,15 @@ func (t *prestateTracer) OnTxStart(env *tracing.VMContext, tx *types.Transaction t.lookupAccount(from) t.lookupAccount(t.to) t.lookupAccount(env.Coinbase) + + // Add accounts with authorizations to the prestate before they get applied. + for _, auth := range tx.SetCodeAuthorizations() { + addr, err := auth.Authority() + if err != nil { + continue + } + t.lookupAccount(addr) + } } func (t *prestateTracer) OnTxEnd(receipt *types.Receipt, err error) { @@ -211,7 +221,6 @@ func (t *prestateTracer) processDiffState() { postAccount := &account{Storage: make(map[common.Hash]common.Hash)} newBalance := t.env.StateDB.GetBalance(addr).ToBig() newNonce := t.env.StateDB.GetNonce(addr) - newCode := t.env.StateDB.GetCode(addr) if newBalance.Cmp(t.pre[addr].Balance) != 0 { modified = true @@ -221,25 +230,30 @@ func (t *prestateTracer) processDiffState() { modified = true postAccount.Nonce = newNonce } - if !bytes.Equal(newCode, t.pre[addr].Code) { - modified = true - postAccount.Code = newCode + if !t.config.DisableCode { + newCode := t.env.StateDB.GetCode(addr) + if !bytes.Equal(newCode, t.pre[addr].Code) { + modified = true + postAccount.Code = newCode + } } - for key, val := range state.Storage { - // don't include the empty slot - if val == (common.Hash{}) { - delete(t.pre[addr].Storage, key) - } + if !t.config.DisableStorage { + for key, val := range state.Storage { + // don't include the empty slot + if val == (common.Hash{}) { + delete(t.pre[addr].Storage, key) + } - newVal := t.env.StateDB.GetState(addr, key) - if val == newVal { - // Omit unchanged slots - delete(t.pre[addr].Storage, key) - } else { - modified = true - if newVal != (common.Hash{}) { - postAccount.Storage[key] = newVal + newVal := t.env.StateDB.GetState(addr, key) + if val == newVal { + // Omit unchanged slots + delete(t.pre[addr].Storage, key) + } else { + modified = true + if newVal != (common.Hash{}) { + postAccount.Storage[key] = newVal + } } } } @@ -264,11 +278,17 @@ func (t *prestateTracer) lookupAccount(addr common.Address) { Balance: t.env.StateDB.GetBalance(addr).ToBig(), Nonce: t.env.StateDB.GetNonce(addr), Code: t.env.StateDB.GetCode(addr), - Storage: make(map[common.Hash]common.Hash), } if !acc.exists() { acc.empty = true } + // The code must be fetched first for the emptiness check. + if t.config.DisableCode { + acc.Code = nil + } + if !t.config.DisableStorage { + acc.Storage = make(map[common.Hash]common.Hash) + } t.pre[addr] = acc } @@ -276,6 +296,9 @@ func (t *prestateTracer) lookupAccount(addr common.Address) { // it to the prestate of the given contract. It assumes `lookupAccount` // has been performed on the contract before. func (t *prestateTracer) lookupStorage(addr common.Address, key common.Hash) { + if t.config.DisableStorage { + return + } if _, ok := t.pre[addr].Storage[key]; ok { return } diff --git a/eth/tracers/tracers_test.go b/eth/tracers/tracers_test.go index 3cce7bffa1..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,16 +88,15 @@ func BenchmarkTransactionTrace(b *testing.B) { b.ReportAllocs() for i := 0; i < b.N; i++ { + tracer := logger.NewStructLogger(&logger.Config{}).Hooks() + tracer.OnTxStart(evm.GetVMContext(), tx, msg.From) + evm.Config.Tracer = tracer + snap := state.StateDB.Snapshot() - st := core.NewStateTransition(evm, msg, new(core.GasPool).AddGas(tx.Gas())) - _, err = st.TransitionDb() + _, err := core.ApplyMessage(evm, msg, new(core.GasPool).AddGas(tx.Gas())) if err != nil { b.Fatal(err) } 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/ethclient.go b/ethclient/ethclient.go index 0972644d80..f10626c01f 100644 --- a/ethclient/ethclient.go +++ b/ethclient/ethclient.go @@ -630,6 +630,23 @@ func (ec *Client) SendTransaction(ctx context.Context, tx *types.Transaction) er return ec.c.CallContext(ctx, nil, "eth_sendRawTransaction", hexutil.Encode(data)) } +// RevertErrorData returns the 'revert reason' data of a contract call. +// +// This can be used with CallContract and EstimateGas, and only when the server is Geth. +func RevertErrorData(err error) ([]byte, bool) { + var ec rpc.Error + var ed rpc.DataError + if errors.As(err, &ec) && errors.As(err, &ed) && ec.ErrorCode() == 3 { + if eds, ok := ed.ErrorData().(string); ok { + revertData, err := hexutil.Decode(eds) + if err == nil { + return revertData, true + } + } + } + return nil, false +} + func toBlockNumArg(number *big.Int) string { if number == nil { return "latest" diff --git a/ethclient/ethclient_test.go b/ethclient/ethclient_test.go index 1b7e26fb74..4ad8a552d2 100644 --- a/ethclient/ethclient_test.go +++ b/ethclient/ethclient_test.go @@ -14,18 +14,20 @@ // You should have received a copy of the GNU Lesser General Public License // along with the go-ethereum library. If not, see . -package ethclient +package ethclient_test import ( "bytes" "context" "errors" + "fmt" "math/big" "reflect" "testing" "time" "github.com/ethereum/go-ethereum" + "github.com/ethereum/go-ethereum/accounts/abi" "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/consensus/ethash" "github.com/ethereum/go-ethereum/core" @@ -33,6 +35,7 @@ import ( "github.com/ethereum/go-ethereum/crypto" "github.com/ethereum/go-ethereum/eth" "github.com/ethereum/go-ethereum/eth/ethconfig" + "github.com/ethereum/go-ethereum/ethclient" "github.com/ethereum/go-ethereum/node" "github.com/ethereum/go-ethereum/params" "github.com/ethereum/go-ethereum/rpc" @@ -40,154 +43,33 @@ import ( // Verify that Client implements the ethereum interfaces. var ( - _ = ethereum.ChainReader(&Client{}) - _ = ethereum.TransactionReader(&Client{}) - _ = ethereum.ChainStateReader(&Client{}) - _ = ethereum.ChainSyncReader(&Client{}) - _ = ethereum.ContractCaller(&Client{}) - _ = ethereum.GasEstimator(&Client{}) - _ = ethereum.GasPricer(&Client{}) - _ = ethereum.LogFilterer(&Client{}) - _ = ethereum.PendingStateReader(&Client{}) - // _ = ethereum.PendingStateEventer(&Client{}) - _ = ethereum.PendingContractCaller(&Client{}) + _ = ethereum.ChainReader(ðclient.Client{}) + _ = ethereum.TransactionReader(ðclient.Client{}) + _ = ethereum.ChainStateReader(ðclient.Client{}) + _ = ethereum.ChainSyncReader(ðclient.Client{}) + _ = ethereum.ContractCaller(ðclient.Client{}) + _ = ethereum.GasEstimator(ðclient.Client{}) + _ = ethereum.GasPricer(ðclient.Client{}) + _ = ethereum.LogFilterer(ðclient.Client{}) + _ = ethereum.PendingStateReader(ðclient.Client{}) + // _ = ethereum.PendingStateEventer(ðclient.Client{}) + _ = ethereum.PendingContractCaller(ðclient.Client{}) ) -func TestToFilterArg(t *testing.T) { - blockHashErr := errors.New("cannot specify both BlockHash and FromBlock/ToBlock") - addresses := []common.Address{ - common.HexToAddress("0xD36722ADeC3EdCB29c8e7b5a47f352D701393462"), - } - blockHash := common.HexToHash( - "0xeb94bb7d78b73657a9d7a99792413f50c0a45c51fc62bdcb08a53f18e9a2b4eb", - ) - - for _, testCase := range []struct { - name string - input ethereum.FilterQuery - output interface{} - err error - }{ - { - "without BlockHash", - ethereum.FilterQuery{ - Addresses: addresses, - FromBlock: big.NewInt(1), - ToBlock: big.NewInt(2), - Topics: [][]common.Hash{}, - }, - map[string]interface{}{ - "address": addresses, - "fromBlock": "0x1", - "toBlock": "0x2", - "topics": [][]common.Hash{}, - }, - nil, - }, - { - "with nil fromBlock and nil toBlock", - ethereum.FilterQuery{ - Addresses: addresses, - Topics: [][]common.Hash{}, - }, - map[string]interface{}{ - "address": addresses, - "fromBlock": "0x0", - "toBlock": "latest", - "topics": [][]common.Hash{}, - }, - nil, - }, - { - "with negative fromBlock and negative toBlock", - ethereum.FilterQuery{ - Addresses: addresses, - FromBlock: big.NewInt(-1), - ToBlock: big.NewInt(-1), - Topics: [][]common.Hash{}, - }, - map[string]interface{}{ - "address": addresses, - "fromBlock": "pending", - "toBlock": "pending", - "topics": [][]common.Hash{}, - }, - nil, - }, - { - "with blockhash", - ethereum.FilterQuery{ - Addresses: addresses, - BlockHash: &blockHash, - Topics: [][]common.Hash{}, - }, - map[string]interface{}{ - "address": addresses, - "blockHash": blockHash, - "topics": [][]common.Hash{}, - }, - nil, - }, - { - "with blockhash and from block", - ethereum.FilterQuery{ - Addresses: addresses, - BlockHash: &blockHash, - FromBlock: big.NewInt(1), - Topics: [][]common.Hash{}, - }, - nil, - blockHashErr, - }, - { - "with blockhash and to block", - ethereum.FilterQuery{ - Addresses: addresses, - BlockHash: &blockHash, - ToBlock: big.NewInt(1), - Topics: [][]common.Hash{}, - }, - nil, - blockHashErr, - }, - { - "with blockhash and both from / to block", - ethereum.FilterQuery{ - Addresses: addresses, - BlockHash: &blockHash, - FromBlock: big.NewInt(1), - ToBlock: big.NewInt(2), - Topics: [][]common.Hash{}, - }, - nil, - blockHashErr, - }, - } { - t.Run(testCase.name, func(t *testing.T) { - output, err := toFilterArg(testCase.input) - if (testCase.err == nil) != (err == nil) { - t.Fatalf("expected error %v but got %v", testCase.err, err) - } - if testCase.err != nil { - if testCase.err.Error() != err.Error() { - t.Fatalf("expected error %v but got %v", testCase.err, err) - } - } else if !reflect.DeepEqual(testCase.output, output) { - t.Fatalf("expected filter arg %v but got %v", testCase.output, output) - } - }) - } -} - var ( - testKey, _ = crypto.HexToECDSA("b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291") - testAddr = crypto.PubkeyToAddress(testKey.PublicKey) - testBalance = big.NewInt(2e15) + testKey, _ = crypto.HexToECDSA("b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291") + testAddr = crypto.PubkeyToAddress(testKey.PublicKey) + testBalance = big.NewInt(2e15) + revertContractAddr = common.HexToAddress("290f1b36649a61e369c6276f6d29463335b4400c") + revertCode = common.FromHex("7f08c379a0000000000000000000000000000000000000000000000000000000006000526020600452600a6024527f75736572206572726f7200000000000000000000000000000000000000000000604452604e6000fd") ) var genesis = &core.Genesis{ - Config: params.AllEthashProtocolChanges, - Alloc: types.GenesisAlloc{testAddr: {Balance: testBalance}}, + Config: params.AllEthashProtocolChanges, + Alloc: types.GenesisAlloc{ + testAddr: {Balance: testBalance}, + revertContractAddr: {Code: revertCode}, + }, ExtraData: []byte("test genesis"), Timestamp: 9000, BaseFee: big.NewInt(params.InitialBaseFee), @@ -209,27 +91,30 @@ var testTx2 = types.MustSignNewTx(testKey, types.LatestSigner(genesis.Config), & To: &common.Address{2}, }) -func newTestBackend(t *testing.T) (*node.Node, []*types.Block) { +func newTestBackend(config *node.Config) (*node.Node, []*types.Block, error) { // Generate test chain. blocks := generateTestChain() // Create node - n, err := node.New(&node.Config{}) + if config == nil { + config = new(node.Config) + } + n, err := node.New(config) if err != nil { - t.Fatalf("can't create new node: %v", err) + return nil, nil, fmt.Errorf("can't create new node: %v", err) } // Create Ethereum Service - config := ðconfig.Config{Genesis: genesis, RPCGasCap: 1000000} - ethservice, err := eth.New(n, config) + ecfg := ðconfig.Config{Genesis: genesis, RPCGasCap: 1000000} + ethservice, err := eth.New(n, ecfg) if err != nil { - t.Fatalf("can't create new ethereum service: %v", err) + return nil, nil, fmt.Errorf("can't create new ethereum service: %v", err) } // Import the test chain. if err := n.Start(); err != nil { - t.Fatalf("can't start test node: %v", err) + return nil, nil, fmt.Errorf("can't start test node: %v", err) } if _, err := ethservice.BlockChain().InsertChain(blocks[1:]); err != nil { - t.Fatalf("can't import test blocks: %v", err) + return nil, nil, fmt.Errorf("can't import test blocks: %v", err) } // Ensure the tx indexing is fully generated for ; ; time.Sleep(time.Millisecond * 100) { @@ -238,7 +123,7 @@ func newTestBackend(t *testing.T) (*node.Node, []*types.Block) { break } } - return n, blocks + return n, blocks, nil } func generateTestChain() []*types.Block { @@ -256,7 +141,10 @@ func generateTestChain() []*types.Block { } func TestEthClient(t *testing.T) { - backend, chain := newTestBackend(t) + backend, chain, err := newTestBackend(nil) + if err != nil { + t.Fatal(err) + } client := backend.Attach() defer backend.Close() defer client.Close() @@ -324,7 +212,7 @@ func testHeader(t *testing.T, chain []*types.Block, client *rpc.Client) { } for name, tt := range tests { t.Run(name, func(t *testing.T) { - ec := NewClient(client) + ec := ethclient.NewClient(client) ctx, cancel := context.WithTimeout(context.Background(), 100*time.Millisecond) defer cancel() @@ -373,7 +261,7 @@ func testBalanceAt(t *testing.T, client *rpc.Client) { } for name, tt := range tests { t.Run(name, func(t *testing.T) { - ec := NewClient(client) + ec := ethclient.NewClient(client) ctx, cancel := context.WithTimeout(context.Background(), 100*time.Millisecond) defer cancel() @@ -389,7 +277,7 @@ func testBalanceAt(t *testing.T, client *rpc.Client) { } func testTransactionInBlock(t *testing.T, client *rpc.Client) { - ec := NewClient(client) + ec := ethclient.NewClient(client) // Get current block by number. block, err := ec.BlockByNumber(context.Background(), nil) @@ -421,7 +309,7 @@ func testTransactionInBlock(t *testing.T, client *rpc.Client) { } func testChainID(t *testing.T, client *rpc.Client) { - ec := NewClient(client) + ec := ethclient.NewClient(client) id, err := ec.ChainID(context.Background()) if err != nil { t.Fatalf("unexpected error: %v", err) @@ -432,7 +320,7 @@ func testChainID(t *testing.T, client *rpc.Client) { } func testGetBlock(t *testing.T, client *rpc.Client) { - ec := NewClient(client) + ec := ethclient.NewClient(client) // Get current block number blockNumber, err := ec.BlockNumber(context.Background()) @@ -477,7 +365,7 @@ func testGetBlock(t *testing.T, client *rpc.Client) { } func testStatusFunctions(t *testing.T, client *rpc.Client) { - ec := NewClient(client) + ec := ethclient.NewClient(client) // Sync progress progress, err := ec.SyncProgress(context.Background()) @@ -540,7 +428,7 @@ func testStatusFunctions(t *testing.T, client *rpc.Client) { } func testCallContractAtHash(t *testing.T, client *rpc.Client) { - ec := NewClient(client) + ec := ethclient.NewClient(client) // EstimateGas msg := ethereum.CallMsg{ @@ -567,7 +455,7 @@ func testCallContractAtHash(t *testing.T, client *rpc.Client) { } func testCallContract(t *testing.T, client *rpc.Client) { - ec := NewClient(client) + ec := ethclient.NewClient(client) // EstimateGas msg := ethereum.CallMsg{ @@ -594,7 +482,7 @@ func testCallContract(t *testing.T, client *rpc.Client) { } func testAtFunctions(t *testing.T, client *rpc.Client) { - ec := NewClient(client) + ec := ethclient.NewClient(client) block, err := ec.HeaderByNumber(context.Background(), big.NewInt(1)) if err != nil { @@ -697,7 +585,7 @@ func testAtFunctions(t *testing.T, client *rpc.Client) { } func testTransactionSender(t *testing.T, client *rpc.Client) { - ec := NewClient(client) + ec := ethclient.NewClient(client) ctx := context.Background() // Retrieve testTx1 via RPC. @@ -737,7 +625,7 @@ func testTransactionSender(t *testing.T, client *rpc.Client) { } } -func sendTransaction(ec *Client) error { +func sendTransaction(ec *ethclient.Client) error { chainID, err := ec.ChainID(context.Background()) if err != nil { return err @@ -760,3 +648,40 @@ func sendTransaction(ec *Client) error { } return ec.SendTransaction(context.Background(), tx) } + +// Here we show how to get the error message of reverted contract call. +func ExampleRevertErrorData() { + // First create an ethclient.Client instance. + ctx := context.Background() + ec, _ := ethclient.DialContext(ctx, exampleNode.HTTPEndpoint()) + + // Call the contract. + // Note we expect the call to return an error. + contract := common.HexToAddress("290f1b36649a61e369c6276f6d29463335b4400c") + call := ethereum.CallMsg{To: &contract, Gas: 30000} + result, err := ec.CallContract(ctx, call, nil) + if len(result) > 0 { + panic("got result") + } + if err == nil { + panic("call did not return error") + } + + // Extract the low-level revert data from the error. + revertData, ok := ethclient.RevertErrorData(err) + if !ok { + panic("unpacking revert failed") + } + fmt.Printf("revert: %x\n", revertData) + + // Parse the revert data to obtain the error message. + message, err := abi.UnpackRevert(revertData) + if err != nil { + panic("parsing ABI error failed: " + err.Error()) + } + fmt.Println("message:", message) + + // Output: + // revert: 08c379a00000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000a75736572206572726f72 + // message: user error +} diff --git a/console/bridge_test.go b/ethclient/example_test.go similarity index 52% rename from console/bridge_test.go rename to ethclient/example_test.go index e57e294fc5..5d0038f0c7 100644 --- a/console/bridge_test.go +++ b/ethclient/example_test.go @@ -1,4 +1,4 @@ -// Copyright 2020 The go-ethereum Authors +// 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 @@ -14,35 +14,22 @@ // You should have received a copy of the GNU Lesser General Public License // along with the go-ethereum library. If not, see . -package console +package ethclient_test import ( - "testing" - - "github.com/dop251/goja" - "github.com/ethereum/go-ethereum/internal/jsre" + "github.com/ethereum/go-ethereum/node" ) -// TestUndefinedAsParam ensures that personal functions can receive -// `undefined` as a parameter. -func TestUndefinedAsParam(t *testing.T) { - b := bridge{} - call := jsre.Call{} - call.Arguments = []goja.Value{goja.Undefined()} +var exampleNode *node.Node - b.UnlockAccount(call) - b.Sign(call) - b.Sleep(call) -} - -// TestNullAsParam ensures that personal functions can receive -// `null` as a parameter. -func TestNullAsParam(t *testing.T) { - b := bridge{} - call := jsre.Call{} - call.Arguments = []goja.Value{goja.Null()} - - b.UnlockAccount(call) - b.Sign(call) - b.Sleep(call) +// launch example server +func init() { + config := &node.Config{ + HTTPHost: "127.0.0.1", + } + n, _, err := newTestBackend(config) + if err != nil { + panic("can't launch node: " + err.Error()) + } + exampleNode = n } diff --git a/ethclient/gethclient/gethclient_test.go b/ethclient/gethclient/gethclient_test.go index 36ea290a85..65d006d1e6 100644 --- a/ethclient/gethclient/gethclient_test.go +++ b/ethclient/gethclient/gethclient_test.go @@ -21,6 +21,7 @@ import ( "context" "encoding/json" "math/big" + "strings" "testing" "github.com/ethereum/go-ethereum" @@ -164,55 +165,85 @@ func TestGethClient(t *testing.T) { func testAccessList(t *testing.T, client *rpc.Client) { ec := New(client) - // Test transfer - msg := ethereum.CallMsg{ - From: testAddr, - To: &common.Address{}, - Gas: 21000, - GasPrice: big.NewInt(875000000), - Value: big.NewInt(1), - } - al, gas, vmErr, err := ec.CreateAccessList(context.Background(), msg) - if err != nil { - t.Fatalf("unexpected error: %v", err) - } - if vmErr != "" { - t.Fatalf("unexpected vm error: %v", vmErr) - } - if gas != 21000 { - t.Fatalf("unexpected gas used: %v", gas) - } - if len(*al) != 0 { - t.Fatalf("unexpected length of accesslist: %v", len(*al)) - } - // Test reverting transaction - msg = ethereum.CallMsg{ - From: testAddr, - To: nil, - Gas: 100000, - GasPrice: big.NewInt(1000000000), - Value: big.NewInt(1), - Data: common.FromHex("0x608060806080608155fd"), - } - al, gas, vmErr, err = ec.CreateAccessList(context.Background(), msg) - if err != nil { - t.Fatalf("unexpected error: %v", err) - } - if vmErr == "" { - t.Fatalf("wanted vmErr, got none") - } - if gas == 21000 { - t.Fatalf("unexpected gas used: %v", gas) - } - if len(*al) != 1 || al.StorageKeys() != 1 { - t.Fatalf("unexpected length of accesslist: %v", len(*al)) - } - // address changes between calls, so we can't test for it. - if (*al)[0].Address == common.HexToAddress("0x0") { - t.Fatalf("unexpected address: %v", (*al)[0].Address) - } - if (*al)[0].StorageKeys[0] != common.HexToHash("0x0000000000000000000000000000000000000000000000000000000000000081") { - t.Fatalf("unexpected storage key: %v", (*al)[0].StorageKeys[0]) + + for i, tc := range []struct { + msg ethereum.CallMsg + wantGas uint64 + wantErr string + wantVMErr string + wantAL string + }{ + { // Test transfer + msg: ethereum.CallMsg{ + From: testAddr, + To: &common.Address{}, + Gas: 21000, + GasPrice: big.NewInt(875000000), + Value: big.NewInt(1), + }, + wantGas: 21000, + wantAL: `[]`, + }, + { // Test reverting transaction + msg: ethereum.CallMsg{ + From: testAddr, + To: nil, + Gas: 100000, + GasPrice: big.NewInt(1000000000), + Value: big.NewInt(1), + Data: common.FromHex("0x608060806080608155fd"), + }, + wantGas: 77496, + wantVMErr: "execution reverted", + wantAL: `[ + { + "address": "0x3a220f351252089d385b29beca14e27f204c296a", + "storageKeys": [ + "0x0000000000000000000000000000000000000000000000000000000000000081" + ] + } +]`, + }, + { // error when gasPrice is less than baseFee + msg: ethereum.CallMsg{ + From: testAddr, + To: &common.Address{}, + Gas: 21000, + GasPrice: big.NewInt(1), // less than baseFee + Value: big.NewInt(1), + }, + wantErr: "max fee per gas less than block base fee", + }, + { // when gasPrice is not specified + msg: ethereum.CallMsg{ + From: testAddr, + To: &common.Address{}, + Gas: 21000, + Value: big.NewInt(1), + }, + wantGas: 21000, + wantAL: `[]`, + }, + } { + al, gas, vmErr, err := ec.CreateAccessList(context.Background(), tc.msg) + if tc.wantErr != "" { + if !strings.Contains(err.Error(), tc.wantErr) { + t.Fatalf("test %d: wrong error: %v", i, err) + } + continue + } else if err != nil { + t.Fatalf("test %d: wrong error: %v", i, err) + } + if have, want := vmErr, tc.wantVMErr; have != want { + t.Fatalf("test %d: vmErr wrong, have %v want %v", i, have, want) + } + if have, want := gas, tc.wantGas; have != want { + t.Fatalf("test %d: gas wrong, have %v want %v", i, have, want) + } + haveList, _ := json.MarshalIndent(al, "", " ") + if have, want := string(haveList), tc.wantAL; have != want { + t.Fatalf("test %d: access list wrong, have:\n%v\nwant:\n%v", i, have, want) + } } } diff --git a/ethclient/simulated/backend.go b/ethclient/simulated/backend.go index 6e07aa68d0..65d44b9efa 100644 --- a/ethclient/simulated/backend.go +++ b/ethclient/simulated/backend.go @@ -26,7 +26,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" @@ -85,7 +84,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/ethclient/types_test.go b/ethclient/types_test.go new file mode 100644 index 0000000000..02f9f21758 --- /dev/null +++ b/ethclient/types_test.go @@ -0,0 +1,153 @@ +// Copyright 2016 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 ethclient + +import ( + "errors" + "math/big" + "reflect" + "testing" + + "github.com/ethereum/go-ethereum" + "github.com/ethereum/go-ethereum/common" +) + +func TestToFilterArg(t *testing.T) { + blockHashErr := errors.New("cannot specify both BlockHash and FromBlock/ToBlock") + addresses := []common.Address{ + common.HexToAddress("0xD36722ADeC3EdCB29c8e7b5a47f352D701393462"), + } + blockHash := common.HexToHash( + "0xeb94bb7d78b73657a9d7a99792413f50c0a45c51fc62bdcb08a53f18e9a2b4eb", + ) + + for _, testCase := range []struct { + name string + input ethereum.FilterQuery + output interface{} + err error + }{ + { + "without BlockHash", + ethereum.FilterQuery{ + Addresses: addresses, + FromBlock: big.NewInt(1), + ToBlock: big.NewInt(2), + Topics: [][]common.Hash{}, + }, + map[string]interface{}{ + "address": addresses, + "fromBlock": "0x1", + "toBlock": "0x2", + "topics": [][]common.Hash{}, + }, + nil, + }, + { + "with nil fromBlock and nil toBlock", + ethereum.FilterQuery{ + Addresses: addresses, + Topics: [][]common.Hash{}, + }, + map[string]interface{}{ + "address": addresses, + "fromBlock": "0x0", + "toBlock": "latest", + "topics": [][]common.Hash{}, + }, + nil, + }, + { + "with negative fromBlock and negative toBlock", + ethereum.FilterQuery{ + Addresses: addresses, + FromBlock: big.NewInt(-1), + ToBlock: big.NewInt(-1), + Topics: [][]common.Hash{}, + }, + map[string]interface{}{ + "address": addresses, + "fromBlock": "pending", + "toBlock": "pending", + "topics": [][]common.Hash{}, + }, + nil, + }, + { + "with blockhash", + ethereum.FilterQuery{ + Addresses: addresses, + BlockHash: &blockHash, + Topics: [][]common.Hash{}, + }, + map[string]interface{}{ + "address": addresses, + "blockHash": blockHash, + "topics": [][]common.Hash{}, + }, + nil, + }, + { + "with blockhash and from block", + ethereum.FilterQuery{ + Addresses: addresses, + BlockHash: &blockHash, + FromBlock: big.NewInt(1), + Topics: [][]common.Hash{}, + }, + nil, + blockHashErr, + }, + { + "with blockhash and to block", + ethereum.FilterQuery{ + Addresses: addresses, + BlockHash: &blockHash, + ToBlock: big.NewInt(1), + Topics: [][]common.Hash{}, + }, + nil, + blockHashErr, + }, + { + "with blockhash and both from / to block", + ethereum.FilterQuery{ + Addresses: addresses, + BlockHash: &blockHash, + FromBlock: big.NewInt(1), + ToBlock: big.NewInt(2), + Topics: [][]common.Hash{}, + }, + nil, + blockHashErr, + }, + } { + t.Run(testCase.name, func(t *testing.T) { + output, err := toFilterArg(testCase.input) + if (testCase.err == nil) != (err == nil) { + t.Fatalf("expected error %v but got %v", testCase.err, err) + } + if testCase.err != nil { + if testCase.err.Error() != err.Error() { + t.Fatalf("expected error %v but got %v", testCase.err, err) + } + } else if !reflect.DeepEqual(testCase.output, output) { + t.Fatalf("expected filter arg %v but got %v", testCase.output, output) + } + }) + } +} diff --git a/ethdb/database.go b/ethdb/database.go index c6e76fd2fe..323f8f5d6f 100644 --- a/ethdb/database.go +++ b/ethdb/database.go @@ -37,6 +37,13 @@ type KeyValueWriter interface { Delete(key []byte) error } +// KeyValueRangeDeleter wraps the DeleteRange method of a backing data store. +type KeyValueRangeDeleter interface { + // DeleteRange deletes all of the keys (and values) in the range [start,end) + // (inclusive on start, exclusive on end). + DeleteRange(start, end []byte) error +} + // KeyValueStater wraps the Stat method of a backing data store. type KeyValueStater interface { // Stat returns the statistic data of the database. @@ -61,6 +68,7 @@ type KeyValueStore interface { KeyValueReader KeyValueWriter KeyValueStater + KeyValueRangeDeleter Batcher Iteratee Compacter @@ -154,25 +162,12 @@ type Reader interface { AncientReader } -// Writer contains the methods required to write data to both key-value as well as -// immutable ancient data. -type Writer interface { - KeyValueWriter - AncientWriter -} - -// Stater contains the methods required to retrieve states from both key-value as well as -// immutable ancient data. -type Stater interface { - KeyValueStater - AncientStater -} - // AncientStore contains all the methods required to allow handling different // ancient data stores backing immutable data store. type AncientStore interface { AncientReader AncientWriter + AncientStater io.Closer } @@ -187,11 +182,6 @@ type ResettableAncientStore interface { // Database contains all the methods required by the high level database to not // only access the key-value data store but also the ancient chain store. type Database interface { - Reader - Writer - Batcher - Iteratee - Stater - Compacter - io.Closer + KeyValueStore + AncientStore } diff --git a/ethdb/dbtest/testsuite.go b/ethdb/dbtest/testsuite.go index 1af55a0e38..52e6b287cf 100644 --- a/ethdb/dbtest/testsuite.go +++ b/ethdb/dbtest/testsuite.go @@ -21,6 +21,7 @@ import ( "crypto/rand" "slices" "sort" + "strconv" "testing" "github.com/ethereum/go-ethereum/ethdb" @@ -343,6 +344,64 @@ func TestDatabaseSuite(t *testing.T, New func() ethdb.KeyValueStore) { t.Fatalf("expected error on batch.Write after Close") } }) + + t.Run("DeleteRange", func(t *testing.T) { + db := New() + defer db.Close() + + addRange := func(start, stop int) { + for i := start; i <= stop; i++ { + db.Put([]byte(strconv.Itoa(i)), nil) + } + } + + checkRange := func(start, stop int, exp bool) { + for i := start; i <= stop; i++ { + has, _ := db.Has([]byte(strconv.Itoa(i))) + if has && !exp { + t.Fatalf("unexpected key %d", i) + } + if !has && exp { + t.Fatalf("missing expected key %d", i) + } + } + } + + addRange(1, 9) + db.DeleteRange([]byte("9"), []byte("1")) + checkRange(1, 9, true) + db.DeleteRange([]byte("5"), []byte("5")) + checkRange(1, 9, true) + db.DeleteRange([]byte("5"), []byte("50")) + checkRange(1, 4, true) + checkRange(5, 5, false) + checkRange(6, 9, true) + db.DeleteRange([]byte(""), []byte("a")) + checkRange(1, 9, false) + + addRange(1, 999) + db.DeleteRange([]byte("12345"), []byte("54321")) + checkRange(1, 1, true) + checkRange(2, 5, false) + checkRange(6, 12, true) + checkRange(13, 54, false) + checkRange(55, 123, true) + checkRange(124, 543, false) + checkRange(544, 999, true) + + addRange(1, 999) + db.DeleteRange([]byte("3"), []byte("7")) + checkRange(1, 2, true) + checkRange(3, 6, false) + checkRange(7, 29, true) + checkRange(30, 69, false) + checkRange(70, 299, true) + checkRange(300, 699, false) + checkRange(700, 999, true) + + db.DeleteRange([]byte(""), []byte("a")) + checkRange(1, 999, false) + }) } // BenchDatabaseSuite runs a suite of benchmarks against a KeyValueStore database @@ -438,6 +497,29 @@ func BenchDatabaseSuite(b *testing.B, New func() ethdb.KeyValueStore) { benchBatchWrite(b, keys, vals) }) }) + b.Run("DeleteRange", func(b *testing.B) { + benchDeleteRange := func(b *testing.B, count int) { + db := New() + defer db.Close() + + for i := 0; i < count; i++ { + db.Put([]byte(strconv.Itoa(i)), nil) + } + b.ResetTimer() + b.ReportAllocs() + + db.DeleteRange([]byte("0"), []byte("999999999")) + } + b.Run("DeleteRange100", func(b *testing.B) { + benchDeleteRange(b, 100) + }) + b.Run("DeleteRange1k", func(b *testing.B) { + benchDeleteRange(b, 1000) + }) + b.Run("DeleteRange10k", func(b *testing.B) { + benchDeleteRange(b, 10000) + }) + }) } func iterateKeys(it ethdb.Iterator) []string { diff --git a/ethdb/leveldb/leveldb.go b/ethdb/leveldb/leveldb.go index 24925a4f04..f18503c941 100644 --- a/ethdb/leveldb/leveldb.go +++ b/ethdb/leveldb/leveldb.go @@ -21,6 +21,7 @@ package leveldb import ( + "bytes" "fmt" "sync" "time" @@ -61,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) + 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 + 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 @@ -206,6 +207,36 @@ func (db *Database) Delete(key []byte) error { return db.db.Delete(key, nil) } +var ErrTooManyKeys = errors.New("too many keys in deleted range") + +// DeleteRange deletes all of the keys (and values) in the range [start,end) +// (inclusive on start, exclusive on end). +// Note that this is a fallback implementation as leveldb does not natively +// support range deletion. It can be slow and therefore the number of deleted +// keys is limited in order to avoid blocking for a very long time. +// ErrTooManyKeys is returned if the range has only been partially deleted. +// In this case the caller can repeat the call until it finally succeeds. +func (db *Database) DeleteRange(start, end []byte) error { + batch := db.NewBatch() + it := db.NewIterator(nil, start) + defer it.Release() + + var count int + for it.Next() && bytes.Compare(end, it.Key()) > 0 { + count++ + if count > 10000 { // should not block for more than a second + if err := batch.Write(); err != nil { + return err + } + return ErrTooManyKeys + } + if err := batch.Delete(it.Key()); err != nil { + return err + } + } + return batch.Write() +} + // NewBatch creates a write-only key-value store that buffers changes to its host // database until a final write is called. func (db *Database) NewBatch() ethdb.Batch { diff --git a/ethdb/memorydb/memorydb.go b/ethdb/memorydb/memorydb.go index 532e0dfe3f..c6fba39cfd 100644 --- a/ethdb/memorydb/memorydb.go +++ b/ethdb/memorydb/memorydb.go @@ -18,6 +18,7 @@ package memorydb import ( + "bytes" "errors" "sort" "strings" @@ -121,6 +122,20 @@ func (db *Database) Delete(key []byte) error { return nil } +// DeleteRange deletes all of the keys (and values) in the range [start,end) +// (inclusive on start, exclusive on end). +func (db *Database) DeleteRange(start, end []byte) error { + it := db.NewIterator(nil, start) + defer it.Release() + + for it.Next() && bytes.Compare(end, it.Key()) > 0 { + if err := db.Delete(it.Key()); err != nil { + return err + } + } + return nil +} + // NewBatch creates a write-only key-value store that buffers changes to its host // database until a final write is called. func (db *Database) NewBatch() ethdb.Batch { diff --git a/ethdb/pebble/pebble.go b/ethdb/pebble/pebble.go index 7a3be797a3..8d6fcd2d51 100644 --- a/ethdb/pebble/pebble.go +++ b/ethdb/pebble/pebble.go @@ -58,21 +58,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 + 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 + 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 @@ -144,7 +144,7 @@ func (l panicLogger) Fatalf(format string, args ...interface{}) { // New returns a wrapped pebble DB object. The namespace is the prefix that the // metrics reporting should use for surfacing internal stats. -func New(file string, cache int, handles int, namespace string, readonly bool, ephemeral bool) (*Database, error) { +func New(file string, cache int, handles int, namespace string, readonly bool) (*Database, error) { // Ensure we have some minimal caching and file guarantees if cache < minCache { cache = minCache @@ -185,7 +185,7 @@ func New(file string, cache int, handles int, namespace string, readonly bool, e fn: file, log: logger, quitChan: make(chan chan error), - writeOptions: &pebble.WriteOptions{Sync: !ephemeral}, + writeOptions: &pebble.WriteOptions{Sync: false}, } opt := &pebble.Options{ // Pebble has a single combined cache area and the write @@ -213,12 +213,12 @@ func New(file string, cache int, handles int, namespace string, readonly bool, e // options for the last level are used for all subsequent levels. Levels: []pebble.LevelOptions{ {TargetFileSize: 2 * 1024 * 1024, FilterPolicy: bloom.FilterPolicy(10)}, - {TargetFileSize: 2 * 1024 * 1024, FilterPolicy: bloom.FilterPolicy(10)}, - {TargetFileSize: 2 * 1024 * 1024, FilterPolicy: bloom.FilterPolicy(10)}, - {TargetFileSize: 2 * 1024 * 1024, FilterPolicy: bloom.FilterPolicy(10)}, - {TargetFileSize: 2 * 1024 * 1024, FilterPolicy: bloom.FilterPolicy(10)}, - {TargetFileSize: 2 * 1024 * 1024, FilterPolicy: bloom.FilterPolicy(10)}, - {TargetFileSize: 2 * 1024 * 1024, FilterPolicy: bloom.FilterPolicy(10)}, + {TargetFileSize: 4 * 1024 * 1024, FilterPolicy: bloom.FilterPolicy(10)}, + {TargetFileSize: 8 * 1024 * 1024, FilterPolicy: bloom.FilterPolicy(10)}, + {TargetFileSize: 16 * 1024 * 1024, FilterPolicy: bloom.FilterPolicy(10)}, + {TargetFileSize: 32 * 1024 * 1024, FilterPolicy: bloom.FilterPolicy(10)}, + {TargetFileSize: 64 * 1024 * 1024, FilterPolicy: bloom.FilterPolicy(10)}, + {TargetFileSize: 128 * 1024 * 1024, FilterPolicy: bloom.FilterPolicy(10)}, }, ReadOnly: readonly, EventListener: &pebble.EventListener{ @@ -335,7 +335,18 @@ func (d *Database) Delete(key []byte) error { if d.closed { return pebble.ErrClosed } - return d.db.Delete(key, nil) + return d.db.Delete(key, d.writeOptions) +} + +// DeleteRange deletes all of the keys (and values) in the range [start,end) +// (inclusive on start, exclusive on end). +func (d *Database) DeleteRange(start, end []byte) error { + d.quitLock.RLock() + defer d.quitLock.RUnlock() + if d.closed { + return pebble.ErrClosed + } + return d.db.DeleteRange(start, end, d.writeOptions) } // NewBatch creates a write-only key-value store that buffers changes to its host diff --git a/ethdb/remotedb/remotedb.go b/ethdb/remotedb/remotedb.go index d0f018cb01..247a4392db 100644 --- a/ethdb/remotedb/remotedb.go +++ b/ethdb/remotedb/remotedb.go @@ -94,6 +94,10 @@ func (db *Database) Delete(key []byte) error { panic("not supported") } +func (db *Database) DeleteRange(start, end []byte) error { + panic("not supported") +} + func (db *Database) ModifyAncients(f func(ethdb.AncientWriteOp) error) (int64, error) { panic("not supported") } diff --git a/ethstats/ethstats.go b/ethstats/ethstats.go index c845db1164..afed5332df 100644 --- a/ethstats/ethstats.go +++ b/ethstats/ethstats.go @@ -219,7 +219,7 @@ func (s *Service) loop(chainHeadCh chan core.ChainHeadEvent, txEventCh chan core // Start a goroutine that exhausts the subscriptions to avoid events piling up var ( quitCh = make(chan struct{}) - headCh = make(chan *types.Block, 1) + headCh = make(chan *types.Header, 1) txCh = make(chan struct{}, 1) ) go func() { @@ -231,7 +231,7 @@ func (s *Service) loop(chainHeadCh chan core.ChainHeadEvent, txEventCh chan core // Notify of chain head events, but drop if too frequent case head := <-chainHeadCh: select { - case headCh <- head.Block: + case headCh <- head.Header: default: } @@ -602,9 +602,9 @@ func (s uncleStats) MarshalJSON() ([]byte, error) { } // reportBlock retrieves the current chain head and reports it to the stats server. -func (s *Service) reportBlock(conn *connWrapper, block *types.Block) error { +func (s *Service) reportBlock(conn *connWrapper, header *types.Header) error { // Gather the block details from the header or block chain - details := s.assembleBlockStats(block) + details := s.assembleBlockStats(header) // Short circuit if the block detail is not available. if details == nil { @@ -625,10 +625,9 @@ func (s *Service) reportBlock(conn *connWrapper, block *types.Block) error { // assembleBlockStats retrieves any required metadata to report a single block // and assembles the block stats. If block is nil, the current head is processed. -func (s *Service) assembleBlockStats(block *types.Block) *blockStats { +func (s *Service) assembleBlockStats(header *types.Header) *blockStats { // Gather the block infos from the local blockchain var ( - header *types.Header td *big.Int txs []txStats uncles []*types.Header @@ -638,16 +637,13 @@ func (s *Service) assembleBlockStats(block *types.Block) *blockStats { fullBackend, ok := s.backend.(fullNodeBackend) if ok { // Retrieve current chain head if no block is given. - if block == nil { - head := fullBackend.CurrentBlock() - block, _ = fullBackend.BlockByNumber(context.Background(), rpc.BlockNumber(head.Number.Uint64())) + if header == nil { + header = fullBackend.CurrentBlock() } - // Short circuit if no block is available. It might happen when - // the blockchain is reorging. + block, _ := fullBackend.BlockByNumber(context.Background(), rpc.BlockNumber(header.Number.Uint64())) if block == nil { return nil } - header = block.Header() td = fullBackend.GetTd(context.Background(), header.Hash()) txs = make([]txStats, len(block.Transactions())) @@ -657,15 +653,12 @@ func (s *Service) assembleBlockStats(block *types.Block) *blockStats { uncles = block.Uncles() } else { // Light nodes would need on-demand lookups for transactions/uncles, skip - if block != nil { - header = block.Header() - } else { + if header == nil { header = s.backend.CurrentHeader() } td = s.backend.GetTd(context.Background(), header.Hash()) txs = []txStats{} } - // Assemble and return the block stats author, _ := s.engine.Author(header) @@ -708,19 +701,10 @@ func (s *Service) reportHistory(conn *connWrapper, list []uint64) error { // Gather the batch of blocks to report history := make([]*blockStats, len(indexes)) for i, number := range indexes { - fullBackend, ok := s.backend.(fullNodeBackend) // Retrieve the next block if it's known to us - var block *types.Block - if ok { - block, _ = fullBackend.BlockByNumber(context.Background(), rpc.BlockNumber(number)) // TODO ignore error here ? - } else { - if header, _ := s.backend.HeaderByNumber(context.Background(), rpc.BlockNumber(number)); header != nil { - block = types.NewBlockWithHeader(header) - } - } - // If we do have the block, add to the history and continue - if block != nil { - history[len(history)-1-i] = s.assembleBlockStats(block) + header, _ := s.backend.HeaderByNumber(context.Background(), rpc.BlockNumber(number)) + if header != nil { + history[len(history)-1-i] = s.assembleBlockStats(header) continue } // Ran out of blocks, cut the report short and send diff --git a/go.mod b/go.mod index e9692cf8b3..35018fbe08 100644 --- a/go.mod +++ b/go.mod @@ -12,26 +12,26 @@ require ( github.com/aws/aws-sdk-go-v2/config v1.18.45 github.com/aws/aws-sdk-go-v2/credentials v1.13.43 github.com/aws/aws-sdk-go-v2/service/route53 v1.30.2 - github.com/btcsuite/btcd/btcec/v2 v2.3.4 github.com/cespare/cp v0.1.0 github.com/cloudflare/cloudflare-go v0.79.0 github.com/cockroachdb/pebble v1.1.2 - github.com/consensys/gnark-crypto v0.12.1 - github.com/crate-crypto/go-ipa v0.0.0-20240223125850-b1e8a79f509c - github.com/crate-crypto/go-kzg-4844 v1.0.0 + github.com/consensys/gnark-crypto v0.14.0 + github.com/crate-crypto/go-ipa v0.0.0-20240724233137-53bbb0ceb27a + github.com/crate-crypto/go-kzg-4844 v1.1.0 github.com/davecgh/go-spew v1.1.1 github.com/deckarep/golang-set/v2 v2.6.0 + github.com/decred/dcrd/dcrec/secp256k1/v4 v4.0.1 github.com/donovanhide/eventsource v0.0.0-20210830082556-c59027999da0 github.com/dop251/goja v0.0.0-20230605162241-28ee0ee714f3 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/ferranbt/fastssz v0.1.2 github.com/fjl/gencodec v0.0.0-20230517082657-f9840df7b83e github.com/fsnotify/fsnotify v1.6.0 github.com/gballet/go-libpcsclite v0.0.0-20190607065134-2772fd86a8ff github.com/gofrs/flock v0.8.1 - github.com/golang-jwt/jwt/v4 v4.5.0 + github.com/golang-jwt/jwt/v4 v4.5.1 github.com/golang/snappy v0.0.5-0.20220116011046-fa5810519dcb github.com/google/gofuzz v1.2.0 github.com/google/uuid v1.3.0 @@ -40,14 +40,13 @@ require ( github.com/hashicorp/go-bexpr v0.1.10 github.com/holiman/billy v0.0.0-20240216141850-2abb0c79d3c4 github.com/holiman/bloomfilter/v2 v2.0.3 - github.com/holiman/uint256 v1.3.1 + github.com/holiman/uint256 v1.3.2 github.com/huin/goupnp v1.3.0 github.com/influxdata/influxdb-client-go/v2 v2.4.0 github.com/influxdata/influxdb1-client v0.0.0-20220302092344-a9ab5670611c github.com/jackpal/go-nat-pmp v1.0.2 github.com/jedisct1/go-minisign v0.0.0-20230811132847-661be99b8267 github.com/karalabe/hid v1.0.1-0.20240306101548-573246063e52 - github.com/kilic/bls12-381 v0.1.0 github.com/kylelemons/godebug v1.1.0 github.com/mattn/go-colorable v0.1.13 github.com/mattn/go-isatty v0.0.20 @@ -63,16 +62,15 @@ require ( github.com/stretchr/testify v1.9.0 github.com/supranational/blst v0.3.13 github.com/syndtr/goleveldb v1.0.1-0.20210819022825-2ae1ddf74ef7 - github.com/tyler-smith/go-bip39 v1.1.0 github.com/urfave/cli/v2 v2.25.7 go.uber.org/automaxprocs v1.5.2 - golang.org/x/crypto v0.22.0 + golang.org/x/crypto v0.31.0 golang.org/x/exp v0.0.0-20231110203233-9a3e6036ecaa - 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.21.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 @@ -93,16 +91,15 @@ require ( github.com/aws/aws-sdk-go-v2/service/sts v1.23.2 // indirect github.com/aws/smithy-go v1.15.0 // 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/cespare/xxhash/v2 v2.3.0 // indirect github.com/cockroachdb/errors v1.11.3 // indirect github.com/cockroachdb/fifo v0.0.0-20240606204812-0bbfbd93a7ce // indirect 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/cpuguy83/go-md2man/v2 v2.0.2 // indirect - github.com/decred/dcrd/dcrec/secp256k1/v4 v4.0.1 // indirect github.com/deepmap/oapi-codegen v1.6.0 // indirect github.com/dlclark/regexp2 v1.7.0 // indirect github.com/garslo/gogen v0.0.0-20170306192744-1d203ffc1f61 // indirect @@ -118,6 +115,7 @@ require ( github.com/hashicorp/go-retryablehttp v0.7.4 // indirect github.com/influxdata/line-protocol v0.0.0-20200327222509-2487e7298839 // indirect github.com/jmespath/go-jmespath v0.4.0 // indirect + github.com/kilic/bls12-381 v0.1.0 // indirect github.com/klauspost/compress v1.16.0 // indirect github.com/klauspost/cpuid/v2 v2.0.9 // indirect github.com/kr/pretty v0.3.1 // indirect @@ -137,13 +135,13 @@ require ( github.com/prometheus/common v0.32.1 // indirect github.com/prometheus/procfs v0.7.3 // indirect github.com/rivo/uniseg v0.2.0 // indirect - github.com/rogpeppe/go-internal v1.9.0 // indirect + github.com/rogpeppe/go-internal v1.12.0 // indirect github.com/russross/blackfriday/v2 v2.1.0 // indirect github.com/tklauser/go-sysconf v0.3.12 // indirect github.com/tklauser/numcpus v0.6.1 // indirect github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673 // 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 gopkg.in/yaml.v2 v2.4.0 // indirect rsc.io/tmplfunc v0.0.3 // indirect ) diff --git a/go.sum b/go.sum index 21a5e5bcd8..ded07f4867 100644 --- a/go.sum +++ b/go.sum @@ -90,12 +90,8 @@ github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24 github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8= 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/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/btcsuite/btcd/btcec/v2 v2.3.4 h1:3EJjcN70HCu/mwqlUsGK8GcNVyLVxFDlWurTXGPFfiQ= -github.com/btcsuite/btcd/btcec/v2 v2.3.4/go.mod h1:zYzJ8etWJQIv1Ogk7OzpWjowwOdXY1W/17j2MW85J04= -github.com/btcsuite/btcd/chaincfg/chainhash v1.0.1 h1:q0rUy8C/TYNBQS1+CGKw68tLOFYSNEs0TFnxxnS9+4U= -github.com/btcsuite/btcd/chaincfg/chainhash v1.0.1/go.mod h1:7SFka0XMvUgj3hfZtydOrQY2mwhPclbT2snogU7SQQc= +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/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= github.com/cespare/cp v0.1.0 h1:SE+dxFebS7Iik5LK0tsi1k9ZCxEaFX4AjQmoyA+1dJk= github.com/cespare/cp v0.1.0/go.mod h1:SOGHArjBr4JWaSDEVpWpo/hNg6RoKrls6Oh40hiwW+s= @@ -128,16 +124,16 @@ github.com/cockroachdb/redact v1.1.5 h1:u1PMllDkdFfPWaNGMyLD1+so+aq3uUItthCFqzwP github.com/cockroachdb/redact v1.1.5/go.mod h1:BVNblN9mBWFyMyqK1k3AAiSxhvhfK2oOZZ2lK+dpvRg= github.com/cockroachdb/tokenbucket v0.0.0-20230807174530-cc333fc44b06 h1:zuQyyAKVxetITBuuhv3BI9cMrmStnpT18zmgmTxunpo= github.com/cockroachdb/tokenbucket v0.0.0-20230807174530-cc333fc44b06/go.mod h1:7nc4anLGjupUW/PeY5qiNYsdNXj7zopG+eqsS7To5IQ= -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/gnark-crypto v0.12.1 h1:lHH39WuuFgVHONRl3J0LRBtuYdQTumFSDtJF7HpyG8M= -github.com/consensys/gnark-crypto v0.12.1/go.mod h1:v2Gy7L/4ZRosZ7Ivs+9SfUDr0f5UlG+EM5t7MPHiLuY= +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.14.0 h1:DDBdl4HaBtdQsq/wfMwJvZNE80sHidrK3Nfrefatm0E= +github.com/consensys/gnark-crypto v0.14.0/go.mod h1:CU4UijNPsHawiVGNxe9co07FkzCeWHHrb1li/n1XoU0= github.com/cpuguy83/go-md2man/v2 v2.0.2 h1:p1EgwI/C7NhT0JmVkwCD2ZBK8j4aeHQX2pMHHBfMQ6w= github.com/cpuguy83/go-md2man/v2 v2.0.2/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= -github.com/crate-crypto/go-ipa v0.0.0-20240223125850-b1e8a79f509c h1:uQYC5Z1mdLRPrZhHjHxufI8+2UG/i25QG92j0Er9p6I= -github.com/crate-crypto/go-ipa v0.0.0-20240223125850-b1e8a79f509c/go.mod h1:geZJZH3SzKCqnz5VT0q/DyIG/tvu/dZk+VIfXicupJs= -github.com/crate-crypto/go-kzg-4844 v1.0.0 h1:TsSgHwrkTKecKJ4kadtHi4b3xHW5dCFUDFnUp1TsawI= -github.com/crate-crypto/go-kzg-4844 v1.0.0/go.mod h1:1kMhvPgI0Ky3yIa+9lFySEBUBXkYxeOi8ZF1sYioxhc= +github.com/crate-crypto/go-ipa v0.0.0-20240724233137-53bbb0ceb27a h1:W8mUrRp6NOVl3J+MYp5kPMoUZPp7aOYHtaua31lwRHg= +github.com/crate-crypto/go-ipa v0.0.0-20240724233137-53bbb0ceb27a/go.mod h1:sTwzHBvIzm2RfVCGNEBZgRyjwK40bVoun3ZnGOCafNM= +github.com/crate-crypto/go-kzg-4844 v1.1.0 h1:EN/u9k2TF6OWSHrCCDBBU6GLNMq88OspHHlMnHfoyU4= +github.com/crate-crypto/go-kzg-4844 v1.1.0/go.mod h1:JolLjpSff1tCCJKaJx4psrlEdlXuJEC996PL3tTAFks= github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= github.com/cyberdelia/templates v0.0.0-20141128023046-ca7fffd4298c/go.mod h1:GyV+0YP4qX0UQ7r2MoYZ+AvYDp12OF5yg4q8rGnyNh4= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= @@ -170,8 +166,8 @@ github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1m github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= 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/fatih/color v1.16.0 h1:zmkK9Ngbjj+K0yRhTVONQh1p/HknKYSlNT+vZCzyokM= github.com/fatih/color v1.16.0/go.mod h1:fL2Sau1YI5c0pdGEVCbKQbLXB6edEj1ZgiY4NijnWvE= github.com/ferranbt/fastssz v0.1.2 h1:Dky6dXlngF6Qjc+EfDipAkE83N5I5DE68bY6O0VLNPk= @@ -217,8 +213,8 @@ github.com/gofrs/flock v0.8.1/go.mod h1:F1TvTiK9OcQqauNUHlbJvyl9Qa1QvF/gOUDKA14j github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q= github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q= -github.com/golang-jwt/jwt/v4 v4.5.0 h1:7cYmW1XlMY7h7ii7UhUyChSgS5wUJEnm9uZVTGqOWzg= -github.com/golang-jwt/jwt/v4 v4.5.0/go.mod h1:m21LjoU+eqJr34lmDMbreY2eSTRJ1cv77w39/MY0Ch0= +github.com/golang-jwt/jwt/v4 v4.5.1 h1:JdqV9zKUdtaa9gdPlywC3aeoEsR681PlKC+4F5gQgeo= +github.com/golang-jwt/jwt/v4 v4.5.1/go.mod h1:m21LjoU+eqJr34lmDMbreY2eSTRJ1cv77w39/MY0Ch0= github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/groupcache v0.0.0-20191227052852-215e87163ea7/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= @@ -265,8 +261,8 @@ github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/ github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.8/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= -github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38= -github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= +github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= +github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/google/go-querystring v1.1.0 h1:AnCroh3fv4ZBgVIf1Iwtovgjaw/GiKJo8M8yD/fhyJ8= github.com/google/go-querystring v1.1.0/go.mod h1:Kcdr2DB4koayq7X8pmAG4sNG59So17icRSOU623lUBU= github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= @@ -310,8 +306,8 @@ github.com/holiman/billy v0.0.0-20240216141850-2abb0c79d3c4/go.mod h1:5GuXa7vkL8 github.com/holiman/bloomfilter/v2 v2.0.3 h1:73e0e/V0tCydx14a0SCYS/EWCxgwLZ18CZcZKVu0fao= github.com/holiman/bloomfilter/v2 v2.0.3/go.mod h1:zpoh+gs7qcpqrHr3dB55AMiJwo0iURXE7ZOP9L9hSkA= github.com/holiman/uint256 v1.2.0/go.mod h1:y4ga/t+u+Xwd7CpDgZESaRcWy0I7XMlTMA25ApIH5Jw= -github.com/holiman/uint256 v1.3.1 h1:JfTzmih28bittyHM8z360dCjIA9dbPIBlcTI6lmctQs= -github.com/holiman/uint256 v1.3.1/go.mod h1:EOMSn4q6Nyt9P6efbI3bueV4e1b3dGlUCXeiRV4ng7E= +github.com/holiman/uint256 v1.3.2 h1:a9EgMPSC1AAaj1SZL5zIQD3WbwTuHrMGOerLjGmM/TA= +github.com/holiman/uint256 v1.3.2/go.mod h1:EOMSn4q6Nyt9P6efbI3bueV4e1b3dGlUCXeiRV4ng7E= github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= github.com/huin/goupnp v1.3.0 h1:UvLUlWDNpoUdYzb2TCn+MuTWtcjXKSza2n6CBdQ0xXc= github.com/huin/goupnp v1.3.0/go.mod h1:gnGPsThkYa7bFi/KWmEysQRf48l2dvR5bxr2OFckNX8= @@ -367,8 +363,8 @@ github.com/kylelemons/godebug v1.1.0 h1:RPNrshWIDI6G2gRW9EHilWtl7Z6Sb1BR0xunSBf0 github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw= github.com/labstack/echo/v4 v4.2.1/go.mod h1:AA49e0DZ8kk5jTOOCKNuPR6oTnBS0dYiM4FW1e6jwpg= github.com/labstack/gommon v0.3.0/go.mod h1:MULnywXg0yavhxWKc+lOruYdAhDwPK9wf0OL7NoOu+k= -github.com/leanovate/gopter v0.2.9 h1:fQjYxZaynp97ozCzfOyOuAGOU4aU/z37zf/tOujFk7c= -github.com/leanovate/gopter v0.2.9/go.mod h1:U2L/78B+KVFIx2VmW6onHJQzXtFb+p5y3y2Sh+Jxxv8= +github.com/leanovate/gopter v0.2.11 h1:vRjThO1EKPb/1NsDXuDrzldR28RLkBflWYcU9CvzWu4= +github.com/leanovate/gopter v0.2.11/go.mod h1:aK3tzZP/C+p1m3SPRE4SYZFGP7jjkuSI4f7Xvpt0S9c= github.com/mailru/easyjson v0.0.0-20190614124828-94de47d64c63/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= github.com/mailru/easyjson v0.0.0-20190626092158-b2ccc519800e/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= github.com/matryer/moq v0.0.0-20190312154309-6cfb0558e1bd/go.mod h1:9ELz6aaclSIGnZBoaSLZ3NAl1VTufbOrXBPvtcy6WiQ= @@ -473,8 +469,9 @@ github.com/rivo/uniseg v0.2.0 h1:S1pD9weZBuJdFmowNwbpi7BJ8TNftyUImj/0WQi72jY= github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc= 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 h1:73kH8U+JUqXU8lRuOHeVHaa/SZPifC7BkcraZVejAe8= github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs= +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.7.0 h1:+88SsELBHx5r+hZ8TCkggzSstaWNbDvThkVK8H6f9ik= github.com/rs/cors v1.7.0/go.mod h1:gFx+x8UowdsKA9AchylcLynDq+nNFfI8FkUZdN/jGCU= github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk= @@ -503,8 +500,6 @@ github.com/tklauser/go-sysconf v0.3.12 h1:0QaGUFOdQaIVdPgfITYzaTegZvdCjmYO52cSFA github.com/tklauser/go-sysconf v0.3.12/go.mod h1:Ho14jnntGE1fpdOqQEEaiKRpvIavV0hSfmBq8nJbHYI= github.com/tklauser/numcpus v0.6.1 h1:ng9scYS7az0Bk4OZLvrNXNSAO2Pxr1XXRAPyjhIx+Fk= github.com/tklauser/numcpus v0.6.1/go.mod h1:1XfjsgE2zo8GVw7POkMbHENHzVg3GzmoZ9fESEdAacY= -github.com/tyler-smith/go-bip39 v1.1.0 h1:5eUemwrMargf3BSLRRCalXT93Ns6pQJIjYQN2nyfOP8= -github.com/tyler-smith/go-bip39 v1.1.0/go.mod h1:gUYDtqQw1JS3ZJ8UWVcGTGqqr6YIN3CWg+kkNaLt55U= github.com/urfave/cli/v2 v2.25.7 h1:VAzn5oq403l5pHjc4OhD54+XGO9cdKVL/7lDjF+iKUs= github.com/urfave/cli/v2 v2.25.7/go.mod h1:8qnjx1vcq5s2/wpsqoZFndg2CE5tNFyrTvS6SinrnYQ= github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc= @@ -533,8 +528,8 @@ golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPh golang.org/x/crypto v0.0.0-20200820211705-5c72a883971a/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20201221181555-eec23a3978ad/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I= golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= -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.31.0 h1:ihbySMvVjLAeSH1IbfcRTkD/iNscyz8rGzjF/E5hV6U= +golang.org/x/crypto v0.31.0/go.mod h1:kDsLvtWBEx7MV9tJOj9bnXsPbxwJQ6csT/x4KIN4Ssk= 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= @@ -606,8 +601,8 @@ golang.org/x/net v0.0.0-20210119194325-5f4716e94777/go.mod h1:m0MpNAwzfU5UDzcl9v golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/net v0.0.0-20210525063256-abc453219eb5/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-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= @@ -626,8 +621,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-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= @@ -689,8 +684,8 @@ golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.11.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= @@ -703,8 +698,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.21.0 h1:zyQAAkrwaneQ066sspRyJaG9VNi/YJ1NfzcGB3hZ/qo= +golang.org/x/text v0.21.0/go.mod h1:4IBbMaMmOPCJ8SecivzSH54+73PCFmPWxNTLm+vZkEQ= 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= golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= @@ -755,8 +750,8 @@ golang.org/x/tools v0.0.0-20200804011535-6c149bb5ef0d/go.mod h1:njjCfa9FT2d7l9Bc golang.org/x/tools v0.0.0-20200825202427-b303f430e36d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= 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/graphql/graphql.go b/graphql/graphql.go index 90d70bf142..7af1adbb4a 100644 --- a/graphql/graphql.go +++ b/graphql/graphql.go @@ -30,7 +30,6 @@ import ( "github.com/ethereum/go-ethereum" "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common/hexutil" - "github.com/ethereum/go-ethereum/common/math" "github.com/ethereum/go-ethereum/consensus/misc/eip1559" "github.com/ethereum/go-ethereum/core/state" "github.com/ethereum/go-ethereum/core/types" @@ -277,7 +276,11 @@ func (t *Transaction) GasPrice(ctx context.Context) hexutil.Big { if block != nil { if baseFee, _ := block.BaseFeePerGas(ctx); baseFee != nil { // price = min(gasTipCap + baseFee, gasFeeCap) - return (hexutil.Big)(*math.BigMin(new(big.Int).Add(tx.GasTipCap(), baseFee.ToInt()), tx.GasFeeCap())) + gasFeeCap, effectivePrice := tx.GasFeeCap(), new(big.Int).Add(tx.GasTipCap(), baseFee.ToInt()) + if effectivePrice.Cmp(gasFeeCap) < 0 { + return (hexutil.Big)(*effectivePrice) + } + return (hexutil.Big)(*gasFeeCap) } } return hexutil.Big(*tx.GasPrice()) @@ -302,7 +305,11 @@ func (t *Transaction) EffectiveGasPrice(ctx context.Context) (*hexutil.Big, erro if header.BaseFee == nil { return (*hexutil.Big)(tx.GasPrice()), nil } - return (*hexutil.Big)(math.BigMin(new(big.Int).Add(tx.GasTipCap(), header.BaseFee), tx.GasFeeCap())), nil + gasFeeCap, effectivePrice := tx.GasFeeCap(), new(big.Int).Add(tx.GasTipCap(), header.BaseFee) + if effectivePrice.Cmp(gasFeeCap) < 0 { + return (*hexutil.Big)(effectivePrice), nil + } + return (*hexutil.Big)(gasFeeCap), nil } func (t *Transaction) MaxFeePerGas(ctx context.Context) *hexutil.Big { @@ -311,7 +318,7 @@ func (t *Transaction) MaxFeePerGas(ctx context.Context) *hexutil.Big { return nil } switch tx.Type() { - case types.DynamicFeeTxType, types.BlobTxType: + case types.DynamicFeeTxType, types.BlobTxType, types.SetCodeTxType: return (*hexutil.Big)(tx.GasFeeCap()) default: return nil @@ -324,7 +331,7 @@ func (t *Transaction) MaxPriorityFeePerGas(ctx context.Context) *hexutil.Big { return nil } switch tx.Type() { - case types.DynamicFeeTxType, types.BlobTxType: + case types.DynamicFeeTxType, types.BlobTxType, types.SetCodeTxType: return (*hexutil.Big)(tx.GasTipCap()) default: return nil @@ -1201,7 +1208,7 @@ func (b *Block) Call(ctx context.Context, args struct { func (b *Block) EstimateGas(ctx context.Context, args struct { Data ethapi.TransactionArgs }) (hexutil.Uint64, error) { - return ethapi.DoEstimateGas(ctx, b.r.backend, args.Data, *b.numberOrHash, nil, b.r.backend.RPCGasCap()) + return ethapi.DoEstimateGas(ctx, b.r.backend, args.Data, *b.numberOrHash, nil, nil, b.r.backend.RPCGasCap()) } type Pending struct { @@ -1265,7 +1272,7 @@ func (p *Pending) EstimateGas(ctx context.Context, args struct { Data ethapi.TransactionArgs }) (hexutil.Uint64, error) { latestBlockNr := rpc.BlockNumberOrHashWithNumber(rpc.LatestBlockNumber) - return ethapi.DoEstimateGas(ctx, p.r.backend, args.Data, latestBlockNr, nil, p.r.backend.RPCGasCap()) + return ethapi.DoEstimateGas(ctx, p.r.backend, args.Data, latestBlockNr, nil, nil, p.r.backend.RPCGasCap()) } // Resolver is the top-level object in the GraphQL hierarchy. diff --git a/graphql/graphql_test.go b/graphql/graphql_test.go index a6be589a0f..231170c273 100644 --- a/graphql/graphql_test.go +++ b/graphql/graphql_test.go @@ -21,6 +21,7 @@ import ( "encoding/json" "fmt" "io" + "math" "math/big" "net/http" "strings" @@ -459,13 +460,14 @@ func newGQLService(t *testing.T, stack *node.Node, shanghai bool, gspec *core.Ge var engine consensus.Engine = ethash.NewFaker() if shanghai { engine = beacon.NewFaker() - chainCfg := gspec.Config - chainCfg.TerminalTotalDifficultyPassed = true - chainCfg.TerminalTotalDifficulty = common.Big0 + gspec.Config.TerminalTotalDifficulty = common.Big0 // GenerateChain will increment timestamps by 10. // Shanghai upgrade at block 1. shanghaiTime := uint64(5) - chainCfg.ShanghaiTime = &shanghaiTime + gspec.Config.ShanghaiTime = &shanghaiTime + } else { + // set an arbitrary large ttd as chains are required to be known to be merged + gspec.Config.TerminalTotalDifficulty = big.NewInt(math.MaxInt64) } ethBackend, err := eth.New(stack, ethConf) if err != nil { diff --git a/internal/build/file.go b/internal/build/file.go new file mode 100644 index 0000000000..2d8c993f36 --- /dev/null +++ b/internal/build/file.go @@ -0,0 +1,102 @@ +// 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 build + +import ( + "crypto/sha256" + "io" + "os" + "path/filepath" + "sort" + "strings" +) + +// FileExist checks if a file exists at path. +func FileExist(path string) bool { + _, err := os.Stat(path) + if err != nil && os.IsNotExist(err) { + return false + } + return true +} + +// HashFiles iterates the provided set of files, computing the hash of each. +func HashFiles(files []string) (map[string][32]byte, error) { + res := make(map[string][32]byte) + for _, filePath := range files { + f, err := os.OpenFile(filePath, os.O_RDONLY, 0666) + if err != nil { + return nil, err + } + hasher := sha256.New() + if _, err := io.Copy(hasher, f); err != nil { + return nil, err + } + res[filePath] = [32]byte(hasher.Sum(nil)) + } + return res, nil +} + +// HashFolder iterates all files under the given directory, computing the hash +// of each. +func HashFolder(folder string, exlude []string) (map[string][32]byte, error) { + res := make(map[string][32]byte) + err := filepath.WalkDir(folder, func(path string, d os.DirEntry, _ error) error { + // Skip anything that's exluded or not a regular file + for _, skip := range exlude { + if strings.HasPrefix(path, filepath.FromSlash(skip)) { + return filepath.SkipDir + } + } + if !d.Type().IsRegular() { + return nil + } + // Regular file found, hash it + f, err := os.OpenFile(path, os.O_RDONLY, 0666) + if err != nil { + return err + } + hasher := sha256.New() + if _, err := io.Copy(hasher, f); err != nil { + return err + } + res[path] = [32]byte(hasher.Sum(nil)) + return nil + }) + if err != nil { + return nil, err + } + return res, nil +} + +// DiffHashes compares two maps of file hashes and returns the changed files. +func DiffHashes(a map[string][32]byte, b map[string][32]byte) []string { + var updates []string + + for file := range a { + if _, ok := b[file]; !ok || a[file] != b[file] { + updates = append(updates, file) + } + } + for file := range b { + if _, ok := a[file]; !ok { + updates = append(updates, file) + } + } + sort.Strings(updates) + return updates +} 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 d66e32b8c2..d9cec560ea 100644 --- a/internal/ethapi/api.go +++ b/internal/ethapi/api.go @@ -21,15 +21,13 @@ import ( "encoding/hex" "errors" "fmt" - "maps" + gomath "math" "math/big" "strings" "time" "github.com/davecgh/go-spew/spew" "github.com/ethereum/go-ethereum/accounts" - "github.com/ethereum/go-ethereum/accounts/keystore" - "github.com/ethereum/go-ethereum/accounts/scwallet" "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common/hexutil" "github.com/ethereum/go-ethereum/common/math" @@ -37,20 +35,18 @@ import ( "github.com/ethereum/go-ethereum/consensus/misc/eip1559" "github.com/ethereum/go-ethereum/core" "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" "github.com/ethereum/go-ethereum/eth/gasestimator" "github.com/ethereum/go-ethereum/eth/tracers/logger" + "github.com/ethereum/go-ethereum/internal/ethapi/override" "github.com/ethereum/go-ethereum/log" "github.com/ethereum/go-ethereum/p2p" "github.com/ethereum/go-ethereum/params" "github.com/ethereum/go-ethereum/rlp" "github.com/ethereum/go-ethereum/rpc" "github.com/ethereum/go-ethereum/trie" - "github.com/holiman/uint256" - "github.com/tyler-smith/go-bip39" ) // estimateGasErrorRatio is the amount of overestimation eth_estimateGas is @@ -256,7 +252,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()) } @@ -297,344 +293,6 @@ func (api *EthereumAccountAPI) Accounts() []common.Address { return api.am.Accounts() } -// PersonalAccountAPI provides an API to access accounts managed by this node. -// It offers methods to create, (un)lock en list accounts. Some methods accept -// passwords and are therefore considered private by default. -type PersonalAccountAPI struct { - am *accounts.Manager - nonceLock *AddrLocker - b Backend -} - -// NewPersonalAccountAPI creates a new PersonalAccountAPI. -func NewPersonalAccountAPI(b Backend, nonceLock *AddrLocker) *PersonalAccountAPI { - return &PersonalAccountAPI{ - am: b.AccountManager(), - nonceLock: nonceLock, - b: b, - } -} - -// ListAccounts will return a list of addresses for accounts this node manages. -func (api *PersonalAccountAPI) ListAccounts() []common.Address { - return api.am.Accounts() -} - -// rawWallet is a JSON representation of an accounts.Wallet interface, with its -// data contents extracted into plain fields. -type rawWallet struct { - URL string `json:"url"` - Status string `json:"status"` - Failure string `json:"failure,omitempty"` - Accounts []accounts.Account `json:"accounts,omitempty"` -} - -// ListWallets will return a list of wallets this node manages. -func (api *PersonalAccountAPI) ListWallets() []rawWallet { - wallets := make([]rawWallet, 0) // return [] instead of nil if empty - for _, wallet := range api.am.Wallets() { - status, failure := wallet.Status() - - raw := rawWallet{ - URL: wallet.URL().String(), - Status: status, - Accounts: wallet.Accounts(), - } - if failure != nil { - raw.Failure = failure.Error() - } - wallets = append(wallets, raw) - } - return wallets -} - -// OpenWallet initiates a hardware wallet opening procedure, establishing a USB -// connection and attempting to authenticate via the provided passphrase. Note, -// the method may return an extra challenge requiring a second open (e.g. the -// Trezor PIN matrix challenge). -func (api *PersonalAccountAPI) OpenWallet(url string, passphrase *string) error { - wallet, err := api.am.Wallet(url) - if err != nil { - return err - } - pass := "" - if passphrase != nil { - pass = *passphrase - } - return wallet.Open(pass) -} - -// DeriveAccount requests an HD wallet to derive a new account, optionally pinning -// it for later reuse. -func (api *PersonalAccountAPI) DeriveAccount(url string, path string, pin *bool) (accounts.Account, error) { - wallet, err := api.am.Wallet(url) - if err != nil { - return accounts.Account{}, err - } - derivPath, err := accounts.ParseDerivationPath(path) - if err != nil { - return accounts.Account{}, err - } - if pin == nil { - pin = new(bool) - } - return wallet.Derive(derivPath, *pin) -} - -// NewAccount will create a new account and returns the address for the new account. -func (api *PersonalAccountAPI) NewAccount(password string) (common.AddressEIP55, error) { - ks, err := fetchKeystore(api.am) - if err != nil { - return common.AddressEIP55{}, err - } - acc, err := ks.NewAccount(password) - if err == nil { - addrEIP55 := common.AddressEIP55(acc.Address) - log.Info("Your new key was generated", "address", addrEIP55.String()) - log.Warn("Please backup your key file!", "path", acc.URL.Path) - log.Warn("Please remember your password!") - return addrEIP55, nil - } - return common.AddressEIP55{}, err -} - -// fetchKeystore retrieves the encrypted keystore from the account manager. -func fetchKeystore(am *accounts.Manager) (*keystore.KeyStore, error) { - if ks := am.Backends(keystore.KeyStoreType); len(ks) > 0 { - return ks[0].(*keystore.KeyStore), nil - } - return nil, errors.New("local keystore not used") -} - -// ImportRawKey stores the given hex encoded ECDSA key into the key directory, -// encrypting it with the passphrase. -func (api *PersonalAccountAPI) ImportRawKey(privkey string, password string) (common.Address, error) { - key, err := crypto.HexToECDSA(privkey) - if err != nil { - return common.Address{}, err - } - ks, err := fetchKeystore(api.am) - if err != nil { - return common.Address{}, err - } - acc, err := ks.ImportECDSA(key, password) - return acc.Address, err -} - -// UnlockAccount will unlock the account associated with the given address with -// the given password for duration seconds. If duration is nil it will use a -// default of 300 seconds. It returns an indication if the account was unlocked. -func (api *PersonalAccountAPI) UnlockAccount(ctx context.Context, addr common.Address, password string, duration *uint64) (bool, error) { - // When the API is exposed by external RPC(http, ws etc), unless the user - // explicitly specifies to allow the insecure account unlocking, otherwise - // it is disabled. - if api.b.ExtRPCEnabled() && !api.b.AccountManager().Config().InsecureUnlockAllowed { - return false, errors.New("account unlock with HTTP access is forbidden") - } - - const max = uint64(time.Duration(math.MaxInt64) / time.Second) - var d time.Duration - if duration == nil { - d = 300 * time.Second - } else if *duration > max { - return false, errors.New("unlock duration too large") - } else { - d = time.Duration(*duration) * time.Second - } - ks, err := fetchKeystore(api.am) - if err != nil { - return false, err - } - err = ks.TimedUnlock(accounts.Account{Address: addr}, password, d) - if err != nil { - log.Warn("Failed account unlock attempt", "address", addr, "err", err) - } - return err == nil, err -} - -// LockAccount will lock the account associated with the given address when it's unlocked. -func (api *PersonalAccountAPI) LockAccount(addr common.Address) bool { - if ks, err := fetchKeystore(api.am); err == nil { - return ks.Lock(addr) == nil - } - return false -} - -// signTransaction sets defaults and signs the given transaction -// NOTE: the caller needs to ensure that the nonceLock is held, if applicable, -// and release it after the transaction has been submitted to the tx pool -func (api *PersonalAccountAPI) signTransaction(ctx context.Context, args *TransactionArgs, passwd string) (*types.Transaction, error) { - // Look up the wallet containing the requested signer - account := accounts.Account{Address: args.from()} - wallet, err := api.am.Find(account) - if err != nil { - return nil, err - } - // Set some sanity defaults and terminate on failure - if err := args.setDefaults(ctx, api.b, false); err != nil { - return nil, err - } - // Assemble the transaction and sign with the wallet - tx := args.ToTransaction(types.LegacyTxType) - - return wallet.SignTxWithPassphrase(account, passwd, tx, api.b.ChainConfig().ChainID) -} - -// SendTransaction will create a transaction from the given arguments and -// tries to sign it with the key associated with args.From. If the given -// passwd isn't able to decrypt the key it fails. -func (api *PersonalAccountAPI) SendTransaction(ctx context.Context, args TransactionArgs, passwd string) (common.Hash, error) { - if args.Nonce == nil { - // Hold the mutex around signing to prevent concurrent assignment of - // the same nonce to multiple accounts. - api.nonceLock.LockAddr(args.from()) - defer api.nonceLock.UnlockAddr(args.from()) - } - if args.IsEIP4844() { - return common.Hash{}, errBlobTxNotSupported - } - signed, err := api.signTransaction(ctx, &args, passwd) - if err != nil { - log.Warn("Failed transaction send attempt", "from", args.from(), "to", args.To, "value", args.Value.ToInt(), "err", err) - return common.Hash{}, err - } - return SubmitTransaction(ctx, api.b, signed) -} - -// SignTransaction will create a transaction from the given arguments and -// tries to sign it with the key associated with args.From. If the given passwd isn't -// able to decrypt the key it fails. The transaction is returned in RLP-form, not broadcast -// to other nodes -func (api *PersonalAccountAPI) SignTransaction(ctx context.Context, args TransactionArgs, passwd string) (*SignTransactionResult, error) { - // No need to obtain the noncelock mutex, since we won't be sending this - // tx into the transaction pool, but right back to the user - if args.From == nil { - return nil, errors.New("sender not specified") - } - if args.Gas == nil { - return nil, errors.New("gas not specified") - } - if args.GasPrice == nil && (args.MaxFeePerGas == nil || args.MaxPriorityFeePerGas == nil) { - return nil, errors.New("missing gasPrice or maxFeePerGas/maxPriorityFeePerGas") - } - if args.IsEIP4844() { - return nil, errBlobTxNotSupported - } - if args.Nonce == nil { - return nil, errors.New("nonce not specified") - } - // Before actually signing the transaction, ensure the transaction fee is reasonable. - tx := args.ToTransaction(types.LegacyTxType) - if err := checkTxFee(tx.GasPrice(), tx.Gas(), api.b.RPCTxFeeCap()); err != nil { - return nil, err - } - signed, err := api.signTransaction(ctx, &args, passwd) - if err != nil { - log.Warn("Failed transaction sign attempt", "from", args.from(), "to", args.To, "value", args.Value.ToInt(), "err", err) - return nil, err - } - data, err := signed.MarshalBinary() - if err != nil { - return nil, err - } - return &SignTransactionResult{data, signed}, nil -} - -// Sign calculates an Ethereum ECDSA signature for: -// keccak256("\x19Ethereum Signed Message:\n" + len(message) + message)) -// -// Note, the produced signature conforms to the secp256k1 curve R, S and V values, -// where the V value will be 27 or 28 for legacy reasons. -// -// The key used to calculate the signature is decrypted with the given password. -// -// https://geth.ethereum.org/docs/interacting-with-geth/rpc/ns-personal#personal-sign -func (api *PersonalAccountAPI) Sign(ctx context.Context, data hexutil.Bytes, addr common.Address, passwd string) (hexutil.Bytes, error) { - // Look up the wallet containing the requested signer - account := accounts.Account{Address: addr} - - wallet, err := api.b.AccountManager().Find(account) - if err != nil { - return nil, err - } - // Assemble sign the data with the wallet - signature, err := wallet.SignTextWithPassphrase(account, passwd, data) - if err != nil { - log.Warn("Failed data sign attempt", "address", addr, "err", err) - return nil, err - } - signature[crypto.RecoveryIDOffset] += 27 // Transform V from 0/1 to 27/28 according to the yellow paper - return signature, nil -} - -// EcRecover returns the address for the account that was used to create the signature. -// Note, this function is compatible with eth_sign and personal_sign. As such it recovers -// the address of: -// hash = keccak256("\x19Ethereum Signed Message:\n"${message length}${message}) -// addr = ecrecover(hash, signature) -// -// Note, the signature must conform to the secp256k1 curve R, S and V values, where -// the V value must be 27 or 28 for legacy reasons. -// -// https://geth.ethereum.org/docs/interacting-with-geth/rpc/ns-personal#personal-ecrecover -func (api *PersonalAccountAPI) EcRecover(ctx context.Context, data, sig hexutil.Bytes) (common.Address, error) { - if len(sig) != crypto.SignatureLength { - return common.Address{}, fmt.Errorf("signature must be %d bytes long", crypto.SignatureLength) - } - if sig[crypto.RecoveryIDOffset] != 27 && sig[crypto.RecoveryIDOffset] != 28 { - return common.Address{}, errors.New("invalid Ethereum signature (V is not 27 or 28)") - } - sig[crypto.RecoveryIDOffset] -= 27 // Transform yellow paper V from 27/28 to 0/1 - - rpk, err := crypto.SigToPub(accounts.TextHash(data), sig) - if err != nil { - return common.Address{}, err - } - return crypto.PubkeyToAddress(*rpk), nil -} - -// InitializeWallet initializes a new wallet at the provided URL, by generating and returning a new private key. -func (api *PersonalAccountAPI) InitializeWallet(ctx context.Context, url string) (string, error) { - wallet, err := api.am.Wallet(url) - if err != nil { - return "", err - } - - entropy, err := bip39.NewEntropy(256) - if err != nil { - return "", err - } - - mnemonic, err := bip39.NewMnemonic(entropy) - if err != nil { - return "", err - } - - seed := bip39.NewSeed(mnemonic, "") - - switch wallet := wallet.(type) { - case *scwallet.Wallet: - return mnemonic, wallet.Initialize(seed) - default: - return "", errors.New("specified wallet does not support initialization") - } -} - -// Unpair deletes a pairing between wallet and geth. -func (api *PersonalAccountAPI) Unpair(ctx context.Context, url string, pin string) error { - wallet, err := api.am.Wallet(url) - if err != nil { - return err - } - - switch wallet := wallet.(type) { - case *scwallet.Wallet: - return wallet.Unpair([]byte(pin)) - default: - return errors.New("specified wallet does not support pairing") - } -} - // BlockChainAPI provides an API to access Ethereum blockchain data. type BlockChainAPI struct { b Backend @@ -962,171 +620,6 @@ func (api *BlockChainAPI) GetBlockReceipts(ctx context.Context, blockNrOrHash rp return result, nil } -// OverrideAccount indicates the overriding fields of account during the execution -// of a message call. -// Note, state and stateDiff can't be specified at the same time. If state is -// set, message execution will only use the data in the given state. Otherwise -// if stateDiff is set, all diff will be applied first and then execute the call -// message. -type OverrideAccount struct { - Nonce *hexutil.Uint64 `json:"nonce"` - Code *hexutil.Bytes `json:"code"` - Balance *hexutil.Big `json:"balance"` - State map[common.Hash]common.Hash `json:"state"` - StateDiff map[common.Hash]common.Hash `json:"stateDiff"` - MovePrecompileTo *common.Address `json:"movePrecompileToAddress"` -} - -// StateOverride is the collection of overridden accounts. -type StateOverride map[common.Address]OverrideAccount - -func (diff *StateOverride) has(address common.Address) bool { - _, ok := (*diff)[address] - return ok -} - -// Apply overrides the fields of specified accounts into the given state. -func (diff *StateOverride) Apply(statedb *state.StateDB, precompiles vm.PrecompiledContracts) error { - if diff == nil { - return nil - } - // Tracks destinations of precompiles that were moved. - dirtyAddrs := make(map[common.Address]struct{}) - for addr, account := range *diff { - // If a precompile was moved to this address already, it can't be overridden. - if _, ok := dirtyAddrs[addr]; ok { - return fmt.Errorf("account %s has already been overridden by a precompile", addr.Hex()) - } - p, isPrecompile := precompiles[addr] - // The MoveTo feature makes it possible to move a precompile - // code to another address. If the target address is another precompile - // the code for the latter is lost for this session. - // Note the destination account is not cleared upon move. - if account.MovePrecompileTo != nil { - if !isPrecompile { - return fmt.Errorf("account %s is not a precompile", addr.Hex()) - } - // Refuse to move a precompile to an address that has been - // or will be overridden. - if diff.has(*account.MovePrecompileTo) { - return fmt.Errorf("account %s is already overridden", account.MovePrecompileTo.Hex()) - } - precompiles[*account.MovePrecompileTo] = p - dirtyAddrs[*account.MovePrecompileTo] = struct{}{} - } - if isPrecompile { - delete(precompiles, addr) - } - // Override account nonce. - if account.Nonce != nil { - statedb.SetNonce(addr, uint64(*account.Nonce)) - } - // Override account(contract) code. - if account.Code != nil { - statedb.SetCode(addr, *account.Code) - } - // Override account balance. - if account.Balance != nil { - u256Balance, _ := uint256.FromBig((*big.Int)(account.Balance)) - statedb.SetBalance(addr, u256Balance, tracing.BalanceChangeUnspecified) - } - if account.State != nil && account.StateDiff != nil { - return fmt.Errorf("account %s has both 'state' and 'stateDiff'", addr.Hex()) - } - // Replace entire state if caller requires. - if account.State != nil { - statedb.SetStorage(addr, account.State) - } - // Apply state diff into specified accounts. - if account.StateDiff != nil { - for key, value := range account.StateDiff { - statedb.SetState(addr, key, value) - } - } - } - // Now finalize the changes. Finalize is normally performed between transactions. - // By using finalize, the overrides are semantically behaving as - // if they were created in a transaction just before the tracing occur. - statedb.Finalise(false) - return nil -} - -// BlockOverrides is a set of header fields to override. -type BlockOverrides struct { - Number *hexutil.Big - Difficulty *hexutil.Big // No-op if we're simulating post-merge calls. - Time *hexutil.Uint64 - GasLimit *hexutil.Uint64 - FeeRecipient *common.Address - PrevRandao *common.Hash - BaseFeePerGas *hexutil.Big - BlobBaseFee *hexutil.Big -} - -// Apply overrides the given header fields into the given block context. -func (o *BlockOverrides) Apply(blockCtx *vm.BlockContext) { - if o == nil { - return - } - if o.Number != nil { - blockCtx.BlockNumber = o.Number.ToInt() - } - if o.Difficulty != nil { - blockCtx.Difficulty = o.Difficulty.ToInt() - } - if o.Time != nil { - blockCtx.Time = uint64(*o.Time) - } - if o.GasLimit != nil { - blockCtx.GasLimit = uint64(*o.GasLimit) - } - if o.FeeRecipient != nil { - blockCtx.Coinbase = *o.FeeRecipient - } - if o.PrevRandao != nil { - blockCtx.Random = o.PrevRandao - } - if o.BaseFeePerGas != nil { - blockCtx.BaseFee = o.BaseFeePerGas.ToInt() - } - if o.BlobBaseFee != nil { - blockCtx.BlobBaseFee = o.BlobBaseFee.ToInt() - } -} - -// MakeHeader returns a new header object with the overridden -// fields. -// Note: MakeHeader ignores BlobBaseFee if set. That's because -// header has no such field. -func (o *BlockOverrides) MakeHeader(header *types.Header) *types.Header { - if o == nil { - return header - } - h := types.CopyHeader(header) - if o.Number != nil { - h.Number = o.Number.ToInt() - } - if o.Difficulty != nil { - h.Difficulty = o.Difficulty.ToInt() - } - if o.Time != nil { - h.Time = uint64(*o.Time) - } - if o.GasLimit != nil { - h.GasLimit = uint64(*o.GasLimit) - } - if o.FeeRecipient != nil { - h.Coinbase = *o.FeeRecipient - } - if o.PrevRandao != nil { - h.MixDigest = *o.PrevRandao - } - if o.BaseFeePerGas != nil { - h.BaseFee = o.BaseFeePerGas.ToInt() - } - return h -} - // ChainContextBackend provides methods required to implement ChainContext. type ChainContextBackend interface { Engine() consensus.Engine @@ -1159,13 +652,13 @@ func (context *ChainContext) GetHeader(hash common.Hash, number uint64) *types.H return header } -func doCall(ctx context.Context, b Backend, args TransactionArgs, state *state.StateDB, header *types.Header, overrides *StateOverride, blockOverrides *BlockOverrides, timeout time.Duration, globalGasCap uint64) (*core.ExecutionResult, error) { +func doCall(ctx context.Context, b Backend, args TransactionArgs, state *state.StateDB, header *types.Header, overrides *override.StateOverride, blockOverrides *override.BlockOverrides, timeout time.Duration, globalGasCap uint64) (*core.ExecutionResult, error) { blockCtx := core.NewEVMBlockContext(header, NewChainContext(ctx, b), nil) if blockOverrides != nil { 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 } @@ -1183,7 +676,7 @@ func doCall(ctx context.Context, b Backend, args TransactionArgs, state *state.S defer cancel() gp := new(core.GasPool) if globalGasCap == 0 { - gp.AddGas(math.MaxUint64) + gp.AddGas(gomath.MaxUint64) } else { gp.AddGas(globalGasCap) } @@ -1204,15 +697,20 @@ 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) } - - return applyMessageWithEVM(ctx, evm, msg, state, timeout, gp) + res, err := applyMessageWithEVM(ctx, evm, msg, timeout, gp) + // If an internal state error occurred, let that have precedence. Otherwise, + // a "trie root missing" type of error will masquerade as e.g. "insufficient gas" + if err := state.Error(); err != nil { + return nil, err + } + return res, err } -func applyMessageWithEVM(ctx context.Context, evm *vm.EVM, msg *core.Message, state *state.StateDB, timeout time.Duration, gp *core.GasPool) (*core.ExecutionResult, error) { +func applyMessageWithEVM(ctx context.Context, evm *vm.EVM, msg *core.Message, timeout time.Duration, gp *core.GasPool) (*core.ExecutionResult, error) { // Wait for the context to be done and cancel the evm. Even if the // EVM has finished, cancelling may be done (repeatedly) go func() { @@ -1222,9 +720,6 @@ func applyMessageWithEVM(ctx context.Context, evm *vm.EVM, msg *core.Message, st // Execute the message. result, err := core.ApplyMessage(evm, msg, gp) - if err := state.Error(); err != nil { - return nil, err - } // If the timer caused an abort, return an appropriate error message if evm.Cancelled() { @@ -1236,7 +731,7 @@ func applyMessageWithEVM(ctx context.Context, evm *vm.EVM, msg *core.Message, st return result, nil } -func DoCall(ctx context.Context, b Backend, args TransactionArgs, blockNrOrHash rpc.BlockNumberOrHash, overrides *StateOverride, blockOverrides *BlockOverrides, timeout time.Duration, globalGasCap uint64) (*core.ExecutionResult, error) { +func DoCall(ctx context.Context, b Backend, args TransactionArgs, blockNrOrHash rpc.BlockNumberOrHash, overrides *override.StateOverride, blockOverrides *override.BlockOverrides, timeout time.Duration, globalGasCap uint64) (*core.ExecutionResult, error) { defer func(start time.Time) { log.Debug("Executing EVM call finished", "runtime", time.Since(start)) }(time.Now()) state, header, err := b.StateAndHeaderByNumberOrHash(ctx, blockNrOrHash) @@ -1252,7 +747,7 @@ func DoCall(ctx context.Context, b Backend, args TransactionArgs, blockNrOrHash // // Note, this function doesn't make and changes in the state/blockchain and is // useful to execute and retrieve values. -func (api *BlockChainAPI) Call(ctx context.Context, args TransactionArgs, blockNrOrHash *rpc.BlockNumberOrHash, overrides *StateOverride, blockOverrides *BlockOverrides) (hexutil.Bytes, error) { +func (api *BlockChainAPI) Call(ctx context.Context, args TransactionArgs, blockNrOrHash *rpc.BlockNumberOrHash, overrides *override.StateOverride, blockOverrides *override.BlockOverrides) (hexutil.Bytes, error) { if blockNrOrHash == nil { latest := rpc.BlockNumberOrHashWithNumber(rpc.LatestBlockNumber) blockNrOrHash = &latest @@ -1291,7 +786,7 @@ func (api *BlockChainAPI) SimulateV1(ctx context.Context, opts simOpts, blockNrO } gasCap := api.b.RPCGasCap() if gasCap == 0 { - gasCap = math.MaxUint64 + gasCap = gomath.MaxUint64 } sim := &simulator{ b: api.b, @@ -1311,7 +806,7 @@ func (api *BlockChainAPI) SimulateV1(ctx context.Context, opts simOpts, blockNrO // successfully at block `blockNrOrHash`. It returns error if the transaction would revert, or if // there are unexpected failures. The gas limit is capped by both `args.Gas` (if non-nil & // non-zero) and `gasCap` (if non-zero). -func DoEstimateGas(ctx context.Context, b Backend, args TransactionArgs, blockNrOrHash rpc.BlockNumberOrHash, overrides *StateOverride, gasCap uint64) (hexutil.Uint64, error) { +func DoEstimateGas(ctx context.Context, b Backend, args TransactionArgs, blockNrOrHash rpc.BlockNumberOrHash, overrides *override.StateOverride, blockOverrides *override.BlockOverrides, gasCap uint64) (hexutil.Uint64, error) { // Retrieve the base state and mutate it with any overrides state, header, err := b.StateAndHeaderByNumberOrHash(ctx, blockNrOrHash) if state == nil || err != nil { @@ -1322,11 +817,12 @@ func DoEstimateGas(ctx context.Context, b Backend, args TransactionArgs, blockNr } // Construct the gas estimator option from the user input opts := &gasestimator.Options{ - Config: b.ChainConfig(), - Chain: NewChainContext(ctx, b), - Header: header, - State: state, - ErrorRatio: estimateGasErrorRatio, + Config: b.ChainConfig(), + Chain: NewChainContext(ctx, b), + Header: header, + BlockOverrides: blockOverrides, + State: state, + ErrorRatio: estimateGasErrorRatio, } // Set any required transaction default, but make sure the gas cap itself is not messed with // if it was not specified in the original argument list. @@ -1355,12 +851,12 @@ func DoEstimateGas(ctx context.Context, b Backend, args TransactionArgs, blockNr // value is capped by both `args.Gas` (if non-nil & non-zero) and the backend's RPCGasCap // configuration (if non-zero). // Note: Required blob gas is not computed in this method. -func (api *BlockChainAPI) EstimateGas(ctx context.Context, args TransactionArgs, blockNrOrHash *rpc.BlockNumberOrHash, overrides *StateOverride) (hexutil.Uint64, error) { +func (api *BlockChainAPI) EstimateGas(ctx context.Context, args TransactionArgs, blockNrOrHash *rpc.BlockNumberOrHash, overrides *override.StateOverride, blockOverrides *override.BlockOverrides) (hexutil.Uint64, error) { bNrOrHash := rpc.BlockNumberOrHashWithNumber(rpc.LatestBlockNumber) if blockNrOrHash != nil { bNrOrHash = *blockNrOrHash } - return DoEstimateGas(ctx, api.b, args, bNrOrHash, overrides, api.b.RPCGasCap()) + return DoEstimateGas(ctx, api.b, args, bNrOrHash, overrides, blockOverrides, api.b.RPCGasCap()) } // RPCMarshalHeader converts the given header to the RPC output . @@ -1399,7 +895,7 @@ func RPCMarshalHeader(head *types.Header) map[string]interface{} { result["parentBeaconBlockRoot"] = head.ParentBeaconRoot } if head.RequestsHash != nil { - result["requestsRoot"] = head.RequestsHash + result["requestsHash"] = head.RequestsHash } return result } @@ -1441,28 +937,29 @@ func RPCMarshalBlock(block *types.Block, inclTx bool, fullTx bool, config *param // RPCTransaction represents a transaction that will serialize to the RPC representation of a transaction type RPCTransaction struct { - BlockHash *common.Hash `json:"blockHash"` - BlockNumber *hexutil.Big `json:"blockNumber"` - From common.Address `json:"from"` - Gas hexutil.Uint64 `json:"gas"` - GasPrice *hexutil.Big `json:"gasPrice"` - GasFeeCap *hexutil.Big `json:"maxFeePerGas,omitempty"` - GasTipCap *hexutil.Big `json:"maxPriorityFeePerGas,omitempty"` - MaxFeePerBlobGas *hexutil.Big `json:"maxFeePerBlobGas,omitempty"` - Hash common.Hash `json:"hash"` - Input hexutil.Bytes `json:"input"` - Nonce hexutil.Uint64 `json:"nonce"` - To *common.Address `json:"to"` - TransactionIndex *hexutil.Uint64 `json:"transactionIndex"` - Value *hexutil.Big `json:"value"` - Type hexutil.Uint64 `json:"type"` - Accesses *types.AccessList `json:"accessList,omitempty"` - ChainID *hexutil.Big `json:"chainId,omitempty"` - BlobVersionedHashes []common.Hash `json:"blobVersionedHashes,omitempty"` - V *hexutil.Big `json:"v"` - R *hexutil.Big `json:"r"` - S *hexutil.Big `json:"s"` - YParity *hexutil.Uint64 `json:"yParity,omitempty"` + BlockHash *common.Hash `json:"blockHash"` + BlockNumber *hexutil.Big `json:"blockNumber"` + From common.Address `json:"from"` + Gas hexutil.Uint64 `json:"gas"` + GasPrice *hexutil.Big `json:"gasPrice"` + GasFeeCap *hexutil.Big `json:"maxFeePerGas,omitempty"` + GasTipCap *hexutil.Big `json:"maxPriorityFeePerGas,omitempty"` + MaxFeePerBlobGas *hexutil.Big `json:"maxFeePerBlobGas,omitempty"` + Hash common.Hash `json:"hash"` + Input hexutil.Bytes `json:"input"` + Nonce hexutil.Uint64 `json:"nonce"` + To *common.Address `json:"to"` + TransactionIndex *hexutil.Uint64 `json:"transactionIndex"` + Value *hexutil.Big `json:"value"` + Type hexutil.Uint64 `json:"type"` + Accesses *types.AccessList `json:"accessList,omitempty"` + ChainID *hexutil.Big `json:"chainId,omitempty"` + BlobVersionedHashes []common.Hash `json:"blobVersionedHashes,omitempty"` + AuthorizationList []types.SetCodeAuthorization `json:"authorizationList,omitempty"` + V *hexutil.Big `json:"v"` + R *hexutil.Big `json:"r"` + S *hexutil.Big `json:"s"` + YParity *hexutil.Uint64 `json:"yParity,omitempty"` } // newRPCTransaction returns a transaction that will serialize to the RPC @@ -1537,6 +1034,22 @@ func newRPCTransaction(tx *types.Transaction, blockHash common.Hash, blockNumber } result.MaxFeePerBlobGas = (*hexutil.Big)(tx.BlobGasFeeCap()) result.BlobVersionedHashes = tx.BlobHashes() + + case types.SetCodeTxType: + al := tx.AccessList() + yparity := hexutil.Uint64(v.Sign()) + result.Accesses = &al + result.ChainID = (*hexutil.Big)(tx.ChainId()) + result.YParity = &yparity + result.GasFeeCap = (*hexutil.Big)(tx.GasFeeCap()) + result.GasTipCap = (*hexutil.Big)(tx.GasTipCap()) + // if the transaction has been mined, compute the effective gas price + if baseFee != nil && blockHash != (common.Hash{}) { + result.GasPrice = (*hexutil.Big)(effectiveGasPrice(tx, baseFee)) + } else { + result.GasPrice = (*hexutil.Big)(tx.GasFeeCap()) + } + result.AuthorizationList = tx.SetCodeAuthorizations() } return result } @@ -1625,9 +1138,18 @@ func AccessList(ctx context.Context, b Backend, blockNrOrHash rpc.BlockNumberOrH } // Ensure any missing fields are filled, extract the recipient and input data - if err := args.setDefaults(ctx, b, true); err != nil { + if err = args.setFeeDefaults(ctx, b, header); err != nil { return nil, 0, nil, err } + if args.Nonce == nil { + nonce := hexutil.Uint64(db.GetNonce(args.from())) + args.Nonce = &nonce + } + blockCtx := core.NewEVMBlockContext(header, NewChainContext(ctx, b), nil) + if err = args.CallDefaults(b.RPCGasCap(), blockCtx.BaseFee, b.ChainConfig().ChainID); err != nil { + return nil, 0, nil, err + } + var to common.Address if args.To != nil { to = *args.To @@ -1660,16 +1182,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) } @@ -2089,11 +1612,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 384ca9f1cc..0303a0a6ea 100644 --- a/internal/ethapi/api_test.go +++ b/internal/ethapi/api_test.go @@ -24,7 +24,6 @@ import ( "encoding/json" "errors" "fmt" - "maps" "math/big" "os" "path/filepath" @@ -34,6 +33,9 @@ import ( "testing" "time" + "github.com/ethereum/go-ethereum/accounts/abi" + "github.com/ethereum/go-ethereum/internal/ethapi/override" + "github.com/ethereum/go-ethereum" "github.com/ethereum/go-ethereum/accounts" "github.com/ethereum/go-ethereum/accounts/keystore" @@ -55,7 +57,6 @@ import ( "github.com/ethereum/go-ethereum/internal/blocktest" "github.com/ethereum/go-ethereum/params" "github.com/ethereum/go-ethereum/rpc" - "github.com/ethereum/go-ethereum/triedb" "github.com/holiman/uint256" "github.com/stretchr/testify/require" ) @@ -416,7 +417,7 @@ func allBlobTxs(addr common.Address, config *params.ChainConfig) []txData { func newTestAccountManager(t *testing.T) (*accounts.Manager, accounts.Account) { var ( dir = t.TempDir() - am = accounts.NewManager(&accounts.Config{InsecureUnlockAllowed: true}) + am = accounts.NewManager(nil) b = keystore.NewKeyStore(dir, 2, 1) testKey, _ = crypto.HexToECDSA("b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291") ) @@ -570,16 +571,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") @@ -587,9 +587,6 @@ func (b testBackend) SubscribeChainEvent(ch chan<- core.ChainEvent) event.Subscr func (b testBackend) SubscribeChainHeadEvent(ch chan<- core.ChainHeadEvent) event.Subscription { panic("implement me") } -func (b testBackend) SubscribeChainSideEvent(ch chan<- core.ChainSideEvent) event.Subscription { - panic("implement me") -} func (b testBackend) SendTx(ctx context.Context, signedTx *types.Transaction) error { panic("implement me") } @@ -632,18 +629,30 @@ func TestEstimateGas(t *testing.T) { t.Parallel() // Initialize test accounts var ( - accounts = newAccounts(2) + accounts = newAccounts(4) genesis = &core.Genesis{ Config: params.MergedTestChainConfig, Alloc: types.GenesisAlloc{ accounts[0].addr: {Balance: big.NewInt(params.Ether)}, accounts[1].addr: {Balance: big.NewInt(params.Ether)}, + accounts[2].addr: {Balance: big.NewInt(params.Ether), Code: append(types.DelegationPrefix, accounts[3].addr.Bytes()...)}, }, } genBlocks = 10 signer = types.HomesteadSigner{} randomAccounts = newAccounts(2) ) + packRevert := func(revertMessage string) []byte { + var revertSelector = crypto.Keccak256([]byte("Error(string)"))[:4] + stringType, _ := abi.NewType("string", "", nil) + args := abi.Arguments{ + {Type: stringType}, + } + encodedMessage, _ := args.Pack(revertMessage) + + return append(revertSelector, encodedMessage...) + } + api := NewBlockChainAPI(newTestBackend(t, genBlocks, genesis, beacon.New(ethash.NewFaker()), func(i int, b *core.BlockGen) { // Transfer from account[0] to account[1] // value: 1000 wei @@ -652,14 +661,16 @@ func TestEstimateGas(t *testing.T) { b.AddTx(tx) b.SetPoS() })) + var testSuite = []struct { - blockNumber rpc.BlockNumber - call TransactionArgs - overrides StateOverride - expectErr error - want uint64 + blockNumber rpc.BlockNumber + call TransactionArgs + overrides override.StateOverride + blockOverrides override.BlockOverrides + expectErr error + want uint64 }{ - // simple transfer on latest block + //simple transfer on latest block { blockNumber: rpc.LatestBlockNumber, call: TransactionArgs{ @@ -691,8 +702,8 @@ func TestEstimateGas(t *testing.T) { { blockNumber: rpc.LatestBlockNumber, call: TransactionArgs{}, - overrides: StateOverride{ - randomAccounts[0].addr: OverrideAccount{Balance: newRPCBalance(new(big.Int).Mul(big.NewInt(1), big.NewInt(params.Ether)))}, + overrides: override.StateOverride{ + randomAccounts[0].addr: override.OverrideAccount{Balance: newRPCBalance(new(big.Int).Mul(big.NewInt(1), big.NewInt(params.Ether)))}, }, expectErr: nil, want: 53000, @@ -704,8 +715,8 @@ func TestEstimateGas(t *testing.T) { To: &randomAccounts[1].addr, Value: (*hexutil.Big)(big.NewInt(1000)), }, - overrides: StateOverride{ - randomAccounts[0].addr: OverrideAccount{Balance: newRPCBalance(big.NewInt(0))}, + overrides: override.StateOverride{ + randomAccounts[0].addr: override.OverrideAccount{Balance: newRPCBalance(big.NewInt(0))}, }, expectErr: core.ErrInsufficientFunds, }, @@ -762,16 +773,85 @@ func TestEstimateGas(t *testing.T) { }, want: 21000, }, + // // SPDX-License-Identifier: GPL-3.0 + //pragma solidity >=0.8.2 <0.9.0; + // + //contract BlockOverridesTest { + // function call() public view returns (uint256) { + // return block.number; + // } + // + // function estimate() public view { + // revert(string.concat("block ", uint2str(block.number))); + // } + // + // function uint2str(uint256 _i) internal pure returns (string memory str) { + // if (_i == 0) { + // return "0"; + // } + // uint256 j = _i; + // uint256 length; + // while (j != 0) { + // length++; + // j /= 10; + // } + // bytes memory bstr = new bytes(length); + // uint256 k = length; + // j = _i; + // while (j != 0) { + // bstr[--k] = bytes1(uint8(48 + (j % 10))); + // j /= 10; + // } + // str = string(bstr); + // } + //} + { + blockNumber: rpc.LatestBlockNumber, + call: TransactionArgs{ + From: &accounts[0].addr, + To: &accounts[1].addr, + Data: hex2Bytes("0x3592d016"), //estimate + }, + overrides: override.StateOverride{ + accounts[1].addr: override.OverrideAccount{ + Code: hex2Bytes("608060405234801561000f575f5ffd5b5060043610610034575f3560e01c806328b5e32b146100385780633592d0161461004b575b5f5ffd5b4360405190815260200160405180910390f35b610053610055565b005b61005e4361009d565b60405160200161006e91906101a5565b60408051601f198184030181529082905262461bcd60e51b8252610094916004016101cd565b60405180910390fd5b6060815f036100c35750506040805180820190915260018152600360fc1b602082015290565b815f5b81156100ec57806100d681610216565b91506100e59050600a83610242565b91506100c6565b5f8167ffffffffffffffff81111561010657610106610255565b6040519080825280601f01601f191660200182016040528015610130576020820181803683370190505b508593509050815b831561019c57610149600a85610269565b61015490603061027c565b60f81b8261016183610295565b92508281518110610174576101746102aa565b60200101906001600160f81b03191690815f1a905350610195600a85610242565b9350610138565b50949350505050565b650313637b1b5960d51b81525f82518060208501600685015e5f920160060191825250919050565b602081525f82518060208401528060208501604085015e5f604082850101526040601f19601f83011684010191505092915050565b634e487b7160e01b5f52601160045260245ffd5b5f6001820161022757610227610202565b5060010190565b634e487b7160e01b5f52601260045260245ffd5b5f826102505761025061022e565b500490565b634e487b7160e01b5f52604160045260245ffd5b5f826102775761027761022e565b500690565b8082018082111561028f5761028f610202565b92915050565b5f816102a3576102a3610202565b505f190190565b634e487b7160e01b5f52603260045260245ffdfea2646970667358221220a253cad1e2e3523b8c053c1d0cd1e39d7f3bafcedd73440a244872701f05dab264736f6c634300081c0033"), + }, + }, + blockOverrides: override.BlockOverrides{Number: (*hexutil.Big)(big.NewInt(11))}, + expectErr: newRevertError(packRevert("block 11")), + }, + // Should be able to send to an EIP-7702 delegated account. + { + blockNumber: rpc.LatestBlockNumber, + call: TransactionArgs{ + From: &accounts[0].addr, + To: &accounts[2].addr, + Value: (*hexutil.Big)(big.NewInt(1)), + }, + want: 21000, + }, + // Should be able to send as EIP-7702 delegated account. + { + blockNumber: rpc.LatestBlockNumber, + call: TransactionArgs{ + From: &accounts[2].addr, + To: &accounts[1].addr, + Value: (*hexutil.Big)(big.NewInt(1)), + }, + want: 21000, + }, } for i, tc := range testSuite { - result, err := api.EstimateGas(context.Background(), tc.call, &rpc.BlockNumberOrHash{BlockNumber: &tc.blockNumber}, &tc.overrides) + result, err := api.EstimateGas(context.Background(), tc.call, &rpc.BlockNumberOrHash{BlockNumber: &tc.blockNumber}, &tc.overrides, &tc.blockOverrides) if tc.expectErr != nil { if err == nil { t.Errorf("test %d: want error %v, have nothing", i, tc.expectErr) continue } if !errors.Is(err, tc.expectErr) { - t.Errorf("test %d: error mismatch, want %v, have %v", i, tc.expectErr, err) + if !reflect.DeepEqual(err, tc.expectErr) { + t.Errorf("test %d: error mismatch, want %v, have %v", i, tc.expectErr, err) + } } continue } @@ -822,9 +902,9 @@ func TestCall(t *testing.T) { var testSuite = []struct { name string blockNumber rpc.BlockNumber - overrides StateOverride + overrides override.StateOverride call TransactionArgs - blockOverrides BlockOverrides + blockOverrides override.BlockOverrides expectErr error want string }{ @@ -884,8 +964,8 @@ func TestCall(t *testing.T) { To: &randomAccounts[1].addr, Value: (*hexutil.Big)(big.NewInt(1000)), }, - overrides: StateOverride{ - randomAccounts[0].addr: OverrideAccount{Balance: newRPCBalance(new(big.Int).Mul(big.NewInt(1), big.NewInt(params.Ether)))}, + overrides: override.StateOverride{ + randomAccounts[0].addr: override.OverrideAccount{Balance: newRPCBalance(new(big.Int).Mul(big.NewInt(1), big.NewInt(params.Ether)))}, }, want: "0x", }, @@ -924,27 +1004,60 @@ func TestCall(t *testing.T) { To: &randomAccounts[2].addr, Data: hex2Bytes("8381f58a"), // call number() }, - overrides: StateOverride{ - randomAccounts[2].addr: OverrideAccount{ + overrides: override.StateOverride{ + randomAccounts[2].addr: override.OverrideAccount{ Code: hex2Bytes("6080604052348015600f57600080fd5b506004361060285760003560e01c80638381f58a14602d575b600080fd5b60336049565b6040518082815260200191505060405180910390f35b6000548156fea2646970667358221220eab35ffa6ab2adfe380772a48b8ba78e82a1b820a18fcb6f59aa4efb20a5f60064736f6c63430007040033"), StateDiff: map[common.Hash]common.Hash{{}: common.BigToHash(big.NewInt(123))}, }, }, want: "0x000000000000000000000000000000000000000000000000000000000000007b", }, - // Block overrides should work + // // SPDX-License-Identifier: GPL-3.0 + //pragma solidity >=0.8.2 <0.9.0; + // + //contract BlockOverridesTest { + // function call() public view returns (uint256) { + // return block.number; + // } + // + // function estimate() public view { + // revert(string.concat("block ", uint2str(block.number))); + // } + // + // function uint2str(uint256 _i) internal pure returns (string memory str) { + // if (_i == 0) { + // return "0"; + // } + // uint256 j = _i; + // uint256 length; + // while (j != 0) { + // length++; + // j /= 10; + // } + // bytes memory bstr = new bytes(length); + // uint256 k = length; + // j = _i; + // while (j != 0) { + // bstr[--k] = bytes1(uint8(48 + (j % 10))); + // j /= 10; + // } + // str = string(bstr); + // } + //} { - name: "block-override", + name: "block-override-with-state-override", blockNumber: rpc.LatestBlockNumber, call: TransactionArgs{ From: &accounts[1].addr, - Input: &hexutil.Bytes{ - 0x43, // NUMBER - 0x60, 0x00, 0x52, // MSTORE offset 0 - 0x60, 0x20, 0x60, 0x00, 0xf3, + To: &accounts[2].addr, + Data: hex2Bytes("0x28b5e32b"), //call + }, + overrides: override.StateOverride{ + accounts[2].addr: override.OverrideAccount{ + Code: hex2Bytes("608060405234801561000f575f5ffd5b5060043610610034575f3560e01c806328b5e32b146100385780633592d0161461004b575b5f5ffd5b4360405190815260200160405180910390f35b610053610055565b005b61005e4361009d565b60405160200161006e91906101a5565b60408051601f198184030181529082905262461bcd60e51b8252610094916004016101cd565b60405180910390fd5b6060815f036100c35750506040805180820190915260018152600360fc1b602082015290565b815f5b81156100ec57806100d681610216565b91506100e59050600a83610242565b91506100c6565b5f8167ffffffffffffffff81111561010657610106610255565b6040519080825280601f01601f191660200182016040528015610130576020820181803683370190505b508593509050815b831561019c57610149600a85610269565b61015490603061027c565b60f81b8261016183610295565b92508281518110610174576101746102aa565b60200101906001600160f81b03191690815f1a905350610195600a85610242565b9350610138565b50949350505050565b650313637b1b5960d51b81525f82518060208501600685015e5f920160060191825250919050565b602081525f82518060208401528060208501604085015e5f604082850101526040601f19601f83011684010191505092915050565b634e487b7160e01b5f52601160045260245ffd5b5f6001820161022757610227610202565b5060010190565b634e487b7160e01b5f52601260045260245ffd5b5f826102505761025061022e565b500490565b634e487b7160e01b5f52604160045260245ffd5b5f826102775761027761022e565b500690565b8082018082111561028f5761028f610202565b92915050565b5f816102a3576102a3610202565b505f190190565b634e487b7160e01b5f52603260045260245ffdfea2646970667358221220a253cad1e2e3523b8c053c1d0cd1e39d7f3bafcedd73440a244872701f05dab264736f6c634300081c0033"), }, }, - blockOverrides: BlockOverrides{Number: (*hexutil.Big)(big.NewInt(11))}, + blockOverrides: override.BlockOverrides{Number: (*hexutil.Big)(big.NewInt(11))}, want: "0x000000000000000000000000000000000000000000000000000000000000000b", }, // Clear storage trie @@ -967,8 +1080,8 @@ func TestCall(t *testing.T) { // } Input: hex2Bytes("610dad6000813103600f57600080fd5b6000548060005260206000f3"), }, - overrides: StateOverride{ - dad: OverrideAccount{ + overrides: override.StateOverride{ + dad: override.OverrideAccount{ State: map[common.Hash]common.Hash{}, }, }, @@ -995,7 +1108,7 @@ func TestCall(t *testing.T) { BlobHashes: []common.Hash{{0x01, 0x22}}, BlobFeeCap: (*hexutil.Big)(big.NewInt(1)), }, - overrides: StateOverride{ + overrides: override.StateOverride{ randomAccounts[2].addr: { Code: hex2Bytes("60004960005260206000f3"), }, @@ -1021,8 +1134,8 @@ func TestCall(t *testing.T) { // } Input: hex2Bytes("610dad6000813103600f57600080fd5b6000548060005260206000f3"), }, - overrides: StateOverride{ - dad: OverrideAccount{ + overrides: override.StateOverride{ + dad: override.OverrideAccount{ State: map[common.Hash]common.Hash{}, }, }, @@ -1176,8 +1289,8 @@ func TestSimulateV1(t *testing.T) { name: "simple", tag: latest, blocks: []simBlock{{ - StateOverrides: &StateOverride{ - randomAccounts[0].addr: OverrideAccount{Balance: newRPCBalance(big.NewInt(1000))}, + StateOverrides: &override.StateOverride{ + randomAccounts[0].addr: override.OverrideAccount{Balance: newRPCBalance(big.NewInt(1000))}, }, Calls: []TransactionArgs{{ From: &randomAccounts[0].addr, @@ -1219,8 +1332,8 @@ func TestSimulateV1(t *testing.T) { name: "simple-multi-block", tag: latest, blocks: []simBlock{{ - StateOverrides: &StateOverride{ - randomAccounts[0].addr: OverrideAccount{Balance: newRPCBalance(big.NewInt(2000))}, + StateOverrides: &override.StateOverride{ + randomAccounts[0].addr: override.OverrideAccount{Balance: newRPCBalance(big.NewInt(2000))}, }, Calls: []TransactionArgs{ { @@ -1234,8 +1347,8 @@ func TestSimulateV1(t *testing.T) { }, }, }, { - StateOverrides: &StateOverride{ - randomAccounts[3].addr: OverrideAccount{Balance: newRPCBalance(big.NewInt(0))}, + StateOverrides: &override.StateOverride{ + randomAccounts[3].addr: override.OverrideAccount{Balance: newRPCBalance(big.NewInt(0))}, }, Calls: []TransactionArgs{ { @@ -1293,8 +1406,8 @@ func TestSimulateV1(t *testing.T) { name: "evm-error", tag: latest, blocks: []simBlock{{ - StateOverrides: &StateOverride{ - randomAccounts[2].addr: OverrideAccount{Code: hex2Bytes("f3")}, + StateOverrides: &override.StateOverride{ + randomAccounts[2].addr: override.OverrideAccount{Code: hex2Bytes("f3")}, }, Calls: []TransactionArgs{{ From: &randomAccounts[0].addr, @@ -1320,7 +1433,7 @@ func TestSimulateV1(t *testing.T) { name: "block-overrides", tag: latest, blocks: []simBlock{{ - BlockOverrides: &BlockOverrides{ + BlockOverrides: &override.BlockOverrides{ Number: (*hexutil.Big)(big.NewInt(11)), FeeRecipient: &cac, }, @@ -1335,7 +1448,7 @@ func TestSimulateV1(t *testing.T) { }, }, }, { - BlockOverrides: &BlockOverrides{ + BlockOverrides: &override.BlockOverrides{ Number: (*hexutil.Big)(big.NewInt(12)), }, Calls: []TransactionArgs{{ @@ -1378,7 +1491,7 @@ func TestSimulateV1(t *testing.T) { name: "block-number-order", tag: latest, blocks: []simBlock{{ - BlockOverrides: &BlockOverrides{ + BlockOverrides: &override.BlockOverrides{ Number: (*hexutil.Big)(big.NewInt(12)), }, Calls: []TransactionArgs{{ @@ -1390,7 +1503,7 @@ func TestSimulateV1(t *testing.T) { }, }}, }, { - BlockOverrides: &BlockOverrides{ + BlockOverrides: &override.BlockOverrides{ Number: (*hexutil.Big)(big.NewInt(11)), }, Calls: []TransactionArgs{{ @@ -1410,8 +1523,8 @@ func TestSimulateV1(t *testing.T) { name: "storage-contract", tag: latest, blocks: []simBlock{{ - StateOverrides: &StateOverride{ - randomAccounts[2].addr: OverrideAccount{ + StateOverrides: &override.StateOverride{ + randomAccounts[2].addr: override.OverrideAccount{ Code: hex2Bytes("608060405234801561001057600080fd5b50600436106100365760003560e01c80632e64cec11461003b5780636057361d14610059575b600080fd5b610043610075565b60405161005091906100d9565b60405180910390f35b610073600480360381019061006e919061009d565b61007e565b005b60008054905090565b8060008190555050565b60008135905061009781610103565b92915050565b6000602082840312156100b3576100b26100fe565b5b60006100c184828501610088565b91505092915050565b6100d3816100f4565b82525050565b60006020820190506100ee60008301846100ca565b92915050565b6000819050919050565b600080fd5b61010c816100f4565b811461011757600080fd5b5056fea2646970667358221220404e37f487a89a932dca5e77faaf6ca2de3b991f93d230604b1b8daaef64766264736f6c63430008070033"), }, }, @@ -1452,8 +1565,8 @@ func TestSimulateV1(t *testing.T) { name: "logs", tag: latest, blocks: []simBlock{{ - StateOverrides: &StateOverride{ - randomAccounts[2].addr: OverrideAccount{ + StateOverrides: &override.StateOverride{ + randomAccounts[2].addr: override.OverrideAccount{ // Yul code: // object "Test" { // code { @@ -1494,8 +1607,8 @@ func TestSimulateV1(t *testing.T) { name: "ecrecover-override", tag: latest, blocks: []simBlock{{ - StateOverrides: &StateOverride{ - randomAccounts[2].addr: OverrideAccount{ + StateOverrides: &override.StateOverride{ + randomAccounts[2].addr: override.OverrideAccount{ // Yul code that returns ecrecover(0, 0, 0, 0). // object "Test" { // code { @@ -1522,7 +1635,7 @@ func TestSimulateV1(t *testing.T) { // } Code: hex2Bytes("6040516000815260006020820152600060408201526000606082015260208160808360015afa60008103603157600080fd5b601482f3"), }, - common.BytesToAddress([]byte{0x01}): OverrideAccount{ + common.BytesToAddress([]byte{0x01}): override.OverrideAccount{ // Yul code that returns the address of the caller. // object "Test" { // code { @@ -1559,8 +1672,8 @@ func TestSimulateV1(t *testing.T) { name: "precompile-move", tag: latest, blocks: []simBlock{{ - StateOverrides: &StateOverride{ - sha256Address: OverrideAccount{ + StateOverrides: &override.StateOverride{ + sha256Address: override.OverrideAccount{ // Yul code that returns the calldata. // object "Test" { // code { @@ -1614,8 +1727,8 @@ func TestSimulateV1(t *testing.T) { name: "transfer-logs", tag: latest, blocks: []simBlock{{ - StateOverrides: &StateOverride{ - randomAccounts[0].addr: OverrideAccount{ + StateOverrides: &override.StateOverride{ + randomAccounts[0].addr: override.OverrideAccount{ Balance: newRPCBalance(big.NewInt(100)), // Yul code that transfers 100 wei to address passed in calldata: // object "Test" { @@ -1757,8 +1870,8 @@ func TestSimulateV1(t *testing.T) { name: "validation-checks-from-contract", tag: latest, blocks: []simBlock{{ - StateOverrides: &StateOverride{ - randomAccounts[2].addr: OverrideAccount{ + StateOverrides: &override.StateOverride{ + randomAccounts[2].addr: override.OverrideAccount{ Balance: newRPCBalance(big.NewInt(2098640803896784)), Code: hex2Bytes("00"), Nonce: newUint64(1), @@ -1792,11 +1905,11 @@ func TestSimulateV1(t *testing.T) { name: "validation-checks-success", tag: latest, blocks: []simBlock{{ - BlockOverrides: &BlockOverrides{ + BlockOverrides: &override.BlockOverrides{ BaseFeePerGas: (*hexutil.Big)(big.NewInt(1)), }, - StateOverrides: &StateOverride{ - randomAccounts[0].addr: OverrideAccount{Balance: newRPCBalance(big.NewInt(10000000))}, + StateOverrides: &override.StateOverride{ + randomAccounts[0].addr: override.OverrideAccount{Balance: newRPCBalance(big.NewInt(10000000))}, }, Calls: []TransactionArgs{{ From: &randomAccounts[0].addr, @@ -1825,7 +1938,7 @@ func TestSimulateV1(t *testing.T) { name: "clear-storage", tag: latest, blocks: []simBlock{{ - StateOverrides: &StateOverride{ + StateOverrides: &override.StateOverride{ randomAccounts[2].addr: { Code: newBytes(genesis.Alloc[bab].Code), StateDiff: map[common.Hash]common.Hash{ @@ -1847,7 +1960,7 @@ func TestSimulateV1(t *testing.T) { To: &bab, }}, }, { - StateOverrides: &StateOverride{ + StateOverrides: &override.StateOverride{ randomAccounts[2].addr: { State: map[common.Hash]common.Hash{ common.BigToHash(big.NewInt(1)): common.BigToHash(big.NewInt(5)), @@ -1894,10 +2007,10 @@ func TestSimulateV1(t *testing.T) { name: "blockhash-opcode", tag: latest, blocks: []simBlock{{ - BlockOverrides: &BlockOverrides{ + BlockOverrides: &override.BlockOverrides{ Number: (*hexutil.Big)(big.NewInt(12)), }, - StateOverrides: &StateOverride{ + StateOverrides: &override.StateOverride{ randomAccounts[2].addr: { Code: hex2Bytes("600035804060008103601057600080fd5b5050"), }, @@ -1919,7 +2032,7 @@ func TestSimulateV1(t *testing.T) { Input: uint256ToBytes(uint256.NewInt(10)), }}, }, { - BlockOverrides: &BlockOverrides{ + BlockOverrides: &override.BlockOverrides{ Number: (*hexutil.Big)(big.NewInt(16)), }, Calls: []TransactionArgs{{ @@ -2009,7 +2122,7 @@ func TestSimulateV1(t *testing.T) { name: "basefee-non-validation", tag: latest, blocks: []simBlock{{ - StateOverrides: &StateOverride{ + StateOverrides: &override.StateOverride{ randomAccounts[2].addr: { // Yul code: // object "Test" { @@ -2044,7 +2157,7 @@ func TestSimulateV1(t *testing.T) { }, }, }, { - BlockOverrides: &BlockOverrides{ + BlockOverrides: &override.BlockOverrides{ BaseFeePerGas: (*hexutil.Big)(big.NewInt(1)), }, Calls: []TransactionArgs{{ @@ -2117,7 +2230,7 @@ func TestSimulateV1(t *testing.T) { name: "basefee-validation-mode", tag: latest, blocks: []simBlock{{ - StateOverrides: &StateOverride{ + StateOverrides: &override.StateOverride{ randomAccounts[2].addr: { // Yul code: // object "Test" { @@ -2195,6 +2308,7 @@ func TestSimulateV1(t *testing.T) { t.Fatalf("failed to unmarshal result: %v", err) } if !reflect.DeepEqual(have, tc.want) { + t.Log(string(resBytes)) t.Errorf("test %s, result mismatch, have\n%v\n, want\n%v\n", tc.name, have, tc.want) } }) @@ -3289,97 +3403,6 @@ func TestRPCGetBlockReceipts(t *testing.T) { } } -type precompileContract struct{} - -func (p *precompileContract) RequiredGas(input []byte) uint64 { return 0 } - -func (p *precompileContract) Run(input []byte) ([]byte, error) { return nil, nil } - -func TestStateOverrideMovePrecompile(t *testing.T) { - db := state.NewDatabase(triedb.NewDatabase(rawdb.NewMemoryDatabase(), nil), nil) - statedb, err := state.New(common.Hash{}, db) - if err != nil { - t.Fatalf("failed to create statedb: %v", err) - } - precompiles := map[common.Address]vm.PrecompiledContract{ - common.BytesToAddress([]byte{0x1}): &precompileContract{}, - common.BytesToAddress([]byte{0x2}): &precompileContract{}, - } - bytes2Addr := func(b []byte) *common.Address { - a := common.BytesToAddress(b) - return &a - } - var testSuite = []struct { - overrides StateOverride - expectedPrecompiles map[common.Address]struct{} - fail bool - }{ - { - overrides: StateOverride{ - common.BytesToAddress([]byte{0x1}): { - Code: hex2Bytes("0xff"), - MovePrecompileTo: bytes2Addr([]byte{0x2}), - }, - common.BytesToAddress([]byte{0x2}): { - Code: hex2Bytes("0x00"), - }, - }, - // 0x2 has already been touched by the moveTo. - fail: true, - }, { - overrides: StateOverride{ - common.BytesToAddress([]byte{0x1}): { - Code: hex2Bytes("0xff"), - MovePrecompileTo: bytes2Addr([]byte{0xff}), - }, - common.BytesToAddress([]byte{0x3}): { - Code: hex2Bytes("0x00"), - MovePrecompileTo: bytes2Addr([]byte{0xfe}), - }, - }, - // 0x3 is not a precompile. - fail: true, - }, { - overrides: StateOverride{ - common.BytesToAddress([]byte{0x1}): { - Code: hex2Bytes("0xff"), - MovePrecompileTo: bytes2Addr([]byte{0xff}), - }, - common.BytesToAddress([]byte{0x2}): { - Code: hex2Bytes("0x00"), - MovePrecompileTo: bytes2Addr([]byte{0xfe}), - }, - }, - expectedPrecompiles: map[common.Address]struct{}{common.BytesToAddress([]byte{0xfe}): {}, common.BytesToAddress([]byte{0xff}): {}}, - }, - } - - for i, tt := range testSuite { - cpy := maps.Clone(precompiles) - // Apply overrides - err := tt.overrides.Apply(statedb, cpy) - if tt.fail { - if err == nil { - t.Errorf("test %d: want error, have nothing", i) - } - continue - } - if err != nil { - t.Errorf("test %d: want no error, have %v", i, err) - continue - } - // Precompile keys - if len(cpy) != len(tt.expectedPrecompiles) { - t.Errorf("test %d: precompile mismatch, want %d, have %d", i, len(tt.expectedPrecompiles), len(cpy)) - } - for k := range tt.expectedPrecompiles { - if _, ok := cpy[k]; !ok { - t.Errorf("test %d: precompile not found: %s", i, k.String()) - } - } - } -} - func testRPCResponseWithFile(t *testing.T, testid int, result interface{}, rpc string, file string) { data, err := json.MarshalIndent(result, "", " ") if err != nil { diff --git a/internal/ethapi/backend.go b/internal/ethapi/backend.go index 0e991592b4..3d8f7aa700 100644 --- a/internal/ethapi/backend.go +++ b/internal/ethapi/backend.go @@ -68,10 +68,9 @@ 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 - SubscribeChainSideEvent(ch chan<- core.ChainSideEvent) event.Subscription // Transaction pool API SendTx(ctx context.Context, signedTx *types.Transaction) error @@ -119,9 +118,6 @@ func GetAPIs(apiBackend Backend) []rpc.API { }, { Namespace: "eth", Service: NewEthereumAccountAPI(apiBackend.AccountManager()), - }, { - Namespace: "personal", - Service: NewPersonalAccountAPI(apiBackend, nonceLock), }, } } diff --git a/internal/ethapi/override/override.go b/internal/ethapi/override/override.go new file mode 100644 index 0000000000..70b6210275 --- /dev/null +++ b/internal/ethapi/override/override.go @@ -0,0 +1,195 @@ +// 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 override + +import ( + "fmt" + "math/big" + + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/common/hexutil" + "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/holiman/uint256" +) + +// OverrideAccount indicates the overriding fields of account during the execution +// of a message call. +// Note, state and stateDiff can't be specified at the same time. If state is +// set, message execution will only use the data in the given state. Otherwise +// if stateDiff is set, all diff will be applied first and then execute the call +// message. +type OverrideAccount struct { + Nonce *hexutil.Uint64 `json:"nonce"` + Code *hexutil.Bytes `json:"code"` + Balance *hexutil.Big `json:"balance"` + State map[common.Hash]common.Hash `json:"state"` + StateDiff map[common.Hash]common.Hash `json:"stateDiff"` + MovePrecompileTo *common.Address `json:"movePrecompileToAddress"` +} + +// StateOverride is the collection of overridden accounts. +type StateOverride map[common.Address]OverrideAccount + +func (diff *StateOverride) has(address common.Address) bool { + _, ok := (*diff)[address] + return ok +} + +// Apply overrides the fields of specified accounts into the given state. +func (diff *StateOverride) Apply(statedb *state.StateDB, precompiles vm.PrecompiledContracts) error { + if diff == nil { + return nil + } + // Tracks destinations of precompiles that were moved. + dirtyAddrs := make(map[common.Address]struct{}) + for addr, account := range *diff { + // If a precompile was moved to this address already, it can't be overridden. + if _, ok := dirtyAddrs[addr]; ok { + return fmt.Errorf("account %s has already been overridden by a precompile", addr.Hex()) + } + p, isPrecompile := precompiles[addr] + // The MoveTo feature makes it possible to move a precompile + // code to another address. If the target address is another precompile + // the code for the latter is lost for this session. + // Note the destination account is not cleared upon move. + if account.MovePrecompileTo != nil { + if !isPrecompile { + return fmt.Errorf("account %s is not a precompile", addr.Hex()) + } + // Refuse to move a precompile to an address that has been + // or will be overridden. + if diff.has(*account.MovePrecompileTo) { + return fmt.Errorf("account %s is already overridden", account.MovePrecompileTo.Hex()) + } + precompiles[*account.MovePrecompileTo] = p + dirtyAddrs[*account.MovePrecompileTo] = struct{}{} + } + if isPrecompile { + delete(precompiles, addr) + } + // Override account nonce. + if account.Nonce != nil { + statedb.SetNonce(addr, uint64(*account.Nonce)) + } + // Override account(contract) code. + if account.Code != nil { + statedb.SetCode(addr, *account.Code) + } + // Override account balance. + if account.Balance != nil { + u256Balance, _ := uint256.FromBig((*big.Int)(account.Balance)) + statedb.SetBalance(addr, u256Balance, tracing.BalanceChangeUnspecified) + } + if account.State != nil && account.StateDiff != nil { + return fmt.Errorf("account %s has both 'state' and 'stateDiff'", addr.Hex()) + } + // Replace entire state if caller requires. + if account.State != nil { + statedb.SetStorage(addr, account.State) + } + // Apply state diff into specified accounts. + if account.StateDiff != nil { + for key, value := range account.StateDiff { + statedb.SetState(addr, key, value) + } + } + } + // Now finalize the changes. Finalize is normally performed between transactions. + // By using finalize, the overrides are semantically behaving as + // if they were created in a transaction just before the tracing occur. + statedb.Finalise(false) + return nil +} + +// BlockOverrides is a set of header fields to override. +type BlockOverrides struct { + Number *hexutil.Big + Difficulty *hexutil.Big // No-op if we're simulating post-merge calls. + Time *hexutil.Uint64 + GasLimit *hexutil.Uint64 + FeeRecipient *common.Address + PrevRandao *common.Hash + BaseFeePerGas *hexutil.Big + BlobBaseFee *hexutil.Big +} + +// Apply overrides the given header fields into the given block context. +func (o *BlockOverrides) Apply(blockCtx *vm.BlockContext) { + if o == nil { + return + } + if o.Number != nil { + blockCtx.BlockNumber = o.Number.ToInt() + } + if o.Difficulty != nil { + blockCtx.Difficulty = o.Difficulty.ToInt() + } + if o.Time != nil { + blockCtx.Time = uint64(*o.Time) + } + if o.GasLimit != nil { + blockCtx.GasLimit = uint64(*o.GasLimit) + } + if o.FeeRecipient != nil { + blockCtx.Coinbase = *o.FeeRecipient + } + if o.PrevRandao != nil { + blockCtx.Random = o.PrevRandao + } + if o.BaseFeePerGas != nil { + blockCtx.BaseFee = o.BaseFeePerGas.ToInt() + } + if o.BlobBaseFee != nil { + blockCtx.BlobBaseFee = o.BlobBaseFee.ToInt() + } +} + +// MakeHeader returns a new header object with the overridden +// fields. +// Note: MakeHeader ignores BlobBaseFee if set. That's because +// header has no such field. +func (o *BlockOverrides) MakeHeader(header *types.Header) *types.Header { + if o == nil { + return header + } + h := types.CopyHeader(header) + if o.Number != nil { + h.Number = o.Number.ToInt() + } + if o.Difficulty != nil { + h.Difficulty = o.Difficulty.ToInt() + } + if o.Time != nil { + h.Time = uint64(*o.Time) + } + if o.GasLimit != nil { + h.GasLimit = uint64(*o.GasLimit) + } + if o.FeeRecipient != nil { + h.Coinbase = *o.FeeRecipient + } + if o.PrevRandao != nil { + h.MixDigest = *o.PrevRandao + } + if o.BaseFeePerGas != nil { + h.BaseFee = o.BaseFeePerGas.ToInt() + } + return h +} diff --git a/internal/ethapi/override/override_test.go b/internal/ethapi/override/override_test.go new file mode 100644 index 0000000000..07ed6442b2 --- /dev/null +++ b/internal/ethapi/override/override_test.go @@ -0,0 +1,125 @@ +// 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 override + +import ( + "maps" + "testing" + + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/common/hexutil" + "github.com/ethereum/go-ethereum/core/rawdb" + "github.com/ethereum/go-ethereum/core/state" + "github.com/ethereum/go-ethereum/core/vm" + "github.com/ethereum/go-ethereum/triedb" +) + +type precompileContract struct{} + +func (p *precompileContract) RequiredGas(input []byte) uint64 { return 0 } + +func (p *precompileContract) Run(input []byte) ([]byte, error) { return nil, nil } + +func TestStateOverrideMovePrecompile(t *testing.T) { + db := state.NewDatabase(triedb.NewDatabase(rawdb.NewMemoryDatabase(), nil), nil) + statedb, err := state.New(common.Hash{}, db) + if err != nil { + t.Fatalf("failed to create statedb: %v", err) + } + precompiles := map[common.Address]vm.PrecompiledContract{ + common.BytesToAddress([]byte{0x1}): &precompileContract{}, + common.BytesToAddress([]byte{0x2}): &precompileContract{}, + } + bytes2Addr := func(b []byte) *common.Address { + a := common.BytesToAddress(b) + return &a + } + var testSuite = []struct { + overrides StateOverride + expectedPrecompiles map[common.Address]struct{} + fail bool + }{ + { + overrides: StateOverride{ + common.BytesToAddress([]byte{0x1}): { + Code: hex2Bytes("0xff"), + MovePrecompileTo: bytes2Addr([]byte{0x2}), + }, + common.BytesToAddress([]byte{0x2}): { + Code: hex2Bytes("0x00"), + }, + }, + // 0x2 has already been touched by the moveTo. + fail: true, + }, { + overrides: StateOverride{ + common.BytesToAddress([]byte{0x1}): { + Code: hex2Bytes("0xff"), + MovePrecompileTo: bytes2Addr([]byte{0xff}), + }, + common.BytesToAddress([]byte{0x3}): { + Code: hex2Bytes("0x00"), + MovePrecompileTo: bytes2Addr([]byte{0xfe}), + }, + }, + // 0x3 is not a precompile. + fail: true, + }, { + overrides: StateOverride{ + common.BytesToAddress([]byte{0x1}): { + Code: hex2Bytes("0xff"), + MovePrecompileTo: bytes2Addr([]byte{0xff}), + }, + common.BytesToAddress([]byte{0x2}): { + Code: hex2Bytes("0x00"), + MovePrecompileTo: bytes2Addr([]byte{0xfe}), + }, + }, + expectedPrecompiles: map[common.Address]struct{}{common.BytesToAddress([]byte{0xfe}): {}, common.BytesToAddress([]byte{0xff}): {}}, + }, + } + + for i, tt := range testSuite { + cpy := maps.Clone(precompiles) + // Apply overrides + err := tt.overrides.Apply(statedb, cpy) + if tt.fail { + if err == nil { + t.Errorf("test %d: want error, have nothing", i) + } + continue + } + if err != nil { + t.Errorf("test %d: want no error, have %v", i, err) + continue + } + // Precompile keys + if len(cpy) != len(tt.expectedPrecompiles) { + t.Errorf("test %d: precompile mismatch, want %d, have %d", i, len(tt.expectedPrecompiles), len(cpy)) + } + for k := range tt.expectedPrecompiles { + if _, ok := cpy[k]; !ok { + t.Errorf("test %d: precompile not found: %s", i, k.String()) + } + } + } +} + +func hex2Bytes(str string) *hexutil.Bytes { + rpcBytes := hexutil.Bytes(common.FromHex(str)) + return &rpcBytes +} diff --git a/internal/ethapi/simulate.go b/internal/ethapi/simulate.go index 4371a42464..5bd0079a93 100644 --- a/internal/ethapi/simulate.go +++ b/internal/ethapi/simulate.go @@ -21,7 +21,6 @@ import ( "encoding/json" "errors" "fmt" - "maps" "math/big" "time" @@ -34,6 +33,7 @@ import ( "github.com/ethereum/go-ethereum/core/state" "github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/core/vm" + "github.com/ethereum/go-ethereum/internal/ethapi/override" "github.com/ethereum/go-ethereum/params" "github.com/ethereum/go-ethereum/rpc" "github.com/ethereum/go-ethereum/trie" @@ -50,8 +50,8 @@ const ( // simBlock is a batch of calls to be simulated sequentially. type simBlock struct { - BlockOverrides *BlockOverrides - StateOverrides *StateOverride + BlockOverrides *override.BlockOverrides + StateOverrides *override.StateOverride Calls []TransactionArgs } @@ -185,9 +185,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) ) - sim.state.SetLogger(tracer.Hooks()) + 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 { @@ -205,8 +208,7 @@ 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), sim.state) - result, err := applyMessageWithEVM(ctx, evm, msg, sim.state, timeout, sim.gp) + result, err := applyMessageWithEVM(ctx, evm, msg, timeout, sim.gp) if err != nil { txErr := txValidationError(err) return nil, nil, txErr @@ -214,7 +216,7 @@ func (sim *simulator) processBlock(ctx context.Context, block *simBlock, header, // Update the state with pending changes. var root []byte if sim.chainConfig.IsByzantium(blockContext.BlockNumber) { - sim.state.Finalise(true) + tracingStateDB.Finalise(true) } else { root = sim.state.IntermediateRoot(sim.chainConfig.IsEIP158(blockContext.BlockNumber)).Bytes() } @@ -262,7 +264,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) @@ -286,7 +288,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 @@ -302,7 +304,7 @@ func (sim *simulator) sanitizeChain(blocks []simBlock) ([]simBlock, error) { ) for _, block := range blocks { if block.BlockOverrides == nil { - block.BlockOverrides = new(BlockOverrides) + block.BlockOverrides = new(override.BlockOverrides) } if block.BlockOverrides.Number == nil { n := new(big.Int).Add(prevNumber, big.NewInt(1)) @@ -322,7 +324,7 @@ func (sim *simulator) sanitizeChain(blocks []simBlock) ([]simBlock, error) { for i := uint64(0); i < gap.Uint64(); i++ { n := new(big.Int).Add(prevNumber, big.NewInt(int64(i+1))) t := prevTimestamp + timestampIncrement - b := simBlock{BlockOverrides: &BlockOverrides{Number: (*hexutil.Big)(n), Time: (*hexutil.Uint64)(&t)}} + b := simBlock{BlockOverrides: &override.BlockOverrides{Number: (*hexutil.Big)(n), Time: (*hexutil.Uint64)(&t)}} prevTimestamp = t res = append(res, b) } diff --git a/internal/ethapi/simulate_test.go b/internal/ethapi/simulate_test.go index 37883924ac..52ae40cad0 100644 --- a/internal/ethapi/simulate_test.go +++ b/internal/ethapi/simulate_test.go @@ -22,6 +22,7 @@ import ( "github.com/ethereum/go-ethereum/common/hexutil" "github.com/ethereum/go-ethereum/core/types" + "github.com/ethereum/go-ethereum/internal/ethapi/override" ) func TestSimulateSanitizeBlockOrder(t *testing.T) { @@ -45,37 +46,37 @@ func TestSimulateSanitizeBlockOrder(t *testing.T) { { baseNumber: 10, baseTimestamp: 50, - blocks: []simBlock{{BlockOverrides: &BlockOverrides{Number: newInt(13), Time: newUint64(70)}}, {}}, + blocks: []simBlock{{BlockOverrides: &override.BlockOverrides{Number: newInt(13), Time: newUint64(70)}}, {}}, expected: []result{{number: 11, timestamp: 51}, {number: 12, timestamp: 52}, {number: 13, timestamp: 70}, {number: 14, timestamp: 71}}, }, { baseNumber: 10, baseTimestamp: 50, - blocks: []simBlock{{BlockOverrides: &BlockOverrides{Number: newInt(11)}}, {BlockOverrides: &BlockOverrides{Number: newInt(14)}}, {}}, + blocks: []simBlock{{BlockOverrides: &override.BlockOverrides{Number: newInt(11)}}, {BlockOverrides: &override.BlockOverrides{Number: newInt(14)}}, {}}, expected: []result{{number: 11, timestamp: 51}, {number: 12, timestamp: 52}, {number: 13, timestamp: 53}, {number: 14, timestamp: 54}, {number: 15, timestamp: 55}}, }, { baseNumber: 10, baseTimestamp: 50, - blocks: []simBlock{{BlockOverrides: &BlockOverrides{Number: newInt(13)}}, {BlockOverrides: &BlockOverrides{Number: newInt(12)}}}, + blocks: []simBlock{{BlockOverrides: &override.BlockOverrides{Number: newInt(13)}}, {BlockOverrides: &override.BlockOverrides{Number: newInt(12)}}}, err: "block numbers must be in order: 12 <= 13", }, { baseNumber: 10, baseTimestamp: 50, - blocks: []simBlock{{BlockOverrides: &BlockOverrides{Number: newInt(13), Time: newUint64(52)}}}, + blocks: []simBlock{{BlockOverrides: &override.BlockOverrides{Number: newInt(13), Time: newUint64(52)}}}, err: "block timestamps must be in order: 52 <= 52", }, { baseNumber: 10, baseTimestamp: 50, - blocks: []simBlock{{BlockOverrides: &BlockOverrides{Number: newInt(11), Time: newUint64(60)}}, {BlockOverrides: &BlockOverrides{Number: newInt(12), Time: newUint64(55)}}}, + blocks: []simBlock{{BlockOverrides: &override.BlockOverrides{Number: newInt(11), Time: newUint64(60)}}, {BlockOverrides: &override.BlockOverrides{Number: newInt(12), Time: newUint64(55)}}}, err: "block timestamps must be in order: 55 <= 60", }, { baseNumber: 10, baseTimestamp: 50, - blocks: []simBlock{{BlockOverrides: &BlockOverrides{Number: newInt(11), Time: newUint64(60)}}, {BlockOverrides: &BlockOverrides{Number: newInt(13), Time: newUint64(61)}}}, + blocks: []simBlock{{BlockOverrides: &override.BlockOverrides{Number: newInt(11), Time: newUint64(60)}}, {BlockOverrides: &override.BlockOverrides{Number: newInt(13), Time: newUint64(61)}}}, err: "block timestamps must be in order: 61 <= 61", }, } { 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.go b/internal/ethapi/transaction_args.go index f9835a96da..a39a6666f4 100644 --- a/internal/ethapi/transaction_args.go +++ b/internal/ethapi/transaction_args.go @@ -22,11 +22,11 @@ import ( "crypto/sha256" "errors" "fmt" + "math" "math/big" "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common/hexutil" - "github.com/ethereum/go-ethereum/common/math" "github.com/ethereum/go-ethereum/consensus/misc/eip4844" "github.com/ethereum/go-ethereum/core" "github.com/ethereum/go-ethereum/core/types" @@ -72,6 +72,9 @@ type TransactionArgs struct { Commitments []kzg4844.Commitment `json:"commitments"` Proofs []kzg4844.Proof `json:"proofs"` + // For SetCodeTxType + AuthorizationList []types.SetCodeAuthorization `json:"authorizationList"` + // This configures whether blobs are allowed to be passed. blobSidecarAllowed bool } @@ -100,7 +103,7 @@ func (args *TransactionArgs) setDefaults(ctx context.Context, b Backend, skipGas if err := args.setBlobTxSidecar(ctx); err != nil { return err } - if err := args.setFeeDefaults(ctx, b); err != nil { + if err := args.setFeeDefaults(ctx, b, b.CurrentHeader()); err != nil { return err } @@ -160,7 +163,7 @@ func (args *TransactionArgs) setDefaults(ctx context.Context, b Backend, skipGas BlobHashes: args.BlobHashes, } latestBlockNr := rpc.BlockNumberOrHashWithNumber(rpc.LatestBlockNumber) - estimated, err := DoEstimateGas(ctx, b, callArgs, latestBlockNr, nil, b.RPCGasCap()) + estimated, err := DoEstimateGas(ctx, b, callArgs, latestBlockNr, nil, nil, b.RPCGasCap()) if err != nil { return err } @@ -183,15 +186,12 @@ func (args *TransactionArgs) setDefaults(ctx context.Context, b Backend, skipGas } // setFeeDefaults fills in default fee values for unspecified tx fields. -func (args *TransactionArgs) setFeeDefaults(ctx context.Context, b Backend) error { - head := b.CurrentHeader() +func (args *TransactionArgs) setFeeDefaults(ctx context.Context, b Backend, head *types.Header) error { // Sanity check the EIP-4844 fee parameters. if args.BlobFeeCap != nil && args.BlobFeeCap.ToInt().Sign() == 0 { return errors.New("maxFeePerBlobGas, if specified, must be non-zero") } - if err := args.setCancunFeeDefaults(ctx, head, b); err != nil { - return err - } + args.setCancunFeeDefaults(head) // If both gasPrice and at least one of the EIP-1559 fee parameters are specified, error. if args.GasPrice != nil && (args.MaxFeePerGas != nil || args.MaxPriorityFeePerGas != nil) { return errors.New("both gasPrice and (maxFeePerGas or maxPriorityFeePerGas) specified") @@ -243,7 +243,7 @@ func (args *TransactionArgs) setFeeDefaults(ctx context.Context, b Backend) erro } // setCancunFeeDefaults fills in reasonable default fee values for unspecified fields. -func (args *TransactionArgs) setCancunFeeDefaults(ctx context.Context, head *types.Header, b Backend) error { +func (args *TransactionArgs) setCancunFeeDefaults(head *types.Header) { // Set maxFeePerBlobGas if it is missing. if args.BlobHashes != nil && args.BlobFeeCap == nil { var excessBlobGas uint64 @@ -258,7 +258,6 @@ func (args *TransactionArgs) setCancunFeeDefaults(ctx context.Context, head *typ val := new(big.Int).Mul(blobBaseFee, big.NewInt(2)) args.BlobFeeCap = (*hexutil.Big)(val) } - return nil } // setLondonFeeDefaults fills in reasonable default fee values for unspecified fields. @@ -443,7 +442,10 @@ func (args *TransactionArgs) ToMessage(baseFee *big.Int, skipNonceCheck, skipEoA // Backfill the legacy gasPrice for EVM execution, unless we're all zeroes gasPrice = new(big.Int) if gasFeeCap.BitLen() > 0 || gasTipCap.BitLen() > 0 { - gasPrice = math.BigMin(new(big.Int).Add(gasTipCap, baseFee), gasFeeCap) + gasPrice = gasPrice.Add(gasTipCap, baseFee) + if gasPrice.Cmp(gasFeeCap) > 0 { + gasPrice = gasFeeCap + } } } } @@ -452,20 +454,21 @@ func (args *TransactionArgs) ToMessage(baseFee *big.Int, skipNonceCheck, skipEoA accessList = *args.AccessList } return &core.Message{ - From: args.from(), - To: args.To, - Value: (*big.Int)(args.Value), - Nonce: uint64(*args.Nonce), - GasLimit: uint64(*args.Gas), - GasPrice: gasPrice, - GasFeeCap: gasFeeCap, - GasTipCap: gasTipCap, - Data: args.data(), - AccessList: accessList, - BlobGasFeeCap: (*big.Int)(args.BlobFeeCap), - BlobHashes: args.BlobHashes, - SkipNonceChecks: skipNonceCheck, - SkipFromEOACheck: skipEoACheck, + From: args.from(), + To: args.To, + Value: (*big.Int)(args.Value), + Nonce: uint64(*args.Nonce), + GasLimit: uint64(*args.Gas), + GasPrice: gasPrice, + GasFeeCap: gasFeeCap, + GasTipCap: gasTipCap, + Data: args.data(), + AccessList: accessList, + BlobGasFeeCap: (*big.Int)(args.BlobFeeCap), + BlobHashes: args.BlobHashes, + SetCodeAuthorizations: args.AuthorizationList, + SkipNonceChecks: skipNonceCheck, + SkipFromEOACheck: skipEoACheck, } } @@ -474,6 +477,8 @@ func (args *TransactionArgs) ToMessage(baseFee *big.Int, skipNonceCheck, skipEoA func (args *TransactionArgs) ToTransaction(defaultType int) *types.Transaction { usedType := types.LegacyTxType switch { + case args.AuthorizationList != nil || defaultType == types.SetCodeTxType: + usedType = types.SetCodeTxType case args.BlobHashes != nil || defaultType == types.BlobTxType: usedType = types.BlobTxType case args.MaxFeePerGas != nil || defaultType == types.DynamicFeeTxType: @@ -487,6 +492,28 @@ func (args *TransactionArgs) ToTransaction(defaultType int) *types.Transaction { } var data types.TxData switch usedType { + case types.SetCodeTxType: + al := types.AccessList{} + if args.AccessList != nil { + al = *args.AccessList + } + authList := []types.SetCodeAuthorization{} + if args.AuthorizationList != nil { + authList = args.AuthorizationList + } + data = &types.SetCodeTx{ + To: *args.To, + ChainID: args.ChainID.ToInt().Uint64(), + Nonce: uint64(*args.Nonce), + Gas: uint64(*args.Gas), + GasFeeCap: uint256.MustFromBig((*big.Int)(args.MaxFeePerGas)), + GasTipCap: uint256.MustFromBig((*big.Int)(args.MaxPriorityFeePerGas)), + Value: uint256.MustFromBig((*big.Int)(args.Value)), + Data: args.data(), + AccessList: al, + AuthList: authList, + } + case types.BlobTxType: al := types.AccessList{} if args.AccessList != nil { diff --git a/internal/ethapi/transaction_args_test.go b/internal/ethapi/transaction_args_test.go index 5317828173..7172fc883f 100644 --- a/internal/ethapi/transaction_args_test.go +++ b/internal/ethapi/transaction_args_test.go @@ -238,7 +238,7 @@ func TestSetFeeDefaults(t *testing.T) { t.Fatalf("failed to set fork: %v", err) } got := test.in - err := got.setFeeDefaults(ctx, b) + err := got.setFeeDefaults(ctx, b, b.CurrentHeader()) if err != nil { if test.err == nil { t.Fatalf("test %d (%s): unexpected error: %s", i, test.name, err) @@ -370,16 +370,13 @@ 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 } func (b *backendMock) SubscribeChainHeadEvent(ch chan<- core.ChainHeadEvent) event.Subscription { return nil } -func (b *backendMock) SubscribeChainSideEvent(ch chan<- core.ChainSideEvent) event.Subscription { - return nil -} func (b *backendMock) SendTx(ctx context.Context, signedTx *types.Transaction) error { return nil } func (b *backendMock) GetTransaction(ctx context.Context, txHash common.Hash) (bool, *types.Transaction, common.Hash, uint64, uint64, error) { return false, nil, [32]byte{}, 0, 0, nil diff --git a/internal/flags/flags.go b/internal/flags/flags.go index bf62c53adf..b858e73d04 100644 --- a/internal/flags/flags.go +++ b/internal/flags/flags.go @@ -17,7 +17,6 @@ package flags import ( - "encoding" "errors" "flag" "fmt" @@ -91,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 } @@ -122,119 +121,6 @@ func (f *DirectoryFlag) GetDefaultText() string { return f.GetValue() } -type TextMarshaler interface { - encoding.TextMarshaler - encoding.TextUnmarshaler -} - -// textMarshalerVal turns a TextMarshaler into a flag.Value -type textMarshalerVal struct { - v TextMarshaler -} - -func (v textMarshalerVal) String() string { - if v.v == nil { - return "" - } - text, _ := v.v.MarshalText() - return string(text) -} - -func (v textMarshalerVal) Set(s string) error { - return v.v.UnmarshalText([]byte(s)) -} - -var ( - _ cli.Flag = (*TextMarshalerFlag)(nil) - _ cli.RequiredFlag = (*TextMarshalerFlag)(nil) - _ cli.VisibleFlag = (*TextMarshalerFlag)(nil) - _ cli.DocGenerationFlag = (*TextMarshalerFlag)(nil) - _ cli.CategorizableFlag = (*TextMarshalerFlag)(nil) -) - -// TextMarshalerFlag wraps a TextMarshaler value. -type TextMarshalerFlag struct { - Name string - - Category string - DefaultText string - Usage string - - Required bool - Hidden bool - HasBeenSet bool - - Value TextMarshaler - - Aliases []string - EnvVars []string -} - -// For cli.Flag: - -func (f *TextMarshalerFlag) Names() []string { return append([]string{f.Name}, f.Aliases...) } -func (f *TextMarshalerFlag) IsSet() bool { return f.HasBeenSet } -func (f *TextMarshalerFlag) String() string { return cli.FlagStringer(f) } - -func (f *TextMarshalerFlag) Apply(set *flag.FlagSet) error { - for _, envVar := range f.EnvVars { - envVar = strings.TrimSpace(envVar) - if value, found := syscall.Getenv(envVar); found { - if err := f.Value.UnmarshalText([]byte(value)); err != nil { - return fmt.Errorf("could not parse %q from environment variable %q for flag %s: %s", value, envVar, f.Name, err) - } - f.HasBeenSet = true - break - } - } - eachName(f, func(name string) { - set.Var(textMarshalerVal{f.Value}, f.Name, f.Usage) - }) - return nil -} - -// For cli.RequiredFlag: - -func (f *TextMarshalerFlag) IsRequired() bool { return f.Required } - -// For cli.VisibleFlag: - -func (f *TextMarshalerFlag) IsVisible() bool { return !f.Hidden } - -// For cli.CategorizableFlag: - -func (f *TextMarshalerFlag) GetCategory() string { return f.Category } - -// For cli.DocGenerationFlag: - -func (f *TextMarshalerFlag) TakesValue() bool { return true } -func (f *TextMarshalerFlag) GetUsage() string { return f.Usage } -func (f *TextMarshalerFlag) GetEnvVars() []string { return f.EnvVars } - -func (f *TextMarshalerFlag) GetValue() string { - t, err := f.Value.MarshalText() - if err != nil { - return "(ERR: " + err.Error() + ")" - } - return string(t) -} - -func (f *TextMarshalerFlag) GetDefaultText() string { - if f.DefaultText != "" { - return f.DefaultText - } - return f.GetValue() -} - -// GlobalTextMarshaler returns the value of a TextMarshalerFlag from the global flag set. -func GlobalTextMarshaler(ctx *cli.Context, name string) TextMarshaler { - val := ctx.Generic(name) - if val == nil { - return nil - } - return val.(textMarshalerVal).v -} - var ( _ cli.Flag = (*BigFlag)(nil) _ cli.RequiredFlag = (*BigFlag)(nil) @@ -286,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/internal/flags/helpers.go b/internal/flags/helpers.go index 0112724fa1..fd706869f1 100644 --- a/internal/flags/helpers.go +++ b/internal/flags/helpers.go @@ -25,7 +25,6 @@ import ( "github.com/ethereum/go-ethereum/internal/version" "github.com/ethereum/go-ethereum/log" - "github.com/ethereum/go-ethereum/params" "github.com/mattn/go-isatty" "github.com/urfave/cli/v2" ) @@ -39,7 +38,7 @@ func NewApp(usage string) *cli.App { git, _ := version.VCS() app := cli.NewApp() app.EnableBashCompletion = true - app.Version = params.VersionWithCommit(git.Commit, git.Date) + app.Version = version.WithCommit(git.Commit, git.Date) app.Usage = usage app.Copyright = "Copyright 2013-2024 The go-ethereum Authors" app.Before = func(ctx *cli.Context) error { @@ -49,15 +48,6 @@ func NewApp(usage string) *cli.App { return app } -// Merge merges the given flag slices. -func Merge(groups ...[]cli.Flag) []cli.Flag { - var ret []cli.Flag - for _, group := range groups { - ret = append(ret, group...) - } - return ret -} - var migrationApplied = map[*cli.Command]struct{}{} // MigrateGlobalFlags makes all global flag values available in the @@ -266,9 +256,6 @@ func AutoEnvVars(flags []cli.Flag, prefix string) { case *BigFlag: flag.EnvVars = append(flag.EnvVars, envvar) - case *TextMarshalerFlag: - flag.EnvVars = append(flag.EnvVars, envvar) - case *DirectoryFlag: flag.EnvVars = append(flag.EnvVars, envvar) } diff --git a/internal/version/vcs.go b/internal/version/vcs.go index 21de8946e8..7ee87bb1b9 100644 --- a/internal/version/vcs.go +++ b/internal/version/vcs.go @@ -28,6 +28,31 @@ const ( ourTimeLayout = "20060102" ) +// These variables are set at build-time by the linker when the build is +// done by build/ci.go. +var gitCommit, gitDate string + +// VCSInfo represents the git repository state. +type VCSInfo struct { + Commit string // head commit hash + Date string // commit time in YYYYMMDD format + Dirty bool +} + +// VCS returns version control information of the current executable. +func VCS() (VCSInfo, bool) { + if gitCommit != "" { + // Use information set by the build script if present. + return VCSInfo{Commit: gitCommit, Date: gitDate}, true + } + if buildInfo, ok := debug.ReadBuildInfo(); ok { + if buildInfo.Main.Path == ourPath { + return buildInfoVCS(buildInfo) + } + } + return VCSInfo{}, false +} + // buildInfoVCS returns VCS information of the build. func buildInfoVCS(info *debug.BuildInfo) (s VCSInfo, ok bool) { for _, v := range info.Settings { diff --git a/internal/version/version.go b/internal/version/version.go index 2cca54b20f..a667827273 100644 --- a/internal/version/version.go +++ b/internal/version/version.go @@ -23,34 +23,49 @@ import ( "runtime/debug" "strings" - "github.com/ethereum/go-ethereum/params" + "github.com/ethereum/go-ethereum/version" ) const ourPath = "github.com/ethereum/go-ethereum" // Path to our module -// These variables are set at build-time by the linker when the build is -// done by build/ci.go. -var gitCommit, gitDate string +// Family holds the textual version string for major.minor +var Family = fmt.Sprintf("%d.%d", version.Major, version.Minor) -// VCSInfo represents the git repository state. -type VCSInfo struct { - Commit string // head commit hash - Date string // commit time in YYYYMMDD format - Dirty bool +// Semantic holds the textual version string for major.minor.patch. +var Semantic = fmt.Sprintf("%d.%d.%d", version.Major, version.Minor, version.Patch) + +// WithMeta holds the textual version string including the metadata. +var WithMeta = func() string { + v := Semantic + if version.Meta != "" { + v += "-" + version.Meta + } + return v +}() + +func WithCommit(gitCommit, gitDate string) string { + vsn := WithMeta + if len(gitCommit) >= 8 { + vsn += "-" + gitCommit[:8] + } + if (version.Meta != "stable") && (gitDate != "") { + vsn += "-" + gitDate + } + return vsn } -// VCS returns version control information of the current executable. -func VCS() (VCSInfo, bool) { - if gitCommit != "" { - // Use information set by the build script if present. - return VCSInfo{Commit: gitCommit, Date: gitDate}, true +// Archive holds the textual version string used for Geth archives. e.g. +// "1.8.11-dea1ce05" for stable releases, or "1.8.13-unstable-21c059b6" for unstable +// releases. +func Archive(gitCommit string) string { + vsn := Semantic + if version.Meta != "stable" { + vsn += "-" + version.Meta } - if buildInfo, ok := debug.ReadBuildInfo(); ok { - if buildInfo.Main.Path == ourPath { - return buildInfoVCS(buildInfo) - } + if len(gitCommit) >= 8 { + vsn += "-" + gitCommit[:8] } - return VCSInfo{}, false + return vsn } // ClientName creates a software name/version identifier according to common @@ -59,7 +74,7 @@ func ClientName(clientIdentifier string) string { git, _ := VCS() return fmt.Sprintf("%s/v%v/%v-%v/%v", strings.Title(clientIdentifier), - params.VersionWithCommit(git.Commit, git.Date), + WithCommit(git.Commit, git.Date), runtime.GOOS, runtime.GOARCH, runtime.Version(), ) @@ -72,7 +87,7 @@ func ClientName(clientIdentifier string) string { // it will assume it's imported by a third-party and will return the imported // version and whether it was replaced by another module. func Info() (version, vcs string) { - version = params.VersionWithMeta + version = WithMeta buildInfo, ok := debug.ReadBuildInfo() if !ok { return version, "" @@ -115,7 +130,7 @@ func versionInfo(info *debug.BuildInfo) string { // If our module path wasn't imported, it's unclear which // version of our code they are running. Fallback to hardcoded // version. - return version + fmt.Sprintf("geth %s", params.VersionWithMeta) + return version + fmt.Sprintf("geth %s", WithMeta) } // Our package is a dependency for the main module. Return path and // version data for both. diff --git a/internal/web3ext/web3ext.go b/internal/web3ext/web3ext.go index 927ebc2ef0..8ac8f44958 100644 --- a/internal/web3ext/web3ext.go +++ b/internal/web3ext/web3ext.go @@ -18,16 +18,15 @@ package web3ext var Modules = map[string]string{ - "admin": AdminJs, - "clique": CliqueJs, - "debug": DebugJs, - "eth": EthJs, - "miner": MinerJs, - "net": NetJs, - "personal": PersonalJs, - "rpc": RpcJs, - "txpool": TxpoolJs, - "dev": DevJs, + "admin": AdminJs, + "clique": CliqueJs, + "debug": DebugJs, + "eth": EthJs, + "miner": MinerJs, + "net": NetJs, + "rpc": RpcJs, + "txpool": TxpoolJs, + "dev": DevJs, } const CliqueJs = ` @@ -504,8 +503,8 @@ web3._extend({ new web3._extend.Method({ name: 'estimateGas', call: 'eth_estimateGas', - params: 3, - inputFormatter: [web3._extend.formatters.inputCallFormatter, web3._extend.formatters.inputBlockNumberFormatter, null], + params: 4, + inputFormatter: [web3._extend.formatters.inputCallFormatter, web3._extend.formatters.inputBlockNumberFormatter, null, null], outputFormatter: web3._extend.utils.toDecimal }), new web3._extend.Method({ @@ -658,62 +657,6 @@ web3._extend({ }); ` -const PersonalJs = ` -web3._extend({ - property: 'personal', - methods: [ - new web3._extend.Method({ - name: 'importRawKey', - call: 'personal_importRawKey', - params: 2 - }), - new web3._extend.Method({ - name: 'sign', - call: 'personal_sign', - params: 3, - inputFormatter: [null, web3._extend.formatters.inputAddressFormatter, null] - }), - new web3._extend.Method({ - name: 'ecRecover', - call: 'personal_ecRecover', - params: 2 - }), - new web3._extend.Method({ - name: 'openWallet', - call: 'personal_openWallet', - params: 2 - }), - new web3._extend.Method({ - name: 'deriveAccount', - call: 'personal_deriveAccount', - params: 3 - }), - new web3._extend.Method({ - name: 'signTransaction', - call: 'personal_signTransaction', - params: 2, - inputFormatter: [web3._extend.formatters.inputTransactionFormatter, null] - }), - new web3._extend.Method({ - name: 'unpair', - call: 'personal_unpair', - params: 2 - }), - new web3._extend.Method({ - name: 'initializeWallet', - call: 'personal_initializeWallet', - params: 1 - }) - ], - properties: [ - new web3._extend.Property({ - name: 'listWallets', - getter: 'personal_listWallets' - }), - ] -}) -` - const RpcJs = ` web3._extend({ property: 'rpc', diff --git a/log/logger_test.go b/log/logger_test.go index f1a9a93bce..3ec6d2e19c 100644 --- a/log/logger_test.go +++ b/log/logger_test.go @@ -7,7 +7,6 @@ import ( "io" "log/slog" "math/big" - "os" "strings" "testing" "time" @@ -70,7 +69,7 @@ func TestJSONHandler(t *testing.T) { } func BenchmarkTraceLogging(b *testing.B) { - SetDefault(NewLogger(NewTerminalHandler(os.Stderr, true))) + SetDefault(NewLogger(NewTerminalHandler(io.Discard, true))) b.ResetTimer() for i := 0; i < b.N; i++ { Trace("a message", "v", i) 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 { + if r == nil { 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 { - 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) } +func (c CounterSnapshot) Count() int64 { return int64(c) } -// NilCounter is a no-op Counter. -type NilCounter struct{} - -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) + 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 CounterFloat64. +func NewCounterFloat64() *CounterFloat64 { + return new(CounterFloat64) } -// NewCounterFloat64 constructs a new StandardCounterFloat64. -func NewCounterFloat64() CounterFloat64 { - if !Enabled { - return NilCounterFloat64{} - } - return &StandardCounterFloat64{} -} - -// NewCounterFloat64Forced constructs a new StandardCounterFloat64 and returns it no matter if -// the global switch is enabled or not. -func NewCounterFloat64Forced() CounterFloat64 { - return &StandardCounterFloat64{} -} - -// 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 { + if r == nil { 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 { - 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) } +func (c CounterFloat64Snapshot) Count() float64 { return float64(c) } -type NilCounterFloat64 struct{} - -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() - c.Dec(2.0) - if count := c.Snapshot().Count(); count != -2.0 { - t.Errorf("c.Count(): -2.0 != %v\n", 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) - } -} - -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) - } -} - -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) + c.Dec(2.0) + if count := c.Snapshot().Count(); count != -3.0 { + t.Errorf("wrong count: %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) + if count := c.Snapshot().Count(); count != -2.0 { + t.Errorf("wrong count: %v", count) + } + c.Inc(2.0) + if count := c.Snapshot().Count(); count != 0.0 { + t.Errorf("wrong count: %v", count) + } + if count := snapshot.Count(); count != -1.0 { + t.Errorf("snapshot count wrong: %v", 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 7e3f82a075..5213979aa2 100644 --- a/metrics/exp/exp.go +++ b/metrics/exp/exp.go @@ -146,7 +146,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()) @@ -155,7 +155,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()) @@ -174,7 +174,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())) @@ -188,23 +188,23 @@ func (exp *exp) publishResettingTimer(name string, metric metrics.ResettingTimer 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) default: panic(fmt.Sprintf("unsupported type for '%s': %T", 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) } +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{} } - -// 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 5879af7cf6..547da138b8 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 } +func (m *MeterSnapshot) RateMean() float64 { return m.rateMean } -// NilMeter is a no-op Meter. -type NilMeter struct{} - -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 c7fe5c7333..a9d6623173 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" ) -// Enabled is checked by the constructor functions for all of the -// standard metrics. If it is true, the metric returned is a stub. +var ( + metricsEnabled = false +) + +// 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) + var wg sync.WaitGroup + wg.Add(128) + for i := 0; i < 128; i++ { 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) - 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 353336763b..31b8c51b65 100644 --- a/metrics/prometheus/collector.go +++ b/metrics/prometheus/collector.go @@ -51,23 +51,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()) default: return fmt.Errorf("unknown prometheus metric type %T", i) @@ -106,11 +106,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()) @@ -121,7 +121,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 ca4741feef..527da6238d 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: + case *Counter, *CounterFloat64, *Gauge, *GaugeFloat64, *GaugeInfo, *Healthcheck, Histogram, *Meter, *Timer, *ResettingTimer: 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,178 +10,21 @@ 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) } -// 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". -// -// -type ExpDecaySample struct { - alpha float64 - count int64 - mutex sync.Mutex - reservoirSize int - t0, t1 time.Time - values *expDecaySampleHeap - rand *rand.Rand -} +var ( + _ Sample = (*ExpDecaySample)(nil) + _ Sample = (*UniformSample)(nil) + _ Sample = (*resettingSample)(nil) +) -// 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, - t0: time.Now(), - values: newExpDecaySampleHeap(reservoirSize), - } - s.t1 = s.t0.Add(rescaleThreshold) - return s -} - -// SetRand sets the random source (useful in tests) -func (s *ExpDecaySample) SetRand(prng *rand.Rand) Sample { - s.rand = prng - return s -} - -// Clear clears all samples. -func (s *ExpDecaySample) Clear() { - s.mutex.Lock() - defer s.mutex.Unlock() - s.count = 0 - s.t0 = time.Now() - s.t1 = s.t0.Add(rescaleThreshold) - s.values.Clear() -} - -// Snapshot returns a read-only copy of the sample. -func (s *ExpDecaySample) Snapshot() SampleSnapshot { - s.mutex.Lock() - defer s.mutex.Unlock() - var ( - samples = s.values.Values() - values = make([]int64, len(samples)) - max int64 = math.MinInt64 - min int64 = math.MaxInt64 - sum int64 - ) - for i, item := range samples { - v := item.v - values[i] = v - sum += v - if v > max { - max = v - } - if v < min { - min = v - } - } - return newSampleSnapshotPrecalculated(s.count, values, min, max, sum) -} - -// Update samples a new value. -func (s *ExpDecaySample) Update(v int64) { - s.update(time.Now(), v) -} - -// update samples a new value at a particular timestamp. This is a method all -// its own to facilitate testing. -func (s *ExpDecaySample) update(t time.Time, v int64) { - s.mutex.Lock() - defer s.mutex.Unlock() - s.count++ - if s.values.Size() == s.reservoirSize { - s.values.Pop() - } - var f64 float64 - if s.rand != nil { - f64 = s.rand.Float64() - } else { - f64 = rand.Float64() - } - s.values.Push(expDecaySample{ - k: math.Exp(t.Sub(s.t0).Seconds()*s.alpha) / f64, - v: v, - }) - if t.After(s.t1) { - values := s.values.Values() - t0 := s.t0 - s.values.Clear() - s.t0 = t - s.t1 = s.t0.Add(rescaleThreshold) - for _, v := range values { - v.k = v.k * math.Exp(-s.alpha*s.t0.Sub(t0).Seconds()) - s.values.Push(v) - } - } -} - -// 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] -} - -// CalculatePercentiles returns a slice of arbitrary percentiles of the slice of -// int64. This method returns interpolated results, so e.g. if there are only two -// values, [0, 10], a 50% percentile will land between them. -// -// Note: As a side-effect, this method will also sort the slice of values. -// Note2: The input format for percentiles is NOT percent! To express 50%, use 0.5, not 50. -func CalculatePercentiles(values []int64, ps []float64) []float64 { - scores := make([]float64, len(ps)) - size := len(values) - if size == 0 { - return scores - } - slices.Sort(values) - for i, p := range ps { - pos := p * float64(size+1) - - if pos < 1.0 { - scores[i] = float64(values[0]) - } else if pos >= float64(size) { - scores[i] = float64(values[size-1]) - } else { - lower := float64(values[int(pos)-1]) - upper := float64(values[int(pos)]) - scores[i] = lower + (pos-math.Floor(pos))*(upper-lower) - } - } - return scores -} - -// sampleSnapshot is a read-only copy of another Sample. +// sampleSnapshot is a read-only copy of a Sample. type sampleSnapshot struct { count int64 values []int64 @@ -259,9 +102,6 @@ func (s *sampleSnapshot) Percentiles(ps []float64) []float64 { // 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 { @@ -276,9 +116,7 @@ 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 + return slices.Clone(s.values) } // Variance returns the variance of values at the time the snapshot was taken. @@ -289,6 +127,149 @@ func (s *sampleSnapshot) Variance() float64 { 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". +// +// +type ExpDecaySample struct { + alpha float64 + count int64 + mutex sync.Mutex + reservoirSize int + t0, t1 time.Time + values *expDecaySampleHeap + rand *rand.Rand +} + +// NewExpDecaySample constructs a new exponentially-decaying sample with the +// given reservoir size and alpha. +func NewExpDecaySample(reservoirSize int, alpha float64) Sample { + s := &ExpDecaySample{ + alpha: alpha, + reservoirSize: reservoirSize, + t0: time.Now(), + values: newExpDecaySampleHeap(reservoirSize), + } + s.t1 = s.t0.Add(rescaleThreshold) + return s +} + +// SetRand sets the random source (useful in tests) +func (s *ExpDecaySample) SetRand(prng *rand.Rand) Sample { + s.rand = prng + return s +} + +// Clear clears all samples. +func (s *ExpDecaySample) Clear() { + s.mutex.Lock() + defer s.mutex.Unlock() + s.count = 0 + s.t0 = time.Now() + s.t1 = s.t0.Add(rescaleThreshold) + s.values.Clear() +} + +// Snapshot returns a read-only copy of the sample. +func (s *ExpDecaySample) Snapshot() *sampleSnapshot { + s.mutex.Lock() + defer s.mutex.Unlock() + var ( + samples = s.values.Values() + values = make([]int64, len(samples)) + max int64 = math.MinInt64 + min int64 = math.MaxInt64 + sum int64 + ) + for i, item := range samples { + v := item.v + values[i] = v + sum += v + if v > max { + max = v + } + if v < min { + min = v + } + } + return newSampleSnapshotPrecalculated(s.count, values, min, max, sum) +} + +// Update samples a new value. +func (s *ExpDecaySample) Update(v int64) { + if !metricsEnabled { + return + } + s.update(time.Now(), v) +} + +// update samples a new value at a particular timestamp. This is a method all +// its own to facilitate testing. +func (s *ExpDecaySample) update(t time.Time, v int64) { + s.mutex.Lock() + defer s.mutex.Unlock() + s.count++ + if s.values.Size() == s.reservoirSize { + s.values.Pop() + } + var f64 float64 + if s.rand != nil { + f64 = s.rand.Float64() + } else { + f64 = rand.Float64() + } + s.values.Push(expDecaySample{ + k: math.Exp(t.Sub(s.t0).Seconds()*s.alpha) / f64, + v: v, + }) + if t.After(s.t1) { + values := s.values.Values() + t0 := s.t0 + s.values.Clear() + s.t0 = t + s.t1 = s.t0.Add(rescaleThreshold) + for _, v := range values { + v.k = v.k * math.Exp(-s.alpha*s.t0.Sub(t0).Seconds()) + s.values.Push(v) + } + } +} + +// SamplePercentile returns an arbitrary percentile of the slice of int64. +func SamplePercentile(values []int64, p float64) float64 { + return CalculatePercentiles(values, []float64{p})[0] +} + +// CalculatePercentiles returns a slice of arbitrary percentiles of the slice of +// int64. This method returns interpolated results, so e.g. if there are only two +// values, [0, 10], a 50% percentile will land between them. +// +// Note: As a side-effect, this method will also sort the slice of values. +// Note2: The input format for percentiles is NOT percent! To express 50%, use 0.5, not 50. +func CalculatePercentiles(values []int64, ps []float64) []float64 { + scores := make([]float64, len(ps)) + size := len(values) + if size == 0 { + return scores + } + slices.Sort(values) + for i, p := range ps { + pos := p * float64(size+1) + + if pos < 1.0 { + scores[i] = float64(values[0]) + } else if pos >= float64(size) { + scores[i] = float64(values[size-1]) + } else { + lower := float64(values[int(pos)-1]) + upper := float64(values[int(pos)]) + scores[i] = lower + (pos-math.Floor(pos))*(upper-lower) + } + } + return scores +} + // 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 4227b43ef7..6619eb1e9e 100644 --- a/metrics/sample_test.go +++ b/metrics/sample_test.go @@ -3,7 +3,6 @@ package metrics import ( "math" "math/rand" - "runtime" "testing" "time" ) @@ -27,6 +26,7 @@ func BenchmarkCompute1000(b *testing.B) { SampleVariance(mean, s) } } + func BenchmarkCompute1000000(b *testing.B) { s := make([]int64, 1000000) var sum int64 @@ -40,28 +40,6 @@ func BenchmarkCompute1000000(b *testing.B) { SampleVariance(mean, s) } } -func BenchmarkCopy1000(b *testing.B) { - s := make([]int64, 1000) - for i := 0; i < len(s); i++ { - s[i] = int64(i) - } - b.ResetTimer() - for i := 0; i < b.N; i++ { - sCopy := make([]int64, len(s)) - copy(sCopy, s) - } -} -func BenchmarkCopy1000000(b *testing.B) { - s := make([]int64, 1000000) - for i := 0; i < len(s); i++ { - s[i] = int64(i) - } - b.ResetTimer() - for i := 0; i < b.N; i++ { - sCopy := make([]int64, len(s)) - copy(sCopy, s) - } -} func BenchmarkExpDecaySample257(b *testing.B) { benchmarkSample(b, NewExpDecaySample(257, 0.015)) @@ -108,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) } @@ -133,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]) @@ -188,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) @@ -206,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++ { @@ -237,20 +213,12 @@ func TestUniformSampleStatistics(t *testing.T) { } func benchmarkSample(b *testing.B, s Sample) { - var memStats runtime.MemStats - runtime.ReadMemStats(&memStats) - pauseTotalNs := memStats.PauseTotalNs - b.ResetTimer() for i := 0; i < b.N; i++ { s.Update(1) } - b.StopTimer() - runtime.GC() - runtime.ReadMemStats(&memStats) - b.Logf("GC cost: %d ns/op", int(memStats.PauseTotalNs-pauseTotalNs)/b.N) } -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) } @@ -281,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/payload_building.go b/miner/payload_building.go index 1260d839c9..3090de5d4b 100644 --- a/miner/payload_building.go +++ b/miner/payload_building.go @@ -145,7 +145,7 @@ func (payload *Payload) Resolve() *engine.ExecutionPayloadEnvelope { close(payload.stop) } if payload.full != nil { - envelope := engine.BlockToExecutableData(payload.full, payload.fullFees, payload.sidecars, payload.emptyRequests) + envelope := engine.BlockToExecutableData(payload.full, payload.fullFees, payload.sidecars, payload.requests) if payload.fullWitness != nil { envelope.Witness = new(hexutil.Bytes) *envelope.Witness, _ = rlp.EncodeToBytes(payload.fullWitness) // cannot fail diff --git a/miner/payload_building_test.go b/miner/payload_building_test.go index aad87627e6..e5eb0297a1 100644 --- a/miner/payload_building_test.go +++ b/miner/payload_building_test.go @@ -22,7 +22,6 @@ import ( "testing" "time" - "github.com/ethereum/go-ethereum/accounts" "github.com/ethereum/go-ethereum/beacon/engine" "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/consensus" @@ -114,9 +113,7 @@ func newTestWorkerBackend(t *testing.T, chainConfig *params.ChainConfig, engine case *clique.Clique: gspec.ExtraData = make([]byte, 32+common.AddressLength+crypto.SignatureLength) copy(gspec.ExtraData[32:32+common.AddressLength], testBankAddress.Bytes()) - e.Authorize(testBankAddress, func(account accounts.Account, s string, data []byte) ([]byte, error) { - return crypto.Sign(crypto.Keccak256(data), testBankKey) - }) + e.Authorize(testBankAddress) case *ethash.Ethash: default: t.Fatalf("unexpected consensus engine type: %T", engine) diff --git a/miner/worker.go b/miner/worker.go index db2fac3871..b5aa080025 100644 --- a/miner/worker.go +++ b/miner/worker.go @@ -51,6 +51,7 @@ type environment struct { tcount int // tx count in cycle gasPool *core.GasPool // available gas used to pack transactions coinbase common.Address + evm *vm.EVM header *types.Header txs []*types.Transaction @@ -120,21 +121,15 @@ func (miner *Miner) generateWork(params *generateParams, witness bool) *newPaylo // Collect consensus-layer requests if Prague is enabled. var requests [][]byte if miner.chainConfig.IsPrague(work.header.Number, work.header.Time) { + requests = [][]byte{} // EIP-6110 deposits - depositRequests, err := core.ParseDepositLogs(allLogs, miner.chainConfig) - if err != nil { + if err := core.ParseDepositLogs(&requests, allLogs, miner.chainConfig); err != nil { return &newPayloadResult{err: err} } - requests = append(requests, depositRequests) - // create EVM for system calls - blockContext := core.NewEVMBlockContext(work.header, miner.chain, &work.header.Coinbase) - vmenv := vm.NewEVM(blockContext, vm.TxContext{}, work.state, miner.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) @@ -233,14 +228,10 @@ func (miner *Miner) prepareWork(genParams *generateParams, witness bool) (*envir return nil, err } if header.ParentBeaconRoot != nil { - context := core.NewEVMBlockContext(header, miner.chain, nil) - vmenv := vm.NewEVM(context, vm.TxContext{}, env.state, miner.chainConfig, vm.Config{}) - core.ProcessBeaconBlockRoot(*header.ParentBeaconRoot, vmenv, env.state) + core.ProcessBeaconBlockRoot(*header.ParentBeaconRoot, env.evm) } if miner.chainConfig.IsPrague(header.Number, header.Time) { - context := core.NewEVMBlockContext(header, miner.chain, nil) - vmenv := vm.NewEVM(context, vm.TxContext{}, env.state, miner.chainConfig, vm.Config{}) - core.ProcessParentBlockHash(header.ParentHash, vmenv, env.state) + core.ProcessParentBlockHash(header.ParentHash, env.evm) } return env, nil } @@ -266,6 +257,7 @@ func (miner *Miner) makeEnv(parent *types.Header, header *types.Header, coinbase coinbase: coinbase, header: header, witness: state.Witness(), + evm: vm.NewEVM(core.NewEVMBlockContext(header, miner.chain, &coinbase), state, miner.chainConfig, vm.Config{}), }, nil } @@ -314,7 +306,7 @@ func (miner *Miner) applyTransaction(env *environment, tx *types.Transaction) (* snap = env.state.Snapshot() gp = env.gasPool.Gas() ) - receipt, err := core.ApplyTransaction(miner.chainConfig, miner.chain, &env.coinbase, env.gasPool, env.state, env.header, tx, &env.header.GasUsed, vm.Config{}) + receipt, err := core.ApplyTransaction(env.evm, env.gasPool, env.state, env.header, tx, &env.header.GasUsed) if err != nil { env.state.RevertToSnapshot(snap) env.gasPool.SetGas(gp) diff --git a/node/config.go b/node/config.go index 949db887e4..dc436876cc 100644 --- a/node/config.go +++ b/node/config.go @@ -83,7 +83,7 @@ type Config struct { // scrypt KDF at the expense of security. UseLightweightKDF bool `toml:",omitempty"` - // InsecureUnlockAllowed allows user to unlock accounts in unsafe http environment. + // InsecureUnlockAllowed is a deprecated option to allow users to accounts in unsafe http environment. InsecureUnlockAllowed bool `toml:",omitempty"` // NoUSB disables hardware wallet monitoring and connectivity. diff --git a/node/database.go b/node/database.go new file mode 100644 index 0000000000..e3ccb91066 --- /dev/null +++ b/node/database.go @@ -0,0 +1,111 @@ +// 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 node + +import ( + "fmt" + + "github.com/ethereum/go-ethereum/core/rawdb" + "github.com/ethereum/go-ethereum/ethdb" + "github.com/ethereum/go-ethereum/ethdb/leveldb" + "github.com/ethereum/go-ethereum/ethdb/pebble" + "github.com/ethereum/go-ethereum/log" +) + +// openOptions contains the options to apply when opening a database. +// OBS: If AncientsDirectory is empty, it indicates that no freezer is to be used. +type openOptions struct { + Type string // "leveldb" | "pebble" + Directory string // the datadir + AncientsDirectory string // the ancients-dir + Namespace string // the namespace for database relevant metrics + Cache int // the capacity(in megabytes) of the data caching + Handles int // number of files to be open simultaneously + ReadOnly bool +} + +// openDatabase opens both a disk-based key-value database such as leveldb or pebble, but also +// integrates it with a freezer database -- if the AncientDir option has been +// set on the provided OpenOptions. +// The passed o.AncientDir indicates the path of root ancient directory where +// the chain freezer can be opened. +func openDatabase(o openOptions) (ethdb.Database, error) { + kvdb, err := openKeyValueDatabase(o) + if err != nil { + return nil, err + } + if len(o.AncientsDirectory) == 0 { + return kvdb, nil + } + frdb, err := rawdb.NewDatabaseWithFreezer(kvdb, o.AncientsDirectory, o.Namespace, o.ReadOnly) + if err != nil { + kvdb.Close() + return nil, err + } + return frdb, nil +} + +// openKeyValueDatabase opens a disk-based key-value database, e.g. leveldb or pebble. +// +// type == null type != null +// +---------------------------------------- +// db is non-existent | pebble default | specified type +// db is existent | from db | specified type (if compatible) +func openKeyValueDatabase(o openOptions) (ethdb.Database, error) { + // Reject any unsupported database type + if len(o.Type) != 0 && o.Type != rawdb.DBLeveldb && o.Type != rawdb.DBPebble { + return nil, fmt.Errorf("unknown db.engine %v", o.Type) + } + // Retrieve any pre-existing database's type and use that or the requested one + // as long as there's no conflict between the two types + existingDb := rawdb.PreexistingDatabase(o.Directory) + if len(existingDb) != 0 && len(o.Type) != 0 && o.Type != existingDb { + return nil, fmt.Errorf("db.engine choice was %v but found pre-existing %v database in specified data directory", o.Type, existingDb) + } + if o.Type == rawdb.DBPebble || existingDb == rawdb.DBPebble { + log.Info("Using pebble as the backing database") + return newPebbleDBDatabase(o.Directory, o.Cache, o.Handles, o.Namespace, o.ReadOnly) + } + if o.Type == rawdb.DBLeveldb || existingDb == rawdb.DBLeveldb { + log.Info("Using leveldb as the backing database") + return newLevelDBDatabase(o.Directory, o.Cache, o.Handles, o.Namespace, o.ReadOnly) + } + // No pre-existing database, no user-requested one either. Default to Pebble. + log.Info("Defaulting to pebble as the backing database") + return newPebbleDBDatabase(o.Directory, o.Cache, o.Handles, o.Namespace, o.ReadOnly) +} + +// newLevelDBDatabase creates a persistent key-value database without a freezer +// moving immutable chain segments into cold storage. +func newLevelDBDatabase(file string, cache int, handles int, namespace string, readonly bool) (ethdb.Database, error) { + db, err := leveldb.New(file, cache, handles, namespace, readonly) + if err != nil { + return nil, err + } + log.Info("Using LevelDB as the backing database") + return rawdb.NewDatabase(db), nil +} + +// newPebbleDBDatabase creates a persistent key-value database without a freezer +// moving immutable chain segments into cold storage. +func newPebbleDBDatabase(file string, cache int, handles int, namespace string, readonly bool) (ethdb.Database, error) { + db, err := pebble.New(file, cache, handles, namespace, readonly) + if err != nil { + return nil, err + } + return rawdb.NewDatabase(db), nil +} diff --git a/node/node.go b/node/node.go index 633f88f058..ec7382e725 100644 --- a/node/node.go +++ b/node/node.go @@ -130,7 +130,7 @@ func New(conf *Config) (*Node, error) { node.keyDirTemp = isEphem // Creates an empty AccountManager with no backends. Callers (e.g. cmd/geth) // are required to add the backends later on. - node.accman = accounts.NewManager(&accounts.Config{InsecureUnlockAllowed: conf.InsecureUnlockAllowed}) + node.accman = accounts.NewManager(nil) // Initialize the p2p server. This creates the node key and discovery databases. node.server.Config.PrivateKey = node.config.NodeKey() @@ -375,25 +375,13 @@ func (n *Node) obtainJWTSecret(cliParam string) ([]byte, error) { // startup. It's not meant to be called at any time afterwards as it makes certain // assumptions about the state of the node. func (n *Node) startRPC() error { - // Filter out personal api - var apis []rpc.API - for _, api := range n.rpcAPIs { - if api.Namespace == "personal" { - if n.config.EnablePersonal { - log.Warn("Deprecated personal namespace activated") - } else { - continue - } - } - apis = append(apis, api) - } - if err := n.startInProc(apis); err != nil { + if err := n.startInProc(n.rpcAPIs); err != nil { return err } // Configure IPC. if n.ipc.endpoint != "" { - if err := n.ipc.start(apis); err != nil { + if err := n.ipc.start(n.rpcAPIs); err != nil { return err } } @@ -723,7 +711,7 @@ func (n *Node) OpenDatabase(name string, cache, handles int, namespace string, r if n.config.DataDir == "" { db = rawdb.NewMemoryDatabase() } else { - db, err = rawdb.Open(rawdb.OpenOptions{ + db, err = openDatabase(openOptions{ Type: n.config.DBEngine, Directory: n.ResolvePath(name), Namespace: namespace, @@ -732,7 +720,6 @@ func (n *Node) OpenDatabase(name string, cache, handles int, namespace string, r ReadOnly: readonly, }) } - if err == nil { db = n.wrapDatabase(db) } @@ -755,7 +742,7 @@ func (n *Node) OpenDatabaseWithFreezer(name string, cache, handles int, ancient if n.config.DataDir == "" { db, err = rawdb.NewDatabaseWithFreezer(memorydb.New(), "", namespace, readonly) } else { - db, err = rawdb.Open(rawdb.OpenOptions{ + db, err = openDatabase(openOptions{ Type: n.config.DBEngine, Directory: n.ResolvePath(name), AncientsDirectory: n.ResolveAncient(name, ancient), @@ -765,7 +752,6 @@ func (n *Node) OpenDatabaseWithFreezer(name string, cache, handles int, ancient ReadOnly: readonly, }) } - if err == nil { db = n.wrapDatabase(db) } diff --git a/oss-fuzz.sh b/oss-fuzz.sh index 7993dc9c64..1f222c433b 100644 --- a/oss-fuzz.sh +++ b/oss-fuzz.sh @@ -160,6 +160,10 @@ compile_fuzzer github.com/ethereum/go-ethereum/tests/fuzzers/bls12381 \ FuzzG1Add fuzz_g1_add\ $repo/tests/fuzzers/bls12381/bls12381_test.go +compile_fuzzer github.com/ethereum/go-ethereum/tests/fuzzers/bls12381 \ + FuzzCrossG1Mul fuzz_cross_g1_mul\ + $repo/tests/fuzzers/bls12381/bls12381_test.go + compile_fuzzer github.com/ethereum/go-ethereum/tests/fuzzers/bls12381 \ FuzzG1Mul fuzz_g1_mul\ $repo/tests/fuzzers/bls12381/bls12381_test.go @@ -172,6 +176,10 @@ compile_fuzzer github.com/ethereum/go-ethereum/tests/fuzzers/bls12381 \ FuzzG2Add fuzz_g2_add \ $repo/tests/fuzzers/bls12381/bls12381_test.go +compile_fuzzer github.com/ethereum/go-ethereum/tests/fuzzers/bls12381 \ + FuzzCrossG2Mul fuzz_cross_g2_mul\ + $repo/tests/fuzzers/bls12381/bls12381_test.go + compile_fuzzer github.com/ethereum/go-ethereum/tests/fuzzers/bls12381 \ FuzzG2Mul fuzz_g2_mul\ $repo/tests/fuzzers/bls12381/bls12381_test.go @@ -204,6 +212,10 @@ compile_fuzzer github.com/ethereum/go-ethereum/tests/fuzzers/bls12381 \ FuzzCrossG2Add fuzz_cross_g2_add \ $repo/tests/fuzzers/bls12381/bls12381_test.go +compile_fuzzer github.com/ethereum/go-ethereum/tests/fuzzers/bls12381 \ + FuzzCrossG2MultiExp fuzz_cross_g2_multiexp \ + $repo/tests/fuzzers/bls12381/bls12381_test.go + compile_fuzzer github.com/ethereum/go-ethereum/tests/fuzzers/bls12381 \ FuzzCrossPairing fuzz_cross_pairing\ $repo/tests/fuzzers/bls12381/bls12381_test.go @@ -220,8 +232,8 @@ compile_fuzzer github.com/ethereum/go-ethereum/tests/fuzzers/secp256k1 \ Fuzz fuzzSecp256k1\ $repo/tests/fuzzers/secp256k1/secp_test.go +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/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/dial.go b/p2p/dial.go index 24d4dc2e89..225709427c 100644 --- a/p2p/dial.go +++ b/p2p/dial.go @@ -24,6 +24,7 @@ import ( "fmt" mrand "math/rand" "net" + "net/netip" "sync" "sync/atomic" "time" @@ -31,6 +32,7 @@ import ( "github.com/ethereum/go-ethereum/common/mclock" "github.com/ethereum/go-ethereum/log" "github.com/ethereum/go-ethereum/p2p/enode" + "github.com/ethereum/go-ethereum/p2p/enr" "github.com/ethereum/go-ethereum/p2p/netutil" ) @@ -77,6 +79,7 @@ var ( errRecentlyDialed = errors.New("recently dialed") errNetRestrict = errors.New("not contained in netrestrict list") errNoPort = errors.New("node does not provide TCP port") + errNoResolvedIP = errors.New("node does not provide a resolved IP") ) // dialer creates outbound connections and submits them into Server. @@ -90,16 +93,17 @@ var ( // to create peer connections to nodes arriving through the iterator. type dialScheduler struct { dialConfig - setupFunc dialSetupFunc - wg sync.WaitGroup - cancel context.CancelFunc - ctx context.Context - nodesIn chan *enode.Node - doneCh chan *dialTask - addStaticCh chan *enode.Node - remStaticCh chan *enode.Node - addPeerCh chan *conn - remPeerCh chan *conn + setupFunc dialSetupFunc + dnsLookupFunc func(ctx context.Context, network string, name string) ([]netip.Addr, error) + wg sync.WaitGroup + cancel context.CancelFunc + ctx context.Context + nodesIn chan *enode.Node + doneCh chan *dialTask + addStaticCh chan *enode.Node + remStaticCh chan *enode.Node + addPeerCh chan *conn + remPeerCh chan *conn // Everything below here belongs to loop and // should only be accessed by code on the loop goroutine. @@ -159,18 +163,19 @@ func (cfg dialConfig) withDefaults() dialConfig { func newDialScheduler(config dialConfig, it enode.Iterator, setupFunc dialSetupFunc) *dialScheduler { cfg := config.withDefaults() d := &dialScheduler{ - dialConfig: cfg, - historyTimer: mclock.NewAlarm(cfg.clock), - setupFunc: setupFunc, - dialing: make(map[enode.ID]*dialTask), - static: make(map[enode.ID]*dialTask), - peers: make(map[enode.ID]struct{}), - doneCh: make(chan *dialTask), - nodesIn: make(chan *enode.Node), - addStaticCh: make(chan *enode.Node), - remStaticCh: make(chan *enode.Node), - addPeerCh: make(chan *conn), - remPeerCh: make(chan *conn), + dialConfig: cfg, + historyTimer: mclock.NewAlarm(cfg.clock), + setupFunc: setupFunc, + dnsLookupFunc: net.DefaultResolver.LookupNetIP, + dialing: make(map[enode.ID]*dialTask), + static: make(map[enode.ID]*dialTask), + peers: make(map[enode.ID]struct{}), + doneCh: make(chan *dialTask), + nodesIn: make(chan *enode.Node), + addStaticCh: make(chan *enode.Node), + remStaticCh: make(chan *enode.Node), + addPeerCh: make(chan *conn), + remPeerCh: make(chan *conn), } d.lastStatsLog = d.clock.Now() d.ctx, d.cancel = context.WithCancel(context.Background()) @@ -274,7 +279,7 @@ loop: case node := <-d.addStaticCh: id := node.ID() _, exists := d.static[id] - d.log.Trace("Adding static node", "id", id, "ip", node.IPAddr(), "added", !exists) + d.log.Trace("Adding static node", "id", id, "endpoint", nodeEndpointForLog(node), "added", !exists) if exists { continue loop } @@ -433,10 +438,68 @@ func (d *dialScheduler) removeFromStaticPool(idx int) { task.staticPoolIndex = -1 } +// dnsResolveHostname updates the given node from its DNS hostname. +// This is used to resolve static dial targets. +func (d *dialScheduler) dnsResolveHostname(n *enode.Node) (*enode.Node, error) { + if n.Hostname() == "" { + return n, nil + } + + ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second) + defer cancel() + foundIPs, err := d.dnsLookupFunc(ctx, "ip", n.Hostname()) + if err != nil { + return n, err + } + + // Check for IP updates. + var ( + nodeIP4, nodeIP6 netip.Addr + foundIP4, foundIP6 netip.Addr + ) + n.Load((*enr.IPv4Addr)(&nodeIP4)) + n.Load((*enr.IPv6Addr)(&nodeIP6)) + for _, ip := range foundIPs { + if ip.Is4() && !foundIP4.IsValid() { + foundIP4 = ip + } + if ip.Is6() && !foundIP6.IsValid() { + foundIP6 = ip + } + } + + if !foundIP4.IsValid() && !foundIP6.IsValid() { + // Lookup failed. + return n, errNoResolvedIP + } + if foundIP4 == nodeIP4 && foundIP6 == nodeIP6 { + // No updates necessary. + d.log.Trace("Node DNS lookup had no update", "id", n.ID(), "name", n.Hostname(), "ip", foundIP4, "ip6", foundIP6) + return n, nil + } + + // Update the node. Note this invalidates the ENR signature, because we use SignNull + // to create a modified copy. But this should be OK, since we just use the node as a + // dial target. And nodes will usually only have a DNS hostname if they came from a + // enode:// URL, which has no signature anyway. If it ever becomes a problem, the + // resolved IP could also be stored into dialTask instead of the node. + rec := n.Record() + if foundIP4.IsValid() { + rec.Set(enr.IPv4Addr(foundIP4)) + } + if foundIP6.IsValid() { + rec.Set(enr.IPv6Addr(foundIP6)) + } + rec.SetSeq(n.Seq()) // ensure seq not bumped by update + newNode := enode.SignNull(rec, n.ID()).WithHostname(n.Hostname()) + d.log.Debug("Node updated from DNS lookup", "id", n.ID(), "name", n.Hostname(), "ip", newNode.IP()) + return newNode, nil +} + // startDial runs the given dial task in a separate goroutine. func (d *dialScheduler) startDial(task *dialTask) { node := task.dest() - d.log.Trace("Starting p2p dial", "id", node.ID(), "ip", node.IPAddr(), "flag", task.flags) + d.log.Trace("Starting p2p dial", "id", node.ID(), "endpoint", nodeEndpointForLog(node), "flag", task.flags) hkey := string(node.ID().Bytes()) d.history.add(hkey, d.clock.Now().Add(dialHistoryExpiration)) d.dialing[node.ID()] = task @@ -473,14 +536,29 @@ func (t *dialTask) dest() *enode.Node { } func (t *dialTask) run(d *dialScheduler) { - if t.needResolve() && !t.resolve(d) { - return + if t.isStatic() { + // Resolve DNS. + if n := t.dest(); n.Hostname() != "" { + resolved, err := d.dnsResolveHostname(n) + if err != nil { + d.log.Warn("DNS lookup of static node failed", "id", n.ID(), "name", n.Hostname(), "err", err) + } else { + t.destPtr.Store(resolved) + } + } + // Try resolving node ID through the DHT if there is no IP address. + if !t.dest().IPAddr().IsValid() { + if !t.resolve(d) { + return // DHT resolve failed, skip dial. + } + } } err := t.dial(d, t.dest()) if err != nil { // For static nodes, resolve one more time if dialing fails. - if _, ok := err.(*dialError); ok && t.flags&staticDialedConn != 0 { + var dialErr *dialError + if errors.As(err, &dialErr) && t.isStatic() { if t.resolve(d) { t.dial(d, t.dest()) } @@ -488,8 +566,8 @@ func (t *dialTask) run(d *dialScheduler) { } } -func (t *dialTask) needResolve() bool { - return t.flags&staticDialedConn != 0 && !t.dest().IPAddr().IsValid() +func (t *dialTask) isStatic() bool { + return t.flags&staticDialedConn != 0 } // resolve attempts to find the current endpoint for the destination @@ -553,3 +631,10 @@ func cleanupDialErr(err error) error { } return err } + +func nodeEndpointForLog(n *enode.Node) string { + if n.Hostname() != "" { + return n.Hostname() + } + return n.IPAddr().String() +} diff --git a/p2p/dial_test.go b/p2p/dial_test.go index 13908f11ea..f18dacce2a 100644 --- a/p2p/dial_test.go +++ b/p2p/dial_test.go @@ -22,6 +22,7 @@ import ( "fmt" "math/rand" "net" + "net/netip" "reflect" "sync" "testing" @@ -394,6 +395,34 @@ func TestDialSchedResolve(t *testing.T) { }) } +func TestDialSchedDNSHostname(t *testing.T) { + t.Parallel() + + config := dialConfig{ + maxActiveDials: 1, + maxDialPeers: 1, + } + node := newNode(uintID(0x01), ":30303").WithHostname("node-hostname") + resolved := newNode(uintID(0x01), "1.2.3.4:30303").WithHostname("node-hostname") + runDialTest(t, config, []dialTestRound{ + { + update: func(d *dialScheduler) { + d.dnsLookupFunc = func(ctx context.Context, network string, name string) ([]netip.Addr, error) { + if name != "node-hostname" { + t.Error("wrong hostname in DNS lookup:", name) + } + result := []netip.Addr{netip.MustParseAddr("1.2.3.4")} + return result, nil + } + d.addStatic(node) + }, + wantNewDials: []*enode.Node{ + resolved, + }, + }, + }) +} + // ------- // Code below here is the framework for the tests above. 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 cbe2972541..392279f905 100644 --- a/p2p/discover/table.go +++ b/p2p/discover/table.go @@ -570,7 +570,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) } } @@ -580,7 +580,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/enode/node.go b/p2p/enode/node.go index 4d93d3f6be..d6f2ac7ff5 100644 --- a/p2p/enode/node.go +++ b/p2p/enode/node.go @@ -37,6 +37,10 @@ var errMissingPrefix = errors.New("missing 'enr:' prefix for base64-encoded reco type Node struct { r enr.Record id ID + + // hostname tracks the DNS name of the node. + hostname string + // endpoint information ip netip.Addr udp uint16 @@ -77,6 +81,8 @@ func newNodeWithID(r *enr.Record, id ID) *Node { n.setIP4(ip4) case valid6: n.setIP6(ip6) + default: + n.setIPv4Ports() } return n } @@ -103,6 +109,10 @@ func localityScore(ip netip.Addr) int { func (n *Node) setIP4(ip netip.Addr) { n.ip = ip + n.setIPv4Ports() +} + +func (n *Node) setIPv4Ports() { n.Load((*enr.UDP)(&n.udp)) n.Load((*enr.TCP)(&n.tcp)) } @@ -184,6 +194,18 @@ func (n *Node) TCP() int { return int(n.tcp) } +// WithHostname adds a DNS hostname to the node. +func (n *Node) WithHostname(hostname string) *Node { + cpy := *n + cpy.hostname = hostname + return &cpy +} + +// Hostname returns the DNS name assigned by WithHostname. +func (n *Node) Hostname() string { + return n.hostname +} + // UDPEndpoint returns the announced UDP endpoint. func (n *Node) UDPEndpoint() (netip.AddrPort, bool) { if !n.ip.IsValid() || n.ip.IsUnspecified() || n.udp == 0 { diff --git a/p2p/enode/node_test.go b/p2p/enode/node_test.go index f38c77415e..e9fe631f34 100644 --- a/p2p/enode/node_test.go +++ b/p2p/enode/node_test.go @@ -74,6 +74,7 @@ func TestNodeEndpoints(t *testing.T) { wantUDP int wantTCP int wantQUIC int + wantDNS string } tests := []endpointTest{ { @@ -90,6 +91,7 @@ func TestNodeEndpoints(t *testing.T) { r.Set(enr.UDP(9000)) return SignNull(&r, id) }(), + wantUDP: 9000, }, { name: "tcp-only", @@ -98,6 +100,7 @@ func TestNodeEndpoints(t *testing.T) { r.Set(enr.TCP(9000)) return SignNull(&r, id) }(), + wantTCP: 9000, }, { name: "quic-only", @@ -268,6 +271,19 @@ func TestNodeEndpoints(t *testing.T) { wantIP: netip.MustParseAddr("2001::ff00:0042:8329"), wantQUIC: 9001, }, + { + name: "dns-only", + node: func() *Node { + var r enr.Record + r.Set(enr.UDP(30303)) + r.Set(enr.TCP(30303)) + n := SignNull(&r, id).WithHostname("example.com") + return n + }(), + wantTCP: 30303, + wantUDP: 30303, + wantDNS: "example.com", + }, } for _, test := range tests { @@ -284,6 +300,9 @@ func TestNodeEndpoints(t *testing.T) { if quic, _ := test.node.QUICEndpoint(); test.wantQUIC != int(quic.Port()) { t.Errorf("node has wrong QUIC port %d, want %d", quic.Port(), test.wantQUIC) } + if test.wantDNS != test.node.Hostname() { + t.Errorf("node has wrong DNS name %s, want %s", test.node.Hostname(), test.wantDNS) + } }) } } diff --git a/p2p/enode/urlv4.go b/p2p/enode/urlv4.go index a55dfa6632..b455cd4533 100644 --- a/p2p/enode/urlv4.go +++ b/p2p/enode/urlv4.go @@ -33,7 +33,6 @@ import ( var ( incompleteNodeURL = regexp.MustCompile("(?i)^(?:enode://)?([0-9a-f]+)$") - lookupIPFunc = net.LookupIP ) // MustParseV4 parses a node URL. It panics if the URL is not valid. @@ -126,20 +125,9 @@ func parseComplete(rawurl string) (*Node, error) { if id, err = parsePubkey(u.User.String()); err != nil { return nil, fmt.Errorf("invalid public key (%v)", err) } - // Parse the IP address. + + // Parse the IP and ports. ip := net.ParseIP(u.Hostname()) - if ip == nil { - ips, err := lookupIPFunc(u.Hostname()) - if err != nil { - return nil, err - } - ip = ips[0] - } - // Ensure the IP is 4 bytes long for IPv4 addresses. - if ipv4 := ip.To4(); ipv4 != nil { - ip = ipv4 - } - // Parse the port numbers. if tcpPort, err = strconv.ParseUint(u.Port(), 10, 16); err != nil { return nil, errors.New("invalid port") } @@ -151,7 +139,13 @@ func parseComplete(rawurl string) (*Node, error) { return nil, errors.New("invalid discport in query") } } - return NewV4(id, ip, int(tcpPort), int(udpPort)), nil + + // Create the node. + node := NewV4(id, ip, int(tcpPort), int(udpPort)) + if ip == nil && u.Hostname() != "" { + node = node.WithHostname(u.Hostname()) + } + return node, nil } // parsePubkey parses a hex-encoded secp256k1 public key. @@ -181,15 +175,23 @@ func (n *Node) URLv4() string { nodeid = fmt.Sprintf("%s.%x", scheme, n.id[:]) } u := url.URL{Scheme: "enode"} - if !n.ip.IsValid() { - u.Host = nodeid - } else { + if n.Hostname() != "" { + // For nodes with a DNS name: include DNS name, TCP port, and optional UDP port + u.User = url.User(nodeid) + u.Host = fmt.Sprintf("%s:%d", n.Hostname(), n.TCP()) + if n.UDP() != n.TCP() { + u.RawQuery = "discport=" + strconv.Itoa(n.UDP()) + } + } else if n.ip.IsValid() { + // For IP-based nodes: include IP address, TCP port, and optional UDP port addr := net.TCPAddr{IP: n.IP(), Port: n.TCP()} u.User = url.User(nodeid) u.Host = addr.String() if n.UDP() != n.TCP() { u.RawQuery = "discport=" + strconv.Itoa(n.UDP()) } + } else { + u.Host = nodeid } return u.String() } diff --git a/p2p/enode/urlv4_test.go b/p2p/enode/urlv4_test.go index 33de96cc57..f39d5a2deb 100644 --- a/p2p/enode/urlv4_test.go +++ b/p2p/enode/urlv4_test.go @@ -18,7 +18,6 @@ package enode import ( "crypto/ecdsa" - "errors" "net" "reflect" "strings" @@ -28,15 +27,6 @@ import ( "github.com/ethereum/go-ethereum/p2p/enr" ) -func init() { - lookupIPFunc = func(name string) ([]net.IP, error) { - if name == "node.example.org" { - return []net.IP{{33, 44, 55, 66}}, nil - } - return nil, errors.New("no such host") - } -} - var parseNodeTests = []struct { input string wantError string @@ -70,10 +60,6 @@ var parseNodeTests = []struct { wantError: enr.ErrInvalidSig.Error(), }, // Complete node URLs with IP address and ports - { - input: "enode://1dd9d65c4552b5eb43d5ad55a2ee3f56c6cbc1c64a5c8d659f51fcd51bace24351232b8d7821617d2b29b54b81cdefb9b3e9c37d7fd5f63270bcc9e1a6f6a439@invalid.:3", - wantError: `no such host`, - }, { input: "enode://1dd9d65c4552b5eb43d5ad55a2ee3f56c6cbc1c64a5c8d659f51fcd51bace24351232b8d7821617d2b29b54b81cdefb9b3e9c37d7fd5f63270bcc9e1a6f6a439@127.0.0.1:foo", wantError: `invalid port`, @@ -91,6 +77,15 @@ var parseNodeTests = []struct { 52150, ), }, + { + input: "enode://1dd9d65c4552b5eb43d5ad55a2ee3f56c6cbc1c64a5c8d659f51fcd51bace24351232b8d7821617d2b29b54b81cdefb9b3e9c37d7fd5f63270bcc9e1a6f6a439@valid.:3", + wantResult: NewV4( + hexPubkey("1dd9d65c4552b5eb43d5ad55a2ee3f56c6cbc1c64a5c8d659f51fcd51bace24351232b8d7821617d2b29b54b81cdefb9b3e9c37d7fd5f63270bcc9e1a6f6a439"), + nil, + 3, + 3, + ).WithHostname("valid."), + }, { input: "enode://1dd9d65c4552b5eb43d5ad55a2ee3f56c6cbc1c64a5c8d659f51fcd51bace24351232b8d7821617d2b29b54b81cdefb9b3e9c37d7fd5f63270bcc9e1a6f6a439@[::]:52150", wantResult: NewV4( diff --git a/p2p/metrics.go b/p2p/metrics.go index a2ae213b70..1fd0f26db3 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) // handshake error meters dialTooManyPeers = metrics.NewRegisteredMeter("p2p/dials/error/saturated", nil) @@ -61,25 +61,10 @@ var ( dialProtoHandshakeError = metrics.NewRegisteredMeter("p2p/dials/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 { @@ -113,7 +98,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/netutil/addrutil_test.go b/p2p/netutil/addrutil_test.go new file mode 100644 index 0000000000..0abbabb54b --- /dev/null +++ b/p2p/netutil/addrutil_test.go @@ -0,0 +1,140 @@ +// 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 netutil + +import ( + "net" + "net/netip" + "path/filepath" + "testing" +) + +// customNetAddr is a custom implementation of net.Addr for testing purposes. +type customNetAddr struct{} + +func (c *customNetAddr) Network() string { return "custom" } +func (c *customNetAddr) String() string { return "custom" } + +func TestAddrAddr(t *testing.T) { + tempDir := t.TempDir() + tests := []struct { + name string + addr net.Addr + want netip.Addr + }{ + { + name: "IPAddr IPv4", + addr: &net.IPAddr{IP: net.ParseIP("192.0.2.1")}, + want: netip.MustParseAddr("192.0.2.1"), + }, + { + name: "IPAddr IPv6", + addr: &net.IPAddr{IP: net.ParseIP("2001:db8::1")}, + want: netip.MustParseAddr("2001:db8::1"), + }, + { + name: "TCPAddr IPv4", + addr: &net.TCPAddr{IP: net.ParseIP("192.0.2.1"), Port: 8080}, + want: netip.MustParseAddr("192.0.2.1"), + }, + { + name: "TCPAddr IPv6", + addr: &net.TCPAddr{IP: net.ParseIP("2001:db8::1"), Port: 8080}, + want: netip.MustParseAddr("2001:db8::1"), + }, + { + name: "UDPAddr IPv4", + addr: &net.UDPAddr{IP: net.ParseIP("192.0.2.1"), Port: 8080}, + want: netip.MustParseAddr("192.0.2.1"), + }, + { + name: "UDPAddr IPv6", + addr: &net.UDPAddr{IP: net.ParseIP("2001:db8::1"), Port: 8080}, + want: netip.MustParseAddr("2001:db8::1"), + }, + { + name: "Unsupported Addr type", + addr: &net.UnixAddr{Name: filepath.Join(tempDir, "test.sock"), Net: "unix"}, + want: netip.Addr{}, + }, + { + name: "Nil input", + addr: nil, + want: netip.Addr{}, + }, + { + name: "Custom net.Addr implementation", + addr: &customNetAddr{}, + want: netip.Addr{}, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + if got := AddrAddr(tt.addr); got != tt.want { + t.Errorf("AddrAddr() = %v, want %v", got, tt.want) + } + }) + } +} + +func TestIPToAddr(t *testing.T) { + tests := []struct { + name string + ip net.IP + want netip.Addr + }{ + { + name: "IPv4", + ip: net.ParseIP("192.0.2.1"), + want: netip.MustParseAddr("192.0.2.1"), + }, + { + name: "IPv6", + ip: net.ParseIP("2001:db8::1"), + want: netip.MustParseAddr("2001:db8::1"), + }, + { + name: "Invalid IP", + ip: net.IP{1, 2, 3}, + want: netip.Addr{}, + }, + { + name: "Invalid IP (5 octets)", + ip: net.IP{192, 0, 2, 1, 1}, + want: netip.Addr{}, + }, + { + name: "IPv4-mapped IPv6", + ip: net.ParseIP("::ffff:192.0.2.1"), + want: netip.MustParseAddr("192.0.2.1"), + }, + { + name: "Nil input", + ip: nil, + want: netip.Addr{}, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + if got := IPToAddr(tt.ip); got != tt.want { + t.Errorf("IPToAddr() = %v, want %v", got, tt.want) + } + }) + } +} diff --git a/p2p/peer.go b/p2p/peer.go index c3834965cc..a01df63d0c 100644 --- a/p2p/peer.go +++ b/p2p/peer.go @@ -345,9 +345,7 @@ func (p *Peer) handle(msg Msg) error { case msg.Code == discMsg: // This is the last message. We don't need to discard or // check errors because, the connection will be closed after it. - var m struct{ R DiscReason } - rlp.Decode(msg.Payload, &m) - return m.R + return decodeDisconnectMessage(msg.Payload) case msg.Code < baseProtocolLength: // ignore other base protocol messages return msg.Discard() @@ -357,7 +355,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) @@ -372,6 +370,27 @@ func (p *Peer) handle(msg Msg) error { return nil } +// decodeDisconnectMessage decodes the payload of discMsg. +func decodeDisconnectMessage(r io.Reader) (reason DiscReason) { + s := rlp.NewStream(r, 100) + k, _, err := s.Kind() + if err != nil { + return DiscInvalid + } + if k == rlp.List { + s.List() + err = s.Decode(&reason) + } else { + // Legacy path: some implementations, including geth, used to send the disconnect + // reason as a byte array by accident. + err = s.Decode(&reason) + } + if err != nil { + reason = DiscInvalid + } + return reason +} + func countMatchingProtocols(protocols []Protocol, caps []Cap) int { n := 0 for _, cap := range caps { diff --git a/p2p/peer_error.go b/p2p/peer_error.go index ebc59de251..dcdadf7fe3 100644 --- a/p2p/peer_error.go +++ b/p2p/peer_error.go @@ -70,6 +70,8 @@ const ( DiscSelf DiscReadTimeout DiscSubprotocolError = DiscReason(0x10) + + DiscInvalid = 0xff ) var discReasonToString = [...]string{ @@ -86,10 +88,11 @@ var discReasonToString = [...]string{ DiscSelf: "connected to self", DiscReadTimeout: "read timeout", DiscSubprotocolError: "subprotocol error", + DiscInvalid: "invalid disconnect reason", } func (d DiscReason) String() string { - if len(discReasonToString) <= int(d) { + if len(discReasonToString) <= int(d) || discReasonToString[d] == "" { return fmt.Sprintf("unknown disconnect reason %d", d) } return discReasonToString[d] diff --git a/p2p/peer_test.go b/p2p/peer_test.go index 4308bbd2eb..dea72875fe 100644 --- a/p2p/peer_test.go +++ b/p2p/peer_test.go @@ -60,22 +60,22 @@ func uintID(i uint16) enode.ID { // newNode creates a node record with the given address. func newNode(id enode.ID, addr string) *enode.Node { var r enr.Record - if addr != "" { - // Set the port if present. - if strings.Contains(addr, ":") { - hs, ps, err := net.SplitHostPort(addr) - if err != nil { - panic(fmt.Errorf("invalid address %q", addr)) - } - port, err := strconv.Atoi(ps) - if err != nil { - panic(fmt.Errorf("invalid port in %q", addr)) - } - r.Set(enr.TCP(port)) - r.Set(enr.UDP(port)) - addr = hs + // Set the port if present. + if strings.Contains(addr, ":") { + hs, ps, err := net.SplitHostPort(addr) + if err != nil { + panic(fmt.Errorf("invalid address %q", addr)) } - // Set the IP. + port, err := strconv.Atoi(ps) + if err != nil { + panic(fmt.Errorf("invalid port in %q", addr)) + } + r.Set(enr.TCP(port)) + r.Set(enr.UDP(port)) + addr = hs + } + // Set the IP. + if addr != "" { ip := net.ParseIP(addr) if ip == nil { panic(fmt.Errorf("invalid IP %q", addr)) 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 5fc7686feb..87d3013f11 100644 --- a/p2p/transport.go +++ b/p2p/transport.go @@ -98,7 +98,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) @@ -113,15 +113,14 @@ func (t *rlpxTransport) close(err error) { // Tell the remote end why we're disconnecting if possible. // We only bother doing this if the underlying connection supports // setting a timeout tough. - if t.conn != nil { - if r, ok := err.(DiscReason); ok && r != DiscNetworkError { - deadline := time.Now().Add(discWriteTimeout) - if err := t.conn.SetWriteDeadline(deadline); err == nil { - // Connection supports write deadline. - t.wbuf.Reset() - rlp.Encode(&t.wbuf, []DiscReason{r}) - t.conn.Write(discMsg, t.wbuf.Bytes()) - } + if reason, ok := err.(DiscReason); ok && reason != DiscNetworkError { + // We do not use the WriteMsg func since we want a custom deadline + deadline := time.Now().Add(discWriteTimeout) + if err := t.conn.SetWriteDeadline(deadline); err == nil { + // Connection supports write deadline. + t.wbuf.Reset() + rlp.Encode(&t.wbuf, []any{reason}) + t.conn.Write(discMsg, t.wbuf.Bytes()) } } t.conn.Close() @@ -163,11 +162,8 @@ func readProtocolHandshake(rw MsgReader) (*protoHandshake, error) { if msg.Code == discMsg { // Disconnect before protocol handshake is valid according to the // spec and we send it ourself if the post-handshake checks fail. - // We can't return the reason directly, though, because it is echoed - // back otherwise. Wrap it in a string instead. - var reason [1]DiscReason - rlp.Decode(msg.Payload, &reason) - return nil, reason[0] + r := decodeDisconnectMessage(msg.Payload) + return nil, r } if msg.Code != handshakeMsg { return nil, fmt.Errorf("expected handshake, got %x", msg.Code) diff --git a/p2p/transport_test.go b/p2p/transport_test.go index 01695cd3af..777be1bd0d 100644 --- a/p2p/transport_test.go +++ b/p2p/transport_test.go @@ -97,7 +97,7 @@ func TestProtocolHandshake(t *testing.T) { return } - if err := ExpectMsg(rlpx, discMsg, []DiscReason{DiscQuitting}); err != nil { + if err := ExpectMsg(rlpx, discMsg, []any{DiscQuitting}); err != nil { t.Errorf("error receiving disconnect: %v", err) } }() @@ -112,7 +112,13 @@ func TestProtocolHandshakeErrors(t *testing.T) { }{ { code: discMsg, - msg: []DiscReason{DiscQuitting}, + msg: []any{DiscQuitting}, + err: DiscQuitting, + }, + { + // legacy disconnect encoding as byte array + code: discMsg, + msg: []byte{byte(DiscQuitting)}, err: DiscQuitting, }, { diff --git a/params/config.go b/params/config.go index cac948bf2d..9b3b92484a 100644 --- a/params/config.go +++ b/params/config.go @@ -18,6 +18,7 @@ package params import ( "fmt" + "math" "math/big" "github.com/ethereum/go-ethereum/common" @@ -38,249 +39,240 @@ var ( // MainnetChainConfig is the chain parameters to run a node on the main network. MainnetChainConfig = &ChainConfig{ - ChainID: big.NewInt(1), - HomesteadBlock: big.NewInt(1_150_000), - DAOForkBlock: big.NewInt(1_920_000), - DAOForkSupport: true, - EIP150Block: big.NewInt(2_463_000), - EIP155Block: big.NewInt(2_675_000), - EIP158Block: big.NewInt(2_675_000), - ByzantiumBlock: big.NewInt(4_370_000), - ConstantinopleBlock: big.NewInt(7_280_000), - PetersburgBlock: big.NewInt(7_280_000), - IstanbulBlock: big.NewInt(9_069_000), - MuirGlacierBlock: big.NewInt(9_200_000), - BerlinBlock: big.NewInt(12_244_000), - LondonBlock: big.NewInt(12_965_000), - ArrowGlacierBlock: big.NewInt(13_773_000), - GrayGlacierBlock: big.NewInt(15_050_000), - TerminalTotalDifficulty: MainnetTerminalTotalDifficulty, // 58_750_000_000_000_000_000_000 - TerminalTotalDifficultyPassed: true, - ShanghaiTime: newUint64(1681338455), - CancunTime: newUint64(1710338135), - DepositContractAddress: common.HexToAddress("0x00000000219ab540356cbb839cbe05303d7705fa"), - Ethash: new(EthashConfig), + ChainID: big.NewInt(1), + HomesteadBlock: big.NewInt(1_150_000), + DAOForkBlock: big.NewInt(1_920_000), + DAOForkSupport: true, + EIP150Block: big.NewInt(2_463_000), + EIP155Block: big.NewInt(2_675_000), + EIP158Block: big.NewInt(2_675_000), + ByzantiumBlock: big.NewInt(4_370_000), + ConstantinopleBlock: big.NewInt(7_280_000), + PetersburgBlock: big.NewInt(7_280_000), + IstanbulBlock: big.NewInt(9_069_000), + MuirGlacierBlock: big.NewInt(9_200_000), + BerlinBlock: big.NewInt(12_244_000), + LondonBlock: big.NewInt(12_965_000), + ArrowGlacierBlock: big.NewInt(13_773_000), + GrayGlacierBlock: big.NewInt(15_050_000), + TerminalTotalDifficulty: MainnetTerminalTotalDifficulty, // 58_750_000_000_000_000_000_000 + ShanghaiTime: newUint64(1681338455), + CancunTime: newUint64(1710338135), + DepositContractAddress: common.HexToAddress("0x00000000219ab540356cbb839cbe05303d7705fa"), + Ethash: new(EthashConfig), } // HoleskyChainConfig contains the chain parameters to run a node on the Holesky test network. HoleskyChainConfig = &ChainConfig{ - ChainID: big.NewInt(17000), - HomesteadBlock: big.NewInt(0), - DAOForkBlock: nil, - DAOForkSupport: true, - EIP150Block: big.NewInt(0), - EIP155Block: big.NewInt(0), - EIP158Block: big.NewInt(0), - ByzantiumBlock: big.NewInt(0), - ConstantinopleBlock: big.NewInt(0), - PetersburgBlock: big.NewInt(0), - IstanbulBlock: big.NewInt(0), - MuirGlacierBlock: nil, - BerlinBlock: big.NewInt(0), - LondonBlock: big.NewInt(0), - ArrowGlacierBlock: nil, - GrayGlacierBlock: nil, - TerminalTotalDifficulty: big.NewInt(0), - TerminalTotalDifficultyPassed: true, - MergeNetsplitBlock: nil, - ShanghaiTime: newUint64(1696000704), - CancunTime: newUint64(1707305664), - Ethash: new(EthashConfig), + ChainID: big.NewInt(17000), + HomesteadBlock: big.NewInt(0), + DAOForkBlock: nil, + DAOForkSupport: true, + EIP150Block: big.NewInt(0), + EIP155Block: big.NewInt(0), + EIP158Block: big.NewInt(0), + ByzantiumBlock: big.NewInt(0), + ConstantinopleBlock: big.NewInt(0), + PetersburgBlock: big.NewInt(0), + IstanbulBlock: big.NewInt(0), + MuirGlacierBlock: nil, + BerlinBlock: big.NewInt(0), + LondonBlock: big.NewInt(0), + ArrowGlacierBlock: nil, + GrayGlacierBlock: nil, + TerminalTotalDifficulty: big.NewInt(0), + MergeNetsplitBlock: nil, + ShanghaiTime: newUint64(1696000704), + CancunTime: newUint64(1707305664), + Ethash: new(EthashConfig), } // SepoliaChainConfig contains the chain parameters to run a node on the Sepolia test network. SepoliaChainConfig = &ChainConfig{ - ChainID: big.NewInt(11155111), - HomesteadBlock: big.NewInt(0), - DAOForkBlock: nil, - DAOForkSupport: true, - EIP150Block: big.NewInt(0), - EIP155Block: big.NewInt(0), - EIP158Block: big.NewInt(0), - ByzantiumBlock: big.NewInt(0), - ConstantinopleBlock: big.NewInt(0), - PetersburgBlock: big.NewInt(0), - IstanbulBlock: big.NewInt(0), - MuirGlacierBlock: big.NewInt(0), - BerlinBlock: big.NewInt(0), - LondonBlock: big.NewInt(0), - ArrowGlacierBlock: nil, - GrayGlacierBlock: nil, - TerminalTotalDifficulty: big.NewInt(17_000_000_000_000_000), - TerminalTotalDifficultyPassed: true, - MergeNetsplitBlock: big.NewInt(1735371), - ShanghaiTime: newUint64(1677557088), - CancunTime: newUint64(1706655072), - Ethash: new(EthashConfig), + ChainID: big.NewInt(11155111), + HomesteadBlock: big.NewInt(0), + DAOForkBlock: nil, + DAOForkSupport: true, + EIP150Block: big.NewInt(0), + EIP155Block: big.NewInt(0), + EIP158Block: big.NewInt(0), + ByzantiumBlock: big.NewInt(0), + ConstantinopleBlock: big.NewInt(0), + PetersburgBlock: big.NewInt(0), + IstanbulBlock: big.NewInt(0), + MuirGlacierBlock: big.NewInt(0), + BerlinBlock: big.NewInt(0), + LondonBlock: big.NewInt(0), + ArrowGlacierBlock: nil, + GrayGlacierBlock: nil, + TerminalTotalDifficulty: big.NewInt(17_000_000_000_000_000), + MergeNetsplitBlock: big.NewInt(1735371), + ShanghaiTime: newUint64(1677557088), + CancunTime: newUint64(1706655072), + Ethash: new(EthashConfig), } // AllEthashProtocolChanges contains every protocol change (EIPs) introduced // and accepted by the Ethereum core developers into the Ethash consensus. AllEthashProtocolChanges = &ChainConfig{ - ChainID: big.NewInt(1337), - HomesteadBlock: big.NewInt(0), - DAOForkBlock: nil, - DAOForkSupport: false, - EIP150Block: big.NewInt(0), - EIP155Block: big.NewInt(0), - EIP158Block: big.NewInt(0), - ByzantiumBlock: big.NewInt(0), - ConstantinopleBlock: big.NewInt(0), - PetersburgBlock: big.NewInt(0), - IstanbulBlock: big.NewInt(0), - MuirGlacierBlock: big.NewInt(0), - BerlinBlock: big.NewInt(0), - LondonBlock: big.NewInt(0), - ArrowGlacierBlock: big.NewInt(0), - GrayGlacierBlock: big.NewInt(0), - MergeNetsplitBlock: nil, - ShanghaiTime: nil, - CancunTime: nil, - PragueTime: nil, - VerkleTime: nil, - TerminalTotalDifficulty: nil, - TerminalTotalDifficultyPassed: true, - Ethash: new(EthashConfig), - Clique: nil, + ChainID: big.NewInt(1337), + HomesteadBlock: big.NewInt(0), + DAOForkBlock: nil, + DAOForkSupport: false, + EIP150Block: big.NewInt(0), + EIP155Block: big.NewInt(0), + EIP158Block: big.NewInt(0), + ByzantiumBlock: big.NewInt(0), + ConstantinopleBlock: big.NewInt(0), + PetersburgBlock: big.NewInt(0), + IstanbulBlock: big.NewInt(0), + MuirGlacierBlock: big.NewInt(0), + BerlinBlock: big.NewInt(0), + LondonBlock: big.NewInt(0), + ArrowGlacierBlock: big.NewInt(0), + GrayGlacierBlock: big.NewInt(0), + TerminalTotalDifficulty: big.NewInt(math.MaxInt64), + MergeNetsplitBlock: nil, + ShanghaiTime: nil, + CancunTime: nil, + PragueTime: nil, + VerkleTime: nil, + Ethash: new(EthashConfig), + Clique: nil, } AllDevChainProtocolChanges = &ChainConfig{ - ChainID: big.NewInt(1337), - HomesteadBlock: big.NewInt(0), - EIP150Block: big.NewInt(0), - EIP155Block: big.NewInt(0), - EIP158Block: big.NewInt(0), - ByzantiumBlock: big.NewInt(0), - ConstantinopleBlock: big.NewInt(0), - PetersburgBlock: big.NewInt(0), - IstanbulBlock: big.NewInt(0), - MuirGlacierBlock: big.NewInt(0), - BerlinBlock: big.NewInt(0), - LondonBlock: big.NewInt(0), - ArrowGlacierBlock: big.NewInt(0), - GrayGlacierBlock: big.NewInt(0), - ShanghaiTime: newUint64(0), - CancunTime: newUint64(0), - PragueTime: newUint64(0), - TerminalTotalDifficulty: big.NewInt(0), - TerminalTotalDifficultyPassed: true, + ChainID: big.NewInt(1337), + HomesteadBlock: big.NewInt(0), + EIP150Block: big.NewInt(0), + EIP155Block: big.NewInt(0), + EIP158Block: big.NewInt(0), + ByzantiumBlock: big.NewInt(0), + ConstantinopleBlock: big.NewInt(0), + PetersburgBlock: big.NewInt(0), + IstanbulBlock: big.NewInt(0), + MuirGlacierBlock: big.NewInt(0), + BerlinBlock: big.NewInt(0), + LondonBlock: big.NewInt(0), + ArrowGlacierBlock: big.NewInt(0), + GrayGlacierBlock: big.NewInt(0), + ShanghaiTime: newUint64(0), + CancunTime: newUint64(0), + TerminalTotalDifficulty: big.NewInt(0), + PragueTime: newUint64(0), } // AllCliqueProtocolChanges contains every protocol change (EIPs) introduced // and accepted by the Ethereum core developers into the Clique consensus. AllCliqueProtocolChanges = &ChainConfig{ - ChainID: big.NewInt(1337), - HomesteadBlock: big.NewInt(0), - DAOForkBlock: nil, - DAOForkSupport: false, - EIP150Block: big.NewInt(0), - EIP155Block: big.NewInt(0), - EIP158Block: big.NewInt(0), - ByzantiumBlock: big.NewInt(0), - ConstantinopleBlock: big.NewInt(0), - PetersburgBlock: big.NewInt(0), - IstanbulBlock: big.NewInt(0), - MuirGlacierBlock: big.NewInt(0), - BerlinBlock: big.NewInt(0), - LondonBlock: big.NewInt(0), - ArrowGlacierBlock: nil, - GrayGlacierBlock: nil, - MergeNetsplitBlock: nil, - ShanghaiTime: nil, - CancunTime: nil, - PragueTime: nil, - VerkleTime: nil, - TerminalTotalDifficulty: nil, - TerminalTotalDifficultyPassed: false, - Ethash: nil, - Clique: &CliqueConfig{Period: 0, Epoch: 30000}, + ChainID: big.NewInt(1337), + HomesteadBlock: big.NewInt(0), + DAOForkBlock: nil, + DAOForkSupport: false, + EIP150Block: big.NewInt(0), + EIP155Block: big.NewInt(0), + EIP158Block: big.NewInt(0), + ByzantiumBlock: big.NewInt(0), + ConstantinopleBlock: big.NewInt(0), + PetersburgBlock: big.NewInt(0), + IstanbulBlock: big.NewInt(0), + MuirGlacierBlock: big.NewInt(0), + BerlinBlock: big.NewInt(0), + LondonBlock: big.NewInt(0), + ArrowGlacierBlock: nil, + GrayGlacierBlock: nil, + MergeNetsplitBlock: nil, + ShanghaiTime: nil, + CancunTime: nil, + PragueTime: nil, + VerkleTime: nil, + TerminalTotalDifficulty: big.NewInt(math.MaxInt64), + Ethash: nil, + Clique: &CliqueConfig{Period: 0, Epoch: 30000}, } // TestChainConfig contains every protocol change (EIPs) introduced // and accepted by the Ethereum core developers for testing purposes. TestChainConfig = &ChainConfig{ - ChainID: big.NewInt(1), - HomesteadBlock: big.NewInt(0), - DAOForkBlock: nil, - DAOForkSupport: false, - EIP150Block: big.NewInt(0), - EIP155Block: big.NewInt(0), - EIP158Block: big.NewInt(0), - ByzantiumBlock: big.NewInt(0), - ConstantinopleBlock: big.NewInt(0), - PetersburgBlock: big.NewInt(0), - IstanbulBlock: big.NewInt(0), - MuirGlacierBlock: big.NewInt(0), - BerlinBlock: big.NewInt(0), - LondonBlock: big.NewInt(0), - ArrowGlacierBlock: big.NewInt(0), - GrayGlacierBlock: big.NewInt(0), - MergeNetsplitBlock: nil, - ShanghaiTime: nil, - CancunTime: nil, - PragueTime: nil, - VerkleTime: nil, - TerminalTotalDifficulty: nil, - TerminalTotalDifficultyPassed: false, - Ethash: new(EthashConfig), - Clique: nil, + ChainID: big.NewInt(1), + HomesteadBlock: big.NewInt(0), + DAOForkBlock: nil, + DAOForkSupport: false, + EIP150Block: big.NewInt(0), + EIP155Block: big.NewInt(0), + EIP158Block: big.NewInt(0), + ByzantiumBlock: big.NewInt(0), + ConstantinopleBlock: big.NewInt(0), + PetersburgBlock: big.NewInt(0), + IstanbulBlock: big.NewInt(0), + MuirGlacierBlock: big.NewInt(0), + BerlinBlock: big.NewInt(0), + LondonBlock: big.NewInt(0), + ArrowGlacierBlock: big.NewInt(0), + GrayGlacierBlock: big.NewInt(0), + MergeNetsplitBlock: nil, + ShanghaiTime: nil, + CancunTime: nil, + PragueTime: nil, + VerkleTime: nil, + TerminalTotalDifficulty: big.NewInt(math.MaxInt64), + Ethash: new(EthashConfig), + Clique: nil, } // MergedTestChainConfig contains every protocol change (EIPs) introduced // and accepted by the Ethereum core developers for testing purposes. MergedTestChainConfig = &ChainConfig{ - ChainID: big.NewInt(1), - HomesteadBlock: big.NewInt(0), - DAOForkBlock: nil, - DAOForkSupport: false, - EIP150Block: big.NewInt(0), - EIP155Block: big.NewInt(0), - EIP158Block: big.NewInt(0), - ByzantiumBlock: big.NewInt(0), - ConstantinopleBlock: big.NewInt(0), - PetersburgBlock: big.NewInt(0), - IstanbulBlock: big.NewInt(0), - MuirGlacierBlock: big.NewInt(0), - BerlinBlock: big.NewInt(0), - LondonBlock: big.NewInt(0), - ArrowGlacierBlock: big.NewInt(0), - GrayGlacierBlock: big.NewInt(0), - MergeNetsplitBlock: big.NewInt(0), - ShanghaiTime: newUint64(0), - CancunTime: newUint64(0), - PragueTime: newUint64(0), - VerkleTime: nil, - TerminalTotalDifficulty: big.NewInt(0), - TerminalTotalDifficultyPassed: true, - Ethash: new(EthashConfig), - Clique: nil, + ChainID: big.NewInt(1), + HomesteadBlock: big.NewInt(0), + DAOForkBlock: nil, + DAOForkSupport: false, + EIP150Block: big.NewInt(0), + EIP155Block: big.NewInt(0), + EIP158Block: big.NewInt(0), + ByzantiumBlock: big.NewInt(0), + ConstantinopleBlock: big.NewInt(0), + PetersburgBlock: big.NewInt(0), + IstanbulBlock: big.NewInt(0), + MuirGlacierBlock: big.NewInt(0), + BerlinBlock: big.NewInt(0), + LondonBlock: big.NewInt(0), + ArrowGlacierBlock: big.NewInt(0), + GrayGlacierBlock: big.NewInt(0), + MergeNetsplitBlock: big.NewInt(0), + ShanghaiTime: newUint64(0), + CancunTime: newUint64(0), + PragueTime: newUint64(0), + VerkleTime: nil, + TerminalTotalDifficulty: big.NewInt(0), + Ethash: new(EthashConfig), + Clique: nil, } // NonActivatedConfig defines the chain configuration without activating // any protocol change (EIPs). NonActivatedConfig = &ChainConfig{ - ChainID: big.NewInt(1), - HomesteadBlock: nil, - DAOForkBlock: nil, - DAOForkSupport: false, - EIP150Block: nil, - EIP155Block: nil, - EIP158Block: nil, - ByzantiumBlock: nil, - ConstantinopleBlock: nil, - PetersburgBlock: nil, - IstanbulBlock: nil, - MuirGlacierBlock: nil, - BerlinBlock: nil, - LondonBlock: nil, - ArrowGlacierBlock: nil, - GrayGlacierBlock: nil, - MergeNetsplitBlock: nil, - ShanghaiTime: nil, - CancunTime: nil, - PragueTime: nil, - VerkleTime: nil, - TerminalTotalDifficulty: nil, - TerminalTotalDifficultyPassed: false, - Ethash: new(EthashConfig), - Clique: nil, + ChainID: big.NewInt(1), + HomesteadBlock: nil, + DAOForkBlock: nil, + DAOForkSupport: false, + EIP150Block: nil, + EIP155Block: nil, + EIP158Block: nil, + ByzantiumBlock: nil, + ConstantinopleBlock: nil, + PetersburgBlock: nil, + IstanbulBlock: nil, + MuirGlacierBlock: nil, + BerlinBlock: nil, + LondonBlock: nil, + ArrowGlacierBlock: nil, + GrayGlacierBlock: nil, + MergeNetsplitBlock: nil, + ShanghaiTime: nil, + CancunTime: nil, + PragueTime: nil, + VerkleTime: nil, + TerminalTotalDifficulty: big.NewInt(math.MaxInt64), + Ethash: new(EthashConfig), + Clique: nil, } TestRules = TestChainConfig.Rules(new(big.Int), false, 0) ) @@ -332,13 +324,6 @@ type ChainConfig struct { // the network that triggers the consensus upgrade. TerminalTotalDifficulty *big.Int `json:"terminalTotalDifficulty,omitempty"` - // TerminalTotalDifficultyPassed is a flag specifying that the network already - // passed the terminal total difficulty. Its purpose is to disable legacy sync - // even without having seen the TTD locally (safer long term). - // - // TODO(karalabe): Drop this field eventually (always assuming PoS mode) - TerminalTotalDifficultyPassed bool `json:"terminalTotalDifficultyPassed,omitempty"` - DepositContractAddress common.Address `json:"depositContractAddress,omitempty"` // Various consensus engines @@ -377,21 +362,9 @@ func (c *ChainConfig) Description() string { banner += fmt.Sprintf("Chain ID: %v (%s)\n", c.ChainID, network) switch { case c.Ethash != nil: - if c.TerminalTotalDifficulty == nil { - banner += "Consensus: Ethash (proof-of-work)\n" - } else if !c.TerminalTotalDifficultyPassed { - banner += "Consensus: Beacon (proof-of-stake), merging from Ethash (proof-of-work)\n" - } else { - banner += "Consensus: Beacon (proof-of-stake), merged from Ethash (proof-of-work)\n" - } + banner += "Consensus: Beacon (proof-of-stake), merged from Ethash (proof-of-work)\n" case c.Clique != nil: - if c.TerminalTotalDifficulty == nil { - banner += "Consensus: Clique (proof-of-authority)\n" - } else if !c.TerminalTotalDifficultyPassed { - banner += "Consensus: Beacon (proof-of-stake), merging from Clique (proof-of-authority)\n" - } else { - banner += "Consensus: Beacon (proof-of-stake), merged from Clique (proof-of-authority)\n" - } + banner += "Consensus: Beacon (proof-of-stake), merged from Clique (proof-of-authority)\n" default: banner += "Consensus: unknown\n" } @@ -426,17 +399,12 @@ func (c *ChainConfig) Description() string { banner += "\n" // Add a special section for the merge as it's non-obvious - if c.TerminalTotalDifficulty == nil { - banner += "The Merge is not yet available for this network!\n" - banner += " - Hard-fork specification: https://github.com/ethereum/execution-specs/blob/master/network-upgrades/mainnet-upgrades/paris.md\n" - } else { - banner += "Merge configured:\n" - banner += " - Hard-fork specification: https://github.com/ethereum/execution-specs/blob/master/network-upgrades/mainnet-upgrades/paris.md\n" - banner += fmt.Sprintf(" - Network known to be merged: %v\n", c.TerminalTotalDifficultyPassed) - banner += fmt.Sprintf(" - Total terminal difficulty: %v\n", c.TerminalTotalDifficulty) - if c.MergeNetsplitBlock != nil { - banner += fmt.Sprintf(" - Merge netsplit block: #%-8v\n", c.MergeNetsplitBlock) - } + banner += "Merge configured:\n" + banner += " - Hard-fork specification: https://github.com/ethereum/execution-specs/blob/master/network-upgrades/mainnet-upgrades/paris.md\n" + banner += " - Network known to be merged\n" + banner += fmt.Sprintf(" - Total terminal difficulty: %v\n", c.TerminalTotalDifficulty) + if c.MergeNetsplitBlock != nil { + banner += fmt.Sprintf(" - Merge netsplit block: #%-8v\n", c.MergeNetsplitBlock) } banner += "\n" diff --git a/params/config_test.go b/params/config_test.go index fa444a1d0b..f658c336dc 100644 --- a/params/config_test.go +++ b/params/config_test.go @@ -17,12 +17,12 @@ package params import ( + "math" "math/big" "reflect" "testing" "time" - "github.com/ethereum/go-ethereum/common/math" "github.com/stretchr/testify/require" ) diff --git a/params/protocol_params.go b/params/protocol_params.go index 90e7487cff..b46e8d66b2 100644 --- a/params/protocol_params.go +++ b/params/protocol_params.go @@ -90,10 +90,11 @@ const ( SelfdestructRefundGas uint64 = 24000 // Refunded following a selfdestruct operation. MemoryGas uint64 = 3 // Times the address of the (highest referenced byte in memory + 1). NOTE: referencing happens on read, write and in instructions such as RETURN and CALL. - TxDataNonZeroGasFrontier uint64 = 68 // Per byte of data attached to a transaction that is not equal to zero. NOTE: Not payable on data of calls between transactions. - TxDataNonZeroGasEIP2028 uint64 = 16 // Per byte of non zero data attached to a transaction after EIP 2028 (part in Istanbul) - TxAccessListAddressGas uint64 = 2400 // Per address specified in EIP 2930 access list - TxAccessListStorageKeyGas uint64 = 1900 // Per storage key specified in EIP 2930 access list + TxDataNonZeroGasFrontier uint64 = 68 // Per byte of data attached to a transaction that is not equal to zero. NOTE: Not payable on data of calls between transactions. + TxDataNonZeroGasEIP2028 uint64 = 16 // Per byte of non zero data attached to a transaction after EIP 2028 (part in Istanbul) + TxAccessListAddressGas uint64 = 2400 // Per address specified in EIP 2930 access list + TxAccessListStorageKeyGas uint64 = 1900 // Per storage key specified in EIP 2930 access list + TxAuthTupleGas uint64 = 12500 // Per auth tuple code specified in EIP-7702 // These have been changed during the course of the chain CallGasFrontier uint64 = 40 // Once per CALL operation & message call transaction. @@ -199,14 +200,14 @@ var ( BeaconRootsCode = common.FromHex("3373fffffffffffffffffffffffffffffffffffffffe14604d57602036146024575f5ffd5b5f35801560495762001fff810690815414603c575f5ffd5b62001fff01545f5260205ff35b5f5ffd5b62001fff42064281555f359062001fff015500") // EIP-2935 - Serve historical block hashes from state - HistoryStorageAddress = common.HexToAddress("0x0aae40965e6800cd9b1f4b05ff21581047e3f91e") - HistoryStorageCode = common.FromHex("3373fffffffffffffffffffffffffffffffffffffffe1460575767ffffffffffffffff5f3511605357600143035f3511604b575f35612000014311604b57611fff5f3516545f5260205ff35b5f5f5260205ff35b5f5ffd5b5f35611fff60014303165500") + HistoryStorageAddress = common.HexToAddress("0x0F792be4B0c0cb4DAE440Ef133E90C0eCD48CCCC") + HistoryStorageCode = common.FromHex("3373fffffffffffffffffffffffffffffffffffffffe14604657602036036042575f35600143038111604257611fff81430311604257611fff9006545f5260205ff35b5f5ffd5b5f35611fff60014303065500") // EIP-7002 - Execution layer triggerable withdrawals - WithdrawalQueueAddress = common.HexToAddress("0x09Fc772D0857550724b07B850a4323f39112aAaA") - WithdrawalQueueCode = common.FromHex("3373fffffffffffffffffffffffffffffffffffffffe1460c7573615156028575f545f5260205ff35b36603814156101f05760115f54807fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff146101f057600182026001905f5b5f821115608057810190830284830290049160010191906065565b9093900434106101f057600154600101600155600354806003026004013381556001015f35815560010160203590553360601b5f5260385f601437604c5fa0600101600355005b6003546002548082038060101160db575060105b5f5b81811461017f5780604c02838201600302600401805490600101805490600101549160601b83528260140152807fffffffffffffffffffffffffffffffff0000000000000000000000000000000016826034015260401c906044018160381c81600701538160301c81600601538160281c81600501538160201c81600401538160181c81600301538160101c81600201538160081c81600101535360010160dd565b9101809214610191579060025561019c565b90505f6002555f6003555b5f54807fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff14156101c957505f5b6001546002828201116101de5750505f6101e4565b01600290035b5f555f600155604c025ff35b5f5ffd") + WithdrawalQueueAddress = common.HexToAddress("0x0c15F14308530b7CDB8460094BbB9cC28b9AaaAA") + WithdrawalQueueCode = common.FromHex("3373fffffffffffffffffffffffffffffffffffffffe1460cb5760115f54807fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff146101f457600182026001905f5b5f82111560685781019083028483029004916001019190604d565b909390049250505036603814608857366101f457346101f4575f5260205ff35b34106101f457600154600101600155600354806003026004013381556001015f35815560010160203590553360601b5f5260385f601437604c5fa0600101600355005b6003546002548082038060101160df575060105b5f5b8181146101835782810160030260040181604c02815460601b8152601401816001015481526020019060020154807fffffffffffffffffffffffffffffffff00000000000000000000000000000000168252906010019060401c908160381c81600701538160301c81600601538160281c81600501538160201c81600401538160181c81600301538160101c81600201538160081c81600101535360010160e1565b910180921461019557906002556101a0565b90505f6002555f6003555b5f54807fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff14156101cd57505f5b6001546002828201116101e25750505f6101e8565b01600290035b5f555f600155604c025ff35b5f5ffd") // EIP-7251 - Increase the MAX_EFFECTIVE_BALANCE - ConsolidationQueueAddress = common.HexToAddress("0x01aBEa29659e5e97C95107F20bb753cD3e09bBBb") - ConsolidationQueueCode = common.FromHex("3373fffffffffffffffffffffffffffffffffffffffe1460cf573615156028575f545f5260205ff35b366060141561019a5760115f54807fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff1461019a57600182026001905f5b5f821115608057810190830284830290049160010191906065565b90939004341061019a57600154600101600155600354806004026004013381556001015f358155600101602035815560010160403590553360601b5f5260605f60143760745fa0600101600355005b6003546002548082038060011160e3575060015b5f5b8181146101295780607402838201600402600401805490600101805490600101805490600101549260601b84529083601401528260340152906054015260010160e5565b910180921461013b5790600255610146565b90505f6002555f6003555b5f54807fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff141561017357505f5b6001546001828201116101885750505f61018e565b01600190035b5f555f6001556074025ff35b5f5ffd") + ConsolidationQueueAddress = common.HexToAddress("0x00431F263cE400f4455c2dCf564e53007Ca4bbBb") + ConsolidationQueueCode = common.FromHex("3373fffffffffffffffffffffffffffffffffffffffe1460d35760115f54807fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff1461019a57600182026001905f5b5f82111560685781019083028483029004916001019190604d565b9093900492505050366060146088573661019a573461019a575f5260205ff35b341061019a57600154600101600155600354806004026004013381556001015f358155600101602035815560010160403590553360601b5f5260605f60143760745fa0600101600355005b6003546002548082038060021160e7575060025b5f5b8181146101295782810160040260040181607402815460601b815260140181600101548152602001816002015481526020019060030154905260010160e9565b910180921461013b5790600255610146565b90505f6002555f6003555b5f54807fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff141561017357505f5b6001546001828201116101885750505f61018e565b01600190035b5f555f6001556074025ff35b5f5ffd") ) diff --git a/params/version.go b/params/version.go deleted file mode 100644 index e895503e7f..0000000000 --- a/params/version.go +++ /dev/null @@ -1,67 +0,0 @@ -// Copyright 2016 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 params - -import ( - "fmt" -) - -const ( - VersionMajor = 1 // Major version component of the current release - VersionMinor = 14 // Minor version component of the current release - VersionPatch = 12 // Patch version component of the current release - VersionMeta = "unstable" // Version metadata to append to the version string -) - -// Version holds the textual version string. -var Version = func() string { - return fmt.Sprintf("%d.%d.%d", VersionMajor, VersionMinor, VersionPatch) -}() - -// VersionWithMeta holds the textual version string including the metadata. -var VersionWithMeta = func() string { - v := Version - if VersionMeta != "" { - v += "-" + VersionMeta - } - return v -}() - -// ArchiveVersion holds the textual version string used for Geth archives. e.g. -// "1.8.11-dea1ce05" for stable releases, or "1.8.13-unstable-21c059b6" for unstable -// releases. -func ArchiveVersion(gitCommit string) string { - vsn := Version - if VersionMeta != "stable" { - vsn += "-" + VersionMeta - } - if len(gitCommit) >= 8 { - vsn += "-" + gitCommit[:8] - } - return vsn -} - -func VersionWithCommit(gitCommit, gitDate string) string { - vsn := VersionWithMeta - if len(gitCommit) >= 8 { - vsn += "-" + gitCommit[:8] - } - if (VersionMeta != "stable") && (gitDate != "") { - vsn += "-" + gitDate - } - return vsn -} diff --git a/rlp/decode_test.go b/rlp/decode_test.go index 8479a95b25..3e492188e8 100644 --- a/rlp/decode_test.go +++ b/rlp/decode_test.go @@ -22,6 +22,7 @@ import ( "errors" "fmt" "io" + gomath "math" "math/big" "reflect" "strings" @@ -555,7 +556,7 @@ var decodeTests = []decodeTest{ // uint256 {input: "80", ptr: new(*uint256.Int), value: uint256.NewInt(0)}, {input: "01", ptr: new(*uint256.Int), value: uint256.NewInt(1)}, - {input: "88FFFFFFFFFFFFFFFF", ptr: new(*uint256.Int), value: uint256.NewInt(math.MaxUint64)}, + {input: "88FFFFFFFFFFFFFFFF", ptr: new(*uint256.Int), value: uint256.NewInt(gomath.MaxUint64)}, {input: "89FFFFFFFFFFFFFFFFFF", ptr: new(*uint256.Int), value: veryBigInt256}, {input: "10", ptr: new(uint256.Int), value: *uint256.NewInt(16)}, // non-pointer also works diff --git a/rpc/client_test.go b/rpc/client_test.go index 49f2350b40..6c1a4f8f6c 100644 --- a/rpc/client_test.go +++ b/rpc/client_test.go @@ -38,6 +38,8 @@ import ( ) func TestClientRequest(t *testing.T) { + t.Parallel() + server := newTestServer() defer server.Stop() client := DialInProc(server) @@ -53,6 +55,8 @@ func TestClientRequest(t *testing.T) { } func TestClientResponseType(t *testing.T) { + t.Parallel() + server := newTestServer() defer server.Stop() client := DialInProc(server) @@ -71,6 +75,8 @@ func TestClientResponseType(t *testing.T) { // This test checks calling a method that returns 'null'. func TestClientNullResponse(t *testing.T) { + t.Parallel() + server := newTestServer() defer server.Stop() @@ -91,6 +97,8 @@ func TestClientNullResponse(t *testing.T) { // This test checks that server-returned errors with code and data come out of Client.Call. func TestClientErrorData(t *testing.T) { + t.Parallel() + server := newTestServer() defer server.Stop() client := DialInProc(server) @@ -121,6 +129,8 @@ func TestClientErrorData(t *testing.T) { } func TestClientBatchRequest(t *testing.T) { + t.Parallel() + server := newTestServer() defer server.Stop() client := DialInProc(server) @@ -172,6 +182,8 @@ func TestClientBatchRequest(t *testing.T) { // This checks that, for HTTP connections, the length of batch responses is validated to // match the request exactly. func TestClientBatchRequest_len(t *testing.T) { + t.Parallel() + b, err := json.Marshal([]jsonrpcMessage{ {Version: "2.0", ID: json.RawMessage("1"), Result: json.RawMessage(`"0x1"`)}, {Version: "2.0", ID: json.RawMessage("2"), Result: json.RawMessage(`"0x2"`)}, @@ -188,6 +200,8 @@ func TestClientBatchRequest_len(t *testing.T) { t.Cleanup(s.Close) t.Run("too-few", func(t *testing.T) { + t.Parallel() + client, err := Dial(s.URL) if err != nil { t.Fatal("failed to dial test server:", err) @@ -218,6 +232,8 @@ func TestClientBatchRequest_len(t *testing.T) { }) t.Run("too-many", func(t *testing.T) { + t.Parallel() + client, err := Dial(s.URL) if err != nil { t.Fatal("failed to dial test server:", err) @@ -249,6 +265,8 @@ func TestClientBatchRequest_len(t *testing.T) { // This checks that the client can handle the case where the server doesn't // respond to all requests in a batch. func TestClientBatchRequestLimit(t *testing.T) { + t.Parallel() + server := newTestServer() defer server.Stop() server.SetBatchLimits(2, 100000) @@ -285,6 +303,8 @@ func TestClientBatchRequestLimit(t *testing.T) { } func TestClientNotify(t *testing.T) { + t.Parallel() + server := newTestServer() defer server.Stop() client := DialInProc(server) @@ -392,6 +412,8 @@ func testClientCancel(transport string, t *testing.T) { } func TestClientSubscribeInvalidArg(t *testing.T) { + t.Parallel() + server := newTestServer() defer server.Stop() client := DialInProc(server) @@ -422,6 +444,8 @@ func TestClientSubscribeInvalidArg(t *testing.T) { } func TestClientSubscribe(t *testing.T) { + t.Parallel() + server := newTestServer() defer server.Stop() client := DialInProc(server) @@ -454,6 +478,8 @@ func TestClientSubscribe(t *testing.T) { // In this test, the connection drops while Subscribe is waiting for a response. func TestClientSubscribeClose(t *testing.T) { + t.Parallel() + server := newTestServer() service := ¬ificationTestService{ gotHangSubscriptionReq: make(chan struct{}), @@ -498,6 +524,8 @@ func TestClientSubscribeClose(t *testing.T) { // This test reproduces https://github.com/ethereum/go-ethereum/issues/17837 where the // client hangs during shutdown when Unsubscribe races with Client.Close. func TestClientCloseUnsubscribeRace(t *testing.T) { + t.Parallel() + server := newTestServer() defer server.Stop() @@ -540,6 +568,8 @@ func (b *unsubscribeBlocker) readBatch() ([]*jsonrpcMessage, bool, error) { // not respond. // It reproducers the issue https://github.com/ethereum/go-ethereum/issues/30156 func TestUnsubscribeTimeout(t *testing.T) { + t.Parallel() + srv := NewServer() srv.RegisterName("nftest", new(notificationTestService)) @@ -674,6 +704,8 @@ func TestClientSubscriptionChannelClose(t *testing.T) { // This test checks that Client doesn't lock up when a single subscriber // doesn't read subscription events. func TestClientNotificationStorm(t *testing.T) { + t.Parallel() + server := newTestServer() defer server.Stop() @@ -726,6 +758,8 @@ func TestClientNotificationStorm(t *testing.T) { } func TestClientSetHeader(t *testing.T) { + t.Parallel() + var gotHeader bool srv := newTestServer() httpsrv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { @@ -762,6 +796,8 @@ func TestClientSetHeader(t *testing.T) { } func TestClientHTTP(t *testing.T) { + t.Parallel() + server := newTestServer() defer server.Stop() @@ -804,6 +840,8 @@ func TestClientHTTP(t *testing.T) { } func TestClientReconnect(t *testing.T) { + t.Parallel() + startServer := func(addr string) (*Server, net.Listener) { srv := newTestServer() l, err := net.Listen("tcp", addr) diff --git a/rpc/http_test.go b/rpc/http_test.go index ad86ca15ae..6c268b6292 100644 --- a/rpc/http_test.go +++ b/rpc/http_test.go @@ -58,24 +58,34 @@ func confirmRequestValidationCode(t *testing.T, method, contentType, body string } func TestHTTPErrorResponseWithDelete(t *testing.T) { + t.Parallel() + confirmRequestValidationCode(t, http.MethodDelete, contentType, "", http.StatusMethodNotAllowed) } func TestHTTPErrorResponseWithPut(t *testing.T) { + t.Parallel() + confirmRequestValidationCode(t, http.MethodPut, contentType, "", http.StatusMethodNotAllowed) } func TestHTTPErrorResponseWithMaxContentLength(t *testing.T) { + t.Parallel() + body := make([]rune, defaultBodyLimit+1) confirmRequestValidationCode(t, http.MethodPost, contentType, string(body), http.StatusRequestEntityTooLarge) } func TestHTTPErrorResponseWithEmptyContentType(t *testing.T) { + t.Parallel() + confirmRequestValidationCode(t, http.MethodPost, "", "", http.StatusUnsupportedMediaType) } func TestHTTPErrorResponseWithValidRequest(t *testing.T) { + t.Parallel() + confirmRequestValidationCode(t, http.MethodPost, contentType, "", 0) } @@ -101,11 +111,15 @@ func confirmHTTPRequestYieldsStatusCode(t *testing.T, method, contentType, body } func TestHTTPResponseWithEmptyGet(t *testing.T) { + t.Parallel() + confirmHTTPRequestYieldsStatusCode(t, http.MethodGet, "", "", http.StatusOK) } // This checks that maxRequestContentLength is not applied to the response of a request. func TestHTTPRespBodyUnlimited(t *testing.T) { + t.Parallel() + const respLength = defaultBodyLimit * 3 s := NewServer() @@ -132,6 +146,8 @@ func TestHTTPRespBodyUnlimited(t *testing.T) { // Tests that an HTTP error results in an HTTPError instance // being returned with the expected attributes. func TestHTTPErrorResponse(t *testing.T) { + t.Parallel() + ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { http.Error(w, "error has occurred!", http.StatusTeapot) })) @@ -169,6 +185,8 @@ func TestHTTPErrorResponse(t *testing.T) { } func TestHTTPPeerInfo(t *testing.T) { + t.Parallel() + s := newTestServer() defer s.Stop() ts := httptest.NewServer(s) @@ -205,6 +223,8 @@ func TestHTTPPeerInfo(t *testing.T) { } func TestNewContextWithHeaders(t *testing.T) { + t.Parallel() + expectedHeaders := 0 server := httptest.NewServer(http.HandlerFunc(func(writer http.ResponseWriter, request *http.Request) { for i := 0; i < expectedHeaders; i++ { diff --git a/rpc/server_test.go b/rpc/server_test.go index 9d1c7fb5f0..9ee545d81a 100644 --- a/rpc/server_test.go +++ b/rpc/server_test.go @@ -29,6 +29,8 @@ import ( ) func TestServerRegisterName(t *testing.T) { + t.Parallel() + server := NewServer() service := new(testService) @@ -53,6 +55,8 @@ func TestServerRegisterName(t *testing.T) { } func TestServer(t *testing.T) { + t.Parallel() + files, err := os.ReadDir("testdata") if err != nil { t.Fatal("where'd my testdata go?") @@ -64,6 +68,8 @@ func TestServer(t *testing.T) { path := filepath.Join("testdata", f.Name()) name := strings.TrimSuffix(f.Name(), filepath.Ext(f.Name())) t.Run(name, func(t *testing.T) { + t.Parallel() + runTestScript(t, path) }) } @@ -116,6 +122,8 @@ func runTestScript(t *testing.T, file string) { // This test checks that responses are delivered for very short-lived connections that // only carry a single request. func TestServerShortLivedConn(t *testing.T) { + t.Parallel() + server := newTestServer() defer server.Stop() @@ -156,6 +164,8 @@ func TestServerShortLivedConn(t *testing.T) { } func TestServerBatchResponseSizeLimit(t *testing.T) { + t.Parallel() + server := newTestServer() defer server.Stop() server.SetBatchLimits(100, 60) diff --git a/rpc/subscription_test.go b/rpc/subscription_test.go index ab40ab169f..e52f390adb 100644 --- a/rpc/subscription_test.go +++ b/rpc/subscription_test.go @@ -33,6 +33,8 @@ import ( ) func TestNewID(t *testing.T) { + t.Parallel() + hexchars := "0123456789ABCDEFabcdef" for i := 0; i < 100; i++ { id := string(NewID()) @@ -54,6 +56,8 @@ func TestNewID(t *testing.T) { } func TestSubscriptions(t *testing.T) { + t.Parallel() + var ( namespaces = []string{"eth", "bzz"} service = ¬ificationTestService{} @@ -132,6 +136,8 @@ func TestSubscriptions(t *testing.T) { // This test checks that unsubscribing works. func TestServerUnsubscribe(t *testing.T) { + t.Parallel() + p1, p2 := net.Pipe() defer p2.Close() @@ -260,6 +266,8 @@ func BenchmarkNotify(b *testing.B) { } func TestNotify(t *testing.T) { + t.Parallel() + out := new(bytes.Buffer) id := ID("test") notifier := &Notifier{ diff --git a/rpc/types_test.go b/rpc/types_test.go index 64833ffea6..9dd6fa6508 100644 --- a/rpc/types_test.go +++ b/rpc/types_test.go @@ -18,14 +18,16 @@ package rpc import ( "encoding/json" + "math" "reflect" "testing" "github.com/ethereum/go-ethereum/common" - "github.com/ethereum/go-ethereum/common/math" ) func TestBlockNumberJSONUnmarshal(t *testing.T) { + t.Parallel() + tests := []struct { input string mustFail bool @@ -70,6 +72,8 @@ func TestBlockNumberJSONUnmarshal(t *testing.T) { } func TestBlockNumberOrHash_UnmarshalJSON(t *testing.T) { + t.Parallel() + tests := []struct { input string mustFail bool @@ -131,6 +135,8 @@ func TestBlockNumberOrHash_UnmarshalJSON(t *testing.T) { } func TestBlockNumberOrHash_WithNumber_MarshalAndUnmarshal(t *testing.T) { + t.Parallel() + tests := []struct { name string number int64 @@ -144,6 +150,8 @@ func TestBlockNumberOrHash_WithNumber_MarshalAndUnmarshal(t *testing.T) { } for _, test := range tests { t.Run(test.name, func(t *testing.T) { + t.Parallel() + bnh := BlockNumberOrHashWithNumber(BlockNumber(test.number)) marshalled, err := json.Marshal(bnh) if err != nil { @@ -162,6 +170,8 @@ func TestBlockNumberOrHash_WithNumber_MarshalAndUnmarshal(t *testing.T) { } func TestBlockNumberOrHash_StringAndUnmarshal(t *testing.T) { + t.Parallel() + tests := []BlockNumberOrHash{ BlockNumberOrHashWithNumber(math.MaxInt64), BlockNumberOrHashWithNumber(PendingBlockNumber), diff --git a/rpc/websocket_test.go b/rpc/websocket_test.go index c6ea325d29..10a998b351 100644 --- a/rpc/websocket_test.go +++ b/rpc/websocket_test.go @@ -174,6 +174,8 @@ func TestWebsocketLargeRead(t *testing.T) { } func TestWebsocketPeerInfo(t *testing.T) { + t.Parallel() + var ( s = newTestServer() ts = httptest.NewServer(s.WebsocketHandler([]string{"origin.example.com"})) @@ -259,6 +261,8 @@ func TestClientWebsocketPing(t *testing.T) { // This checks that the websocket transport can deal with large messages. func TestClientWebsocketLargeMessage(t *testing.T) { + t.Parallel() + var ( srv = NewServer() httpsrv = httptest.NewServer(srv.WebsocketHandler(nil)) diff --git a/signer/core/api.go b/signer/core/api.go index 23ddcd0a20..def2d6041f 100644 --- a/signer/core/api.go +++ b/signer/core/api.go @@ -184,9 +184,7 @@ func StartClefAccountManager(ksLocation string, nousb, lightKDF bool, scpath str } } } - - // Clef doesn't allow insecure http account unlock. - return accounts.NewManager(&accounts.Config{InsecureUnlockAllowed: false}, backends...) + return accounts.NewManager(nil, backends...) } // MetadataFromContext extracts Metadata from a given context.Context diff --git a/signer/core/apitypes/signed_data_internal_test.go b/signer/core/apitypes/signed_data_internal_test.go index 8b4a839c1d..1a14b35ef2 100644 --- a/signer/core/apitypes/signed_data_internal_test.go +++ b/signer/core/apitypes/signed_data_internal_test.go @@ -18,12 +18,18 @@ package apitypes import ( "bytes" + "encoding/json" + "fmt" "math/big" + "os" "testing" "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common/hexutil" "github.com/ethereum/go-ethereum/common/math" + "github.com/ethereum/go-ethereum/crypto" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" ) func TestBytesPadding(t *testing.T) { @@ -244,45 +250,42 @@ func TestConvertAddressDataToSlice(t *testing.T) { func TestTypedDataArrayValidate(t *testing.T) { t.Parallel() - typedData := TypedData{ - Types: Types{ - "BulkOrder": []Type{ - // Should be able to accept fixed size arrays - {Name: "tree", Type: "OrderComponents[2][2]"}, - }, - "OrderComponents": []Type{ - {Name: "offerer", Type: "address"}, - {Name: "amount", Type: "uint8"}, - }, - "EIP712Domain": []Type{ - {Name: "name", Type: "string"}, - {Name: "version", Type: "string"}, - {Name: "chainId", Type: "uint8"}, - {Name: "verifyingContract", Type: "address"}, - }, - }, - PrimaryType: "BulkOrder", - Domain: TypedDataDomain{ - VerifyingContract: "0xCcCCccccCCCCcCCCCCCcCcCccCcCCCcCcccccccC", - }, - Message: TypedDataMessage{}, + type testDataInput struct { + Name string `json:"name"` + Domain TypedDataDomain `json:"domain"` + PrimaryType string `json:"primaryType"` + Types Types `json:"types"` + Message TypedDataMessage `json:"data"` + Digest string `json:"digest"` } + fc, err := os.ReadFile("./testdata/typed-data.json") + require.NoError(t, err, "error reading test data file") - if err := typedData.validate(); err != nil { - t.Errorf("expected typed data to pass validation, got: %v", err) - } + var tests []testDataInput + err = json.Unmarshal(fc, &tests) + require.NoError(t, err, "error unmarshalling test data file contents") - // Should be able to accept dynamic arrays - typedData.Types["BulkOrder"][0].Type = "OrderComponents[]" + for _, tc := range tests { + t.Run(tc.Name, func(t *testing.T) { + t.Parallel() - if err := typedData.validate(); err != nil { - t.Errorf("expected typed data to pass validation, got: %v", err) - } + td := TypedData{ + Types: tc.Types, + PrimaryType: tc.PrimaryType, + Domain: tc.Domain, + Message: tc.Message, + } - // Should be able to accept standard types - typedData.Types["BulkOrder"][0].Type = "OrderComponents" + domainSeparator, tErr := td.HashStruct("EIP712Domain", td.Domain.Map()) + assert.NoError(t, tErr, "failed to hash domain separator: %v", tErr) - if err := typedData.validate(); err != nil { - t.Errorf("expected typed data to pass validation, got: %v", err) + messageHash, tErr := td.HashStruct(td.PrimaryType, td.Message) + assert.NoError(t, tErr, "failed to hash message: %v", tErr) + + digest := crypto.Keccak256Hash([]byte(fmt.Sprintf("%s%s%s", "\x19\x01", string(domainSeparator), string(messageHash)))) + assert.Equal(t, tc.Digest, digest.String(), "digest doesn't not match") + + assert.NoError(t, td.validate(), "validation failed", tErr) + }) } } diff --git a/signer/core/apitypes/testdata/typed-data.json b/signer/core/apitypes/testdata/typed-data.json new file mode 100644 index 0000000000..f77f72e5eb --- /dev/null +++ b/signer/core/apitypes/testdata/typed-data.json @@ -0,0 +1,6089 @@ +[ + { + "name": "random-0", + "domain": { + "name": "Moo é🚀ooéééMooooM🚀 o🚀🚀o M oM🚀éo 🚀🚀🚀🚀éoMoéo🚀o", + "version": "28.44.13" + }, + "primaryType": "Struct3", + "types": { + "EIP712Domain": [ + { + "name": "name", + "type": "string" + }, + { + "name": "version", + "type": "string" + } + ], + "Struct3": [ + { + "name": "param2", + "type": "bytes" + } + ] + }, + "data": { + "param2": "0xdce44ca98616ee629199215ae5401c97040664637c48" + }, + "encoded": "0xcdf7d44b9a42bfc5a90b1624215e30c70425b44f1c62f94244b32551826d2dd995cff8fcf943ffa581b017b61b02703628c843642652c382dd15c9a471fe28d9", + "digest": "0xf1a2769507736a9aa306204169e6862f4416e055035d7d2cc9ab6f1921604905" + }, + { + "name": "random-1", + "domain": { + "name": "Moo é🚀éoMo🚀 oé🚀🚀🚀MéooMéooo éo oé 🚀M🚀 🚀 o", + "version": "22.43.44", + "chainId": 1268, + "salt": "0x6ebb306942854acbb10134c9dee015937042c39da2ee124eb926ad77df52dbe0" + }, + "primaryType": "Struct6", + "types": { + "EIP712Domain": [ + { + "name": "name", + "type": "string" + }, + { + "name": "version", + "type": "string" + }, + { + "name": "chainId", + "type": "uint256" + }, + { + "name": "salt", + "type": "bytes32" + } + ], + "Struct6": [ + { + "name": "param2", + "type": "bytes" + }, + { + "name": "param3", + "type": "bytes11" + }, + { + "name": "param4", + "type": "bytes" + }, + { + "name": "param5", + "type": "string" + } + ] + }, + "data": { + "param2": "0x2364d8559a1777b684a9121d132c4b4237e2534bd5a0", + "param3": "0x90166c1d5cf7f1be5e4535", + "param4": "0x0f6c35f4b0fa348c603ee0070c8f4f971805c4d9d2ddb8acb82e806e1f4b2c1bc500e41b882213648af39dd4a29d303a31f68476cf803ef8c9024509b2f164", + "param5": "Moo é🚀MoM🚀éoMMooM🚀ooéM Mééo" + }, + "encoded": "0xda8977a44f657114a662894ef7761924845f9e7530ec21622e1a6d5526de0d1ec611cbc6650fb22a47f359606312a4412acbc7b648fa712da2d0e65a00e44f8990166c1d5cf7f1be5e453500000000000000000000000000000000000000000082609c13a160a82264f3293420c066ad847fc2c658862f6282c13848e7c2bfa3c422f8f8d57a0d73e4448edcb393d45cd1969652b199e87011a5c54171f7a548", + "digest": "0xdca475186d6626bdd727f5a216758f6351c56b36ae77683f3b381c5b296d1099" + }, + { + "name": "random-2", + "domain": { + "verifyingContract": "0xb98ccb3b2f1843cdd391295779890c162f2833ea" + }, + "primaryType": "Struct6", + "types": { + "EIP712Domain": [ + { + "name": "verifyingContract", + "type": "address" + } + ], + "Struct6": [ + { + "name": "param2", + "type": "int32" + }, + { + "name": "param3", + "type": "string[3]" + }, + { + "name": "param5", + "type": "address" + } + ] + }, + "data": { + "param2": "-828619503", + "param3": [ + "Moo é🚀o🚀oo🚀o ooééM M🚀éoééoMMooo🚀éoooéooMéoéMM oé🚀Mé éé", + "Moo é🚀o🚀M ééé🚀 o oMéoMéM o🚀oMoo🚀é🚀 é é🚀M🚀é Mooééo🚀é", + "Moo é🚀 🚀oooéé o🚀oéMooM🚀🚀 oo M🚀M🚀ooMoMoooé🚀M🚀 🚀M🚀🚀🚀éM" + ], + "param5": "0xd5cf50b584016c19732d845cc9c8d3a43ce41362" + }, + "encoded": "0x67fb8e8c0399ea6a53c5be40a9cc57f8682c0e4887d4e92733d7e77e358fb473ffffffffffffffffffffffffffffffffffffffffffffffffffffffffce9c451142865dc16e0353e94811b8b8df478cf0a5714219aa578dd5881f162ef224cb2c000000000000000000000000d5cf50b584016c19732d845cc9c8d3a43ce41362", + "digest": "0x6c32dc60957ea693087837ae10ba9d9e31febf7a0c2ed00f6b57ac02f4d4b37e" + }, + { + "name": "random-3", + "domain": { + "name": "Moo é🚀M oMoo🚀éoo🚀ooo ooéééo🚀éMoo🚀o o oo 🚀oooM ", + "version": "31.7.9", + "chainId": 793 + }, + "primaryType": "Struct5", + "types": { + "EIP712Domain": [ + { + "name": "name", + "type": "string" + }, + { + "name": "version", + "type": "string" + }, + { + "name": "chainId", + "type": "uint256" + } + ], + "Struct5": [ + { + "name": "param2", + "type": "bytes" + }, + { + "name": "param3", + "type": "bytes" + }, + { + "name": "param4", + "type": "string" + } + ] + }, + "data": { + "param2": "0x2302fce888f2dc9d6ec2b3d3fc06aa212ec06b07f4035f64fcc58f1e178bee", + "param3": "0xb1d7e299", + "param4": "Moo é🚀 ooo🚀🚀o🚀ooéé oé" + }, + "encoded": "0xb294e832799f75dfe653c1529c1464de82ad988a243b4cd2dad2e8231ce02ac8f98e5673d8c98474e896eb51f7710e3096ac480f57c343aa4b6940f14ba864cfc9825dc5acadefe8114be8b3b40ff1735c38ce7a2bd1af26b8f896f448f71b2d92ca886c2f1728e95855af472331fec2b8cbcb28901e0b5e5e7c0fcdfb82df75", + "digest": "0x29afbb5d796c6d1b9e79071d245061a8d284ffabf3138483d13736a61780ccdd" + }, + { + "name": "random-4", + "domain": { + "name": "Moo é🚀oo ", + "chainId": 1190, + "salt": "0x1f37012abd2887491b2dc97283565221433f671fe1e39aa52501bfb6aa8b93c3" + }, + "primaryType": "Struct6", + "types": { + "EIP712Domain": [ + { + "name": "name", + "type": "string" + }, + { + "name": "chainId", + "type": "uint256" + }, + { + "name": "salt", + "type": "bytes32" + } + ], + "Struct6": [ + { + "name": "param2", + "type": "bytes10" + }, + { + "name": "param3", + "type": "string[1]" + }, + { + "name": "param5", + "type": "bytes27" + } + ] + }, + "data": { + "param2": "0x9bb8048b699386b24539", + "param3": [ + "Moo é🚀 éo🚀oMMéoo ooM🚀ooMo oMoéoéé oo 🚀oMMé🚀🚀ooooMéMoé 🚀oéooooéM" + ], + "param5": "0x87522812e1a8337045160896fb3e61f869b4154b737a082b3dfeb7" + }, + "encoded": "0xd6c2b6107cccf91b779f9954f2110d1b60cbc77e11fba6eaea01e944fd9cf1779bb8048b699386b2453900000000000000000000000000000000000000000000efd410fd47fe79acfda00711d702442f7cf6312190754acb4613b1c2aca0dec187522812e1a8337045160896fb3e61f869b4154b737a082b3dfeb70000000000", + "digest": "0xf4f1328085f730d46a20fa49f6e7ac254f35447282c91a7530242a3a14474116" + }, + { + "name": "random-5", + "domain": { + "version": "7.9.9" + }, + "primaryType": "Struct4", + "types": { + "EIP712Domain": [ + { + "name": "version", + "type": "string" + } + ], + "Struct4": [ + { + "name": "param2", + "type": "address" + }, + { + "name": "param3", + "type": "int224" + } + ] + }, + "data": { + "param2": "0x2f89d3d2d83f46d0147efc081e3a3f1012406c69", + "param3": "-1117663001459922125771233981131891587208615016687544676675378200714" + }, + "encoded": "0xded571df13886530cfad8a76abc529603094afe8d4763a3c714a8a86241c16b10000000000000000000000002f89d3d2d83f46d0147efc081e3a3f1012406c69fffffffff5631c9b4cdb160013e3cedb0d8e15ad26177e422ad39cfdf0483f76", + "digest": "0x1f4896667fd2210e54760bd5f00b964447be3c0a20d9d64f8324821a44dbe9a8" + }, + { + "name": "random-6", + "domain": { + "name": "Moo é🚀", + "verifyingContract": "0x501809f11ffb4ec90411dca095641b87f3229df9", + "salt": "0x613b2e73477c57fc4e28b4c06f436f9825b5aa4d839c3d07a89179ef2774f76e" + }, + "primaryType": "Struct6", + "types": { + "EIP712Domain": [ + { + "name": "name", + "type": "string" + }, + { + "name": "verifyingContract", + "type": "address" + }, + { + "name": "salt", + "type": "bytes32" + } + ], + "Struct6": [ + { + "name": "param2", + "type": "bytes" + }, + { + "name": "param3", + "type": "bytes" + }, + { + "name": "param4", + "type": "bool" + }, + { + "name": "param5", + "type": "address" + } + ] + }, + "data": { + "param2": "0x85a6f822054409e83460b9da070ce6c48843e4793c5720ad69102099b4a52978a90e32242a8b7df6fc70ddc8f6056c1df4585c727e4dac", + "param3": "0xac0ca9396225af9df605b4d3a3273d0c075c5dc2254b1d69fabcffb9cb191cb7253e55c547f8627a79", + "param4": false, + "param5": "0x083e80e0d94dbb979ce2c44a2c746ecfa1fca9f1" + }, + "encoded": "0xe5b40a524086d02c883ca8067a58dc9b784c44542de0bc37d5769a0dbeca31dfdcd5d1450de8a86034460ed9842ded280ce4faa8919759337539cca0876a7da6c95d6f26124f374b1007778a627aabfe799b09620b14cd82e0ce2dc27b5b4aba0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000083e80e0d94dbb979ce2c44a2c746ecfa1fca9f1", + "digest": "0x77f779953f0ad6e059d13f714999b8f2e0916f3c4263db272606efa34c18c4eb" + }, + { + "name": "random-7", + "domain": { + "version": "29.42.9", + "verifyingContract": "0xa2f34b603e4ee3de26502a40c8dc33886c1bb7e0" + }, + "primaryType": "Struct6", + "types": { + "EIP712Domain": [ + { + "name": "version", + "type": "string" + }, + { + "name": "verifyingContract", + "type": "address" + } + ], + "Struct6": [ + { + "name": "param2", + "type": "bytes" + }, + { + "name": "param3", + "type": "bytes[2]" + }, + { + "name": "param5", + "type": "string" + } + ] + }, + "data": { + "param2": "0xb7788f98b3107c588bee30ccc844e129a7772df540c3193239df", + "param3": [ + "0xe85938a8c29ab7b82264cc2e0822673fe17637364d6b384eb49f89e1adf61a11", + "0x10ec7831da9a49dbf10818" + ], + "param5": "Moo é🚀ooooM🚀 oMééM éé🚀🚀oMé 🚀 é éé" + }, + "encoded": "0xcf3bbe1dcf56fb9655824a3f90f1953ea791787867fb34004275382e4f47a2bb1c88290b2a99cb28a10db644f89833ecc797c9d2d9c142a5b633939e1c8819bc58bf6d520785f87bcfe82d2eab0d7ba35d93478d835dd7de28c44e0dccc8619c45dd7a5ec721fb2064788313d7807140e33459ce08dbf30bceb4d093096db6f3", + "digest": "0x446d1bab14cbf7b0bc6dad1cf30aeb89585df7150e0a19d809ea23c7bc4d0908" + }, + { + "name": "random-8", + "domain": { + "name": "Moo é🚀oo Moé🚀oMM🚀🚀o oéoéooé é" + }, + "primaryType": "Struct12", + "types": { + "EIP712Domain": [ + { + "name": "name", + "type": "string" + } + ], + "Struct10": [ + { + "name": "param4", + "type": "int40[3]" + }, + { + "name": "param6", + "type": "string" + }, + { + "name": "param7", + "type": "int152[1]" + }, + { + "name": "param9", + "type": "bytes10" + } + ], + "Struct12": [ + { + "name": "param2", + "type": "string" + }, + { + "name": "param3", + "type": "Struct10" + }, + { + "name": "param11", + "type": "bytes7" + } + ] + }, + "data": { + "param11": "0x35c68a72cc994a", + "param2": "Moo é🚀ooM🚀 MooMoo oM🚀M🚀🚀oMoo🚀éoéMoooM ", + "param3": { + "param4": [ + "-502183273437", + "-181945777056", + "-454055253301" + ], + "param6": "Moo é🚀 🚀ooMo🚀Mo Mé MééM🚀éo🚀é 🚀é éé oééé🚀🚀ooéM🚀🚀o", + "param7": [ + "2830948558399330007235690811772897616211515216" + ], + "param9": "0x31ee08b2239ded1369a2" + } + }, + "encoded": "0xb791414599e8b56f4b0391cb8b4d422bcf755603c5ddd6e103893a71ceba46241f5744b35eb3f3ef0a6e8cab59bf3b8b6a6691dd30ebf8939d36dd52b98acaea10c8cdc69e509a68998f2d5e30e095c9849a68b644b21c3dfcfd30ffc502114735c68a72cc994a00000000000000000000000000000000000000000000000000", + "digest": "0x8694ca7a01c36837d2449232907509801ed64e23dc023fcf736315f5eec1053c" + }, + { + "name": "random-9", + "domain": { + "chainId": 437 + }, + "primaryType": "Struct4", + "types": { + "EIP712Domain": [ + { + "name": "chainId", + "type": "uint256" + } + ], + "Struct4": [ + { + "name": "param2", + "type": "string" + }, + { + "name": "param3", + "type": "bytes8" + } + ] + }, + "data": { + "param2": "Moo é🚀🚀M🚀🚀oé Mo ééMo", + "param3": "0xc4737eceba804e84" + }, + "encoded": "0x979ff42839e8663fdf777083b1648c659b691503a28c6a2910d1d7cd162a3aa17bcd164aa59dabcd7c3be7e32492e7fe4a48e70aee5b964137249e361ebc9a41c4737eceba804e84000000000000000000000000000000000000000000000000", + "digest": "0x32e8f650680b00d4b5515e9de9684995c3245b4761046780b33ef9f6ee05362c" + }, + { + "name": "random-10", + "domain": { + "name": "Moo é🚀 o éé Mé🚀éoo 🚀oéM MM🚀é 🚀éMM🚀éMo M oé🚀MMoo é o é 🚀o🚀ooo", + "version": "45.0.46" + }, + "primaryType": "Struct8", + "types": { + "EIP712Domain": [ + { + "name": "name", + "type": "string" + }, + { + "name": "version", + "type": "string" + } + ], + "Struct8": [ + { + "name": "param2", + "type": "bytes2" + }, + { + "name": "param3", + "type": "address[3]" + }, + { + "name": "param5", + "type": "int8[3][1]" + } + ] + }, + "data": { + "param2": "0xdc77", + "param3": [ + "0x6fe07a398b47ee3d064460f74fb00b8454577d8f", + "0x4489956d5c84285dd2337de059733fd7caff5e3b", + "0xc562d2e19f4c8416f7adcdecacb47c7f3b429a18" + ], + "param5": [ + [ + "-10", + "-97", + "-22" + ] + ] + }, + "encoded": "0x9ca5f56281a4926287c930891cd4218d7e9d7e5bd6f8f35ca52190b2a54c5989dc7700000000000000000000000000000000000000000000000000000000000050b4d189d5c6d2e1fe9f152de6692da9fc34d41324dab5ced8a52397fa5ebfff5bb6e21b54c9b4e7f21b6cfe5999121f5dec6f1101cd5e0fb73a634001e5f2ea", + "digest": "0x59c2336278c748fca8889f8b98d6c386f1badb72f28668a5c760c3aff20a922a" + }, + { + "name": "random-11", + "domain": { + "salt": "0x60a7ef7f891f64c8c659e7b3f448e0e82939483d467088aae991d873c2371932" + }, + "primaryType": "Struct3", + "types": { + "EIP712Domain": [ + { + "name": "salt", + "type": "bytes32" + } + ], + "Struct3": [ + { + "name": "param2", + "type": "int88" + } + ] + }, + "data": { + "param2": "-109147385638873134356016336" + }, + "encoded": "0x22f16ecbd883f7cd7823cc5ec368999cf3bf08546bbd7c708ca231a54353cb86ffffffffffffffffffffffffffffffffffffffffffa5b7245e537649c9e75330", + "digest": "0x1c1c3e593358ec697d2f26c497abc72af68d617441a9825b6cb7c79e92bb2a01" + }, + { + "name": "random-12", + "domain": { + "name": "Moo é🚀oéé🚀🚀🚀o🚀é MoéoMooooo🚀 MMo Moo 🚀 ééo", + "version": "13.24.35", + "salt": "0x9f4a7cb7e30809076edbb99bcbc310ec27d7a563cbc24cddd0a2ae6d275677ef" + }, + "primaryType": "Struct3", + "types": { + "EIP712Domain": [ + { + "name": "name", + "type": "string" + }, + { + "name": "version", + "type": "string" + }, + { + "name": "salt", + "type": "bytes32" + } + ], + "Struct3": [ + { + "name": "param2", + "type": "address" + } + ] + }, + "data": { + "param2": "0xff4d2657142a58d3cc787e089ca7f20a6b66776e" + }, + "encoded": "0x98edda54668461e736d24a4033f2d11540bceac48ea14c7da0fea73b8b9a55db000000000000000000000000ff4d2657142a58d3cc787e089ca7f20a6b66776e", + "digest": "0xf73e0203cb874ce615b0ceb1c8c8358eb44719bbab11ebe963e00a914434ce62" + }, + { + "name": "random-13", + "domain": { + "name": "Moo é🚀", + "chainId": 348, + "salt": "0xc70f3b18e636eee4a800a307d1b38bf2cf6fbb923a5c3ae3b14becbfc2ce1f9b" + }, + "primaryType": "Struct4", + "types": { + "EIP712Domain": [ + { + "name": "name", + "type": "string" + }, + { + "name": "chainId", + "type": "uint256" + }, + { + "name": "salt", + "type": "bytes32" + } + ], + "Struct4": [ + { + "name": "param2", + "type": "string" + }, + { + "name": "param3", + "type": "bytes" + } + ] + }, + "data": { + "param2": "Moo é🚀🚀oé MoMMéo", + "param3": "0xc3e57dbac4c0a82334953baa752df10cffd5a75a2edcbe00c7f2" + }, + "encoded": "0x4d0024736263d208aca6e84c7bb7336060a12a4693f70ea14290ed4308d052e5eb273f8d13bbd23d14b25bd00e006757fa7de6d637f4c49e5fdcf4a07f024aea1f53f499e56140df7f6326e0c99cd10d9a7ceac70c4f9343111769b6ac62e1ed", + "digest": "0x407d6005ebd258cd798e326e17786b0b61a927309b8584db513bdb62f4f5ee3f" + }, + { + "name": "random-14", + "domain": { + "version": "25.49.7", + "verifyingContract": "0xb0e0d9999c8c74a0d4ed79414a9f8bf363e9caaa" + }, + "primaryType": "Struct5", + "types": { + "EIP712Domain": [ + { + "name": "version", + "type": "string" + }, + { + "name": "verifyingContract", + "type": "address" + } + ], + "Struct5": [ + { + "name": "param2", + "type": "bool" + }, + { + "name": "param3", + "type": "bytes[]" + } + ] + }, + "data": { + "param2": true, + "param3": [ + "0x96ae69774e732b9214a3ebb03c0fd01602bc7a5fcd21", + "0x60a6103ace6cc41a8df5b6518b24e6ecd490c13acfc67765f3540a4a7aa93d074a77313622786513f0199d0dad5e012c" + ] + }, + "encoded": "0x8a9a6129ffa166123d1ac6c76c2bfc752f7cf2e64b76b36df081cfc7c032a75b0000000000000000000000000000000000000000000000000000000000000001e8da1f36005f78d7a373d2f0da3b4215c9b2ab40e5a8d62e8ab338374600329f", + "digest": "0x0b1d8e9823e30d163cbd911aea02c15a62bd0bbc0168183ccd2965a8b601a40b" + }, + { + "name": "random-15", + "domain": { + "verifyingContract": "0x6f5c960aebe912340a5a72935aa334afa8fbfe58" + }, + "primaryType": "Struct6", + "types": { + "EIP712Domain": [ + { + "name": "verifyingContract", + "type": "address" + } + ], + "Struct6": [ + { + "name": "param2", + "type": "bytes" + }, + { + "name": "param3", + "type": "bytes31" + }, + { + "name": "param4", + "type": "bytes31" + }, + { + "name": "param5", + "type": "string" + } + ] + }, + "data": { + "param2": "0x4a18799bc6b7f0fab7d57bc456346bca7261a53cb04fb48d82dba056", + "param3": "0x8d48a6cc9e0c92080dec2155382a276181f25c746da97f8199f3a846421180", + "param4": "0xb165a49183e897da692ca460c5c88a0597e5e4ad7b43471bac25bfd9a780e0", + "param5": "Moo é🚀M🚀éééé oooMé🚀ooM🚀 o🚀oMMooMé🚀é 🚀oMoé🚀" + }, + "encoded": "0x195b09fb47455d098743f9c930f4078d642ea0a6f994b66198c7213629164dc250d60588d6db4ae7e3b0d4d8f93aed4781b86403d576fb4e374af761f1238cc38d48a6cc9e0c92080dec2155382a276181f25c746da97f8199f3a84642118000b165a49183e897da692ca460c5c88a0597e5e4ad7b43471bac25bfd9a780e000a315cbc15f8ad1d34c761a0523412f8bd958f3b121ab055ad39eed2d1c894c3c", + "digest": "0x859f85bc1d34695b4db0f3650dc448e4f26e1310bbf4386a47154dd0ff2f7134" + }, + { + "name": "random-16", + "domain": { + "version": "46.37.5", + "chainId": 398, + "verifyingContract": "0x7d5c6babf358d15f834e4e66fb38f47029692f45", + "salt": "0xf631d7b0a81f0bb5d43c5c6cbe06f0c46868590f0951afc1b55f38598f5caf4d" + }, + "primaryType": "Struct4", + "types": { + "EIP712Domain": [ + { + "name": "version", + "type": "string" + }, + { + "name": "chainId", + "type": "uint256" + }, + { + "name": "verifyingContract", + "type": "address" + }, + { + "name": "salt", + "type": "bytes32" + } + ], + "Struct4": [ + { + "name": "param2", + "type": "uint200" + }, + { + "name": "param3", + "type": "uint192" + } + ] + }, + "data": { + "param2": "902991113240896234120897942256968907309321984644966427837857", + "param3": "1793643122398654278545911502385423630752811461294794060392" + }, + "encoded": "0xc905891cccd69edb3dcead4f2229a9b1c0f7ad0264bd9e32ca83cf8d4835ca94000000000000008fdad31cff441c443d69458e4033c11eeb0b4928ce9452c5a100000000000000004926820a408021ed575bd94e89af5672c9c4faaa93adb268", + "digest": "0xa8ca54bd8b0380e9680fee86021be77dd65fe79954ca8aace6708fda1e62d959" + }, + { + "name": "random-17", + "domain": { + "version": "13.22.4", + "salt": "0x9a939ab1fd21216c5fcfed335f5d6ad5e83f7815e6bf7d521dd0341e9445415a" + }, + "primaryType": "Struct4", + "types": { + "EIP712Domain": [ + { + "name": "version", + "type": "string" + }, + { + "name": "salt", + "type": "bytes32" + } + ], + "Struct4": [ + { + "name": "param2", + "type": "bytes" + }, + { + "name": "param3", + "type": "string" + } + ] + }, + "data": { + "param2": "0xde2c0dae771d5cb66092b8905ba5691226740d7398459c60712a5793f0c9434c", + "param3": "Moo é🚀 oooéMo é🚀o🚀MM 🚀o MMMoMoooooooo o🚀éoM" + }, + "encoded": "0x686167f4c27dbc607c8b7b3febaeb35fa7dbcccbfac18ae20803b8e2bfc040fb053664f618ae8de97996df7189f0b8b97619489a71e0b0e370e6ef9b581df98208744de9ee650d2089505fa1d121e39ff8bd12472e14e192ad7a450cce4feb3b", + "digest": "0x0b5413c0bce8ba1599a5415a0a8729659df2c4dbfc6f71655c228101a523126b" + }, + { + "name": "random-18", + "domain": { + "chainId": 1154 + }, + "primaryType": "Struct6", + "types": { + "EIP712Domain": [ + { + "name": "chainId", + "type": "uint256" + } + ], + "Struct6": [ + { + "name": "param2", + "type": "bytes12" + }, + { + "name": "param3", + "type": "bytes22" + }, + { + "name": "param4", + "type": "string" + }, + { + "name": "param5", + "type": "address" + } + ] + }, + "data": { + "param2": "0x5f7bd6650aedd689f688617f", + "param3": "0x9d8ca86236fa8ca5e654b767c43bc31e4e2be542b128", + "param4": "Moo é🚀MoMoo🚀 🚀MoooM ooMM oMoo🚀 é oooM🚀Mo é 🚀 éo", + "param5": "0xae09d2fea99189d5171ddcd7c90e4ba4902e9df3" + }, + "encoded": "0x7a78c404a07e9f6956aaff71efb4b16130954663e72d5ee2de28e982b75d86675f7bd6650aedd689f688617f00000000000000000000000000000000000000009d8ca86236fa8ca5e654b767c43bc31e4e2be542b128000000000000000000000fb03f67aff6a2b40a0dbb1b99236c9ca92063bb9c1a604b32893a7f03e9364f000000000000000000000000ae09d2fea99189d5171ddcd7c90e4ba4902e9df3", + "digest": "0x680060a5f5fc62a31a89bfa6ee31116827439b3a46de888d9d43b1251fb8a3f2" + }, + { + "name": "random-19", + "domain": { + "chainId": 1013, + "verifyingContract": "0x9b76aa9473e60427cbc99f4161665fc37a013015", + "salt": "0xb99a959f6fcb296e9be34584a14761c7b4ce82e99d4394a5f109ed1c55cde477" + }, + "primaryType": "Struct3", + "types": { + "EIP712Domain": [ + { + "name": "chainId", + "type": "uint256" + }, + { + "name": "verifyingContract", + "type": "address" + }, + { + "name": "salt", + "type": "bytes32" + } + ], + "Struct3": [ + { + "name": "param2", + "type": "address" + } + ] + }, + "data": { + "param2": "0x010d8955e5cecc6705b73064baa37a703d65cb97" + }, + "encoded": "0x98edda54668461e736d24a4033f2d11540bceac48ea14c7da0fea73b8b9a55db000000000000000000000000010d8955e5cecc6705b73064baa37a703d65cb97", + "digest": "0x8ca2e0e8b9d5ce1c66d5ea245d6a9c653e10adc303e7440d4298c331b011c503" + }, + { + "name": "random-20", + "domain": { + "chainId": 888 + }, + "primaryType": "Struct4", + "types": { + "EIP712Domain": [ + { + "name": "chainId", + "type": "uint256" + } + ], + "Struct4": [ + { + "name": "param2", + "type": "bytes" + }, + { + "name": "param3", + "type": "bool" + } + ] + }, + "data": { + "param2": "0x981ad5555974b8b6b4f767d774c32f9e09e0009d5d914e29614ec739e70c36aaaa454aab9a9409b890c32e9304c42ec8b05a7f7ac0f60be35f1e", + "param3": true + }, + "encoded": "0x889d1fd6a543c2550a4d6fbf451f7004ee9509e508b7627d82b578e77ed3541a1d8c4d6a1c1c4c8ed69df55267e53b5289aca47b178f775978694815b4f990640000000000000000000000000000000000000000000000000000000000000001", + "digest": "0xc1319be39e82c2e51795b588e89ae2ffa116d8e6e5e0aaff6c9a42379e18e35d" + }, + { + "name": "random-21", + "domain": { + "name": "Moo é🚀éM", + "chainId": 1189, + "verifyingContract": "0x20595d9bc36f43302fc02a5b8b1afcd8751ec829", + "salt": "0x616e7f89169e2cc84d244f9e3fe0e03661dfd04909f43f379752069848cccf7e" + }, + "primaryType": "Struct8", + "types": { + "EIP712Domain": [ + { + "name": "name", + "type": "string" + }, + { + "name": "chainId", + "type": "uint256" + }, + { + "name": "verifyingContract", + "type": "address" + }, + { + "name": "salt", + "type": "bytes32" + } + ], + "Struct7": [ + { + "name": "param5", + "type": "bytes" + }, + { + "name": "param6", + "type": "bool" + } + ], + "Struct8": [ + { + "name": "param2", + "type": "bytes[2]" + }, + { + "name": "param4", + "type": "Struct7" + } + ] + }, + "data": { + "param2": [ + "0xb7bdd490b49bd7ab", + "0x864b5bb60e5d3fe67c853222aa05ff96aa441cf26a394fc4" + ], + "param4": { + "param5": "0x109f4bc90e17530b89b8fbbd2c39477a4a8bf23a1109d71902516214aa", + "param6": false + } + }, + "encoded": "0xc870a8ea20ea5a0e48665e1cceac4d0911ebdd5211d9bbe9ea6b02742610630c2274465ff0a09b20e8e027da59fb88f7c11871f7ecd6354e3ed5099296cfd04be1885a775bd5e9fe96ba4a0d693d8c0ff869e2956d2445c820173815e807f2e9", + "digest": "0xfe17ec225aeca7f68177514dcf12e22fb0b39840aa6a6c22c26f59dd033ec5c7" + }, + { + "name": "random-22", + "domain": { + "name": "Moo é🚀M M🚀ééoMoMo o é oMéé🚀", + "chainId": 941, + "salt": "0x504bcce324addceda39d36f209fbfcb3b14f834e21f104979f550ea5c6f9917b" + }, + "primaryType": "Struct4", + "types": { + "EIP712Domain": [ + { + "name": "name", + "type": "string" + }, + { + "name": "chainId", + "type": "uint256" + }, + { + "name": "salt", + "type": "bytes32" + } + ], + "Struct4": [ + { + "name": "param2", + "type": "address" + }, + { + "name": "param3", + "type": "bytes" + } + ] + }, + "data": { + "param2": "0x7d44e5363b38914b63ccfe012685ce3d2e65121a", + "param3": "0x0e" + }, + "encoded": "0x424c0018d1cc135145554306c5c9e3e9e8a4cca8848cfa101804ff94674c8d6c0000000000000000000000007d44e5363b38914b63ccfe012685ce3d2e65121a7d74985e988688526ac76b8ff8f86df2934c34abd4c430c49bf3b8a821b4e87e", + "digest": "0xb701808badfe14419c62c8108a8123a1531555b8d6c40bd0aea773e4cc7faff5" + }, + { + "name": "random-23", + "domain": { + "name": "Moo é🚀éé oé🚀🚀éoo 🚀o🚀🚀 M🚀éo🚀é🚀 o", + "verifyingContract": "0x4b290dacba7bd6da6bb491d627c27c59abcd55c8" + }, + "primaryType": "Struct5", + "types": { + "EIP712Domain": [ + { + "name": "name", + "type": "string" + }, + { + "name": "verifyingContract", + "type": "address" + } + ], + "Struct5": [ + { + "name": "param2", + "type": "string" + }, + { + "name": "param3", + "type": "bytes27" + }, + { + "name": "param4", + "type": "bytes" + } + ] + }, + "data": { + "param2": "Moo é🚀 éo ooo🚀🚀oMéooo🚀ooo🚀ooo🚀 M🚀 ooMoéooo Méé🚀oéoo", + "param3": "0xb72fa668214dee979e4ab0b212697763c7a644140a47417af60c4b", + "param4": "0x0526eab9" + }, + "encoded": "0x381b9d935686d6436962f0aa124ec705db0c223f0aa58c87e2e87a502e83b175cdd03bb0b8d5f0ab36918e793771d1a727264b826e6d52705aa7d8ac060af095b72fa668214dee979e4ab0b212697763c7a644140a47417af60c4b00000000005315d4abcf9b7169289f8f9fb986abe1e1648425138878b59807cf047bdcf743", + "digest": "0x9d78aed8da7518e44916c6da939b27f32296254456d1d12317f9d8d49414f704" + }, + { + "name": "random-24", + "domain": { + "version": "33.33.10", + "salt": "0xa465767278b9436adbcda02c178d53ced5ad9a5063042056fafac91f3eb256aa" + }, + "primaryType": "Struct6", + "types": { + "EIP712Domain": [ + { + "name": "version", + "type": "string" + }, + { + "name": "salt", + "type": "bytes32" + } + ], + "Struct6": [ + { + "name": "param2", + "type": "address" + }, + { + "name": "param3", + "type": "bytes29" + }, + { + "name": "param4", + "type": "address" + }, + { + "name": "param5", + "type": "string" + } + ] + }, + "data": { + "param2": "0xa41a2812f302436d74f6322e3d24f7d0894d445b", + "param3": "0xfbd8fa19905015b516f07997d9ee9ef2a3e70acb6a5cef4d58d25ae9a6", + "param4": "0xf8f1b9b200727a131c0868f5b0d5224e91acdd49", + "param5": "Moo é🚀" + }, + "encoded": "0x3406cf367962412603498937e52eb1fae44b403ffce6931f487e0191dc1da76e000000000000000000000000a41a2812f302436d74f6322e3d24f7d0894d445bfbd8fa19905015b516f07997d9ee9ef2a3e70acb6a5cef4d58d25ae9a6000000000000000000000000000000f8f1b9b200727a131c0868f5b0d5224e91acdd4973afb58374689378893745fc96a2fc65d1568ee4015275cae05c7ad1ba4ee814", + "digest": "0x776fdda1fd58d3c90228f4bfe52e9243a6a0c1b8804f91ecf7570b51038090f4" + }, + { + "name": "random-25", + "domain": { + "chainId": 725, + "salt": "0xc2de9b40a3c72c4b8080784d0abe7e2a81ee8c6d00ade54cbfa186d05b80a30f" + }, + "primaryType": "Struct10", + "types": { + "EIP712Domain": [ + { + "name": "chainId", + "type": "uint256" + }, + { + "name": "salt", + "type": "bytes32" + } + ], + "Struct10": [ + { + "name": "param2", + "type": "address" + }, + { + "name": "param3", + "type": "address" + }, + { + "name": "param4", + "type": "bytes" + }, + { + "name": "param5", + "type": "Struct9" + } + ], + "Struct9": [ + { + "name": "param6", + "type": "uint80" + }, + { + "name": "param7", + "type": "bool" + }, + { + "name": "param8", + "type": "bytes17" + } + ] + }, + "data": { + "param2": "0x0e1c5e8d98e793c508605de14b557517086a154a", + "param3": "0x4643f9f0fff83c5992655ea60b4ee4d87962402c", + "param4": "0xe24ad838b2dff3ce1ceb5cf89d5382a3b7ae58e3b83ed04b361dd24e858604e086cf3aae72e10af9157ca91a07aacb76ff5f4e714bf0af4e2767a5c1b7", + "param5": { + "param6": "1037360565969580415751062", + "param7": true, + "param8": "0xb400c0f629151c1c2efc8678b90a90db50" + } + }, + "encoded": "0xda5e84ad82b98296cf11f22beffa9ac554199abd2907793cf612a0cde3a3a6e00000000000000000000000000e1c5e8d98e793c508605de14b557517086a154a0000000000000000000000004643f9f0fff83c5992655ea60b4ee4d87962402cb5c868e1fa67a6af58ad2de258728150a4257a8fc3a0e8b1cd0c693a9154e78a19b8a5510cc83eb41b72cdb218809b5fb097d4f56a210465d7e4434ae4f93ae2", + "digest": "0x20c6d3b7d3a08127d2675417f10192a19404eb7f2b4a7cc826291c200d059c51" + }, + { + "name": "random-26", + "domain": { + "salt": "0x84ecb12e99120d7e16d5ea8b41f3a1712b04779027244978e3c14e6b779bff43" + }, + "primaryType": "Struct3", + "types": { + "EIP712Domain": [ + { + "name": "salt", + "type": "bytes32" + } + ], + "Struct3": [ + { + "name": "param2", + "type": "bytes15" + } + ] + }, + "data": { + "param2": "0xd9d920215e9cef3f5d2c992b29d02c" + }, + "encoded": "0x3124ca50b2729655ab7183f9cdc9ce21f39c5f9af1539ebbebddafd8d5124eebd9d920215e9cef3f5d2c992b29d02c0000000000000000000000000000000000", + "digest": "0x98647f029b556997bd7cf686927a1075276cdeb949c7324cb258265daa43e9d0" + }, + { + "name": "random-27", + "domain": { + "name": "Moo é🚀Mo🚀o M ééoééoMéMoMooooéoéoo o🚀oM🚀 oMo o🚀 Mo oé", + "verifyingContract": "0xa25337ce36273880f4e9e61f7d6e42cdf375343e" + }, + "primaryType": "Struct9", + "types": { + "EIP712Domain": [ + { + "name": "name", + "type": "string" + }, + { + "name": "verifyingContract", + "type": "address" + } + ], + "Struct5": [ + { + "name": "param3", + "type": "int88" + }, + { + "name": "param4", + "type": "address" + } + ], + "Struct9": [ + { + "name": "param2", + "type": "Struct5" + }, + { + "name": "param6", + "type": "string" + }, + { + "name": "param7", + "type": "uint144" + }, + { + "name": "param8", + "type": "string" + } + ] + }, + "data": { + "param2": { + "param3": "-127309812032058079971015355", + "param4": "0xfea50d6aa0beb7541421a003b305176bfafd3bf7" + }, + "param6": "Moo é🚀 🚀🚀M🚀Mo", + "param7": "21679346468763708829870107432985082613037936", + "param8": "Moo é🚀🚀M é🚀🚀oMo oo🚀éoooéMo" + }, + "encoded": "0x5ed3e5cf198704fbd48783461bff2bf9894ce18fa02dfd09804f85f9897fe3b6457fa76bc117d82ca5c78822611af029603b99095c728469d2b6e0db0d38430dab622c3246af0064a7ca87eee9696c6f580b5b628a1c3ccc7ca8ccb5d099b5b30000000000000000000000000000f8dddf8de4ce294ba243327c1bb5c1bd7b7017c8ef26c2ec6558fcdaa26969c050886d3cd13aa63fd7c1b4c951a029775a19", + "digest": "0xc01ab63f5b6821f38672244e3f54b38b1f997cfa987996d97c2d0b45e4378116" + }, + { + "name": "random-28", + "domain": { + "name": "Moo é🚀Moéo oéMMéMé o ooo 🚀o o éMo🚀M 🚀oo🚀🚀oM🚀éM🚀🚀ooM ooooé", + "version": "6.36.29", + "verifyingContract": "0xbda80f1b5ee3f31847bfab9e1f2d577a736ae9ae" + }, + "primaryType": "Struct16", + "types": { + "EIP712Domain": [ + { + "name": "name", + "type": "string" + }, + { + "name": "version", + "type": "string" + }, + { + "name": "verifyingContract", + "type": "address" + } + ], + "Struct15": [ + { + "name": "param11", + "type": "bytes26" + }, + { + "name": "param12", + "type": "address" + }, + { + "name": "param13", + "type": "uint240" + }, + { + "name": "param14", + "type": "string" + } + ], + "Struct16": [ + { + "name": "param2", + "type": "Struct7" + }, + { + "name": "param8", + "type": "string" + }, + { + "name": "param9", + "type": "int240" + }, + { + "name": "param10", + "type": "Struct15" + } + ], + "Struct7": [ + { + "name": "param3", + "type": "bytes[1]" + }, + { + "name": "param5", + "type": "bool" + }, + { + "name": "param6", + "type": "bytes21" + } + ] + }, + "data": { + "param10": { + "param11": "0x3c8aa45d6e60df8fa4b94a9f1a9873982cf117b7870019a77257", + "param12": "0x18dbfcc4b58ac047af1fa1ea5014e9a3c122d695", + "param13": "1403160610370024385322518859671151374504293477415157376516004093379141099", + "param14": "Moo é🚀MéM 🚀éoéé🚀 M 🚀🚀 🚀éooMéoMoM é" + }, + "param2": { + "param3": [ + "0xdd89a7e45431dc5bcf2575124ce071addb7e049856e3ec3a698aa73965f2cdebe3d8c1561c943beb06906bc333a5" + ], + "param5": false, + "param6": "0x40cdeaa8cfa786861d28835935341469073906c16c" + }, + "param8": "Moo é🚀o🚀o🚀 oé🚀oMoéo🚀 o", + "param9": "-728201913157045709305151288883895292116444273394221376510603682943551543" + }, + "encoded": "0xdb90bb6421637471a75a3d7bc8dc38f85ab6d86fe66817a5c5e52e1582dc9d8d05eae0968871a1fb36bb967307259c285125d151dee777d1b9bba85ec0c5924ab953967c8b9dd2e0f5cd1fe230ad4ddf4fa6f38f5286db1d29957c90a18c2d8bffff967d7d66a999668fc7fbff14f873fe30522876aee45bc8ad4fbc9c24b7c936513d68064480110d163aeea34e925427d939bc37d8892caa329a9d3558c51e", + "digest": "0x4f9a08cb3212e843a37e628e8a3899c9632ba8fe184f4955aabfd568690c8cbc" + }, + { + "name": "random-29", + "domain": { + "chainId": 356, + "verifyingContract": "0xa2cb255c54cb952b1d29aa906d0db8a0369b07ac", + "salt": "0xa747cdaee63c094c448b58b76e9b7555459bf28fe41e33d8e485334b3955dee3" + }, + "primaryType": "Struct3", + "types": { + "EIP712Domain": [ + { + "name": "chainId", + "type": "uint256" + }, + { + "name": "verifyingContract", + "type": "address" + }, + { + "name": "salt", + "type": "bytes32" + } + ], + "Struct3": [ + { + "name": "param2", + "type": "string" + } + ] + }, + "data": { + "param2": "Moo é🚀ééé🚀o M🚀🚀 🚀🚀🚀oM🚀oé🚀🚀oéoM éM🚀éMé o🚀 " + }, + "encoded": "0x5927d86a0ef9a01a131f7a41d2a9c89a8c82e0f454d6b4502f955f90f152eb512a656f0bd407a79f684b714713b1e71478383b1f47d7c72f652658ece534dd0b", + "digest": "0x8f21c436df4d7b8ea1470f3f4cac3d756774982b25a60bd7e76587c6b519df6a" + }, + { + "name": "random-30", + "domain": { + "version": "1.46.13", + "salt": "0x13ae4c3150715b5ad9a1e19f9e5d7acb57041c056751bf3517085406225bc939" + }, + "primaryType": "Struct4", + "types": { + "EIP712Domain": [ + { + "name": "version", + "type": "string" + }, + { + "name": "salt", + "type": "bytes32" + } + ], + "Struct4": [ + { + "name": "param2", + "type": "int136[1]" + } + ] + }, + "data": { + "param2": [ + "8166199623935941567874687986584718264154" + ] + }, + "encoded": "0x4d74029cfac37baeca9753d910ae221b711f6f012a1bea4dab197ee55395b8c7639803471fd3db6691ea1e09d239e931d1e355db6dd3a8e271e91cd893cfb588", + "digest": "0xb6546c223ab20fefa301d938ffbc21f17128f0620fd926a149725887b3cccf4a" + }, + { + "name": "random-31", + "domain": { + "name": "Moo é🚀 o 🚀 🚀M M oooMM ", + "chainId": 908 + }, + "primaryType": "Struct6", + "types": { + "EIP712Domain": [ + { + "name": "name", + "type": "string" + }, + { + "name": "chainId", + "type": "uint256" + } + ], + "Struct6": [ + { + "name": "param2", + "type": "address" + }, + { + "name": "param3", + "type": "bytes" + }, + { + "name": "param4", + "type": "string" + }, + { + "name": "param5", + "type": "address" + } + ] + }, + "data": { + "param2": "0x4ede0deacb3773e5137435f4d889ed859498566a", + "param3": "0x62dd238d0e2d216ab536acf19569d82284ec73724af965e1e635a5e02de69f89315c86d4b43dd3fea6b5", + "param4": "Moo é🚀éoo🚀MooM🚀Moo oéM🚀🚀ooé🚀 oé oéoM🚀MéoMé🚀Mo🚀oooo🚀🚀ooo🚀 ", + "param5": "0x4e35a3945fb38294c8acf415464978b576dddadd" + }, + "encoded": "0x7f6d973155b0bcc8d11dd8547d4396f95e47a6b1663de76876af0dd85aa48b3f0000000000000000000000004ede0deacb3773e5137435f4d889ed859498566a84c9e1c43faf98cea6c5fcbc1fd1dd44de98d2a6fd8094cd3379479cf19d02d3f60e96d7ac3d6507f6e8030df384f326924853ce6a85f91dfae3efb3d566f6170000000000000000000000004e35a3945fb38294c8acf415464978b576dddadd", + "digest": "0xb6efd1b092fc83b8ad6f1562b7b87e3d06f28ff1ad2287008b557a4b325e4c94" + }, + { + "name": "random-32", + "domain": { + "version": "28.18.35", + "verifyingContract": "0x860a0e50434a1e67fea76e2ecaadd0f7a34606a5", + "salt": "0xa79039bbd2738722f49a7a10b34a89be4701d18545207a60ed7b1a0d647d3ede" + }, + "primaryType": "Struct6", + "types": { + "EIP712Domain": [ + { + "name": "version", + "type": "string" + }, + { + "name": "verifyingContract", + "type": "address" + }, + { + "name": "salt", + "type": "bytes32" + } + ], + "Struct6": [ + { + "name": "param2", + "type": "string" + }, + { + "name": "param3", + "type": "address" + }, + { + "name": "param4", + "type": "address" + }, + { + "name": "param5", + "type": "bytes19" + } + ] + }, + "data": { + "param2": "Moo é🚀M oo 🚀Mé 🚀🚀 ooo🚀oMéoéo 🚀🚀oooo 🚀ooé🚀ooo", + "param3": "0x47fe050a8243b24324599b07443a44409ae0ae5f", + "param4": "0x4e9cfb53ea5657c6f71a9f6d180ef603b1491593", + "param5": "0x9bb5cc4641fd491758c28f1334074c4fd8c30d" + }, + "encoded": "0x1cf4f0e08a7941dbcc88554ec00340e272c27c135e2814a9aa84b7a0cb59090ac3fc89c9396771c6833539097c737e43749df63d9374e94038e87806138dbf3e00000000000000000000000047fe050a8243b24324599b07443a44409ae0ae5f0000000000000000000000004e9cfb53ea5657c6f71a9f6d180ef603b14915939bb5cc4641fd491758c28f1334074c4fd8c30d00000000000000000000000000", + "digest": "0x44170d64ef7cf254e72fa3a1fce021939881e52ddc6b53804b7ed17c3cc2cf4b" + }, + { + "name": "random-34", + "domain": { + "name": "Moo é🚀 é 🚀oé🚀o🚀é éoMMo🚀oooéo é MMoo 🚀 é 🚀MM🚀o🚀oo Moo", + "version": "13.20.41", + "chainId": 168, + "verifyingContract": "0x4cb0f9a774003fde250f9498bf59af14a58a4ba6", + "salt": "0xd10a489e053686f001e06c5f95be04abf66384251beee98aac3352a576309497" + }, + "primaryType": "Struct11", + "types": { + "EIP712Domain": [ + { + "name": "name", + "type": "string" + }, + { + "name": "version", + "type": "string" + }, + { + "name": "chainId", + "type": "uint256" + }, + { + "name": "verifyingContract", + "type": "address" + }, + { + "name": "salt", + "type": "bytes32" + } + ], + "Struct11": [ + { + "name": "param2", + "type": "Struct7" + }, + { + "name": "param8", + "type": "string" + }, + { + "name": "param9", + "type": "address" + }, + { + "name": "param10", + "type": "bytes" + } + ], + "Struct7": [ + { + "name": "param3", + "type": "bool" + }, + { + "name": "param4", + "type": "bytes30" + }, + { + "name": "param5", + "type": "address" + }, + { + "name": "param6", + "type": "bytes" + } + ] + }, + "data": { + "param10": "0x172fb5da45fc748aab90701f16e488faf3aeb7e2631bdb4e89178e291c54276fc0a09fde49c1d8d83ae459b0de8af4a32e752f7cde484933c819bb984fdd", + "param2": { + "param3": false, + "param4": "0x5f13d3b44f1f7b3e9509e0e6a6e86a82bad560c0463ff6f4107ed0f91ce9", + "param5": "0x854a1ce60d811708da436e758e7bb7cb3c4d3645", + "param6": "0x631b36ea5aac0b4b7a59d000cde1336adc46b54c9e2c27211a" + }, + "param8": "Moo é🚀éoéMo oéo🚀", + "param9": "0xd4116d1167d22db322c0f7ea24958097646ee98c" + }, + "encoded": "0x8d56be8d93acc8d9b7c480a48b942024d438e759597af81d8ce0c269bbfd7ba148f76de9e7e52c7befac76d291947c16f2d706470cb5a7fa41f654e7a6515feab9ae4544a83872d12bd236f00a1e723895219ed2e761b1451b728f7da4c07fea000000000000000000000000d4116d1167d22db322c0f7ea24958097646ee98c57aea085c98b9350d480022e6d063faa8011110987c0cd42488d34ffaad7596f", + "digest": "0xd916f454522d064210bb959fdb97edfc71ed9d6493f69ef3aa59ac602a4cb557" + }, + { + "name": "random-35", + "domain": { + "version": "18.1.21", + "salt": "0xa30efc1f7cb3062cc649929f0661f023168871c712710e3e2bcfb86cea245bfa" + }, + "primaryType": "Struct6", + "types": { + "EIP712Domain": [ + { + "name": "version", + "type": "string" + }, + { + "name": "salt", + "type": "bytes32" + } + ], + "Struct6": [ + { + "name": "param2", + "type": "bytes" + }, + { + "name": "param3", + "type": "bool" + }, + { + "name": "param4", + "type": "address[]" + } + ] + }, + "data": { + "param2": "0x6d5b8e2c382bcf3f732b63a56fd3a40216ce312c135083ffd5e345323397d458a568e0b5677f5b037db50782", + "param3": false, + "param4": [] + }, + "encoded": "0x3196893d5e65faa04abf32b95ea06dd0d6c08930740797c9505a8db7c0dd8d783c9347e5b56b01554824ad8ab3894c26f214fefd98de34e7c5ca8fe394ab16040000000000000000000000000000000000000000000000000000000000000000c5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470", + "digest": "0x55d568f5b6d8fcb20a1daf9434f22bef6f84008a548c2011c428c3fefa5f5ecf" + }, + { + "name": "random-36", + "domain": { + "name": "Moo é🚀o🚀MMoM ooé🚀MMM 🚀éooo🚀o🚀oo🚀 oM ", + "chainId": 640, + "verifyingContract": "0xd83848a872fece3b3d46e7db4c2b77d3f860e293" + }, + "primaryType": "Struct6", + "types": { + "EIP712Domain": [ + { + "name": "name", + "type": "string" + }, + { + "name": "chainId", + "type": "uint256" + }, + { + "name": "verifyingContract", + "type": "address" + } + ], + "Struct4": [ + { + "name": "param3", + "type": "uint208" + } + ], + "Struct6": [ + { + "name": "param2", + "type": "Struct4" + }, + { + "name": "param5", + "type": "bool" + } + ] + }, + "data": { + "param2": { + "param3": "328075064361718410561598645543330760805893258006339352712528380" + }, + "param5": false + }, + "encoded": "0xd9c78b76c5945cdad51cc594f4f98013082d5b29c5acd60ecaae6e197d0282175d5d216ebd03b033f8465a6ad96f1018494e4217e6f613aafe8b727c911047170000000000000000000000000000000000000000000000000000000000000000", + "digest": "0x199e8bfe6ee9ef6a4a030554635b74168da0c20cf8c5a19ec689aa5df1161795" + }, + { + "name": "random-37", + "domain": { + "name": "Moo é🚀🚀 o🚀oéooMMo éMM é", + "version": "28.27.34", + "verifyingContract": "0x7eabc8de425d63e9221ab1d30fa6dd0cefc081a3", + "salt": "0xe0523cb396cb9dc27ff363d713948ac735f273623989b6af7612f2fcd2ff6d2b" + }, + "primaryType": "Struct4", + "types": { + "EIP712Domain": [ + { + "name": "name", + "type": "string" + }, + { + "name": "version", + "type": "string" + }, + { + "name": "verifyingContract", + "type": "address" + }, + { + "name": "salt", + "type": "bytes32" + } + ], + "Struct4": [ + { + "name": "param2", + "type": "int200" + }, + { + "name": "param3", + "type": "address" + } + ] + }, + "data": { + "param2": "-444299975383749025331326339923423112873391432830281341721190", + "param3": "0xed41029f84e192af5415531ee38e17f30d120a88" + }, + "encoded": "0xfa093f4ea3bf0aa4213f3f060eac93ca7ab21ae17ace77be8dc0692eb11b3bf8ffffffffffffffb9380bd11977ced6065cd29a646828eb18b6e6f2b19f3c259a000000000000000000000000ed41029f84e192af5415531ee38e17f30d120a88", + "digest": "0x863b7af213ea9f4d6e5bddd79a651e5868a89780ea3fe954aa60bc4265ed20d7" + }, + { + "name": "random-38", + "domain": { + "name": "Moo é🚀o🚀é🚀 oo Mé o", + "chainId": 1268, + "verifyingContract": "0x929fa2120c0710256df5b8617b56ab4e970ecb45" + }, + "primaryType": "Struct5", + "types": { + "EIP712Domain": [ + { + "name": "name", + "type": "string" + }, + { + "name": "chainId", + "type": "uint256" + }, + { + "name": "verifyingContract", + "type": "address" + } + ], + "Struct5": [ + { + "name": "param2", + "type": "address" + }, + { + "name": "param3", + "type": "bytes" + }, + { + "name": "param4", + "type": "address" + } + ] + }, + "data": { + "param2": "0x6271d301aedc2d2eace8d5c39530cb69fa33c087", + "param3": "0xee756a5c2cd1aa40fb2b0ae05eea3e40b7436592", + "param4": "0x7909af95849ea0e6482d4979aa97b6de40184c5a" + }, + "encoded": "0xd76e74eec5c6ba52563c5d6c259dd7ab7b3c1c568662d8e52ee9e9eb1fb3b08f0000000000000000000000006271d301aedc2d2eace8d5c39530cb69fa33c087d37b7d384539b90a02ac9a5fcdffb5ea5e19dcd612cac7a8d08e701244f586f10000000000000000000000007909af95849ea0e6482d4979aa97b6de40184c5a", + "digest": "0x7fa9e8243fd155df5677ac9c26861ade73b0a6ab52e98999956cbef093e96249" + }, + { + "name": "random-39", + "domain": { + "verifyingContract": "0xe3ac435708684db0ae31e22bbefb1e44317338c4", + "salt": "0xe26aafa719c7da18b0ded1aa1520a9366af0fb6b459916877728cb9a5c87a7d6" + }, + "primaryType": "Struct5", + "types": { + "EIP712Domain": [ + { + "name": "verifyingContract", + "type": "address" + }, + { + "name": "salt", + "type": "bytes32" + } + ], + "Struct5": [ + { + "name": "param2", + "type": "string" + }, + { + "name": "param3", + "type": "bytes26" + }, + { + "name": "param4", + "type": "bytes32" + } + ] + }, + "data": { + "param2": "Moo é🚀oé 🚀M🚀éMééoo é🚀o éo o🚀 o ééM🚀é🚀ooooMoé oo éo🚀🚀oooo🚀o 🚀oM", + "param3": "0xdc0aa250b183fce60762c98a54246dd7b9bb956bd6341da3e3b8", + "param4": "0xb72dc336b24a1bbf207488efe80ffcb2fcac731fd7663809b3d2fc49ba7dfc41" + }, + "encoded": "0x7dfa18ea85b7f90d6621faeda9cf913de2386da0479309954a0255dc716d1597813bc527d887adf055ba505a819f5c99c5959f37552f3546a3621eecdbab9fa9dc0aa250b183fce60762c98a54246dd7b9bb956bd6341da3e3b8000000000000b72dc336b24a1bbf207488efe80ffcb2fcac731fd7663809b3d2fc49ba7dfc41", + "digest": "0x657847e82a36f749a49a0249eb01d42a8f84e155799a89a361326c7e059f4857" + }, + { + "name": "random-40", + "domain": { + "version": "27.48.40", + "verifyingContract": "0xb01dee94f0bffe39c63b53c94d0a9fcbc1384c7d", + "salt": "0x2d69edf19eed3feff59d5ff3d202299b0a2f7cfd471953c708c84ff2c8ef15e3" + }, + "primaryType": "Struct4", + "types": { + "EIP712Domain": [ + { + "name": "version", + "type": "string" + }, + { + "name": "verifyingContract", + "type": "address" + }, + { + "name": "salt", + "type": "bytes32" + } + ], + "Struct4": [ + { + "name": "param2", + "type": "bool" + }, + { + "name": "param3", + "type": "bytes" + } + ] + }, + "data": { + "param2": false, + "param3": "0xaf4961b17878b5ba9013de1d9fd0f3642f1e7294" + }, + "encoded": "0x08b05ab3afcc030e7935ce6c66d5d4b79136f50d1f567b347bde3906d91197e800000000000000000000000000000000000000000000000000000000000000007a9bca0401d98432dd02eb0e29aa6b8a2d7db41d77d60789e3f2a6b4672eb697", + "digest": "0xe8d2de50871488e0d941f58e407218d0e53232673e4f0c087c4ddc60f4b12997" + }, + { + "name": "random-41", + "domain": { + "chainId": 1047, + "verifyingContract": "0x374b2b8301b1edbcc86612a691376d7ac3ced722", + "salt": "0x35b3516e1c75b47e8c6c9e67c454eeb7ee4bdfdce3bff554800152182ef7c097" + }, + "primaryType": "Struct7", + "types": { + "EIP712Domain": [ + { + "name": "chainId", + "type": "uint256" + }, + { + "name": "verifyingContract", + "type": "address" + }, + { + "name": "salt", + "type": "bytes32" + } + ], + "Struct7": [ + { + "name": "param2", + "type": "bool" + }, + { + "name": "param3", + "type": "int80" + }, + { + "name": "param4", + "type": "bool[]" + }, + { + "name": "param6", + "type": "bytes4" + } + ] + }, + "data": { + "param2": true, + "param3": "29080156520360861738698", + "param4": [ + true, + false + ], + "param6": "0x5aedc9f9" + }, + "encoded": "0x9ca37edfac674c2ff136c55e6301a602590e506fcacca5c9b6b9620af18ee3d60000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000628703dd9fee4a7a6caada5013122d395ba3c54772283fb069b10426056ef8ca54750cb9bb552a59e7d5aedc9f900000000000000000000000000000000000000000000000000000000", + "digest": "0x6c6d10e7a34cd9b044d3ef2f2ed2d4333e2822ac7d10b38eb6d99bb8b1771bed" + }, + { + "name": "random-42", + "domain": { + "version": "5.35.42", + "verifyingContract": "0x43b33fe40f841899d259fa0c36dea65ec8fde1d3", + "salt": "0x081bea430df1ade6eb0b35829797621ef878f32df1dc4b9d0c53cb94d441390b" + }, + "primaryType": "Struct7", + "types": { + "EIP712Domain": [ + { + "name": "version", + "type": "string" + }, + { + "name": "verifyingContract", + "type": "address" + }, + { + "name": "salt", + "type": "bytes32" + } + ], + "Struct7": [ + { + "name": "param2", + "type": "bytes" + }, + { + "name": "param3", + "type": "address[]" + }, + { + "name": "param5", + "type": "address" + }, + { + "name": "param6", + "type": "bytes" + } + ] + }, + "data": { + "param2": "0x3b9ca4b5f6b5e21e938265fa8c67e1caf4aab1f01b17be03ae60ee2d6635a495847dedc266f2a7c2e5567b52e52be5d3", + "param3": [ + "0x699d241b2b53540a703d8f00870c1739b6e5f72d" + ], + "param5": "0x0109f2064fb32e6c22911ac905fb319ef0cee5a4", + "param6": "0x0ca1fb3d6077a402783877ceff53" + }, + "encoded": "0x853568bcfdf9ba7ef41211e721fea9318ec93b8bf70306ce21829c25ba577bbd6865c9161273dd536df66549067730a5b9d1f65060fad8a53e1ac0f39ccdaa52f571a282e60d64a3ded8eaa67254087841c598a176a7cd626f00d50732f7f95f0000000000000000000000000109f2064fb32e6c22911ac905fb319ef0cee5a4c435cc0d815f1d138a42be987d6cc47160918ac555121a5bd5278c32fd472060", + "digest": "0x804d08480ce1b1d7ddf3646c58bf0a49a114f62d28e81ec9210e03f4f9f3a617" + }, + { + "name": "random-43", + "domain": { + "version": "24.21.37" + }, + "primaryType": "Struct5", + "types": { + "EIP712Domain": [ + { + "name": "version", + "type": "string" + } + ], + "Struct5": [ + { + "name": "param2", + "type": "address" + }, + { + "name": "param3", + "type": "bool" + }, + { + "name": "param4", + "type": "address" + } + ] + }, + "data": { + "param2": "0xfe1827974ce7e150af244d1b889f747c8f0a776e", + "param3": true, + "param4": "0x13a081fac5b7be3ce686e044bf4da3abef57fe9b" + }, + "encoded": "0xd9df0580bd644a74f3c333bfcc151b8847cff76c671a86edc3130dbf71458cfb000000000000000000000000fe1827974ce7e150af244d1b889f747c8f0a776e000000000000000000000000000000000000000000000000000000000000000100000000000000000000000013a081fac5b7be3ce686e044bf4da3abef57fe9b", + "digest": "0xced726b5f99a87ac8035856b471812b2d20ad43e8e600fe5ef6e82ef52f1d653" + }, + { + "name": "random-44", + "domain": { + "chainId": 437 + }, + "primaryType": "Struct10", + "types": { + "EIP712Domain": [ + { + "name": "chainId", + "type": "uint256" + } + ], + "Struct10": [ + { + "name": "param2", + "type": "string" + }, + { + "name": "param3", + "type": "int32" + }, + { + "name": "param4", + "type": "Struct9" + } + ], + "Struct9": [ + { + "name": "param5", + "type": "bytes22" + }, + { + "name": "param6", + "type": "string" + }, + { + "name": "param7", + "type": "bytes28" + }, + { + "name": "param8", + "type": "bool" + } + ] + }, + "data": { + "param2": "Moo é🚀Mo o", + "param3": "582504756", + "param4": { + "param5": "0x111983d0af17dcfcc318c61ae1136ee42ccf1f2687e1", + "param6": "Moo é🚀Méoo🚀 oooé 🚀o🚀🚀éMéMooo Moo🚀oMo🚀é 🚀 oo🚀🚀M🚀é🚀Méé", + "param7": "0xd6c6a4a7600a2e4bf9c4add944c09f63cbde8e22b0d6fcbf19615fef", + "param8": false + } + }, + "encoded": "0x52230b1722ba9fa8df5a2874a719206cde8f49cae1cc265121e414b677873a98fb6852300dd5f88a8abd38b78e451c8cb00c5a9868c44b7df99b4a2f1c2d25620000000000000000000000000000000000000000000000000000000022b851341e0d5386101b1d456726a186dea7a1d769e76ac7dcf595f563d047ae9d74727d", + "digest": "0x205d02e271ba4c6b9f25c8f47ee1d77575d64cd4f00e63fffdc59472397d92e1" + }, + { + "name": "random-45", + "domain": { + "version": "47.45.42", + "chainId": 1206, + "verifyingContract": "0xf947022a0e71d11aab373afabfe9befd44681e93" + }, + "primaryType": "Struct6", + "types": { + "EIP712Domain": [ + { + "name": "version", + "type": "string" + }, + { + "name": "chainId", + "type": "uint256" + }, + { + "name": "verifyingContract", + "type": "address" + } + ], + "Struct6": [ + { + "name": "param2", + "type": "bytes29" + }, + { + "name": "param3", + "type": "bytes" + }, + { + "name": "param4", + "type": "string[]" + } + ] + }, + "data": { + "param2": "0x8bc51a1cd258fe6d63ac29ace059b760205588a0a9161c84c856c159f7", + "param3": "0x413b8e4fe25c1c744f85d99604ca804e2244bd4dab2ce6f5887da8e1d44ff150224b771fa7fcfdd80522c67e2315e1a2", + "param4": [ + "Moo é🚀M 🚀o 🚀o", + "Moo é🚀oo🚀Méoé🚀🚀éé🚀é🚀éoM🚀MoMéMM", + "Moo é🚀" + ] + }, + "encoded": "0x3ae8fde34490f7400f057134dfa1b5e09466c9c820ae931d27906d7f0ad91c9c8bc51a1cd258fe6d63ac29ace059b760205588a0a9161c84c856c159f7000000283f4a8f7f4aa0508213c3acfea1b9eadcc0134f291f7062983fb7b5faa4c0433ce102fe157fdae81a22e96b212ccdb916af0903abd81ed9dede694888ad8069", + "digest": "0x594e4bf0d590269799cca8859ba0a0bbdc1f50f79b62484b426596dd352185eb" + }, + { + "name": "random-46", + "domain": { + "version": "20.6.0", + "chainId": 1182, + "verifyingContract": "0x48828af2bff4cea1847888835dd26ef645440919" + }, + "primaryType": "Struct3", + "types": { + "EIP712Domain": [ + { + "name": "version", + "type": "string" + }, + { + "name": "chainId", + "type": "uint256" + }, + { + "name": "verifyingContract", + "type": "address" + } + ], + "Struct3": [ + { + "name": "param2", + "type": "bool" + } + ] + }, + "data": { + "param2": false + }, + "encoded": "0xd827c65b54cb667b8cb0c84a4aa21ffe3d188aa9cdaede527a285c318ad7274e0000000000000000000000000000000000000000000000000000000000000000", + "digest": "0x95645087033defeaf6a222f850ccd5f3f6ff9d051aeb02dd2c25cc7e237f38d9" + }, + { + "name": "random-47", + "domain": { + "name": "Moo é🚀", + "version": "17.27.18" + }, + "primaryType": "Struct19", + "types": { + "EIP712Domain": [ + { + "name": "name", + "type": "string" + }, + { + "name": "version", + "type": "string" + } + ], + "Struct10": [ + { + "name": "param8", + "type": "bool" + }, + { + "name": "param9", + "type": "uint248" + } + ], + "Struct13": [ + { + "name": "param7", + "type": "Struct10" + }, + { + "name": "param11", + "type": "uint8" + }, + { + "name": "param12", + "type": "bytes" + } + ], + "Struct14": [ + { + "name": "param6", + "type": "Struct13" + } + ], + "Struct18": [ + { + "name": "param5", + "type": "Struct14" + }, + { + "name": "param15", + "type": "bool" + }, + { + "name": "param16", + "type": "bytes25" + }, + { + "name": "param17", + "type": "string" + } + ], + "Struct19": [ + { + "name": "param2", + "type": "bytes[2]" + }, + { + "name": "param4", + "type": "Struct18" + } + ] + }, + "data": { + "param2": [ + "0x3cce5e65b51c8ab21c69ab4c6cf7a0098464d6fd4cc4dab8916ac0221f3ee8ea47479755ffbf", + "0xad11241adfe1c3d9ff8487a28064a49b65e77955dd25cbd4a6874161c9c93aa0502f1644dd08ce5b717e08f3eec80232527d509a8da2bcbf" + ], + "param4": { + "param15": false, + "param16": "0xeaba82b14185bf405877677729bd3cf35984e2de6795d26af8", + "param17": "Moo é🚀oM🚀ooééoo é M🚀oéo 🚀 o oooooMMéoéMoMM🚀oMo🚀MoéM é oo ", + "param5": { + "param6": { + "param11": "250", + "param12": "0xdf", + "param7": { + "param8": false, + "param9": "100895587724354576608296576780149850234295977769426801441167174141880048070" + } + } + } + } + }, + "encoded": "0xa2503d8dab2dd52492c8bd1e75e197a963b1be16d8704f24b034c5c3ebb5ae22ab435560740a636c972580194f75d3a1a37686a2281bff1c37e9ca6ccc1c69bebed7f0eb879274b19967141ea60f229549d8509e8c2719506fca12599149d972", + "digest": "0xf87ee3f08c482046207edfd23e1303358eb48bd840500f424192948eed97c044" + }, + { + "name": "random-48", + "domain": { + "name": "Moo é🚀Méoooo🚀 🚀🚀ééMMo o MM 🚀o é o", + "salt": "0xc03f10b7364e134ddc9de679561060f22c44058fc7abab3b8a06eeff8a0cda3e" + }, + "primaryType": "Struct4", + "types": { + "EIP712Domain": [ + { + "name": "name", + "type": "string" + }, + { + "name": "salt", + "type": "bytes32" + } + ], + "Struct4": [ + { + "name": "param2", + "type": "bytes" + }, + { + "name": "param3", + "type": "bytes" + } + ] + }, + "data": { + "param2": "0x6be8", + "param3": "0x48c2efb8ba7ee5d877ba4641d56d960ef4857d19b913c44af5c4b9bda9cfa25179" + }, + "encoded": "0x39b0d4be9d0cd319e0a49b11a9725cad68c2545bda0a2875a0510a5846e63ce43b716dc9026fa5a0f9d7f44f4f87e3bf20d3ba1c57dc9820987bf547c9cd1c92caefa7da9321f9199f0d71f17e36c56f1ec520e39306893bb0216f64129f029d", + "digest": "0x32e5e620ebdc87817f3c8028e2e1dfa11bc8281ce740d373ec1d3e6944eb795d" + }, + { + "name": "random-49", + "domain": { + "name": "Moo é🚀o", + "chainId": 103, + "salt": "0x3a5a1eb3351d1a1688d37b0d12422c89298146d8fb17ba3c25fb4b5c86c4bac9" + }, + "primaryType": "Struct5", + "types": { + "EIP712Domain": [ + { + "name": "name", + "type": "string" + }, + { + "name": "chainId", + "type": "uint256" + }, + { + "name": "salt", + "type": "bytes32" + } + ], + "Struct5": [ + { + "name": "param2", + "type": "bytes" + }, + { + "name": "param3", + "type": "string" + }, + { + "name": "param4", + "type": "bool" + } + ] + }, + "data": { + "param2": "0x26dfef70c4ef8dcc7b392b64e54778600e4342729ff538043a4180f710d08d7b5456214a42f08ba7fac949be1bbc22b9de57aa01223cb5556b82844077", + "param3": "Moo é🚀o🚀 éMMoo", + "param4": false + }, + "encoded": "0xbb972fe1a70ee0d67e7cacbc55c17b51b6f3a17a25f4f86f5941e8562149d8baf133a0ff1f13504e6c624d2de18b24dcea844936d869bcec139368394b038fd1dd59271cb718f83ded679ab9a17cd95cf12c4045bddd47bb221f8ba9f1d192320000000000000000000000000000000000000000000000000000000000000000", + "digest": "0xf5f4417e3f7452c53925e17a8d761742c795a21d1399bbdbcc6655d28ae01898" + }, + { + "name": "random-50", + "domain": { + "version": "29.42.1", + "chainId": 1327, + "verifyingContract": "0xfa7dc57fcaae354b92e4116c0bdd2cf47d8f689b", + "salt": "0x03da468bb06a9b9dab725571b916aff5422068b4ed5447355b3c5f70bc15171f" + }, + "primaryType": "Struct3", + "types": { + "EIP712Domain": [ + { + "name": "version", + "type": "string" + }, + { + "name": "chainId", + "type": "uint256" + }, + { + "name": "verifyingContract", + "type": "address" + }, + { + "name": "salt", + "type": "bytes32" + } + ], + "Struct3": [ + { + "name": "param2", + "type": "string" + } + ] + }, + "data": { + "param2": "Moo é🚀ooé MM🚀o🚀éoéé🚀oM éoMMMMoo 🚀 o" + }, + "encoded": "0x5927d86a0ef9a01a131f7a41d2a9c89a8c82e0f454d6b4502f955f90f152eb51cb99a22ed97e79bbd189fcbf151341d2ba13a10693a9f98ff01bde8e4b54b0ed", + "digest": "0xd10b65f29d8c77544a7a31a419b8158854a6efdbd142c11b13e5e914720eab09" + }, + { + "name": "random-51", + "domain": { + "name": "Moo é🚀éMMoéMéMoooo oo🚀 é🚀é ", + "version": "17.12.32", + "chainId": 850, + "verifyingContract": "0xee57c317107b6016ee17355d870066437b91cfc4" + }, + "primaryType": "Struct9", + "types": { + "EIP712Domain": [ + { + "name": "name", + "type": "string" + }, + { + "name": "version", + "type": "string" + }, + { + "name": "chainId", + "type": "uint256" + }, + { + "name": "verifyingContract", + "type": "address" + } + ], + "Struct8": [ + { + "name": "param4", + "type": "string" + }, + { + "name": "param5", + "type": "bytes2" + }, + { + "name": "param6", + "type": "address[3]" + } + ], + "Struct9": [ + { + "name": "param2", + "type": "uint176" + }, + { + "name": "param3", + "type": "Struct8" + } + ] + }, + "data": { + "param2": "41925907111722419066054857838161787127898051241694288", + "param3": { + "param4": "Moo é🚀M o🚀 oéé éM 🚀M🚀 é 🚀🚀 oo 🚀é o ééMoMé 🚀o o Moé éoMo o🚀 ", + "param5": "0x34cf", + "param6": [ + "0x93ec006c48ba16d99c6a572425637e732a9ea0dc", + "0x31cf2777fa3591e4d92e44b68f531fe304770850", + "0xf193bb64fd886bbb4490a31d7ab87a4e69f72240" + ] + } + }, + "encoded": "0x6b5499a7890f6a8255933b37decd0f637928da8bd61d925b3fc04f155551707200000000000000000000700edea808dc2e1e1937df49f85355a1244560c5d450fb8722e70eec19fe4a54da68f47326495d284ae333f3f2db8057ec987bb743b0", + "digest": "0x95199a1c903ef5b161ea35770728ff9753d9fe4cf63a68001bd910bfd7cd8930" + }, + { + "name": "random-52", + "domain": { + "name": "Moo é🚀éo oéé é M é 🚀🚀oo🚀Mo 🚀oooéMé", + "chainId": 1092, + "salt": "0xab35002bd126820952bb44438b2a342cefd29a7e41533cf101af42d980f231e5" + }, + "primaryType": "Struct4", + "types": { + "EIP712Domain": [ + { + "name": "name", + "type": "string" + }, + { + "name": "chainId", + "type": "uint256" + }, + { + "name": "salt", + "type": "bytes32" + } + ], + "Struct4": [ + { + "name": "param2", + "type": "int168[3]" + } + ] + }, + "data": { + "param2": [ + "95857240772386493629523551458434344427800456907887", + "28274122495329753421185292621092753236174200542842", + "32557294196900688742163694164485808400870334488946" + ] + }, + "encoded": "0x9c928539a37b7c8e4f1ce136ca12eba2a05b8ae934748884491b03ad7547973a0a839d9c631d1e54947847568fb9b7170b4c91ff638a57cd7bdee89b100734ae", + "digest": "0xf56d12fc86d9c13a69e7e53913981ae56a0bfdc19f6c1693cb3a808aef256f80" + }, + { + "name": "random-53", + "domain": { + "name": "Moo é🚀oooo🚀éoooéoéoé🚀é é Moéo oooMo ooo", + "version": "13.16.19", + "chainId": 568, + "verifyingContract": "0x1c60b5eb2fef17a1cdd14f962e6f7d3b3b6d6500" + }, + "primaryType": "Struct6", + "types": { + "EIP712Domain": [ + { + "name": "name", + "type": "string" + }, + { + "name": "version", + "type": "string" + }, + { + "name": "chainId", + "type": "uint256" + }, + { + "name": "verifyingContract", + "type": "address" + } + ], + "Struct6": [ + { + "name": "param2", + "type": "bytes" + }, + { + "name": "param3", + "type": "uint72" + }, + { + "name": "param4", + "type": "bool" + }, + { + "name": "param5", + "type": "bool" + } + ] + }, + "data": { + "param2": "0x2a82f8de19144e48d8bd0ca7dcdcffc3efdcad1c261eb97bd6111b6918b4daab39158153", + "param3": "1620817067840645147941", + "param4": true, + "param5": true + }, + "encoded": "0x09cfbba2e1e5ea4a3a82d13e5af185c9af63919209274a8b7c749ae5d84b713cb669a821b131bc1c35ea0a46ea180437711dceaf7c7157512bc9794f513ae1c0000000000000000000000000000000000000000000000057dd5af7be83dcad2500000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000001", + "digest": "0x508f38594346bfeecaf00d106d48313a5fda1a95aad52b5543d5b14b9d7196a7" + }, + { + "name": "random-54", + "domain": { + "name": "Moo é🚀éo é🚀🚀oéMMooéé MéMMéoooooo Mo 🚀o", + "chainId": 903 + }, + "primaryType": "Struct10", + "types": { + "EIP712Domain": [ + { + "name": "name", + "type": "string" + }, + { + "name": "chainId", + "type": "uint256" + } + ], + "Struct10": [ + { + "name": "param2", + "type": "bool[1][2][1]" + }, + { + "name": "param6", + "type": "address" + }, + { + "name": "param7", + "type": "Struct9" + } + ], + "Struct9": [ + { + "name": "param8", + "type": "uint216" + } + ] + }, + "data": { + "param2": [ + [ + [ + false + ], + [ + true + ] + ] + ], + "param6": "0x8e95a960e9a83b777c7862a94e8110fe91da147a", + "param7": { + "param8": "86338055400144793659912925038806297757314957088810913205015089413" + } + }, + "encoded": "0xc2160e538078feaa17b54d2e42d2d30202df0ca254993ea38c93773174f229ffc62f64b185683f6d01ccc0e8d936d70806faa97827a9a5f882dfd39e417384780000000000000000000000008e95a960e9a83b777c7862a94e8110fe91da147aafdbe910404b0c3b44c47734e11d0efc9d25900e7fcb2a08b20eb55cf7c271e6", + "digest": "0xbc69ff6a3a430987fe2afffe25c78d68fe98948b160b03aa8bb31dc93bb40cf8" + }, + { + "name": "random-55", + "domain": { + "version": "14.37.24", + "chainId": 406, + "verifyingContract": "0xfa8846e816297a43ce7d94b8c6bdb6cca1159fd7", + "salt": "0x242778aa1964c438a31f0e78cb2939d4452e9498a49b3765a9a401a5ac362230" + }, + "primaryType": "Struct3", + "types": { + "EIP712Domain": [ + { + "name": "version", + "type": "string" + }, + { + "name": "chainId", + "type": "uint256" + }, + { + "name": "verifyingContract", + "type": "address" + }, + { + "name": "salt", + "type": "bytes32" + } + ], + "Struct3": [ + { + "name": "param2", + "type": "bytes21" + } + ] + }, + "data": { + "param2": "0xa6eacd95abf006c234ec8ada1a0a1efe733fb8fecf" + }, + "encoded": "0xe3f2482dcc01a81435fc0dd8a8d9e82fb6aeac6d724cfcb3ec568286b0182780a6eacd95abf006c234ec8ada1a0a1efe733fb8fecf0000000000000000000000", + "digest": "0x49a860a4b3d28e51559860477bea7a0dd0cd2cc65aefc1be534af93dbda125cd" + }, + { + "name": "random-56", + "domain": { + "chainId": 1035, + "verifyingContract": "0x1c37f1b939feea69ce5757b2ff01a4dbe98cee99" + }, + "primaryType": "Struct9", + "types": { + "EIP712Domain": [ + { + "name": "chainId", + "type": "uint256" + }, + { + "name": "verifyingContract", + "type": "address" + } + ], + "Struct8": [ + { + "name": "param6", + "type": "address" + }, + { + "name": "param7", + "type": "bytes" + } + ], + "Struct9": [ + { + "name": "param2", + "type": "int248" + }, + { + "name": "param3", + "type": "bool[2]" + }, + { + "name": "param5", + "type": "Struct8" + } + ] + }, + "data": { + "param2": "189736845901057709645072716699447242597536589852507924815517840586006295739", + "param3": [ + true, + false + ], + "param5": { + "param6": "0xc84f54254fadc15051904a3aaf01a63695b229c6", + "param7": "0x17e0e79daf5a18eeea785f936e9d0b2f867e5adbf00c319da45f5db04d42397d4b9439754f98bef9ea96a1f6b3d8517c9c272e7d7ba1dc6852c8ea06b9de" + } + }, + "encoded": "0xb9cd0c37eacfb7cce86fa6e1179348a2dffc13cef7976b514a468bdf7afb4063006b6322c21ecaba15e76c656e3fc5d69ed353a0eb9ae49a9c4ef0ed9f0b74bbada5013122d395ba3c54772283fb069b10426056ef8ca54750cb9bb552a59e7de75e33b115454660e8bf1399340fd2a08c02c80da600b7f6607e4fdaa98b71dd", + "digest": "0x89a21c0061b072cf4fe51034a22f646423cb79528c45920db9f8dd9b8ef023d1" + }, + { + "name": "random-57", + "domain": { + "name": "Moo é🚀oooooéo🚀MM🚀o", + "verifyingContract": "0xd5fdebacaa6fd0e4c4e3dcb01d49d0d0b5169fd3", + "salt": "0xf46bae2676d80f6d7ed7eebfe927e808a1a9d7f16bb9c5733025eb1c54fb3cd7" + }, + "primaryType": "Struct4", + "types": { + "EIP712Domain": [ + { + "name": "name", + "type": "string" + }, + { + "name": "verifyingContract", + "type": "address" + }, + { + "name": "salt", + "type": "bytes32" + } + ], + "Struct4": [ + { + "name": "param2", + "type": "bytes24" + }, + { + "name": "param3", + "type": "bytes" + } + ] + }, + "data": { + "param2": "0x11e04fffbb632f04623d274c69004680bbfe47f642d78193", + "param3": "0xc4340156b62bc39a03b5f8bf3405e1" + }, + "encoded": "0x15a4a682c969e6c9e1a691efb9e6eac924562774edc9c70b2dea59192b5ffc4511e04fffbb632f04623d274c69004680bbfe47f642d78193000000000000000059b64b2801ddb0f37604d34c0c2b712b9ba10b111c50bf93ad769dc5372943f4", + "digest": "0xfdd39c246fa31fb5a02b7eed6bc3ccf206bf15199c22155fc730d523c884376f" + }, + { + "name": "random-58", + "domain": { + "chainId": 382, + "salt": "0xedba234675d29838cea84fca64d51c6ec1a8569eb7dc50ab6db9fdf81cbda9c7" + }, + "primaryType": "Struct7", + "types": { + "EIP712Domain": [ + { + "name": "chainId", + "type": "uint256" + }, + { + "name": "salt", + "type": "bytes32" + } + ], + "Struct7": [ + { + "name": "param2", + "type": "address" + }, + { + "name": "param3", + "type": "bool" + }, + { + "name": "param4", + "type": "address[2]" + }, + { + "name": "param6", + "type": "address" + } + ] + }, + "data": { + "param2": "0xea0f9d8e13e45e85a35d02b4523d47aed0e2acb5", + "param3": false, + "param4": [ + "0x0c861e080792c701e98212618cf4fa7fab7bf08b", + "0x899bb3224127617acf0ba2c0002c2f2ab1fa53b8" + ], + "param6": "0xf36ebef74931bfa226e964551dfe43911f2d130e" + }, + "encoded": "0xc143354b22d04023559bed7cae63b574cfb78cfd7ac0c43a128d77d6e5e614ec000000000000000000000000ea0f9d8e13e45e85a35d02b4523d47aed0e2acb50000000000000000000000000000000000000000000000000000000000000000c71de37770f9c52cb0725ff89e17b4cf1847e4d111693996075aacaff8157569000000000000000000000000f36ebef74931bfa226e964551dfe43911f2d130e", + "digest": "0x2d06931eec7d70a1db63d07d25e4b8b90f7bcb3a2b55d972c48278dd66acb0db" + }, + { + "name": "random-59", + "domain": { + "version": "30.30.24", + "verifyingContract": "0x561277eeb6bbd800e12ebc6cd0dbee892d139824", + "salt": "0x14bc6a6ff4765450e40c9f760db83f7b207a736f8588a22106af54f936524eda" + }, + "primaryType": "Struct10", + "types": { + "EIP712Domain": [ + { + "name": "version", + "type": "string" + }, + { + "name": "verifyingContract", + "type": "address" + }, + { + "name": "salt", + "type": "bytes32" + } + ], + "Struct10": [ + { + "name": "param2", + "type": "Struct7" + }, + { + "name": "param8", + "type": "bool" + }, + { + "name": "param9", + "type": "bool" + } + ], + "Struct7": [ + { + "name": "param3", + "type": "bytes22[1][3]" + }, + { + "name": "param6", + "type": "string" + } + ] + }, + "data": { + "param2": { + "param3": [ + [ + "0x507f079711e94366216b3db9f773265034311ecd25b8" + ], + [ + "0x5f6c0a03617cd85a8d36eaf3b00f3bf7250d52ef44d1" + ], + [ + "0xaaffa60b2f4aeca0cd5e1633701c0fbc7801574995f3" + ] + ], + "param6": "Moo é🚀oéoMéoé🚀M🚀" + }, + "param8": true, + "param9": false + }, + "encoded": "0xaf138bad46576af052316d07c060c9e4148c2bff6809ec200b5bd3de4aa870cf4f3e0e37be2547e04487ab720f75f451fc5f4feabfe1102ce08d0e4ec14306d000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000000", + "digest": "0xaf9d9edf7f72d55542c3a9d8b875cee65261f3aa6c7e96280440c183661bed0d" + }, + { + "name": "random-60", + "domain": { + "chainId": 563, + "verifyingContract": "0x724d9d0a75aeefec930256ab77b13f018432dc13", + "salt": "0xf766cba081e9030cf506f1616682083a65c89daebf5cf7e828d5bf85a47a819f" + }, + "primaryType": "Struct4", + "types": { + "EIP712Domain": [ + { + "name": "chainId", + "type": "uint256" + }, + { + "name": "verifyingContract", + "type": "address" + }, + { + "name": "salt", + "type": "bytes32" + } + ], + "Struct4": [ + { + "name": "param2", + "type": "int80" + }, + { + "name": "param3", + "type": "bool" + } + ] + }, + "data": { + "param2": "-149389809661940077828877", + "param3": false + }, + "encoded": "0x773a0a5ad6d608cfce67b33cbffe13f6c510c8e08ce51877432cbe3f5e8efe0effffffffffffffffffffffffffffffffffffffffffffe05d8febbd996b6de4f30000000000000000000000000000000000000000000000000000000000000000", + "digest": "0x638795e70617a8e19fb1971a5531c28225e07cbf26f3330c59f6d5bee2b237ed" + }, + { + "name": "random-61", + "domain": { + "version": "7.6.2" + }, + "primaryType": "Struct5", + "types": { + "EIP712Domain": [ + { + "name": "version", + "type": "string" + } + ], + "Struct5": [ + { + "name": "param2", + "type": "address" + }, + { + "name": "param3", + "type": "uint40" + }, + { + "name": "param4", + "type": "bytes" + } + ] + }, + "data": { + "param2": "0x2ea8b4cdab8e0585a626248968a0f3a97dbc1668", + "param3": "113948542974", + "param4": "0xde0c11a1e17e64435e2d0a8414cc368302fe788c8aac8e47741336362229cf9b9c4b029f6b" + }, + "encoded": "0x1bf518570cadc618d41542f220dc4096c36b1bdf93cbd49b2a8d9d8702b1b6540000000000000000000000002ea8b4cdab8e0585a626248968a0f3a97dbc16680000000000000000000000000000000000000000000000000000001a87dcc7fedee76d97debe8dea474b08619fc6a3b04e96d191aa5ddd4309330f5f6cc24603", + "digest": "0xe825e97cef6530ad8cd98b08e3d94b08c18a3ae7be1a742e6c9378b419c266ab" + }, + { + "name": "random-62", + "domain": { + "name": "Moo é🚀ééMMMMM🚀é", + "version": "39.10.1", + "chainId": 955, + "verifyingContract": "0x72c212d52a7da827cf299e1063d0f5481a10aba3", + "salt": "0x911b6be26e46725a5a9b2f66813850cb246cf850f316a8abd17c3255a59b1b73" + }, + "primaryType": "Struct5", + "types": { + "EIP712Domain": [ + { + "name": "name", + "type": "string" + }, + { + "name": "version", + "type": "string" + }, + { + "name": "chainId", + "type": "uint256" + }, + { + "name": "verifyingContract", + "type": "address" + }, + { + "name": "salt", + "type": "bytes32" + } + ], + "Struct5": [ + { + "name": "param2", + "type": "bool" + }, + { + "name": "param3", + "type": "uint120" + }, + { + "name": "param4", + "type": "address" + } + ] + }, + "data": { + "param2": true, + "param3": "267318752341245407126510725002347495", + "param4": "0x12a0cdc5797b0e2cd0fcc3b5f3d63d0b40060713" + }, + "encoded": "0xb1d573600e493816acf7786e0db884238646f98d2925ad6bc1d0e58f7a84557600000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000337bd501dc8adfc1e062ad2c26c3e700000000000000000000000012a0cdc5797b0e2cd0fcc3b5f3d63d0b40060713", + "digest": "0xa6448bcf64129f8f3357ad27ea6cb54528261ae82c77fa911cfd42326c5a32b9" + }, + { + "name": "random-63", + "domain": { + "chainId": 52, + "salt": "0x63a0b8d00ebb49dc92aa7466795df26f52f7f097a8951212994dcf084f3115ee" + }, + "primaryType": "Struct11", + "types": { + "EIP712Domain": [ + { + "name": "chainId", + "type": "uint256" + }, + { + "name": "salt", + "type": "bytes32" + } + ], + "Struct10": [ + { + "name": "param6", + "type": "string[3]" + }, + { + "name": "param8", + "type": "bytes" + }, + { + "name": "param9", + "type": "string" + } + ], + "Struct11": [ + { + "name": "param2", + "type": "bytes" + }, + { + "name": "param3", + "type": "uint176" + }, + { + "name": "param4", + "type": "string" + }, + { + "name": "param5", + "type": "Struct10" + } + ] + }, + "data": { + "param2": "0x3e22bfaf4192ea7bbd24be98894f62", + "param3": "8643822919333434052426646511186265705818267649977701", + "param4": "Moo é🚀 MoéM 🚀oo🚀M🚀é🚀ooooéoMoMMé é🚀🚀ééoooo🚀oo🚀ééo🚀 ooé ", + "param5": { + "param6": [ + "Moo é🚀éM MoMoéooé🚀oéo", + "Moo é🚀MMooéoo🚀é🚀🚀é🚀oéoé éo Méooo 🚀M oé🚀oé M o🚀éoé oéoo o", + "Moo é🚀" + ], + "param8": "0x855aef", + "param9": "Moo é🚀" + } + }, + "encoded": "0xac316d647c74bc3589294fae161068a7566854edd7dd16d2eda15b770a4375daa7942ff4d68d8349802c6984bdc9c4c2b6d470e42e79142bbc940d35ac64002400000000000000000000171a57f90c59a9aa6004324b4c1e5ca16b31e5062d65320516b8c2848eb919a422085947b5272cee2812540b5bf353b28c2cb5f048a86ed4f8451f4da89498fab2ccc0b5f4f96071ca26c12b291e2fb6545c1fdf6657", + "digest": "0x3312dae7779358b36e3d0901d23210c35ae47615a6ced82730958afeecba27b4" + }, + { + "name": "random-64", + "domain": { + "version": "12.22.26", + "chainId": 1072, + "verifyingContract": "0xcfadcc6d18290da205685c27be85e3d12daef93a" + }, + "primaryType": "Struct7", + "types": { + "EIP712Domain": [ + { + "name": "version", + "type": "string" + }, + { + "name": "chainId", + "type": "uint256" + }, + { + "name": "verifyingContract", + "type": "address" + } + ], + "Struct7": [ + { + "name": "param2", + "type": "bool" + }, + { + "name": "param3", + "type": "address[]" + }, + { + "name": "param5", + "type": "bytes14" + }, + { + "name": "param6", + "type": "string" + } + ] + }, + "data": { + "param2": true, + "param3": [], + "param5": "0x8e08e58d1d810440dff7199e28d0", + "param6": "Moo é🚀🚀oooéé " + }, + "encoded": "0x1a13a4e4f65db23bb29d9f84dd419677df8345f546cd92242e7e392b4fbc15360000000000000000000000000000000000000000000000000000000000000001c5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a4708e08e58d1d810440dff7199e28d00000000000000000000000000000000000000d7aba156c05cec0dbf187401b9ed4df82bcd9fd30870c0924ad1cf2b4f3d704", + "digest": "0xaf2a42e1f7ec4b6c7b7dba5b36c575bda2972f52479f34190d1c3750e18ad5a7" + }, + { + "name": "random-66", + "domain": { + "chainId": 1336 + }, + "primaryType": "Struct4", + "types": { + "EIP712Domain": [ + { + "name": "chainId", + "type": "uint256" + } + ], + "Struct4": [ + { + "name": "param2", + "type": "bool" + }, + { + "name": "param3", + "type": "bytes5" + } + ] + }, + "data": { + "param2": true, + "param3": "0xec970b3168" + }, + "encoded": "0x2bc752ac2910b71f3084d69bc9772a7de5341093c84f7c9e5fd2ac361544ad460000000000000000000000000000000000000000000000000000000000000001ec970b3168000000000000000000000000000000000000000000000000000000", + "digest": "0x8feef116af30b282bfe16c1890206276e9aae7dd7f74e5f988614956135e273f" + }, + { + "name": "random-68", + "domain": { + "version": "18.42.3", + "chainId": 5, + "verifyingContract": "0x2909159c78bf78c499a576986e657f6221814a1b", + "salt": "0x4757ed28e3e7f1ce3bdcb44424f8d26cc8f55f00ea55759f5200e5e58c2df0f4" + }, + "primaryType": "Struct9", + "types": { + "EIP712Domain": [ + { + "name": "version", + "type": "string" + }, + { + "name": "chainId", + "type": "uint256" + }, + { + "name": "verifyingContract", + "type": "address" + }, + { + "name": "salt", + "type": "bytes32" + } + ], + "Struct9": [ + { + "name": "param2", + "type": "string" + }, + { + "name": "param3", + "type": "string" + }, + { + "name": "param4", + "type": "bool[3][3][1]" + }, + { + "name": "param8", + "type": "bool" + } + ] + }, + "data": { + "param2": "Moo é🚀 🚀ooMMé", + "param3": "Moo é🚀ééoooéM ooM Mé🚀Mo Mo", + "param4": [ + [ + [ + true, + false, + true + ], + [ + true, + true, + false + ], + [ + false, + true, + false + ] + ] + ], + "param8": false + }, + "encoded": "0xe1a6b9ce8570dfc1929b0986ecaa84c675a38f130aa0dd754f0f3efb28a7946e7b41e0b88acf6e22ccb8ab0ceb0b4e4498e5569bdada5470159cb4aee1977181aec0dcca7af9e7739a3bb043c812bb554c938bd178a9b815ff2bcc12571dedcdcb524ab786db4edc230bef0de43dcb5bc64e388a0e6e7e776ad754bde112e7530000000000000000000000000000000000000000000000000000000000000000", + "digest": "0x93c2c7376679e56c01ad0855a122657ad37474c3c3cf2d943693ed74c9bdf20a" + }, + { + "name": "random-69", + "domain": { + "version": "29.38.34", + "chainId": 479, + "salt": "0x4d41456ce1c41c3d581d10c26cb759d4a47f54ce1a9368f438e2a81977f51d93" + }, + "primaryType": "Struct7", + "types": { + "EIP712Domain": [ + { + "name": "version", + "type": "string" + }, + { + "name": "chainId", + "type": "uint256" + }, + { + "name": "salt", + "type": "bytes32" + } + ], + "Struct6": [ + { + "name": "param3", + "type": "address" + }, + { + "name": "param4", + "type": "bool" + }, + { + "name": "param5", + "type": "string" + } + ], + "Struct7": [ + { + "name": "param2", + "type": "Struct6" + } + ] + }, + "data": { + "param2": { + "param3": "0x824518b68d26baa46978e389d86ffce76fc865b6", + "param4": true, + "param5": "Moo é🚀éMMéééoo 🚀o🚀o🚀oM oé🚀o oéooé🚀éoéoMéMoo🚀🚀oo🚀" + } + }, + "encoded": "0x30c11efb6065f6bda2985909969cdeae08dca57414e9e0f27cd2aa3b7be28049e16d3c3bef1a3a648aefca75d723e43c4c44ae6a69831e585a5e3cf1af9cb02e", + "digest": "0x5f28a72d46bcf0e734304e0fdcdb6ce0371bff2f2db3c66dbef72582ca768429" + }, + { + "name": "random-70", + "domain": { + "version": "18.40.21", + "verifyingContract": "0xb24a517ffcbfdd9bc47720db3e5190fda2e01002" + }, + "primaryType": "Struct4", + "types": { + "EIP712Domain": [ + { + "name": "version", + "type": "string" + }, + { + "name": "verifyingContract", + "type": "address" + } + ], + "Struct4": [ + { + "name": "param2", + "type": "bytes3" + }, + { + "name": "param3", + "type": "bytes" + } + ] + }, + "data": { + "param2": "0x41ce81", + "param3": "0xc83aff0255120cca20588d23ada5f3f38f7195f030873a49a7d16fe8dcaf2b19ac789fca2b3f90470ffe0345332fdcb0" + }, + "encoded": "0x3ba0a68a11c3149e7b8cce5c07245e59ffa9acdaf38fcc613b25ec0abd0fde9b41ce8100000000000000000000000000000000000000000000000000000000003a478a822fa1170b9116c77ad351ea423f4a9ea13a9942e3f02beff4c8c9e6cf", + "digest": "0x26a000eff8c5171403d5d31329e4675bbb567bf0fb2938737fc72b35070bd0a8" + }, + { + "name": "random-72", + "domain": { + "name": "Moo é🚀é🚀 M🚀 ", + "version": "5.17.26" + }, + "primaryType": "Struct3", + "types": { + "EIP712Domain": [ + { + "name": "name", + "type": "string" + }, + { + "name": "version", + "type": "string" + } + ], + "Struct3": [ + { + "name": "param2", + "type": "bytes32" + } + ] + }, + "data": { + "param2": "0x1a4984f48e88befe512b3cb96ab6dcd897b12c4178dc46a3eb0174834581a306" + }, + "encoded": "0xf0b1d4f6e88accb50c11c83238640185fa1db3c76ce20f17fa4916dadf9db31c1a4984f48e88befe512b3cb96ab6dcd897b12c4178dc46a3eb0174834581a306", + "digest": "0x56403e65cffb42a3d27fc46982bf5f109216c216e600ae1d5e9eeb0f2d535c4c" + }, + { + "name": "random-73", + "domain": { + "name": "Moo é🚀ooMo🚀oo🚀é ooo M🚀éM oo🚀 éooo🚀 MMoé 🚀oM 🚀 é é 🚀oé🚀ooooéo", + "version": "35.11.14", + "chainId": 1042, + "salt": "0x7b9937e8531f141e3ee6fe381c9a9e86ffdcd0c68a8e1ae17112e620aaab338c" + }, + "primaryType": "Struct12", + "types": { + "EIP712Domain": [ + { + "name": "name", + "type": "string" + }, + { + "name": "version", + "type": "string" + }, + { + "name": "chainId", + "type": "uint256" + }, + { + "name": "salt", + "type": "bytes32" + } + ], + "Struct10": [ + { + "name": "param3", + "type": "bytes30" + }, + { + "name": "param4", + "type": "bytes" + }, + { + "name": "param5", + "type": "Struct8" + }, + { + "name": "param9", + "type": "address" + } + ], + "Struct12": [ + { + "name": "param2", + "type": "Struct10" + }, + { + "name": "param11", + "type": "string" + } + ], + "Struct8": [ + { + "name": "param6", + "type": "bool" + }, + { + "name": "param7", + "type": "address" + } + ] + }, + "data": { + "param11": "Moo é🚀oéoéé", + "param2": { + "param3": "0x36edbef26bd4681e7135942feae3864374031194d1426af5a7e481a728d8", + "param4": "0x1a59b3c0be1b6635e52bdf4cfe067940b50d4e696c363000edc866d8d22c9e49398b54f25d4e9718feb3f99eb46ce3751b926fa7855343150f9d", + "param5": { + "param6": false, + "param7": "0xe6e18c2cb91d1abe36baf44ab6122b3fd30a74c0" + }, + "param9": "0xc6013a4ce04bb384ce0b272cd0680e04df0cc7b7" + } + }, + "encoded": "0xeb7ccb02ebd334f8f8df1a4ee2070e684c143ac9a985b10cd81de8a58fc80adbc5dd6b5081adebdb29fdae09d95ee5ea84d03f41c3772434f05e3b05708112fc70204aaa5c94f68894203bf3369b48b4d60e7f149b87d2b0805639a833c2fe66", + "digest": "0x67912c3108d4a59226f95dc0c3c275382f78beac03a86f9e077c1caa1eeec14a" + }, + { + "name": "random-74", + "domain": { + "name": "Moo é🚀é 🚀éo🚀o 🚀o é M🚀é🚀éMo🚀🚀o🚀Mo oMo🚀 oo🚀 éo é", + "salt": "0x370860feeba70f3953279dba52f02a3a04612484aefa84f31663d9e5227e1d6c" + }, + "primaryType": "Struct6", + "types": { + "EIP712Domain": [ + { + "name": "name", + "type": "string" + }, + { + "name": "salt", + "type": "bytes32" + } + ], + "Struct6": [ + { + "name": "param2", + "type": "uint80" + }, + { + "name": "param3", + "type": "bytes" + }, + { + "name": "param4", + "type": "bytes5" + }, + { + "name": "param5", + "type": "bytes" + } + ] + }, + "data": { + "param2": "908478282363712209846374", + "param3": "0xef8eb40ace87b08ffe0e7aefbe3bd8e6", + "param4": "0x597d2ea26a", + "param5": "0x8922c08275eb23909ccfca4daf11662e5d" + }, + "encoded": "0x76603b49ee0d4f7ea115ee0a57044fe4c0401d1221ad71b9dc5321cc33fef7b900000000000000000000000000000000000000000000c060b4d4a59107f2806606a0b7daf9092ca6ab2e9126f86c784769009fa8f8d71e7e03679ce2ca8d87a0597d2ea26a000000000000000000000000000000000000000000000000000000cd50ee07435f2e2e828292fc6ef0446ca0f9be9bdc428a8eaffa42a5286a965f", + "digest": "0xa75d4290b6749b842bf573b2eb09823bb3873d1027d942b4ea6d4d81b842d6c0" + }, + { + "name": "random-75", + "domain": { + "name": "Moo é🚀🚀🚀MoééMoo M🚀 o MoMéMoo🚀Méééé éééo Mo", + "salt": "0x38eaa8c2cfc07d160da608dbe8f1e194e7effef6978024db13d7dca0f3272c39" + }, + "primaryType": "Struct7", + "types": { + "EIP712Domain": [ + { + "name": "name", + "type": "string" + }, + { + "name": "salt", + "type": "bytes32" + } + ], + "Struct5": [ + { + "name": "param4", + "type": "address" + } + ], + "Struct7": [ + { + "name": "param2", + "type": "bytes13" + }, + { + "name": "param3", + "type": "Struct5" + }, + { + "name": "param6", + "type": "bool" + } + ] + }, + "data": { + "param2": "0x784f74ad457e1d6fcfb45d7367", + "param3": { + "param4": "0xdce541656a93cfcb9e179045ad051a9ecdd7189b" + }, + "param6": false + }, + "encoded": "0xcb86038251ab323aac49c6b590bcaa00d47d51116aa52134673db28594f5bf75784f74ad457e1d6fcfb45d736700000000000000000000000000000000000000a8a20cb63e84557a5347128e3f0c2a1b5a0afb541230866c1088af198275a6b80000000000000000000000000000000000000000000000000000000000000000", + "digest": "0xefef2081cc8e3873505b925f3297102ae37293e502c1a8f0c8f74ccdbb89d4bc" + }, + { + "name": "random-76", + "domain": { + "version": "36.49.23", + "chainId": 754, + "salt": "0x43db1a3f28a17091824b3c3dab8e15ec297daf2313c6755b3e40e58f96c9b179" + }, + "primaryType": "Struct3", + "types": { + "EIP712Domain": [ + { + "name": "version", + "type": "string" + }, + { + "name": "chainId", + "type": "uint256" + }, + { + "name": "salt", + "type": "bytes32" + } + ], + "Struct3": [ + { + "name": "param2", + "type": "address" + } + ] + }, + "data": { + "param2": "0x3a0cba0c53b27c471b7897a7d6b055079c47d518" + }, + "encoded": "0x98edda54668461e736d24a4033f2d11540bceac48ea14c7da0fea73b8b9a55db0000000000000000000000003a0cba0c53b27c471b7897a7d6b055079c47d518", + "digest": "0x6e9544e309d43df400883cf848f3963329e1e69e89a787509df5a9d5cefe94c7" + }, + { + "name": "random-78", + "domain": { + "name": "Moo é🚀 ", + "chainId": 1205, + "salt": "0x9af868b51e603e4c79a4f3ec3f3117dc294b46a36297214095a50f193489bb42" + }, + "primaryType": "Struct5", + "types": { + "EIP712Domain": [ + { + "name": "name", + "type": "string" + }, + { + "name": "chainId", + "type": "uint256" + }, + { + "name": "salt", + "type": "bytes32" + } + ], + "Struct5": [ + { + "name": "param2", + "type": "bool" + }, + { + "name": "param3", + "type": "bool" + }, + { + "name": "param4", + "type": "bytes15" + } + ] + }, + "data": { + "param2": false, + "param3": false, + "param4": "0x60217b1a8953bfec781042b8770a75" + }, + "encoded": "0x1676a9ea413a00e543593a76cb92d87b3f3d82e3116ed0efa051c85413c5ee3d0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000060217b1a8953bfec781042b8770a750000000000000000000000000000000000", + "digest": "0x7f79e4826551d80cda20954308c0daf2e009e2af2aa6f3d60bec8130d535dda6" + }, + { + "name": "random-79", + "domain": { + "version": "6.7.22", + "chainId": 483, + "verifyingContract": "0x55f4aa781b99afe9ce65b85e20b467cbdec997e6" + }, + "primaryType": "Struct8", + "types": { + "EIP712Domain": [ + { + "name": "version", + "type": "string" + }, + { + "name": "chainId", + "type": "uint256" + }, + { + "name": "verifyingContract", + "type": "address" + } + ], + "Struct7": [ + { + "name": "param5", + "type": "uint80" + }, + { + "name": "param6", + "type": "bytes18" + } + ], + "Struct8": [ + { + "name": "param2", + "type": "bytes8" + }, + { + "name": "param3", + "type": "string" + }, + { + "name": "param4", + "type": "Struct7" + } + ] + }, + "data": { + "param2": "0x8b52a6ddb851805f", + "param3": "Moo é🚀🚀🚀ooo🚀MoM o🚀🚀é", + "param4": { + "param5": "610461354364077444926076", + "param6": "0x7161dd90da510a700714562a8e29d7bf559f" + } + }, + "encoded": "0xfcabf7393dcfae718d48a70508ca009b50740ada3608bceaff6b492ed20c200f8b52a6ddb851805f000000000000000000000000000000000000000000000000aca36f7ecb9904e737ae9a06d9a7bc75f52075619eb5d2238d49e56ff0722a4c50896c94a314c8d9e77fdb0734871b909b734cdaac41d6f6412bac6bbe685e74", + "digest": "0x52bc2e8e33e9a8f5c1e02b4eb1af26d9bfb646f90a802b87e7b05e1c492334b5" + }, + { + "name": "random-80", + "domain": { + "name": "Moo é🚀 oo é ", + "salt": "0x12196565c18218e3d5c30fb8c3ed8aa5368b9270d1e50f96dae8c01c9524bd66" + }, + "primaryType": "Struct5", + "types": { + "EIP712Domain": [ + { + "name": "name", + "type": "string" + }, + { + "name": "salt", + "type": "bytes32" + } + ], + "Struct5": [ + { + "name": "param2", + "type": "address" + }, + { + "name": "param3", + "type": "bytes" + }, + { + "name": "param4", + "type": "uint152" + } + ] + }, + "data": { + "param2": "0x51f4089446d4fac6b978ebc92cb8c136a3ec12d4", + "param3": "0x9fa41910ccf9", + "param4": "1327849360637990991145689530428201101848957792" + }, + "encoded": "0xc3cf69c88a7f7d51e14da0539c7562c4aef44fdccccf126abab02153850a06c900000000000000000000000051f4089446d4fac6b978ebc92cb8c136a3ec12d43550fb9376fce556153ac9c01b5a3d92905e20a0f5e9d20c7f84b82b55b23996000000000000000000000000003b8af68e30d0b55cd77e2a766ab742aacb5360", + "digest": "0xfa4436f23aab9a7adce0a1ed9ccb76fb12aa9c3fd4ea8f550d3d9c21ea8ad71b" + }, + { + "name": "random-81", + "domain": { + "version": "37.42.30", + "verifyingContract": "0x8ddfee525b882a427923e62ea4c6c4f15fd30ac4", + "salt": "0xea7e38d5a4a991401101f92da453029636a589cc8a5a04f5e8b821d09fb72d62" + }, + "primaryType": "Struct3", + "types": { + "EIP712Domain": [ + { + "name": "version", + "type": "string" + }, + { + "name": "verifyingContract", + "type": "address" + }, + { + "name": "salt", + "type": "bytes32" + } + ], + "Struct3": [ + { + "name": "param2", + "type": "bytes6" + } + ] + }, + "data": { + "param2": "0xa15ee47cd2ae" + }, + "encoded": "0xb6ad09a6e56a9b085d21c37fc35456a42e929d83a6c454464108bba8e5bd24eaa15ee47cd2ae0000000000000000000000000000000000000000000000000000", + "digest": "0x5cf352f0b86d7a40b2722d6fee422cc31791ea6a756e975f94cc418482322967" + }, + { + "name": "random-82", + "domain": { + "chainId": 1294, + "verifyingContract": "0xedd346d21be942f85ef9d7e2837019da832bdd15" + }, + "primaryType": "Struct4", + "types": { + "EIP712Domain": [ + { + "name": "chainId", + "type": "uint256" + }, + { + "name": "verifyingContract", + "type": "address" + } + ], + "Struct4": [ + { + "name": "param2", + "type": "string" + }, + { + "name": "param3", + "type": "uint216" + } + ] + }, + "data": { + "param2": "Moo é🚀o éMo🚀oM", + "param3": "60842439233483756494403810394413324880914448649588060989930178516" + }, + "encoded": "0x0005f47efff5a6a0a15e05a2eec2b6e6fc783b60032aa8390118dbd32ba2cad636c6269b518467469df2499c64aa468f9cb44ac0c27f5aff6efe92d99caae298000000000093e657c796b03e22877711b903453f372f0a33b18b1f15119187d4", + "digest": "0xd4949695b7d6eec2e8c8f26d6d861ba735fcd61670fbca8de68609e0fd469e30" + }, + { + "name": "random-83", + "domain": { + "verifyingContract": "0x7b9546d050c94a8270fa5c0d0e37ad2df4a1e763" + }, + "primaryType": "Struct11", + "types": { + "EIP712Domain": [ + { + "name": "verifyingContract", + "type": "address" + } + ], + "Struct10": [ + { + "name": "param5", + "type": "bytes" + }, + { + "name": "param6", + "type": "bool" + }, + { + "name": "param7", + "type": "bytes27[3]" + }, + { + "name": "param9", + "type": "uint176" + } + ], + "Struct11": [ + { + "name": "param2", + "type": "int232" + }, + { + "name": "param3", + "type": "Struct10[1]" + } + ] + }, + "data": { + "param2": "753575330828539899242077719223027729803561407233093589597780073631396", + "param3": [ + { + "param5": "0x43e53fca9177478563526d0765abe7705b1808451964d8207fb75278cac13fb991bb4dff4c6dde00456d5236a93fd20edc2081b46c087d9f", + "param6": true, + "param7": [ + "0xa413dea0bd7fb350af2baf9e1878c127d2ab62367c57b6d4b4efc8", + "0x7514110a681f7d3bf5b932d467bedbdeee6681d6cb7a454978d6b2", + "0xdb56718cbc96a605ae9624a29a2432db87f2ff1cc1f86e2e70648a" + ], + "param9": "57122701991999599773377300580648345488081114848136112" + } + ] + }, + "encoded": "0x7c13f5ac1c582fab4bda02838e5b34f88377ed3a9f2e39c4e9a99259893244cd0000001bf3a027ad30dc440fff233a7cc1bb6d49fee7c5273eff291e2f99cea417512f0ab3c462fc44f916934f29d7ab18f18c1b7cffe28b41d46b729680442e", + "digest": "0x2d36f076787c1920a351171a2c32575d984b68f652b1dca4db83687b2be45551" + }, + { + "name": "random-84", + "domain": { + "version": "0.4.17", + "chainId": 911, + "salt": "0x3e7b8591b2ff12889933bff797d082b473fc6519049106aa59b07753e926bd66" + }, + "primaryType": "Struct4", + "types": { + "EIP712Domain": [ + { + "name": "version", + "type": "string" + }, + { + "name": "chainId", + "type": "uint256" + }, + { + "name": "salt", + "type": "bytes32" + } + ], + "Struct4": [ + { + "name": "param2", + "type": "bytes" + }, + { + "name": "param3", + "type": "bool" + } + ] + }, + "data": { + "param2": "0x41ca2691711b6289b07e9d60adcd530d82049e195a22202170b18328a7852d66398d41a006185887266fa7aad4ac89bfb33f94dc6cd2eaa7f2b9b12a01", + "param3": true + }, + "encoded": "0x889d1fd6a543c2550a4d6fbf451f7004ee9509e508b7627d82b578e77ed3541a549ef74b3d255ff952161e092604766989b5f79184cb72d5f1687d04ddedb4390000000000000000000000000000000000000000000000000000000000000001", + "digest": "0x789a03f8f59d24aee8073b378663b9289f823791b818602a0477434b99c8b768" + }, + { + "name": "random-85", + "domain": { + "name": "Moo é🚀oM o MMoéMé🚀MéMéM", + "chainId": 900 + }, + "primaryType": "Struct5", + "types": { + "EIP712Domain": [ + { + "name": "name", + "type": "string" + }, + { + "name": "chainId", + "type": "uint256" + } + ], + "Struct5": [ + { + "name": "param2", + "type": "string[3][3]" + } + ] + }, + "data": { + "param2": [ + [ + "Moo é🚀MMo 🚀🚀MM oéoooéé", + "Moo é🚀 éo🚀 🚀oéoMo", + "Moo é🚀oééoéM oMé Moéoo oo M MMoééoooo🚀M🚀o🚀oéMo oo é Moo ooo oo🚀 " + ], + [ + "Moo é🚀o🚀ooéMMooooMo oo o🚀 M🚀Mooo oé🚀o🚀oéoM 🚀M oéo🚀 🚀🚀 🚀o🚀 M", + "Moo é🚀oéé🚀ooéo ooooM🚀🚀éo🚀🚀🚀ooé🚀 éooM🚀oooooMoo Mo🚀ooooMM 🚀 🚀", + "Moo é🚀oo🚀M o🚀oo🚀éoMoooM oM M🚀ooMM🚀 éo MooMM éooo" + ], + [ + "Moo é🚀MMMééo oM o🚀 🚀🚀 Mo o🚀éo🚀oMoé éé oo🚀éé🚀Méoé🚀🚀oéoo 🚀", + "Moo é🚀 🚀M", + "Moo é🚀oo🚀Mo🚀🚀oMo🚀M🚀 o MMoo ééMoé MoMoMMooééoo🚀 éo" + ] + ] + }, + "encoded": "0x988b57712611b9efb362d55270a70ca3e280c7c23ee803db000637ee03249611aa5a4b824d61608ae0c2f6d5840f3f4e9f9984b3536adf111a3b3f81e01b75c0", + "digest": "0x42bfc8f80f73b02a800f2cf5f3b9b96c6774a43c706758c8f34f1fabf946b001" + }, + { + "name": "random-86", + "domain": { + "name": "Moo é🚀Mééo🚀oé", + "verifyingContract": "0xa55e763720cf41f5a539aab57701a95d60e29f67" + }, + "primaryType": "Struct4", + "types": { + "EIP712Domain": [ + { + "name": "name", + "type": "string" + }, + { + "name": "verifyingContract", + "type": "address" + } + ], + "Struct4": [ + { + "name": "param2", + "type": "uint104" + }, + { + "name": "param3", + "type": "string" + } + ] + }, + "data": { + "param2": "1292496995405503073422960358486", + "param3": "Moo é🚀MM o🚀o🚀 éMMoooéoo éé🚀oé🚀 éMoo🚀é🚀M é🚀oo Mo🚀 éoo" + }, + "encoded": "0xa7b111f9ba803946689ccd05331e3dfd450deaf9afc546b5d2a08764dbb3075f0000000000000000000000000000000000000010504874d57b4ef8afc891f456a48ff30b0c7e041b00beb25671621131298873a003e9ec839b5d75eb5b1da286", + "digest": "0xf3af9321483c3d61bcdda3a128184aa6841318dc718edc3be9a2f0b976416641" + }, + { + "name": "random-87", + "domain": { + "name": "Moo é🚀 éooo🚀Moo🚀oooMMo🚀🚀o ooMé éo o🚀éM ", + "version": "45.45.47", + "chainId": 759, + "verifyingContract": "0xdd7404edd91ced2e440ae26be5de770826936aa1", + "salt": "0x3e9fe589ec829de82a24cfdfb4f51be7b494d92d767ddce47df39f2d695fef40" + }, + "primaryType": "Struct3", + "types": { + "EIP712Domain": [ + { + "name": "name", + "type": "string" + }, + { + "name": "version", + "type": "string" + }, + { + "name": "chainId", + "type": "uint256" + }, + { + "name": "verifyingContract", + "type": "address" + }, + { + "name": "salt", + "type": "bytes32" + } + ], + "Struct3": [ + { + "name": "param2", + "type": "bytes31" + } + ] + }, + "data": { + "param2": "0x76418c1f3cc8305025ce913e271c62064302cfc52a3cbcee02ffae444e402b" + }, + "encoded": "0x5d03dd39f516dbdce0dda1249a18778b381083c568572fa40e677a1e8bcba69176418c1f3cc8305025ce913e271c62064302cfc52a3cbcee02ffae444e402b00", + "digest": "0x70669d88e5b14dde860f7967400d4001ea51ad347d9dc8e9e14a78a046620a0f" + }, + { + "name": "random-88", + "domain": { + "version": "34.19.0" + }, + "primaryType": "Struct6", + "types": { + "EIP712Domain": [ + { + "name": "version", + "type": "string" + } + ], + "Struct6": [ + { + "name": "param2", + "type": "bool[3]" + }, + { + "name": "param4", + "type": "string" + }, + { + "name": "param5", + "type": "bool" + } + ] + }, + "data": { + "param2": [ + true, + false, + true + ], + "param4": "Moo é🚀o éo🚀é Moo🚀Mo🚀 éoMoooooM ", + "param5": true + }, + "encoded": "0xeb561f00a2ce847d36d192a18b66be2e5938b7c84798b9a1730ef44ce41ef7c35c6090c0461491a2941743bda5c3658bf1ea53bbd3edcde54e16205e18b457922e1277d90577b137ca94be5c47d97fdd62924edbcd364fffd8f2543a0b3974cb0000000000000000000000000000000000000000000000000000000000000001", + "digest": "0x284ff781d137b80a6aedaf25217692a7bee1529b50f717d20fc8b113ea3d10eb" + }, + { + "name": "random-89", + "domain": { + "name": "Moo é🚀MooMé 🚀é é éM ooo🚀M 🚀oo🚀 🚀oooo🚀éooooM 🚀🚀 oMé", + "verifyingContract": "0x94104688cc800c04a9f4ce356606469f7aaf0889", + "salt": "0xccd084925ccd16ac0a852763036118c175fe5a86811c7ed8ba75bd427a63985b" + }, + "primaryType": "Struct3", + "types": { + "EIP712Domain": [ + { + "name": "name", + "type": "string" + }, + { + "name": "verifyingContract", + "type": "address" + }, + { + "name": "salt", + "type": "bytes32" + } + ], + "Struct3": [ + { + "name": "param2", + "type": "address" + } + ] + }, + "data": { + "param2": "0x7af6a9843bfac96994bcecebe57b40d67942fded" + }, + "encoded": "0x98edda54668461e736d24a4033f2d11540bceac48ea14c7da0fea73b8b9a55db0000000000000000000000007af6a9843bfac96994bcecebe57b40d67942fded", + "digest": "0x7db9f61f14ad00540fd7f51c7adeeff792ab7ef3bdfd672713223f0ff3920fea" + }, + { + "name": "random-90", + "domain": { + "verifyingContract": "0x9511e9dcad6fbe5e6693a1d7031a592f27c5c4ef" + }, + "primaryType": "Struct5", + "types": { + "EIP712Domain": [ + { + "name": "verifyingContract", + "type": "address" + } + ], + "Struct5": [ + { + "name": "param2", + "type": "bytes16" + }, + { + "name": "param3", + "type": "uint56" + }, + { + "name": "param4", + "type": "bytes25" + } + ] + }, + "data": { + "param2": "0x88f0dd9c8cbee263cf947a650a3b3151", + "param3": "17583036303696475", + "param4": "0x00153b38e3509586ea81d62acfaf69f58033c03989a5e9ed83" + }, + "encoded": "0x192ce62b78aff9164de71a0394d492eb37f524dfda52e333ccd477c065fbe73a88f0dd9c8cbee263cf947a650a3b315100000000000000000000000000000000000000000000000000000000000000000000000000000000003e77ada8f4625b00153b38e3509586ea81d62acfaf69f58033c03989a5e9ed8300000000000000", + "digest": "0x39c13ee9471e159919485ab461d454a50ca52a6ddd8cdadfbed90326801f74ee" + }, + { + "name": "random-91", + "domain": { + "version": "48.32.32", + "verifyingContract": "0xdc2f8681bcb020c14bb5a61803be1f1904934a7c" + }, + "primaryType": "Struct3", + "types": { + "EIP712Domain": [ + { + "name": "version", + "type": "string" + }, + { + "name": "verifyingContract", + "type": "address" + } + ], + "Struct3": [ + { + "name": "param2", + "type": "bool" + } + ] + }, + "data": { + "param2": true + }, + "encoded": "0xd827c65b54cb667b8cb0c84a4aa21ffe3d188aa9cdaede527a285c318ad7274e0000000000000000000000000000000000000000000000000000000000000001", + "digest": "0x465f1b1db471abcc586e028f388b54cb4e0c9d341d9c03fd4def6118ca7ac479" + }, + { + "name": "random-92", + "domain": { + "name": "Moo é🚀🚀MMéo 🚀🚀MoooooéMM🚀ooMoo🚀ooo 🚀o oéoo 🚀éoooé🚀Moo 🚀éooM🚀é🚀", + "version": "15.24.22", + "salt": "0xf6b0ebe21875667066601b50ad91163654013e109e51fae6a8bf19a988a51adf" + }, + "primaryType": "Struct8", + "types": { + "EIP712Domain": [ + { + "name": "name", + "type": "string" + }, + { + "name": "version", + "type": "string" + }, + { + "name": "salt", + "type": "bytes32" + } + ], + "Struct4": [ + { + "name": "param3", + "type": "uint160" + } + ], + "Struct8": [ + { + "name": "param2", + "type": "Struct4" + }, + { + "name": "param5", + "type": "bytes25" + }, + { + "name": "param6", + "type": "bool" + }, + { + "name": "param7", + "type": "bool" + } + ] + }, + "data": { + "param2": { + "param3": "990067310911447424079721892655020403107523900579" + }, + "param5": "0x929ecef7eecff36ca4a793303bfeb2f7666d3015f472177f92", + "param6": false, + "param7": true + }, + "encoded": "0xfc75a8485d94b0cf924739d8db8392a3933b768aaf87990e0ef313c9613af2d569d3dc7ee6ddfd3cff85001b724976fea388b0d40fe7864b4d79e32eb76adc8c929ecef7eecff36ca4a793303bfeb2f7666d3015f472177f920000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001", + "digest": "0x650fa8632ebb3ad17d74fd519317c5bd99cf2210d6742be16bba20ad7bdb3055" + }, + { + "name": "random-93", + "domain": { + "name": "Moo é🚀MéMM éooéoMoo🚀 M🚀M🚀o🚀🚀🚀🚀oo🚀🚀🚀Mo🚀🚀🚀M o🚀oMMoé M M" + }, + "primaryType": "Struct9", + "types": { + "EIP712Domain": [ + { + "name": "name", + "type": "string" + } + ], + "Struct8": [ + { + "name": "param4", + "type": "bytes21" + }, + { + "name": "param5", + "type": "bool" + }, + { + "name": "param6", + "type": "bool" + }, + { + "name": "param7", + "type": "address" + } + ], + "Struct9": [ + { + "name": "param2", + "type": "Struct8[]" + } + ] + }, + "data": { + "param2": [] + }, + "encoded": "0xef6a7dac2342e48bc7bee2421009cf3129bbeb0bb7fdeaba99402bea15f82c76c5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470", + "digest": "0xba75038176156de4c9a80a8bb70ac20bb140c22b707179be3ae4a554a4e8ea23" + }, + { + "name": "random-94", + "domain": { + "name": "Moo é🚀 🚀éooéoooo ooMo éMo o M🚀Mé🚀ooM o Mé🚀éoMMoo", + "chainId": 1017 + }, + "primaryType": "Struct3", + "types": { + "EIP712Domain": [ + { + "name": "name", + "type": "string" + }, + { + "name": "chainId", + "type": "uint256" + } + ], + "Struct3": [ + { + "name": "param2", + "type": "bool" + } + ] + }, + "data": { + "param2": true + }, + "encoded": "0xd827c65b54cb667b8cb0c84a4aa21ffe3d188aa9cdaede527a285c318ad7274e0000000000000000000000000000000000000000000000000000000000000001", + "digest": "0x69ec47b02da2c0ebfa801692ca474f282d9f0df84f87c45cdc921ce1a0d860bb" + }, + { + "name": "random-95", + "domain": { + "version": "18.32.12", + "chainId": 742, + "verifyingContract": "0xabe3c4f5cf4ed1b97303bd7cae8b067c5e7f8c26" + }, + "primaryType": "Struct4", + "types": { + "EIP712Domain": [ + { + "name": "version", + "type": "string" + }, + { + "name": "chainId", + "type": "uint256" + }, + { + "name": "verifyingContract", + "type": "address" + } + ], + "Struct4": [ + { + "name": "param2", + "type": "bytes[]" + } + ] + }, + "data": { + "param2": [ + "0x38a9ee766abe7a928c112125e65ffb984ace", + "0x3a3613418bc9b1045fe5809d" + ] + }, + "encoded": "0xfbb1adeb7d907dcad24d9dcc6c70efeb95e9983697f2b3d761bbc66df10b6de0c645b2e2e519bae0fadbaf7ef2932226f7e7935d3d837af12839bd360aa64a4d", + "digest": "0xc58c6e7c7e79b525438c151e97c6457f8c4c7b3065827d36e227d6efdbd5cb94" + }, + { + "name": "random-96", + "domain": { + "chainId": 239, + "verifyingContract": "0x0f77912b8136385729a659bd3ef9107a61df2481" + }, + "primaryType": "Struct36", + "types": { + "EIP712Domain": [ + { + "name": "chainId", + "type": "uint256" + }, + { + "name": "verifyingContract", + "type": "address" + } + ], + "Struct12": [ + { + "name": "param11", + "type": "bool" + } + ], + "Struct25": [ + { + "name": "param21", + "type": "bytes" + }, + { + "name": "param22", + "type": "string" + }, + { + "name": "param23", + "type": "uint232" + }, + { + "name": "param24", + "type": "int104" + } + ], + "Struct29": [ + { + "name": "param20", + "type": "Struct25" + }, + { + "name": "param26", + "type": "bool" + }, + { + "name": "param27", + "type": "string" + }, + { + "name": "param28", + "type": "string" + } + ], + "Struct30": [ + { + "name": "param19", + "type": "Struct29" + } + ], + "Struct31": [ + { + "name": "param15", + "type": "address" + }, + { + "name": "param16", + "type": "bytes" + }, + { + "name": "param17", + "type": "bytes" + }, + { + "name": "param18", + "type": "Struct30" + } + ], + "Struct33": [ + { + "name": "param10", + "type": "Struct12" + }, + { + "name": "param13", + "type": "string" + }, + { + "name": "param14", + "type": "Struct31" + }, + { + "name": "param32", + "type": "bool" + } + ], + "Struct36": [ + { + "name": "param2", + "type": "Struct8" + }, + { + "name": "param9", + "type": "Struct33" + }, + { + "name": "param34", + "type": "string" + }, + { + "name": "param35", + "type": "bytes" + } + ], + "Struct6": [ + { + "name": "param5", + "type": "bytes" + } + ], + "Struct8": [ + { + "name": "param3", + "type": "bool" + }, + { + "name": "param4", + "type": "Struct6" + }, + { + "name": "param7", + "type": "address" + } + ] + }, + "data": { + "param2": { + "param3": false, + "param4": { + "param5": "0xa997ae99319d4d5f78001116304b85963cfd598ae95fcc739b489ed254c80862bcefb6dd5c83fed172b8" + }, + "param7": "0x6a437c4aa417145b8550cb480a73690dc14b69c7" + }, + "param34": "Moo é🚀🚀Mééé o 🚀 é 🚀éo MMMo Méoéo", + "param35": "0xed7ea3d3b80622c23d53b27611234455c18fde3d2ed4b55c99a0d1afaa6b15dc9f", + "param9": { + "param10": { + "param11": true + }, + "param13": "Moo é🚀M ooMooé Moo🚀éMo🚀éoé🚀🚀🚀é🚀 Mo🚀 MMo", + "param14": { + "param15": "0xba4bc495f2ea2017b703f7ef1fcbbb762fd05bae", + "param16": "0xceb6a00304eb1d36e62a93652e03722fa1978be1a5b799a6715055006e3de8d49f47f5dea681b7c3f3e032351f494f4eebd5ecfc2ea1", + "param17": "0x53ceecbbeef73e6004a2a48e4b38e8c524fd820f8ac5feba048a31d2c1244c4be8f824f1146f5d882aac2ac4d0421443b56141d09e9b9fc1256c469446", + "param18": { + "param19": { + "param20": { + "param21": "0x575b70fdb462625a1e9cdbee09c61a7a9704ee2a1cbdeba468344e2f97f548158c56b0", + "param22": "Moo é🚀ééM éé éoooMMooM ", + "param23": "3630081370417832362319236809711268993163922661583325307782224246317793", + "param24": "7856537978551137247557348592909" + }, + "param26": false, + "param27": "Moo é🚀🚀 ooMooo éo oM 🚀oéMM🚀oMMooéoéoMoo 🚀éMooéooo MM🚀 oo éo🚀oo ", + "param28": "Moo é🚀oMMéoM o" + } + } + }, + "param32": true + } + }, + "encoded": "0xf9e484b4d24731dcead8e64be7ead35857e953e16e64f5a50ed612f8605ce07d78b6a6b52e423f732676bd55ebd747cc95b670636328d911128444f847b62abd043e6379a56f021f2d7de4165f4f085e23e25ef8e536f8c2415663bfc2ef9323655ae94c06fc9d476b0d105722d4a32d1eb7a4b5529891ec029139fae945d061fcafb084efc157e2ff1daf20bdd536dd513ed329787f2f85f3c3e2c52b597d3b", + "digest": "0xbc43bb6f933954e8ed291b32d35556398a692421734dab354bb9401c2ecaa775" + }, + { + "name": "random-97", + "domain": { + "verifyingContract": "0xc0b2427daf81b3b1ae287b45177cd8cb2dd7fcf0" + }, + "primaryType": "Struct4", + "types": { + "EIP712Domain": [ + { + "name": "verifyingContract", + "type": "address" + } + ], + "Struct4": [ + { + "name": "param2", + "type": "address" + }, + { + "name": "param3", + "type": "string" + } + ] + }, + "data": { + "param2": "0xa145dd9dccf1d0d72382ec1f2d82d7cd48827ec3", + "param3": "Moo é🚀🚀éoM ooooéo Méé M o🚀oééééMoMoéo🚀oo🚀 Mo🚀oo🚀oo o" + }, + "encoded": "0x666400a396b19434e5bdf93d49163a67dd8b6e60e8df8160e510c7c1288ac5da000000000000000000000000a145dd9dccf1d0d72382ec1f2d82d7cd48827ec3d6991f578c45fb43694bc4e1cf51abc5fb10782beff88449580449e1ab8b1b09", + "digest": "0xce67b3dc8f8249f8ee121de29a726e1c1f4ac4b73971002fb69823d3e752b5b0" + }, + { + "name": "random-98", + "domain": { + "verifyingContract": "0xd2489de35e4726e26c6d21423d8627a34ed723b1", + "salt": "0xdee197f170390ffe117985bc1b6f75d2aa76cffd744a1ed741d9e5fdef371804" + }, + "primaryType": "Struct6", + "types": { + "EIP712Domain": [ + { + "name": "verifyingContract", + "type": "address" + }, + { + "name": "salt", + "type": "bytes32" + } + ], + "Struct6": [ + { + "name": "param2", + "type": "bool" + }, + { + "name": "param3", + "type": "bytes2" + }, + { + "name": "param4", + "type": "bool" + }, + { + "name": "param5", + "type": "bytes8" + } + ] + }, + "data": { + "param2": true, + "param3": "0x2512", + "param4": false, + "param5": "0x0f8158ca29114619" + }, + "encoded": "0x23b6f6f83199a6201a90b8b8b9457c300d054549f0d8c6084fc8a0b44442f7f30000000000000000000000000000000000000000000000000000000000000001251200000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000f8158ca29114619000000000000000000000000000000000000000000000000", + "digest": "0xe4baa2c2a442cd32eaabe94c6db4c30460bd5ab3e50e1238cd265e7e39a5b17f" + }, + { + "name": "random-99", + "domain": { + "name": "Moo é🚀 o ooéM🚀🚀🚀éé🚀éoé 🚀🚀oéM éMooooM ooooéo M oMMMoo🚀éooooo ", + "version": "44.14.34", + "chainId": 401 + }, + "primaryType": "Struct5", + "types": { + "EIP712Domain": [ + { + "name": "name", + "type": "string" + }, + { + "name": "version", + "type": "string" + }, + { + "name": "chainId", + "type": "uint256" + } + ], + "Struct5": [ + { + "name": "param2", + "type": "bytes26" + }, + { + "name": "param3", + "type": "bool" + }, + { + "name": "param4", + "type": "string" + } + ] + }, + "data": { + "param2": "0x77c8ed30964f4ad426b83e5a079c394eeb6108670d59ea82bbef", + "param3": false, + "param4": "Moo é🚀 o oM🚀 éoMoé🚀MMooMM🚀 oo🚀🚀oo ooMoo🚀M🚀M" + }, + "encoded": "0x22ff007229938669cf96193b9dcf351dadbe0c48990093a41d72b39335e9accb77c8ed30964f4ad426b83e5a079c394eeb6108670d59ea82bbef000000000000000000000000000000000000000000000000000000000000000000000000000037ed224a35fef81262bbee1f953d29657ee71ff814272f38dd451a46d46ab71f", + "digest": "0xdedd776daec0f194b00d73d63004c5e0883fad5072a458f61ba77635fe04b766" + }, + { + "name": "random-100", + "domain": { + "name": "Moo é🚀M éMooo 🚀éM M 🚀oooMéo🚀MoM🚀o🚀oo🚀oéoM🚀🚀éo🚀o é 🚀🚀 MéMooéo", + "version": "47.45.45", + "chainId": 1297, + "salt": "0x46fd6ce92f74a7398373a2ccbcb0585fe3453b45e4a6a0a03dcab2c2dc9c9d7f" + }, + "primaryType": "Struct8", + "types": { + "EIP712Domain": [ + { + "name": "name", + "type": "string" + }, + { + "name": "version", + "type": "string" + }, + { + "name": "chainId", + "type": "uint256" + }, + { + "name": "salt", + "type": "bytes32" + } + ], + "Struct7": [ + { + "name": "param4", + "type": "address" + }, + { + "name": "param5", + "type": "address" + }, + { + "name": "param6", + "type": "string" + } + ], + "Struct8": [ + { + "name": "param2", + "type": "bytes21" + }, + { + "name": "param3", + "type": "Struct7" + } + ] + }, + "data": { + "param2": "0x48cd5f1c09c5e268f1a924b1aa9d6483e7de7344da", + "param3": { + "param4": "0xee40a1c631d8f9a0ed7522896b5af8909914caa6", + "param5": "0xf4e494cef1caac185b2886e37a0c0df00b8ccc95", + "param6": "Moo é🚀🚀éoMMoMoéooMo o M🚀o" + } + }, + "encoded": "0x8efe36edac72ec5f57c5c2283101ce2d7121dad612fdb566b4b332088a533f1048cd5f1c09c5e268f1a924b1aa9d6483e7de7344da0000000000000000000000490aa4d83fa47e2e7ec10d335cc7598977c6a4c02ac383ac4f0498c6fcf0370d", + "digest": "0xc757c9b58178cdd87b024fc62b56cde9056291433a95211420bfc2fd3dc67804" + }, + { + "name": "random-101", + "domain": { + "name": "Moo é🚀🚀 🚀éMo M ooMoo🚀 ooooo🚀Mo🚀é", + "version": "32.48.49", + "chainId": 1182, + "salt": "0x89cb0d39315e605914b2629ef0e737a9b2a18c3393c649515dcad65464619c68" + }, + "primaryType": "Struct18", + "types": { + "EIP712Domain": [ + { + "name": "name", + "type": "string" + }, + { + "name": "version", + "type": "string" + }, + { + "name": "chainId", + "type": "uint256" + }, + { + "name": "salt", + "type": "bytes32" + } + ], + "Struct13": [ + { + "name": "param10", + "type": "address" + }, + { + "name": "param11", + "type": "bool" + }, + { + "name": "param12", + "type": "address" + } + ], + "Struct16": [ + { + "name": "param15", + "type": "address" + } + ], + "Struct17": [ + { + "name": "param4", + "type": "Struct8" + }, + { + "name": "param9", + "type": "Struct13" + }, + { + "name": "param14", + "type": "Struct16" + } + ], + "Struct18": [ + { + "name": "param2", + "type": "address" + }, + { + "name": "param3", + "type": "Struct17" + } + ], + "Struct8": [ + { + "name": "param5", + "type": "bytes" + }, + { + "name": "param6", + "type": "bytes" + }, + { + "name": "param7", + "type": "bytes" + } + ] + }, + "data": { + "param2": "0x6dcdf91a71234de95982294b661b0be4040ab340", + "param3": { + "param14": { + "param15": "0x6e821dcdcc749da3ccacd453a49ba1484ca8cc05" + }, + "param4": { + "param5": "0x8f32b8d15469bb5a8cddeb0167cd84302a4e426eb67e11405ab05aac13b295828323e287c95df22a50869d5dd7ce", + "param6": "0x5456f8009eb577a667fec8439a686e117fc007519326c10d2e9e55d870266cc277694e42aed00e176912b316d8a91ad20009e8abd316d63963ed4f749c8c9e", + "param7": "0x561026b99073b6eecdf3c4c6d47c27e2754a923f461b79c523362b936a" + }, + "param9": { + "param10": "0xe59adaff5dc1857e78e568e4a1d128d73b117e42", + "param11": true, + "param12": "0x4eef176f86a9782d9191463852afd6dd3a1abae3" + } + } + }, + "encoded": "0x3542196ee308e13b334816ef2496b2d489531edbfedf1df81b61e1d68142a6b30000000000000000000000006dcdf91a71234de95982294b661b0be4040ab340d6736ac6f05a75e17728b8ba7bdb28a31b515912dada4600176132be7e17f85f", + "digest": "0x06dadcc084886d4e4d42c01748794cc70125d59dfc9140a304ac4eecc93faaa5" + }, + { + "name": "random-102", + "domain": { + "version": "5.37.45", + "chainId": 1112, + "verifyingContract": "0xad34d80f19994d30f98da4a9127394233f5d1b62" + }, + "primaryType": "Struct6", + "types": { + "EIP712Domain": [ + { + "name": "version", + "type": "string" + }, + { + "name": "chainId", + "type": "uint256" + }, + { + "name": "verifyingContract", + "type": "address" + } + ], + "Struct5": [ + { + "name": "param4", + "type": "bytes" + } + ], + "Struct6": [ + { + "name": "param2", + "type": "bytes29" + }, + { + "name": "param3", + "type": "Struct5" + } + ] + }, + "data": { + "param2": "0xa22678edf23dbbd8eaff7e09c5a6898c086e15f5b9afa1d3085ef7c551", + "param3": { + "param4": "0x3d04e4139a6340cf4e183cd5ee6b6d0f585ebad98bbe98a6934a4880a940095e66da08286166fe86feec2b680597deeb4486089095d235" + } + }, + "encoded": "0xa8831e2b1ae12bad49bd738de2fec417a85c70cbd1582c58b1a0fa67d0db417ba22678edf23dbbd8eaff7e09c5a6898c086e15f5b9afa1d3085ef7c5510000004e3b46155fd76195aa056c321070eaf751287eb0839bf1f1f03f0b67d98a5331", + "digest": "0x53b91d82e450b29ebfcd9d912233db26fd277b4ea288c15a4700981b56a723da" + }, + { + "name": "random-103", + "domain": { + "name": "Moo é🚀o M🚀 MoooMéM🚀🚀oé 🚀o🚀M🚀o éMo ", + "version": "34.7.4", + "verifyingContract": "0xffb9dcc2ad72de5d51a6523c3c59db76b5efce82", + "salt": "0x91a6bbeac26f0d774f89f90732838632f7848fba09dccbbbfc4dd0d0fc1a21ae" + }, + "primaryType": "Struct5", + "types": { + "EIP712Domain": [ + { + "name": "name", + "type": "string" + }, + { + "name": "version", + "type": "string" + }, + { + "name": "verifyingContract", + "type": "address" + }, + { + "name": "salt", + "type": "bytes32" + } + ], + "Struct5": [ + { + "name": "param2", + "type": "uint168" + }, + { + "name": "param3", + "type": "bytes7" + }, + { + "name": "param4", + "type": "string" + } + ] + }, + "data": { + "param2": "52618101586894465619352011808458733029823328733625", + "param3": "0xed40337a19d528", + "param4": "Moo é🚀oé🚀🚀éoo MM ooéééM MMoM éo MoooM 🚀oo🚀oooM🚀éoM oéMéo🚀" + }, + "encoded": "0x166448b3a08b4d0577e607bbf8fa9117e476d4eebfef40d0a03af3022fc95d3c00000000000000000000002400b54743f81e79d07dbe7589e7130815622ec9b9ed40337a19d52800000000000000000000000000000000000000000000000000fcce90f9868722c814b5068c652b2374b1623dcf15ef57b3a67abfb337c9b30d", + "digest": "0xde37ba3588f474a46a8f87b3a97d06f80c18f5f640c2c82eee6b8d35009a2710" + }, + { + "name": "random-104", + "domain": { + "salt": "0x1eda86bf849f89bc2e6f69245118892e38bc6e78ae6b8952228a26f52872db44" + }, + "primaryType": "Struct4", + "types": { + "EIP712Domain": [ + { + "name": "salt", + "type": "bytes32" + } + ], + "Struct4": [ + { + "name": "param2", + "type": "string" + }, + { + "name": "param3", + "type": "bytes" + } + ] + }, + "data": { + "param2": "Moo é🚀ooo oo🚀oMoooo🚀M🚀M🚀oMoM🚀 M oooéMM🚀o ooéé", + "param3": "0x734299d683c01075c7d7806cabbf" + }, + "encoded": "0x4d0024736263d208aca6e84c7bb7336060a12a4693f70ea14290ed4308d052e586c6628e3abdd5327542024ebfd60ba121feeb94ad0a9fc69718ad35d60e8d7dacc5b4662798e9bfefc01e39285d731e00c77445dad6f1ce9bb875db4cd56e11", + "digest": "0x25bfcd9f9bf4070fa8e64500fc1f03d8b47e1d8475454f4164f235b568b61373" + }, + { + "name": "random-105", + "domain": { + "name": "Moo é🚀ooéoM 🚀🚀 o🚀ééé é🚀éoMo", + "chainId": 1004, + "salt": "0x4bf151c92fcec680b466e8f725517067ecba2bfebd218159b4f7865020861285" + }, + "primaryType": "Struct6", + "types": { + "EIP712Domain": [ + { + "name": "name", + "type": "string" + }, + { + "name": "chainId", + "type": "uint256" + }, + { + "name": "salt", + "type": "bytes32" + } + ], + "Struct6": [ + { + "name": "param2", + "type": "string" + }, + { + "name": "param3", + "type": "int40[3]" + }, + { + "name": "param5", + "type": "int40" + } + ] + }, + "data": { + "param2": "Moo é🚀 o 🚀oM🚀M 🚀🚀éooM🚀🚀éMoéo", + "param3": [ + "242858918476", + "77291718371", + "530770037136" + ], + "param5": "263215721237" + }, + "encoded": "0x47b2e0706327e941b7095dbab4500c0798ada3576db783a0499106e8538863be00db7a448333a0944bbe0ded5bee79fc3ea07f15863bc059c42eefdc8a3db436175773b0cff47dcc6167eafef357b7743e0fe0c2e5912473777f63bd463b41fa0000000000000000000000000000000000000000000000000000003d48e12b15", + "digest": "0x9ff750f7890c19f215900f6fe0a413c0a18a8765e974c8d73b996f78ad62c0f9" + }, + { + "name": "random-106", + "domain": { + "verifyingContract": "0xfceb71a9ad2869c8e3dff6fa3669abc9352f783d" + }, + "primaryType": "Struct3", + "types": { + "EIP712Domain": [ + { + "name": "verifyingContract", + "type": "address" + } + ], + "Struct3": [ + { + "name": "param2", + "type": "string" + } + ] + }, + "data": { + "param2": "Moo é🚀 oMooMoéMoooMo🚀éoMooM🚀M🚀oM M éMéoé🚀MoM oMM 🚀o🚀o🚀o" + }, + "encoded": "0x5927d86a0ef9a01a131f7a41d2a9c89a8c82e0f454d6b4502f955f90f152eb519053df716c21aad94a049e52bc52aad715c95913062abd88cd324bdc0e09d147", + "digest": "0xbee1559cffdba5659309d50ed1ad306c33a388b34a1535fd5404851db7fa4ab3" + }, + { + "name": "random-107", + "domain": { + "salt": "0x5bfb5f78bd6682c97b6f3c94bc4cf62d34110e5ad83b21b3f9432fbeaf8bcd4f" + }, + "primaryType": "Struct6", + "types": { + "EIP712Domain": [ + { + "name": "salt", + "type": "bytes32" + } + ], + "Struct6": [ + { + "name": "param2", + "type": "bytes" + }, + { + "name": "param3", + "type": "bool" + }, + { + "name": "param4", + "type": "bool" + }, + { + "name": "param5", + "type": "string" + } + ] + }, + "data": { + "param2": "0x26d3edf93ed79e6435ed818c07a5dd71b20c7690c3c09850f10f3155ff7e2c1b7e6d993cd4f2df0e05488c19ea16e55e14da0596492eadcd1ae2", + "param3": true, + "param4": false, + "param5": "Moo é🚀🚀éM 🚀🚀Mo é Méé o M🚀é" + }, + "encoded": "0x89e191a0c9d59f32cab1f2423029c95a85ca34531f959e94053e40ebf4267c5f4fccd6381664502c41c49bc5cd078cffe187640d7f9304c0661bb223f9b2dcdb00000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000000fd174bfd259833e8efb11ee94ba88bfd1632028092b0028084e3ce4c0015cd34", + "digest": "0xdc19c7f5a4fb303efd1e2de89b9ca2082a6d702dc248e95764d90a109cd986ef" + }, + { + "name": "random-108", + "domain": { + "version": "39.28.36", + "chainId": 631, + "verifyingContract": "0x9bd5ff86e47b56b27505902cc94b3d6329a1ed9f" + }, + "primaryType": "Struct8", + "types": { + "EIP712Domain": [ + { + "name": "version", + "type": "string" + }, + { + "name": "chainId", + "type": "uint256" + }, + { + "name": "verifyingContract", + "type": "address" + } + ], + "Struct6": [ + { + "name": "param5", + "type": "address" + } + ], + "Struct8": [ + { + "name": "param2", + "type": "string" + }, + { + "name": "param3", + "type": "string" + }, + { + "name": "param4", + "type": "Struct6" + }, + { + "name": "param7", + "type": "address" + } + ] + }, + "data": { + "param2": "Moo é🚀o🚀 éooooM oo🚀o o", + "param3": "Moo é🚀 Mo", + "param4": { + "param5": "0xce4186253cc508af322c4e39cff34a1319599a15" + }, + "param7": "0xd75f12aef677e43bdaa89783ee4572178e3bf2a7" + }, + "encoded": "0x0cf85620ea91e290d99f71f9ee10e2d73daa1b4b71b823e2362954720b7005706d7c4cf22de5351fbb6d76b5fa30da35546a802285ffee4d2bbbbbb9b6363631f03d6b228022926635e1c91913f4f990e5091fdfb5833fa5926f70aef635fd07cbed95fc50b5e3a119bc2286b468bb71a1d3fda6d9b488e82b6f9d38eae3f1fd000000000000000000000000d75f12aef677e43bdaa89783ee4572178e3bf2a7", + "digest": "0xde95998c10032e5774f76f2cadc5e385237bb061d35ca1ff3e3fec1abc60b2c5" + }, + { + "name": "random-109", + "domain": { + "name": "Moo é🚀 🚀M🚀éooé oMoé", + "version": "47.13.9", + "chainId": 1135, + "verifyingContract": "0xf3e8de92425a724a4b5f5f34ff462f4a21c022a6", + "salt": "0x92a39b51b11fe26a4b7c9b5c9dc46e4eed900408b0acdaade7ef2d2000f2180c" + }, + "primaryType": "Struct7", + "types": { + "EIP712Domain": [ + { + "name": "name", + "type": "string" + }, + { + "name": "version", + "type": "string" + }, + { + "name": "chainId", + "type": "uint256" + }, + { + "name": "verifyingContract", + "type": "address" + }, + { + "name": "salt", + "type": "bytes32" + } + ], + "Struct4": [ + { + "name": "param3", + "type": "bool" + } + ], + "Struct7": [ + { + "name": "param2", + "type": "Struct4" + }, + { + "name": "param5", + "type": "bool" + }, + { + "name": "param6", + "type": "bytes" + } + ] + }, + "data": { + "param2": { + "param3": false + }, + "param5": true, + "param6": "0x2b9eb331163da92c413824164a25f6f94e6e101867847a81de73eb550e5587bcf8737d49f18648d069dbeb0a88bd48c4806a0bd22d07fe7ee7f03096ae61f1" + }, + "encoded": "0x92bcc5ebbe1a24ca641c980aeedbf345d2bb96c75c6e7160a63416ce671e5ba350ba05c95cd64746bc1afc3818f62a6d56861e27a783ff9f39b0f6dc1bebd746000000000000000000000000000000000000000000000000000000000000000114e055471446e3c12424b16ad746b49bb92201f598be3c5f5c500e39cd4b420c", + "digest": "0x26ed036a8e9356433ec6273d9d6bc87f49faacb9b4aee0c1922ab2054032a5dd" + }, + { + "name": "random-110", + "domain": { + "version": "33.16.36", + "salt": "0x685eeb5072a1173bec4979c7dfdc054601f6f80cbe7ad78f4eb43a7e1bfbf55d" + }, + "primaryType": "Struct7", + "types": { + "EIP712Domain": [ + { + "name": "version", + "type": "string" + }, + { + "name": "salt", + "type": "bytes32" + } + ], + "Struct7": [ + { + "name": "param2", + "type": "bytes" + }, + { + "name": "param3", + "type": "string[1]" + }, + { + "name": "param5", + "type": "bytes32" + }, + { + "name": "param6", + "type": "bytes" + } + ] + }, + "data": { + "param2": "0xa413fc1c6885bc0c48bf8084ef2521d68ee45bc1e23b79e72a7e414511", + "param3": [ + "Moo é🚀🚀ooo🚀Mé🚀M 🚀é🚀é🚀éé M🚀oé🚀ooé🚀Méoé🚀Mo M MMMo🚀 " + ], + "param5": "0xa1f41b79c9af58bec8b31385ecf4c6f1418a351add6fabfeabfa2014583520e4", + "param6": "0xff444eb48a465c6021e011bafad0310066ac09abe890fbe1159732d8f010ca0474270fb7a911db0dfb09882fbb87c29df1f1bf87" + }, + "encoded": "0x2e43045a9a14c67d9b0a9014b72f4aa002f40464467aed0285d4c0c05453173c13b6f03fd29a02d0b024351ffe30c322d189714f11a5645864b1d1ae509c2e6cfe3999130ddf7df630cb54ae95658105ce7dd2c1e39561092b26d42f71c0089fa1f41b79c9af58bec8b31385ecf4c6f1418a351add6fabfeabfa2014583520e436b28fe9e0f31ab8023143ce0634d9618d6068007d0eb02006adbb5fce6c648c", + "digest": "0x853e9a5edaa2da1980831dadec0754c352e34785ecf55789d599a00e28287f70" + }, + { + "name": "random-111", + "domain": { + "verifyingContract": "0x96733f3f5e8bcff66190bf82dc5523bbc8145456" + }, + "primaryType": "Struct3", + "types": { + "EIP712Domain": [ + { + "name": "verifyingContract", + "type": "address" + } + ], + "Struct3": [ + { + "name": "param2", + "type": "bytes4" + } + ] + }, + "data": { + "param2": "0x1ba2e5ab" + }, + "encoded": "0x19a687863ba581a953b0f694f34073f5005a5d39e5c94be277688507ea013a261ba2e5ab00000000000000000000000000000000000000000000000000000000", + "digest": "0x7f0d4714c742f29a9f2a182af25643153529da444383e342937489226f5a0a15" + }, + { + "name": "random-112", + "domain": { + "version": "44.26.33", + "salt": "0x2287f7611aabf794bc8f829715f8794eb76fc9c73ba6143df65e32b231cc8e55" + }, + "primaryType": "Struct6", + "types": { + "EIP712Domain": [ + { + "name": "version", + "type": "string" + }, + { + "name": "salt", + "type": "bytes32" + } + ], + "Struct6": [ + { + "name": "param2", + "type": "bytes30" + }, + { + "name": "param3", + "type": "int152" + }, + { + "name": "param4", + "type": "address" + }, + { + "name": "param5", + "type": "bytes27" + } + ] + }, + "data": { + "param2": "0xab54e61a59de9271cf657d72cc1fed2256d52d003f3ca02ad8e407182637", + "param3": "-316082391023394535331711337027447687645790587", + "param4": "0x0af88ce740fb6aa140d5e7a7828bfb6755fd7f98", + "param5": "0xd0ae690c13a9cefd7fdf00cc425174f32afc2c13f94bf6fcb96c87" + }, + "encoded": "0x821c71dc6eceb8c412faf525df599ffeb9b576bb83836862f7c6d2bf3817f7aeab54e61a59de9271cf657d72cc1fed2256d52d003f3ca02ad8e4071826370000fffffffffffffffffffffffffff1d38d565f73f2bb8b6566354cf4080bf37e850000000000000000000000000af88ce740fb6aa140d5e7a7828bfb6755fd7f98d0ae690c13a9cefd7fdf00cc425174f32afc2c13f94bf6fcb96c870000000000", + "digest": "0x2bea9c81bb7324309e6659333720bcd45837c9360578495badf83b04d2c602c7" + }, + { + "name": "random-113", + "domain": { + "version": "40.24.13", + "verifyingContract": "0xc246f6e3450948c0f556d212e3129013f3bbdf3f", + "salt": "0x0c5313fdf2e387a82d3e2ce3eda9de53e5c65bfea57db445206b3a4532fc9d10" + }, + "primaryType": "Struct6", + "types": { + "EIP712Domain": [ + { + "name": "version", + "type": "string" + }, + { + "name": "verifyingContract", + "type": "address" + }, + { + "name": "salt", + "type": "bytes32" + } + ], + "Struct6": [ + { + "name": "param2", + "type": "string" + }, + { + "name": "param3", + "type": "int200" + }, + { + "name": "param4", + "type": "bool" + }, + { + "name": "param5", + "type": "bool" + } + ] + }, + "data": { + "param2": "Moo é🚀éé 🚀 🚀 🚀🚀é", + "param3": "-58887618035548045232392023063198065294192444334212910225231", + "param4": false, + "param5": true + }, + "encoded": "0xa161771f2f5479ff0f53bcfbb589145f09ab64fa5e603ddd44010178ec5e3f62d5e9200f0ca4e5342abc1609bb3d7f8a1d820568d292e810a5b38eff5cf8c2f8fffffffffffffff69e6094a73803f15dc86478bdaeca4e2c460355eaee2ee8b100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001", + "digest": "0xca0aeafff982a7caa90778ef79c02370f41d7d3c118d6f0674db47aa844dcb53" + }, + { + "name": "random-114", + "domain": { + "salt": "0x53dd66e28993d92f3726c54eebc889ee1feb5de9e6956f17916a70af94a9be8c" + }, + "primaryType": "Struct6", + "types": { + "EIP712Domain": [ + { + "name": "salt", + "type": "bytes32" + } + ], + "Struct6": [ + { + "name": "param2", + "type": "bytes23[2]" + }, + { + "name": "param4", + "type": "bytes" + }, + { + "name": "param5", + "type": "string" + } + ] + }, + "data": { + "param2": [ + "0xe5efc795069c358388b3f891b191fae431455542adeab9", + "0x270b5f56338f2e2f812a703007e9b0efd6b563e140f758" + ], + "param4": "0x6339392fbd707f0e31c2d9069e3903c19b7d37766ae856d49d18d3d9012d313ac1803f91d08af3fd5d7534f11c6f29476ef6c7302d9f5cdb7e408ab9", + "param5": "Moo é🚀🚀M🚀éo🚀 oé oéé " + }, + "encoded": "0xcbe088c466df5bc47ff7eb50eee07ae89b3ad92aa609d8de035f94d7e942142df1274645ab982ea41af65c3e80ee1ef7d1c848bcd352be42d34068c3d48695726d07a447e03c51b86b9c89ffe0fe4fb894f1082820e4a6ca78b0a32fa2d3ac3cffd3f56909e6332406c11c8e04dd583a0302d9e70ba15e08c78ebc314a6f3542", + "digest": "0xa6298b22e1312894d0867b42c22acac11b272a64e8826554eb644f019ced7080" + }, + { + "name": "random-115", + "domain": { + "name": "Moo é🚀éM éoo🚀🚀🚀MMMM🚀", + "chainId": 519, + "verifyingContract": "0xb6a9e28479e2eefe2b652cd1035e244aa631ae8b", + "salt": "0x37afd12343de89e1a0d00825845be1fff4e39ee71c0047c70e39802a5c647e1c" + }, + "primaryType": "Struct8", + "types": { + "EIP712Domain": [ + { + "name": "name", + "type": "string" + }, + { + "name": "chainId", + "type": "uint256" + }, + { + "name": "verifyingContract", + "type": "address" + }, + { + "name": "salt", + "type": "bytes32" + } + ], + "Struct5": [ + { + "name": "param4", + "type": "string" + } + ], + "Struct8": [ + { + "name": "param2", + "type": "bytes" + }, + { + "name": "param3", + "type": "Struct5" + }, + { + "name": "param6", + "type": "int64" + }, + { + "name": "param7", + "type": "bytes" + } + ] + }, + "data": { + "param2": "0x1a23dd5b36e844d8ff6f162b51fe79f412ab64ecfc0a08f0701ab744dc0a", + "param3": { + "param4": "Moo é🚀o🚀é🚀🚀 🚀 oéMo" + }, + "param6": "-7428993099217415080", + "param7": "0xebcf5f2e21ef4078d79de76bc2136de32d78b8da45785d9320293cf88f56fdbb7ed2d99971bdc91313" + }, + "encoded": "0xf9efb6691f7dd1e64ca5c079b4a65046ce7e9b124f0b5a82fca4df1cef20cbf7fe19a4668dfcc60d76d18f9fdb5404410f46b99cd9c46102434bcfe4b002810aa039e88b3516312fbf1a2f8ba092aadc17e7aba94dec383eac1c3f9d30cd41cfffffffffffffffffffffffffffffffffffffffffffffffff98e6ea23025154585e3ed849708604724bc345250b999c8afbbdc5d2d7d899f6b7f0306fd22804c5", + "digest": "0x4c0c04f6ce70c4bbb681ef0ad6b6398acb8b7c6ed135e03749d01ac7581e6aaf" + }, + { + "name": "random-116", + "domain": { + "name": "Moo é🚀🚀🚀🚀M 🚀o🚀Mo🚀MMM o o ooo M🚀", + "version": "36.49.20", + "salt": "0x153291d7e60f50493d9bef0386f2e52efa990e8ef60f067395b548a323336f52" + }, + "primaryType": "Struct17", + "types": { + "EIP712Domain": [ + { + "name": "name", + "type": "string" + }, + { + "name": "version", + "type": "string" + }, + { + "name": "salt", + "type": "bytes32" + } + ], + "Struct11": [ + { + "name": "param9", + "type": "bool" + }, + { + "name": "param10", + "type": "int104" + } + ], + "Struct12": [ + { + "name": "param8", + "type": "Struct11" + } + ], + "Struct14": [ + { + "name": "param6", + "type": "bytes12" + }, + { + "name": "param7", + "type": "Struct12" + }, + { + "name": "param13", + "type": "bytes27" + } + ], + "Struct15": [ + { + "name": "param3", + "type": "bytes" + }, + { + "name": "param4", + "type": "bytes21" + }, + { + "name": "param5", + "type": "Struct14" + } + ], + "Struct17": [ + { + "name": "param2", + "type": "Struct15" + }, + { + "name": "param16", + "type": "bytes" + } + ] + }, + "data": { + "param16": "0xce845349870b816ffa1153a0d0d74a4fd43a536acba89d7800946365a8185361b43757a6c74d0bc23c99281b20fa1f21efc8f9e70260", + "param2": { + "param3": "0x9be214b7b1e40a654367ce51d46d42eefa6aa7583582ad890407c9a7db193b4a23423edabd2ca74f7bff562e7666b14ff056f859623d", + "param4": "0x70b9c3d3590df445cd2a809ff3fd39d14633ed606d", + "param5": { + "param13": "0x945110dc2331d4d36e6625c582a213f159fd3785e81a1ac51adfa4", + "param6": "0xb74aa08d1da2d00b53573848", + "param7": { + "param8": { + "param10": "7382596839099108206672673364998", + "param9": false + } + } + } + } + }, + "encoded": "0x2404a0d0da38cc2a26c0cae1fd80a615dcead8277266a934eb557d4a2a7b7616058530b4284971e4882f70ed579c18add8c7bf057fab40370e1641f4a2c1e84f26bed6a2942cc3da8e01ab975174dc95d1634c469b28a05029e7a338f26447a5", + "digest": "0x2859aeba18c02d3b96c7cac2c37cb1fc02efeb93bc0162e93cf849f24fc48ec0" + }, + { + "name": "random-117", + "domain": { + "chainId": 404, + "salt": "0xca3833452e02a6b4fa1ac8abc4b15c96f2d7646e05ac0dc999df4c02381fdb4a" + }, + "primaryType": "Struct3", + "types": { + "EIP712Domain": [ + { + "name": "chainId", + "type": "uint256" + }, + { + "name": "salt", + "type": "bytes32" + } + ], + "Struct3": [ + { + "name": "param2", + "type": "address" + } + ] + }, + "data": { + "param2": "0xe298d05b8a6eee625460e7ffde7a4080b66181e2" + }, + "encoded": "0x98edda54668461e736d24a4033f2d11540bceac48ea14c7da0fea73b8b9a55db000000000000000000000000e298d05b8a6eee625460e7ffde7a4080b66181e2", + "digest": "0x0e9b9a6735b599e2aa2bd15b5a6b6e35ba3f28a6a7727883d8f7b63a9bb6dfc6" + }, + { + "name": "random-118", + "domain": { + "version": "29.19.11", + "chainId": 1069 + }, + "primaryType": "Struct4", + "types": { + "EIP712Domain": [ + { + "name": "version", + "type": "string" + }, + { + "name": "chainId", + "type": "uint256" + } + ], + "Struct4": [ + { + "name": "param2", + "type": "bytes19" + }, + { + "name": "param3", + "type": "uint112" + } + ] + }, + "data": { + "param2": "0xa187410677f35e34d7d2d4a67323f45587a17a", + "param3": "2374623193016793084186176676934718" + }, + "encoded": "0x6d5a7844c94095e64fa9ad1a945731964eeecfab4dafa5aa7d52244c21f7fabba187410677f35e34d7d2d4a67323f45587a17a000000000000000000000000000000000000000000000000000000000000007513f55b61db9e82c36d9b97b43e", + "digest": "0x40254d9a8b1827c010a5df5a2e776142c72a8cb6ac9dca1e4e56853db63c637b" + }, + { + "name": "random-119", + "domain": { + "name": "Moo é🚀MéoM🚀Moé🚀éoM é ooMéMé🚀oéMMoM🚀éMo o 🚀 éé oéé🚀o M🚀o ", + "version": "23.47.23", + "chainId": 392 + }, + "primaryType": "Struct14", + "types": { + "EIP712Domain": [ + { + "name": "name", + "type": "string" + }, + { + "name": "version", + "type": "string" + }, + { + "name": "chainId", + "type": "uint256" + } + ], + "Struct10": [ + { + "name": "param7", + "type": "bytes14[]" + }, + { + "name": "param9", + "type": "string" + } + ], + "Struct12": [ + { + "name": "param4", + "type": "bytes" + }, + { + "name": "param5", + "type": "int200" + }, + { + "name": "param6", + "type": "Struct10" + }, + { + "name": "param11", + "type": "string" + } + ], + "Struct13": [ + { + "name": "param3", + "type": "Struct12" + } + ], + "Struct14": [ + { + "name": "param2", + "type": "Struct13" + } + ] + }, + "data": { + "param2": { + "param3": { + "param11": "Moo é🚀é ", + "param4": "0x087123d5b3662f", + "param5": "731211289069049313617748368694424686603157878585440644275800", + "param6": { + "param7": [ + "0xfec28eba8cdd8454e8a263553157" + ], + "param9": "Moo é🚀éo🚀MM M 🚀🚀o MM🚀o🚀éo éé🚀 oé🚀o Mooéo oooMo " + } + } + } + }, + "encoded": "0x66a8655159b0710afca0f457c3bde2322122bdb9a1fea4237e6c302a129fa9c76a011fb94ca2e8ba68ca2390abc1167bab6b644ce5915ec5f1165b3ce7572375", + "digest": "0xbb821d9f1b55a93033ab1e22aff08ea2cdbae58acb7799b89ac7f9583020c16c" + }, + { + "name": "random-120", + "domain": { + "name": "Moo é🚀é🚀é ooM oMo Mo🚀🚀ooé M🚀🚀MooMo Mé 🚀oé oo o🚀é", + "version": "2.8.22", + "verifyingContract": "0xd92defa9762b6708d9306f94e639013de8bd2b4e", + "salt": "0xf2b1503c65653a66bb23b43d0b646d9920e2968aea19375f3f744ab0e2d5d3df" + }, + "primaryType": "Struct6", + "types": { + "EIP712Domain": [ + { + "name": "name", + "type": "string" + }, + { + "name": "version", + "type": "string" + }, + { + "name": "verifyingContract", + "type": "address" + }, + { + "name": "salt", + "type": "bytes32" + } + ], + "Struct6": [ + { + "name": "param2", + "type": "string[2][1]" + }, + { + "name": "param5", + "type": "bool" + } + ] + }, + "data": { + "param2": [ + [ + "Moo é🚀ééoo🚀🚀 oo🚀éo oé🚀oMM🚀o Mooééé", + "Moo é🚀🚀ooééoM🚀" + ] + ], + "param5": false + }, + "encoded": "0x73e644b97783bf4c80cd97dff29c383f1205915fd9399b0a95ccc9d335d190d6e3d0ef52ef1875273cf8cadc1df7b0c681dc2c124e44110285e59de2514e06680000000000000000000000000000000000000000000000000000000000000000", + "digest": "0x9d331f60c2e243c0c380f493df01d216381cbdfd1e8726bf2d95623b966e12d4" + }, + { + "name": "random-122", + "domain": { + "name": "Moo é🚀🚀Mo éééoééé oéMMM oM 🚀o 🚀MoMooéoMéé oo Méé éM", + "version": "38.16.6", + "chainId": 1155 + }, + "primaryType": "Struct9", + "types": { + "EIP712Domain": [ + { + "name": "name", + "type": "string" + }, + { + "name": "version", + "type": "string" + }, + { + "name": "chainId", + "type": "uint256" + } + ], + "Struct8": [ + { + "name": "param4", + "type": "address" + }, + { + "name": "param5", + "type": "string" + }, + { + "name": "param6", + "type": "address" + }, + { + "name": "param7", + "type": "bytes18" + } + ], + "Struct9": [ + { + "name": "param2", + "type": "address" + }, + { + "name": "param3", + "type": "Struct8" + } + ] + }, + "data": { + "param2": "0x1a97d3dd8afe04195e4cf65f6642bb9abf0e30d0", + "param3": { + "param4": "0x5438b331d76c3e973291921307c08d5137bccb2f", + "param5": "Moo é🚀oooo 🚀oo🚀 oo éo🚀oééé🚀o oo🚀é🚀o Mo🚀éo🚀éoMéMMM🚀 MM 🚀é MM", + "param6": "0xa2c5910463111798e60a5dcea5d09fa3928dd865", + "param7": "0x38aeb5bff041e7eb1186d63e6dc85a5972fa" + } + }, + "encoded": "0x4dc6c24fe5adc7b222933880278854e343fff0c6e3008ff0ae0acb2676515b800000000000000000000000001a97d3dd8afe04195e4cf65f6642bb9abf0e30d0031afafd11b84538ee39d9de32f0581663dde8f1b5b8413f1693cadaf1f5f383", + "digest": "0xcf807730a8b53a7c355704e7acd17208573fe6f07eba715f9c3eff5552130cd7" + }, + { + "name": "random-123", + "domain": { + "name": "Moo é🚀🚀🚀oé o🚀 MM🚀 éoé🚀 🚀ooéé 🚀éo🚀 é oM🚀Moéooo MéoéoMo", + "verifyingContract": "0x6fb9e64197fbfffeb79e710b1786fad0adcded2a", + "salt": "0x50e5121d17089bc449b8a20c541511a7fdd92d31a6e6f028fccd96044f12e2eb" + }, + "primaryType": "Struct4", + "types": { + "EIP712Domain": [ + { + "name": "name", + "type": "string" + }, + { + "name": "verifyingContract", + "type": "address" + }, + { + "name": "salt", + "type": "bytes32" + } + ], + "Struct4": [ + { + "name": "param2", + "type": "bool" + }, + { + "name": "param3", + "type": "address" + } + ] + }, + "data": { + "param2": true, + "param3": "0xd1f004a769a4d83c51ef78ae86c9f45497820157" + }, + "encoded": "0x210ade7f30d7f00dbd3bcfd9b6e33d421cb4677f314bb1d18b47c7532f4a33da0000000000000000000000000000000000000000000000000000000000000001000000000000000000000000d1f004a769a4d83c51ef78ae86c9f45497820157", + "digest": "0x3607930da601b81fc487c79b4c2a6a5fd546a2ff2f80e5e45693813adf27a56d" + }, + { + "name": "random-124", + "domain": { + "name": "Moo é🚀 🚀ooo éoéooMM oo🚀Mé🚀o🚀🚀ooo MoooM 🚀ééo🚀é éMéé o", + "chainId": 737, + "verifyingContract": "0xa67338c83e7cad895cd912fd10b293c30b93507c", + "salt": "0x29fec8e11edc8a14fed2ebc136bb9718842edaa9fa0661f05b23adba9bbd360c" + }, + "primaryType": "Struct11", + "types": { + "EIP712Domain": [ + { + "name": "name", + "type": "string" + }, + { + "name": "chainId", + "type": "uint256" + }, + { + "name": "verifyingContract", + "type": "address" + }, + { + "name": "salt", + "type": "bytes32" + } + ], + "Struct10": [ + { + "name": "param8", + "type": "address" + }, + { + "name": "param9", + "type": "string" + } + ], + "Struct11": [ + { + "name": "param2", + "type": "address" + }, + { + "name": "param3", + "type": "Struct6" + }, + { + "name": "param7", + "type": "Struct10" + } + ], + "Struct6": [ + { + "name": "param4", + "type": "address" + }, + { + "name": "param5", + "type": "address" + } + ] + }, + "data": { + "param2": "0x289a7fc0abbf5814916b435aedd05110f990b8a8", + "param3": { + "param4": "0xc91643ad99f82f44075c7b2c3176d9fd070b7854", + "param5": "0xa45759a377d7c8f1e6684c1ff727b0b33e812797" + }, + "param7": { + "param8": "0xfb13444aafca282020cc4a4c5fa66eeee5c9a215", + "param9": "Moo é🚀éoo🚀 éM🚀oo o é🚀MoMooM MoM o🚀éo🚀 Méo🚀éoo🚀 é oMé o" + } + }, + "encoded": "0xe41195b80dea8f516fb631cf6457d52b859a33fee20b1678715321d816e04f34000000000000000000000000289a7fc0abbf5814916b435aedd05110f990b8a8c975b7d342e5f9bd4cece3275d900bc4463445fe2c102fc24cdd8bdd9c741af46d6c322f4335555294c4862917e57478c76ba1b8b90655a5134f7532df97e50d", + "digest": "0x8d1983077b551e2dc3ff4d7c564c4eaf4d8ac90e5d66ff1f778bc642dc729582" + }, + { + "name": "random-125", + "domain": { + "name": "Moo é🚀MMooMo Moé", + "version": "49.24.49", + "verifyingContract": "0xcf398d52a18e0a6f91a16c23bae18afd490e004a" + }, + "primaryType": "Struct4", + "types": { + "EIP712Domain": [ + { + "name": "name", + "type": "string" + }, + { + "name": "version", + "type": "string" + }, + { + "name": "verifyingContract", + "type": "address" + } + ], + "Struct4": [ + { + "name": "param2", + "type": "bool" + }, + { + "name": "param3", + "type": "bytes" + } + ] + }, + "data": { + "param2": false, + "param3": "0x40caa66524b856" + }, + "encoded": "0x08b05ab3afcc030e7935ce6c66d5d4b79136f50d1f567b347bde3906d91197e80000000000000000000000000000000000000000000000000000000000000000f234b4abf550f95ad2b3e18a8327dfad72bafbc9c735d4d70db98855f86e4d3e", + "digest": "0x8869dd15921fcbfb0942af0803bc4a69e79254d54aecffd8d092de03241b7b29" + }, + { + "name": "random-126", + "domain": { + "chainId": 1089 + }, + "primaryType": "Struct9", + "types": { + "EIP712Domain": [ + { + "name": "chainId", + "type": "uint256" + } + ], + "Struct9": [ + { + "name": "param2", + "type": "bytes" + }, + { + "name": "param3", + "type": "uint152[1][2]" + }, + { + "name": "param6", + "type": "string" + }, + { + "name": "param7", + "type": "address[1]" + } + ] + }, + "data": { + "param2": "0x3d6cac9f032d82af63c2d077790354598cd194d920dadcf9996b", + "param3": [ + [ + "2215482091649323250149612409952168968001124083" + ], + [ + "3720428939728345178604370629051737310101916132" + ] + ], + "param6": "Moo é🚀o ééo🚀🚀 M🚀éo🚀🚀o🚀éé oéo🚀o🚀🚀oo éoMé🚀o 🚀éoéMM", + "param7": [ + "0xc8ce8e423057eb465576542851c8ae163f44bef2" + ] + }, + "encoded": "0xae9a402c7fe6b3d9bde74954593223cd4c7c61f3764929e780ca44432ba4edf88ebaa4befe0af4b10f9ff1d424dc9f2e501c9349db90511b8fc2ae5be855444f4c981d388229b9d3b092a114e1ab19340cd5ea397c66da5afc0bf366562778ba45cee17bca9a453477906b4710d48e03be64fbe1527a2a76f4c454afdec7e47587b1d2abb5f508f09df8e2d9dafbaa5dd87e71487b42b75e4b5a7d89feebfbc2", + "digest": "0xdbf3e30a06348c3a82df230158a8d3fe7be0948ba7e7e482eacdc44cae526ff9" + }, + { + "name": "random-127", + "domain": { + "name": "Moo é🚀", + "version": "42.13.26" + }, + "primaryType": "Struct13", + "types": { + "EIP712Domain": [ + { + "name": "name", + "type": "string" + }, + { + "name": "version", + "type": "string" + } + ], + "Struct13": [ + { + "name": "param2", + "type": "bytes" + }, + { + "name": "param3", + "type": "Struct8" + }, + { + "name": "param9", + "type": "address" + }, + { + "name": "param10", + "type": "bytes[2][]" + } + ], + "Struct8": [ + { + "name": "param4", + "type": "uint40" + }, + { + "name": "param5", + "type": "bytes" + }, + { + "name": "param6", + "type": "string[]" + } + ] + }, + "data": { + "param10": [ + [ + "0xe0a9f45f1a48a64f182328", + "0xd18e3c88f7b41cdb54f9a02dec1f9f2138b6c4f3718241bec86692e6912e4f3bdae4e15c968065966fc4c6" + ] + ], + "param2": "0xeed140f7e6f2069c90466326aca3d6c3af16", + "param3": { + "param4": "934183269602", + "param5": "0xa941fdd7a8fe8bfed7614a3a7ce12d949025684e32ab78ab621caa", + "param6": [] + }, + "param9": "0x219b81e0367f33c3face28f7ca0f98f42a3aad3f" + }, + "encoded": "0xec4d8bc63ac053b4abb7da4dcf5f9d853222e50f2cf3a7a70102a4ccf963484724c38815168dfc92aea0b45d4da4e474a0cccad6525ff2fa8584df0f4add65567a1e9576f0b9eccfba97fa5a64cec39eafe610e076674eeb749db12c47055f87000000000000000000000000219b81e0367f33c3face28f7ca0f98f42a3aad3f771f547647235bc1319baeaa2a6b4c3a394d0fee465d0d44fe7f3e6cc9cbf7d0", + "digest": "0x800e34308adad5c2fb5a176196e58c026d3e7e8a0485bc3f83f5f552bcf7e8aa" + }, + { + "name": "EIP712 example", + "domain": { + "name": "Ether Mail", + "version": "1", + "chainId": 1, + "verifyingContract": "0xCcCCccccCCCCcCCCCCCcCcCccCcCCCcCcccccccC" + }, + "primaryType": "Mail", + "types": { + "EIP712Domain": [ + { + "name": "name", + "type": "string" + }, + { + "name": "version", + "type": "string" + }, + { + "name": "chainId", + "type": "uint256" + }, + { + "name": "verifyingContract", + "type": "address" + } + ], + "Mail": [ + { + "name": "from", + "type": "Person" + }, + { + "name": "to", + "type": "Person" + }, + { + "name": "contents", + "type": "string" + } + ], + "Person": [ + { + "name": "name", + "type": "string" + }, + { + "name": "wallet", + "type": "address" + } + ] + }, + "data": { + "contents": "Hello, Bob!", + "from": { + "name": "Cow", + "wallet": "0xCD2a3d9F938E13CD947Ec05AbC7FE734Df8DD826" + }, + "to": { + "name": "Bob", + "wallet": "0xbBbBBBBbbBBBbbbBbbBbbbbBBbBbbbbBbBbbBBbB" + } + }, + "encoded": "0xa0cedeb2dc280ba39b857546d74f5549c3a1d7bdc2dd96bf881f76108e23dac2fc71e5fa27ff56c350aa531bc129ebdf613b772b6604664f5d8dbe21b85eb0c8cd54f074a4af31b4411ff6a60c9719dbd559c221c8ac3492d9d872b041d703d1b5aadf3154a261abdd9086fc627b61efca26ae5702701d05cd2305f7c52a2fc8", + "digest": "0xbe609aee343fb3c4b28e1df9e632fca64fcfaede20f02e86244efddf30957bd2" + } +] \ No newline at end of file diff --git a/signer/core/apitypes/types.go b/signer/core/apitypes/types.go index 72cad88ec2..b56931c1d1 100644 --- a/signer/core/apitypes/types.go +++ b/signer/core/apitypes/types.go @@ -325,18 +325,17 @@ type Type struct { Type string `json:"type"` } +// isArray returns true if the type is a fixed or variable sized array. +// This method may return false positives, in case the Type is not a valid +// expression, e.g. "fooo[[[[". func (t *Type) isArray() bool { - return strings.HasSuffix(t.Type, "[]") + return strings.IndexByte(t.Type, '[') > 0 } -// typeName returns the canonical name of the type. If the type is 'Person[]', then +// typeName returns the canonical name of the type. If the type is 'Person[]' or 'Person[2]', then // this method returns 'Person' func (t *Type) typeName() string { - if strings.Contains(t.Type, "[") { - re := regexp.MustCompile(`\[\d*\]`) - return re.ReplaceAllString(t.Type, "") - } - return t.Type + return strings.Split(t.Type, "[")[0] } type Types map[string][]Type @@ -387,7 +386,7 @@ func (typedData *TypedData) HashStruct(primaryType string, data TypedDataMessage // Dependencies returns an array of custom types ordered by their hierarchical reference tree func (typedData *TypedData) Dependencies(primaryType string, found []string) []string { - primaryType = strings.TrimSuffix(primaryType, "[]") + primaryType = strings.Split(primaryType, "[")[0] if slices.Contains(found, primaryType) { return found @@ -465,34 +464,11 @@ func (typedData *TypedData) EncodeData(primaryType string, data map[string]inter encType := field.Type encValue := data[field.Name] if encType[len(encType)-1:] == "]" { - arrayValue, err := convertDataToSlice(encValue) + encodedData, err := typedData.encodeArrayValue(encValue, encType, depth) if err != nil { - return nil, dataMismatchError(encType, encValue) + return nil, err } - - arrayBuffer := bytes.Buffer{} - parsedType := strings.Split(encType, "[")[0] - for _, item := range arrayValue { - if typedData.Types[parsedType] != nil { - mapValue, ok := item.(map[string]interface{}) - if !ok { - return nil, dataMismatchError(parsedType, item) - } - encodedData, err := typedData.EncodeData(parsedType, mapValue, depth+1) - if err != nil { - return nil, err - } - arrayBuffer.Write(crypto.Keccak256(encodedData)) - } else { - bytesValue, err := typedData.EncodePrimitiveValue(parsedType, item, depth) - if err != nil { - return nil, err - } - arrayBuffer.Write(bytesValue) - } - } - - buffer.Write(crypto.Keccak256(arrayBuffer.Bytes())) + buffer.Write(encodedData) } else if typedData.Types[field.Type] != nil { mapValue, ok := encValue.(map[string]interface{}) if !ok { @@ -514,6 +490,46 @@ func (typedData *TypedData) EncodeData(primaryType string, data map[string]inter return buffer.Bytes(), nil } +func (typedData *TypedData) encodeArrayValue(encValue interface{}, encType string, depth int) (hexutil.Bytes, error) { + arrayValue, err := convertDataToSlice(encValue) + if err != nil { + return nil, dataMismatchError(encType, encValue) + } + + arrayBuffer := new(bytes.Buffer) + parsedType := strings.Split(encType, "[")[0] + for _, item := range arrayValue { + if reflect.TypeOf(item).Kind() == reflect.Slice || + reflect.TypeOf(item).Kind() == reflect.Array { + encodedData, err := typedData.encodeArrayValue(item, parsedType, depth+1) + if err != nil { + return nil, err + } + arrayBuffer.Write(encodedData) + } else { + if typedData.Types[parsedType] != nil { + mapValue, ok := item.(map[string]interface{}) + if !ok { + return nil, dataMismatchError(parsedType, item) + } + encodedData, err := typedData.EncodeData(parsedType, mapValue, depth+1) + if err != nil { + return nil, err + } + digest := crypto.Keccak256(encodedData) + arrayBuffer.Write(digest) + } else { + bytesValue, err := typedData.EncodePrimitiveValue(parsedType, item, depth) + if err != nil { + return nil, err + } + arrayBuffer.Write(bytesValue) + } + } + } + return crypto.Keccak256(arrayBuffer.Bytes()), nil +} + // Attempt to parse bytes in different formats: byte array, hex string, hexutil.Bytes. func parseBytes(encType interface{}) ([]byte, bool) { // Handle array types. @@ -660,7 +676,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) } @@ -871,7 +887,8 @@ func init() { // Checks if the primitive value is valid func isPrimitiveTypeValid(primitiveType string) bool { - _, ok := validPrimitiveTypes[primitiveType] + input := strings.Split(primitiveType, "[")[0] + _, ok := validPrimitiveTypes[input] return ok } diff --git a/signer/core/apitypes/types_test.go b/signer/core/apitypes/types_test.go index 7ea32f298c..22bbeba19e 100644 --- a/signer/core/apitypes/types_test.go +++ b/signer/core/apitypes/types_test.go @@ -31,8 +31,9 @@ func TestIsPrimitive(t *testing.T) { t.Parallel() // Expected positives for i, tc := range []string{ - "int24", "int24[]", "uint88", "uint88[]", "uint", "uint[]", "int256", "int256[]", - "uint96", "uint96[]", "int96", "int96[]", "bytes17[]", "bytes17", + "int24", "int24[]", "int[]", "int[2]", "uint88", "uint88[]", "uint", "uint[]", "uint[2]", "int256", "int256[]", + "uint96", "uint96[]", "int96", "int96[]", "bytes17[]", "bytes17", "address[2]", "bool[4]", "string[5]", "bytes[2]", + "bytes32", "bytes32[]", "bytes32[4]", } { if !isPrimitiveTypeValid(tc) { t.Errorf("test %d: expected '%v' to be a valid primitive", i, tc) @@ -141,3 +142,94 @@ func TestBlobTxs(t *testing.T) { } t.Logf("tx %v", string(data)) } + +func TestType_IsArray(t *testing.T) { + t.Parallel() + // Expected positives + for i, tc := range []Type{ + { + Name: "type1", + Type: "int24[]", + }, + { + Name: "type2", + Type: "int24[2]", + }, + { + Name: "type3", + Type: "int24[2][2][2]", + }, + } { + if !tc.isArray() { + t.Errorf("test %d: expected '%v' to be an array", i, tc) + } + } + // Expected negatives + for i, tc := range []Type{ + { + Name: "type1", + Type: "int24", + }, + { + Name: "type2", + Type: "uint88", + }, + { + Name: "type3", + Type: "bytes32", + }, + } { + if tc.isArray() { + t.Errorf("test %d: expected '%v' to not be an array", i, tc) + } + } +} + +func TestType_TypeName(t *testing.T) { + t.Parallel() + + for i, tc := range []struct { + Input Type + Expected string + }{ + { + Input: Type{ + Name: "type1", + Type: "int24[]", + }, + Expected: "int24", + }, + { + Input: Type{ + Name: "type2", + Type: "int26[2][2][2]", + }, + Expected: "int26", + }, + { + Input: Type{ + Name: "type3", + Type: "int24", + }, + Expected: "int24", + }, + { + Input: Type{ + Name: "type4", + Type: "uint88", + }, + Expected: "uint88", + }, + { + Input: Type{ + Name: "type5", + Type: "bytes32[2]", + }, + Expected: "bytes32", + }, + } { + if tc.Input.typeName() != tc.Expected { + t.Errorf("test %d: expected typeName value of '%v' but got '%v'", i, tc.Expected, tc.Input) + } + } +} diff --git a/signer/core/signed_data.go b/signer/core/signed_data.go index f8b3c9d86d..c62b513145 100644 --- a/signer/core/signed_data.go +++ b/signer/core/signed_data.go @@ -294,7 +294,7 @@ func typedDataRequest(data any) (*SignDataRequest, error) { func (api *SignerAPI) EcRecover(ctx context.Context, data hexutil.Bytes, sig hexutil.Bytes) (common.Address, error) { // Returns the address for the Account that was used to create the signature. // - // Note, this function is compatible with eth_sign and personal_sign. As such it recovers + // Note, this function is compatible with eth_sign. As such it recovers // the address of: // hash = keccak256("\x19Ethereum Signed Message:\n${message length}${message}") // addr = ecrecover(hash, signature) diff --git a/swarm/README.md b/swarm/README.md deleted file mode 100644 index 41b6aa9100..0000000000 --- a/swarm/README.md +++ /dev/null @@ -1,7 +0,0 @@ -# Swarm - -https://www.ethswarm.org/ - -Swarm is a distributed storage platform and content distribution service, a native base layer service of the ethereum web3 stack. The primary objective of Swarm is to provide a decentralized and redundant store for dapp code and data as well as block chain and state data. Swarm is also set out to provide various base layer services for web3, including node-to-node messaging, media streaming, decentralised database services and scalable state-channel infrastructure for decentralised service economies. - -**Note**: The codebase has been moved to [ethersphere/bee](https://github.com/ethersphere/bee) diff --git a/tests/block_test_util.go b/tests/block_test_util.go index b0a31a6972..77bf945e40 100644 --- a/tests/block_test_util.go +++ b/tests/block_test_util.go @@ -22,6 +22,7 @@ import ( "encoding/hex" "encoding/json" "fmt" + stdmath "math" "math/big" "os" "reflect" @@ -129,6 +130,11 @@ func (t *BlockTest) Run(snapshotter bool, scheme string, witness bool, tracer *t } // Commit genesis state gspec := t.genesis(config) + + // if ttd is not specified, set an arbitrary huge value + if gspec.Config.TerminalTotalDifficulty == nil { + gspec.Config.TerminalTotalDifficulty = big.NewInt(stdmath.MaxInt64) + } triedb := triedb.NewDatabase(db, tconf) gblock, err := gspec.Commit(db, triedb) if err != nil { diff --git a/tests/difficulty_test.go b/tests/difficulty_test.go index 03e14df7c4..74ac28363b 100644 --- a/tests/difficulty_test.go +++ b/tests/difficulty_test.go @@ -36,22 +36,21 @@ var ( } ropstenChainConfig = params.ChainConfig{ - ChainID: big.NewInt(3), - HomesteadBlock: big.NewInt(0), - DAOForkBlock: nil, - DAOForkSupport: true, - EIP150Block: big.NewInt(0), - EIP155Block: big.NewInt(10), - EIP158Block: big.NewInt(10), - ByzantiumBlock: big.NewInt(1_700_000), - ConstantinopleBlock: big.NewInt(4_230_000), - PetersburgBlock: big.NewInt(4_939_394), - IstanbulBlock: big.NewInt(6_485_846), - MuirGlacierBlock: big.NewInt(7_117_117), - BerlinBlock: big.NewInt(9_812_189), - LondonBlock: big.NewInt(10_499_401), - TerminalTotalDifficulty: new(big.Int).SetUint64(50_000_000_000_000_000), - TerminalTotalDifficultyPassed: true, + ChainID: big.NewInt(3), + HomesteadBlock: big.NewInt(0), + DAOForkBlock: nil, + DAOForkSupport: true, + EIP150Block: big.NewInt(0), + EIP155Block: big.NewInt(10), + EIP158Block: big.NewInt(10), + ByzantiumBlock: big.NewInt(1_700_000), + ConstantinopleBlock: big.NewInt(4_230_000), + PetersburgBlock: big.NewInt(4_939_394), + IstanbulBlock: big.NewInt(6_485_846), + MuirGlacierBlock: big.NewInt(7_117_117), + BerlinBlock: big.NewInt(9_812_189), + LondonBlock: big.NewInt(10_499_401), + TerminalTotalDifficulty: new(big.Int).SetUint64(50_000_000_000_000_000), } ) diff --git a/tests/fuzzers/bls12381/bls12381_fuzz.go b/tests/fuzzers/bls12381/bls12381_fuzz.go index 4efc749b6f..a3e0e9f72b 100644 --- a/tests/fuzzers/bls12381/bls12381_fuzz.go +++ b/tests/fuzzers/bls12381/bls12381_fuzz.go @@ -31,42 +31,33 @@ import ( "github.com/consensys/gnark-crypto/ecc/bls12-381/fp" "github.com/consensys/gnark-crypto/ecc/bls12-381/fr" "github.com/ethereum/go-ethereum/common" - bls12381 "github.com/kilic/bls12-381" blst "github.com/supranational/blst/bindings/go" ) func fuzzG1SubgroupChecks(data []byte) int { input := bytes.NewReader(data) - kpG1, cpG1, blG1, err := getG1Points(input) + cpG1, blG1, err := getG1Points(input) if err != nil { return 0 } - inSubGroupKilic := bls12381.NewG1().InCorrectSubgroup(kpG1) inSubGroupGnark := cpG1.IsInSubGroup() inSubGroupBLST := blG1.InG1() - if inSubGroupKilic != inSubGroupGnark { - panic(fmt.Sprintf("differing subgroup check, kilic %v, gnark %v", inSubGroupKilic, inSubGroupGnark)) - } - if inSubGroupKilic != inSubGroupBLST { - panic(fmt.Sprintf("differing subgroup check, kilic %v, blst %v", inSubGroupKilic, inSubGroupBLST)) + if inSubGroupGnark != inSubGroupBLST { + panic(fmt.Sprintf("differing subgroup check, gnark %v, blst %v", inSubGroupGnark, inSubGroupBLST)) } return 1 } func fuzzG2SubgroupChecks(data []byte) int { input := bytes.NewReader(data) - kpG2, cpG2, blG2, err := getG2Points(input) + gpG2, blG2, err := getG2Points(input) if err != nil { return 0 } - inSubGroupKilic := bls12381.NewG2().InCorrectSubgroup(kpG2) - inSubGroupGnark := cpG2.IsInSubGroup() + inSubGroupGnark := gpG2.IsInSubGroup() inSubGroupBLST := blG2.InG2() - if inSubGroupKilic != inSubGroupGnark { - panic(fmt.Sprintf("differing subgroup check, kilic %v, gnark %v", inSubGroupKilic, inSubGroupGnark)) - } - if inSubGroupKilic != inSubGroupBLST { - panic(fmt.Sprintf("differing subgroup check, kilic %v, blst %v", inSubGroupKilic, inSubGroupBLST)) + if inSubGroupGnark != inSubGroupBLST { + panic(fmt.Sprintf("differing subgroup check, gnark %v, blst %v", inSubGroupGnark, inSubGroupBLST)) } return 1 } @@ -75,38 +66,28 @@ func fuzzCrossPairing(data []byte) int { input := bytes.NewReader(data) // get random G1 points - kpG1, cpG1, blG1, err := getG1Points(input) + cpG1, blG1, err := getG1Points(input) if err != nil { return 0 } // get random G2 points - kpG2, cpG2, blG2, err := getG2Points(input) + cpG2, blG2, err := getG2Points(input) if err != nil { return 0 } - // compute pairing using geth - engine := bls12381.NewEngine() - engine.AddPair(kpG1, kpG2) - kResult := engine.Result() - // compute pairing using gnark cResult, err := gnark.Pair([]gnark.G1Affine{*cpG1}, []gnark.G2Affine{*cpG2}) if err != nil { panic(fmt.Sprintf("gnark/bls12381 encountered error: %v", err)) } - // compare result - if !(bytes.Equal(cResult.Marshal(), bls12381.NewGT().ToBytes(kResult))) { - panic("pairing mismatch gnark / geth ") - } - // compute pairing using blst blstResult := blst.Fp12MillerLoop(blG2, blG1) blstResult.FinalExp() res := massageBLST(blstResult.ToBendian()) - if !(bytes.Equal(res, bls12381.NewGT().ToBytes(kResult))) { + if !(bytes.Equal(res, cResult.Marshal())) { panic("pairing mismatch blst / geth") } @@ -141,32 +122,22 @@ func fuzzCrossG1Add(data []byte) int { input := bytes.NewReader(data) // get random G1 points - kp1, cp1, bl1, err := getG1Points(input) + cp1, bl1, err := getG1Points(input) if err != nil { return 0 } // get random G1 points - kp2, cp2, bl2, err := getG1Points(input) + cp2, bl2, err := getG1Points(input) if err != nil { return 0 } - // compute kp = kp1 + kp2 - g1 := bls12381.NewG1() - kp := bls12381.PointG1{} - g1.Add(&kp, kp1, kp2) - // compute cp = cp1 + cp2 _cp1 := new(gnark.G1Jac).FromAffine(cp1) _cp2 := new(gnark.G1Jac).FromAffine(cp2) cp := new(gnark.G1Affine).FromJacobian(_cp1.AddAssign(_cp2)) - // compare result - if !(bytes.Equal(cp.Marshal(), g1.ToBytes(&kp))) { - panic("G1 point addition mismatch gnark / geth ") - } - bl3 := blst.P1AffinesAdd([]*blst.P1Affine{bl1, bl2}) if !(bytes.Equal(cp.Marshal(), bl3.Serialize())) { panic("G1 point addition mismatch blst / geth ") @@ -179,34 +150,24 @@ func fuzzCrossG2Add(data []byte) int { input := bytes.NewReader(data) // get random G2 points - kp1, cp1, bl1, err := getG2Points(input) + gp1, bl1, err := getG2Points(input) if err != nil { return 0 } // get random G2 points - kp2, cp2, bl2, err := getG2Points(input) + gp2, bl2, err := getG2Points(input) if err != nil { return 0 } - // compute kp = kp1 + kp2 - g2 := bls12381.NewG2() - kp := bls12381.PointG2{} - g2.Add(&kp, kp1, kp2) - // compute cp = cp1 + cp2 - _cp1 := new(gnark.G2Jac).FromAffine(cp1) - _cp2 := new(gnark.G2Jac).FromAffine(cp2) - cp := new(gnark.G2Affine).FromJacobian(_cp1.AddAssign(_cp2)) - - // compare result - if !(bytes.Equal(cp.Marshal(), g2.ToBytes(&kp))) { - panic("G2 point addition mismatch gnark / geth ") - } + _gp1 := new(gnark.G2Jac).FromAffine(gp1) + _gp2 := new(gnark.G2Jac).FromAffine(gp2) + gp := new(gnark.G2Affine).FromJacobian(_gp1.AddAssign(_gp2)) bl3 := blst.P2AffinesAdd([]*blst.P2Affine{bl1, bl2}) - if !(bytes.Equal(cp.Marshal(), bl3.Serialize())) { + if !(bytes.Equal(gp.Marshal(), bl3.Serialize())) { panic("G1 point addition mismatch blst / geth ") } @@ -216,10 +177,10 @@ func fuzzCrossG2Add(data []byte) int { func fuzzCrossG1MultiExp(data []byte) int { var ( input = bytes.NewReader(data) - gethScalars []*bls12381.Fr gnarkScalars []fr.Element - gethPoints []*bls12381.PointG1 gnarkPoints []gnark.G1Affine + blstScalars []*blst.Scalar + blstPoints []*blst.P1Affine ) // n random scalars (max 17) for i := 0; i < 17; i++ { @@ -229,46 +190,147 @@ func fuzzCrossG1MultiExp(data []byte) int { break } // get a random G1 point as basis - kp1, cp1, _, err := getG1Points(input) + cp1, bl1, err := getG1Points(input) if err != nil { break } - gethScalars = append(gethScalars, bls12381.NewFr().FromBytes(s.Bytes())) - var gnarkScalar = &fr.Element{} - gnarkScalar = gnarkScalar.SetBigInt(s) - gnarkScalars = append(gnarkScalars, *gnarkScalar) - gethPoints = append(gethPoints, new(bls12381.PointG1).Set(kp1)) + gnarkScalar := new(fr.Element).SetBigInt(s) + gnarkScalars = append(gnarkScalars, *gnarkScalar) gnarkPoints = append(gnarkPoints, *cp1) + + blstScalar := new(blst.Scalar).FromBEndian(common.LeftPadBytes(s.Bytes(), 32)) + blstScalars = append(blstScalars, blstScalar) + blstPoints = append(blstPoints, bl1) } - if len(gethScalars) == 0 { + + if len(gnarkScalars) == 0 || len(gnarkScalars) != len(gnarkPoints) { return 0 } - // compute multi exponentiation - g1 := bls12381.NewG1() - kp := bls12381.PointG1{} - if _, err := g1.MultiExp(&kp, gethPoints, gethScalars); err != nil { - panic(fmt.Sprintf("G1 multi exponentiation errored (geth): %v", err)) - } - // note that geth/crypto/bls12381.MultiExp mutates the scalars slice (and sets all the scalars to zero) // gnark multi exp cp := new(gnark.G1Affine) cp.MultiExp(gnarkPoints, gnarkScalars, ecc.MultiExpConfig{}) - // compare result - if !(bytes.Equal(cp.Marshal(), g1.ToBytes(&kp))) { - panic("G1 multi exponentiation mismatch gnark / geth ") + expectedGnark := multiExpG1Gnark(gnarkPoints, gnarkScalars) + if !bytes.Equal(cp.Marshal(), expectedGnark.Marshal()) { + panic("g1 multi exponentiation mismatch") } + // blst multi exp + expectedBlst := blst.P1AffinesMult(blstPoints, blstScalars, 256).ToAffine() + if !bytes.Equal(cp.Marshal(), expectedBlst.Serialize()) { + panic("g1 multi exponentiation mismatch, gnark/blst") + } return 1 } -func getG1Points(input io.Reader) (*bls12381.PointG1, *gnark.G1Affine, *blst.P1Affine, error) { +func fuzzCrossG1Mul(data []byte) int { + input := bytes.NewReader(data) + gp, blpAffine, err := getG1Points(input) + if err != nil { + return 0 + } + scalar, err := randomScalar(input, fp.Modulus()) + if err != nil { + return 0 + } + + blScalar := new(blst.Scalar).FromBEndian(common.LeftPadBytes(scalar.Bytes(), 32)) + + blp := new(blst.P1) + blp.FromAffine(blpAffine) + + resBl := blp.Mult(blScalar) + resGeth := (new(gnark.G1Affine)).ScalarMultiplication(gp, scalar) + + if !bytes.Equal(resGeth.Marshal(), resBl.Serialize()) { + panic("bytes(blst.G1) != bytes(geth.G1)") + } + return 1 +} + +func fuzzCrossG2Mul(data []byte) int { + input := bytes.NewReader(data) + gp, blpAffine, err := getG2Points(input) + if err != nil { + return 0 + } + scalar, err := randomScalar(input, fp.Modulus()) + if err != nil { + return 0 + } + + blScalar := new(blst.Scalar).FromBEndian(common.LeftPadBytes(scalar.Bytes(), 32)) + + blp := new(blst.P2) + blp.FromAffine(blpAffine) + + resBl := blp.Mult(blScalar) + resGeth := (new(gnark.G2Affine)).ScalarMultiplication(gp, scalar) + + if !bytes.Equal(resGeth.Marshal(), resBl.Serialize()) { + panic("bytes(blst.G1) != bytes(geth.G1)") + } + return 1 +} + +func fuzzCrossG2MultiExp(data []byte) int { + var ( + input = bytes.NewReader(data) + gnarkScalars []fr.Element + gnarkPoints []gnark.G2Affine + blstScalars []*blst.Scalar + blstPoints []*blst.P2Affine + ) + // n random scalars (max 17) + for i := 0; i < 17; i++ { + // note that geth/crypto/bls12381 works only with scalars <= 32bytes + s, err := randomScalar(input, fr.Modulus()) + if err != nil { + break + } + // get a random G1 point as basis + cp1, bl1, err := getG2Points(input) + if err != nil { + break + } + + gnarkScalar := new(fr.Element).SetBigInt(s) + gnarkScalars = append(gnarkScalars, *gnarkScalar) + gnarkPoints = append(gnarkPoints, *cp1) + + blstScalar := new(blst.Scalar).FromBEndian(common.LeftPadBytes(s.Bytes(), 32)) + blstScalars = append(blstScalars, blstScalar) + blstPoints = append(blstPoints, bl1) + } + + if len(gnarkScalars) == 0 || len(gnarkScalars) != len(gnarkPoints) { + return 0 + } + + // gnark multi exp + cp := new(gnark.G2Affine) + cp.MultiExp(gnarkPoints, gnarkScalars, ecc.MultiExpConfig{}) + + expectedGnark := multiExpG2Gnark(gnarkPoints, gnarkScalars) + if !bytes.Equal(cp.Marshal(), expectedGnark.Marshal()) { + panic("g1 multi exponentiation mismatch") + } + + // blst multi exp + expectedBlst := blst.P2AffinesMult(blstPoints, blstScalars, 256).ToAffine() + if !bytes.Equal(cp.Marshal(), expectedBlst.Serialize()) { + panic("g1 multi exponentiation mismatch, gnark/blst") + } + return 1 +} + +func getG1Points(input io.Reader) (*gnark.G1Affine, *blst.P1Affine, error) { // sample a random scalar s, err := randomScalar(input, fp.Modulus()) if err != nil { - return nil, nil, nil, err + return nil, nil, err } // compute a random point @@ -277,58 +339,39 @@ func getG1Points(input io.Reader) (*bls12381.PointG1, *gnark.G1Affine, *blst.P1A cp.ScalarMultiplication(&g1Gen, s) cpBytes := cp.Marshal() - // marshal gnark point -> geth point - g1 := bls12381.NewG1() - kp, err := g1.FromBytes(cpBytes) - if err != nil { - panic(fmt.Sprintf("Could not marshal gnark.G1 -> geth.G1: %v", err)) - } - if !bytes.Equal(g1.ToBytes(kp), cpBytes) { - panic("bytes(gnark.G1) != bytes(geth.G1)") - } - // marshal gnark point -> blst point scalar := new(blst.Scalar).FromBEndian(common.LeftPadBytes(s.Bytes(), 32)) p1 := new(blst.P1Affine).From(scalar) - if !bytes.Equal(p1.Serialize(), cpBytes) { - panic("bytes(blst.G1) != bytes(geth.G1)") + blstRes := p1.Serialize() + if !bytes.Equal(blstRes, cpBytes) { + panic(fmt.Sprintf("bytes(blst.G1) != bytes(geth.G1)\nblst.G1: %x\ngeth.G1: %x\n", blstRes, cpBytes)) } - return kp, cp, p1, nil + return cp, p1, nil } -func getG2Points(input io.Reader) (*bls12381.PointG2, *gnark.G2Affine, *blst.P2Affine, error) { +func getG2Points(input io.Reader) (*gnark.G2Affine, *blst.P2Affine, error) { // sample a random scalar s, err := randomScalar(input, fp.Modulus()) if err != nil { - return nil, nil, nil, err + return nil, nil, err } // compute a random point - cp := new(gnark.G2Affine) + gp := new(gnark.G2Affine) _, _, _, g2Gen := gnark.Generators() - cp.ScalarMultiplication(&g2Gen, s) - cpBytes := cp.Marshal() - - // marshal gnark point -> geth point - g2 := bls12381.NewG2() - kp, err := g2.FromBytes(cpBytes) - if err != nil { - panic(fmt.Sprintf("Could not marshal gnark.G2 -> geth.G2: %v", err)) - } - if !bytes.Equal(g2.ToBytes(kp), cpBytes) { - panic("bytes(gnark.G2) != bytes(geth.G2)") - } + gp.ScalarMultiplication(&g2Gen, s) + cpBytes := gp.Marshal() // marshal gnark point -> blst point // Left pad the scalar to 32 bytes scalar := new(blst.Scalar).FromBEndian(common.LeftPadBytes(s.Bytes(), 32)) p2 := new(blst.P2Affine).From(scalar) if !bytes.Equal(p2.Serialize(), cpBytes) { - panic("bytes(blst.G2) != bytes(geth.G2)") + panic("bytes(blst.G2) != bytes(bls12381.G2)") } - return kp, cp, p2, nil + return gp, p2, nil } func randomScalar(r io.Reader, max *big.Int) (k *big.Int, err error) { @@ -339,3 +382,29 @@ func randomScalar(r io.Reader, max *big.Int) (k *big.Int, err error) { } } } + +// multiExpG1Gnark is a naive implementation of G1 multi-exponentiation +func multiExpG1Gnark(gs []gnark.G1Affine, scalars []fr.Element) gnark.G1Affine { + res := gnark.G1Affine{} + for i := 0; i < len(gs); i++ { + tmp := new(gnark.G1Affine) + sb := scalars[i].Bytes() + scalarBytes := new(big.Int).SetBytes(sb[:]) + tmp.ScalarMultiplication(&gs[i], scalarBytes) + res.Add(&res, tmp) + } + return res +} + +// multiExpG1Gnark is a naive implementation of G1 multi-exponentiation +func multiExpG2Gnark(gs []gnark.G2Affine, scalars []fr.Element) gnark.G2Affine { + res := gnark.G2Affine{} + for i := 0; i < len(gs); i++ { + tmp := new(gnark.G2Affine) + sb := scalars[i].Bytes() + scalarBytes := new(big.Int).SetBytes(sb[:]) + tmp.ScalarMultiplication(&gs[i], scalarBytes) + res.Add(&res, tmp) + } + return res +} diff --git a/tests/fuzzers/bls12381/bls12381_test.go b/tests/fuzzers/bls12381/bls12381_test.go index fd782f7813..d4e5e20e04 100644 --- a/tests/fuzzers/bls12381/bls12381_test.go +++ b/tests/fuzzers/bls12381/bls12381_test.go @@ -27,6 +27,12 @@ func FuzzCrossPairing(f *testing.F) { }) } +func FuzzCrossG2MultiExp(f *testing.F) { + f.Fuzz(func(t *testing.T, data []byte) { + fuzzCrossG2MultiExp(data) + }) +} + func FuzzCrossG1Add(f *testing.F) { f.Fuzz(func(t *testing.T, data []byte) { fuzzCrossG1Add(data) @@ -51,9 +57,9 @@ func FuzzG1Add(f *testing.F) { }) } -func FuzzG1Mul(f *testing.F) { +func FuzzCrossG1Mul(f *testing.F) { f.Fuzz(func(t *testing.T, data []byte) { - fuzz(blsG1Mul, data) + fuzzCrossG1Mul(data) }) } @@ -69,9 +75,9 @@ func FuzzG2Add(f *testing.F) { }) } -func FuzzG2Mul(f *testing.F) { +func FuzzCrossG2Mul(f *testing.F) { f.Fuzz(func(t *testing.T, data []byte) { - fuzz(blsG2Mul, data) + fuzzCrossG2Mul(data) }) } @@ -110,3 +116,15 @@ func FuzzG2SubgroupChecks(f *testing.F) { fuzzG2SubgroupChecks(data) }) } + +func FuzzG2Mul(f *testing.F) { + f.Fuzz(func(t *testing.T, data []byte) { + fuzz(blsG2Mul, data) + }) +} + +func FuzzG1Mul(f *testing.F) { + f.Fuzz(func(t *testing.T, data []byte) { + fuzz(blsG1Mul, data) + }) +} diff --git a/tests/fuzzers/bn256/bn256_fuzz.go b/tests/fuzzers/bn256/bn256_fuzz.go index 75f7d59dee..4521f6b0db 100644 --- a/tests/fuzzers/bn256/bn256_fuzz.go +++ b/tests/fuzzers/bn256/bn256_fuzz.go @@ -22,12 +22,12 @@ import ( "io" "math/big" - "github.com/consensys/gnark-crypto/ecc/bn254" cloudflare "github.com/ethereum/go-ethereum/crypto/bn256/cloudflare" + gnark "github.com/ethereum/go-ethereum/crypto/bn256/gnark" google "github.com/ethereum/go-ethereum/crypto/bn256/google" ) -func getG1Points(input io.Reader) (*cloudflare.G1, *google.G1, *bn254.G1Affine) { +func getG1Points(input io.Reader) (*cloudflare.G1, *google.G1, *gnark.G1) { _, xc, err := cloudflare.RandomG1(input) if err != nil { // insufficient input @@ -37,14 +37,14 @@ func getG1Points(input io.Reader) (*cloudflare.G1, *google.G1, *bn254.G1Affine) if _, err := xg.Unmarshal(xc.Marshal()); err != nil { panic(fmt.Sprintf("Could not marshal cloudflare -> google: %v", err)) } - xs := new(bn254.G1Affine) - if err := xs.Unmarshal(xc.Marshal()); err != nil { + xs := new(gnark.G1) + if _, err := xs.Unmarshal(xc.Marshal()); err != nil { panic(fmt.Sprintf("Could not marshal cloudflare -> gnark: %v", err)) } return xc, xg, xs } -func getG2Points(input io.Reader) (*cloudflare.G2, *google.G2, *bn254.G2Affine) { +func getG2Points(input io.Reader) (*cloudflare.G2, *google.G2, *gnark.G2) { _, xc, err := cloudflare.RandomG2(input) if err != nil { // insufficient input @@ -54,14 +54,14 @@ func getG2Points(input io.Reader) (*cloudflare.G2, *google.G2, *bn254.G2Affine) if _, err := xg.Unmarshal(xc.Marshal()); err != nil { panic(fmt.Sprintf("Could not marshal cloudflare -> google: %v", err)) } - xs := new(bn254.G2Affine) - if err := xs.Unmarshal(xc.Marshal()); err != nil { + xs := new(gnark.G2) + if _, err := xs.Unmarshal(xc.Marshal()); err != nil { panic(fmt.Sprintf("Could not marshal cloudflare -> gnark: %v", err)) } return xc, xg, xs } -// fuzzAdd fuzzez bn256 addition between the Google and Cloudflare libraries. +// fuzzAdd fuzzes bn256 addition between the Google, Cloudflare and Gnark libraries. func fuzzAdd(data []byte) int { input := bytes.NewReader(data) xc, xg, xs := getG1Points(input) @@ -72,7 +72,7 @@ func fuzzAdd(data []byte) int { if yc == nil { return 0 } - // Ensure both libs can parse the second curve point + // Ensure libs can parse the second curve point // Add the two points and ensure they result in the same output rc := new(cloudflare.G1) rc.Add(xc, yc) @@ -80,9 +80,8 @@ func fuzzAdd(data []byte) int { rg := new(google.G1) rg.Add(xg, yg) - tmpX := new(bn254.G1Jac).FromAffine(xs) - tmpY := new(bn254.G1Jac).FromAffine(ys) - rs := new(bn254.G1Affine).FromJacobian(tmpX.AddAssign(tmpY)) + rs := new(gnark.G1) + rs.Add(xs, ys) if !bytes.Equal(rc.Marshal(), rg.Marshal()) { panic("add mismatch: cloudflare/google") @@ -94,8 +93,8 @@ func fuzzAdd(data []byte) int { return 1 } -// fuzzMul fuzzez bn256 scalar multiplication between the Google and Cloudflare -// libraries. +// fuzzMul fuzzes bn256 scalar multiplication between the Google, Cloudflare +// and Gnark libraries. func fuzzMul(data []byte) int { input := bytes.NewReader(data) pc, pg, ps := getG1Points(input) @@ -122,15 +121,13 @@ func fuzzMul(data []byte) int { rg := new(google.G1) rg.ScalarMult(pg, new(big.Int).SetBytes(buf)) - rs := new(bn254.G1Jac) - psJac := new(bn254.G1Jac).FromAffine(ps) - rs.ScalarMultiplication(psJac, new(big.Int).SetBytes(buf)) - rsAffine := new(bn254.G1Affine).FromJacobian(rs) + rs := new(gnark.G1) + rs.ScalarMult(ps, new(big.Int).SetBytes(buf)) if !bytes.Equal(rc.Marshal(), rg.Marshal()) { panic("scalar mul mismatch: cloudflare/google") } - if !bytes.Equal(rc.Marshal(), rsAffine.Marshal()) { + if !bytes.Equal(rc.Marshal(), rs.Marshal()) { panic("scalar mul mismatch: cloudflare/gnark") } return 1 @@ -150,17 +147,26 @@ func fuzzPair(data []byte) int { // Pair the two points and ensure they result in the same output clPair := cloudflare.Pair(pc, tc).Marshal() gPair := google.Pair(pg, tg).Marshal() + sPair := gnark.Pair(ps, ts).Marshal() + if !bytes.Equal(clPair, gPair) { panic("pairing mismatch: cloudflare/google") } - cPair, err := bn254.Pair([]bn254.G1Affine{*ps}, []bn254.G2Affine{*ts}) - if err != nil { - panic(fmt.Sprintf("gnark/bn254 encountered error: %v", err)) + + normalizedClPair := normalizeGTToGnark(clPair).Marshal() + if !bytes.Equal(normalizedClPair, sPair) { + panic("pairing mismatch: cloudflare/gnark") } - // gnark uses a different pairing algorithm which might produce - // different but also correct outputs, we need to scale the output by s + return 1 +} +// normalizeGTToGnark scales a Cloudflare/Google GT element by `s` +// so that it can be compared with a gnark GT point. +// +// For the definition of `s` see 3.5 in https://eprint.iacr.org/2015/192.pdf +func normalizeGTToGnark(cloudflareOrGoogleGT []byte) *gnark.GT { + // Compute s = 2*u(6*u^2 + 3*u + 1) u, _ := new(big.Int).SetString("0x44e992b44a6909f1", 0) u_exp2 := new(big.Int).Exp(u, big.NewInt(2), nil) // u^2 u_6_exp2 := new(big.Int).Mul(big.NewInt(6), u_exp2) // 6*u^2 @@ -170,14 +176,12 @@ func fuzzPair(data []byte) int { u_2 := new(big.Int).Mul(big.NewInt(2), u) // 2*u s := u_2.Mul(u_2, inner) // 2*u(6*u^2 + 3*u + 1) - gRes := new(bn254.GT) - if err := gRes.SetBytes(clPair); err != nil { + // Scale the Cloudflare/Google GT element by `s` + gRes := new(gnark.GT) + if err := gRes.Unmarshal(cloudflareOrGoogleGT); err != nil { panic(err) } gRes = gRes.Exp(*gRes, s) - if !bytes.Equal(cPair.Marshal(), gRes.Marshal()) { - panic("pairing mismatch: cloudflare/gnark") - } - return 1 + return gRes } diff --git a/tests/fuzzers/secp256k1/secp_test.go b/tests/fuzzers/secp256k1/secp_test.go index ca3039764b..3345a66a67 100644 --- a/tests/fuzzers/secp256k1/secp_test.go +++ b/tests/fuzzers/secp256k1/secp_test.go @@ -20,7 +20,7 @@ import ( "fmt" "testing" - "github.com/btcsuite/btcd/btcec/v2" + dcred_secp256k1 "github.com/decred/dcrd/dcrec/secp256k1/v4" "github.com/ethereum/go-ethereum/crypto/secp256k1" ) @@ -38,7 +38,7 @@ func Fuzz(f *testing.F) { func fuzz(dataP1, dataP2 []byte) { var ( curveA = secp256k1.S256() - curveB = btcec.S256() + curveB = dcred_secp256k1.S256() ) // first point x1, y1 := curveB.ScalarBaseMult(dataP1) diff --git a/tests/gen_stauthorization.go b/tests/gen_stauthorization.go new file mode 100644 index 0000000000..fbafd6fdea --- /dev/null +++ b/tests/gen_stauthorization.go @@ -0,0 +1,74 @@ +// Code generated by github.com/fjl/gencodec. DO NOT EDIT. + +package tests + +import ( + "encoding/json" + "errors" + "math/big" + + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/common/math" +) + +var _ = (*stAuthorizationMarshaling)(nil) + +// MarshalJSON marshals as JSON. +func (s stAuthorization) MarshalJSON() ([]byte, error) { + type stAuthorization struct { + ChainID math.HexOrDecimal64 + Address common.Address `json:"address" gencodec:"required"` + Nonce math.HexOrDecimal64 `json:"nonce" gencodec:"required"` + V math.HexOrDecimal64 `json:"v" gencodec:"required"` + R *math.HexOrDecimal256 `json:"r" gencodec:"required"` + S *math.HexOrDecimal256 `json:"s" gencodec:"required"` + } + var enc stAuthorization + enc.ChainID = math.HexOrDecimal64(s.ChainID) + enc.Address = s.Address + enc.Nonce = math.HexOrDecimal64(s.Nonce) + enc.V = math.HexOrDecimal64(s.V) + enc.R = (*math.HexOrDecimal256)(s.R) + enc.S = (*math.HexOrDecimal256)(s.S) + return json.Marshal(&enc) +} + +// UnmarshalJSON unmarshals from JSON. +func (s *stAuthorization) UnmarshalJSON(input []byte) error { + type stAuthorization struct { + ChainID *math.HexOrDecimal64 + Address *common.Address `json:"address" gencodec:"required"` + Nonce *math.HexOrDecimal64 `json:"nonce" gencodec:"required"` + V *math.HexOrDecimal64 `json:"v" gencodec:"required"` + R *math.HexOrDecimal256 `json:"r" gencodec:"required"` + S *math.HexOrDecimal256 `json:"s" gencodec:"required"` + } + var dec stAuthorization + if err := json.Unmarshal(input, &dec); err != nil { + return err + } + if dec.ChainID != nil { + s.ChainID = uint64(*dec.ChainID) + } + if dec.Address == nil { + return errors.New("missing required field 'address' for stAuthorization") + } + s.Address = *dec.Address + if dec.Nonce == nil { + return errors.New("missing required field 'nonce' for stAuthorization") + } + s.Nonce = uint64(*dec.Nonce) + if dec.V == nil { + return errors.New("missing required field 'v' for stAuthorization") + } + s.V = uint8(*dec.V) + if dec.R == nil { + return errors.New("missing required field 'r' for stAuthorization") + } + s.R = (*big.Int)(dec.R) + if dec.S == nil { + return errors.New("missing required field 's' for stAuthorization") + } + s.S = (*big.Int)(dec.S) + return nil +} diff --git a/tests/gen_sttransaction.go b/tests/gen_sttransaction.go index 9b5aecbfe6..b25ce76166 100644 --- a/tests/gen_sttransaction.go +++ b/tests/gen_sttransaction.go @@ -30,6 +30,7 @@ func (s stTransaction) MarshalJSON() ([]byte, error) { Sender *common.Address `json:"sender"` BlobVersionedHashes []common.Hash `json:"blobVersionedHashes,omitempty"` BlobGasFeeCap *math.HexOrDecimal256 `json:"maxFeePerBlobGas,omitempty"` + AuthorizationList []*stAuthorization `json:"authorizationList,omitempty"` } var enc stTransaction enc.GasPrice = (*math.HexOrDecimal256)(s.GasPrice) @@ -50,6 +51,7 @@ func (s stTransaction) MarshalJSON() ([]byte, error) { enc.Sender = s.Sender enc.BlobVersionedHashes = s.BlobVersionedHashes enc.BlobGasFeeCap = (*math.HexOrDecimal256)(s.BlobGasFeeCap) + enc.AuthorizationList = s.AuthorizationList return json.Marshal(&enc) } @@ -69,6 +71,7 @@ func (s *stTransaction) UnmarshalJSON(input []byte) error { Sender *common.Address `json:"sender"` BlobVersionedHashes []common.Hash `json:"blobVersionedHashes,omitempty"` BlobGasFeeCap *math.HexOrDecimal256 `json:"maxFeePerBlobGas,omitempty"` + AuthorizationList []*stAuthorization `json:"authorizationList,omitempty"` } var dec stTransaction if err := json.Unmarshal(input, &dec); err != nil { @@ -116,5 +119,8 @@ func (s *stTransaction) UnmarshalJSON(input []byte) error { if dec.BlobGasFeeCap != nil { s.BlobGasFeeCap = (*big.Int)(dec.BlobGasFeeCap) } + if dec.AuthorizationList != nil { + s.AuthorizationList = dec.AuthorizationList + } return nil } diff --git a/tests/state_test.go b/tests/state_test.go index 76d5a601c7..7b82b05e58 100644 --- a/tests/state_test.go +++ b/tests/state_test.go @@ -302,7 +302,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 cf0ce9777f..6e66bbaa72 100644 --- a/tests/state_test_util.go +++ b/tests/state_test_util.go @@ -123,6 +123,7 @@ type stTransaction struct { Sender *common.Address `json:"sender"` BlobVersionedHashes []common.Hash `json:"blobVersionedHashes,omitempty"` BlobGasFeeCap *big.Int `json:"maxFeePerBlobGas,omitempty"` + AuthorizationList []*stAuthorization `json:"authorizationList,omitempty"` } type stTransactionMarshaling struct { @@ -135,6 +136,27 @@ type stTransactionMarshaling struct { BlobGasFeeCap *math.HexOrDecimal256 } +//go:generate go run github.com/fjl/gencodec -type stAuthorization -field-override stAuthorizationMarshaling -out gen_stauthorization.go + +// Authorization is an authorization from an account to deploy code at it's address. +type stAuthorization struct { + ChainID uint64 + Address common.Address `json:"address" gencodec:"required"` + Nonce uint64 `json:"nonce" gencodec:"required"` + V uint8 `json:"v" gencodec:"required"` + R *big.Int `json:"r" gencodec:"required"` + S *big.Int `json:"s" gencodec:"required"` +} + +// field type overrides for gencodec +type stAuthorizationMarshaling struct { + ChainID math.HexOrDecimal64 + Nonce math.HexOrDecimal64 + V math.HexOrDecimal64 + R *math.HexOrDecimal256 + S *math.HexOrDecimal256 +} + // GetChainConfig takes a fork definition and returns a chain config. // The fork definition can be // - a plain forkname, e.g. `Byzantium`, @@ -196,7 +218,7 @@ func (t *StateTest) checkError(subtest StateSubtest, err error) error { // Run executes a specific subtest and verifies the post-state and logs func (t *StateTest) Run(subtest StateSubtest, vmconfig vm.Config, snapshotter bool, scheme string, postCheck func(err error, st *StateTestState)) (result error) { - st, root, err := t.RunNoVerify(subtest, vmconfig, snapshotter, scheme) + st, root, _, err := t.RunNoVerify(subtest, vmconfig, snapshotter, scheme) // Invoke the callback at the end of function for further analysis. defer func() { postCheck(result, &st) @@ -228,10 +250,10 @@ func (t *StateTest) Run(subtest StateSubtest, vmconfig vm.Config, snapshotter bo // RunNoVerify runs a specific subtest and returns the statedb and post-state root. // Remember to call state.Close after verifying the test result! -func (t *StateTest) RunNoVerify(subtest StateSubtest, vmconfig vm.Config, snapshotter bool, scheme string) (st StateTestState, root common.Hash, err error) { +func (t *StateTest) RunNoVerify(subtest StateSubtest, vmconfig vm.Config, snapshotter bool, scheme string) (st StateTestState, root common.Hash, gasUsed uint64, err error) { config, eips, err := GetChainConfig(subtest.Fork) if err != nil { - return st, common.Hash{}, UnsupportedForkError{subtest.Fork} + return st, common.Hash{}, 0, UnsupportedForkError{subtest.Fork} } vmconfig.ExtraEips = eips @@ -250,7 +272,7 @@ func (t *StateTest) RunNoVerify(subtest StateSubtest, vmconfig vm.Config, snapsh post := t.json.Post[subtest.Fork][subtest.Index] msg, err := t.json.Tx.toMessage(post, baseFee) if err != nil { - return st, common.Hash{}, err + return st, common.Hash{}, 0, err } { // Blob transactions may be present after the Cancun fork. @@ -260,7 +282,7 @@ func (t *StateTest) RunNoVerify(subtest StateSubtest, vmconfig vm.Config, snapsh // Here, we just do this shortcut smaller fix, since state tests do not // utilize those codepaths if len(msg.BlobHashes)*params.BlobTxBlobGasPerBlob > params.MaxBlobGasPerBlock { - return st, common.Hash{}, errors.New("blob gas exceeds maximum") + return st, common.Hash{}, 0, errors.New("blob gas exceeds maximum") } } @@ -269,15 +291,14 @@ func (t *StateTest) RunNoVerify(subtest StateSubtest, vmconfig vm.Config, snapsh var ttx types.Transaction err := ttx.UnmarshalBinary(post.TxBytes) if err != nil { - return st, common.Hash{}, err + return st, common.Hash{}, 0, err } if _, err := types.Sender(types.LatestSigner(config), &ttx); err != nil { - return st, common.Hash{}, err + return st, common.Hash{}, 0, err } } // 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 +314,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) @@ -308,6 +329,7 @@ func (t *StateTest) RunNoVerify(subtest StateSubtest, vmconfig vm.Config, snapsh if tracer := evm.Config.Tracer; tracer != nil && tracer.OnTxEnd != nil { evm.Config.Tracer.OnTxEnd(nil, err) } + return st, common.Hash{}, 0, err } // Add 0-value mining reward. This only makes a difference in the cases // where @@ -322,7 +344,7 @@ func (t *StateTest) RunNoVerify(subtest StateSubtest, vmconfig vm.Config, snapsh receipt := &types.Receipt{GasUsed: vmRet.UsedGas} tracer.OnTxEnd(receipt, nil) } - return st, root, err + return st, root, vmRet.UsedGas, nil } func (t *StateTest) gasLimit(subtest StateSubtest) uint64 { @@ -411,26 +433,43 @@ func (tx *stTransaction) toMessage(ps stPostState, baseFee *big.Int) (*core.Mess if tx.MaxPriorityFeePerGas == nil { tx.MaxPriorityFeePerGas = tx.MaxFeePerGas } - gasPrice = math.BigMin(new(big.Int).Add(tx.MaxPriorityFeePerGas, baseFee), - tx.MaxFeePerGas) + gasPrice = new(big.Int).Add(tx.MaxPriorityFeePerGas, baseFee) + if gasPrice.Cmp(tx.MaxFeePerGas) > 0 { + gasPrice = tx.MaxFeePerGas + } } if gasPrice == nil { return nil, errors.New("no gas price provided") } + var authList []types.SetCodeAuthorization + if tx.AuthorizationList != nil { + authList = make([]types.SetCodeAuthorization, len(tx.AuthorizationList)) + for i, auth := range tx.AuthorizationList { + authList[i] = types.SetCodeAuthorization{ + ChainID: auth.ChainID, + Address: auth.Address, + Nonce: auth.Nonce, + V: auth.V, + R: *uint256.MustFromBig(auth.R), + S: *uint256.MustFromBig(auth.S), + } + } + } msg := &core.Message{ - From: from, - To: to, - Nonce: tx.Nonce, - Value: value, - GasLimit: gasLimit, - GasPrice: gasPrice, - GasFeeCap: tx.MaxFeePerGas, - GasTipCap: tx.MaxPriorityFeePerGas, - Data: data, - AccessList: accessList, - BlobHashes: tx.BlobVersionedHashes, - BlobGasFeeCap: tx.BlobGasFeeCap, + From: from, + To: to, + Nonce: tx.Nonce, + Value: value, + GasLimit: gasLimit, + GasPrice: gasPrice, + GasFeeCap: tx.MaxFeePerGas, + GasTipCap: tx.MaxPriorityFeePerGas, + Data: data, + AccessList: accessList, + BlobHashes: tx.BlobVersionedHashes, + BlobGasFeeCap: tx.BlobGasFeeCap, + SetCodeAuthorizations: authList, } return msg, nil } diff --git a/tests/transaction_test_util.go b/tests/transaction_test_util.go index d3dbbd5db2..55b76df89c 100644 --- a/tests/transaction_test_util.go +++ b/tests/transaction_test_util.go @@ -59,7 +59,7 @@ func (tt *TransactionTest) Run(config *params.ChainConfig) error { return nil, nil, err } // Intrinsic gas - requiredGas, err := core.IntrinsicGas(tx.Data(), tx.AccessList(), tx.To() == nil, isHomestead, isIstanbul, false) + requiredGas, err := core.IntrinsicGas(tx.Data(), tx.AccessList(), tx.SetCodeAuthorizations(), tx.To() == nil, isHomestead, isIstanbul, false) if err != nil { return nil, nil, err } diff --git a/trie/database_test.go b/trie/database_test.go index aed508b368..729d9f699b 100644 --- a/trie/database_test.go +++ b/trie/database_test.go @@ -73,7 +73,7 @@ func newTestDatabase(diskdb ethdb.Database, scheme string) *testDb { } } -func (db *testDb) Reader(stateRoot common.Hash) (database.Reader, error) { +func (db *testDb) NodeReader(stateRoot common.Hash) (database.NodeReader, error) { nodes, _ := db.dirties(stateRoot, true) return &testReader{db: db.disk, scheme: db.scheme, nodes: nodes}, nil } diff --git a/trie/iterator_test.go b/trie/iterator_test.go index b463294b09..74a1aa378c 100644 --- a/trie/iterator_test.go +++ b/trie/iterator_test.go @@ -146,7 +146,7 @@ func testNodeIteratorCoverage(t *testing.T, scheme string) { } } // Cross check the hashes and the database itself - reader, err := nodeDb.Reader(trie.Hash()) + reader, err := nodeDb.NodeReader(trie.Hash()) if err != nil { t.Fatalf("state is not available %x", trie.Hash()) } 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/secure_trie.go b/trie/secure_trie.go index 91fd38269f..f53b10758f 100644 --- a/trie/secure_trie.go +++ b/trie/secure_trie.go @@ -40,7 +40,7 @@ type SecureTrie = StateTrie // NewSecure creates a new StateTrie. // Deprecated: use NewStateTrie. -func NewSecure(stateRoot common.Hash, owner common.Hash, root common.Hash, db database.Database) (*SecureTrie, error) { +func NewSecure(stateRoot common.Hash, owner common.Hash, root common.Hash, db database.NodeDatabase) (*SecureTrie, error) { id := &ID{ StateRoot: stateRoot, Owner: owner, @@ -61,7 +61,7 @@ func NewSecure(stateRoot common.Hash, owner common.Hash, root common.Hash, db da // StateTrie is not safe for concurrent use. type StateTrie struct { trie Trie - db database.Database + db database.NodeDatabase preimages preimageStore hashKeyBuf [common.HashLength]byte secKeyCache map[string][]byte @@ -73,7 +73,7 @@ type StateTrie struct { // If root is the zero hash or the sha3 hash of an empty string, the // trie is initially empty. Otherwise, New will panic if db is nil // and returns MissingNodeError if the root node cannot be found. -func NewStateTrie(id *ID, db database.Database) (*StateTrie, error) { +func NewStateTrie(id *ID, db database.NodeDatabase) (*StateTrie, error) { if db == nil { panic("trie.NewStateTrie called without a database") } diff --git a/trie/sync_test.go b/trie/sync_test.go index ccdee7d014..2ff02576d4 100644 --- a/trie/sync_test.go +++ b/trie/sync_test.go @@ -183,7 +183,7 @@ func testIterativeSync(t *testing.T, count int, bypath bool, scheme string) { syncPath: NewSyncPath([]byte(paths[i])), }) } - reader, err := srcDb.Reader(srcTrie.Hash()) + reader, err := srcDb.NodeReader(srcTrie.Hash()) if err != nil { t.Fatalf("State is not available %x", srcTrie.Hash()) } @@ -258,7 +258,7 @@ func testIterativeDelayedSync(t *testing.T, scheme string) { syncPath: NewSyncPath([]byte(paths[i])), }) } - reader, err := srcDb.Reader(srcTrie.Hash()) + reader, err := srcDb.NodeReader(srcTrie.Hash()) if err != nil { t.Fatalf("State is not available %x", srcTrie.Hash()) } @@ -327,7 +327,7 @@ func testIterativeRandomSync(t *testing.T, count int, scheme string) { syncPath: NewSyncPath([]byte(paths[i])), } } - reader, err := srcDb.Reader(srcTrie.Hash()) + reader, err := srcDb.NodeReader(srcTrie.Hash()) if err != nil { t.Fatalf("State is not available %x", srcTrie.Hash()) } @@ -394,7 +394,7 @@ func testIterativeRandomDelayedSync(t *testing.T, scheme string) { syncPath: NewSyncPath([]byte(path)), } } - reader, err := srcDb.Reader(srcTrie.Hash()) + reader, err := srcDb.NodeReader(srcTrie.Hash()) if err != nil { t.Fatalf("State is not available %x", srcTrie.Hash()) } @@ -466,7 +466,7 @@ func testDuplicateAvoidanceSync(t *testing.T, scheme string) { syncPath: NewSyncPath([]byte(paths[i])), }) } - reader, err := srcDb.Reader(srcTrie.Hash()) + reader, err := srcDb.NodeReader(srcTrie.Hash()) if err != nil { t.Fatalf("State is not available %x", srcTrie.Hash()) } @@ -542,7 +542,7 @@ func testIncompleteSync(t *testing.T, scheme string) { syncPath: NewSyncPath([]byte(paths[i])), }) } - reader, err := srcDb.Reader(srcTrie.Hash()) + reader, err := srcDb.NodeReader(srcTrie.Hash()) if err != nil { t.Fatalf("State is not available %x", srcTrie.Hash()) } @@ -634,7 +634,7 @@ func testSyncOrdering(t *testing.T, scheme string) { }) reqs = append(reqs, NewSyncPath([]byte(paths[i]))) } - reader, err := srcDb.Reader(srcTrie.Hash()) + reader, err := srcDb.NodeReader(srcTrie.Hash()) if err != nil { t.Fatalf("State is not available %x", srcTrie.Hash()) } @@ -704,7 +704,7 @@ func syncWithHookWriter(t *testing.T, root common.Hash, db ethdb.Database, srcDb syncPath: NewSyncPath([]byte(paths[i])), }) } - reader, err := srcDb.Reader(root) + reader, err := srcDb.NodeReader(root) if err != nil { t.Fatalf("State is not available %x", root) } diff --git a/trie/trie.go b/trie/trie.go index 372684683c..ae2a7b21a2 100644 --- a/trie/trie.go +++ b/trie/trie.go @@ -83,7 +83,7 @@ func (t *Trie) Copy() *Trie { // zero hash or the sha3 hash of an empty string, then trie is initially // empty, otherwise, the root node must be present in database or returns // a MissingNodeError if not. -func New(id *ID, db database.Database) (*Trie, error) { +func New(id *ID, db database.NodeDatabase) (*Trie, error) { reader, err := newTrieReader(id.StateRoot, id.Owner, db) if err != nil { return nil, err @@ -104,7 +104,7 @@ func New(id *ID, db database.Database) (*Trie, error) { } // NewEmpty is a shortcut to create empty tree. It's mostly used in tests. -func NewEmpty(db database.Database) *Trie { +func NewEmpty(db database.NodeDatabase) *Trie { tr, _ := New(TrieID(types.EmptyRootHash), db) return tr } @@ -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/trie_reader.go b/trie/trie_reader.go index adbf43d287..4b8ba808df 100644 --- a/trie/trie_reader.go +++ b/trie/trie_reader.go @@ -27,19 +27,19 @@ import ( // for concurrent usage. type trieReader struct { owner common.Hash - reader database.Reader + reader database.NodeReader banned map[string]struct{} // Marker to prevent node from being accessed, for tests } // newTrieReader initializes the trie reader with the given node reader. -func newTrieReader(stateRoot, owner common.Hash, db database.Database) (*trieReader, error) { +func newTrieReader(stateRoot, owner common.Hash, db database.NodeDatabase) (*trieReader, error) { if stateRoot == (common.Hash{}) || stateRoot == types.EmptyRootHash { if stateRoot == (common.Hash{}) { log.Error("Zero state root hash!") } return &trieReader{owner: owner}, nil } - reader, err := db.Reader(stateRoot) + reader, err := db.NodeReader(stateRoot) if err != nil { return nil, &MissingNodeError{Owner: owner, NodeHash: stateRoot, err: err} } @@ -55,6 +55,9 @@ func newEmptyReader() *trieReader { // node retrieves the rlp-encoded trie node with the provided trie node // information. An MissingNodeError will be returned in case the node is // not found or any error is encountered. +// +// Don't modify the returned byte slice since it's not deep-copied and +// still be referenced by database. func (r *trieReader) node(path []byte, hash common.Hash) ([]byte, error) { // Perform the logics in tests for preventing trie node access. if r.banned != nil { diff --git a/trie/trie_test.go b/trie/trie_test.go index 9b2530bdd4..423ed30fe8 100644 --- a/trie/trie_test.go +++ b/trie/trie_test.go @@ -819,6 +819,7 @@ type spongeDb struct { func (s *spongeDb) Has(key []byte) (bool, error) { panic("implement me") } func (s *spongeDb) Get(key []byte) ([]byte, error) { return nil, errors.New("no such elem") } func (s *spongeDb) Delete(key []byte) error { panic("implement me") } +func (s *spongeDb) DeleteRange(start, end []byte) error { panic("implement me") } func (s *spongeDb) NewBatch() ethdb.Batch { return &spongeBatch{s} } func (s *spongeDb) NewBatchWithSize(size int) ethdb.Batch { return &spongeBatch{s} } func (s *spongeDb) Stat() (string, error) { panic("implement me") } diff --git a/trie/trienode/node.go b/trie/trienode/node.go index 7debe6ecbc..047a7a4bd8 100644 --- a/trie/trienode/node.go +++ b/trie/trienode/node.go @@ -153,6 +153,15 @@ func (set *NodeSet) Size() (int, int) { return set.updates, set.deletes } +// HashSet returns a set of trie nodes keyed by node hash. +func (set *NodeSet) HashSet() map[common.Hash][]byte { + ret := make(map[common.Hash][]byte, len(set.Nodes)) + for _, n := range set.Nodes { + ret[n.Hash] = n.Blob + } + return ret +} + // Summary returns a string-representation of the NodeSet. func (set *NodeSet) Summary() string { var out = new(strings.Builder) diff --git a/trie/trienode/proof.go b/trie/trienode/proof.go index d3075ecccf..01a07c05b0 100644 --- a/trie/trienode/proof.go +++ b/trie/trienode/proof.go @@ -69,6 +69,10 @@ func (db *ProofSet) Delete(key []byte) error { return nil } +func (db *ProofSet) DeleteRange(start, end []byte) error { + panic("not supported") +} + // Get returns a stored node func (db *ProofSet) Get(key []byte) ([]byte, error) { db.lock.RLock() diff --git a/trie/triestate/state.go b/trie/triestate/state.go deleted file mode 100644 index 62a9043873..0000000000 --- a/trie/triestate/state.go +++ /dev/null @@ -1,53 +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 triestate - -import "github.com/ethereum/go-ethereum/common" - -// Set represents a collection of mutated states during a state transition. -// The value refers to the original content of state before the transition -// is made. Nil means that the state was not present previously. -type Set struct { - Accounts map[common.Address][]byte // Mutated account set, nil means the account was not present - Storages map[common.Address]map[common.Hash][]byte // Mutated storage set, nil means the slot was not present - size common.StorageSize // Approximate size of set -} - -// New constructs the state set with provided data. -func New(accounts map[common.Address][]byte, storages map[common.Address]map[common.Hash][]byte) *Set { - return &Set{ - Accounts: accounts, - Storages: storages, - } -} - -// Size returns the approximate memory size occupied by the set. -func (s *Set) Size() common.StorageSize { - if s.size != 0 { - return s.size - } - for _, account := range s.Accounts { - s.size += common.StorageSize(common.AddressLength + len(account)) - } - for _, slots := range s.Storages { - for _, val := range slots { - s.size += common.StorageSize(common.HashLength + len(val)) - } - s.size += common.StorageSize(common.AddressLength) - } - return s.size -} diff --git a/trie/utils/verkle.go b/trie/utils/verkle.go index 12e02de9a4..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] @@ -156,10 +156,6 @@ func GetTreeKey(address []byte, treeIndex *uint256.Int, subIndex byte) []byte { func GetTreeKeyWithEvaluatedAddress(evaluated *verkle.Point, treeIndex *uint256.Int, subIndex byte) []byte { var poly [5]fr.Element - poly[0].SetZero() - poly[1].SetZero() - poly[2].SetZero() - // little-endian, 32-byte aligned treeIndex var index [32]byte for i := 0; i < len(treeIndex); i++ { @@ -204,10 +200,10 @@ func CodeChunkKey(address []byte, chunk *uint256.Int) []byte { return GetTreeKey(address, treeIndex, subIndex) } -func StorageIndex(bytes []byte) (*uint256.Int, byte) { +func StorageIndex(storageKey []byte) (*uint256.Int, byte) { // If the storage slot is in the header, we need to add the header offset. var key uint256.Int - key.SetBytes(bytes) + key.SetBytes(storageKey) if key.Cmp(codeStorageDelta) < 0 { // This addition is always safe; it can't ever overflow since pos. + +package pathdb + +import ( + "fmt" + "time" + + "github.com/VictoriaMetrics/fastcache" + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/core/rawdb" + "github.com/ethereum/go-ethereum/ethdb" + "github.com/ethereum/go-ethereum/log" + "github.com/ethereum/go-ethereum/trie/trienode" +) + +// buffer is a collection of modified states along with the modified trie nodes. +// They are cached here to aggregate the disk write. The content of the buffer +// 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 + states *stateSet // Aggregated state set +} + +// newBuffer initializes the buffer with the provided states and trie nodes. +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, states *stateSet) *buffer { + b.layers++ + b.nodes.merge(nodes) + b.states.merge(states) + return b +} + +// 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) 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 + } + b.layers-- + + // Reset the entire buffer if only a single transition left + if b.layers == 0 { + b.reset() + return nil + } + b.nodes.revertTo(db, nodes) + b.states.revertTo(accounts, storages) + return nil +} + +// reset cleans up the disk cache. +func (b *buffer) reset() { + b.layers = 0 + b.nodes.reset() + b.states.reset() +} + +// empty returns an indicator if buffer is empty. +func (b *buffer) empty() bool { + return b.layers == 0 +} + +// full returns an indicator if the size of accumulated content exceeds the +// configured threshold. +func (b *buffer) full() bool { + return b.size() > b.limit +} + +// size returns the approximate memory size of the held content. +func (b *buffer) size() uint64 { + return b.states.size + b.nodes.size +} + +// 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 (b *buffer) flush(db ethdb.KeyValueStore, freezer ethdb.AncientWriter, nodesCache *fastcache.Cache, id uint64) error { + // Ensure the target state id is aligned with the internal counter. + head := rawdb.ReadPersistentStateID(db) + if head+b.layers != id { + return fmt.Errorf("buffer layers (%d) cannot be applied on top of persisted state id (%d) to reach requested state id (%d)", b.layers, head, id) + } + // Terminate the state snapshot generation if it's active + var ( + start = time.Now() + batch = db.NewBatchWithSize(b.nodes.dbsize() * 11 / 10) // extra 10% for potential pebble internal stuff + ) + // 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 := b.nodes.write(batch, nodesCache) + 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) + b.reset() + log.Debug("Persisted buffer content", "nodes", nodes, "bytes", common.StorageSize(size), "elapsed", common.PrettyDuration(time.Since(start))) + return nil +} diff --git a/triedb/pathdb/database.go b/triedb/pathdb/database.go index 31e478117c..914b17de5b 100644 --- a/triedb/pathdb/database.go +++ b/triedb/pathdb/database.go @@ -31,7 +31,6 @@ import ( "github.com/ethereum/go-ethereum/log" "github.com/ethereum/go-ethereum/params" "github.com/ethereum/go-ethereum/trie/trienode" - "github.com/ethereum/go-ethereum/trie/triestate" ) const ( @@ -39,17 +38,17 @@ const ( defaultCleanSize = 16 * 1024 * 1024 // maxBufferSize is the maximum memory allowance of node buffer. - // Too large nodebuffer will cause the system to pause for a long + // Too large buffer will cause the system to pause for a long // time when write happens. Also, the largest batch that pebble can // support is 4GB, node will panic if batch size exceeds this limit. maxBufferSize = 256 * 1024 * 1024 - // DefaultBufferSize is the default memory allowance of node buffer + // defaultBufferSize is the default memory allowance of node buffer // that aggregates the writes from above until it's flushed into the // disk. It's meant to be used once the initial sync is finished. // Do not increase the buffer size arbitrarily, otherwise the system // pause time will increase when the database writes happen. - DefaultBufferSize = 64 * 1024 * 1024 + defaultBufferSize = 64 * 1024 * 1024 ) var ( @@ -64,9 +63,29 @@ type layer interface { // if the read operation exits abnormally. Specifically, if the layer is // already stale. // - // Note, no error will be returned if the requested node is not found in database. + // Note: + // - the returned node is not a copy, please don't modify it. + // - no error will be returned if the requested node is not found in database. node(owner common.Hash, path []byte, 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 @@ -80,7 +99,7 @@ type layer interface { // the provided dirty trie nodes along with the state change set. // // Note, the maps are retained by the method to avoid copying everything. - update(root common.Hash, id uint64, block uint64, nodes map[common.Hash]map[string]*trienode.Node, states *triestate.Set) *diffLayer + update(root common.Hash, id uint64, block uint64, nodes *nodeSet, states *StateSetWithOrigin) *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 layer without @@ -90,57 +109,70 @@ type layer interface { // Config contains the settings for database. type Config struct { - StateHistory uint64 // Number of recent blocks to maintain state history for - CleanCacheSize int // Maximum memory allowance (in bytes) for caching clean nodes - DirtyCacheSize int // Maximum memory allowance (in bytes) for caching dirty nodes - ReadOnly bool // Flag whether the database is opened in read only mode. + StateHistory uint64 // Number of recent blocks to maintain state history for + CleanCacheSize int // Maximum memory allowance (in bytes) for caching clean nodes + WriteBufferSize int // Maximum memory allowance (in bytes) for write buffer + ReadOnly bool // Flag whether the database is opened in read only mode. } // sanitize checks the provided user configurations and changes anything that's // unreasonable or unworkable. func (c *Config) sanitize() *Config { conf := *c - if conf.DirtyCacheSize > maxBufferSize { - log.Warn("Sanitizing invalid node buffer size", "provided", common.StorageSize(conf.DirtyCacheSize), "updated", common.StorageSize(maxBufferSize)) - conf.DirtyCacheSize = maxBufferSize + if conf.WriteBufferSize > maxBufferSize { + log.Warn("Sanitizing invalid node buffer size", "provided", common.StorageSize(conf.WriteBufferSize), "updated", common.StorageSize(maxBufferSize)) + conf.WriteBufferSize = maxBufferSize } return &conf } +// fields returns a list of attributes of config for printing. +func (c *Config) fields() []interface{} { + var list []interface{} + if c.ReadOnly { + list = append(list, "readonly", true) + } + list = append(list, "cache", common.StorageSize(c.CleanCacheSize)) + list = append(list, "buffer", common.StorageSize(c.WriteBufferSize)) + list = append(list, "history", c.StateHistory) + return list +} + // Defaults contains default settings for Ethereum mainnet. var Defaults = &Config{ - StateHistory: params.FullImmutabilityThreshold, - CleanCacheSize: defaultCleanSize, - DirtyCacheSize: DefaultBufferSize, + StateHistory: params.FullImmutabilityThreshold, + CleanCacheSize: defaultCleanSize, + WriteBufferSize: defaultBufferSize, } // 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 // the shutdown to reject all following unexpected mutations. - readOnly bool // Flag if database is opened in read only mode - waitSync bool // Flag if database is deactivated due to initial state sync - isVerkle bool // Flag if database is used for verkle tree - bufferSize int // Memory allowance (in bytes) for caching dirty nodes - config *Config // Configuration for database - diskdb ethdb.Database // Persistent storage for matured trie nodes - tree *layerTree // The group for all known layers - freezer ethdb.ResettableAncientStore // Freezer for storing trie histories, nil possible in tests - lock sync.RWMutex // Lock to prevent mutations from happening at the same time + readOnly bool // Flag if database is opened in read only mode + waitSync bool // Flag if database is deactivated due to initial state sync + isVerkle bool // Flag if database is used for verkle tree + + config *Config // Configuration for database + diskdb ethdb.Database // Persistent storage for matured trie nodes + tree *layerTree // The group for all known layers + freezer ethdb.ResettableAncientStore // Freezer for storing trie histories, nil possible in tests + lock sync.RWMutex // Lock to prevent mutations from happening at the same time } // New attempts to load an already existing layer from a persistent key-value @@ -161,11 +193,10 @@ func New(diskdb ethdb.Database, config *Config, isVerkle bool) *Database { diskdb = rawdb.NewTable(diskdb, string(rawdb.VerklePrefix)) } db := &Database{ - readOnly: config.ReadOnly, - isVerkle: isVerkle, - bufferSize: config.DirtyCacheSize, - config: config, - diskdb: diskdb, + readOnly: config.ReadOnly, + isVerkle: isVerkle, + config: config, + diskdb: diskdb, } // Construct the layer tree by resolving the in-disk singleton state // and in-memory layer journal. @@ -174,7 +205,7 @@ func New(diskdb ethdb.Database, config *Config, isVerkle bool) *Database { // Repair the state history, which might not be aligned with the state // in the key-value store due to an unclean shutdown. if err := db.repairHistory(); err != nil { - log.Crit("Failed to repair pathdb", "err", err) + log.Crit("Failed to repair state history", "err", err) } // Disable database in case node is still in the initial state sync stage. if rawdb.ReadSnapSyncStatusFlag(diskdb) == rawdb.StateSyncRunning && !db.readOnly { @@ -182,6 +213,11 @@ func New(diskdb ethdb.Database, config *Config, isVerkle bool) *Database { log.Crit("Failed to disable database", "err", err) // impossible to happen } } + fields := config.fields() + if db.isVerkle { + fields = append(fields, "verkle", true) + } + log.Info("Initialized path database", fields...) return db } @@ -241,7 +277,7 @@ func (db *Database) repairHistory() error { // // The passed in maps(nodes, states) will be retained to avoid copying everything. // Therefore, these maps must not be changed afterwards. -func (db *Database) Update(root common.Hash, parentRoot common.Hash, block uint64, nodes *trienode.MergedNodeSet, states *triestate.Set) error { +func (db *Database) Update(root common.Hash, parentRoot common.Hash, block uint64, nodes *trienode.MergedNodeSet, states *StateSetWithOrigin) error { // Hold the lock to prevent concurrent mutations. db.lock.Lock() defer db.lock.Unlock() @@ -341,7 +377,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, newNodeBuffer(db.bufferSize, nil, 0))) + db.tree.reset(newDiskLayer(root, 0, db, nil, newBuffer(db.config.WriteBufferSize, nil, nil, 0))) // Re-enable the database as the final step. db.waitSync = false @@ -357,19 +393,19 @@ func (db *Database) Recover(root common.Hash) error { db.lock.Lock() defer db.lock.Unlock() - // Short circuit if rollback operation is not supported. + // Short circuit if rollback operation is not supported if err := db.modifyAllowed(); err != nil { return err } if db.freezer == nil { return errors.New("state rollback is non-supported") } - // Short circuit if the target state is not recoverable. + // Short circuit if the target state is not recoverable root = types.TrieRootHash(root) if !db.Recoverable(root) { return errStateUnrecoverable } - // Apply the state histories upon the disk layer in order. + // Apply the state histories upon the disk layer in order var ( start = time.Now() dl = db.tree.bottom() @@ -454,7 +490,7 @@ func (db *Database) Close() error { func (db *Database) Size() (diffs common.StorageSize, nodes common.StorageSize) { db.tree.forEach(func(layer layer) { if diff, ok := layer.(*diffLayer); ok { - diffs += common.StorageSize(diff.memory) + diffs += common.StorageSize(diff.size()) } if disk, ok := layer.(*diskLayer); ok { nodes += disk.size() @@ -478,19 +514,6 @@ func (db *Database) Initialized(genesisRoot common.Hash) bool { return inited } -// SetBufferSize sets the node buffer size to the provided value(in bytes). -func (db *Database) SetBufferSize(size int) error { - db.lock.Lock() - defer db.lock.Unlock() - - if size > maxBufferSize { - log.Info("Capped node buffer size", "provided", common.StorageSize(size), "adjusted", common.StorageSize(maxBufferSize)) - size = maxBufferSize - } - db.bufferSize = size - return db.tree.bottom().setBufferSize(db.bufferSize) -} - // modifyAllowed returns the indicator if mutation is allowed. This function // assumes the db.lock is already held. func (db *Database) modifyAllowed() error { @@ -532,3 +555,15 @@ func (db *Database) StorageHistory(address common.Address, slot common.Hash, sta func (db *Database) HistoryRange() (uint64, uint64, error) { return historyRange(db.freezer) } + +// AccountIterator creates a new account iterator for the specified root hash and +// seeks to a starting account hash. +func (db *Database) AccountIterator(root common.Hash, seek common.Hash) (AccountIterator, error) { + return newFastAccountIterator(db, root, seek) +} + +// StorageIterator creates a new storage iterator for the specified root hash and +// account. The iterator will be moved to the specific start position. +func (db *Database) StorageIterator(root common.Hash, account common.Hash, seek common.Hash) (StorageIterator, error) { + return newFastStorageIterator(db, root, account, seek) +} diff --git a/triedb/pathdb/database_test.go b/triedb/pathdb/database_test.go index f667944784..648230df15 100644 --- a/triedb/pathdb/database_test.go +++ b/triedb/pathdb/database_test.go @@ -31,7 +31,6 @@ import ( "github.com/ethereum/go-ethereum/rlp" "github.com/ethereum/go-ethereum/trie" "github.com/ethereum/go-ethereum/trie/trienode" - "github.com/ethereum/go-ethereum/trie/triestate" "github.com/holiman/uint256" ) @@ -108,9 +107,9 @@ func newTester(t *testing.T, historyLimit uint64) *tester { var ( disk, _ = rawdb.NewDatabaseWithFreezer(rawdb.NewMemoryDatabase(), t.TempDir(), "", false) db = New(disk, &Config{ - StateHistory: historyLimit, - CleanCacheSize: 16 * 1024, - DirtyCacheSize: 16 * 1024, + StateHistory: historyLimit, + CleanCacheSize: 16 * 1024, + WriteBufferSize: 16 * 1024, }, false) obj = &tester{ db: db, @@ -217,7 +216,7 @@ func (t *tester) clearStorage(ctx *genctx, addr common.Address, root common.Hash return root } -func (t *tester) generate(parent common.Hash) (common.Hash, *trienode.MergedNodeSet, *triestate.Set) { +func (t *tester) generate(parent common.Hash) (common.Hash, *trienode.MergedNodeSet, *StateSetWithOrigin) { var ( ctx = newCtx(parent) dirties = make(map[common.Hash]struct{}) @@ -310,7 +309,7 @@ func (t *tester) generate(parent common.Hash) (common.Hash, *trienode.MergedNode delete(t.storages, addrHash) } } - return root, ctx.nodes, triestate.New(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 6b87883482..c06026b6ca 100644 --- a/triedb/pathdb/difflayer.go +++ b/triedb/pathdb/difflayer.go @@ -22,8 +22,6 @@ import ( "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/log" - "github.com/ethereum/go-ethereum/trie/trienode" - "github.com/ethereum/go-ethereum/trie/triestate" ) // diffLayer represents a collection of modifications made to the in-memory tries @@ -33,45 +31,29 @@ import ( // made to the state, that have not yet graduated into a semi-immutable state. type diffLayer struct { // Immutables - root common.Hash // Root hash to which this layer diff belongs to - id uint64 // Corresponding state id - block uint64 // Associated block number - nodes map[common.Hash]map[string]*trienode.Node // Cached trie nodes indexed by owner and path - states *triestate.Set // Associated state change set for building history - memory uint64 // Approximate guess as to how much memory we use + root common.Hash // Root hash to which this layer diff belongs to + id uint64 // Corresponding state id + block uint64 // Associated block number + nodes *nodeSet // Cached trie nodes indexed by owner and path + states *StateSetWithOrigin // Associated state changes along with origin value parent layer // Parent layer modified by this one, never nil, **can be changed** lock sync.RWMutex // Lock used to protect parent } // newDiffLayer creates a new diff layer on top of an existing layer. -func newDiffLayer(parent layer, root common.Hash, id uint64, block uint64, nodes map[common.Hash]map[string]*trienode.Node, states *triestate.Set) *diffLayer { - var ( - size int64 - count int - ) +func newDiffLayer(parent layer, root common.Hash, id uint64, block uint64, nodes *nodeSet, states *StateSetWithOrigin) *diffLayer { dl := &diffLayer{ root: root, id: id, block: block, + parent: parent, nodes: nodes, states: states, - parent: parent, } - for _, subset := range nodes { - for path, n := range subset { - dl.memory += uint64(n.Size() + len(path)) - size += int64(len(n.Blob) + len(path)) - } - count += len(subset) - } - if states != nil { - dl.memory += uint64(states.Size()) - } - dirtyWriteMeter.Mark(size) - diffLayerNodesMeter.Mark(int64(count)) - diffLayerBytesMeter.Mark(int64(dl.memory)) - log.Debug("Created new diff layer", "id", id, "block", block, "nodes", count, "size", common.StorageSize(dl.memory)) + 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 } @@ -104,23 +86,72 @@ func (dl *diffLayer) node(owner common.Hash, path []byte, depth int) ([]byte, co defer dl.lock.RUnlock() // If the trie node is known locally, return it - subset, ok := dl.nodes[owner] + n, ok := dl.nodes.node(owner, path) if ok { - n, ok := subset[string(path)] - if ok { - dirtyHitMeter.Mark(1) - dirtyNodeHitDepthHist.Update(int64(depth)) - dirtyReadMeter.Mark(int64(len(n.Blob))) - return n.Blob, n.Hash, &nodeLoc{loc: locDiffLayer, depth: depth}, nil - } + dirtyNodeHitMeter.Mark(1) + dirtyNodeHitDepthHist.Update(int64(depth)) + dirtyNodeReadMeter.Mark(int64(len(n.Blob))) + return n.Blob, n.Hash, &nodeLoc{loc: locDiffLayer, depth: depth}, nil } // Trie node unknown to this layer, resolve from parent return dl.parent.node(owner, path, 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 map[common.Hash]map[string]*trienode.Node, states *triestate.Set) *diffLayer { +func (dl *diffLayer) update(root common.Hash, id uint64, block uint64, nodes *nodeSet, states *StateSetWithOrigin) *diffLayer { return newDiffLayer(dl, root, id, block, nodes, states) } @@ -145,6 +176,11 @@ func (dl *diffLayer) persist(force bool) (layer, error) { return diffToDisk(dl, force) } +// size returns the approximate memory size occupied by this diff layer. +func (dl *diffLayer) size() uint64 { + return dl.nodes.size + dl.states.size +} + // diffToDisk merges a bottom-most diff into the persistent disk layer underneath // it. The method will panic if called onto a non-bottom-most diff layer. func diffToDisk(layer *diffLayer, force bool) (layer, error) { diff --git a/triedb/pathdb/difflayer_test.go b/triedb/pathdb/difflayer_test.go index 1e93a3f892..7176d9964d 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: newNodeBuffer(DefaultBufferSize, nil, 0), + buffer: newBuffer(defaultBufferSize, 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, nodes, 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, nodes, 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,8 +156,7 @@ func BenchmarkJournal(b *testing.B) { ) nodes[common.Hash{}][string(path)] = node } - // TODO(rjl493456442) a non-nil state set is expected. - return newDiffLayer(parent, common.Hash{}, 0, 0, nodes, nil) + 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 eadcfacef7..003431b19b 100644 --- a/triedb/pathdb/disklayer.go +++ b/triedb/pathdb/disklayer.go @@ -17,6 +17,7 @@ package pathdb import ( + "errors" "fmt" "sync" @@ -25,8 +26,6 @@ import ( "github.com/ethereum/go-ethereum/core/rawdb" "github.com/ethereum/go-ethereum/crypto" "github.com/ethereum/go-ethereum/log" - "github.com/ethereum/go-ethereum/trie/trienode" - "github.com/ethereum/go-ethereum/trie/triestate" ) // diskLayer is a low level persistent layer built on top of a key-value store. @@ -34,25 +33,25 @@ type diskLayer struct { root common.Hash // Immutable, root hash to which this layer was made for id uint64 // Immutable, corresponding state id db *Database // Path-based trie database - cleans *fastcache.Cache // GC friendly memory cache of clean node RLPs - buffer *nodebuffer // Node buffer to aggregate writes + nodes *fastcache.Cache // GC friendly memory cache of clean nodes + buffer *buffer // 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 } // newDiskLayer creates a new disk layer based on the passing arguments. -func newDiskLayer(root common.Hash, id uint64, db *Database, cleans *fastcache.Cache, buffer *nodebuffer) *diskLayer { +func newDiskLayer(root common.Hash, id uint64, db *Database, nodes *fastcache.Cache, buffer *buffer) *diskLayer { // Initialize a clean cache if the memory allowance is not zero // or reuse the provided cache if it is not nil (inherited from // the original disk layer). - if cleans == nil && db.config.CleanCacheSize != 0 { - cleans = fastcache.New(db.config.CleanCacheSize) + if nodes == nil && db.config.CleanCacheSize != 0 { + nodes = fastcache.New(db.config.CleanCacheSize) } return &diskLayer{ root: root, id: id, db: db, - cleans: cleans, + nodes: nodes, buffer: buffer, } } @@ -108,25 +107,25 @@ func (dl *diskLayer) node(owner common.Hash, path []byte, depth int) ([]byte, co // layer as stale. n, found := dl.buffer.node(owner, path) if found { - dirtyHitMeter.Mark(1) - dirtyReadMeter.Mark(int64(len(n.Blob))) + dirtyNodeHitMeter.Mark(1) + dirtyNodeReadMeter.Mark(int64(len(n.Blob))) dirtyNodeHitDepthHist.Update(int64(depth)) return n.Blob, n.Hash, &nodeLoc{loc: locDirtyCache, depth: depth}, nil } - dirtyMissMeter.Mark(1) + dirtyNodeMissMeter.Mark(1) // Try to retrieve the trie node from the clean memory cache h := newHasher() defer h.release() - key := cacheKey(owner, path) - if dl.cleans != nil { - if blob := dl.cleans.Get(nil, key); len(blob) > 0 { - cleanHitMeter.Mark(1) - cleanReadMeter.Mark(int64(len(blob))) + key := nodeCacheKey(owner, path) + if dl.nodes != nil { + if blob := dl.nodes.Get(nil, key); len(blob) > 0 { + cleanNodeHitMeter.Mark(1) + cleanNodeReadMeter.Mark(int64(len(blob))) return blob, h.hash(blob), &nodeLoc{loc: locCleanCache, depth: depth}, nil } - cleanMissMeter.Mark(1) + cleanNodeMissMeter.Mark(1) } // Try to retrieve the trie node from the disk. var blob []byte @@ -135,16 +134,85 @@ func (dl *diskLayer) node(owner common.Hash, path []byte, depth int) ([]byte, co } else { blob = rawdb.ReadStorageTrieNode(dl.db.diskdb, owner, path) } - if dl.cleans != nil && len(blob) > 0 { - dl.cleans.Set(key, blob) - cleanWriteMeter.Mark(int64(len(blob))) + if dl.nodes != nil && len(blob) > 0 { + dl.nodes.Set(key, blob) + cleanNodeWriteMeter.Mark(int64(len(blob))) } 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 map[common.Hash]map[string]*trienode.Node, states *triestate.Set) *diffLayer { +func (dl *diskLayer) update(root common.Hash, id uint64, block uint64, nodes *nodeSet, states *StateSetWithOrigin) *diffLayer { return newDiffLayer(dl, root, id, block, nodes, states) } @@ -190,21 +258,23 @@ func (dl *diskLayer) commit(bottom *diffLayer, force bool) (*diskLayer, error) { } rawdb.WriteStateID(dl.db.diskdb, bottom.rootHash(), bottom.stateID()) - // Construct a new disk layer by merging the nodes from the provided diff - // layer, and flush the content in disk layer if there are too many nodes - // cached. The clean cache is inherited from the original disk layer. - ndl := newDiskLayer(bottom.root, bottom.stateID(), dl.db, dl.cleans, dl.buffer.commit(bottom.nodes)) - // 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 } - if err := ndl.buffer.flush(ndl.db.diskdb, ndl.db.freezer, ndl.cleans, ndl.id, force); err != nil { - return nil, err + // 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 combined.full() || force { + if err := combined.flush(dl.db.diskdb, dl.db.freezer, dl.nodes, bottom.stateID()); err != nil { + return nil, err + } } + ndl := newDiskLayer(bottom.root, bottom.stateID(), dl.db, dl.nodes, combined) + // To remove outdated history objects from the end, we set the 'tail' parameter // to 'oldest-1' due to the offset between the freezer index and the history ID. if overflow { @@ -225,6 +295,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. @@ -244,30 +332,19 @@ 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 } } else { batch := dl.db.diskdb.NewBatch() - writeNodes(batch, nodes, dl.cleans) + writeNodes(batch, nodes, dl.nodes) rawdb.WritePersistentStateID(batch, dl.id-1) if err := batch.Write(); err != nil { log.Crit("Failed to write states", "err", err) } } - return newDiskLayer(h.meta.parent, dl.id-1, dl.db, dl.cleans, dl.buffer), nil -} - -// setBufferSize sets the node buffer size to the provided value. -func (dl *diskLayer) setBufferSize(size int) error { - dl.lock.RLock() - defer dl.lock.RUnlock() - - if dl.stale { - return errSnapshotStale - } - return dl.buffer.setSize(size, dl.db.diskdb, dl.db.freezer, dl.cleans, dl.id) + return newDiskLayer(h.meta.parent, dl.id-1, dl.db, dl.nodes, dl.buffer), nil } // size returns the approximate size of cached nodes in the disk layer. @@ -278,7 +355,7 @@ func (dl *diskLayer) size() common.StorageSize { if dl.stale { return 0 } - return common.StorageSize(dl.buffer.size) + return common.StorageSize(dl.buffer.size()) } // resetCache releases the memory held by clean cache to prevent memory leak. @@ -286,12 +363,12 @@ func (dl *diskLayer) resetCache() { dl.lock.RLock() defer dl.lock.RUnlock() - // Stale disk layer loses the ownership of clean cache. + // Stale disk layer loses the ownership of clean caches. if dl.stale { return } - if dl.cleans != nil { - dl.cleans.Reset() + if dl.nodes != nil { + dl.nodes.Reset() } } diff --git a/triedb/pathdb/execute.go b/triedb/pathdb/execute.go index 9074e4debf..e24d0710f3 100644 --- a/triedb/pathdb/execute.go +++ b/triedb/pathdb/execute.go @@ -43,7 +43,7 @@ type context struct { // apply processes the given state diffs, updates the corresponding post-state // and returns the trie nodes that have been modified. -func apply(db database.Database, prevRoot common.Hash, postRoot common.Hash, accounts map[common.Address][]byte, storages map[common.Address]map[common.Hash][]byte) (map[common.Hash]map[string]*trienode.Node, error) { +func apply(db database.NodeDatabase, prevRoot common.Hash, postRoot common.Hash, accounts map[common.Address][]byte, storages map[common.Address]map[common.Hash][]byte) (map[common.Hash]map[string]*trienode.Node, error) { tr, err := trie.New(trie.TrieID(postRoot), db) if err != nil { return nil, err @@ -80,7 +80,7 @@ func apply(db database.Database, prevRoot common.Hash, postRoot common.Hash, acc // updateAccount the account was present in prev-state, and may or may not // existent in post-state. Apply the reverse diff and verify if the storage // root matches the one in prev-state account. -func updateAccount(ctx *context, db database.Database, addr common.Address) error { +func updateAccount(ctx *context, db database.NodeDatabase, addr common.Address) error { // The account was present in prev-state, decode it from the // 'slim-rlp' format bytes. h := newHasher() @@ -141,7 +141,7 @@ func updateAccount(ctx *context, db database.Database, addr common.Address) erro // deleteAccount the account was not present in prev-state, and is expected // to be existent in post-state. Apply the reverse diff and verify if the // account and storage is wiped out correctly. -func deleteAccount(ctx *context, db database.Database, addr common.Address) error { +func deleteAccount(ctx *context, db database.NodeDatabase, addr common.Address) error { // The account must be existent in post-state, load the account. h := newHasher() defer h.release() diff --git a/triedb/pathdb/flush.go b/triedb/pathdb/flush.go new file mode 100644 index 0000000000..baa0bfb292 --- /dev/null +++ b/triedb/pathdb/flush.go @@ -0,0 +1,65 @@ +// 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 ( + "github.com/VictoriaMetrics/fastcache" + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/core/rawdb" + "github.com/ethereum/go-ethereum/ethdb" + "github.com/ethereum/go-ethereum/trie/trienode" +) + +// nodeCacheKey constructs the unique key of clean cache. The assumption is held +// that zero address does not have any associated storage slots. +func nodeCacheKey(owner common.Hash, path []byte) []byte { + if owner == (common.Hash{}) { + return path + } + return append(owner.Bytes(), path...) +} + +// writeNodes writes the trie nodes into the provided database batch. +// Note this function will also inject all the newly written nodes +// into clean cache. +func writeNodes(batch ethdb.Batch, nodes map[common.Hash]map[string]*trienode.Node, clean *fastcache.Cache) (total int) { + for owner, subset := range nodes { + for path, n := range subset { + if n.IsDeleted() { + if owner == (common.Hash{}) { + rawdb.DeleteAccountTrieNode(batch, []byte(path)) + } else { + rawdb.DeleteStorageTrieNode(batch, owner, []byte(path)) + } + if clean != nil { + clean.Del(nodeCacheKey(owner, []byte(path))) + } + } else { + if owner == (common.Hash{}) { + rawdb.WriteAccountTrieNode(batch, []byte(path), n.Blob) + } else { + rawdb.WriteStorageTrieNode(batch, owner, []byte(path), n.Blob) + } + if clean != nil { + clean.Set(nodeCacheKey(owner, []byte(path)), n.Blob) + } + } + } + total += len(subset) + } + return total +} diff --git a/triedb/pathdb/history.go b/triedb/pathdb/history.go index d77f7aa04d..e1cd981153 100644 --- a/triedb/pathdb/history.go +++ b/triedb/pathdb/history.go @@ -28,7 +28,6 @@ import ( "github.com/ethereum/go-ethereum/core/rawdb" "github.com/ethereum/go-ethereum/ethdb" "github.com/ethereum/go-ethereum/log" - "github.com/ethereum/go-ethereum/trie/triestate" "golang.org/x/exp/maps" ) @@ -243,14 +242,14 @@ type history struct { } // newHistory constructs the state history object with provided state change set. -func newHistory(root common.Hash, parent common.Hash, block uint64, states *triestate.Set) *history { +func newHistory(root common.Hash, parent common.Hash, block uint64, accounts map[common.Address][]byte, storages map[common.Address]map[common.Hash][]byte) *history { var ( - accountList = maps.Keys(states.Accounts) + accountList = maps.Keys(accounts) storageList = make(map[common.Address][]common.Hash) ) slices.SortFunc(accountList, common.Address.Cmp) - for addr, slots := range states.Storages { + for addr, slots := range storages { slist := maps.Keys(slots) slices.SortFunc(slist, common.Hash.Cmp) storageList[addr] = slist @@ -262,9 +261,9 @@ func newHistory(root common.Hash, parent common.Hash, block uint64, states *trie root: root, block: block, }, - accounts: states.Accounts, + accounts: accounts, accountList: accountList, - storages: states.Storages, + storages: storages, storageList: storageList, } } @@ -499,7 +498,7 @@ func writeHistory(writer ethdb.AncientWriter, dl *diffLayer) error { } var ( start = time.Now() - history = newHistory(dl.rootHash(), dl.parentLayer().rootHash(), dl.block, dl.states) + history = newHistory(dl.rootHash(), dl.parentLayer().rootHash(), dl.block, dl.states.accountOrigin, dl.states.storageOrigin) ) accountData, storageData, accountIndex, storageIndex := history.encode() dataSize := common.StorageSize(len(accountData) + len(storageData)) diff --git a/triedb/pathdb/history_test.go b/triedb/pathdb/history_test.go index 586f907fe4..d430706dee 100644 --- a/triedb/pathdb/history_test.go +++ b/triedb/pathdb/history_test.go @@ -28,11 +28,10 @@ import ( "github.com/ethereum/go-ethereum/ethdb" "github.com/ethereum/go-ethereum/internal/testrand" "github.com/ethereum/go-ethereum/rlp" - "github.com/ethereum/go-ethereum/trie/triestate" ) // randomStateSet generates a random state change set. -func randomStateSet(n int) *triestate.Set { +func randomStateSet(n int) (map[common.Address][]byte, map[common.Address]map[common.Hash][]byte) { var ( accounts = make(map[common.Address][]byte) storages = make(map[common.Address]map[common.Hash][]byte) @@ -47,11 +46,12 @@ func randomStateSet(n int) *triestate.Set { account := generateAccount(types.EmptyRootHash) accounts[addr] = types.SlimAccountRLP(account) } - return triestate.New(accounts, storages) + return accounts, storages } func makeHistory() *history { - return newHistory(testrand.Hash(), types.EmptyRootHash, 0, randomStateSet(3)) + accounts, storages := randomStateSet(3) + return newHistory(testrand.Hash(), types.EmptyRootHash, 0, accounts, storages) } func makeHistories(n int) []*history { @@ -61,7 +61,8 @@ func makeHistories(n int) []*history { ) for i := 0; i < n; i++ { root := testrand.Hash() - h := newHistory(root, parent, uint64(i), randomStateSet(3)) + accounts, storages := randomStateSet(3) + h := newHistory(root, parent, uint64(i), accounts, storages) parent = root result = append(result, h) } diff --git a/triedb/pathdb/holdable_iterator.go b/triedb/pathdb/holdable_iterator.go new file mode 100644 index 0000000000..1f8e6b7068 --- /dev/null +++ b/triedb/pathdb/holdable_iterator.go @@ -0,0 +1,97 @@ +// 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 ( + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/ethdb" +) + +// holdableIterator is a wrapper of underlying database iterator. It extends +// the basic iterator interface by adding Hold which can hold the element +// locally where the iterator is currently located and serve it up next time. +type holdableIterator struct { + it ethdb.Iterator + key []byte + val []byte + atHeld bool +} + +// newHoldableIterator initializes the holdableIterator with the given iterator. +func newHoldableIterator(it ethdb.Iterator) *holdableIterator { + return &holdableIterator{it: it} +} + +// Hold holds the element locally where the iterator is currently located which +// can be served up next time. +func (it *holdableIterator) Hold() { + if it.it.Key() == nil { + return // nothing to hold + } + it.key = common.CopyBytes(it.it.Key()) + it.val = common.CopyBytes(it.it.Value()) + it.atHeld = false +} + +// Next moves the iterator to the next key/value pair. It returns whether the +// iterator is exhausted. +func (it *holdableIterator) Next() bool { + if !it.atHeld && it.key != nil { + it.atHeld = true + } else if it.atHeld { + it.atHeld = false + it.key = nil + it.val = nil + } + if it.key != nil { + return true // shifted to locally held value + } + return it.it.Next() +} + +// Error returns any accumulated error. Exhausting all the key/value pairs +// is not considered to be an error. +func (it *holdableIterator) Error() error { return it.it.Error() } + +// Release releases associated resources. Release should always succeed and can +// be called multiple times without causing error. +func (it *holdableIterator) Release() { + it.atHeld = false + it.key = nil + it.val = nil + it.it.Release() +} + +// Key returns the key of the current key/value pair, or nil if done. The caller +// should not modify the contents of the returned slice, and its contents may +// change on the next call to Next. +func (it *holdableIterator) Key() []byte { + if it.key != nil { + return it.key + } + return it.it.Key() +} + +// Value returns the value of the current key/value pair, or nil if done. The +// caller should not modify the contents of the returned slice, and its contents +// may change on the next call to Next. +func (it *holdableIterator) Value() []byte { + if it.val != nil { + return it.val + } + return it.it.Value() +} diff --git a/triedb/pathdb/holdable_iterator_test.go b/triedb/pathdb/holdable_iterator_test.go new file mode 100644 index 0000000000..2abc92e154 --- /dev/null +++ b/triedb/pathdb/holdable_iterator_test.go @@ -0,0 +1,176 @@ +// 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" + "testing" + + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/core/rawdb" + "github.com/ethereum/go-ethereum/ethdb" + "github.com/ethereum/go-ethereum/ethdb/memorydb" +) + +func TestIteratorHold(t *testing.T) { + // Create the key-value data store + var ( + content = map[string]string{"k1": "v1", "k2": "v2", "k3": "v3"} + order = []string{"k1", "k2", "k3"} + db = rawdb.NewMemoryDatabase() + ) + for key, val := range content { + if err := db.Put([]byte(key), []byte(val)); err != nil { + t.Fatalf("failed to insert item %s:%s into database: %v", key, val, err) + } + } + // Iterate over the database with the given configs and verify the results + it, idx := newHoldableIterator(db.NewIterator(nil, nil)), 0 + + // Nothing should be affected for calling Discard on non-initialized iterator + it.Hold() + + for it.Next() { + if len(content) <= idx { + t.Errorf("more items than expected: checking idx=%d (key %q), expecting len=%d", idx, it.Key(), len(order)) + break + } + if !bytes.Equal(it.Key(), []byte(order[idx])) { + t.Errorf("item %d: key mismatch: have %s, want %s", idx, string(it.Key()), order[idx]) + } + if !bytes.Equal(it.Value(), []byte(content[order[idx]])) { + t.Errorf("item %d: value mismatch: have %s, want %s", idx, string(it.Value()), content[order[idx]]) + } + // Should be safe to call discard multiple times + it.Hold() + it.Hold() + + // Shift iterator to the discarded element + it.Next() + if !bytes.Equal(it.Key(), []byte(order[idx])) { + t.Errorf("item %d: key mismatch: have %s, want %s", idx, string(it.Key()), order[idx]) + } + if !bytes.Equal(it.Value(), []byte(content[order[idx]])) { + t.Errorf("item %d: value mismatch: have %s, want %s", idx, string(it.Value()), content[order[idx]]) + } + + // Discard/Next combo should work always + it.Hold() + it.Next() + if !bytes.Equal(it.Key(), []byte(order[idx])) { + t.Errorf("item %d: key mismatch: have %s, want %s", idx, string(it.Key()), order[idx]) + } + if !bytes.Equal(it.Value(), []byte(content[order[idx]])) { + t.Errorf("item %d: value mismatch: have %s, want %s", idx, string(it.Value()), content[order[idx]]) + } + idx++ + } + if err := it.Error(); err != nil { + t.Errorf("iteration failed: %v", err) + } + if idx != len(order) { + t.Errorf("iteration terminated prematurely: have %d, want %d", idx, len(order)) + } + db.Close() +} + +func TestReopenIterator(t *testing.T) { + var ( + content = map[common.Hash]string{ + common.HexToHash("a1"): "v1", + common.HexToHash("a2"): "v2", + common.HexToHash("a3"): "v3", + common.HexToHash("a4"): "v4", + common.HexToHash("a5"): "v5", + common.HexToHash("a6"): "v6", + } + order = []common.Hash{ + common.HexToHash("a1"), + common.HexToHash("a2"), + common.HexToHash("a3"), + common.HexToHash("a4"), + common.HexToHash("a5"), + common.HexToHash("a6"), + } + db = rawdb.NewMemoryDatabase() + + reopen = func(db ethdb.KeyValueStore, iter *holdableIterator) *holdableIterator { + if !iter.Next() { + iter.Release() + return newHoldableIterator(memorydb.New().NewIterator(nil, nil)) + } + next := iter.Key() + iter.Release() + return newHoldableIterator(db.NewIterator(rawdb.SnapshotAccountPrefix, next[1:])) + } + ) + for key, val := range content { + rawdb.WriteAccountSnapshot(db, key, []byte(val)) + } + checkVal := func(it *holdableIterator, index int) { + if !bytes.Equal(it.Key(), append(rawdb.SnapshotAccountPrefix, order[index].Bytes()...)) { + t.Fatalf("Unexpected data entry key, want %v got %v", order[index], it.Key()) + } + if !bytes.Equal(it.Value(), []byte(content[order[index]])) { + t.Fatalf("Unexpected data entry key, want %v got %v", []byte(content[order[index]]), it.Value()) + } + } + // Iterate over the database with the given configs and verify the results + dbIter := db.NewIterator(rawdb.SnapshotAccountPrefix, nil) + iter, idx := newHoldableIterator(rawdb.NewKeyLengthIterator(dbIter, 1+common.HashLength)), -1 + + idx++ + iter.Next() + checkVal(iter, idx) + + iter = reopen(db, iter) + idx++ + iter.Next() + checkVal(iter, idx) + + // reopen twice + iter = reopen(db, iter) + iter = reopen(db, iter) + idx++ + iter.Next() + checkVal(iter, idx) + + // reopen iterator with held value + iter.Next() + iter.Hold() + iter = reopen(db, iter) + idx++ + iter.Next() + checkVal(iter, idx) + + // reopen twice iterator with held value + iter.Next() + iter.Hold() + iter = reopen(db, iter) + iter = reopen(db, iter) + idx++ + iter.Next() + checkVal(iter, idx) + + // shift to the end and reopen + iter.Next() // the end + iter = reopen(db, iter) + iter.Next() + if iter.Key() != nil { + t.Fatal("Unexpected iterated entry") + } +} diff --git a/triedb/pathdb/iterator.go b/triedb/pathdb/iterator.go new file mode 100644 index 0000000000..980f228cf5 --- /dev/null +++ b/triedb/pathdb/iterator.go @@ -0,0 +1,369 @@ +// 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" + "fmt" + "sort" + + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/core/rawdb" + "github.com/ethereum/go-ethereum/ethdb" +) + +// Iterator is an iterator to step over all the accounts or the specific +// storage in a snapshot which may or may not be composed of multiple layers. +type Iterator interface { + // Next steps the iterator forward one element, returning false if exhausted, + // or an error if iteration failed for some reason (e.g. root being iterated + // becomes stale and garbage collected). + Next() bool + + // Error returns any failure that occurred during iteration, which might have + // caused a premature iteration exit (e.g. layer stack becoming stale). + Error() error + + // Hash returns the hash of the account or storage slot the iterator is + // currently at. + Hash() common.Hash + + // Release releases associated resources. Release should always succeed and + // can be called multiple times without causing error. + Release() +} + +// AccountIterator is an iterator to step over all the accounts in a snapshot, +// which may or may not be composed of multiple layers. +type AccountIterator interface { + Iterator + + // Account returns the RLP encoded slim account the iterator is currently at. + // An error will be returned if the iterator becomes invalid + Account() []byte +} + +// StorageIterator is an iterator to step over the specific storage in a snapshot, +// which may or may not be composed of multiple layers. +type StorageIterator interface { + Iterator + + // Slot returns the storage slot the iterator is currently at. An error will + // be returned if the iterator becomes invalid + Slot() []byte +} + +type ( + // loadAccount is the function to retrieve the account from the associated + // layer. An error will be returned if the associated layer is stale. + loadAccount func(hash common.Hash) ([]byte, error) + + // loadStorage is the function to retrieve the storage slot from the associated + // layer. An error will be returned if the associated layer is stale. + loadStorage func(addrHash common.Hash, slotHash common.Hash) ([]byte, error) +) + +// diffAccountIterator is an account iterator that steps over the accounts (both +// live and deleted) contained within a state set. Higher order iterators will +// use the deleted accounts to skip deeper iterators. +// +// This iterator could be created from the diff layer or the disk layer (the +// aggregated state buffer). +type diffAccountIterator struct { + curHash common.Hash // The current hash the iterator is positioned on + keys []common.Hash // Keys left in the layer to iterate + fail error // Any failures encountered (stale) + loadFn loadAccount // Function to retrieve the account from with supplied hash +} + +// newDiffAccountIterator creates an account iterator over the given state set. +func newDiffAccountIterator(seek common.Hash, states *stateSet, fn loadAccount) AccountIterator { + // Seek out the requested starting account + hashes := states.accountList() + index := sort.Search(len(hashes), func(i int) bool { + return bytes.Compare(seek[:], hashes[i][:]) <= 0 + }) + // Assemble and returned the already seeked iterator + return &diffAccountIterator{ + keys: hashes[index:], + loadFn: fn, + } +} + +// Next steps the iterator forward one element, returning false if exhausted. +func (it *diffAccountIterator) Next() bool { + // If the iterator was already stale, consider it a programmer error. Although + // we could just return false here, triggering this path would probably mean + // somebody forgot to check for Error, so lets blow up instead of undefined + // behavior that's hard to debug. + if it.fail != nil { + panic(fmt.Sprintf("called Next of failed iterator: %v", it.fail)) + } + // Stop iterating if all keys were exhausted + if len(it.keys) == 0 { + return false + } + // 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 +} + +// Error returns any failure that occurred during iteration, which might have +// caused a premature iteration exit (e.g. the linked state set becoming stale). +func (it *diffAccountIterator) Error() error { + return it.fail +} + +// Hash returns the hash of the account the iterator is currently at. +func (it *diffAccountIterator) Hash() common.Hash { + return it.curHash +} + +// Account returns the RLP encoded slim account the iterator is currently at. +// This method may fail if the associated state goes stale. An error will +// be set to it.fail just in case. +// +// Note the returned account is not a copy, please don't modify it. +func (it *diffAccountIterator) Account() []byte { + blob, err := it.loadFn(it.curHash) + if err != nil { + it.fail = err + return nil + } + return blob +} + +// Release is a noop for diff account iterators as there are no held resources. +func (it *diffAccountIterator) Release() {} + +// diskAccountIterator is an account iterator that steps over the persistent +// accounts within the database. +// +// To simplify, the staleness of the persistent state is not tracked. The disk +// iterator is not intended to be used alone. It should always be wrapped with +// a diff iterator, as the bottom-most disk layer uses both the in-memory +// aggregated buffer and the persistent disk layer as the data sources. The +// staleness of the diff iterator is sufficient to invalidate the iterator pair. +type diskAccountIterator struct { + it ethdb.Iterator +} + +// newDiskAccountIterator creates an account iterator over the persistent state. +func newDiskAccountIterator(db ethdb.KeyValueStore, seek common.Hash) AccountIterator { + pos := common.TrimRightZeroes(seek[:]) + return &diskAccountIterator{ + it: db.NewIterator(rawdb.SnapshotAccountPrefix, pos), + } +} + +// Next steps the iterator forward one element, returning false if exhausted. +func (it *diskAccountIterator) Next() bool { + // If the iterator was already exhausted, don't bother + if it.it == nil { + return false + } + // Try to advance the iterator and release it if we reached the end + for { + if !it.it.Next() { + it.it.Release() + it.it = nil + return false + } + if len(it.it.Key()) == len(rawdb.SnapshotAccountPrefix)+common.HashLength { + break + } + } + return true +} + +// Error returns any failure that occurred during iteration, which might have +// caused a premature iteration exit. (e.g, any error occurred in the database) +func (it *diskAccountIterator) Error() error { + if it.it == nil { + return nil // Iterator is exhausted and released + } + return it.it.Error() +} + +// Hash returns the hash of the account the iterator is currently at. +func (it *diskAccountIterator) Hash() common.Hash { + return common.BytesToHash(it.it.Key()) // The prefix will be truncated +} + +// Account returns the RLP encoded slim account the iterator is currently at. +func (it *diskAccountIterator) Account() []byte { + return it.it.Value() +} + +// Release releases the database snapshot held during iteration. +func (it *diskAccountIterator) Release() { + // The iterator is auto-released on exhaustion, so make sure it's still alive + if it.it != nil { + it.it.Release() + it.it = nil + } +} + +// diffStorageIterator is a storage iterator that steps over the specific storage +// (both live and deleted) contained within a state set. Higher order iterators +// will use the deleted slot to skip deeper iterators. +// +// This iterator could be created from the diff layer or the disk layer (the +// aggregated state buffer). +type diffStorageIterator struct { + curHash common.Hash // The current slot hash the iterator is positioned on + account common.Hash // The account hash the storage slots belonging to + keys []common.Hash // Keys left in the layer to iterate + fail error // Any failures encountered (stale) + loadFn loadStorage // Function to retrieve the storage slot from with supplied hash +} + +// newDiffStorageIterator creates a storage iterator over a single diff layer. +func newDiffStorageIterator(account common.Hash, seek common.Hash, states *stateSet, fn loadStorage) StorageIterator { + hashes := states.storageList(account) + index := sort.Search(len(hashes), func(i int) bool { + return bytes.Compare(seek[:], hashes[i][:]) <= 0 + }) + // Assemble and returned the already seeked iterator + return &diffStorageIterator{ + account: account, + keys: hashes[index:], + loadFn: fn, + } +} + +// Next steps the iterator forward one element, returning false if exhausted. +func (it *diffStorageIterator) Next() bool { + // If the iterator was already stale, consider it a programmer error. Although + // we could just return false here, triggering this path would probably mean + // somebody forgot to check for Error, so lets blow up instead of undefined + // behavior that's hard to debug. + if it.fail != nil { + panic(fmt.Sprintf("called Next of failed iterator: %v", it.fail)) + } + // Stop iterating if all keys were exhausted + if len(it.keys) == 0 { + return false + } + // 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 +} + +// Error returns any failure that occurred during iteration, which might have +// caused a premature iteration exit (e.g. the state set becoming stale). +func (it *diffStorageIterator) Error() error { + return it.fail +} + +// Hash returns the hash of the storage slot the iterator is currently at. +func (it *diffStorageIterator) Hash() common.Hash { + return it.curHash +} + +// Slot returns the raw storage slot value the iterator is currently at. +// This method may fail if the associated state goes stale. An error will +// be set to it.fail just in case. +// +// Note the returned slot is not a copy, please don't modify it. +func (it *diffStorageIterator) Slot() []byte { + storage, err := it.loadFn(it.account, it.curHash) + if err != nil { + it.fail = err + return nil + } + return storage +} + +// Release is a noop for diff account iterators as there are no held resources. +func (it *diffStorageIterator) Release() {} + +// diskStorageIterator is a storage iterator that steps over the persistent +// storage slots contained within the database. +// +// To simplify, the staleness of the persistent state is not tracked. The disk +// iterator is not intended to be used alone. It should always be wrapped with +// a diff iterator, as the bottom-most disk layer uses both the in-memory +// aggregated buffer and the persistent disk layer as the data sources. The +// staleness of the diff iterator is sufficient to invalidate the iterator pair. +type diskStorageIterator struct { + account common.Hash + it ethdb.Iterator +} + +// StorageIterator creates a storage iterator over the persistent state. +func newDiskStorageIterator(db ethdb.KeyValueStore, account common.Hash, seek common.Hash) StorageIterator { + pos := common.TrimRightZeroes(seek[:]) + return &diskStorageIterator{ + account: account, + it: db.NewIterator(append(rawdb.SnapshotStoragePrefix, account.Bytes()...), pos), + } +} + +// Next steps the iterator forward one element, returning false if exhausted. +func (it *diskStorageIterator) Next() bool { + // If the iterator was already exhausted, don't bother + if it.it == nil { + return false + } + // Try to advance the iterator and release it if we reached the end + for { + if !it.it.Next() { + it.it.Release() + it.it = nil + return false + } + if len(it.it.Key()) == len(rawdb.SnapshotStoragePrefix)+common.HashLength+common.HashLength { + break + } + } + return true +} + +// Error returns any failure that occurred during iteration, which might have +// caused a premature iteration exit (e.g. the error occurred in the database). +func (it *diskStorageIterator) Error() error { + if it.it == nil { + return nil // Iterator is exhausted and released + } + return it.it.Error() +} + +// Hash returns the hash of the storage slot the iterator is currently at. +func (it *diskStorageIterator) Hash() common.Hash { + return common.BytesToHash(it.it.Key()) // The prefix will be truncated +} + +// Slot returns the raw storage slot content the iterator is currently at. +func (it *diskStorageIterator) Slot() []byte { + return it.it.Value() +} + +// Release releases the database snapshot held during iteration. +func (it *diskStorageIterator) Release() { + // The iterator is auto-released on exhaustion, so make sure it's still alive + if it.it != nil { + it.it.Release() + it.it = nil + } +} diff --git a/triedb/pathdb/iterator_binary.go b/triedb/pathdb/iterator_binary.go new file mode 100644 index 0000000000..dec31e8f3f --- /dev/null +++ b/triedb/pathdb/iterator_binary.go @@ -0,0 +1,344 @@ +// 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" + + "github.com/ethereum/go-ethereum/common" +) + +// binaryIterator is a simplistic iterator to step over the accounts or storage +// in a snapshot, which may or may not be composed of multiple layers. Performance +// wise this iterator is slow, it's meant for cross validating the fast one. +// +// This iterator cannot be used on its own; it should be wrapped with an outer +// iterator, such as accountBinaryIterator or storageBinaryIterator. +// +// This iterator can only traverse the keys of the entries stored in the layers, +// but cannot obtain the corresponding values. Besides, the deleted entry will +// also be traversed, the outer iterator must check the emptiness before returning. +type binaryIterator struct { + a Iterator + b Iterator + aDone bool + bDone bool + k common.Hash + fail error +} + +// initBinaryAccountIterator creates a simplistic iterator to step over all the +// accounts in a slow, but easily verifiable way. Note this function is used +// for initialization, use `newBinaryAccountIterator` as the API. +func (dl *diskLayer) initBinaryAccountIterator(seek common.Hash) *binaryIterator { + // Create two iterators for state buffer and the persistent state in disk + // respectively and combine them as a binary iterator. + l := &binaryIterator{ + // The account loader function is unnecessary; the account key list + // produced by the supplied buffer alone is sufficient for iteration. + // + // The account key list for iteration is deterministic once the iterator + // is constructed, no matter the referenced disk layer is stale or not + // later. + a: newDiffAccountIterator(seek, dl.buffer.states, nil), + b: newDiskAccountIterator(dl.db.diskdb, seek), + } + l.aDone = !l.a.Next() + l.bDone = !l.b.Next() + return l +} + +// initBinaryAccountIterator creates a simplistic iterator to step over all the +// accounts in a slow, but easily verifiable way. Note this function is used +// for initialization, use `newBinaryAccountIterator` as the API. +func (dl *diffLayer) initBinaryAccountIterator(seek common.Hash) *binaryIterator { + parent, ok := dl.parent.(*diffLayer) + if !ok { + l := &binaryIterator{ + // The account loader function is unnecessary; the account key list + // produced by the supplied state set alone is sufficient for iteration. + // + // The account key list for iteration is deterministic once the iterator + // is constructed, no matter the referenced disk layer is stale or not + // later. + a: newDiffAccountIterator(seek, dl.states.stateSet, nil), + b: dl.parent.(*diskLayer).initBinaryAccountIterator(seek), + } + l.aDone = !l.a.Next() + l.bDone = !l.b.Next() + return l + } + l := &binaryIterator{ + // The account loader function is unnecessary; the account key list + // produced by the supplied state set alone is sufficient for iteration. + // + // The account key list for iteration is deterministic once the iterator + // is constructed, no matter the referenced disk layer is stale or not + // later. + a: newDiffAccountIterator(seek, dl.states.stateSet, nil), + b: parent.initBinaryAccountIterator(seek), + } + l.aDone = !l.a.Next() + l.bDone = !l.b.Next() + return l +} + +// initBinaryStorageIterator creates a simplistic iterator to step over all the +// storage slots in a slow, but easily verifiable way. Note this function is used +// for initialization, use `newBinaryStorageIterator` as the API. +func (dl *diskLayer) initBinaryStorageIterator(account common.Hash, seek common.Hash) *binaryIterator { + // Create two iterators for state buffer and the persistent state in disk + // respectively and combine them as a binary iterator. + l := &binaryIterator{ + // The storage loader function is unnecessary; the storage key list + // produced by the supplied buffer alone is sufficient for iteration. + // + // The storage key list for iteration is deterministic once the iterator + // is constructed, no matter the referenced disk layer is stale or not + // later. + a: newDiffStorageIterator(account, seek, dl.buffer.states, nil), + b: newDiskStorageIterator(dl.db.diskdb, account, seek), + } + l.aDone = !l.a.Next() + l.bDone = !l.b.Next() + return l +} + +// initBinaryStorageIterator creates a simplistic iterator to step over all the +// storage slots in a slow, but easily verifiable way. Note this function is used +// for initialization, use `newBinaryStorageIterator` as the API. +func (dl *diffLayer) initBinaryStorageIterator(account common.Hash, seek common.Hash) *binaryIterator { + parent, ok := dl.parent.(*diffLayer) + if !ok { + l := &binaryIterator{ + // The storage loader function is unnecessary; the storage key list + // produced by the supplied state set alone is sufficient for iteration. + // + // The storage key list for iteration is deterministic once the iterator + // is constructed, no matter the referenced disk layer is stale or not + // later. + a: newDiffStorageIterator(account, seek, dl.states.stateSet, nil), + b: dl.parent.(*diskLayer).initBinaryStorageIterator(account, seek), + } + l.aDone = !l.a.Next() + l.bDone = !l.b.Next() + return l + } + l := &binaryIterator{ + // The storage loader function is unnecessary; the storage key list + // produced by the supplied state set alone is sufficient for iteration. + // + // The storage key list for iteration is deterministic once the iterator + // is constructed, no matter the referenced disk layer is stale or not + // later. + a: newDiffStorageIterator(account, seek, dl.states.stateSet, nil), + b: parent.initBinaryStorageIterator(account, seek), + } + l.aDone = !l.a.Next() + l.bDone = !l.b.Next() + return l +} + +// Next advances the iterator by one element, returning false if both iterators +// are exhausted. Note that the entry pointed to by the iterator may be null +// (e.g., when an account is deleted but still accessible for iteration). +// The outer iterator must verify emptiness before terminating the iteration. +// +// There’s no need to check for errors in the two iterators, as we only iterate +// through the entries without retrieving their values. +func (it *binaryIterator) Next() bool { + if it.aDone && it.bDone { + return false + } + for { + if it.aDone { + it.k = it.b.Hash() + it.bDone = !it.b.Next() + return true + } + if it.bDone { + it.k = it.a.Hash() + it.aDone = !it.a.Next() + return true + } + nextA, nextB := it.a.Hash(), it.b.Hash() + if diff := bytes.Compare(nextA[:], nextB[:]); diff < 0 { + it.aDone = !it.a.Next() + it.k = nextA + return true + } else if diff == 0 { + // Now we need to advance one of them + it.aDone = !it.a.Next() + continue + } + it.bDone = !it.b.Next() + it.k = nextB + return true + } +} + +// Error returns any failure that occurred during iteration, which might have +// caused a premature iteration exit (e.g. snapshot stack becoming stale). +func (it *binaryIterator) Error() error { + return it.fail +} + +// Hash returns the hash of the account the iterator is currently at. +func (it *binaryIterator) Hash() common.Hash { + return it.k +} + +// Release recursively releases all the iterators in the stack. +func (it *binaryIterator) Release() { + it.a.Release() + it.b.Release() +} + +// accountBinaryIterator is a wrapper around a binary iterator that adds functionality +// to retrieve account data from the associated layer at the current position. +type accountBinaryIterator struct { + *binaryIterator + layer layer +} + +// newBinaryAccountIterator creates a simplistic account iterator to step over +// all the accounts in a slow, but easily verifiable way. +// +// nolint:all +func (dl *diskLayer) newBinaryAccountIterator(seek common.Hash) AccountIterator { + return &accountBinaryIterator{ + binaryIterator: dl.initBinaryAccountIterator(seek), + layer: dl, + } +} + +// newBinaryAccountIterator creates a simplistic account iterator to step over +// all the accounts in a slow, but easily verifiable way. +func (dl *diffLayer) newBinaryAccountIterator(seek common.Hash) AccountIterator { + return &accountBinaryIterator{ + binaryIterator: dl.initBinaryAccountIterator(seek), + layer: dl, + } +} + +// Next steps the iterator forward one element, returning false if exhausted, +// or an error if iteration failed for some reason (e.g. the linked layer is +// stale during the iteration). +func (it *accountBinaryIterator) Next() bool { + for { + if !it.binaryIterator.Next() { + return false + } + // Retrieve the account data referenced by the current iterator, the + // associated layers might be outdated due to chain progressing, + // the relative error will be set to it.fail just in case. + // + // Skip the null account which was deleted before and move to the + // next account. + if len(it.Account()) != 0 { + return true + } + // it.fail might be set if error occurs by calling it.Account(). + // Stop iteration if so. + if it.fail != nil { + return false + } + } +} + +// Account returns the RLP encoded slim account the iterator is currently at, or +// nil if the iterated snapshot stack became stale (you can check Error after +// to see if it failed or not). +// +// Note the returned account is not a copy, please don't modify it. +func (it *accountBinaryIterator) Account() []byte { + blob, err := it.layer.account(it.k, 0) + if err != nil { + it.fail = err + return nil + } + return blob +} + +// storageBinaryIterator is a wrapper around a binary iterator that adds functionality +// to retrieve storage slot data from the associated layer at the current position. +type storageBinaryIterator struct { + *binaryIterator + account common.Hash + layer layer +} + +// newBinaryStorageIterator creates a simplistic account iterator to step over +// all the storage slots in a slow, but easily verifiable way. +// +// nolint:all +func (dl *diskLayer) newBinaryStorageIterator(account common.Hash, seek common.Hash) StorageIterator { + return &storageBinaryIterator{ + binaryIterator: dl.initBinaryStorageIterator(account, seek), + account: account, + layer: dl, + } +} + +// newBinaryStorageIterator creates a simplistic account iterator to step over +// all the storage slots in a slow, but easily verifiable way. +func (dl *diffLayer) newBinaryStorageIterator(account common.Hash, seek common.Hash) StorageIterator { + return &storageBinaryIterator{ + binaryIterator: dl.initBinaryStorageIterator(account, seek), + account: account, + layer: dl, + } +} + +// Next steps the iterator forward one element, returning false if exhausted, +// or an error if iteration failed for some reason (e.g. the linked layer is +// stale during the iteration). +func (it *storageBinaryIterator) Next() bool { + for { + if !it.binaryIterator.Next() { + return false + } + // Retrieve the storage data referenced by the current iterator, the + // associated layers might be outdated due to chain progressing, + // the relative error will be set to it.fail just in case. + // + // Skip the null storage which was deleted before and move to the + // next account. + if len(it.Slot()) != 0 { + return true + } + // it.fail might be set if error occurs by calling it.Slot(). + // Stop iteration if so. + if it.fail != nil { + return false + } + } +} + +// Slot returns the raw storage slot data the iterator is currently at, or +// nil if the iterated snapshot stack became stale (you can check Error after +// to see if it failed or not). +// +// Note the returned slot is not a copy, please don't modify it. +func (it *storageBinaryIterator) Slot() []byte { + blob, err := it.layer.storage(it.account, it.k, 0) + if err != nil { + it.fail = err + return nil + } + return blob +} diff --git a/triedb/pathdb/iterator_fast.go b/triedb/pathdb/iterator_fast.go new file mode 100644 index 0000000000..217b211fcc --- /dev/null +++ b/triedb/pathdb/iterator_fast.go @@ -0,0 +1,380 @@ +// 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" + "fmt" + "slices" + "sort" + + "github.com/ethereum/go-ethereum/common" +) + +// weightedIterator is an iterator with an assigned weight. It is used to prioritise +// which account or storage slot is the correct one if multiple iterators find the +// same one (modified in multiple consecutive blocks). +type weightedIterator struct { + it Iterator + priority int +} + +func (it *weightedIterator) Cmp(other *weightedIterator) int { + // Order the iterators primarily by the account hashes + hashI := it.it.Hash() + hashJ := other.it.Hash() + + switch bytes.Compare(hashI[:], hashJ[:]) { + case -1: + return -1 + case 1: + return 1 + } + // Same account/storage-slot in multiple layers, split by priority + if it.priority < other.priority { + return -1 + } + if it.priority > other.priority { + return 1 + } + return 0 +} + +// fastIterator is a more optimized multi-layer iterator which maintains a +// direct mapping of all iterators leading down to the bottom layer. +type fastIterator struct { + curAccount []byte + curSlot []byte + + iterators []*weightedIterator + initiated bool + account bool + fail error +} + +// newFastIterator creates a new hierarchical account or storage iterator with one +// element per diff layer. The returned combo iterator can be used to walk over +// the entire layer stack simultaneously. +func newFastIterator(db *Database, root common.Hash, account common.Hash, seek common.Hash, accountIterator bool) (*fastIterator, error) { + current := db.tree.get(root) + if current == nil { + return nil, fmt.Errorf("unknown layer: %x", root) + } + fi := &fastIterator{ + account: accountIterator, + } + for depth := 0; current != nil; depth++ { + if accountIterator { + switch dl := current.(type) { + case *diskLayer: + fi.iterators = append(fi.iterators, &weightedIterator{ + // The state set in the disk layer is mutable, and the entire state becomes stale + // if a diff layer above is merged into it. Therefore, staleness must be checked, + // and the storage slot should be retrieved with read lock protection. + it: newDiffAccountIterator(seek, dl.buffer.states, func(hash common.Hash) ([]byte, error) { + dl.lock.RLock() + defer dl.lock.RUnlock() + + if dl.stale { + return nil, errSnapshotStale + } + return dl.buffer.states.mustAccount(hash) + }), + priority: depth, + }) + fi.iterators = append(fi.iterators, &weightedIterator{ + it: newDiskAccountIterator(dl.db.diskdb, seek), + priority: depth + 1, + }) + case *diffLayer: + // The state set in diff layer is immutable and will never be stale, + // so the read lock protection is unnecessary. + fi.iterators = append(fi.iterators, &weightedIterator{ + it: newDiffAccountIterator(seek, dl.states.stateSet, dl.states.mustAccount), + priority: depth, + }) + } + } else { + switch dl := current.(type) { + case *diskLayer: + fi.iterators = append(fi.iterators, &weightedIterator{ + // The state set in the disk layer is mutable, and the entire state becomes stale + // if a diff layer above is merged into it. Therefore, staleness must be checked, + // and the storage slot should be retrieved with read lock protection. + it: newDiffStorageIterator(account, seek, dl.buffer.states, func(addrHash common.Hash, slotHash common.Hash) ([]byte, error) { + dl.lock.RLock() + defer dl.lock.RUnlock() + + if dl.stale { + return nil, errSnapshotStale + } + return dl.buffer.states.mustStorage(addrHash, slotHash) + }), + priority: depth, + }) + fi.iterators = append(fi.iterators, &weightedIterator{ + it: newDiskStorageIterator(dl.db.diskdb, account, seek), + priority: depth + 1, + }) + case *diffLayer: + // The state set in diff layer is immutable and will never be stale, + // so the read lock protection is unnecessary. + fi.iterators = append(fi.iterators, &weightedIterator{ + it: newDiffStorageIterator(account, seek, dl.states.stateSet, dl.states.mustStorage), + priority: depth, + }) + } + } + current = current.parentLayer() + } + fi.init() + return fi, nil +} + +// init walks over all the iterators and resolves any clashes between them, after +// which it prepares the stack for step-by-step iteration. +func (fi *fastIterator) init() { + // Track which account hashes are iterators positioned on + var positioned = make(map[common.Hash]int) + + // Position all iterators and track how many remain live + for i := 0; i < len(fi.iterators); i++ { + // Retrieve the first element and if it clashes with a previous iterator, + // advance either the current one or the old one. Repeat until nothing is + // clashing anymore. + it := fi.iterators[i] + for { + // If the iterator is exhausted, drop it off the end + if !it.it.Next() { + it.it.Release() + last := len(fi.iterators) - 1 + + fi.iterators[i] = fi.iterators[last] + fi.iterators[last] = nil + fi.iterators = fi.iterators[:last] + + i-- + break + } + // The iterator is still alive, check for collisions with previous ones + hash := it.it.Hash() + if other, exist := positioned[hash]; !exist { + positioned[hash] = i + break + } else { + // Iterators collide, one needs to be progressed, use priority to + // determine which. + // + // This whole else-block can be avoided, if we instead + // do an initial priority-sort of the iterators. If we do that, + // then we'll only wind up here if a lower-priority (preferred) iterator + // has the same value, and then we will always just continue. + // However, it costs an extra sort, so it's probably not better + if fi.iterators[other].priority < it.priority { + // The 'it' should be progressed + continue + } else { + // The 'other' should be progressed, swap them + it = fi.iterators[other] + fi.iterators[other], fi.iterators[i] = fi.iterators[i], fi.iterators[other] + continue + } + } + } + } + // Re-sort the entire list + slices.SortFunc(fi.iterators, func(a, b *weightedIterator) int { return a.Cmp(b) }) + fi.initiated = false +} + +// Next steps the iterator forward one element, returning false if exhausted. +func (fi *fastIterator) Next() bool { + if len(fi.iterators) == 0 { + return false + } + if !fi.initiated { + // Don't forward first time -- we had to 'Next' once in order to + // do the sorting already + fi.initiated = true + if fi.account { + fi.curAccount = fi.iterators[0].it.(AccountIterator).Account() + } else { + fi.curSlot = fi.iterators[0].it.(StorageIterator).Slot() + } + if innerErr := fi.iterators[0].it.Error(); innerErr != nil { + fi.fail = innerErr + return false + } + if fi.curAccount != nil || fi.curSlot != nil { + return true + } + // Implicit else: we've hit a nil-account or nil-slot, and need to + // fall through to the loop below to land on something non-nil + } + // If an account or a slot is deleted in one of the layers, the key will + // still be there, but the actual value will be nil. However, the iterator + // should not export nil-values (but instead simply omit the key), so we + // need to loop here until we either + // - get a non-nil value, + // - hit an error, + // - or exhaust the iterator + for { + if !fi.next(0) { + return false // exhausted + } + if fi.account { + fi.curAccount = fi.iterators[0].it.(AccountIterator).Account() + } else { + fi.curSlot = fi.iterators[0].it.(StorageIterator).Slot() + } + if innerErr := fi.iterators[0].it.Error(); innerErr != nil { + fi.fail = innerErr + return false // error + } + if fi.curAccount != nil || fi.curSlot != nil { + break // non-nil value found + } + } + return true +} + +// next handles the next operation internally and should be invoked when we know +// that two elements in the list may have the same value. +// +// For example, if the iterated hashes become [2,3,5,5,8,9,10], then we should +// invoke next(3), which will call Next on elem 3 (the second '5') and will +// cascade along the list, applying the same operation if needed. +func (fi *fastIterator) next(idx int) bool { + // If this particular iterator got exhausted, remove it and return true (the + // next one is surely not exhausted yet, otherwise it would have been removed + // already). + if it := fi.iterators[idx].it; !it.Next() { + it.Release() + + fi.iterators = append(fi.iterators[:idx], fi.iterators[idx+1:]...) + return len(fi.iterators) > 0 + } + // If there's no one left to cascade into, return + if idx == len(fi.iterators)-1 { + return true + } + // We next-ed the iterator at 'idx', now we may have to re-sort that element + var ( + cur, next = fi.iterators[idx], fi.iterators[idx+1] + curHash, nextHash = cur.it.Hash(), next.it.Hash() + ) + if diff := bytes.Compare(curHash[:], nextHash[:]); diff < 0 { + // It is still in correct place + return true + } else if diff == 0 && cur.priority < next.priority { + // So still in correct place, but we need to iterate on the next + fi.next(idx + 1) + return true + } + // At this point, the iterator is in the wrong location, but the remaining + // list is sorted. Find out where to move the item. + clash := -1 + index := sort.Search(len(fi.iterators), func(n int) bool { + // The iterator always advances forward, so anything before the old slot + // is known to be behind us, so just skip them altogether. This actually + // is an important clause since the sort order got invalidated. + if n < idx { + return false + } + if n == len(fi.iterators)-1 { + // Can always place an elem last + return true + } + nextHash := fi.iterators[n+1].it.Hash() + if diff := bytes.Compare(curHash[:], nextHash[:]); diff < 0 { + return true + } else if diff > 0 { + return false + } + // The elem we're placing it next to has the same value, + // so whichever winds up on n+1 will need further iteration + clash = n + 1 + + return cur.priority < fi.iterators[n+1].priority + }) + fi.move(idx, index) + if clash != -1 { + fi.next(clash) + } + return true +} + +// move advances an iterator to another position in the list. +func (fi *fastIterator) move(index, newpos int) { + elem := fi.iterators[index] + copy(fi.iterators[index:], fi.iterators[index+1:newpos+1]) + fi.iterators[newpos] = elem +} + +// Error returns any failure that occurred during iteration, which might have +// caused a premature iteration exit (e.g. snapshot stack becoming stale). +func (fi *fastIterator) Error() error { + return fi.fail +} + +// Hash returns the current key +func (fi *fastIterator) Hash() common.Hash { + return fi.iterators[0].it.Hash() +} + +// Account returns the current account blob. +// Note the returned account is not a copy, please don't modify it. +func (fi *fastIterator) Account() []byte { + return fi.curAccount +} + +// Slot returns the current storage slot. +// Note the returned slot is not a copy, please don't modify it. +func (fi *fastIterator) Slot() []byte { + return fi.curSlot +} + +// Release iterates over all the remaining live layer iterators and releases each +// of them individually. +func (fi *fastIterator) Release() { + for _, it := range fi.iterators { + it.it.Release() + } + fi.iterators = nil +} + +// Debug is a convenience helper during testing +func (fi *fastIterator) Debug() { + for _, it := range fi.iterators { + fmt.Printf("[p=%v v=%v] ", it.priority, it.it.Hash()[0]) + } + fmt.Println() +} + +// newFastAccountIterator creates a new hierarchical account iterator with one +// element per diff layer. The returned combo iterator can be used to walk over +// the entire snapshot diff stack simultaneously. +func newFastAccountIterator(db *Database, root common.Hash, seek common.Hash) (AccountIterator, error) { + return newFastIterator(db, root, common.Hash{}, seek, true) +} + +// newFastStorageIterator creates a new hierarchical storage iterator with one +// element per diff layer. The returned combo iterator can be used to walk over +// the entire snapshot diff stack simultaneously. +func newFastStorageIterator(db *Database, root common.Hash, account common.Hash, seek common.Hash) (StorageIterator, error) { + return newFastIterator(db, root, account, seek, false) +} diff --git a/triedb/pathdb/iterator_test.go b/triedb/pathdb/iterator_test.go new file mode 100644 index 0000000000..48b5870b5b --- /dev/null +++ b/triedb/pathdb/iterator_test.go @@ -0,0 +1,1162 @@ +// 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" + "encoding/binary" + "fmt" + "math/rand" + "testing" + + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/core/rawdb" + "github.com/ethereum/go-ethereum/core/types" + "github.com/ethereum/go-ethereum/internal/testrand" + "github.com/ethereum/go-ethereum/rlp" + "github.com/ethereum/go-ethereum/trie/trienode" + "github.com/holiman/uint256" +) + +type verifyContent int + +const ( + verifyNothing verifyContent = iota + verifyAccount + verifyStorage +) + +func verifyIterator(t *testing.T, expCount int, it Iterator, verify verifyContent) { + t.Helper() + + var ( + count = 0 + last = common.Hash{} + ) + for it.Next() { + hash := it.Hash() + if bytes.Compare(last[:], hash[:]) >= 0 { + t.Errorf("wrong order: %x >= %x", last, hash) + } + count++ + if verify == verifyAccount && len(it.(AccountIterator).Account()) == 0 { + t.Errorf("iterator returned nil-value for hash %x", hash) + } else if verify == verifyStorage && len(it.(StorageIterator).Slot()) == 0 { + t.Errorf("iterator returned nil-value for hash %x", hash) + } + last = hash + } + if count != expCount { + t.Errorf("iterator count mismatch: have %d, want %d", count, expCount) + } + if err := it.Error(); err != nil { + t.Errorf("iterator failed: %v", err) + } +} + +// randomAccount generates a random account and returns it RLP encoded. +func randomAccount() []byte { + a := &types.StateAccount{ + Balance: uint256.NewInt(rand.Uint64()), + Nonce: rand.Uint64(), + Root: testrand.Hash(), + CodeHash: types.EmptyCodeHash[:], + } + data, _ := rlp.EncodeToBytes(a) + return data +} + +// randomAccountSet generates a set of random accounts with the given strings as +// the account address hashes. +func randomAccountSet(hashes ...string) map[common.Hash][]byte { + accounts := make(map[common.Hash][]byte) + for _, hash := range hashes { + accounts[common.HexToHash(hash)] = randomAccount() + } + return accounts +} + +// randomStorageSet generates a set of random slots with the given strings as +// the slot addresses. +func randomStorageSet(accounts []string, hashes [][]string, nilStorage [][]string) map[common.Hash]map[common.Hash][]byte { + storages := make(map[common.Hash]map[common.Hash][]byte) + for index, account := range accounts { + storages[common.HexToHash(account)] = make(map[common.Hash][]byte) + + if index < len(hashes) { + hashes := hashes[index] + for _, hash := range hashes { + storages[common.HexToHash(account)][common.HexToHash(hash)] = testrand.Bytes(32) + } + } + if index < len(nilStorage) { + nils := nilStorage[index] + for _, hash := range nils { + storages[common.HexToHash(account)][common.HexToHash(hash)] = nil + } + } + } + return storages +} + +// TestAccountIteratorBasics tests some simple single-layer(diff and disk) iteration +func TestAccountIteratorBasics(t *testing.T) { + var ( + 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++ { + hash := testrand.Hash() + data := testrand.Bytes(32) + accounts[hash] = data + + if rand.Intn(2) == 0 { + accStorage := make(map[common.Hash][]byte) + accStorage[testrand.Hash()] = testrand.Bytes(32) + storage[hash] = accStorage + } + } + states := newStates(accounts, storage) + it := newDiffAccountIterator(common.Hash{}, states, nil) + verifyIterator(t, 100, it, verifyNothing) // Nil is allowed for single layer iterator + + // TODO reenable these tests once the persistent state iteration + // is implemented. + + //db := rawdb.NewMemoryDatabase() + //batch := db.NewBatch() + //states.write(db, batch, nil, nil) + //batch.Write() + //it = newDiskAccountIterator(db, common.Hash{}) + //verifyIterator(t, 100, it, verifyNothing) // Nil is allowed for single layer iterator +} + +// TestStorageIteratorBasics tests some simple single-layer(diff and disk) iteration for storage +func TestStorageIteratorBasics(t *testing.T) { + var ( + nilStorage = make(map[common.Hash]int) + accounts = make(map[common.Hash][]byte) + storage = make(map[common.Hash]map[common.Hash][]byte) + ) + // Fill some random data + for i := 0; i < 10; i++ { + hash := testrand.Hash() + accounts[hash] = testrand.Bytes(32) + + accStorage := make(map[common.Hash][]byte) + var nilstorage int + for i := 0; i < 100; i++ { + if rand.Intn(2) == 0 { + accStorage[testrand.Hash()] = testrand.Bytes(32) + } else { + accStorage[testrand.Hash()] = nil // delete slot + nilstorage += 1 + } + } + storage[hash] = accStorage + nilStorage[hash] = nilstorage + } + states := newStates(accounts, storage) + for account := range accounts { + it := newDiffStorageIterator(account, common.Hash{}, states, nil) + verifyIterator(t, 100, it, verifyNothing) // Nil is allowed for single layer iterator + } + + // TODO reenable these tests once the persistent state iteration + // is implemented. + + //db := rawdb.NewMemoryDatabase() + //batch := db.NewBatch() + //states.write(db, batch, nil, nil) + //batch.Write() + //for account := range accounts { + // it := newDiskStorageIterator(db, account, common.Hash{}) + // verifyIterator(t, 100-nilStorage[account], it, verifyNothing) // Nil is allowed for single layer iterator + //} +} + +type testIterator struct { + values []byte +} + +func newTestIterator(values ...byte) *testIterator { + return &testIterator{values} +} + +func (ti *testIterator) Seek(common.Hash) { + panic("implement me") +} + +func (ti *testIterator) Next() bool { + ti.values = ti.values[1:] + return len(ti.values) > 0 +} + +func (ti *testIterator) Error() error { + return nil +} + +func (ti *testIterator) Hash() common.Hash { + return common.BytesToHash([]byte{ti.values[0]}) +} + +func (ti *testIterator) Account() []byte { + return nil +} + +func (ti *testIterator) Slot() []byte { + return nil +} + +func (ti *testIterator) Release() {} + +func TestFastIteratorBasics(t *testing.T) { + type testCase struct { + lists [][]byte + expKeys []byte + } + for i, tc := range []testCase{ + {lists: [][]byte{{0, 1, 8}, {1, 2, 8}, {2, 9}, {4}, + {7, 14, 15}, {9, 13, 15, 16}}, + expKeys: []byte{0, 1, 2, 4, 7, 8, 9, 13, 14, 15, 16}}, + {lists: [][]byte{{0, 8}, {1, 2, 8}, {7, 14, 15}, {8, 9}, + {9, 10}, {10, 13, 15, 16}}, + expKeys: []byte{0, 1, 2, 7, 8, 9, 10, 13, 14, 15, 16}}, + } { + var iterators []*weightedIterator + for i, data := range tc.lists { + it := newTestIterator(data...) + iterators = append(iterators, &weightedIterator{it, i}) + } + fi := &fastIterator{ + iterators: iterators, + initiated: false, + } + count := 0 + for fi.Next() { + if got, exp := fi.Hash()[31], tc.expKeys[count]; exp != got { + t.Errorf("tc %d, [%d]: got %d exp %d", i, count, got, exp) + } + count++ + } + } +} + +// TestAccountIteratorTraversal tests some simple multi-layer iteration. +func TestAccountIteratorTraversal(t *testing.T) { + config := &Config{ + WriteBufferSize: 0, + } + db := New(rawdb.NewMemoryDatabase(), config, false) + // db.WaitGeneration() + + // Stack three diff layers on top with various overlaps + db.Update(common.HexToHash("0x02"), types.EmptyRootHash, 0, trienode.NewMergedNodeSet(), + NewStateSetWithOrigin(randomAccountSet("0xaa", "0xee", "0xff", "0xf0"), nil, nil, nil)) + + db.Update(common.HexToHash("0x03"), common.HexToHash("0x02"), 0, trienode.NewMergedNodeSet(), + NewStateSetWithOrigin(randomAccountSet("0xbb", "0xdd", "0xf0"), nil, nil, nil)) + + db.Update(common.HexToHash("0x04"), common.HexToHash("0x03"), 0, trienode.NewMergedNodeSet(), + NewStateSetWithOrigin(randomAccountSet("0xcc", "0xf0", "0xff"), nil, nil, nil)) + + // Verify the single and multi-layer iterators + head := db.tree.get(common.HexToHash("0x04")) + + // singleLayer: 0xcc, 0xf0, 0xff + it := newDiffAccountIterator(common.Hash{}, head.(*diffLayer).states.stateSet, nil) + verifyIterator(t, 3, it, verifyNothing) + + // binaryIterator: 0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0xf0, 0xff + verifyIterator(t, 7, head.(*diffLayer).newBinaryAccountIterator(common.Hash{}), verifyAccount) + + // fastIterator: 0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0xf0, 0xff + it, _ = db.AccountIterator(common.HexToHash("0x04"), common.Hash{}) + verifyIterator(t, 7, it, verifyAccount) + it.Release() + + // TODO reenable these tests once the persistent state iteration + // is implemented. + + // Test after persist some bottom-most layers into the disk, + // the functionalities still work. + //db.tree.cap(common.HexToHash("0x04"), 2) + + //head = db.tree.get(common.HexToHash("0x04")) + //verifyIterator(t, 7, head.(*diffLayer).newBinaryAccountIterator(), verifyAccount) + // + //it, _ = db.AccountIterator(common.HexToHash("0x04"), common.Hash{}) + //verifyIterator(t, 7, it, verifyAccount) + //it.Release() +} + +func TestStorageIteratorTraversal(t *testing.T) { + config := &Config{ + WriteBufferSize: 0, + } + db := New(rawdb.NewMemoryDatabase(), config, false) + // db.WaitGeneration() + + // Stack three diff layers on top with various overlaps + db.Update(common.HexToHash("0x02"), types.EmptyRootHash, 0, trienode.NewMergedNodeSet(), + NewStateSetWithOrigin(randomAccountSet("0xaa"), randomStorageSet([]string{"0xaa"}, [][]string{{"0x01", "0x02", "0x03"}}, nil), nil, nil)) + + db.Update(common.HexToHash("0x03"), common.HexToHash("0x02"), 0, trienode.NewMergedNodeSet(), + NewStateSetWithOrigin(randomAccountSet("0xaa"), randomStorageSet([]string{"0xaa"}, [][]string{{"0x04", "0x05", "0x06"}}, nil), nil, nil)) + + db.Update(common.HexToHash("0x04"), common.HexToHash("0x03"), 0, trienode.NewMergedNodeSet(), + NewStateSetWithOrigin(randomAccountSet("0xaa"), randomStorageSet([]string{"0xaa"}, [][]string{{"0x01", "0x02", "0x03"}}, nil), nil, nil)) + + // Verify the single and multi-layer iterators + head := db.tree.get(common.HexToHash("0x04")) + + // singleLayer: 0x1, 0x2, 0x3 + diffIter := newDiffStorageIterator(common.HexToHash("0xaa"), common.Hash{}, head.(*diffLayer).states.stateSet, nil) + verifyIterator(t, 3, diffIter, verifyNothing) + + // binaryIterator: 0x1, 0x2, 0x3, 0x4, 0x5, 0x6 + verifyIterator(t, 6, head.(*diffLayer).newBinaryStorageIterator(common.HexToHash("0xaa"), common.Hash{}), verifyStorage) + + // fastIterator: 0x1, 0x2, 0x3, 0x4, 0x5, 0x6 + it, _ := db.StorageIterator(common.HexToHash("0x04"), common.HexToHash("0xaa"), common.Hash{}) + verifyIterator(t, 6, it, verifyStorage) + it.Release() + + // TODO reenable these tests once the persistent state iteration + // is implemented. + + // Test after persist some bottom-most layers into the disk, + // the functionalities still work. + //db.tree.cap(common.HexToHash("0x04"), 2) + //verifyIterator(t, 6, head.(*diffLayer).newBinaryStorageIterator(common.HexToHash("0xaa")), verifyStorage) + // + //it, _ = db.StorageIterator(common.HexToHash("0x04"), common.HexToHash("0xaa"), common.Hash{}) + //verifyIterator(t, 6, it, verifyStorage) + //it.Release() +} + +// TestAccountIteratorTraversalValues tests some multi-layer iteration, where we +// also expect the correct values to show up. +func TestAccountIteratorTraversalValues(t *testing.T) { + config := &Config{ + WriteBufferSize: 0, + } + db := New(rawdb.NewMemoryDatabase(), config, false) + // db.WaitGeneration() + + // Create a batch of account sets to seed subsequent layers with + var ( + a = make(map[common.Hash][]byte) + b = make(map[common.Hash][]byte) + c = make(map[common.Hash][]byte) + d = make(map[common.Hash][]byte) + e = make(map[common.Hash][]byte) + f = make(map[common.Hash][]byte) + g = make(map[common.Hash][]byte) + h = make(map[common.Hash][]byte) + ) + for i := byte(2); i < 0xff; i++ { + a[common.Hash{i}] = []byte(fmt.Sprintf("layer-%d, key %d", 0, i)) + if i > 20 && i%2 == 0 { + b[common.Hash{i}] = []byte(fmt.Sprintf("layer-%d, key %d", 1, i)) + } + if i%4 == 0 { + c[common.Hash{i}] = []byte(fmt.Sprintf("layer-%d, key %d", 2, i)) + } + if i%7 == 0 { + d[common.Hash{i}] = []byte(fmt.Sprintf("layer-%d, key %d", 3, i)) + } + if i%8 == 0 { + e[common.Hash{i}] = []byte(fmt.Sprintf("layer-%d, key %d", 4, i)) + } + if i > 50 || i < 85 { + f[common.Hash{i}] = []byte(fmt.Sprintf("layer-%d, key %d", 5, i)) + } + if i%64 == 0 { + g[common.Hash{i}] = []byte(fmt.Sprintf("layer-%d, key %d", 6, i)) + } + if i%128 == 0 { + h[common.Hash{i}] = []byte(fmt.Sprintf("layer-%d, key %d", 7, i)) + } + } + // Assemble a stack of snapshots from the account layers + db.Update(common.HexToHash("0x02"), types.EmptyRootHash, 2, trienode.NewMergedNodeSet(), NewStateSetWithOrigin(a, nil, nil, nil)) + db.Update(common.HexToHash("0x03"), common.HexToHash("0x02"), 3, trienode.NewMergedNodeSet(), NewStateSetWithOrigin(b, nil, nil, nil)) + db.Update(common.HexToHash("0x04"), common.HexToHash("0x03"), 4, trienode.NewMergedNodeSet(), NewStateSetWithOrigin(c, nil, nil, nil)) + db.Update(common.HexToHash("0x05"), common.HexToHash("0x04"), 5, trienode.NewMergedNodeSet(), NewStateSetWithOrigin(d, nil, nil, nil)) + db.Update(common.HexToHash("0x06"), common.HexToHash("0x05"), 6, trienode.NewMergedNodeSet(), NewStateSetWithOrigin(e, nil, nil, nil)) + db.Update(common.HexToHash("0x07"), common.HexToHash("0x06"), 7, trienode.NewMergedNodeSet(), NewStateSetWithOrigin(f, nil, nil, nil)) + db.Update(common.HexToHash("0x08"), common.HexToHash("0x07"), 8, trienode.NewMergedNodeSet(), NewStateSetWithOrigin(g, nil, nil, nil)) + db.Update(common.HexToHash("0x09"), common.HexToHash("0x08"), 9, trienode.NewMergedNodeSet(), NewStateSetWithOrigin(h, nil, nil, nil)) + + // binaryIterator + r, _ := db.StateReader(common.HexToHash("0x09")) + head := db.tree.get(common.HexToHash("0x09")) + it := head.(*diffLayer).newBinaryAccountIterator(common.Hash{}) + for it.Next() { + hash := it.Hash() + want, err := r.(*reader).AccountRLP(hash) + if err != nil { + t.Fatalf("failed to retrieve expected account: %v", err) + } + if have := it.Account(); !bytes.Equal(want, have) { + t.Fatalf("hash %x: account mismatch: have %x, want %x", hash, have, want) + } + } + it.Release() + + // fastIterator + it, _ = db.AccountIterator(common.HexToHash("0x09"), common.Hash{}) + for it.Next() { + hash := it.Hash() + want, err := r.(*reader).AccountRLP(hash) + if err != nil { + t.Fatalf("failed to retrieve expected account: %v", err) + } + if have := it.Account(); !bytes.Equal(want, have) { + t.Fatalf("hash %x: account mismatch: have %x, want %x", hash, have, want) + } + } + it.Release() + + // TODO reenable these tests once the persistent state iteration + // is implemented. + + // Test after persist some bottom-most layers into the disk, + // the functionalities still work. + //db.tree.cap(common.HexToHash("0x09"), 2) + // + //it, _ = db.AccountIterator(common.HexToHash("0x09"), common.Hash{}) + //for it.Next() { + // hash := it.Hash() + // account, err := head.Account(hash) + // if err != nil { + // t.Fatalf("failed to retrieve expected account: %v", err) + // } + // want, _ := rlp.EncodeToBytes(account) + // if have := it.Account(); !bytes.Equal(want, have) { + // t.Fatalf("hash %x: account mismatch: have %x, want %x", hash, have, want) + // } + //} + //it.Release() +} + +func TestStorageIteratorTraversalValues(t *testing.T) { + config := &Config{ + WriteBufferSize: 0, + } + db := New(rawdb.NewMemoryDatabase(), config, false) + // db.WaitGeneration() + + wrapStorage := func(storage map[common.Hash][]byte) map[common.Hash]map[common.Hash][]byte { + return map[common.Hash]map[common.Hash][]byte{ + common.HexToHash("0xaa"): storage, + } + } + // Create a batch of storage sets to seed subsequent layers with + var ( + a = make(map[common.Hash][]byte) + b = make(map[common.Hash][]byte) + c = make(map[common.Hash][]byte) + d = make(map[common.Hash][]byte) + e = make(map[common.Hash][]byte) + f = make(map[common.Hash][]byte) + g = make(map[common.Hash][]byte) + h = make(map[common.Hash][]byte) + ) + for i := byte(2); i < 0xff; i++ { + a[common.Hash{i}] = []byte(fmt.Sprintf("layer-%d, key %d", 0, i)) + if i > 20 && i%2 == 0 { + b[common.Hash{i}] = []byte(fmt.Sprintf("layer-%d, key %d", 1, i)) + } + if i%4 == 0 { + c[common.Hash{i}] = []byte(fmt.Sprintf("layer-%d, key %d", 2, i)) + } + if i%7 == 0 { + d[common.Hash{i}] = []byte(fmt.Sprintf("layer-%d, key %d", 3, i)) + } + if i%8 == 0 { + e[common.Hash{i}] = []byte(fmt.Sprintf("layer-%d, key %d", 4, i)) + } + if i > 50 || i < 85 { + f[common.Hash{i}] = []byte(fmt.Sprintf("layer-%d, key %d", 5, i)) + } + if i%64 == 0 { + g[common.Hash{i}] = []byte(fmt.Sprintf("layer-%d, key %d", 6, i)) + } + if i%128 == 0 { + h[common.Hash{i}] = []byte(fmt.Sprintf("layer-%d, key %d", 7, i)) + } + } + // Assemble a stack of snapshots from the account layers + db.Update(common.HexToHash("0x02"), types.EmptyRootHash, 2, trienode.NewMergedNodeSet(), NewStateSetWithOrigin(randomAccountSet("0xaa"), wrapStorage(a), nil, nil)) + db.Update(common.HexToHash("0x03"), common.HexToHash("0x02"), 3, trienode.NewMergedNodeSet(), NewStateSetWithOrigin(randomAccountSet("0xaa"), wrapStorage(b), nil, nil)) + db.Update(common.HexToHash("0x04"), common.HexToHash("0x03"), 4, trienode.NewMergedNodeSet(), NewStateSetWithOrigin(randomAccountSet("0xaa"), wrapStorage(c), nil, nil)) + db.Update(common.HexToHash("0x05"), common.HexToHash("0x04"), 5, trienode.NewMergedNodeSet(), NewStateSetWithOrigin(randomAccountSet("0xaa"), wrapStorage(d), nil, nil)) + db.Update(common.HexToHash("0x06"), common.HexToHash("0x05"), 6, trienode.NewMergedNodeSet(), NewStateSetWithOrigin(randomAccountSet("0xaa"), wrapStorage(e), nil, nil)) + db.Update(common.HexToHash("0x07"), common.HexToHash("0x06"), 7, trienode.NewMergedNodeSet(), NewStateSetWithOrigin(randomAccountSet("0xaa"), wrapStorage(f), nil, nil)) + db.Update(common.HexToHash("0x08"), common.HexToHash("0x07"), 8, trienode.NewMergedNodeSet(), NewStateSetWithOrigin(randomAccountSet("0xaa"), wrapStorage(g), nil, nil)) + db.Update(common.HexToHash("0x09"), common.HexToHash("0x08"), 9, trienode.NewMergedNodeSet(), NewStateSetWithOrigin(randomAccountSet("0xaa"), wrapStorage(h), nil, nil)) + + // binaryIterator + r, _ := db.StateReader(common.HexToHash("0x09")) + head := db.tree.get(common.HexToHash("0x09")) + it := head.(*diffLayer).newBinaryStorageIterator(common.HexToHash("0xaa"), common.Hash{}) + for it.Next() { + hash := it.Hash() + want, err := r.Storage(common.HexToHash("0xaa"), hash) + if err != nil { + t.Fatalf("failed to retrieve expected account: %v", err) + } + if have := it.Slot(); !bytes.Equal(want, have) { + t.Fatalf("hash %x: account mismatch: have %x, want %x", hash, have, want) + } + } + it.Release() + + // fastIterator + it, _ = db.StorageIterator(common.HexToHash("0x09"), common.HexToHash("0xaa"), common.Hash{}) + for it.Next() { + hash := it.Hash() + want, err := r.Storage(common.HexToHash("0xaa"), hash) + if err != nil { + t.Fatalf("failed to retrieve expected storage slot: %v", err) + } + if have := it.Slot(); !bytes.Equal(want, have) { + t.Fatalf("hash %x: slot mismatch: have %x, want %x", hash, have, want) + } + } + it.Release() + + // TODO reenable these tests once the persistent state iteration + // is implemented. + + // Test after persist some bottom-most layers into the disk, + // the functionalities still work. + //db.tree.cap(common.HexToHash("0x09"), 2) + // + //it, _ = db.StorageIterator(common.HexToHash("0x09"), common.HexToHash("0xaa"), common.Hash{}) + //for it.Next() { + // hash := it.Hash() + // want, err := head.Storage(common.HexToHash("0xaa"), hash) + // if err != nil { + // t.Fatalf("failed to retrieve expected slot: %v", err) + // } + // if have := it.Slot(); !bytes.Equal(want, have) { + // t.Fatalf("hash %x: slot mismatch: have %x, want %x", hash, have, want) + // } + //} + //it.Release() +} + +// This testcase is notorious, all layers contain the exact same 200 accounts. +func TestAccountIteratorLargeTraversal(t *testing.T) { + // Create a custom account factory to recreate the same addresses + makeAccounts := func(num int) map[common.Hash][]byte { + accounts := make(map[common.Hash][]byte) + for i := 0; i < num; i++ { + h := common.Hash{} + binary.BigEndian.PutUint64(h[:], uint64(i+1)) + accounts[h] = randomAccount() + } + return accounts + } + // Build up a large stack of snapshots + config := &Config{ + WriteBufferSize: 0, + } + db := New(rawdb.NewMemoryDatabase(), config, false) + // db.WaitGeneration() + for i := 1; i < 128; i++ { + parent := types.EmptyRootHash + if i == 1 { + parent = common.HexToHash(fmt.Sprintf("0x%02x", i)) + } + db.Update(common.HexToHash(fmt.Sprintf("0x%02x", i+1)), parent, uint64(i), trienode.NewMergedNodeSet(), + NewStateSetWithOrigin(makeAccounts(200), nil, nil, nil)) + } + // Iterate the entire stack and ensure everything is hit only once + head := db.tree.get(common.HexToHash("0x80")) + verifyIterator(t, 200, newDiffAccountIterator(common.Hash{}, head.(*diffLayer).states.stateSet, nil), verifyNothing) + verifyIterator(t, 200, head.(*diffLayer).newBinaryAccountIterator(common.Hash{}), verifyAccount) + + it, _ := db.AccountIterator(common.HexToHash("0x80"), common.Hash{}) + verifyIterator(t, 200, it, verifyAccount) + it.Release() + + // TODO reenable these tests once the persistent state iteration + // is implemented. + + // Test after persist some bottom-most layers into the disk, + // the functionalities still work. + //db.tree.cap(common.HexToHash("0x80"), 2) + // + //verifyIterator(t, 200, head.(*diffLayer).newBinaryAccountIterator(), verifyAccount) + // + //it, _ = db.AccountIterator(common.HexToHash("0x80"), common.Hash{}) + //verifyIterator(t, 200, it, verifyAccount) + //it.Release() +} + +// TestAccountIteratorFlattening tests what happens when we +// - have a live iterator on child C (parent C1 -> C2 .. CN) +// - flattens C2 all the way into CN +// - continues iterating +func TestAccountIteratorFlattening(t *testing.T) { + config := &Config{ + WriteBufferSize: 10 * 1024, + } + db := New(rawdb.NewMemoryDatabase(), config, false) + // db.WaitGeneration() + + // Create a stack of diffs on top + db.Update(common.HexToHash("0x02"), types.EmptyRootHash, 1, trienode.NewMergedNodeSet(), + NewStateSetWithOrigin(randomAccountSet("0xaa", "0xee", "0xff", "0xf0"), nil, nil, nil)) + + db.Update(common.HexToHash("0x03"), common.HexToHash("0x02"), 2, trienode.NewMergedNodeSet(), + NewStateSetWithOrigin(randomAccountSet("0xbb", "0xdd", "0xf0"), nil, nil, nil)) + + db.Update(common.HexToHash("0x04"), common.HexToHash("0x03"), 3, trienode.NewMergedNodeSet(), + NewStateSetWithOrigin(randomAccountSet("0xcc", "0xf0", "0xff"), nil, nil, nil)) + + // Create a binary iterator and flatten the data from underneath it + head := db.tree.get(common.HexToHash("0x04")) + bit := head.(*diffLayer).newBinaryAccountIterator(common.Hash{}) + defer bit.Release() + + // Create a fast iterator and flatten the data from underneath it + fit, _ := db.AccountIterator(common.HexToHash("0x04"), common.Hash{}) + defer fit.Release() + + if err := db.tree.cap(common.HexToHash("0x04"), 1); err != nil { + t.Fatalf("failed to flatten snapshot stack: %v", err) + } + verifyIterator(t, 7, bit, verifyAccount) + verifyIterator(t, 7, fit, verifyAccount) +} + +func TestAccountIteratorSeek(t *testing.T) { + config := &Config{ + WriteBufferSize: 0, + } + db := New(rawdb.NewMemoryDatabase(), config, false) + // db.WaitGeneration() + + db.Update(common.HexToHash("0x02"), types.EmptyRootHash, 1, trienode.NewMergedNodeSet(), + NewStateSetWithOrigin(randomAccountSet("0xaa", "0xee", "0xff", "0xf0"), nil, nil, nil)) + + db.Update(common.HexToHash("0x03"), common.HexToHash("0x02"), 2, trienode.NewMergedNodeSet(), + NewStateSetWithOrigin(randomAccountSet("0xbb", "0xdd", "0xf0"), nil, nil, nil)) + + db.Update(common.HexToHash("0x04"), common.HexToHash("0x03"), 3, trienode.NewMergedNodeSet(), + NewStateSetWithOrigin(randomAccountSet("0xcc", "0xf0", "0xff"), nil, nil, nil)) + + // Account set is now + // 02: aa, ee, f0, ff + // 03: aa, bb, dd, ee, f0 (, f0), ff + // 04: aa, bb, cc, dd, ee, f0 (, f0), ff (, ff) + // Construct various iterators and ensure their traversal is correct + it, _ := db.AccountIterator(common.HexToHash("0x02"), common.HexToHash("0xdd")) + defer it.Release() + verifyIterator(t, 3, it, verifyAccount) // expected: ee, f0, ff + + it, _ = db.AccountIterator(common.HexToHash("0x02"), common.HexToHash("0xaa")) + defer it.Release() + verifyIterator(t, 4, it, verifyAccount) // expected: aa, ee, f0, ff + + it, _ = db.AccountIterator(common.HexToHash("0x02"), common.HexToHash("0xff")) + defer it.Release() + verifyIterator(t, 1, it, verifyAccount) // expected: ff + + it, _ = db.AccountIterator(common.HexToHash("0x02"), common.HexToHash("0xff1")) + defer it.Release() + verifyIterator(t, 0, it, verifyAccount) // expected: nothing + + it, _ = db.AccountIterator(common.HexToHash("0x04"), common.HexToHash("0xbb")) + defer it.Release() + verifyIterator(t, 6, it, verifyAccount) // expected: bb, cc, dd, ee, f0, ff + + it, _ = db.AccountIterator(common.HexToHash("0x04"), common.HexToHash("0xef")) + defer it.Release() + verifyIterator(t, 2, it, verifyAccount) // expected: f0, ff + + it, _ = db.AccountIterator(common.HexToHash("0x04"), common.HexToHash("0xf0")) + defer it.Release() + verifyIterator(t, 2, it, verifyAccount) // expected: f0, ff + + it, _ = db.AccountIterator(common.HexToHash("0x04"), common.HexToHash("0xff")) + defer it.Release() + verifyIterator(t, 1, it, verifyAccount) // expected: ff + + it, _ = db.AccountIterator(common.HexToHash("0x04"), common.HexToHash("0xff1")) + defer it.Release() + verifyIterator(t, 0, it, verifyAccount) // expected: nothing +} + +func TestStorageIteratorSeek(t *testing.T) { + t.Run("fast", func(t *testing.T) { + testStorageIteratorSeek(t, func(db *Database, root, account, seek common.Hash) StorageIterator { + it, _ := db.StorageIterator(root, account, seek) + return it + }) + }) + t.Run("binary", func(t *testing.T) { + testStorageIteratorSeek(t, func(db *Database, root, account, seek common.Hash) StorageIterator { + return db.tree.get(root).(*diffLayer).newBinaryStorageIterator(account, seek) + }) + }) +} + +func testStorageIteratorSeek(t *testing.T, newIterator func(db *Database, root, account, seek common.Hash) StorageIterator) { + config := &Config{ + WriteBufferSize: 0, + } + db := New(rawdb.NewMemoryDatabase(), config, false) + // db.WaitGeneration() + + // Stack three diff layers on top with various overlaps + db.Update(common.HexToHash("0x02"), types.EmptyRootHash, 1, trienode.NewMergedNodeSet(), + NewStateSetWithOrigin(randomAccountSet("0xaa"), randomStorageSet([]string{"0xaa"}, [][]string{{"0x01", "0x03", "0x05"}}, nil), nil, nil)) + + db.Update(common.HexToHash("0x03"), common.HexToHash("0x02"), 2, trienode.NewMergedNodeSet(), + NewStateSetWithOrigin(randomAccountSet("0xaa"), randomStorageSet([]string{"0xaa"}, [][]string{{"0x02", "0x05", "0x06"}}, nil), nil, nil)) + + db.Update(common.HexToHash("0x04"), common.HexToHash("0x03"), 3, trienode.NewMergedNodeSet(), + NewStateSetWithOrigin(randomAccountSet("0xaa"), randomStorageSet([]string{"0xaa"}, [][]string{{"0x01", "0x05", "0x08"}}, nil), nil, nil)) + + // Account set is now + // 02: 01, 03, 05 + // 03: 01, 02, 03, 05 (, 05), 06 + // 04: 01(, 01), 02, 03, 05(, 05, 05), 06, 08 + // Construct various iterators and ensure their traversal is correct + it := newIterator(db, common.HexToHash("0x02"), common.HexToHash("0xaa"), common.HexToHash("0x01")) + defer it.Release() + verifyIterator(t, 3, it, verifyStorage) // expected: 01, 03, 05 + + it = newIterator(db, common.HexToHash("0x02"), common.HexToHash("0xaa"), common.HexToHash("0x02")) + defer it.Release() + verifyIterator(t, 2, it, verifyStorage) // expected: 03, 05 + + it = newIterator(db, common.HexToHash("0x02"), common.HexToHash("0xaa"), common.HexToHash("0x5")) + defer it.Release() + verifyIterator(t, 1, it, verifyStorage) // expected: 05 + + it = newIterator(db, common.HexToHash("0x02"), common.HexToHash("0xaa"), common.HexToHash("0x6")) + defer it.Release() + verifyIterator(t, 0, it, verifyStorage) // expected: nothing + + it = newIterator(db, common.HexToHash("0x04"), common.HexToHash("0xaa"), common.HexToHash("0x01")) + defer it.Release() + verifyIterator(t, 6, it, verifyStorage) // expected: 01, 02, 03, 05, 06, 08 + + it = newIterator(db, common.HexToHash("0x04"), common.HexToHash("0xaa"), common.HexToHash("0x05")) + defer it.Release() + verifyIterator(t, 3, it, verifyStorage) // expected: 05, 06, 08 + + it = newIterator(db, common.HexToHash("0x04"), common.HexToHash("0xaa"), common.HexToHash("0x08")) + defer it.Release() + verifyIterator(t, 1, it, verifyStorage) // expected: 08 + + it = newIterator(db, common.HexToHash("0x04"), common.HexToHash("0xaa"), common.HexToHash("0x09")) + defer it.Release() + verifyIterator(t, 0, it, verifyStorage) // expected: nothing +} + +// TestAccountIteratorDeletions tests that the iterator behaves correct when there are +// deleted accounts (where the Account() value is nil). The iterator +// should not output any accounts or nil-values for those cases. +func TestAccountIteratorDeletions(t *testing.T) { + t.Run("fast", func(t *testing.T) { + testAccountIteratorDeletions(t, func(db *Database, root, seek common.Hash) AccountIterator { + it, _ := db.AccountIterator(root, seek) + return it + }) + }) + t.Run("binary", func(t *testing.T) { + testAccountIteratorDeletions(t, func(db *Database, root, seek common.Hash) AccountIterator { + return db.tree.get(root).(*diffLayer).newBinaryAccountIterator(seek) + }) + }) +} + +func testAccountIteratorDeletions(t *testing.T, newIterator func(db *Database, root, seek common.Hash) AccountIterator) { + config := &Config{ + WriteBufferSize: 0, + } + db := New(rawdb.NewMemoryDatabase(), config, false) + // db.WaitGeneration() + + // Stack three diff layers on top with various overlaps + db.Update(common.HexToHash("0x02"), types.EmptyRootHash, 1, trienode.NewMergedNodeSet(), + NewStateSetWithOrigin(randomAccountSet("0x11", "0x22", "0x33"), nil, nil, nil)) + + deleted := common.HexToHash("0x22") + accounts := randomAccountSet("0x11", "0x33") + accounts[deleted] = nil + db.Update(common.HexToHash("0x03"), common.HexToHash("0x02"), 2, trienode.NewMergedNodeSet(), + NewStateSetWithOrigin(accounts, nil, nil, nil)) + + db.Update(common.HexToHash("0x04"), common.HexToHash("0x03"), 3, trienode.NewMergedNodeSet(), + NewStateSetWithOrigin(randomAccountSet("0x33", "0x44", "0x55"), nil, nil, nil)) + + // The output should be 11,33,44,55 + it := newIterator(db, common.HexToHash("0x04"), common.Hash{}) + // Do a quick check + verifyIterator(t, 4, it, verifyAccount) + it.Release() + + // And a more detailed verification that we indeed do not see '0x22' + it = newIterator(db, common.HexToHash("0x04"), common.Hash{}) + defer it.Release() + for it.Next() { + hash := it.Hash() + 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) + } + } +} + +func TestStorageIteratorDeletions(t *testing.T) { + config := &Config{ + WriteBufferSize: 0, + } + db := New(rawdb.NewMemoryDatabase(), config, false) + // db.WaitGeneration() + + // Stack three diff layers on top with various overlaps + db.Update(common.HexToHash("0x02"), types.EmptyRootHash, 1, trienode.NewMergedNodeSet(), + NewStateSetWithOrigin(randomAccountSet("0xaa"), randomStorageSet([]string{"0xaa"}, [][]string{{"0x01", "0x03", "0x05"}}, nil), nil, nil)) + + db.Update(common.HexToHash("0x03"), common.HexToHash("0x02"), 2, trienode.NewMergedNodeSet(), + NewStateSetWithOrigin(randomAccountSet("0xaa"), randomStorageSet([]string{"0xaa"}, [][]string{{"0x02", "0x04", "0x06"}}, [][]string{{"0x01", "0x03"}}), nil, nil)) + + // The output should be 02,04,05,06 + it, _ := db.StorageIterator(common.HexToHash("0x03"), common.HexToHash("0xaa"), common.Hash{}) + verifyIterator(t, 4, it, verifyStorage) + it.Release() + + // The output should be 04,05,06 + it, _ = db.StorageIterator(common.HexToHash("0x03"), common.HexToHash("0xaa"), common.HexToHash("0x03")) + verifyIterator(t, 3, it, verifyStorage) + it.Release() + + // Destruct the whole storage + accounts := map[common.Hash][]byte{ + common.HexToHash("0xaa"): nil, + } + db.Update(common.HexToHash("0x04"), common.HexToHash("0x03"), 3, trienode.NewMergedNodeSet(), + NewStateSetWithOrigin(accounts, randomStorageSet([]string{"0xaa"}, nil, [][]string{{"0x02", "0x04", "0x05", "0x06"}}), nil, nil)) + + it, _ = db.StorageIterator(common.HexToHash("0x04"), common.HexToHash("0xaa"), common.Hash{}) + verifyIterator(t, 0, it, verifyStorage) + it.Release() + + // Re-insert the slots of the same account + db.Update(common.HexToHash("0x05"), common.HexToHash("0x04"), 4, trienode.NewMergedNodeSet(), + NewStateSetWithOrigin(randomAccountSet("0xaa"), randomStorageSet([]string{"0xaa"}, [][]string{{"0x07", "0x08", "0x09"}}, nil), nil, nil)) + + // The output should be 07,08,09 + it, _ = db.StorageIterator(common.HexToHash("0x05"), common.HexToHash("0xaa"), common.Hash{}) + verifyIterator(t, 3, it, verifyStorage) + it.Release() + + // Destruct the whole storage but re-create the account in the same layer + db.Update(common.HexToHash("0x06"), common.HexToHash("0x05"), 5, trienode.NewMergedNodeSet(), + NewStateSetWithOrigin(randomAccountSet("0xaa"), randomStorageSet([]string{"0xaa"}, [][]string{{"0x11", "0x12"}}, [][]string{{"0x07", "0x08", "0x09"}}), nil, nil)) + + it, _ = db.StorageIterator(common.HexToHash("0x06"), common.HexToHash("0xaa"), common.Hash{}) + verifyIterator(t, 2, it, verifyStorage) // The output should be 11,12 + it.Release() + + verifyIterator(t, 2, db.tree.get(common.HexToHash("0x06")).(*diffLayer).newBinaryStorageIterator(common.HexToHash("0xaa"), common.Hash{}), verifyStorage) +} + +// TestStaleIterator tests if the iterator could correctly terminate the iteration +// if the associated layers are outdated. +func TestStaleIterator(t *testing.T) { + testStaleIterator(t, func(db *Database, hash common.Hash) Iterator { + it, _ := db.StorageIterator(hash, common.HexToHash("0xaa"), common.Hash{}) + return it + }) + testStaleIterator(t, func(db *Database, hash common.Hash) Iterator { + head := db.tree.get(hash) + return head.(*diffLayer).newBinaryStorageIterator(common.HexToHash("0xaa"), common.Hash{}) + }) +} + +func testStaleIterator(t *testing.T, newIter func(db *Database, hash common.Hash) Iterator) { + config := &Config{ + WriteBufferSize: 16 * 1024 * 1024, + } + db := New(rawdb.NewMemoryDatabase(), config, false) + // db.WaitGeneration() + + // [02 (disk), 03] + db.Update(common.HexToHash("0x02"), types.EmptyRootHash, 1, trienode.NewMergedNodeSet(), + NewStateSetWithOrigin(randomAccountSet("0xaa"), randomStorageSet([]string{"0xaa"}, [][]string{{"0x01"}}, nil), nil, nil)) + db.Update(common.HexToHash("0x03"), common.HexToHash("0x02"), 2, trienode.NewMergedNodeSet(), + NewStateSetWithOrigin(randomAccountSet("0xaa"), randomStorageSet([]string{"0xaa"}, [][]string{{"0x02"}}, nil), nil, nil)) + db.tree.cap(common.HexToHash("0x03"), 1) + + // [02 (disk), 03, 04] + db.Update(common.HexToHash("0x04"), common.HexToHash("0x03"), 3, trienode.NewMergedNodeSet(), + NewStateSetWithOrigin(randomAccountSet("0xaa"), randomStorageSet([]string{"0xaa"}, [][]string{{"0x03"}}, nil), nil, nil)) + iter := newIter(db, common.HexToHash("0x04")) + + // [04 (disk), 05] + db.Update(common.HexToHash("0x05"), common.HexToHash("0x04"), 3, trienode.NewMergedNodeSet(), + NewStateSetWithOrigin(randomAccountSet("0xaa"), randomStorageSet([]string{"0xaa"}, [][]string{{"0x04"}}, nil), nil, nil)) + db.tree.cap(common.HexToHash("0x05"), 1) + + // Iterator can't finish the traversal as the layer 02 has becoming stale. + for iter.Next() { + } + err := iter.Error() + t.Log(err) + if err == nil { + t.Fatalf("Expected iterator error is not reported") + } +} + +// BenchmarkAccountIteratorTraversal is a bit notorious -- all layers contain the +// exact same 200 accounts. That means that we need to process 2000 items, but +// only spit out 200 values eventually. +// +// The value-fetching benchmark is easy on the binary iterator, since it never has to reach +// down at any depth for retrieving the values -- all are on the topmost layer +// +// BenchmarkAccountIteratorTraversal/binary_iterator_keys-8 759984 1566 ns/op +// BenchmarkAccountIteratorTraversal/binary_iterator_values-8 150028 7900 ns/op +// BenchmarkAccountIteratorTraversal/fast_iterator_keys-8 172809 7006 ns/op +// BenchmarkAccountIteratorTraversal/fast_iterator_values-8 165112 7658 ns/op +func BenchmarkAccountIteratorTraversal(b *testing.B) { + // Create a custom account factory to recreate the same addresses + makeAccounts := func(num int) map[common.Hash][]byte { + accounts := make(map[common.Hash][]byte) + for i := 0; i < num; i++ { + h := common.Hash{} + binary.BigEndian.PutUint64(h[:], uint64(i+1)) + accounts[h] = randomAccount() + } + return accounts + } + config := &Config{ + WriteBufferSize: 0, + } + db := New(rawdb.NewMemoryDatabase(), config, false) + // db.WaitGeneration() + + for i := 1; i <= 100; i++ { + parent := types.EmptyRootHash + if i == 1 { + parent = common.HexToHash(fmt.Sprintf("0x%02x", i)) + } + db.Update(common.HexToHash(fmt.Sprintf("0x%02x", i+1)), parent, uint64(i), trienode.NewMergedNodeSet(), NewStateSetWithOrigin(makeAccounts(200), nil, nil, nil)) + } + // We call this once before the benchmark, so the creation of + // sorted accountlists are not included in the results. + head := db.tree.get(common.HexToHash("0x65")) + head.(*diffLayer).newBinaryAccountIterator(common.Hash{}) + + b.Run("binary iterator keys", func(b *testing.B) { + for i := 0; i < b.N; i++ { + got := 0 + it := head.(*diffLayer).newBinaryAccountIterator(common.Hash{}) + for it.Next() { + got++ + } + if exp := 200; got != exp { + b.Errorf("iterator len wrong, expected %d, got %d", exp, got) + } + } + }) + b.Run("binary iterator values", func(b *testing.B) { + for i := 0; i < b.N; i++ { + got := 0 + it := head.(*diffLayer).newBinaryAccountIterator(common.Hash{}) + for it.Next() { + got++ + head.(*diffLayer).account(it.Hash(), 0) + } + if exp := 200; got != exp { + b.Errorf("iterator len wrong, expected %d, got %d", exp, got) + } + } + }) + b.Run("fast iterator keys", func(b *testing.B) { + for i := 0; i < b.N; i++ { + it, _ := db.AccountIterator(common.HexToHash("0x65"), common.Hash{}) + defer it.Release() + + got := 0 + for it.Next() { + got++ + } + if exp := 200; got != exp { + b.Errorf("iterator len wrong, expected %d, got %d", exp, got) + } + } + }) + b.Run("fast iterator values", func(b *testing.B) { + for i := 0; i < b.N; i++ { + it, _ := db.AccountIterator(common.HexToHash("0x65"), common.Hash{}) + defer it.Release() + + got := 0 + for it.Next() { + got++ + it.Account() + } + if exp := 200; got != exp { + b.Errorf("iterator len wrong, expected %d, got %d", exp, got) + } + } + }) +} + +// BenchmarkAccountIteratorLargeBaselayer is a pretty realistic benchmark, where +// the baselayer is a lot larger than the upper layer. +// +// This is heavy on the binary iterator, which in most cases will have to +// call recursively 100 times for the majority of the values +// +// BenchmarkAccountIteratorLargeBaselayer/binary_iterator_(keys)-6 514 1971999 ns/op +// BenchmarkAccountIteratorLargeBaselayer/binary_iterator_(values)-6 61 18997492 ns/op +// BenchmarkAccountIteratorLargeBaselayer/fast_iterator_(keys)-6 10000 114385 ns/op +// BenchmarkAccountIteratorLargeBaselayer/fast_iterator_(values)-6 4047 296823 ns/op +func BenchmarkAccountIteratorLargeBaselayer(b *testing.B) { + // Create a custom account factory to recreate the same addresses + makeAccounts := func(num int) map[common.Hash][]byte { + accounts := make(map[common.Hash][]byte) + for i := 0; i < num; i++ { + h := common.Hash{} + binary.BigEndian.PutUint64(h[:], uint64(i+1)) + accounts[h] = randomAccount() + } + return accounts + } + config := &Config{ + WriteBufferSize: 0, + } + db := New(rawdb.NewMemoryDatabase(), config, false) + // db.WaitGeneration() + + db.Update(common.HexToHash("0x02"), types.EmptyRootHash, 1, trienode.NewMergedNodeSet(), NewStateSetWithOrigin(makeAccounts(2000), nil, nil, nil)) + for i := 2; i <= 100; i++ { + db.Update(common.HexToHash(fmt.Sprintf("0x%02x", i+1)), common.HexToHash(fmt.Sprintf("0x%02x", i)), uint64(i), trienode.NewMergedNodeSet(), NewStateSetWithOrigin(makeAccounts(20), nil, nil, nil)) + } + // We call this once before the benchmark, so the creation of + // sorted accountlists are not included in the results. + head := db.tree.get(common.HexToHash("0x65")) + head.(*diffLayer).newBinaryAccountIterator(common.Hash{}) + + b.Run("binary iterator (keys)", func(b *testing.B) { + for i := 0; i < b.N; i++ { + got := 0 + it := head.(*diffLayer).newBinaryAccountIterator(common.Hash{}) + for it.Next() { + got++ + } + if exp := 2000; got != exp { + b.Errorf("iterator len wrong, expected %d, got %d", exp, got) + } + } + }) + b.Run("binary iterator (values)", func(b *testing.B) { + for i := 0; i < b.N; i++ { + got := 0 + it := head.(*diffLayer).newBinaryAccountIterator(common.Hash{}) + for it.Next() { + got++ + v := it.Hash() + head.(*diffLayer).account(v, 0) + } + if exp := 2000; got != exp { + b.Errorf("iterator len wrong, expected %d, got %d", exp, got) + } + } + }) + b.Run("fast iterator (keys)", func(b *testing.B) { + for i := 0; i < b.N; i++ { + it, _ := db.AccountIterator(common.HexToHash("0x65"), common.Hash{}) + defer it.Release() + + got := 0 + for it.Next() { + got++ + } + if exp := 2000; got != exp { + b.Errorf("iterator len wrong, expected %d, got %d", exp, got) + } + } + }) + b.Run("fast iterator (values)", func(b *testing.B) { + for i := 0; i < b.N; i++ { + it, _ := db.AccountIterator(common.HexToHash("0x65"), common.Hash{}) + defer it.Release() + + got := 0 + for it.Next() { + it.Account() + got++ + } + if exp := 2000; got != exp { + b.Errorf("iterator len wrong, expected %d, got %d", exp, got) + } + } + }) +} + +/* +func BenchmarkBinaryAccountIteration(b *testing.B) { + benchmarkAccountIteration(b, func(snap snapshot) AccountIterator { + return snap.(*diffLayer).newBinaryAccountIterator() + }) +} + +func BenchmarkFastAccountIteration(b *testing.B) { + benchmarkAccountIteration(b, newFastAccountIterator) +} + +func benchmarkAccountIteration(b *testing.B, iterator func(snap snapshot) AccountIterator) { + // Create a diff stack and randomize the accounts across them + layers := make([]map[common.Hash][]byte, 128) + for i := 0; i < len(layers); i++ { + layers[i] = make(map[common.Hash][]byte) + } + for i := 0; i < b.N; i++ { + depth := rand.Intn(len(layers)) + layers[depth][randomHash()] = randomAccount() + } + stack := snapshot(emptyLayer()) + for _, layer := range layers { + stack = stack.Update(common.Hash{}, layer, nil, nil) + } + // Reset the timers and report all the stats + it := iterator(stack) + + b.ResetTimer() + b.ReportAllocs() + + for it.Next() { + } +} +*/ diff --git a/triedb/pathdb/journal.go b/triedb/pathdb/journal.go index 1740ec5935..779a262fdd 100644 --- a/triedb/pathdb/journal.go +++ b/triedb/pathdb/journal.go @@ -29,8 +29,6 @@ import ( "github.com/ethereum/go-ethereum/crypto" "github.com/ethereum/go-ethereum/log" "github.com/ethereum/go-ethereum/rlp" - "github.com/ethereum/go-ethereum/trie/trienode" - "github.com/ethereum/go-ethereum/trie/triestate" ) var ( @@ -47,33 +45,8 @@ var ( // // - Version 0: initial version // - Version 1: storage.Incomplete field is removed -const journalVersion uint64 = 1 - -// journalNode represents a trie node persisted in the journal. -type journalNode struct { - Path []byte // Path of the node in the trie - Blob []byte // RLP-encoded trie node blob, nil means the node is deleted -} - -// journalNodes represents a list trie nodes belong to a single account -// or the main account trie. -type journalNodes struct { - Owner common.Hash - Nodes []journalNode -} - -// journalAccounts represents a list accounts belong to the layer. -type journalAccounts struct { - Addresses []common.Address - Accounts [][]byte -} - -// journalStorage represents a list of storage slots belong to an account. -type journalStorage struct { - Account common.Address - Hashes []common.Hash - Slots [][]byte -} +// - Version 2: add post-modification state values +const journalVersion uint64 = 2 // loadJournal tries to parse the layer journal from the disk. func (db *Database) loadJournal(diskRoot common.Hash) (layer, error) { @@ -136,7 +109,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, newNodeBuffer(db.bufferSize, nil, 0)) + return newDiskLayer(root, rawdb.ReadPersistentStateID(db.diskdb), db, nil, newBuffer(db.config.WriteBufferSize, nil, nil, 0)) } // loadDiskLayer reads the binary blob from the layer journal, reconstructing @@ -158,26 +131,17 @@ func (db *Database) loadDiskLayer(r *rlp.Stream) (layer, error) { if stored > id { return nil, fmt.Errorf("invalid state id: stored %d resolved %d", stored, id) } - // Resolve nodes cached in node buffer - var encoded []journalNodes - if err := r.Decode(&encoded); err != nil { - return nil, fmt.Errorf("load disk nodes: %v", err) + // Resolve nodes cached in aggregated buffer + var nodes nodeSet + if err := nodes.decode(r); err != nil { + return nil, err } - nodes := make(map[common.Hash]map[string]*trienode.Node) - for _, entry := range encoded { - subset := make(map[string]*trienode.Node) - for _, n := range entry.Nodes { - if len(n.Blob) > 0 { - subset[string(n.Path)] = trienode.New(crypto.Keccak256Hash(n.Blob), n.Blob) - } else { - subset[string(n.Path)] = trienode.NewDeleted() - } - } - nodes[entry.Owner] = subset + // Resolve flat state sets in aggregated buffer + var states stateSet + if err := states.decode(r); err != nil { + return nil, err } - // Calculate the internal state transitions by id difference. - base := newDiskLayer(root, id, db, nil, newNodeBuffer(db.bufferSize, nodes, id-stored)) - return base, nil + return newDiskLayer(root, id, db, nil, newBuffer(db.config.WriteBufferSize, &nodes, &states, id-stored)), nil } // loadDiffLayer reads the next sections of a layer journal, reconstructing a new @@ -197,50 +161,16 @@ func (db *Database) loadDiffLayer(parent layer, r *rlp.Stream) (layer, error) { return nil, fmt.Errorf("load block number: %v", err) } // Read in-memory trie nodes from journal - var encoded []journalNodes - if err := r.Decode(&encoded); err != nil { - return nil, fmt.Errorf("load diff nodes: %v", err) + var nodes nodeSet + if err := nodes.decode(r); err != nil { + return nil, err } - nodes := make(map[common.Hash]map[string]*trienode.Node) - for _, entry := range encoded { - subset := make(map[string]*trienode.Node) - for _, n := range entry.Nodes { - if len(n.Blob) > 0 { - subset[string(n.Path)] = trienode.New(crypto.Keccak256Hash(n.Blob), n.Blob) - } else { - subset[string(n.Path)] = trienode.NewDeleted() - } - } - nodes[entry.Owner] = subset + // Read flat states set (with original value attached) from journal + var stateSet StateSetWithOrigin + if err := stateSet.decode(r); err != nil { + return nil, err } - // Read state changes from journal - var ( - jaccounts journalAccounts - jstorages []journalStorage - accounts = make(map[common.Address][]byte) - storages = make(map[common.Address]map[common.Hash][]byte) - ) - if err := r.Decode(&jaccounts); err != nil { - return nil, fmt.Errorf("load diff accounts: %v", err) - } - for i, addr := range jaccounts.Addresses { - accounts[addr] = jaccounts.Accounts[i] - } - if err := r.Decode(&jstorages); err != nil { - return nil, fmt.Errorf("load diff storages: %v", err) - } - for _, entry := range jstorages { - set := make(map[common.Hash][]byte) - for i, h := range entry.Hashes { - if len(entry.Slots[i]) > 0 { - set[h] = entry.Slots[i] - } else { - set[h] = nil - } - } - storages[entry.Account] = set - } - return db.loadDiffLayer(newDiffLayer(parent, root, parent.stateID()+1, block, nodes, triestate.New(accounts, storages)), r) + return db.loadDiffLayer(newDiffLayer(parent, root, parent.stateID()+1, block, &nodes, &stateSet), r) } // journal implements the layer interface, marshaling the un-flushed trie nodes @@ -261,19 +191,15 @@ func (dl *diskLayer) journal(w io.Writer) error { if err := rlp.Encode(w, dl.id); err != nil { return err } - // Step three, write all unwritten nodes into the journal - nodes := make([]journalNodes, 0, len(dl.buffer.nodes)) - for owner, subset := range dl.buffer.nodes { - entry := journalNodes{Owner: owner} - for path, node := range subset { - entry.Nodes = append(entry.Nodes, journalNode{Path: []byte(path), Blob: node.Blob}) - } - nodes = append(nodes, entry) - } - if err := rlp.Encode(w, nodes); err != nil { + // Step three, write the accumulated trie nodes into the journal + if err := dl.buffer.nodes.encode(w); err != nil { return err } - log.Debug("Journaled pathdb disk layer", "root", dl.root, "nodes", len(dl.buffer.nodes)) + // Step four, write the accumulated flat states into the journal + if err := dl.buffer.states.encode(w); err != nil { + return err + } + log.Debug("Journaled pathdb disk layer", "root", dl.root) return nil } @@ -295,39 +221,14 @@ func (dl *diffLayer) journal(w io.Writer) error { return err } // Write the accumulated trie nodes into buffer - nodes := make([]journalNodes, 0, len(dl.nodes)) - for owner, subset := range dl.nodes { - entry := journalNodes{Owner: owner} - for path, node := range subset { - entry.Nodes = append(entry.Nodes, journalNode{Path: []byte(path), Blob: node.Blob}) - } - nodes = append(nodes, entry) - } - if err := rlp.Encode(w, nodes); err != nil { + if err := dl.nodes.encode(w); err != nil { return err } - // Write the accumulated state changes into buffer - var jacct journalAccounts - for addr, account := range dl.states.Accounts { - jacct.Addresses = append(jacct.Addresses, addr) - jacct.Accounts = append(jacct.Accounts, account) - } - if err := rlp.Encode(w, jacct); err != nil { + // Write the associated flat state set into buffer + if err := dl.states.encode(w); err != nil { return err } - storage := make([]journalStorage, 0, len(dl.states.Storages)) - for addr, slots := range dl.states.Storages { - entry := journalStorage{Account: addr} - for slotHash, slot := range slots { - entry.Hashes = append(entry.Hashes, slotHash) - entry.Slots = append(entry.Slots, slot) - } - storage = append(storage, entry) - } - if err := rlp.Encode(w, storage); err != nil { - return err - } - log.Debug("Journaled pathdb diff layer", "root", dl.root, "parent", dl.parent.rootHash(), "id", dl.stateID(), "block", dl.block, "nodes", len(dl.nodes)) + log.Debug("Journaled pathdb diff layer", "root", dl.root, "parent", dl.parent.rootHash(), "id", dl.stateID(), "block", dl.block) return nil } diff --git a/triedb/pathdb/layertree.go b/triedb/pathdb/layertree.go index d314779910..cf6b14e744 100644 --- a/triedb/pathdb/layertree.go +++ b/triedb/pathdb/layertree.go @@ -24,7 +24,6 @@ import ( "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/trie/trienode" - "github.com/ethereum/go-ethereum/trie/triestate" ) // layerTree is a group of state layers identified by the state root. @@ -86,7 +85,7 @@ func (tree *layerTree) len() int { } // add inserts a new layer into the tree if it can be linked to an existing old parent. -func (tree *layerTree) add(root common.Hash, parentRoot common.Hash, block uint64, nodes *trienode.MergedNodeSet, states *triestate.Set) error { +func (tree *layerTree) add(root common.Hash, parentRoot common.Hash, block uint64, nodes *trienode.MergedNodeSet, states *StateSetWithOrigin) error { // Reject noop updates to avoid self-loops. This is a special case that can // happen for clique networks and proof-of-stake networks where empty blocks // don't modify the state (0 block subsidy). @@ -101,7 +100,7 @@ func (tree *layerTree) add(root common.Hash, parentRoot common.Hash, block uint6 if parent == nil { return fmt.Errorf("triedb parent [%#x] layer missing", parentRoot) } - l := parent.update(root, parent.stateID()+1, block, nodes.Flatten(), states) + l := parent.update(root, parent.stateID()+1, block, newNodeSet(nodes.Flatten()), states) tree.lock.Lock() tree.layers[l.rootHash()] = l diff --git a/triedb/pathdb/metrics.go b/triedb/pathdb/metrics.go index a250f703cb..1a2559e38b 100644 --- a/triedb/pathdb/metrics.go +++ b/triedb/pathdb/metrics.go @@ -19,31 +19,43 @@ package pathdb import "github.com/ethereum/go-ethereum/metrics" var ( - cleanHitMeter = metrics.NewRegisteredMeter("pathdb/clean/hit", nil) - cleanMissMeter = metrics.NewRegisteredMeter("pathdb/clean/miss", nil) - cleanReadMeter = metrics.NewRegisteredMeter("pathdb/clean/read", nil) - cleanWriteMeter = metrics.NewRegisteredMeter("pathdb/clean/write", nil) + cleanNodeHitMeter = metrics.NewRegisteredMeter("pathdb/clean/node/hit", nil) + cleanNodeMissMeter = metrics.NewRegisteredMeter("pathdb/clean/node/miss", nil) + cleanNodeReadMeter = metrics.NewRegisteredMeter("pathdb/clean/node/read", nil) + cleanNodeWriteMeter = metrics.NewRegisteredMeter("pathdb/clean/node/write", nil) - dirtyHitMeter = metrics.NewRegisteredMeter("pathdb/dirty/hit", nil) - dirtyMissMeter = metrics.NewRegisteredMeter("pathdb/dirty/miss", nil) - dirtyReadMeter = metrics.NewRegisteredMeter("pathdb/dirty/read", nil) - dirtyWriteMeter = metrics.NewRegisteredMeter("pathdb/dirty/write", nil) - dirtyNodeHitDepthHist = metrics.NewRegisteredHistogram("pathdb/dirty/depth", nil, metrics.NewExpDecaySample(1028, 0.015)) + dirtyNodeHitMeter = metrics.NewRegisteredMeter("pathdb/dirty/node/hit", nil) + dirtyNodeMissMeter = metrics.NewRegisteredMeter("pathdb/dirty/node/miss", nil) + dirtyNodeReadMeter = metrics.NewRegisteredMeter("pathdb/dirty/node/read", nil) + 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) commitBytesMeter = metrics.NewRegisteredMeter("pathdb/commit/bytes", nil) - gcNodesMeter = metrics.NewRegisteredMeter("pathdb/gc/nodes", nil) - gcBytesMeter = metrics.NewRegisteredMeter("pathdb/gc/bytes", nil) - - diffLayerBytesMeter = metrics.NewRegisteredMeter("pathdb/diff/bytes", nil) - diffLayerNodesMeter = metrics.NewRegisteredMeter("pathdb/diff/nodes", nil) + 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/nodebuffer.go b/triedb/pathdb/nodebuffer.go deleted file mode 100644 index a4788ff9ba..0000000000 --- a/triedb/pathdb/nodebuffer.go +++ /dev/null @@ -1,290 +0,0 @@ -// Copyright 2022 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" - "fmt" - "maps" - "time" - - "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" -) - -// nodebuffer is a collection of modified trie nodes to aggregate the disk -// write. The content of the nodebuffer must be checked before diving into -// disk (since it basically is not-yet-written data). -type nodebuffer 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 -} - -// newNodeBuffer initializes the node buffer with the provided nodes. -func newNodeBuffer(limit int, nodes map[common.Hash]map[string]*trienode.Node, layers uint64) *nodebuffer { - if nodes == nil { - nodes = make(map[common.Hash]map[string]*trienode.Node) - } - var size uint64 - for _, subset := range nodes { - for path, n := range subset { - size += uint64(len(n.Blob) + len(path)) - } - } - return &nodebuffer{ - layers: layers, - nodes: nodes, - size: size, - limit: uint64(limit), - } -} - -// node retrieves the trie node with given node info. -func (b *nodebuffer) node(owner common.Hash, path []byte) (*trienode.Node, bool) { - subset, ok := b.nodes[owner] - if !ok { - return nil, false - } - n, ok := subset[string(path)] - if !ok { - return nil, false - } - return n, true -} - -// commit merges the dirty nodes into 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 (b *nodebuffer) commit(nodes map[common.Hash]map[string]*trienode.Node) *nodebuffer { - var ( - delta int64 - overwrite int64 - overwriteSize int64 - ) - for owner, subset := range nodes { - current, exist := b.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. - for path, n := range subset { - delta += int64(len(n.Blob) + len(path)) - } - b.nodes[owner] = maps.Clone(subset) - 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 - } - b.nodes[owner] = current - } - b.updateSize(delta) - b.layers++ - gcNodesMeter.Mark(overwrite) - gcBytesMeter.Mark(overwriteSize) - return b -} - -// revert 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 (b *nodebuffer) revert(db ethdb.KeyValueReader, nodes map[common.Hash]map[string]*trienode.Node) error { - // Short circuit if no embedded state transition to revert. - if b.layers == 0 { - return errStateUnrecoverable - } - b.layers-- - - // Reset the entire buffer if only a single transition left. - if b.layers == 0 { - b.reset() - return nil - } - var delta int64 - for owner, subset := range nodes { - current, ok := b.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)) - } - // Ignore the clean node in the case described above. - 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)) - } - } - b.updateSize(delta) - return nil -} - -// updateSize updates the total cache size by the given delta. -func (b *nodebuffer) updateSize(delta int64) { - size := int64(b.size) + delta - if size >= 0 { - b.size = uint64(size) - return - } - s := b.size - b.size = 0 - log.Error("Invalid pathdb buffer size", "prev", common.StorageSize(s), "delta", common.StorageSize(delta)) -} - -// reset cleans up the disk cache. -func (b *nodebuffer) reset() { - b.layers = 0 - b.size = 0 - b.nodes = make(map[common.Hash]map[string]*trienode.Node) -} - -// empty returns an indicator if nodebuffer contains any state transition inside. -func (b *nodebuffer) empty() bool { - return b.layers == 0 -} - -// setSize sets the buffer size to the provided number, and invokes a flush -// operation if the current memory usage exceeds the new limit. -func (b *nodebuffer) setSize(size int, db ethdb.KeyValueStore, freezer ethdb.AncientStore, clean *fastcache.Cache, id uint64) error { - b.limit = uint64(size) - return b.flush(db, freezer, clean, id, false) -} - -// allocBatch returns a database batch with pre-allocated buffer. -func (b *nodebuffer) allocBatch(db ethdb.KeyValueStore) ethdb.Batch { - var metasize int - for owner, nodes := range b.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(b.size)) * 11 / 10) // extra 10% for potential pebble internal stuff -} - -// 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 (b *nodebuffer) flush(db ethdb.KeyValueStore, freezer ethdb.AncientWriter, clean *fastcache.Cache, id uint64, force bool) error { - if b.size <= b.limit && !force { - return nil - } - // Ensure the target state id is aligned with the internal counter. - head := rawdb.ReadPersistentStateID(db) - if head+b.layers != id { - return fmt.Errorf("buffer layers (%d) cannot be applied on top of persisted state id (%d) to reach requested state id (%d)", b.layers, head, id) - } - var ( - start = time.Now() - batch = b.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, b.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(b.nodes), "bytes", common.StorageSize(size), "elapsed", common.PrettyDuration(time.Since(start))) - b.reset() - return nil -} - -// writeNodes writes the trie nodes into the provided database batch. -// Note this function will also inject all the newly written nodes -// into clean cache. -func writeNodes(batch ethdb.Batch, nodes map[common.Hash]map[string]*trienode.Node, clean *fastcache.Cache) (total int) { - for owner, subset := range nodes { - for path, n := range subset { - if n.IsDeleted() { - if owner == (common.Hash{}) { - rawdb.DeleteAccountTrieNode(batch, []byte(path)) - } else { - rawdb.DeleteStorageTrieNode(batch, owner, []byte(path)) - } - if clean != nil { - clean.Del(cacheKey(owner, []byte(path))) - } - } else { - if owner == (common.Hash{}) { - rawdb.WriteAccountTrieNode(batch, []byte(path), n.Blob) - } else { - rawdb.WriteStorageTrieNode(batch, owner, []byte(path), n.Blob) - } - if clean != nil { - clean.Set(cacheKey(owner, []byte(path)), n.Blob) - } - } - } - total += len(subset) - } - return total -} - -// cacheKey constructs the unique key of clean cache. -func cacheKey(owner common.Hash, path []byte) []byte { - if owner == (common.Hash{}) { - return path - } - return append(owner.Bytes(), path...) -} diff --git a/triedb/pathdb/nodes.go b/triedb/pathdb/nodes.go new file mode 100644 index 0000000000..dee8c872ac --- /dev/null +++ b/triedb/pathdb/nodes.go @@ -0,0 +1,246 @@ +// 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" + "fmt" + "io" + "maps" + + "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/rlp" + "github.com/ethereum/go-ethereum/trie/trienode" +) + +// nodeSet represents a collection of modified trie nodes resulting from a state +// transition, typically corresponding to a block execution. It can also represent +// the combined trie node set from several aggregated state transitions. +type nodeSet struct { + size uint64 // aggregated size of the trie node + nodes map[common.Hash]map[string]*trienode.Node // node set, mapped by owner and path +} + +// newNodeSet constructs the set with the provided dirty trie nodes. +func newNodeSet(nodes map[common.Hash]map[string]*trienode.Node) *nodeSet { + // Don't panic for the lazy callers, initialize the nil map instead + if nodes == nil { + nodes = make(map[common.Hash]map[string]*trienode.Node) + } + s := &nodeSet{nodes: nodes} + s.computeSize() + return s +} + +// computeSize calculates the database size of the held trie nodes. +func (s *nodeSet) computeSize() { + var size uint64 + for owner, subset := range s.nodes { + var prefix int + if owner != (common.Hash{}) { + prefix = common.HashLength // owner (32 bytes) for storage trie nodes + } + for path, n := range subset { + size += uint64(prefix + len(n.Blob) + len(path)) + } + } + s.size = size +} + +// updateSize updates the total cache size by the given delta. +func (s *nodeSet) updateSize(delta int64) { + size := int64(s.size) + delta + if size >= 0 { + s.size = uint64(size) + return + } + log.Error("Nodeset size underflow", "prev", common.StorageSize(s.size), "delta", common.StorageSize(delta)) + s.size = 0 +} + +// node retrieves the trie node with node path and its trie identifier. +func (s *nodeSet) node(owner common.Hash, path []byte) (*trienode.Node, bool) { + subset, ok := s.nodes[owner] + if !ok { + return nil, false + } + n, ok := subset[string(path)] + if !ok { + return nil, false + } + return n, true +} + +// merge integrates the provided dirty nodes into the set. The provided nodeset +// will remain unchanged, as it may still be referenced by other layers. +func (s *nodeSet) merge(set *nodeSet) { + var ( + delta int64 // size difference resulting from node merging + overwrite counter // counter of nodes being overwritten + ) + for owner, subset := range set.nodes { + var prefix int + if owner != (common.Hash{}) { + prefix = common.HashLength + } + current, exist := s.nodes[owner] + if !exist { + for path, n := range subset { + delta += int64(prefix + len(n.Blob) + len(path)) + } + // Perform a shallow copy of the map for the subset instead of claiming it + // directly from the provided nodeset to avoid potential concurrent map + // read/write issues. The nodes belonging to the original diff layer remain + // accessible even after merging. Therefore, ownership of the nodes map + // should still belong to the original layer, and any modifications to it + // should be prevented. + s.nodes[owner] = maps.Clone(subset) + continue + } + for path, n := range subset { + if orig, exist := current[path]; !exist { + delta += int64(prefix + len(n.Blob) + len(path)) + } else { + delta += int64(len(n.Blob) - len(orig.Blob)) + overwrite.add(prefix + len(orig.Blob) + len(path)) + } + current[path] = n + } + s.nodes[owner] = current + } + overwrite.report(gcTrieNodeMeter, gcTrieNodeBytesMeter) + s.updateSize(delta) +} + +// revertTo merges the provided trie nodes into the set. This should reverse the +// changes made by the most recent state transition. +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] + 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 merkle tree 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)) + } + // Ignore the clean node in the case described above. + 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)) + } + } + s.updateSize(delta) +} + +// journalNode represents a trie node persisted in the journal. +type journalNode struct { + Path []byte // Path of the node in the trie + Blob []byte // RLP-encoded trie node blob, nil means the node is deleted +} + +// journalNodes represents a list trie nodes belong to a single account +// or the main account trie. +type journalNodes struct { + Owner common.Hash + Nodes []journalNode +} + +// encode serializes the content of trie nodes into the provided writer. +func (s *nodeSet) encode(w io.Writer) error { + nodes := make([]journalNodes, 0, len(s.nodes)) + for owner, subset := range s.nodes { + entry := journalNodes{Owner: owner} + for path, node := range subset { + entry.Nodes = append(entry.Nodes, journalNode{ + Path: []byte(path), + Blob: node.Blob, + }) + } + nodes = append(nodes, entry) + } + return rlp.Encode(w, nodes) +} + +// decode deserializes the content from the rlp stream into the nodeset. +func (s *nodeSet) decode(r *rlp.Stream) error { + var encoded []journalNodes + if err := r.Decode(&encoded); err != nil { + return fmt.Errorf("load nodes: %v", err) + } + nodes := make(map[common.Hash]map[string]*trienode.Node) + for _, entry := range encoded { + subset := make(map[string]*trienode.Node) + for _, n := range entry.Nodes { + if len(n.Blob) > 0 { + subset[string(n.Path)] = trienode.New(crypto.Keccak256Hash(n.Blob), n.Blob) + } else { + subset[string(n.Path)] = trienode.NewDeleted() + } + } + nodes[entry.Owner] = subset + } + s.nodes = nodes + s.computeSize() + return nil +} + +// write flushes nodes into the provided database batch as a whole. +func (s *nodeSet) write(batch ethdb.Batch, clean *fastcache.Cache) int { + return writeNodes(batch, s.nodes, clean) +} + +// reset clears all cached trie node data. +func (s *nodeSet) reset() { + s.nodes = make(map[common.Hash]map[string]*trienode.Node) + s.size = 0 +} + +// dbsize returns the approximate size of db write. +func (s *nodeSet) dbsize() int { + var m int + for owner, nodes := range s.nodes { + if owner == (common.Hash{}) { + m += len(nodes) * len(rawdb.TrieNodeAccountPrefix) // database key prefix + } else { + m += len(nodes) * (len(rawdb.TrieNodeStoragePrefix)) // database key prefix + } + } + return m + int(s.size) +} diff --git a/triedb/pathdb/reader.go b/triedb/pathdb/reader.go index 6a58493ba6..a404409035 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" ) @@ -45,14 +47,14 @@ func (loc *nodeLoc) string() string { return fmt.Sprintf("loc: %s, depth: %d", loc.loc, loc.depth) } -// reader implements the database.Reader interface, providing the functionalities to +// reader implements the database.NodeReader interface, providing the functionalities to // retrieve trie nodes by wrapping the internal state layer. type reader struct { layer layer noHashCheck bool } -// Node implements database.Reader interface, retrieving the node with specified +// Node implements database.NodeReader interface, retrieving the node with specified // node info. Don't modify the returned byte slice since it's not deep-copied // and still be referenced by database. func (r *reader) Node(owner common.Hash, path []byte, hash common.Hash) ([]byte, error) { @@ -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,11 +86,65 @@ func (r *reader) Node(owner common.Hash, path []byte, hash common.Hash) ([]byte, return blob, nil } -// Reader retrieves a layer belonging to the given state root. -func (db *Database) Reader(root common.Hash) (database.Reader, error) { +// AccountRLP directly retrieves the account associated with a particular hash. +// An error will be returned if the read operation exits abnormally. Specifically, +// if the layer is already stale. +// +// Note: +// - the returned account data is not a copy, please don't modify it +// - no error will be returned if the requested account is not found in database +func (r *reader) AccountRLP(hash common.Hash) ([]byte, error) { + return r.layer.account(hash, 0) +} + +// 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) if layer == nil { return nil, fmt.Errorf("state %#x is not available", root) } 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 new file mode 100644 index 0000000000..81d34da5df --- /dev/null +++ b/triedb/pathdb/states.go @@ -0,0 +1,567 @@ +// 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 ( + "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. +type counter struct { + n int + size int +} + +// add size to the counter and increase the item counter. +func (c *counter) add(size int) { + c.n++ + c.size += size +} + +// report uploads the cached statistics to meters. +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 +} + +// mustAccount returns the account data associated with the specified address +// hash. The difference is this function will return an error if the account +// is not found. +func (s *stateSet) mustAccount(hash common.Hash) ([]byte, error) { + // If the account is known locally, return it + if data, ok := s.accountData[hash]; ok { + return data, nil + } + return nil, fmt.Errorf("account is not found, %x", hash) +} + +// 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 +} + +// mustStorage returns the storage slot associated with the specified address +// hash and storage key hash. The difference is this function will return an +// error if the storage slot is not found. +func (s *stateSet) mustStorage(accountHash, storageHash common.Hash) ([]byte, error) { + // 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, nil + } + } + return nil, fmt.Errorf("storage slot is not found, %x %x", accountHash, storageHash) +} + +// 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. +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. +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. + accountOrigin map[common.Address][]byte + + // StorageOrigin represents the storage data before the state transition, + // corresponding to storageData and deleted slots of destructSet. It's keyed + // by the account address and slot key hash. The nil value means the slot was + // not present. + storageOrigin map[common.Address]map[common.Hash][]byte + + // Memory size of the state data (accountOrigin and storageOrigin) + size uint64 +} + +// NewStateSetWithOrigin constructs the state set with the provided data. +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) + } + if storageOrigin == nil { + storageOrigin = make(map[common.Address]map[common.Hash][]byte) + } + // Count the memory size occupied by the set. Note that each slot key here + // uses 2*common.HashLength to keep consistent with the calculation method + // of stateSet. + var size int + for _, data := range accountOrigin { + size += common.HashLength + len(data) + } + for _, slots := range storageOrigin { + for _, data := range slots { + size += 2*common.HashLength + len(data) + } + } + set := newStates(accounts, storages) + return &StateSetWithOrigin{ + stateSet: set, + accountOrigin: accountOrigin, + storageOrigin: storageOrigin, + 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 + Accounts [][]byte + } + var accounts Accounts + for address, blob := range s.accountOrigin { + accounts.Addresses = append(accounts.Addresses, address) + accounts.Accounts = append(accounts.Accounts, blob) + } + if err := rlp.Encode(w, accounts); err != nil { + return err + } + // Encode storages + type Storage struct { + Address common.Address + Keys []common.Hash + Vals [][]byte + } + storages := make([]Storage, 0, len(s.storageOrigin)) + for address, slots := range s.storageOrigin { + 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{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 + Accounts [][]byte + } + var ( + accounts Accounts + accountSet = make(map[common.Address][]byte) + ) + if err := r.Decode(&accounts); err != nil { + return fmt.Errorf("load diff account origin set: %v", err) + } + for i := 0; i < len(accounts.Accounts); i++ { + accountSet[accounts.Addresses[i]] = accounts.Accounts[i] + } + s.accountOrigin = accountSet + + // Decode storage origin + type Storage struct { + Address common.Address + Keys []common.Hash + Vals [][]byte + } + var ( + storages []Storage + storageSet = make(map[common.Address]map[common.Hash][]byte) + ) + if err := r.Decode(&storages); err != nil { + return fmt.Errorf("load diff storage origin: %v", err) + } + 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.Vals[i] + } + } + s.storageOrigin = storageSet + return nil +} diff --git a/triedb/pathdb/states_test.go b/triedb/pathdb/states_test.go new file mode 100644 index 0000000000..f097e90e81 --- /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{ + {0xa}: {0xa0}, + {0xb}: {0xb0}, + {0xc}: {0xc0}, + }, + map[common.Hash]map[common.Hash][]byte{ + {0xa}: { + common.Hash{0x1}: {0x10}, + common.Hash{0x2}: {0x20}, + }, + {0xb}: { + common.Hash{0x1}: {0x10}, + }, + {0xc}: { + common.Hash{0x1}: {0x10}, + }, + }, + ) + b := newStates( + map[common.Hash][]byte{ + {0xa}: {0xa1}, + {0xb}: {0xb1}, + {0xc}: nil, // delete account + }, + map[common.Hash]map[common.Hash][]byte{ + {0xa}: { + common.Hash{0x1}: {0x11}, + common.Hash{0x2}: nil, // delete slot + common.Hash{0x3}: {0x31}, + }, + {0xb}: { + common.Hash{0x1}: {0x11}, + }, + {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{ + {0xa}: {0xa0}, + {0xb}: {0xb0}, + {0xc}: {0xc0}, + }, + map[common.Hash]map[common.Hash][]byte{ + {0xa}: { + common.Hash{0x1}: {0x10}, + common.Hash{0x2}: {0x20}, + }, + {0xb}: { + common.Hash{0x1}: {0x10}, + }, + {0xc}: { + common.Hash{0x1}: {0x10}, + }, + }, + ) + b := newStates( + map[common.Hash][]byte{ + {0xa}: {0xa1}, + {0xb}: {0xb1}, + {0xc}: nil, + }, + map[common.Hash]map[common.Hash][]byte{ + {0xa}: { + common.Hash{0x1}: {0x11}, + common.Hash{0x2}: nil, + common.Hash{0x3}: {0x31}, + }, + {0xb}: { + common.Hash{0x1}: {0x11}, + }, + {0xc}: { + common.Hash{0x1}: nil, + }, + }, + ) + a.merge(b) + a.revertTo( + map[common.Hash][]byte{ + {0xa}: {0xa0}, + {0xb}: {0xb0}, + {0xc}: {0xc0}, + }, + map[common.Hash]map[common.Hash][]byte{ + {0xa}: { + common.Hash{0x1}: {0x10}, + common.Hash{0x2}: {0x20}, + common.Hash{0x3}: nil, + }, + {0xb}: { + common.Hash{0x1}: {0x10}, + }, + {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{ + {0xa}: {0xa}, + }, + nil, + ) + a.merge(b) // create account 0xa + a.revertTo( + map[common.Hash][]byte{ + {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{ + {0xa}: {0xa}, + }, nil) // initial state with account 0xa + + b := newStates( + nil, + map[common.Hash]map[common.Hash][]byte{ + {0xa}: { + common.Hash{0x1}: {0x1}, + }, + }, + ) + a.merge(b) // create slot 0x1 + a.revertTo( + nil, + map[common.Hash]map[common.Hash][]byte{ + {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{ + {0x1}: {0x1}, + }, + map[common.Hash]map[common.Hash][]byte{ + {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{ + {0x1}: {0x1}, + }, + map[common.Hash]map[common.Hash][]byte{ + {0x1}: { + common.Hash{0x1}: {0x1}, + }, + }, + map[common.Address][]byte{ + {0x1}: {0x1}, + }, + map[common.Address]map[common.Hash][]byte{ + {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{ + {0xa}: {0xa0}, // common.HashLength+1 + {0xb}: {0xb0}, // common.HashLength+1 + {0xc}: {0xc0}, // common.HashLength+1 + }, + map[common.Hash]map[common.Hash][]byte{ + {0xa}: { + common.Hash{0x1}: {0x10}, // 2*common.HashLength+1 + common.Hash{0x2}: {0x20}, // 2*common.HashLength+1 + }, + {0xb}: { + common.Hash{0x1}: {0x10, 0x11, 0x12}, // 2*common.HashLength+3 + }, + {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{ + {0xa}: {0xa1, 0xa1}, // common.HashLength+2 + {0xb}: {0xb1, 0xb1, 0xb1}, // common.HashLength+3 + {0xc}: nil, // common.HashLength, account deletion + }, + map[common.Hash]map[common.Hash][]byte{ + {0xa}: { + common.Hash{0x1}: {0x11, 0x11, 0x11}, // 2*common.HashLength+3 + common.Hash{0x3}: {0x31, 0x31}, // 2*common.HashLength+2, slot creation + }, + {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 + {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{ + {0xa}: {0xa0}, + {0xb}: {0xb0}, + {0xc}: {0xc0}, + }, + map[common.Hash]map[common.Hash][]byte{ + {0xa}: { + common.Hash{0x1}: {0x10}, + common.Hash{0x2}: {0x20}, + common.Hash{0x3}: nil, // revert slot creation + }, + {0xb}: { + common.Hash{0x1}: {0x10, 0x11, 0x12}, + common.Hash{0x2}: nil, // revert slot creation + }, + {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 new file mode 100644 index 0000000000..fa432e0704 --- /dev/null +++ b/triedb/states.go @@ -0,0 +1,49 @@ +// 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 triedb + +import ( + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/triedb/pathdb" +) + +// StateSet represents a collection of mutated states during a state transition. +type StateSet struct { + 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 + StoragesOrigin map[common.Address]map[common.Hash][]byte // Original values of mutated storage slots in 'prefix-zero-trimmed' RLP format +} + +// NewStateSet initializes an empty state set. +func NewStateSet() *StateSet { + return &StateSet{ + Accounts: make(map[common.Hash][]byte), + AccountsOrigin: make(map[common.Address][]byte), + Storages: make(map[common.Hash]map[common.Hash][]byte), + StoragesOrigin: make(map[common.Address]map[common.Hash][]byte), + } +} + +// internal returns a state set for path database internal usage. +func (set *StateSet) internal() *pathdb.StateSetWithOrigin { + // the nil state set is possible in tests. + if set == nil { + return nil + } + return pathdb.NewStateSetWithOrigin(set.Accounts, set.Storages, set.AccountsOrigin, set.StoragesOrigin) +} diff --git a/version/version.go b/version/version.go new file mode 100644 index 0000000000..52dfa0281e --- /dev/null +++ b/version/version.go @@ -0,0 +1,24 @@ +// Copyright 2016 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 version + +const ( + Major = 1 // Major version component of the current release + Minor = 14 // Minor version component of the current release + Patch = 13 // Patch version component of the current release + Meta = "unstable" // Version metadata to append to the version string +)