From 08e6bdb550712503873fb2a138b30132cc36c481 Mon Sep 17 00:00:00 2001 From: Guillaume Ballet <3272758+gballet@users.noreply.github.com> Date: Fri, 6 Dec 2024 12:14:05 +0100 Subject: [PATCH 01/14] trie/utils: ensure master can generate a correct genesis for kaustinen7 (#30856) This imports the following fixes: - update gnark to 1.1.0 - update go-verkle to 0.2.2 - fix: main storage offset bug (gballet/go-ethereum#329) - fix: tree key generation (gballet/go-ethereum#401) --------- Signed-off-by: Guillaume Ballet <3272758+gballet@users.noreply.github.com> Co-authored-by: Ignacio Hagopian --- core/genesis_test.go | 2 +- go.mod | 26 +++++++++---------- go.sum | 59 ++++++++++++++++++++++---------------------- trie/utils/verkle.go | 16 +++--------- trie/verkle_test.go | 4 +-- 5 files changed, 50 insertions(+), 57 deletions(-) diff --git a/core/genesis_test.go b/core/genesis_test.go index 9eacf2024c..3ec87474e5 100644 --- a/core/genesis_test.go +++ b/core/genesis_test.go @@ -293,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/go.mod b/go.mod index c467cef169..b33c30f41d 100644 --- a/go.mod +++ b/go.mod @@ -15,16 +15,16 @@ require ( 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 @@ -64,13 +64,13 @@ require ( github.com/syndtr/goleveldb v1.0.1-0.20210819022825-2ae1ddf74ef7 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.26.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.17.0 golang.org/x/time v0.5.0 - golang.org/x/tools v0.20.0 + golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d google.golang.org/protobuf v1.34.2 gopkg.in/natefinch/lumberjack.v2 v2.2.1 gopkg.in/yaml.v3 v3.0.1 @@ -91,14 +91,14 @@ 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/deepmap/oapi-codegen v1.6.0 // indirect github.com/dlclark/regexp2 v1.7.0 // indirect @@ -135,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 19a7b9d25c..b61121664f 100644 --- a/go.sum +++ b/go.sum @@ -90,8 +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/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= @@ -124,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= @@ -166,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= @@ -261,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= @@ -363,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= @@ -469,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= @@ -527,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.26.0 h1:RrRspgV4mU+YwB4FYnuBoKsUapNIL5cohGAmSH3azsw= +golang.org/x/crypto v0.26.0/go.mod h1:GY7jblb9wI+FOo5y8/S2oY4zWP07AkOJ4+jxCqdqn54= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8= @@ -600,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= @@ -620,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= @@ -683,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= @@ -697,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.17.0 h1:XtiM5bkSOt+ewxlOE/aE/AKEHibwj/6gvWMl9Rsh0Qc= +golang.org/x/text v0.17.0/go.mod h1:BuEKDfySbSR4drPmRPG/7iBdf8hvFMuRexcpahXilzY= golang.org/x/time v0.0.0-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= @@ -749,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/trie/utils/verkle.go b/trie/utils/verkle.go index b785e512b1..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] @@ -273,17 +273,9 @@ func StorageSlotKeyWithEvaluatedAddress(evaluated *verkle.Point, storageKey []by } func pointToHash(evaluated *verkle.Point, suffix byte) []byte { - // The output of Byte() is big endian for banderwagon. This - // introduces an imbalance in the tree, because hashes are - // elements of a 253-bit field. This means more than half the - // tree would be empty. To avoid this problem, use a little - // endian commitment and chop the MSB. - bytes := evaluated.Bytes() - for i := 0; i < 16; i++ { - bytes[31-i], bytes[i] = bytes[i], bytes[31-i] - } - bytes[31] = suffix - return bytes[:] + retb := verkle.HashPointToBytes(evaluated) + retb[31] = suffix + return retb[:] } func evaluateAddressPoint(address []byte) *verkle.Point { diff --git a/trie/verkle_test.go b/trie/verkle_test.go index 4cd1717c0e..84eec2ed30 100644 --- a/trie/verkle_test.go +++ b/trie/verkle_test.go @@ -136,8 +136,8 @@ func TestVerkleRollBack(t *testing.T) { } } - // ensure there is some code in the 2nd group - keyOf2ndGroup := []byte{141, 124, 185, 236, 50, 22, 185, 39, 244, 47, 97, 209, 96, 235, 22, 13, 205, 38, 18, 201, 128, 223, 0, 59, 146, 199, 222, 119, 133, 13, 91, 0} + // ensure there is some code in the 2nd group of the 1st account + keyOf2ndGroup := utils.CodeChunkKeyWithEvaluatedAddress(tr.cache.Get(common.Address{1}.Bytes()), uint256.NewInt(128)) chunk, err := tr.root.Get(keyOf2ndGroup, nil) if err != nil { t.Fatalf("Failed to get account, %v", err) From a722adb774af1185463a0d413ade687ac26b1cb7 Mon Sep 17 00:00:00 2001 From: steven Date: Tue, 10 Dec 2024 02:29:19 +0800 Subject: [PATCH 02/14] core/txpool: remove unused parameter `local` (#30871) --- core/txpool/legacypool/legacypool.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/core/txpool/legacypool/legacypool.go b/core/txpool/legacypool/legacypool.go index 89ff86df02..70be7034ee 100644 --- a/core/txpool/legacypool/legacypool.go +++ b/core/txpool/legacypool/legacypool.go @@ -626,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, @@ -682,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 From a91dcf3ee5526bcf51b1301d67b397691baf7093 Mon Sep 17 00:00:00 2001 From: rjl493456442 Date: Tue, 10 Dec 2024 10:10:49 +0800 Subject: [PATCH 03/14] core/state: enable partial-functional reader (snapshot integration pt 3) (#30650) It's a pull request based on https://github.com/ethereum/go-ethereum/pull/30643 In this pull request, the partial functional state reader is enabled if **legacy snapshot is not enabled**. The tracked flat states in pathdb will be used to serve the state retrievals, as the second implementation to fasten the state access. This pull request should be a noop change in normal cases. --- core/state/database.go | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/core/state/database.go b/core/state/database.go index fff7f1519f..faf4954650 100644 --- a/core/state/database.go +++ b/core/state/database.go @@ -179,10 +179,19 @@ func (db *CachingDB) Reader(stateRoot common.Hash) (Reader, error) { // is optional and may be partially useful if it's not fully // generated. if db.snap != nil { + // If standalone state snapshot is available (hash scheme), + // then construct the legacy snap reader. snap := db.snap.Snapshot(stateRoot) if snap != nil { readers = append(readers, newFlatReader(snap)) } + } else { + // If standalone state snapshot is not available, try to construct + // the state reader with database. + reader, err := db.triedb.StateReader(stateRoot) + if err == nil { + readers = append(readers, newFlatReader(reader)) // state reader is optional + } } // Set up the trie reader, which is expected to always be available // as the gatekeeper unless the state is corrupted. From 75f847390f5a44614f26c255db67e76cb0f4c308 Mon Sep 17 00:00:00 2001 From: Martin HS Date: Tue, 10 Dec 2024 09:43:24 +0100 Subject: [PATCH 04/14] cmd/evm: consolidate evm output switches (#30849) This PR attempts to clean up some ambiguities and quirks from recent changes to evm flag handling. This PR mainly focuses on `evm run` subcommand, to use the same flags for configuring tracing/output options as `statetest/blocktest` does. Additionally, it adds quite a lot of tests for expected outputs of the various subcommands, to avoid accidental changes. --------- Co-authored-by: Felix Lange --- cmd/evm/internal/t8ntool/transition.go | 1 - cmd/evm/main.go | 18 +- cmd/evm/runner.go | 17 +- cmd/evm/t8n_test.go | 264 ++++--- cmd/evm/testdata/evmrun/1.out.1.txt | 0 cmd/evm/testdata/evmrun/1.out.2.txt | 3 + cmd/evm/testdata/evmrun/2.out.1.txt | 0 cmd/evm/testdata/evmrun/2.out.2.txt | 6 + cmd/evm/testdata/evmrun/3.out.1.txt | 13 + cmd/evm/testdata/evmrun/3.out.2.txt | 9 + cmd/evm/testdata/evmrun/4.out.1.txt | 13 + cmd/evm/testdata/evmrun/4.out.2.txt | 6 + cmd/evm/testdata/evmrun/5.out.1.txt | 0 cmd/evm/testdata/evmrun/5.out.2.txt | 16 + cmd/evm/testdata/evmrun/6.out.1.txt | 9 + cmd/evm/testdata/evmrun/6.out.2.txt | 1 + cmd/evm/testdata/evmrun/7.out.1.txt | 9 + cmd/evm/testdata/evmrun/7.out.2.txt | 925 +++++++++++++++++++++++++ cmd/evm/testdata/evmrun/8.out.1.txt | 9 + cmd/evm/testdata/evmrun/8.out.2.txt | 916 ++++++++++++++++++++++++ cmd/evm/testdata/statetest.json | 57 ++ eth/tracers/api.go | 1 - eth/tracers/logger/logger.go | 15 +- eth/tracers/tracers_test.go | 2 +- 24 files changed, 2188 insertions(+), 122 deletions(-) create mode 100644 cmd/evm/testdata/evmrun/1.out.1.txt create mode 100644 cmd/evm/testdata/evmrun/1.out.2.txt create mode 100644 cmd/evm/testdata/evmrun/2.out.1.txt create mode 100644 cmd/evm/testdata/evmrun/2.out.2.txt create mode 100644 cmd/evm/testdata/evmrun/3.out.1.txt create mode 100644 cmd/evm/testdata/evmrun/3.out.2.txt create mode 100644 cmd/evm/testdata/evmrun/4.out.1.txt create mode 100644 cmd/evm/testdata/evmrun/4.out.2.txt create mode 100644 cmd/evm/testdata/evmrun/5.out.1.txt create mode 100644 cmd/evm/testdata/evmrun/5.out.2.txt create mode 100644 cmd/evm/testdata/evmrun/6.out.1.txt create mode 100644 cmd/evm/testdata/evmrun/6.out.2.txt create mode 100644 cmd/evm/testdata/evmrun/7.out.1.txt create mode 100644 cmd/evm/testdata/evmrun/7.out.2.txt create mode 100644 cmd/evm/testdata/evmrun/8.out.1.txt create mode 100644 cmd/evm/testdata/evmrun/8.out.2.txt create mode 100755 cmd/evm/testdata/statetest.json diff --git a/cmd/evm/internal/t8ntool/transition.go b/cmd/evm/internal/t8ntool/transition.go index d8665d22d3..231576fa42 100644 --- a/cmd/evm/internal/t8ntool/transition.go +++ b/cmd/evm/internal/t8ntool/transition.go @@ -97,7 +97,6 @@ func Transition(ctx *cli.Context) error { DisableStack: ctx.Bool(TraceDisableStackFlag.Name), EnableMemory: ctx.Bool(TraceEnableMemoryFlag.Name), EnableReturnData: ctx.Bool(TraceEnableReturnDataFlag.Name), - Debug: true, } getTracer = func(txIndex int, txHash common.Hash, _ *params.ChainConfig) (*tracers.Tracer, io.WriteCloser, error) { traceFile, err := os.Create(filepath.Join(baseDir, fmt.Sprintf("trace-%d-%v.jsonl", txIndex, txHash.String()))) diff --git a/cmd/evm/main.go b/cmd/evm/main.go index 6e0345ce63..0e6f6fa5be 100644 --- a/cmd/evm/main.go +++ b/cmd/evm/main.go @@ -239,10 +239,20 @@ func tracerFromFlags(ctx *cli.Context) *tracing.Hooks { EnableReturnData: !ctx.Bool(TraceDisableReturnDataFlag.Name), } switch { - case ctx.Bool(TraceFlag.Name) && ctx.String(TraceFormatFlag.Name) == "struct": - return logger.NewStreamingStructLogger(config, os.Stderr).Hooks() - case ctx.Bool(TraceFlag.Name) && ctx.String(TraceFormatFlag.Name) == "json": - return logger.NewJSONLogger(config, os.Stderr) + 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): diff --git a/cmd/evm/runner.go b/cmd/evm/runner.go index 59ad8687d5..c67d3657e2 100644 --- a/cmd/evm/runner.go +++ b/cmd/evm/runner.go @@ -39,7 +39,6 @@ import ( "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" @@ -66,6 +65,7 @@ var runCommand = &cli.Command{ SenderFlag, ValueFlag, StatDumpFlag, + DumpFlag, }, traceFlags), } @@ -197,14 +197,6 @@ func timedExec(bench bool, execFunc func() ([]byte, uint64, error)) ([]byte, exe } func runCmd(ctx *cli.Context) error { - logconfig := &logger.Config{ - EnableMemory: !ctx.Bool(TraceDisableMemoryFlag.Name), - DisableStack: ctx.Bool(TraceDisableStackFlag.Name), - DisableStorage: ctx.Bool(TraceDisableStorageFlag.Name), - EnableReturnData: !ctx.Bool(TraceDisableReturnDataFlag.Name), - Debug: ctx.Bool(DebugFlag.Name), - } - var ( tracer *tracing.Hooks prestate *state.StateDB @@ -215,12 +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) { - tracer = logger.NewStreamingStructLogger(logconfig, os.Stderr).Hooks() - } - + tracer = tracerFromFlags(ctx) initialGas := ctx.Uint64(GasFlag.Name) genesisConfig := new(core.Genesis) genesisConfig.GasLimit = initialGas diff --git a/cmd/evm/t8n_test.go b/cmd/evm/t8n_test.go index 65723694f9..85caf010b3 100644 --- a/cmd/evm/t8n_test.go +++ b/cmd/evm/t8n_test.go @@ -341,98 +341,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 +580,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 +673,93 @@ func cmpJson(a, b []byte) (bool, error) { } return reflect.DeepEqual(j2, j), nil } + +// TestEVMTracing is a test that checks the tracing-output from evm. +func TestEVMTracing(t *testing.T) { + t.Parallel() + tt := cmdtest.NewTestCmd(t, nil) + for i, tc := range []struct { + base string + input []string + expectedTraces []string + }{ + { + base: "./testdata/31", + input: []string{"t8n", + "--input.alloc=./testdata/31/alloc.json", "--input.txs=./testdata/31/txs.json", + "--input.env=./testdata/31/env.json", "--state.fork=Cancun", + "--trace", + }, + expectedTraces: []string{"trace-0-0x88f5fbd1524731a81e49f637aa847543268a5aaf2a6b32a69d2c6d978c45dcfb.jsonl"}, + }, + { + base: "./testdata/31", + input: []string{"t8n", + "--input.alloc=./testdata/31/alloc.json", "--input.txs=./testdata/31/txs.json", + "--input.env=./testdata/31/env.json", "--state.fork=Cancun", + "--trace.tracer", ` +{ + result: function(){ + return "hello world" + }, + fault: function(){} +}`, + }, + expectedTraces: []string{"trace-0-0x88f5fbd1524731a81e49f637aa847543268a5aaf2a6b32a69d2c6d978c45dcfb.json"}, + }, + { + base: "./testdata/32", + input: []string{"t8n", + "--input.alloc=./testdata/32/alloc.json", "--input.txs=./testdata/32/txs.json", + "--input.env=./testdata/32/env.json", "--state.fork=Paris", + "--trace", "--trace.callframes", + }, + expectedTraces: []string{"trace-0-0x47806361c0fa084be3caa18afe8c48156747c01dbdfc1ee11b5aecdbe4fcf23e.jsonl"}, + }, + // TODO, make it possible to run tracers on statetests, e.g: + //{ + // base: "./testdata/31", + // input: []string{"statetest", "--trace", "--trace.tracer", `{ + // result: function(){ + // return "hello world" + // }, + // fault: function(){} + //}`, "./testdata/statetest.json"}, + // expectedTraces: []string{"trace-0-0x88f5fbd1524731a81e49f637aa847543268a5aaf2a6b32a69d2c6d978c45dcfb.json"}, + // }, + } { + // Place the output somewhere we can find it + outdir := t.TempDir() + args := append(tc.input, "--output.basedir", outdir) + + tt.Run("evm-test", args...) + tt.Logf("args: go run ./cmd/evm %v\n", args) + tt.WaitExit() + //t.Log(string(tt.Output())) + + // Compare the expected traces + for _, traceFile := range tc.expectedTraces { + haveFn := lineIterator(filepath.Join(outdir, traceFile)) + wantFn := lineIterator(filepath.Join(tc.base, traceFile)) + + for line := 0; ; line++ { + want, wErr := wantFn() + have, hErr := haveFn() + if want != have { + t.Fatalf("test %d, trace %v, line %d\nwant: %v\nhave: %v\n", + i, traceFile, line, want, have) + } + if wErr != nil && hErr != nil { + break + } + if wErr != nil { + t.Fatal(wErr) + } + if hErr != nil { + t.Fatal(hErr) + } + //t.Logf("%v\n", want) + } + } + } +} diff --git a/cmd/evm/testdata/evmrun/1.out.1.txt b/cmd/evm/testdata/evmrun/1.out.1.txt new file mode 100644 index 0000000000..e69de29bb2 diff --git a/cmd/evm/testdata/evmrun/1.out.2.txt b/cmd/evm/testdata/evmrun/1.out.2.txt new file mode 100644 index 0000000000..97d6c36c60 --- /dev/null +++ b/cmd/evm/testdata/evmrun/1.out.2.txt @@ -0,0 +1,3 @@ +{"pc":0,"op":96,"gas":"0x2540be400","gasCost":"0x3","memSize":0,"stack":[],"depth":1,"refund":0,"opName":"PUSH1"} +{"pc":2,"op":0,"gas":"0x2540be3fd","gasCost":"0x0","memSize":0,"stack":["0x40"],"depth":1,"refund":0,"opName":"STOP"} +{"output":"","gasUsed":"0x3"} diff --git a/cmd/evm/testdata/evmrun/2.out.1.txt b/cmd/evm/testdata/evmrun/2.out.1.txt new file mode 100644 index 0000000000..e69de29bb2 diff --git a/cmd/evm/testdata/evmrun/2.out.2.txt b/cmd/evm/testdata/evmrun/2.out.2.txt new file mode 100644 index 0000000000..4f31d87ccb --- /dev/null +++ b/cmd/evm/testdata/evmrun/2.out.2.txt @@ -0,0 +1,6 @@ +PUSH1 pc=00000000 gas=10000000000 cost=3 + +STOP pc=00000002 gas=9999999997 cost=0 +Stack: +00000000 0x40 + diff --git a/cmd/evm/testdata/evmrun/3.out.1.txt b/cmd/evm/testdata/evmrun/3.out.1.txt new file mode 100644 index 0000000000..44956f54f6 --- /dev/null +++ b/cmd/evm/testdata/evmrun/3.out.1.txt @@ -0,0 +1,13 @@ +{ + "root": "b444481d1367188172f8c6091e948aaa68bae763fd26d6b9e994306a66bf69f9", + "accounts": { + "pre(0x30d7a0694cb29af31b982480e11d7ebb003a3fca4026939149071f014689b142)": { + "balance": "0", + "nonce": 0, + "root": "0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421", + "codeHash": "0x3e48ef54b89079a075f3b8fc253c657a86b110a7aed3568c1517b10edf2c3eb6", + "code": "0x6040", + "key": "0x30d7a0694cb29af31b982480e11d7ebb003a3fca4026939149071f014689b142" + } + } +} diff --git a/cmd/evm/testdata/evmrun/3.out.2.txt b/cmd/evm/testdata/evmrun/3.out.2.txt new file mode 100644 index 0000000000..58e8cbfde7 --- /dev/null +++ b/cmd/evm/testdata/evmrun/3.out.2.txt @@ -0,0 +1,9 @@ +PUSH1 pc=00000000 gas=10000000000 cost=3 + +STOP pc=00000002 gas=9999999997 cost=0 +Stack: +00000000 0x40 + +INFO [12-03|10:37:15.827] Trie dumping started root=b44448..bf69f9 +WARN [12-03|10:37:15.827] Dump incomplete due to missing preimages missing=1 +INFO [12-03|10:37:15.827] Trie dumping complete accounts=1 elapsed="163.513µs" diff --git a/cmd/evm/testdata/evmrun/4.out.1.txt b/cmd/evm/testdata/evmrun/4.out.1.txt new file mode 100644 index 0000000000..44956f54f6 --- /dev/null +++ b/cmd/evm/testdata/evmrun/4.out.1.txt @@ -0,0 +1,13 @@ +{ + "root": "b444481d1367188172f8c6091e948aaa68bae763fd26d6b9e994306a66bf69f9", + "accounts": { + "pre(0x30d7a0694cb29af31b982480e11d7ebb003a3fca4026939149071f014689b142)": { + "balance": "0", + "nonce": 0, + "root": "0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421", + "codeHash": "0x3e48ef54b89079a075f3b8fc253c657a86b110a7aed3568c1517b10edf2c3eb6", + "code": "0x6040", + "key": "0x30d7a0694cb29af31b982480e11d7ebb003a3fca4026939149071f014689b142" + } + } +} diff --git a/cmd/evm/testdata/evmrun/4.out.2.txt b/cmd/evm/testdata/evmrun/4.out.2.txt new file mode 100644 index 0000000000..20441964e6 --- /dev/null +++ b/cmd/evm/testdata/evmrun/4.out.2.txt @@ -0,0 +1,6 @@ +{"pc":0,"op":96,"gas":"0x2540be400","gasCost":"0x3","memSize":0,"stack":[],"depth":1,"refund":0,"opName":"PUSH1"} +{"pc":2,"op":0,"gas":"0x2540be3fd","gasCost":"0x0","memSize":0,"stack":["0x40"],"depth":1,"refund":0,"opName":"STOP"} +{"output":"","gasUsed":"0x3"} +INFO [12-03|10:38:33.360] Trie dumping started root=b44448..bf69f9 +WARN [12-03|10:38:33.361] Dump incomplete due to missing preimages missing=1 +INFO [12-03|10:38:33.361] Trie dumping complete accounts=1 elapsed="240.811µs" diff --git a/cmd/evm/testdata/evmrun/5.out.1.txt b/cmd/evm/testdata/evmrun/5.out.1.txt new file mode 100644 index 0000000000..e69de29bb2 diff --git a/cmd/evm/testdata/evmrun/5.out.2.txt b/cmd/evm/testdata/evmrun/5.out.2.txt new file mode 100644 index 0000000000..356f731f07 --- /dev/null +++ b/cmd/evm/testdata/evmrun/5.out.2.txt @@ -0,0 +1,16 @@ +Pre-execution info: + - from: `0x000000000000000000000000000073656E646572` + - to: `0x0000000000000000000000007265636569766572` + - data: `` + - gas: `10000000000` + - value: `0` wei + +| Pc | Op | Cost | Refund | Stack | +|-------|-------------|------|-----------|-----------| +| 0 | PUSH1 | 3 | 0 | [] | +| 2 | STOP | 0 | 0 | [0x40] | + +Post-execution info: + - output: `` + - consumed gas: `3` + - error: `` diff --git a/cmd/evm/testdata/evmrun/6.out.1.txt b/cmd/evm/testdata/evmrun/6.out.1.txt new file mode 100644 index 0000000000..dc22c2ef3a --- /dev/null +++ b/cmd/evm/testdata/evmrun/6.out.1.txt @@ -0,0 +1,9 @@ +[ + { + "name": "00000006-naivefuzz-0", + "pass": false, + "stateRoot": "0xad1024c87b5548e77c937aa50f72b6cb620d278f4dd79bae7f78f71ff75af458", + "fork": "London", + "error": "post state root mismatch: got ad1024c87b5548e77c937aa50f72b6cb620d278f4dd79bae7f78f71ff75af458, want 0000000000000000000000000000000000000000000000000000000000000000" + } +] diff --git a/cmd/evm/testdata/evmrun/6.out.2.txt b/cmd/evm/testdata/evmrun/6.out.2.txt new file mode 100644 index 0000000000..2d841c4e34 --- /dev/null +++ b/cmd/evm/testdata/evmrun/6.out.2.txt @@ -0,0 +1 @@ +{"stateRoot": "0xad1024c87b5548e77c937aa50f72b6cb620d278f4dd79bae7f78f71ff75af458"} diff --git a/cmd/evm/testdata/evmrun/7.out.1.txt b/cmd/evm/testdata/evmrun/7.out.1.txt new file mode 100644 index 0000000000..dc22c2ef3a --- /dev/null +++ b/cmd/evm/testdata/evmrun/7.out.1.txt @@ -0,0 +1,9 @@ +[ + { + "name": "00000006-naivefuzz-0", + "pass": false, + "stateRoot": "0xad1024c87b5548e77c937aa50f72b6cb620d278f4dd79bae7f78f71ff75af458", + "fork": "London", + "error": "post state root mismatch: got ad1024c87b5548e77c937aa50f72b6cb620d278f4dd79bae7f78f71ff75af458, want 0000000000000000000000000000000000000000000000000000000000000000" + } +] diff --git a/cmd/evm/testdata/evmrun/7.out.2.txt b/cmd/evm/testdata/evmrun/7.out.2.txt new file mode 100644 index 0000000000..d81d96c6f6 --- /dev/null +++ b/cmd/evm/testdata/evmrun/7.out.2.txt @@ -0,0 +1,925 @@ +Pre-execution info: + - from: `0xa94f5374Fce5edBC8E2a8697C15331677e6EbF0B` + - to: `0x00000000000000000000000000000000000000f1` + - data: `0x81fbe24d1e33d7944b2e62ee0ff24811bbbcf8cb311e5617c80623dec4477cc14849fc042b9bbaebca9f03f66cca76c46353c5a68c2e134ef75f8c2425d9702f3a4bd3c5527e93d27579bdbd7d237eaa1c0278fce26479aaf11fb8d00e7478` + - gas: `737811` + - value: `1` wei + +| Pc | Op | Cost | Refund | Stack | +|-------|-------------|------|-----------|-----------| +| 0 | PUSH1 | 3 | 0 | [] | +| 2 | PUSH1 | 3 | 0 | [0x2] | +| 4 | SSTORE | 22100 | 0 | [0x2,0x3] | +| 5 | PUSH1 | 3 | 0 | [] | +| 7 | PUSH1 | 3 | 0 | [0x0] | +| 9 | PUSH1 | 3 | 0 | [0x0,0x0] | +| 11 | PUSH1 | 3 | 0 |[0x0,0x0,0x0] | +| 13 | PUSH1 | 3 | 0 |[0x0,0x0,0x0,0x0] | +| 15 | PUSH1 | 3 | 0 |[0x0,0x0,0x0,0x0,0x0] | +| 17 | GAS | 2 | 0 |[0x0,0x0,0x0,0x0,0x0,0x4] | +| 18 | CALLCODE | 704504 | 0 |[0x0,0x0,0x0,0x0,0x0,0x4,0xaeba5] | +| 19 | POP | 2 | 0 | [0x1] | +| 20 | PUSH32 | 3 | 0 | [] | +| 53 | PUSH1 | 3 | 0 |[0x600254506003545060016003557f7f6008545060006004557f60016004556000] | +| 55 | MSTORE | 6 | 0 |[0x600254506003545060016003557f7f6008545060006004557f60016004556000,0x0] | +| 56 | PUSH32 | 3 | 0 | [] | +| 89 | PUSH1 | 3 | 0 |[0x60045560006000600060006000606000527ff96000527f5af250600060006000] | +| 91 | MSTORE | 6 | 0 |[0x60045560006000600060006000606000527ff96000527f5af250600060006000,0x20] | +| 92 | PUSH32 | 3 | 0 | [] | +| 125 | PUSH1 | 3 | 0 |[0x606000527e60f45af45060006000600060006020527f600060f55a6020527ff1] | +| 127 | MSTORE | 6 | 0 |[0x606000527e60f45af45060006000600060006020527f600060f55a6020527ff1,0x40] | +| 128 | PUSH32 | 3 | 0 | [] | +| 161 | PUSH1 | 3 | 0 |[0x50f001075205846a44a283446020527f8ca2600060006040527f600060006004] | +| 163 | MSTORE | 6 | 0 |[0x50f001075205846a44a283446020527f8ca2600060006040527f600060006004,0x60] | +| 164 | PUSH32 | 3 | 0 | [] | +| 197 | PUSH1 | 3 | 0 |[0x5af4506040527f519930847f3b631c54a49b5f600354503260406060527f527f] | +| 199 | MSTORE | 6 | 0 |[0x5af4506040527f519930847f3b631c54a49b5f600354503260406060527f527f,0x80] | +| 200 | PUSH32 | 3 | 0 | [] | +| 233 | PUSH1 | 3 | 0 |[0x77306b60006000600060006060527f6000600c5af15060006000600060006080] | +| 235 | MSTORE | 6 | 0 |[0x77306b60006000600060006060527f6000600c5af15060006000600060006080,0xa0] | +| 236 | PUSH32 | 3 | 0 | [] | +| 269 | PUSH1 | 3 | 0 |[0x527f60f85af450506060527f066001600255606080527f035450600060005560] | +| 271 | MSTORE | 6 | 0 |[0x527f60f85af450506060527f066001600255606080527f035450600060005560,0xc0] | +| 272 | PUSH31 | 3 | 0 | [] | +| 304 | PUSH1 | 3 | 0 |[0x6060a0527f01556c3394fff4607f7f1684317b6080527f387b9f60a0527f19] | +| 306 | MSTORE | 6 | 0 |[0x6060a0527f01556c3394fff4607f7f1684317b6080527f387b9f60a0527f19,0xe0] | +| 307 | PUSH32 | 3 | 0 | [] | +| 340 | PUSH2 | 3 | 0 |[0x20700184809d60c0527f60015450011899016e6009ff60026001556000527f9f] | +| 343 | MSTORE | 6 | 0 |[0x20700184809d60c0527f60015450011899016e6009ff60026001556000527f9f,0x100] | +| 344 | PUSH32 | 3 | 0 | [] | +| 377 | PUSH2 | 3 | 0 |[0x60a05260c0527f7f600060e0527f527f9981600160045582600eff6000600060] | +| 380 | MSTORE | 6 | 0 |[0x60a05260c0527f7f600060e0527f527f9981600160045582600eff6000600060,0x120] | +| 381 | PUSH31 | 3 | 0 | [] | +| 413 | PUSH2 | 3 | 0 |[0x600060f65af45060006060e052610100527f7f6060c0527f20527e60006000] | +| 416 | MSTORE | 6 | 0 |[0x600060f65af45060006060e052610100527f7f6060c0527f20527e60006000,0x140] | +| 417 | PUSH32 | 3 | 0 | [] | +| 450 | PUSH2 | 3 | 0 |[0x6020527f60f75af4501d7f1903166660006000610120527f60610100527e6000] | +| 453 | MSTORE | 6 | 0 |[0x6020527f60f75af4501d7f1903166660006000610120527f60610100527e6000,0x160] | +| 454 | PUSH32 | 3 | 0 | [] | +| 487 | PUSH2 | 3 | 0 |[0x600060e0527f60046040527f5af150600060006000604052610140527f7f6000] | +| 490 | MSTORE | 6 | 0 |[0x600060e0527f60046040527f5af150600060006000604052610140527f7f6000,0x180] | +| 491 | PUSH32 | 3 | 0 | [] | +| 524 | PUSH2 | 3 | 0 |[0x6009610120527f5af4503c95138e5b8f610100527f7f605a6000536060610160] | +| 527 | MSTORE | 6 | 0 |[0x6009610120527f5af4503c95138e5b8f610100527f7f605a6000536060610160,0x1a0] | +| 528 | PUSH32 | 3 | 0 | [] | +| 561 | PUSH2 | 3 | 0 |[0x527f527f6031600153606b60610140527f02536010606060527f035360456061] | +| 564 | MSTORE | 6 | 0 |[0x527f527f6031600153606b60610140527f02536010606060527f035360456061,0x1c0] | +| 565 | PUSH32 | 3 | 0 | [] | +| 598 | PUSH2 | 3 | 0 |[0x120610180527f527f04536060600553600160608052610160527f7e527f6006] | +| 601 | MSTORE | 6 | 0 |[0x120610180527f527f04536060600553600160608052610160527f7e527f6006,0x1e0] | +| 602 | PUSH32 | 3 | 0 | [] | +| 635 | PUSH2 | 3 | 0 |[0x536060600753606101a0527f02600853606080610140527f527f556009536060] | +| 638 | MSTORE | 6 | 0 |[0x536060600753606101a0527f02600853606080610140527f527f556009536060,0x200] | +| 639 | PUSH32 | 3 | 0 | [] | +| 672 | PUSH2 | 3 | 0 |[0x610180527f600a53600160a06101c0527f527f600b536060600c6020527f5360] | +| 675 | MSTORE | 6 | 0 |[0x610180527f600a53600160a06101c0527f527f600b536060600c6020527f5360,0x220] | +| 676 | PUSH31 | 3 | 0 | [] | +| 708 | PUSH2 | 3 | 0 |[0x60610160527f0d53606101a0527f55606101e0527f0e60a0527f536060600f] | +| 711 | MSTORE | 6 | 0 |[0x60610160527f0d53606101a0527f55606101e0527f0e60a0527f536060600f,0x240] | +| 712 | PUSH32 | 3 | 0 | [] | +| 745 | PUSH2 | 3 | 0 |[0x536060c0527f01601053606060115360026101806101610200527fc0527f527f] | +| 748 | MSTORE | 6 | 0 |[0x536060c0527f01601053606060115360026101806101610200527fc0527f527f,0x260] | +| 749 | PUSH32 | 3 | 0 | [] | +| 782 | PUSH2 | 3 | 0 |[0x601253606040527f55601353606060c0527f60145360e0527f6000610220527f] | +| 785 | MSTORE | 6 | 0 |[0x601253606040527f55601353606060c0527f60145360e0527f6000610220527f,0x280] | +| 786 | PUSH32 | 3 | 0 | [] | +| 819 | PUSH2 | 3 | 0 |[0x6015536101e0527f60606101a0527f601653600060175360f360185360196060] | +| 822 | MSTORE | 6 | 0 |[0x6015536101e0527f60606101a0527f601653600060175360f360185360196060,0x2a0] | +| 823 | PUSH32 | 3 | 0 | [] | +| 856 | PUSH2 | 3 | 0 |[0x610240527f605260006060e052610200527f610100527f7f806101c0527f5360] | +| 859 | MSTORE | 7 | 0 |[0x610240527f605260006060e052610200527f610100527f7f806101c0527f5360,0x2c0] | +| 860 | PUSH32 | 3 | 0 | [] | +| 893 | PUSH2 | 3 | 0 |[0xf360815360610260527f8260006000f060006000600060610220527e845af450] | +| 896 | MSTORE | 6 | 0 |[0xf360815360610260527f8260006000f060006000600060610220527e845af450,0x2e0] | +| 897 | PUSH32 | 3 | 0 | [] | +| 930 | PUSH2 | 3 | 0 |[0x506000600061016101e0610280527f527f20527f60610100527e600060006003] | +| 933 | MSTORE | 6 | 0 |[0x506000600061016101e0610280527f527f20527f60610100527e600060006003,0x300] | +| 934 | PUSH32 | 3 | 0 | [] | +| 967 | PUSH2 | 3 | 0 |[0x610240527f5af15060005450c760006102a0527f6002551309f562610200527f] | +| 970 | MSTORE | 6 | 0 |[0x610240527f5af15060005450c760006102a0527f6002551309f562610200527f,0x320] | +| 971 | PUSH32 | 3 | 0 | [] | +| 1004 | PUSH2 | 3 | 0 |[0x66a486610140527f6b00610260527f1d457161016102c0527f20527f60005450] | +| 1007 | MSTORE | 6 | 0 |[0x66a486610140527f6b00610260527f1d457161016102c0527f20527f60005450,0x340] | +| 1008 | PUSH32 | 3 | 0 | [] | +| 1041 | PUSH2 | 3 | 0 |[0x1c641d373c7f60045450610220527f6000600155610280527f6102e0527f6005] | +| 1044 | MSTORE | 6 | 0 |[0x1c641d373c7f60045450610220527f6000600155610280527f6102e0527f6005,0x360] | +| 1045 | PUSH32 | 3 | 0 | [] | +| 1078 | PUSH2 | 3 | 0 |[0x54610160527f50600160025560085450610140527f60006002610240527f6103] | +| 1081 | MSTORE | 6 | 0 |[0x54610160527f50600160025560085450610140527f60006002610240527f6103,0x380] | +| 1082 | PUSH31 | 3 | 0 | [] | +| 1114 | PUSH2 | 3 | 0 |[0x527f6102a0527f557fd86000606000527e600060610180527e600060005af1] | +| 1117 | MSTORE | 6 | 0 |[0x527f6102a0527f557fd86000606000527e600060610180527e600060005af1,0x3a0] | +| 1118 | PUSH32 | 3 | 0 | [] | +| 1151 | PUSH2 | 3 | 0 |[0x508612610320527f17145147356102c0527f610260527f610160527f5198a37e] | +| 1154 | MSTORE | 6 | 0 |[0x508612610320527f17145147356102c0527f610260527f610160527f5198a37e,0x3c0] | +| 1155 | PUSH32 | 3 | 0 | [] | +| 1188 | PUSH2 | 3 | 0 |[0x127a7efa7c600052610340527f6101a0527f606020527f6102e0527f60602053] | +| 1191 | MSTORE | 7 | 0 |[0x127a7efa7c600052610340527f6101a0527f606020527f6102e0527f60602053,0x3e0] | +| 1192 | PUSH32 | 3 | 0 | [] | +| 1225 | PUSH2 | 3 | 0 |[0x60610280527ff760215360ff60610360527f225360610180527fdb6023536037] | +| 1228 | MSTORE | 6 | 0 |[0x60610280527ff760215360ff60610360527f225360610180527fdb6023536037,0x400] | +| 1229 | PUSH32 | 3 | 0 | [] | +| 1262 | PUSH2 | 3 | 0 |[0x60610300527f6101c0527f24536075606102610380527fa0527f2553609f6060] | +| 1265 | MSTORE | 6 | 0 |[0x60610300527f6101c0527f24536075606102610380527fa0527f2553609f6060,0x420] | +| 1266 | PUSH32 | 3 | 0 | [] | +| 1299 | PUSH2 | 3 | 0 |[0x40527f265360fe60275360610320527f8f60286101a0526103a0527f7f536061] | +| 1302 | MSTORE | 6 | 0 |[0x40527f265360fe60275360610320527f8f60286101a0526103a0527f7f536061,0x440] | +| 1303 | PUSH32 | 3 | 0 | [] | +| 1336 | PUSH2 | 3 | 0 |[0x1e0527f6102c0527f0b6029536060602a53600060610340527f2b536103c052] | +| 1339 | MSTORE | 6 | 0 |[0x1e0527f6102c0527f0b6029536060602a53600060610340527f2b536103c052,0x460] | +| 1340 | PUSH32 | 3 | 0 | [] | +| 1373 | PUSH2 | 3 | 0 |[0x7f6060602c53606052606060805360006061016102e0527f610200527fc05261] | +| 1376 | MSTORE | 6 | 0 |[0x7f6060602c53606052606060805360006061016102e0527f610200527fc05261,0x480] | +| 1377 | PUSH32 | 3 | 0 | [] | +| 1410 | PUSH2 | 3 | 0 |[0x36103e0527f60527f7f81536060608253602d60835360536084536060608553] | +| 1413 | MSTORE | 6 | 0 |[0x36103e0527f60527f7f81536060608253602d60835360536084536060608553,0x4a0] | +| 1414 | PUSH32 | 3 | 0 | [] | +| 1447 | PUSH2 | 3 | 0 |[0x60fd61030052610400527f7f6086610380527f536060610220527f6087536101] | +| 1450 | MSTORE | 6 | 0 |[0x60fd61030052610400527f7f6086610380527f536060610220527f6087536101,0x4c0] | +| 1451 | PUSH32 | 3 | 0 | [] | +| 1484 | PUSH2 | 3 | 0 |[0xe0527f602e608853605360610420527f89536060608a61036103a0527f20527f] | +| 1487 | MSTORE | 7 | 0 |[0xe0527f602e608853605360610420527f89536060608a61036103a0527f20527f,0x4e0] | +| 1488 | PUSH32 | 3 | 0 | [] | +| 1521 | PUSH2 | 3 | 0 |[0x53602f608b536060608c610240527f53610440527f6000608d5360f361020052] | +| 1524 | MSTORE | 6 | 0 |[0x53602f608b536060608c610240527f53610440527f6000608d5360f361020052,0x500] | +| 1525 | PUSH32 | 3 | 0 | [] | +| 1558 | PUSH2 | 3 | 0 |[0x60606103c0527f610220610340527f53608e610221610460527f536053610222] | +| 1561 | MSTORE | 6 | 0 |[0x60606103c0527f610220610340527f53608e610221610460527f536053610222,0x520] | +| 1562 | PUSH32 | 3 | 0 | [] | +| 1595 | PUSH2 | 3 | 0 |[0x536060610260527f610223536103e0527f600061022453606061610480527f03] | +| 1598 | MSTORE | 6 | 0 |[0x536060610260527f610223536103e0527f600061022453606061610480527f03,0x540] | +| 1599 | PUSH32 | 3 | 0 | [] | +| 1632 | PUSH2 | 3 | 0 |[0x60527f61022553608f61022653606061022753600061610400527f0261028061] | +| 1635 | MSTORE | 6 | 0 |[0x60527f61022553608f61022653606061022753600061610400527f0261028061,0x560] | +| 1636 | PUSH32 | 3 | 0 | [] | +| 1669 | PUSH2 | 3 | 0 |[0x4a0527f527f28536060610229610380527f53600061022a5360f561022b5360] | +| 1672 | MSTORE | 6 | 0 |[0x4a0527f527f28536060610229610380527f53600061022a5360f561022b5360,0x580] | +| 1673 | PUSH32 | 3 | 0 | [] | +| 1706 | PUSH2 | 3 | 0 |[0x610420526104c0527f7f6061022c53600061022d536102a0527f60606103a052] | +| 1709 | MSTORE | 7 | 0 |[0x610420526104c0527f7f6061022c53600061022d536102a0527f60606103a052,0x5a0] | +| 1710 | PUSH32 | 3 | 0 | [] | +| 1743 | PUSH2 | 3 | 0 |[0x7f61022e53600061026104e0527f2f610440527f536060610230536000610231] | +| 1746 | MSTORE | 6 | 0 |[0x7f61022e53600061026104e0527f2f610440527f536060610230536000610231,0x5c0] | +| 1747 | PUSH32 | 3 | 0 | [] | +| 1780 | PUSH2 | 3 | 0 |[0x53606061023253600061026103c0610500527f527fc0527f61610460527f0233] | +| 1783 | MSTORE | 6 | 0 |[0x53606061023253600061026103c0610500527f527fc0527f61610460527f0233,0x5e0] | +| 1784 | PUSH32 | 3 | 0 | [] | +| 1817 | PUSH2 | 3 | 0 |[0x53606061023453600061023553608561023653610520527f605a610237536061] | +| 1820 | MSTORE | 6 | 0 |[0x53606061023453600061023553608561023653610520527f605a610237536061,0x600] | +| 1821 | PUSH32 | 3 | 0 | [] | +| 1854 | PUSH2 | 3 | 0 |[0x3e052610480527f7ff261026102e0526038610300536053610540527f610301] | +| 1857 | MSTORE | 6 | 0 |[0x3e052610480527f7ff261026102e0526038610300536053610540527f610301,0x620] | +| 1858 | PUSH32 | 3 | 0 | [] | +| 1891 | PUSH2 | 3 | 0 |[0x536060610302536050610303536104a0527f60610400527f6161030453610560] | +| 1894 | MSTORE | 7 | 0 |[0x536060610302536050610303536104a0527f60610400527f6161030453610560,0x640] | +| 1895 | PUSH32 | 3 | 0 | [] | +| 1928 | PUSH2 | 3 | 0 |[0x527f6002610305536039610306536053610307536060616104c0527f03085360] | +| 1931 | MSTORE | 6 | 0 |[0x527f6002610305536039610306536053610307536060616104c0527f03085360,0x660] | +| 1932 | PUSH32 | 3 | 0 | [] | +| 1965 | PUSH2 | 3 | 0 |[0x5061610580527f610420527f030953606161030a53600261030b53603a61030c] | +| 1968 | MSTORE | 6 | 0 |[0x5061610580527f610420527f030953606161030a53600261030b53603a61030c,0x680] | +| 1969 | PUSH32 | 3 | 0 | [] | +| 2002 | PUSH2 | 3 | 0 |[0x536104e0527f606105a0527f5361030d53606161030e610440527f5360026103] | +| 2005 | MSTORE | 6 | 0 |[0x536104e0527f606105a0527f5361030d53606161030e610440527f5360026103,0x6a0] | +| 2006 | PUSH32 | 3 | 0 | [] | +| 2039 | PUSH2 | 3 | 0 |[0xf53603b61031053606061616105c0527f0500527f03115360006103125360f3] | +| 2042 | MSTORE | 6 | 0 |[0xf53603b61031053606061616105c0527f0500527f03115360006103125360f3,0x6c0] | +| 2043 | PUSH32 | 3 | 0 | [] | +| 2076 | PUSH2 | 3 | 0 |[0x61031353616104605260036104805360146105e0527f61048153610520527f60] | +| 2079 | MSTORE | 7 | 0 |[0x61031353616104605260036104805360146105e0527f61048153610520527f60,0x6e0] | +| 2080 | PUSH32 | 3 | 0 | [] | +| 2113 | PUSH2 | 3 | 0 |[0x60610482536000610483536060610484536000610485610600527f5360f06104] | +| 2116 | MSTORE | 6 | 0 |[0x60610482536000610483536060610484536000610485610600527f5360f06104,0x700] | +| 2117 | PUSH32 | 3 | 0 | [] | +| 2150 | PUSH2 | 3 | 0 |[0x86536060610540527f610487536000610488536060610489536000610620527f] | +| 2153 | MSTORE | 6 | 0 |[0x86536060610540527f610487536000610488536060610489536000610620527f,0x720] | +| 2154 | PUSH32 | 3 | 0 | [] | +| 2187 | PUSH2 | 3 | 0 |[0x61048a53606061048b5360006104610560527f8c53606061048d53600061048e] | +| 2190 | MSTORE | 6 | 0 |[0x61048a53606061048b5360006104610560527f8c53606061048d53600061048e,0x740] | +| 2191 | PUSH32 | 3 | 0 | [] | +| 2224 | PUSH2 | 3 | 0 |[0x610640527f53608461048f53605a6104905360f4610491536105805260606105] | +| 2227 | MSTORE | 7 | 0 |[0x610640527f53608461048f53605a6104905360f4610491536105805260606105,0x760] | +| 2228 | PUSH32 | 3 | 0 | [] | +| 2261 | PUSH2 | 3 | 0 |[0xa053605061610660527f05a15360616105a25360046105a35360926105a45360] | +| 2264 | MSTORE | 6 | 0 |[0xa053605061610660527f05a15360616105a25360046105a35360926105a45360,0x780] | +| 2265 | PUSH32 | 3 | 0 | [] | +| 2298 | PUSH2 | 3 | 0 |[0x536105a55360606105a6610680527f5360506105a75360616105a85360046105] | +| 2301 | MSTORE | 6 | 0 |[0x536105a55360606105a6610680527f5360506105a75360616105a85360046105,0x7a0] | +| 2302 | PUSH32 | 3 | 0 | [] | +| 2335 | PUSH2 | 3 | 0 |[0xa95360936105aa5360536105ab53606106a0527f616105ac5360046105ad5360] | +| 2338 | MSTORE | 6 | 0 |[0xa95360936105aa5360536105ab53606106a0527f616105ac5360046105ad5360,0x7c0] | +| 2339 | PUSH32 | 3 | 0 | [] | +| 2372 | PUSH2 | 3 | 0 |[0x946105ae5360606105af5360006105b05360f3616106c05260056106e05360b1] | +| 2375 | MSTORE | 7 | 0 |[0x946105ae5360606105af5360006105b05360f3616106c05260056106e05360b1,0x7e0] | +| 2376 | PUSH32 | 3 | 0 | [] | +| 2409 | PUSH2 | 3 | 0 |[0x6106e15360536106e25360606106e35360006106e45360616106e55360056106] | +| 2412 | MSTORE | 6 | 0 |[0x6106e15360536106e25360606106e35360006106e45360616106e55360056106,0x800] | +| 2413 | PUSH32 | 3 | 0 | [] | +| 2446 | PUSH2 | 3 | 0 |[0xe65360b26106e75360606106e85360006106e95360606106ea5360006106eb53] | +| 2449 | MSTORE | 6 | 0 |[0xe65360b26106e75360606106e85360006106e95360606106ea5360006106eb53,0x820] | +| 2450 | PUSH32 | 3 | 0 | [] | +| 2483 | PUSH2 | 3 | 0 |[0x60f56106ec5360606106ed5360006106ee5360606106ef5360006106f0536060] | +| 2486 | MSTORE | 6 | 0 |[0x60f56106ec5360606106ed5360006106ee5360606106ef5360006106f0536060,0x840] | +| 2487 | PUSH32 | 3 | 0 | [] | +| 2520 | PUSH2 | 3 | 0 |[0x6106f15360006106f25360606106f35360006106f45360606106f55360006106] | +| 2523 | MSTORE | 7 | 0 |[0x6106f15360006106f25360606106f35360006106f45360606106f55360006106,0x860] | +| 2524 | PUSH32 | 3 | 0 | [] | +| 2557 | PUSH2 | 3 | 0 |[0xf65360856106f753605a6106f85360f16106f95360506106fa5360506106fb53] | +| 2560 | MSTORE | 6 | 0 |[0xf65360856106f753605a6106f85360f16106f95360506106fa5360506106fb53,0x880] | +| 2561 | PUSH1 | 3 | 0 | [] | +| 2563 | PUSH2 | 3 | 0 | [0x61] | +| 2566 | MSTORE8 | 6 | 0 |[0x61,0x8a0] | +| 2567 | PUSH1 | 3 | 0 | [] | +| 2569 | PUSH2 | 3 | 0 | [0x6] | +| 2572 | MSTORE8 | 3 | 0 |[0x6,0x8a1] | +| 2573 | PUSH1 | 3 | 0 | [] | +| 2575 | PUSH2 | 3 | 0 | [0xfc] | +| 2578 | MSTORE8 | 3 | 0 |[0xfc,0x8a2] | +| 2579 | PUSH1 | 3 | 0 | [] | +| 2581 | PUSH2 | 3 | 0 | [0x60] | +| 2584 | MSTORE8 | 3 | 0 |[0x60,0x8a3] | +| 2585 | PUSH1 | 3 | 0 | [] | +| 2587 | PUSH2 | 3 | 0 | [0x0] | +| 2590 | MSTORE8 | 3 | 0 |[0x0,0x8a4] | +| 2591 | PUSH1 | 3 | 0 | [] | +| 2593 | PUSH2 | 3 | 0 | [0xf3] | +| 2596 | MSTORE8 | 3 | 0 |[0xf3,0x8a5] | +| 2597 | PUSH2 | 3 | 0 | [] | +| 2600 | PUSH1 | 3 | 0 | [0x8a6] | +| 2602 | PUSH1 | 3 | 0 |[0x8a6,0x0] | +| 2604 | CREATE | 32000 | 0 |[0x8a6,0x0,0x0] | +| 0 | PUSH1 | 3 | 0 | [] | +| 2 | SLOAD | 2100 | 0 | [0x2] | +| 3 | POP | 2 | 0 | [0x0] | +| 4 | PUSH1 | 3 | 0 | [] | +| 6 | SLOAD | 2100 | 0 | [0x3] | +| 7 | POP | 2 | 0 | [0x0] | +| 8 | PUSH1 | 3 | 0 | [] | +| 10 | PUSH1 | 3 | 0 | [0x1] | +| 12 | SSTORE | 20000 | 0 | [0x1,0x3] | +| 13 | PUSH32 | 3 | 0 | [] | +| 46 | PUSH1 | 3 | 0 |[0x7f6008545060006004557f600160045560006004556000600060006000600060] | +| 48 | MSTORE | 6 | 0 |[0x7f6008545060006004557f600160045560006004556000600060006000600060,0x0] | +| 49 | PUSH32 | 3 | 0 | [] | +| 82 | PUSH1 | 3 | 0 |[0xf96000527f5af250600060006000606000527e60f45af4506000600060006000] | +| 84 | MSTORE | 6 | 0 |[0xf96000527f5af250600060006000606000527e60f45af4506000600060006000,0x20] | +| 85 | PUSH32 | 3 | 0 | [] | +| 118 | PUSH1 | 3 | 0 |[0x600060f55a6020527ff150f001075205846a44a283446020527f8ca260006000] | +| 120 | MSTORE | 6 | 0 |[0x600060f55a6020527ff150f001075205846a44a283446020527f8ca260006000,0x40] | +| 121 | PUSH32 | 3 | 0 | [] | +| 154 | PUSH1 | 3 | 0 |[0x6000600060045af4506040527f519930847f3b631c54a49b5f60035450326040] | +| 156 | MSTORE | 6 | 0 |[0x6000600060045af4506040527f519930847f3b631c54a49b5f60035450326040,0x60] | +| 157 | PUSH32 | 3 | 0 | [] | +| 190 | PUSH1 | 3 | 0 |[0x527f77306b60006000600060006060527f6000600c5af1506000600060006000] | +| 192 | MSTORE | 6 | 0 |[0x527f77306b60006000600060006060527f6000600c5af1506000600060006000,0x80] | +| 193 | PUSH32 | 3 | 0 | [] | +| 226 | PUSH1 | 3 | 0 |[0x60f85af450506060527f066001600255606080527f0354506000600055600060] | +| 228 | MSTORE | 6 | 0 |[0x60f85af450506060527f066001600255606080527f0354506000600055600060,0xa0] | +| 229 | PUSH32 | 3 | 0 | [] | +| 262 | PUSH1 | 3 | 0 |[0x1556c3394fff4607f7f1684317b6080527f387b9f60a0527f1920700184809d] | +| 264 | MSTORE | 6 | 0 |[0x1556c3394fff4607f7f1684317b6080527f387b9f60a0527f1920700184809d,0xc0] | +| 265 | PUSH32 | 3 | 0 | [] | +| 298 | PUSH1 | 3 | 0 |[0x60015450011899016e6009ff60026001556000527f9f60a05260c0527f7f6000] | +| 300 | MSTORE | 6 | 0 |[0x60015450011899016e6009ff60026001556000527f9f60a05260c0527f7f6000,0xe0] | +| 301 | PUSH32 | 3 | 0 | [] | +| 334 | PUSH2 | 3 | 0 |[0x527f9981600160045582600eff600060006000600060f65af45060006060e052] | +| 337 | MSTORE | 6 | 0 |[0x527f9981600160045582600eff600060006000600060f65af45060006060e052,0x100] | +| 338 | PUSH32 | 3 | 0 | [] | +| 371 | PUSH2 | 3 | 0 |[0x7f6060c0527f20527e600060006020527f60f75af4501d7f1903166660006000] | +| 374 | MSTORE | 6 | 0 |[0x7f6060c0527f20527e600060006020527f60f75af4501d7f1903166660006000,0x120] | +| 375 | PUSH32 | 3 | 0 | [] | +| 408 | PUSH2 | 3 | 0 |[0x60610100527e6000600060e0527f60046040527f5af150600060006000604052] | +| 411 | MSTORE | 6 | 0 |[0x60610100527e6000600060e0527f60046040527f5af150600060006000604052,0x140] | +| 412 | PUSH32 | 3 | 0 | [] | +| 445 | PUSH2 | 3 | 0 |[0x7f60006009610120527f5af4503c95138e5b8f610100527f7f605a6000536060] | +| 448 | MSTORE | 6 | 0 |[0x7f60006009610120527f5af4503c95138e5b8f610100527f7f605a6000536060,0x160] | +| 449 | PUSH32 | 3 | 0 | [] | +| 482 | PUSH2 | 3 | 0 |[0x527f6031600153606b60610140527f02536010606060527f0353604560610120] | +| 485 | MSTORE | 6 | 0 |[0x527f6031600153606b60610140527f02536010606060527f0353604560610120,0x180] | +| 486 | PUSH32 | 3 | 0 | [] | +| 519 | PUSH2 | 3 | 0 |[0x527f04536060600553600160608052610160527f7e527f600653606060075360] | +| 522 | MSTORE | 6 | 0 |[0x527f04536060600553600160608052610160527f7e527f600653606060075360,0x1a0] | +| 523 | PUSH32 | 3 | 0 | [] | +| 556 | PUSH2 | 3 | 0 |[0x2600853606080610140527f527f556009536060610180527f600a53600160a0] | +| 559 | MSTORE | 6 | 0 |[0x2600853606080610140527f527f556009536060610180527f600a53600160a0,0x1c0] | +| 560 | PUSH32 | 3 | 0 | [] | +| 593 | PUSH2 | 3 | 0 |[0x527f600b536060600c6020527f53600060610160527f0d53606101a0527f5560] | +| 596 | MSTORE | 6 | 0 |[0x527f600b536060600c6020527f53600060610160527f0d53606101a0527f5560,0x1e0] | +| 597 | PUSH32 | 3 | 0 | [] | +| 630 | PUSH2 | 3 | 0 |[0xe60a0527f536060600f536060c0527f01601053606060115360026101806101] | +| 633 | MSTORE | 6 | 0 |[0xe60a0527f536060600f536060c0527f01601053606060115360026101806101,0x200] | +| 634 | PUSH32 | 3 | 0 | [] | +| 667 | PUSH2 | 3 | 0 |[0xc0527f527f601253606040527f55601353606060c0527f60145360e0527f6000] | +| 670 | MSTORE | 6 | 0 |[0xc0527f527f601253606040527f55601353606060c0527f60145360e0527f6000,0x220] | +| 671 | PUSH32 | 3 | 0 | [] | +| 704 | PUSH2 | 3 | 0 |[0x6015536101e0527f60606101a0527f601653600060175360f360185360196060] | +| 707 | MSTORE | 6 | 0 |[0x6015536101e0527f60606101a0527f601653600060175360f360185360196060,0x240] | +| 708 | PUSH32 | 3 | 0 | [] | +| 741 | PUSH2 | 3 | 0 |[0x605260006060e052610200527f610100527f7f806101c0527f5360f360815360] | +| 744 | MSTORE | 6 | 0 |[0x605260006060e052610200527f610100527f7f806101c0527f5360f360815360,0x260] | +| 745 | PUSH32 | 3 | 0 | [] | +| 778 | PUSH2 | 3 | 0 |[0x8260006000f060006000600060610220527e845af450506000600061016101e0] | +| 781 | MSTORE | 6 | 0 |[0x8260006000f060006000600060610220527e845af450506000600061016101e0,0x280] | +| 782 | PUSH32 | 3 | 0 | [] | +| 815 | PUSH2 | 3 | 0 |[0x527f20527f60610100527e600060006003610240527f5af15060005450c76000] | +| 818 | MSTORE | 6 | 0 |[0x527f20527f60610100527e600060006003610240527f5af15060005450c76000,0x2a0] | +| 819 | PUSH32 | 3 | 0 | [] | +| 852 | PUSH2 | 3 | 0 |[0x6002551309f562610200527f66a486610140527f6b00610260527f1d45716101] | +| 855 | MSTORE | 7 | 0 |[0x6002551309f562610200527f66a486610140527f6b00610260527f1d45716101,0x2c0] | +| 856 | PUSH32 | 3 | 0 | [] | +| 889 | PUSH2 | 3 | 0 |[0x20527f600054501c641d373c7f60045450610220527f6000600155610280527f] | +| 892 | MSTORE | 6 | 0 |[0x20527f600054501c641d373c7f60045450610220527f6000600155610280527f,0x2e0] | +| 893 | PUSH32 | 3 | 0 | [] | +| 926 | PUSH2 | 3 | 0 |[0x600554610160527f50600160025560085450610140527f60006002610240527f] | +| 929 | MSTORE | 6 | 0 |[0x600554610160527f50600160025560085450610140527f60006002610240527f,0x300] | +| 930 | PUSH32 | 3 | 0 | [] | +| 963 | PUSH2 | 3 | 0 |[0x6102a0527f557fd86000606000527e600060610180527e600060005af1508612] | +| 966 | MSTORE | 6 | 0 |[0x6102a0527f557fd86000606000527e600060610180527e600060005af1508612,0x320] | +| 967 | PUSH32 | 3 | 0 | [] | +| 1000 | PUSH2 | 3 | 0 |[0x17145147356102c0527f610260527f610160527f5198a37e127a7efa7c600052] | +| 1003 | MSTORE | 6 | 0 |[0x17145147356102c0527f610260527f610160527f5198a37e127a7efa7c600052,0x340] | +| 1004 | PUSH32 | 3 | 0 | [] | +| 1037 | PUSH2 | 3 | 0 |[0x6101a0527f606020527f6102e0527f6060205360610280527ff760215360ff60] | +| 1040 | MSTORE | 6 | 0 |[0x6101a0527f606020527f6102e0527f6060205360610280527ff760215360ff60,0x360] | +| 1041 | PUSH32 | 3 | 0 | [] | +| 1074 | PUSH2 | 3 | 0 |[0x225360610180527fdb602353603760610300527f6101c0527f24536075606102] | +| 1077 | MSTORE | 6 | 0 |[0x225360610180527fdb602353603760610300527f6101c0527f24536075606102,0x380] | +| 1078 | PUSH32 | 3 | 0 | [] | +| 1111 | PUSH2 | 3 | 0 |[0xa0527f2553609f606040527f265360fe60275360610320527f8f60286101a052] | +| 1114 | MSTORE | 6 | 0 |[0xa0527f2553609f606040527f265360fe60275360610320527f8f60286101a052,0x3a0] | +| 1115 | PUSH32 | 3 | 0 | [] | +| 1148 | PUSH2 | 3 | 0 |[0x7f53606101e0527f6102c0527f0b6029536060602a53600060610340527f2b53] | +| 1151 | MSTORE | 6 | 0 |[0x7f53606101e0527f6102c0527f0b6029536060602a53600060610340527f2b53,0x3c0] | +| 1152 | PUSH32 | 3 | 0 | [] | +| 1185 | PUSH2 | 3 | 0 |[0x6060602c53606052606060805360006061016102e0527f610200527fc0526103] | +| 1188 | MSTORE | 7 | 0 |[0x6060602c53606052606060805360006061016102e0527f610200527fc0526103,0x3e0] | +| 1189 | PUSH32 | 3 | 0 | [] | +| 1222 | PUSH2 | 3 | 0 |[0x60527f7f81536060608253602d6083536053608453606060855360fd61030052] | +| 1225 | MSTORE | 6 | 0 |[0x60527f7f81536060608253602d6083536053608453606060855360fd61030052,0x400] | +| 1226 | PUSH32 | 3 | 0 | [] | +| 1259 | PUSH2 | 3 | 0 |[0x7f6086610380527f536060610220527f6087536101e0527f602e608853605360] | +| 1262 | MSTORE | 6 | 0 |[0x7f6086610380527f536060610220527f6087536101e0527f602e608853605360,0x420] | +| 1263 | PUSH32 | 3 | 0 | [] | +| 1296 | PUSH2 | 3 | 0 |[0x89536060608a61036103a0527f20527f53602f608b536060608c610240527f53] | +| 1299 | MSTORE | 6 | 0 |[0x89536060608a61036103a0527f20527f53602f608b536060608c610240527f53,0x440] | +| 1300 | PUSH32 | 3 | 0 | [] | +| 1333 | PUSH2 | 3 | 0 |[0x6000608d5360f36102005260606103c0527f610220610340527f53608e610221] | +| 1336 | MSTORE | 6 | 0 |[0x6000608d5360f36102005260606103c0527f610220610340527f53608e610221,0x460] | +| 1337 | PUSH32 | 3 | 0 | [] | +| 1370 | PUSH2 | 3 | 0 |[0x536053610222536060610260527f610223536103e0527f600061022453606061] | +| 1373 | MSTORE | 6 | 0 |[0x536053610222536060610260527f610223536103e0527f600061022453606061,0x480] | +| 1374 | PUSH32 | 3 | 0 | [] | +| 1407 | PUSH2 | 3 | 0 |[0x360527f61022553608f61022653606061022753600061610400527f02610280] | +| 1410 | MSTORE | 6 | 0 |[0x360527f61022553608f61022653606061022753600061610400527f02610280,0x4a0] | +| 1411 | PUSH32 | 3 | 0 | [] | +| 1444 | PUSH2 | 3 | 0 |[0x527f28536060610229610380527f53600061022a5360f561022b536061042052] | +| 1447 | MSTORE | 6 | 0 |[0x527f28536060610229610380527f53600061022a5360f561022b536061042052,0x4c0] | +| 1448 | PUSH32 | 3 | 0 | [] | +| 1481 | PUSH2 | 3 | 0 |[0x7f6061022c53600061022d536102a0527f60606103a0527f61022e5360006102] | +| 1484 | MSTORE | 7 | 0 |[0x7f6061022c53600061022d536102a0527f60606103a0527f61022e5360006102,0x4e0] | +| 1485 | PUSH32 | 3 | 0 | [] | +| 1518 | PUSH2 | 3 | 0 |[0x2f610440527f53606061023053600061023153606061023253600061026103c0] | +| 1521 | MSTORE | 6 | 0 |[0x2f610440527f53606061023053600061023153606061023253600061026103c0,0x500] | +| 1522 | PUSH32 | 3 | 0 | [] | +| 1555 | PUSH2 | 3 | 0 |[0x527fc0527f61610460527f023353606061023453600061023553608561023653] | +| 1558 | MSTORE | 6 | 0 |[0x527fc0527f61610460527f023353606061023453600061023553608561023653,0x520] | +| 1559 | PUSH32 | 3 | 0 | [] | +| 1592 | PUSH2 | 3 | 0 |[0x605a61023753606103e052610480527f7ff261026102e0526038610300536053] | +| 1595 | MSTORE | 6 | 0 |[0x605a61023753606103e052610480527f7ff261026102e0526038610300536053,0x540] | +| 1596 | PUSH32 | 3 | 0 | [] | +| 1629 | PUSH2 | 3 | 0 |[0x610301536060610302536050610303536104a0527f60610400527f6161030453] | +| 1632 | MSTORE | 6 | 0 |[0x610301536060610302536050610303536104a0527f60610400527f6161030453,0x560] | +| 1633 | PUSH32 | 3 | 0 | [] | +| 1666 | PUSH2 | 3 | 0 |[0x6002610305536039610306536053610307536060616104c0527f030853605061] | +| 1669 | MSTORE | 6 | 0 |[0x6002610305536039610306536053610307536060616104c0527f030853605061,0x580] | +| 1670 | PUSH32 | 3 | 0 | [] | +| 1703 | PUSH2 | 3 | 0 |[0x610420527f030953606161030a53600261030b53603a61030c536104e0527f60] | +| 1706 | MSTORE | 7 | 0 |[0x610420527f030953606161030a53600261030b53603a61030c536104e0527f60,0x5a0] | +| 1707 | PUSH32 | 3 | 0 | [] | +| 1740 | PUSH2 | 3 | 0 |[0x5361030d53606161030e610440527f53600261030f53603b6103105360606161] | +| 1743 | MSTORE | 6 | 0 |[0x5361030d53606161030e610440527f53600261030f53603b6103105360606161,0x5c0] | +| 1744 | PUSH32 | 3 | 0 | [] | +| 1777 | PUSH2 | 3 | 0 |[0x500527f03115360006103125360f36103135361610460526003610480536014] | +| 1780 | MSTORE | 6 | 0 |[0x500527f03115360006103125360f36103135361610460526003610480536014,0x5e0] | +| 1781 | PUSH32 | 3 | 0 | [] | +| 1814 | PUSH2 | 3 | 0 |[0x61048153610520527f6060610482536000610483536060610484536000610485] | +| 1817 | MSTORE | 6 | 0 |[0x61048153610520527f6060610482536000610483536060610484536000610485,0x600] | +| 1818 | PUSH32 | 3 | 0 | [] | +| 1851 | PUSH2 | 3 | 0 |[0x5360f0610486536060610540527f610487536000610488536060610489536000] | +| 1854 | MSTORE | 6 | 0 |[0x5360f0610486536060610540527f610487536000610488536060610489536000,0x620] | +| 1855 | PUSH32 | 3 | 0 | [] | +| 1888 | PUSH2 | 3 | 0 |[0x61048a53606061048b5360006104610560527f8c53606061048d53600061048e] | +| 1891 | MSTORE | 7 | 0 |[0x61048a53606061048b5360006104610560527f8c53606061048d53600061048e,0x640] | +| 1892 | PUSH32 | 3 | 0 | [] | +| 1925 | PUSH2 | 3 | 0 |[0x53608461048f53605a6104905360f4610491536105805260606105a053605061] | +| 1928 | MSTORE | 6 | 0 |[0x53608461048f53605a6104905360f4610491536105805260606105a053605061,0x660] | +| 1929 | PUSH32 | 3 | 0 | [] | +| 1962 | PUSH2 | 3 | 0 |[0x5a15360616105a25360046105a35360926105a45360536105a55360606105a6] | +| 1965 | MSTORE | 6 | 0 |[0x5a15360616105a25360046105a35360926105a45360536105a55360606105a6,0x680] | +| 1966 | PUSH32 | 3 | 0 | [] | +| 1999 | PUSH2 | 3 | 0 |[0x5360506105a75360616105a85360046105a95360936105aa5360536105ab5360] | +| 2002 | MSTORE | 6 | 0 |[0x5360506105a75360616105a85360046105a95360936105aa5360536105ab5360,0x6a0] | +| 2003 | PUSH32 | 3 | 0 | [] | +| 2036 | PUSH2 | 3 | 0 |[0x616105ac5360046105ad5360946105ae5360606105af5360006105b05360f361] | +| 2039 | MSTORE | 6 | 0 |[0x616105ac5360046105ad5360946105ae5360606105af5360006105b05360f361,0x6c0] | +| 2040 | PUSH1 | 3 | 0 | [] | +| 2042 | PUSH2 | 3 | 0 | [0x5] | +| 2045 | MSTORE8 | 7 | 0 |[0x5,0x6e0] | +| 2046 | PUSH1 | 3 | 0 | [] | +| 2048 | PUSH2 | 3 | 0 | [0xb1] | +| 2051 | MSTORE8 | 3 | 0 |[0xb1,0x6e1] | +| 2052 | PUSH1 | 3 | 0 | [] | +| 2054 | PUSH2 | 3 | 0 | [0x53] | +| 2057 | MSTORE8 | 3 | 0 |[0x53,0x6e2] | +| 2058 | PUSH1 | 3 | 0 | [] | +| 2060 | PUSH2 | 3 | 0 | [0x60] | +| 2063 | MSTORE8 | 3 | 0 |[0x60,0x6e3] | +| 2064 | PUSH1 | 3 | 0 | [] | +| 2066 | PUSH2 | 3 | 0 | [0x0] | +| 2069 | MSTORE8 | 3 | 0 |[0x0,0x6e4] | +| 2070 | PUSH1 | 3 | 0 | [] | +| 2072 | PUSH2 | 3 | 0 | [0x61] | +| 2075 | MSTORE8 | 3 | 0 |[0x61,0x6e5] | +| 2076 | PUSH1 | 3 | 0 | [] | +| 2078 | PUSH2 | 3 | 0 | [0x5] | +| 2081 | MSTORE8 | 3 | 0 |[0x5,0x6e6] | +| 2082 | PUSH1 | 3 | 0 | [] | +| 2084 | PUSH2 | 3 | 0 | [0xb2] | +| 2087 | MSTORE8 | 3 | 0 |[0xb2,0x6e7] | +| 2088 | PUSH1 | 3 | 0 | [] | +| 2090 | PUSH2 | 3 | 0 | [0x60] | +| 2093 | MSTORE8 | 3 | 0 |[0x60,0x6e8] | +| 2094 | PUSH1 | 3 | 0 | [] | +| 2096 | PUSH2 | 3 | 0 | [0x0] | +| 2099 | MSTORE8 | 3 | 0 |[0x0,0x6e9] | +| 2100 | PUSH1 | 3 | 0 | [] | +| 2102 | PUSH2 | 3 | 0 | [0x60] | +| 2105 | MSTORE8 | 3 | 0 |[0x60,0x6ea] | +| 2106 | PUSH1 | 3 | 0 | [] | +| 2108 | PUSH2 | 3 | 0 | [0x0] | +| 2111 | MSTORE8 | 3 | 0 |[0x0,0x6eb] | +| 2112 | PUSH1 | 3 | 0 | [] | +| 2114 | PUSH2 | 3 | 0 | [0xf5] | +| 2117 | MSTORE8 | 3 | 0 |[0xf5,0x6ec] | +| 2118 | PUSH1 | 3 | 0 | [] | +| 2120 | PUSH2 | 3 | 0 | [0x60] | +| 2123 | MSTORE8 | 3 | 0 |[0x60,0x6ed] | +| 2124 | PUSH1 | 3 | 0 | [] | +| 2126 | PUSH2 | 3 | 0 | [0x0] | +| 2129 | MSTORE8 | 3 | 0 |[0x0,0x6ee] | +| 2130 | PUSH1 | 3 | 0 | [] | +| 2132 | PUSH2 | 3 | 0 | [0x60] | +| 2135 | MSTORE8 | 3 | 0 |[0x60,0x6ef] | +| 2136 | PUSH1 | 3 | 0 | [] | +| 2138 | PUSH2 | 3 | 0 | [0x0] | +| 2141 | MSTORE8 | 3 | 0 |[0x0,0x6f0] | +| 2142 | PUSH1 | 3 | 0 | [] | +| 2144 | PUSH2 | 3 | 0 | [0x60] | +| 2147 | MSTORE8 | 3 | 0 |[0x60,0x6f1] | +| 2148 | PUSH1 | 3 | 0 | [] | +| 2150 | PUSH2 | 3 | 0 | [0x0] | +| 2153 | MSTORE8 | 3 | 0 |[0x0,0x6f2] | +| 2154 | PUSH1 | 3 | 0 | [] | +| 2156 | PUSH2 | 3 | 0 | [0x60] | +| 2159 | MSTORE8 | 3 | 0 |[0x60,0x6f3] | +| 2160 | PUSH1 | 3 | 0 | [] | +| 2162 | PUSH2 | 3 | 0 | [0x0] | +| 2165 | MSTORE8 | 3 | 0 |[0x0,0x6f4] | +| 2166 | PUSH1 | 3 | 0 | [] | +| 2168 | PUSH2 | 3 | 0 | [0x60] | +| 2171 | MSTORE8 | 3 | 0 |[0x60,0x6f5] | +| 2172 | PUSH1 | 3 | 0 | [] | +| 2174 | PUSH2 | 3 | 0 | [0x0] | +| 2177 | MSTORE8 | 3 | 0 |[0x0,0x6f6] | +| 2178 | PUSH1 | 3 | 0 | [] | +| 2180 | PUSH2 | 3 | 0 | [0x85] | +| 2183 | MSTORE8 | 3 | 0 |[0x85,0x6f7] | +| 2184 | PUSH1 | 3 | 0 | [] | +| 2186 | PUSH2 | 3 | 0 | [0x5a] | +| 2189 | MSTORE8 | 3 | 0 |[0x5a,0x6f8] | +| 2190 | PUSH1 | 3 | 0 | [] | +| 2192 | PUSH2 | 3 | 0 | [0xf1] | +| 2195 | MSTORE8 | 3 | 0 |[0xf1,0x6f9] | +| 2196 | PUSH1 | 3 | 0 | [] | +| 2198 | PUSH2 | 3 | 0 | [0x50] | +| 2201 | MSTORE8 | 3 | 0 |[0x50,0x6fa] | +| 2202 | PUSH1 | 3 | 0 | [] | +| 2204 | PUSH2 | 3 | 0 | [0x50] | +| 2207 | MSTORE8 | 3 | 0 |[0x50,0x6fb] | +| 2208 | PUSH2 | 3 | 0 | [] | +| 2211 | PUSH1 | 3 | 0 | [0x6fc] | +| 2213 | RETURN | 0 | 0 |[0x6fc,0x0] | +| 2605 | PUSH1 | 3 | 0 |[0x7dce2faf43218578e3fcf2ad22df9918a89e2fba] | +| 2607 | PUSH1 | 3 | 0 |[0x7dce2faf43218578e3fcf2ad22df9918a89e2fba,0x0] | +| 2609 | PUSH1 | 3 | 0 |[0x7dce2faf43218578e3fcf2ad22df9918a89e2fba,0x0,0x0] | +| 2611 | PUSH1 | 3 | 0 |[0x7dce2faf43218578e3fcf2ad22df9918a89e2fba,0x0,0x0,0x0] | +| 2613 | PUSH1 | 3 | 0 |[0x7dce2faf43218578e3fcf2ad22df9918a89e2fba,0x0,0x0,0x0,0x0] | +| 2615 | DUP6 | 3 | 0 |[0x7dce2faf43218578e3fcf2ad22df9918a89e2fba,0x0,0x0,0x0,0x0,0x0] | +| 2616 | GAS | 2 | 0 |[0x7dce2faf43218578e3fcf2ad22df9918a89e2fba,0x0,0x0,0x0,0x0,0x0,0x7dce2faf43218578e3fcf2ad22df9918a89e2fba] | +| 2617 | CALL | 295218 | 0 |[0x7dce2faf43218578e3fcf2ad22df9918a89e2fba,0x0,0x0,0x0,0x0,0x0,0x7dce2faf43218578e3fcf2ad22df9918a89e2fba,0x4937e] | +| 0 | PUSH32 | 3 | 0 | [] | +| 33 | PUSH1 | 3 | 0 |[0x6008545060006004557f600160045560006004556000600060006000600060f9] | +| 35 | MSTORE | 6 | 0 |[0x6008545060006004557f600160045560006004556000600060006000600060f9,0x0] | +| 36 | PUSH32 | 3 | 0 | [] | +| 69 | PUSH1 | 3 | 0 |[0x5af250600060006000606000527e60f45af4506000600060006000600060f55a] | +| 71 | MSTORE | 6 | 0 |[0x5af250600060006000606000527e60f45af4506000600060006000600060f55a,0x20] | +| 72 | PUSH32 | 3 | 0 | [] | +| 105 | PUSH1 | 3 | 0 |[0xf150f001075205846a44a283446020527f8ca2600060006000600060045af450] | +| 107 | MSTORE | 6 | 0 |[0xf150f001075205846a44a283446020527f8ca2600060006000600060045af450,0x40] | +| 108 | PUSH32 | 3 | 0 | [] | +| 141 | PUSH1 | 3 | 0 |[0x519930847f3b631c54a49b5f60035450326040527f77306b6000600060006000] | +| 143 | MSTORE | 6 | 0 |[0x519930847f3b631c54a49b5f60035450326040527f77306b6000600060006000,0x60] | +| 144 | PUSH32 | 3 | 0 | [] | +| 177 | PUSH1 | 3 | 0 |[0x6000600c5af150600060006000600060f85af450506060527f06600160025560] | +| 179 | MSTORE | 6 | 0 |[0x6000600c5af150600060006000600060f85af450506060527f06600160025560,0x80] | +| 180 | PUSH32 | 3 | 0 | [] | +| 213 | PUSH1 | 3 | 0 |[0x35450600060005560006001556c3394fff4607f7f1684317b6080527f387b9f] | +| 215 | MSTORE | 6 | 0 |[0x35450600060005560006001556c3394fff4607f7f1684317b6080527f387b9f,0xa0] | +| 216 | PUSH32 | 3 | 0 | [] | +| 249 | PUSH1 | 3 | 0 |[0x1920700184809d60015450011899016e6009ff60026001556000527f9f60a052] | +| 251 | MSTORE | 6 | 0 |[0x1920700184809d60015450011899016e6009ff60026001556000527f9f60a052,0xc0] | +| 252 | PUSH32 | 3 | 0 | [] | +| 285 | PUSH1 | 3 | 0 |[0x7f6000527f9981600160045582600eff600060006000600060f65af450600060] | +| 287 | MSTORE | 6 | 0 |[0x7f6000527f9981600160045582600eff600060006000600060f65af450600060,0xe0] | +| 288 | PUSH32 | 3 | 0 | [] | +| 321 | PUSH2 | 3 | 0 |[0x6060c0527f20527e600060006020527f60f75af4501d7f190316666000600060] | +| 324 | MSTORE | 6 | 0 |[0x6060c0527f20527e600060006020527f60f75af4501d7f190316666000600060,0x100] | +| 325 | PUSH31 | 3 | 0 | [] | +| 357 | PUSH2 | 3 | 0 |[0x6000600060e0527f60046040527f5af1506000600060006040527f60006009] | +| 360 | MSTORE | 6 | 0 |[0x6000600060e0527f60046040527f5af1506000600060006040527f60006009,0x120] | +| 361 | PUSH32 | 3 | 0 | [] | +| 394 | PUSH2 | 3 | 0 |[0x5af4503c95138e5b8f610100527f7f605a6000536060527f6031600153606b60] | +| 397 | MSTORE | 6 | 0 |[0x5af4503c95138e5b8f610100527f7f605a6000536060527f6031600153606b60,0x140] | +| 398 | PUSH32 | 3 | 0 | [] | +| 431 | PUSH2 | 3 | 0 |[0x2536010606060527f0353604560610120527f04536060600553600160608052] | +| 434 | MSTORE | 6 | 0 |[0x2536010606060527f0353604560610120527f04536060600553600160608052,0x160] | +| 435 | PUSH32 | 3 | 0 | [] | +| 468 | PUSH2 | 3 | 0 |[0x7e527f60065360606007536002600853606080610140527f527f556009536060] | +| 471 | MSTORE | 6 | 0 |[0x7e527f60065360606007536002600853606080610140527f527f556009536060,0x180] | +| 472 | PUSH32 | 3 | 0 | [] | +| 505 | PUSH2 | 3 | 0 |[0x600a53600160a0527f600b536060600c6020527f53600060610160527f0d5360] | +| 508 | MSTORE | 6 | 0 |[0x600a53600160a0527f600b536060600c6020527f53600060610160527f0d5360,0x1a0] | +| 509 | PUSH32 | 3 | 0 | [] | +| 542 | PUSH2 | 3 | 0 |[0x55600e60a0527f536060600f536060c0527f0160105360606011536002610180] | +| 545 | MSTORE | 6 | 0 |[0x55600e60a0527f536060600f536060c0527f0160105360606011536002610180,0x1c0] | +| 546 | PUSH32 | 3 | 0 | [] | +| 579 | PUSH2 | 3 | 0 |[0x527f601253606040527f55601353606060c0527f60145360e0527f6000601553] | +| 582 | MSTORE | 6 | 0 |[0x527f601253606040527f55601353606060c0527f60145360e0527f6000601553,0x1e0] | +| 583 | PUSH32 | 3 | 0 | [] | +| 616 | PUSH2 | 3 | 0 |[0x60606101a0527f601653600060175360f360185360196060605260006060e052] | +| 619 | MSTORE | 6 | 0 |[0x60606101a0527f601653600060175360f360185360196060605260006060e052,0x200] | +| 620 | PUSH32 | 3 | 0 | [] | +| 653 | PUSH2 | 3 | 0 |[0x610100527f7f806101c0527f5360f3608153608260006000f060006000600060] | +| 656 | MSTORE | 6 | 0 |[0x610100527f7f806101c0527f5360f3608153608260006000f060006000600060,0x220] | +| 657 | PUSH31 | 3 | 0 | [] | +| 689 | PUSH2 | 3 | 0 |[0x845af450506000600061016101e0527f20527f60610100527e600060006003] | +| 692 | MSTORE | 6 | 0 |[0x845af450506000600061016101e0527f20527f60610100527e600060006003,0x240] | +| 693 | PUSH32 | 3 | 0 | [] | +| 726 | PUSH2 | 3 | 0 |[0x5af15060005450c760006002551309f562610200527f66a486610140527f6b00] | +| 729 | MSTORE | 6 | 0 |[0x5af15060005450c760006002551309f562610200527f66a486610140527f6b00,0x260] | +| 730 | PUSH32 | 3 | 0 | [] | +| 763 | PUSH2 | 3 | 0 |[0x1d4571610120527f600054501c641d373c7f60045450610220527f6000600155] | +| 766 | MSTORE | 6 | 0 |[0x1d4571610120527f600054501c641d373c7f60045450610220527f6000600155,0x280] | +| 767 | PUSH32 | 3 | 0 | [] | +| 800 | PUSH2 | 3 | 0 |[0x600554610160527f50600160025560085450610140527f60006002610240527f] | +| 803 | MSTORE | 6 | 0 |[0x600554610160527f50600160025560085450610140527f60006002610240527f,0x2a0] | +| 804 | PUSH32 | 3 | 0 | [] | +| 837 | PUSH2 | 3 | 0 |[0x557fd86000606000527e600060610180527e600060005af15086121714514735] | +| 840 | MSTORE | 7 | 0 |[0x557fd86000606000527e600060610180527e600060005af15086121714514735,0x2c0] | +| 841 | PUSH32 | 3 | 0 | [] | +| 874 | PUSH2 | 3 | 0 |[0x610260527f610160527f5198a37e127a7efa7c6000526101a0527f606020527f] | +| 877 | MSTORE | 6 | 0 |[0x610260527f610160527f5198a37e127a7efa7c6000526101a0527f606020527f,0x2e0] | +| 878 | PUSH32 | 3 | 0 | [] | +| 911 | PUSH2 | 3 | 0 |[0x6060205360610280527ff760215360ff60225360610180527fdb602353603760] | +| 914 | MSTORE | 6 | 0 |[0x6060205360610280527ff760215360ff60225360610180527fdb602353603760,0x300] | +| 915 | PUSH32 | 3 | 0 | [] | +| 948 | PUSH2 | 3 | 0 |[0x6101c0527f24536075606102a0527f2553609f606040527f265360fe60275360] | +| 951 | MSTORE | 6 | 0 |[0x6101c0527f24536075606102a0527f2553609f606040527f265360fe60275360,0x320] | +| 952 | PUSH32 | 3 | 0 | [] | +| 985 | PUSH2 | 3 | 0 |[0x8f60286101a0527f53606101e0527f6102c0527f0b6029536060602a53600060] | +| 988 | MSTORE | 6 | 0 |[0x8f60286101a0527f53606101e0527f6102c0527f0b6029536060602a53600060,0x340] | +| 989 | PUSH32 | 3 | 0 | [] | +| 1022 | PUSH2 | 3 | 0 |[0x2b536060602c53606052606060805360006061016102e0527f610200527fc052] | +| 1025 | MSTORE | 6 | 0 |[0x2b536060602c53606052606060805360006061016102e0527f610200527fc052,0x360] | +| 1026 | PUSH32 | 3 | 0 | [] | +| 1059 | PUSH2 | 3 | 0 |[0x7f81536060608253602d6083536053608453606060855360fd610300527f6086] | +| 1062 | MSTORE | 6 | 0 |[0x7f81536060608253602d6083536053608453606060855360fd610300527f6086,0x380] | +| 1063 | PUSH32 | 3 | 0 | [] | +| 1096 | PUSH2 | 3 | 0 |[0x536060610220527f6087536101e0527f602e60885360536089536060608a6103] | +| 1099 | MSTORE | 6 | 0 |[0x536060610220527f6087536101e0527f602e60885360536089536060608a6103,0x3a0] | +| 1100 | PUSH32 | 3 | 0 | [] | +| 1133 | PUSH2 | 3 | 0 |[0x20527f53602f608b536060608c610240527f536000608d5360f3610200526060] | +| 1136 | MSTORE | 6 | 0 |[0x20527f53602f608b536060608c610240527f536000608d5360f3610200526060,0x3c0] | +| 1137 | PUSH32 | 3 | 0 | [] | +| 1170 | PUSH2 | 3 | 0 |[0x610220610340527f53608e610221536053610222536060610260527f61022353] | +| 1173 | MSTORE | 7 | 0 |[0x610220610340527f53608e610221536053610222536060610260527f61022353,0x3e0] | +| 1174 | PUSH32 | 3 | 0 | [] | +| 1207 | PUSH2 | 3 | 0 |[0x6000610224536060610360527f61022553608f61022653606061022753600061] | +| 1210 | MSTORE | 6 | 0 |[0x6000610224536060610360527f61022553608f61022653606061022753600061,0x400] | +| 1211 | PUSH32 | 3 | 0 | [] | +| 1244 | PUSH2 | 3 | 0 |[0x2610280527f28536060610229610380527f53600061022a5360f561022b5360] | +| 1247 | MSTORE | 6 | 0 |[0x2610280527f28536060610229610380527f53600061022a5360f561022b5360,0x420] | +| 1248 | PUSH32 | 3 | 0 | [] | +| 1281 | PUSH2 | 3 | 0 |[0x6061022c53600061022d536102a0527f60606103a0527f61022e53600061022f] | +| 1284 | MSTORE | 6 | 0 |[0x6061022c53600061022d536102a0527f60606103a0527f61022e53600061022f,0x440] | +| 1285 | PUSH32 | 3 | 0 | [] | +| 1318 | PUSH2 | 3 | 0 |[0x53606061023053600061023153606061023253600061026103c0527fc0527f61] | +| 1321 | MSTORE | 6 | 0 |[0x53606061023053600061023153606061023253600061026103c0527fc0527f61,0x460] | +| 1322 | PUSH32 | 3 | 0 | [] | +| 1355 | PUSH2 | 3 | 0 |[0x23353606061023453600061023553608561023653605a61023753606103e052] | +| 1358 | MSTORE | 6 | 0 |[0x23353606061023453600061023553608561023653605a61023753606103e052,0x480] | +| 1359 | PUSH32 | 3 | 0 | [] | +| 1392 | PUSH2 | 3 | 0 |[0x7ff261026102e052603861030053605361030153606061030253605061030353] | +| 1395 | MSTORE | 6 | 0 |[0x7ff261026102e052603861030053605361030153606061030253605061030353,0x4a0] | +| 1396 | PUSH32 | 3 | 0 | [] | +| 1429 | PUSH2 | 3 | 0 |[0x60610400527f6161030453600261030553603961030653605361030753606061] | +| 1432 | MSTORE | 6 | 0 |[0x60610400527f6161030453600261030553603961030653605361030753606061,0x4c0] | +| 1433 | PUSH32 | 3 | 0 | [] | +| 1466 | PUSH2 | 3 | 0 |[0x30853605061610420527f030953606161030a53600261030b53603a61030c53] | +| 1469 | MSTORE | 7 | 0 |[0x30853605061610420527f030953606161030a53600261030b53603a61030c53,0x4e0] | +| 1470 | PUSH32 | 3 | 0 | [] | +| 1503 | PUSH2 | 3 | 0 |[0x605361030d53606161030e610440527f53600261030f53603b61031053606061] | +| 1506 | MSTORE | 6 | 0 |[0x605361030d53606161030e610440527f53600261030f53603b61031053606061,0x500] | +| 1507 | PUSH32 | 3 | 0 | [] | +| 1540 | PUSH2 | 3 | 0 |[0x3115360006103125360f3610313536161046052600361048053601461048153] | +| 1543 | MSTORE | 6 | 0 |[0x3115360006103125360f3610313536161046052600361048053601461048153,0x520] | +| 1544 | PUSH32 | 3 | 0 | [] | +| 1577 | PUSH2 | 3 | 0 |[0x60606104825360006104835360606104845360006104855360f0610486536060] | +| 1580 | MSTORE | 6 | 0 |[0x60606104825360006104835360606104845360006104855360f0610486536060,0x540] | +| 1581 | PUSH32 | 3 | 0 | [] | +| 1614 | PUSH2 | 3 | 0 |[0x61048753600061048853606061048953600061048a53606061048b5360006104] | +| 1617 | MSTORE | 6 | 0 |[0x61048753600061048853606061048953600061048a53606061048b5360006104,0x560] | +| 1618 | PUSH32 | 3 | 0 | [] | +| 1651 | PUSH2 | 3 | 0 |[0x8c53606061048d53600061048e53608461048f53605a6104905360f461049153] | +| 1654 | MSTORE | 6 | 0 |[0x8c53606061048d53600061048e53608461048f53605a6104905360f461049153,0x580] | +| 1655 | PUSH1 | 3 | 0 | [] | +| 1657 | PUSH2 | 3 | 0 | [0x60] | +| 1660 | MSTORE8 | 7 | 0 |[0x60,0x5a0] | +| 1661 | PUSH1 | 3 | 0 | [] | +| 1663 | PUSH2 | 3 | 0 | [0x50] | +| 1666 | MSTORE8 | 3 | 0 |[0x50,0x5a1] | +| 1667 | PUSH1 | 3 | 0 | [] | +| 1669 | PUSH2 | 3 | 0 | [0x61] | +| 1672 | MSTORE8 | 3 | 0 |[0x61,0x5a2] | +| 1673 | PUSH1 | 3 | 0 | [] | +| 1675 | PUSH2 | 3 | 0 | [0x4] | +| 1678 | MSTORE8 | 3 | 0 |[0x4,0x5a3] | +| 1679 | PUSH1 | 3 | 0 | [] | +| 1681 | PUSH2 | 3 | 0 | [0x92] | +| 1684 | MSTORE8 | 3 | 0 |[0x92,0x5a4] | +| 1685 | PUSH1 | 3 | 0 | [] | +| 1687 | PUSH2 | 3 | 0 | [0x53] | +| 1690 | MSTORE8 | 3 | 0 |[0x53,0x5a5] | +| 1691 | PUSH1 | 3 | 0 | [] | +| 1693 | PUSH2 | 3 | 0 | [0x60] | +| 1696 | MSTORE8 | 3 | 0 |[0x60,0x5a6] | +| 1697 | PUSH1 | 3 | 0 | [] | +| 1699 | PUSH2 | 3 | 0 | [0x50] | +| 1702 | MSTORE8 | 3 | 0 |[0x50,0x5a7] | +| 1703 | PUSH1 | 3 | 0 | [] | +| 1705 | PUSH2 | 3 | 0 | [0x61] | +| 1708 | MSTORE8 | 3 | 0 |[0x61,0x5a8] | +| 1709 | PUSH1 | 3 | 0 | [] | +| 1711 | PUSH2 | 3 | 0 | [0x4] | +| 1714 | MSTORE8 | 3 | 0 |[0x4,0x5a9] | +| 1715 | PUSH1 | 3 | 0 | [] | +| 1717 | PUSH2 | 3 | 0 | [0x93] | +| 1720 | MSTORE8 | 3 | 0 |[0x93,0x5aa] | +| 1721 | PUSH1 | 3 | 0 | [] | +| 1723 | PUSH2 | 3 | 0 | [0x53] | +| 1726 | MSTORE8 | 3 | 0 |[0x53,0x5ab] | +| 1727 | PUSH1 | 3 | 0 | [] | +| 1729 | PUSH2 | 3 | 0 | [0x61] | +| 1732 | MSTORE8 | 3 | 0 |[0x61,0x5ac] | +| 1733 | PUSH1 | 3 | 0 | [] | +| 1735 | PUSH2 | 3 | 0 | [0x4] | +| 1738 | MSTORE8 | 3 | 0 |[0x4,0x5ad] | +| 1739 | PUSH1 | 3 | 0 | [] | +| 1741 | PUSH2 | 3 | 0 | [0x94] | +| 1744 | MSTORE8 | 3 | 0 |[0x94,0x5ae] | +| 1745 | PUSH1 | 3 | 0 | [] | +| 1747 | PUSH2 | 3 | 0 | [0x60] | +| 1750 | MSTORE8 | 3 | 0 |[0x60,0x5af] | +| 1751 | PUSH1 | 3 | 0 | [] | +| 1753 | PUSH2 | 3 | 0 | [0x0] | +| 1756 | MSTORE8 | 3 | 0 |[0x0,0x5b0] | +| 1757 | PUSH1 | 3 | 0 | [] | +| 1759 | PUSH2 | 3 | 0 | [0xf3] | +| 1762 | MSTORE8 | 3 | 0 |[0xf3,0x5b1] | +| 1763 | PUSH1 | 3 | 0 | [] | +| 1765 | PUSH2 | 3 | 0 | [0x0] | +| 1768 | PUSH1 | 3 | 0 |[0x0,0x5b2] | +| 1770 | PUSH1 | 3 | 0 |[0x0,0x5b2,0x0] | +| 1772 | CREATE2 | 32276 | 0 |[0x0,0x5b2,0x0,0x0] | +| 0 | PUSH1 | 3 | 0 | [] | +| 2 | SLOAD | 2100 | 0 | [0x8] | +| 3 | POP | 2 | 0 | [0x0] | +| 4 | PUSH1 | 3 | 0 | [] | +| 6 | PUSH1 | 3 | 0 | [0x0] | +| 8 | SSTORE | 2200 | 0 | [0x0,0x4] | +| 9 | PUSH32 | 3 | 0 | [] | +| 42 | PUSH1 | 3 | 0 |[0x600160045560006004556000600060006000600060f95af25060006000600060] | +| 44 | MSTORE | 6 | 0 |[0x600160045560006004556000600060006000600060f95af25060006000600060,0x0] | +| 45 | PUSH31 | 3 | 0 | [] | +| 77 | PUSH1 | 3 | 0 |[0x60f45af4506000600060006000600060f55af150f001075205846a44a28344] | +| 79 | MSTORE | 6 | 0 |[0x60f45af4506000600060006000600060f55af150f001075205846a44a28344,0x20] | +| 80 | PUSH32 | 3 | 0 | [] | +| 113 | PUSH1 | 3 | 0 |[0x8ca2600060006000600060045af450519930847f3b631c54a49b5f6003545032] | +| 115 | MSTORE | 6 | 0 |[0x8ca2600060006000600060045af450519930847f3b631c54a49b5f6003545032,0x40] | +| 116 | PUSH32 | 3 | 0 | [] | +| 149 | PUSH1 | 3 | 0 |[0x77306b60006000600060006000600c5af150600060006000600060f85af45050] | +| 151 | MSTORE | 6 | 0 |[0x77306b60006000600060006000600c5af150600060006000600060f85af45050,0x60] | +| 152 | PUSH32 | 3 | 0 | [] | +| 185 | PUSH1 | 3 | 0 |[0x6600160025560035450600060005560006001556c3394fff4607f7f1684317b] | +| 187 | MSTORE | 6 | 0 |[0x6600160025560035450600060005560006001556c3394fff4607f7f1684317b,0x80] | +| 188 | PUSH32 | 3 | 0 | [] | +| 221 | PUSH1 | 3 | 0 |[0x387b9f1920700184809d60015450011899016e6009ff60026001556000527f9f] | +| 223 | MSTORE | 6 | 0 |[0x387b9f1920700184809d60015450011899016e6009ff60026001556000527f9f,0xa0] | +| 224 | PUSH32 | 3 | 0 | [] | +| 257 | PUSH1 | 3 | 0 |[0x6000527f9981600160045582600eff600060006000600060f65af45060006060] | +| 259 | MSTORE | 6 | 0 |[0x6000527f9981600160045582600eff600060006000600060f65af45060006060,0xc0] | +| 260 | PUSH32 | 3 | 0 | [] | +| 293 | PUSH1 | 3 | 0 |[0x20527e600060006020527f60f75af4501d7f1903166660006000600060006000] | +| 295 | MSTORE | 6 | 0 |[0x20527e600060006020527f60f75af4501d7f1903166660006000600060006000,0xe0] | +| 296 | PUSH32 | 3 | 0 | [] | +| 329 | PUSH2 | 3 | 0 |[0x60046040527f5af1506000600060006040527f600060095af4503c95138e5b8f] | +| 332 | MSTORE | 6 | 0 |[0x60046040527f5af1506000600060006040527f600060095af4503c95138e5b8f,0x100] | +| 333 | PUSH32 | 3 | 0 | [] | +| 366 | PUSH2 | 3 | 0 |[0x7f605a6000536060527f6031600153606b6002536010606060527f0353604560] | +| 369 | MSTORE | 6 | 0 |[0x7f605a6000536060527f6031600153606b6002536010606060527f0353604560,0x120] | +| 370 | PUSH32 | 3 | 0 | [] | +| 403 | PUSH2 | 3 | 0 |[0x45360606005536001606080527e527f60065360606007536002600853606080] | +| 406 | MSTORE | 6 | 0 |[0x45360606005536001606080527e527f60065360606007536002600853606080,0x140] | +| 407 | PUSH32 | 3 | 0 | [] | +| 440 | PUSH2 | 3 | 0 |[0x527f556009536060600a53600160a0527f600b536060600c6020527f53600060] | +| 443 | MSTORE | 6 | 0 |[0x527f556009536060600a53600160a0527f600b536060600c6020527f53600060,0x160] | +| 444 | PUSH32 | 3 | 0 | [] | +| 477 | PUSH2 | 3 | 0 |[0xd536055600e60a0527f536060600f536060c0527f0160105360606011536002] | +| 480 | MSTORE | 6 | 0 |[0xd536055600e60a0527f536060600f536060c0527f0160105360606011536002,0x180] | +| 481 | PUSH32 | 3 | 0 | [] | +| 514 | PUSH2 | 3 | 0 |[0x601253606040527f55601353606060c0527f60145360e0527f60006015536060] | +| 517 | MSTORE | 6 | 0 |[0x601253606040527f55601353606060c0527f60145360e0527f60006015536060,0x1a0] | +| 518 | PUSH32 | 3 | 0 | [] | +| 551 | PUSH2 | 3 | 0 |[0x601653600060175360f360185360196060605260006060e052610100527f7f80] | +| 554 | MSTORE | 6 | 0 |[0x601653600060175360f360185360196060605260006060e052610100527f7f80,0x1c0] | +| 555 | PUSH32 | 3 | 0 | [] | +| 588 | PUSH2 | 3 | 0 |[0x5360f3608153608260006000f06000600060006000845af45050600060006101] | +| 591 | MSTORE | 6 | 0 |[0x5360f3608153608260006000f06000600060006000845af45050600060006101,0x1e0] | +| 592 | PUSH32 | 3 | 0 | [] | +| 625 | PUSH2 | 3 | 0 |[0x20527f60610100527e6000600060035af15060005450c760006002551309f562] | +| 628 | MSTORE | 6 | 0 |[0x20527f60610100527e6000600060035af15060005450c760006002551309f562,0x200] | +| 629 | PUSH32 | 3 | 0 | [] | +| 662 | PUSH2 | 3 | 0 |[0x66a486610140527f6b001d4571610120527f600054501c641d373c7f60045450] | +| 665 | MSTORE | 6 | 0 |[0x66a486610140527f6b001d4571610120527f600054501c641d373c7f60045450,0x220] | +| 666 | PUSH32 | 3 | 0 | [] | +| 699 | PUSH2 | 3 | 0 |[0x6000600155600554610160527f50600160025560085450610140527f60006002] | +| 702 | MSTORE | 6 | 0 |[0x6000600155600554610160527f50600160025560085450610140527f60006002,0x240] | +| 703 | PUSH32 | 3 | 0 | [] | +| 736 | PUSH2 | 3 | 0 |[0x557fd86000606000527e600060610180527e600060005af15086121714514735] | +| 739 | MSTORE | 6 | 0 |[0x557fd86000606000527e600060610180527e600060005af15086121714514735,0x260] | +| 740 | PUSH32 | 3 | 0 | [] | +| 773 | PUSH2 | 3 | 0 |[0x610160527f5198a37e127a7efa7c6000526101a0527f606020527f6060205360] | +| 776 | MSTORE | 6 | 0 |[0x610160527f5198a37e127a7efa7c6000526101a0527f606020527f6060205360,0x280] | +| 777 | PUSH32 | 3 | 0 | [] | +| 810 | PUSH2 | 3 | 0 |[0xf760215360ff60225360610180527fdb6023536037606101c0527f2453607560] | +| 813 | MSTORE | 6 | 0 |[0xf760215360ff60225360610180527fdb6023536037606101c0527f2453607560,0x2a0] | +| 814 | PUSH32 | 3 | 0 | [] | +| 847 | PUSH2 | 3 | 0 |[0x2553609f606040527f265360fe602753608f60286101a0527f53606101e0527f] | +| 850 | MSTORE | 7 | 0 |[0x2553609f606040527f265360fe602753608f60286101a0527f53606101e0527f,0x2c0] | +| 851 | PUSH32 | 3 | 0 | [] | +| 884 | PUSH2 | 3 | 0 |[0xb6029536060602a536000602b536060602c5360605260606080536000606101] | +| 887 | MSTORE | 6 | 0 |[0xb6029536060602a536000602b536060602c5360605260606080536000606101,0x2e0] | +| 888 | PUSH32 | 3 | 0 | [] | +| 921 | PUSH2 | 3 | 0 |[0x610200527fc0527f81536060608253602d6083536053608453606060855360fd] | +| 924 | MSTORE | 6 | 0 |[0x610200527fc0527f81536060608253602d6083536053608453606060855360fd,0x300] | +| 925 | PUSH32 | 3 | 0 | [] | +| 958 | PUSH2 | 3 | 0 |[0x6086536060610220527f6087536101e0527f602e60885360536089536060608a] | +| 961 | MSTORE | 6 | 0 |[0x6086536060610220527f6087536101e0527f602e60885360536089536060608a,0x320] | +| 962 | PUSH32 | 3 | 0 | [] | +| 995 | PUSH2 | 3 | 0 |[0x53602f608b536060608c610240527f536000608d5360f3610200526060610220] | +| 998 | MSTORE | 6 | 0 |[0x53602f608b536060608c610240527f536000608d5360f3610200526060610220,0x340] | +| 999 | PUSH32 | 3 | 0 | [] | +| 1032 | PUSH2 | 3 | 0 |[0x53608e610221536053610222536060610260527f610223536000610224536060] | +| 1035 | MSTORE | 6 | 0 |[0x53608e610221536053610222536060610260527f610223536000610224536060,0x360] | +| 1036 | PUSH32 | 3 | 0 | [] | +| 1069 | PUSH2 | 3 | 0 |[0x61022553608f6102265360606102275360006102610280527f28536060610229] | +| 1072 | MSTORE | 6 | 0 |[0x61022553608f6102265360606102275360006102610280527f28536060610229,0x380] | +| 1073 | PUSH32 | 3 | 0 | [] | +| 1106 | PUSH2 | 3 | 0 |[0x53600061022a5360f561022b53606061022c53600061022d536102a0527f6060] | +| 1109 | MSTORE | 6 | 0 |[0x53600061022a5360f561022b53606061022c53600061022d536102a0527f6060,0x3a0] | +| 1110 | PUSH32 | 3 | 0 | [] | +| 1143 | PUSH2 | 3 | 0 |[0x61022e53600061022f5360606102305360006102315360606102325360006102] | +| 1146 | MSTORE | 6 | 0 |[0x61022e53600061022f5360606102305360006102315360606102325360006102,0x3c0] | +| 1147 | PUSH32 | 3 | 0 | [] | +| 1180 | PUSH2 | 3 | 0 |[0xc0527f61023353606061023453600061023553608561023653605a6102375360] | +| 1183 | MSTORE | 7 | 0 |[0xc0527f61023353606061023453600061023553608561023653605a6102375360,0x3e0] | +| 1184 | PUSH32 | 3 | 0 | [] | +| 1217 | PUSH2 | 3 | 0 |[0xf261026102e05260386103005360536103015360606103025360506103035360] | +| 1220 | MSTORE | 6 | 0 |[0xf261026102e05260386103005360536103015360606103025360506103035360,0x400] | +| 1221 | PUSH32 | 3 | 0 | [] | +| 1254 | PUSH2 | 3 | 0 |[0x6161030453600261030553603961030653605361030753606061030853605061] | +| 1257 | MSTORE | 6 | 0 |[0x6161030453600261030553603961030653605361030753606061030853605061,0x420] | +| 1258 | PUSH32 | 3 | 0 | [] | +| 1291 | PUSH2 | 3 | 0 |[0x30953606161030a53600261030b53603a61030c53605361030d53606161030e] | +| 1294 | MSTORE | 6 | 0 |[0x30953606161030a53600261030b53603a61030c53605361030d53606161030e,0x440] | +| 1295 | PUSH32 | 3 | 0 | [] | +| 1328 | PUSH2 | 3 | 0 |[0x53600261030f53603b6103105360606103115360006103125360f36103135361] | +| 1331 | MSTORE | 6 | 0 |[0x53600261030f53603b6103105360606103115360006103125360f36103135361,0x460] | +| 1332 | PUSH1 | 3 | 0 | [] | +| 1334 | PUSH2 | 3 | 0 | [0x3] | +| 1337 | MSTORE8 | 6 | 0 |[0x3,0x480] | +| 1338 | PUSH1 | 3 | 0 | [] | +| 1340 | PUSH2 | 3 | 0 | [0x14] | +| 1343 | MSTORE8 | 3 | 0 |[0x14,0x481] | +| 1344 | PUSH1 | 3 | 0 | [] | +| 1346 | PUSH2 | 3 | 0 | [0x60] | +| 1349 | MSTORE8 | 3 | 0 |[0x60,0x482] | +| 1350 | PUSH1 | 3 | 0 | [] | +| 1352 | PUSH2 | 3 | 0 | [0x0] | +| 1355 | MSTORE8 | 3 | 0 |[0x0,0x483] | +| 1356 | PUSH1 | 3 | 0 | [] | +| 1358 | PUSH2 | 3 | 0 | [0x60] | +| 1361 | MSTORE8 | 3 | 0 |[0x60,0x484] | +| 1362 | PUSH1 | 3 | 0 | [] | +| 1364 | PUSH2 | 3 | 0 | [0x0] | +| 1367 | MSTORE8 | 3 | 0 |[0x0,0x485] | +| 1368 | PUSH1 | 3 | 0 | [] | +| 1370 | PUSH2 | 3 | 0 | [0xf0] | +| 1373 | MSTORE8 | 3 | 0 |[0xf0,0x486] | +| 1374 | PUSH1 | 3 | 0 | [] | +| 1376 | PUSH2 | 3 | 0 | [0x60] | +| 1379 | MSTORE8 | 3 | 0 |[0x60,0x487] | +| 1380 | PUSH1 | 3 | 0 | [] | +| 1382 | PUSH2 | 3 | 0 | [0x0] | +| 1385 | MSTORE8 | 3 | 0 |[0x0,0x488] | +| 1386 | PUSH1 | 3 | 0 | [] | +| 1388 | PUSH2 | 3 | 0 | [0x60] | +| 1391 | MSTORE8 | 3 | 0 |[0x60,0x489] | +| 1392 | PUSH1 | 3 | 0 | [] | +| 1394 | PUSH2 | 3 | 0 | [0x0] | +| 1397 | MSTORE8 | 3 | 0 |[0x0,0x48a] | +| 1398 | PUSH1 | 3 | 0 | [] | +| 1400 | PUSH2 | 3 | 0 | [0x60] | +| 1403 | MSTORE8 | 3 | 0 |[0x60,0x48b] | +| 1404 | PUSH1 | 3 | 0 | [] | +| 1406 | PUSH2 | 3 | 0 | [0x0] | +| 1409 | MSTORE8 | 3 | 0 |[0x0,0x48c] | +| 1410 | PUSH1 | 3 | 0 | [] | +| 1412 | PUSH2 | 3 | 0 | [0x60] | +| 1415 | MSTORE8 | 3 | 0 |[0x60,0x48d] | +| 1416 | PUSH1 | 3 | 0 | [] | +| 1418 | PUSH2 | 3 | 0 | [0x0] | +| 1421 | MSTORE8 | 3 | 0 |[0x0,0x48e] | +| 1422 | PUSH1 | 3 | 0 | [] | +| 1424 | PUSH2 | 3 | 0 | [0x84] | +| 1427 | MSTORE8 | 3 | 0 |[0x84,0x48f] | +| 1428 | PUSH1 | 3 | 0 | [] | +| 1430 | PUSH2 | 3 | 0 | [0x5a] | +| 1433 | MSTORE8 | 3 | 0 |[0x5a,0x490] | +| 1434 | PUSH1 | 3 | 0 | [] | +| 1436 | PUSH2 | 3 | 0 | [0xf4] | +| 1439 | MSTORE8 | 3 | 0 |[0xf4,0x491] | +| 1440 | PUSH1 | 3 | 0 | [] | +| 1442 | PUSH2 | 3 | 0 | [0x50] | +| 1445 | MSTORE8 | 3 | 0 |[0x50,0x492] | +| 1446 | PUSH1 | 3 | 0 | [] | +| 1448 | PUSH2 | 3 | 0 | [0x50] | +| 1451 | MSTORE8 | 3 | 0 |[0x50,0x493] | +| 1452 | PUSH2 | 3 | 0 | [] | +| 1455 | PUSH1 | 3 | 0 | [0x494] | +| 1457 | RETURN | 0 | 0 |[0x494,0x0] | +| 1773 | PUSH1 | 3 | 0 |[0x94a843a7335fc63be036fbdecc40b1365f3c5f26] | +| 1775 | PUSH1 | 3 | 0 |[0x94a843a7335fc63be036fbdecc40b1365f3c5f26,0x0] | +| 1777 | PUSH1 | 3 | 0 |[0x94a843a7335fc63be036fbdecc40b1365f3c5f26,0x0,0x0] | +| 1779 | PUSH1 | 3 | 0 |[0x94a843a7335fc63be036fbdecc40b1365f3c5f26,0x0,0x0,0x0] | +| 1781 | PUSH1 | 3 | 0 |[0x94a843a7335fc63be036fbdecc40b1365f3c5f26,0x0,0x0,0x0,0x0] | +| 1783 | DUP6 | 3 | 0 |[0x94a843a7335fc63be036fbdecc40b1365f3c5f26,0x0,0x0,0x0,0x0,0x0] | +| 1784 | GAS | 2 | 0 |[0x94a843a7335fc63be036fbdecc40b1365f3c5f26,0x0,0x0,0x0,0x0,0x0,0x94a843a7335fc63be036fbdecc40b1365f3c5f26] | +| 1785 | CALL | 22413 | 0 |[0x94a843a7335fc63be036fbdecc40b1365f3c5f26,0x0,0x0,0x0,0x0,0x0,0x94a843a7335fc63be036fbdecc40b1365f3c5f26,0x58ef] | +| 0 | PUSH1 | 3 | 0 | [] | +| 2 | PUSH1 | 3 | 0 | [0x1] | +| 4 | SSTORE | 20000 | 0 | [0x1,0x4] | +| 5 | PUSH1 | 3 | 0 | [] | +| 7 | PUSH1 | 3 | 0 | [0x0] | +| 9 | SSTORE | 100 | 19900 | [0x0,0x4] | +| 10 | PUSH1 | 3 | 19900 | [] | +| 12 | PUSH1 | 3 | 19900 | [0x0] | +| 14 | PUSH1 | 3 | 19900 | [0x0,0x0] | +| 16 | PUSH1 | 3 | 19900 |[0x0,0x0,0x0] | +| 18 | PUSH1 | 3 | 19900 |[0x0,0x0,0x0,0x0] | +| 20 | PUSH1 | 3 | 19900 |[0x0,0x0,0x0,0x0,0x0] | +| 22 | GAS | 2 | 19900 |[0x0,0x0,0x0,0x0,0x0,0xf9] | +| 23 | CALLCODE | 100 | 19900 |[0x0,0x0,0x0,0x0,0x0,0xf9,0x885] | +Error: out of gas: out of gas +| 1786 | POP | 2 | 0 |[0x94a843a7335fc63be036fbdecc40b1365f3c5f26,0x0] | +| 1787 | POP | 2 | 0 |[0x94a843a7335fc63be036fbdecc40b1365f3c5f26] | +| 1788 | STOP | 0 | 0 | [] | +| 2618 | POP | 2 | 0 |[0x7dce2faf43218578e3fcf2ad22df9918a89e2fba,0x1] | +| 2619 | POP | 2 | 0 |[0x7dce2faf43218578e3fcf2ad22df9918a89e2fba] | +| 2620 | STOP | 0 | 0 | [] | + +Post-execution info: + - output: `` + - consumed gas: `732781` + - error: `` +{"stateRoot": "0xad1024c87b5548e77c937aa50f72b6cb620d278f4dd79bae7f78f71ff75af458"} diff --git a/cmd/evm/testdata/evmrun/8.out.1.txt b/cmd/evm/testdata/evmrun/8.out.1.txt new file mode 100644 index 0000000000..dc22c2ef3a --- /dev/null +++ b/cmd/evm/testdata/evmrun/8.out.1.txt @@ -0,0 +1,9 @@ +[ + { + "name": "00000006-naivefuzz-0", + "pass": false, + "stateRoot": "0xad1024c87b5548e77c937aa50f72b6cb620d278f4dd79bae7f78f71ff75af458", + "fork": "London", + "error": "post state root mismatch: got ad1024c87b5548e77c937aa50f72b6cb620d278f4dd79bae7f78f71ff75af458, want 0000000000000000000000000000000000000000000000000000000000000000" + } +] diff --git a/cmd/evm/testdata/evmrun/8.out.2.txt b/cmd/evm/testdata/evmrun/8.out.2.txt new file mode 100644 index 0000000000..3e6e162e29 --- /dev/null +++ b/cmd/evm/testdata/evmrun/8.out.2.txt @@ -0,0 +1,916 @@ +{"pc":0,"op":96,"gas":"0xb4213","gasCost":"0x3","memSize":0,"stack":[],"depth":1,"refund":0,"opName":"PUSH1"} +{"pc":2,"op":96,"gas":"0xb4210","gasCost":"0x3","memSize":0,"stack":["0x2"],"depth":1,"refund":0,"opName":"PUSH1"} +{"pc":4,"op":85,"gas":"0xb420d","gasCost":"0x5654","memSize":0,"stack":["0x2","0x3"],"depth":1,"refund":0,"opName":"SSTORE"} +{"pc":5,"op":96,"gas":"0xaebb9","gasCost":"0x3","memSize":0,"stack":[],"depth":1,"refund":0,"opName":"PUSH1"} +{"pc":7,"op":96,"gas":"0xaebb6","gasCost":"0x3","memSize":0,"stack":["0x0"],"depth":1,"refund":0,"opName":"PUSH1"} +{"pc":9,"op":96,"gas":"0xaebb3","gasCost":"0x3","memSize":0,"stack":["0x0","0x0"],"depth":1,"refund":0,"opName":"PUSH1"} +{"pc":11,"op":96,"gas":"0xaebb0","gasCost":"0x3","memSize":0,"stack":["0x0","0x0","0x0"],"depth":1,"refund":0,"opName":"PUSH1"} +{"pc":13,"op":96,"gas":"0xaebad","gasCost":"0x3","memSize":0,"stack":["0x0","0x0","0x0","0x0"],"depth":1,"refund":0,"opName":"PUSH1"} +{"pc":15,"op":96,"gas":"0xaebaa","gasCost":"0x3","memSize":0,"stack":["0x0","0x0","0x0","0x0","0x0"],"depth":1,"refund":0,"opName":"PUSH1"} +{"pc":17,"op":90,"gas":"0xaeba7","gasCost":"0x2","memSize":0,"stack":["0x0","0x0","0x0","0x0","0x0","0x4"],"depth":1,"refund":0,"opName":"GAS"} +{"pc":18,"op":242,"gas":"0xaeba5","gasCost":"0xabff8","memSize":0,"stack":["0x0","0x0","0x0","0x0","0x0","0x4","0xaeba5"],"depth":1,"refund":0,"opName":"CALLCODE"} +{"output":"","gasUsed":"0xf"} +{"pc":19,"op":80,"gas":"0xaeb32","gasCost":"0x2","memSize":0,"stack":["0x1"],"depth":1,"refund":0,"opName":"POP"} +{"pc":20,"op":127,"gas":"0xaeb30","gasCost":"0x3","memSize":0,"stack":[],"depth":1,"refund":0,"opName":"PUSH32"} +{"pc":53,"op":96,"gas":"0xaeb2d","gasCost":"0x3","memSize":0,"stack":["0x600254506003545060016003557f7f6008545060006004557f60016004556000"],"depth":1,"refund":0,"opName":"PUSH1"} +{"pc":55,"op":82,"gas":"0xaeb2a","gasCost":"0x6","memSize":0,"stack":["0x600254506003545060016003557f7f6008545060006004557f60016004556000","0x0"],"depth":1,"refund":0,"opName":"MSTORE"} +{"pc":56,"op":127,"gas":"0xaeb24","gasCost":"0x3","memSize":32,"stack":[],"depth":1,"refund":0,"opName":"PUSH32"} +{"pc":89,"op":96,"gas":"0xaeb21","gasCost":"0x3","memSize":32,"stack":["0x60045560006000600060006000606000527ff96000527f5af250600060006000"],"depth":1,"refund":0,"opName":"PUSH1"} +{"pc":91,"op":82,"gas":"0xaeb1e","gasCost":"0x6","memSize":32,"stack":["0x60045560006000600060006000606000527ff96000527f5af250600060006000","0x20"],"depth":1,"refund":0,"opName":"MSTORE"} +{"pc":92,"op":127,"gas":"0xaeb18","gasCost":"0x3","memSize":64,"stack":[],"depth":1,"refund":0,"opName":"PUSH32"} +{"pc":125,"op":96,"gas":"0xaeb15","gasCost":"0x3","memSize":64,"stack":["0x606000527e60f45af45060006000600060006020527f600060f55a6020527ff1"],"depth":1,"refund":0,"opName":"PUSH1"} +{"pc":127,"op":82,"gas":"0xaeb12","gasCost":"0x6","memSize":64,"stack":["0x606000527e60f45af45060006000600060006020527f600060f55a6020527ff1","0x40"],"depth":1,"refund":0,"opName":"MSTORE"} +{"pc":128,"op":127,"gas":"0xaeb0c","gasCost":"0x3","memSize":96,"stack":[],"depth":1,"refund":0,"opName":"PUSH32"} +{"pc":161,"op":96,"gas":"0xaeb09","gasCost":"0x3","memSize":96,"stack":["0x50f001075205846a44a283446020527f8ca2600060006040527f600060006004"],"depth":1,"refund":0,"opName":"PUSH1"} +{"pc":163,"op":82,"gas":"0xaeb06","gasCost":"0x6","memSize":96,"stack":["0x50f001075205846a44a283446020527f8ca2600060006040527f600060006004","0x60"],"depth":1,"refund":0,"opName":"MSTORE"} +{"pc":164,"op":127,"gas":"0xaeb00","gasCost":"0x3","memSize":128,"stack":[],"depth":1,"refund":0,"opName":"PUSH32"} +{"pc":197,"op":96,"gas":"0xaeafd","gasCost":"0x3","memSize":128,"stack":["0x5af4506040527f519930847f3b631c54a49b5f600354503260406060527f527f"],"depth":1,"refund":0,"opName":"PUSH1"} +{"pc":199,"op":82,"gas":"0xaeafa","gasCost":"0x6","memSize":128,"stack":["0x5af4506040527f519930847f3b631c54a49b5f600354503260406060527f527f","0x80"],"depth":1,"refund":0,"opName":"MSTORE"} +{"pc":200,"op":127,"gas":"0xaeaf4","gasCost":"0x3","memSize":160,"stack":[],"depth":1,"refund":0,"opName":"PUSH32"} +{"pc":233,"op":96,"gas":"0xaeaf1","gasCost":"0x3","memSize":160,"stack":["0x77306b60006000600060006060527f6000600c5af15060006000600060006080"],"depth":1,"refund":0,"opName":"PUSH1"} +{"pc":235,"op":82,"gas":"0xaeaee","gasCost":"0x6","memSize":160,"stack":["0x77306b60006000600060006060527f6000600c5af15060006000600060006080","0xa0"],"depth":1,"refund":0,"opName":"MSTORE"} +{"pc":236,"op":127,"gas":"0xaeae8","gasCost":"0x3","memSize":192,"stack":[],"depth":1,"refund":0,"opName":"PUSH32"} +{"pc":269,"op":96,"gas":"0xaeae5","gasCost":"0x3","memSize":192,"stack":["0x527f60f85af450506060527f066001600255606080527f035450600060005560"],"depth":1,"refund":0,"opName":"PUSH1"} +{"pc":271,"op":82,"gas":"0xaeae2","gasCost":"0x6","memSize":192,"stack":["0x527f60f85af450506060527f066001600255606080527f035450600060005560","0xc0"],"depth":1,"refund":0,"opName":"MSTORE"} +{"pc":272,"op":126,"gas":"0xaeadc","gasCost":"0x3","memSize":224,"stack":[],"depth":1,"refund":0,"opName":"PUSH31"} +{"pc":304,"op":96,"gas":"0xaead9","gasCost":"0x3","memSize":224,"stack":["0x6060a0527f01556c3394fff4607f7f1684317b6080527f387b9f60a0527f19"],"depth":1,"refund":0,"opName":"PUSH1"} +{"pc":306,"op":82,"gas":"0xaead6","gasCost":"0x6","memSize":224,"stack":["0x6060a0527f01556c3394fff4607f7f1684317b6080527f387b9f60a0527f19","0xe0"],"depth":1,"refund":0,"opName":"MSTORE"} +{"pc":307,"op":127,"gas":"0xaead0","gasCost":"0x3","memSize":256,"stack":[],"depth":1,"refund":0,"opName":"PUSH32"} +{"pc":340,"op":97,"gas":"0xaeacd","gasCost":"0x3","memSize":256,"stack":["0x20700184809d60c0527f60015450011899016e6009ff60026001556000527f9f"],"depth":1,"refund":0,"opName":"PUSH2"} +{"pc":343,"op":82,"gas":"0xaeaca","gasCost":"0x6","memSize":256,"stack":["0x20700184809d60c0527f60015450011899016e6009ff60026001556000527f9f","0x100"],"depth":1,"refund":0,"opName":"MSTORE"} +{"pc":344,"op":127,"gas":"0xaeac4","gasCost":"0x3","memSize":288,"stack":[],"depth":1,"refund":0,"opName":"PUSH32"} +{"pc":377,"op":97,"gas":"0xaeac1","gasCost":"0x3","memSize":288,"stack":["0x60a05260c0527f7f600060e0527f527f9981600160045582600eff6000600060"],"depth":1,"refund":0,"opName":"PUSH2"} +{"pc":380,"op":82,"gas":"0xaeabe","gasCost":"0x6","memSize":288,"stack":["0x60a05260c0527f7f600060e0527f527f9981600160045582600eff6000600060","0x120"],"depth":1,"refund":0,"opName":"MSTORE"} +{"pc":381,"op":126,"gas":"0xaeab8","gasCost":"0x3","memSize":320,"stack":[],"depth":1,"refund":0,"opName":"PUSH31"} +{"pc":413,"op":97,"gas":"0xaeab5","gasCost":"0x3","memSize":320,"stack":["0x600060f65af45060006060e052610100527f7f6060c0527f20527e60006000"],"depth":1,"refund":0,"opName":"PUSH2"} +{"pc":416,"op":82,"gas":"0xaeab2","gasCost":"0x6","memSize":320,"stack":["0x600060f65af45060006060e052610100527f7f6060c0527f20527e60006000","0x140"],"depth":1,"refund":0,"opName":"MSTORE"} +{"pc":417,"op":127,"gas":"0xaeaac","gasCost":"0x3","memSize":352,"stack":[],"depth":1,"refund":0,"opName":"PUSH32"} +{"pc":450,"op":97,"gas":"0xaeaa9","gasCost":"0x3","memSize":352,"stack":["0x6020527f60f75af4501d7f1903166660006000610120527f60610100527e6000"],"depth":1,"refund":0,"opName":"PUSH2"} +{"pc":453,"op":82,"gas":"0xaeaa6","gasCost":"0x6","memSize":352,"stack":["0x6020527f60f75af4501d7f1903166660006000610120527f60610100527e6000","0x160"],"depth":1,"refund":0,"opName":"MSTORE"} +{"pc":454,"op":127,"gas":"0xaeaa0","gasCost":"0x3","memSize":384,"stack":[],"depth":1,"refund":0,"opName":"PUSH32"} +{"pc":487,"op":97,"gas":"0xaea9d","gasCost":"0x3","memSize":384,"stack":["0x600060e0527f60046040527f5af150600060006000604052610140527f7f6000"],"depth":1,"refund":0,"opName":"PUSH2"} +{"pc":490,"op":82,"gas":"0xaea9a","gasCost":"0x6","memSize":384,"stack":["0x600060e0527f60046040527f5af150600060006000604052610140527f7f6000","0x180"],"depth":1,"refund":0,"opName":"MSTORE"} +{"pc":491,"op":127,"gas":"0xaea94","gasCost":"0x3","memSize":416,"stack":[],"depth":1,"refund":0,"opName":"PUSH32"} +{"pc":524,"op":97,"gas":"0xaea91","gasCost":"0x3","memSize":416,"stack":["0x6009610120527f5af4503c95138e5b8f610100527f7f605a6000536060610160"],"depth":1,"refund":0,"opName":"PUSH2"} +{"pc":527,"op":82,"gas":"0xaea8e","gasCost":"0x6","memSize":416,"stack":["0x6009610120527f5af4503c95138e5b8f610100527f7f605a6000536060610160","0x1a0"],"depth":1,"refund":0,"opName":"MSTORE"} +{"pc":528,"op":127,"gas":"0xaea88","gasCost":"0x3","memSize":448,"stack":[],"depth":1,"refund":0,"opName":"PUSH32"} +{"pc":561,"op":97,"gas":"0xaea85","gasCost":"0x3","memSize":448,"stack":["0x527f527f6031600153606b60610140527f02536010606060527f035360456061"],"depth":1,"refund":0,"opName":"PUSH2"} +{"pc":564,"op":82,"gas":"0xaea82","gasCost":"0x6","memSize":448,"stack":["0x527f527f6031600153606b60610140527f02536010606060527f035360456061","0x1c0"],"depth":1,"refund":0,"opName":"MSTORE"} +{"pc":565,"op":127,"gas":"0xaea7c","gasCost":"0x3","memSize":480,"stack":[],"depth":1,"refund":0,"opName":"PUSH32"} +{"pc":598,"op":97,"gas":"0xaea79","gasCost":"0x3","memSize":480,"stack":["0x120610180527f527f04536060600553600160608052610160527f7e527f6006"],"depth":1,"refund":0,"opName":"PUSH2"} +{"pc":601,"op":82,"gas":"0xaea76","gasCost":"0x6","memSize":480,"stack":["0x120610180527f527f04536060600553600160608052610160527f7e527f6006","0x1e0"],"depth":1,"refund":0,"opName":"MSTORE"} +{"pc":602,"op":127,"gas":"0xaea70","gasCost":"0x3","memSize":512,"stack":[],"depth":1,"refund":0,"opName":"PUSH32"} +{"pc":635,"op":97,"gas":"0xaea6d","gasCost":"0x3","memSize":512,"stack":["0x536060600753606101a0527f02600853606080610140527f527f556009536060"],"depth":1,"refund":0,"opName":"PUSH2"} +{"pc":638,"op":82,"gas":"0xaea6a","gasCost":"0x6","memSize":512,"stack":["0x536060600753606101a0527f02600853606080610140527f527f556009536060","0x200"],"depth":1,"refund":0,"opName":"MSTORE"} +{"pc":639,"op":127,"gas":"0xaea64","gasCost":"0x3","memSize":544,"stack":[],"depth":1,"refund":0,"opName":"PUSH32"} +{"pc":672,"op":97,"gas":"0xaea61","gasCost":"0x3","memSize":544,"stack":["0x610180527f600a53600160a06101c0527f527f600b536060600c6020527f5360"],"depth":1,"refund":0,"opName":"PUSH2"} +{"pc":675,"op":82,"gas":"0xaea5e","gasCost":"0x6","memSize":544,"stack":["0x610180527f600a53600160a06101c0527f527f600b536060600c6020527f5360","0x220"],"depth":1,"refund":0,"opName":"MSTORE"} +{"pc":676,"op":126,"gas":"0xaea58","gasCost":"0x3","memSize":576,"stack":[],"depth":1,"refund":0,"opName":"PUSH31"} +{"pc":708,"op":97,"gas":"0xaea55","gasCost":"0x3","memSize":576,"stack":["0x60610160527f0d53606101a0527f55606101e0527f0e60a0527f536060600f"],"depth":1,"refund":0,"opName":"PUSH2"} +{"pc":711,"op":82,"gas":"0xaea52","gasCost":"0x6","memSize":576,"stack":["0x60610160527f0d53606101a0527f55606101e0527f0e60a0527f536060600f","0x240"],"depth":1,"refund":0,"opName":"MSTORE"} +{"pc":712,"op":127,"gas":"0xaea4c","gasCost":"0x3","memSize":608,"stack":[],"depth":1,"refund":0,"opName":"PUSH32"} +{"pc":745,"op":97,"gas":"0xaea49","gasCost":"0x3","memSize":608,"stack":["0x536060c0527f01601053606060115360026101806101610200527fc0527f527f"],"depth":1,"refund":0,"opName":"PUSH2"} +{"pc":748,"op":82,"gas":"0xaea46","gasCost":"0x6","memSize":608,"stack":["0x536060c0527f01601053606060115360026101806101610200527fc0527f527f","0x260"],"depth":1,"refund":0,"opName":"MSTORE"} +{"pc":749,"op":127,"gas":"0xaea40","gasCost":"0x3","memSize":640,"stack":[],"depth":1,"refund":0,"opName":"PUSH32"} +{"pc":782,"op":97,"gas":"0xaea3d","gasCost":"0x3","memSize":640,"stack":["0x601253606040527f55601353606060c0527f60145360e0527f6000610220527f"],"depth":1,"refund":0,"opName":"PUSH2"} +{"pc":785,"op":82,"gas":"0xaea3a","gasCost":"0x6","memSize":640,"stack":["0x601253606040527f55601353606060c0527f60145360e0527f6000610220527f","0x280"],"depth":1,"refund":0,"opName":"MSTORE"} +{"pc":786,"op":127,"gas":"0xaea34","gasCost":"0x3","memSize":672,"stack":[],"depth":1,"refund":0,"opName":"PUSH32"} +{"pc":819,"op":97,"gas":"0xaea31","gasCost":"0x3","memSize":672,"stack":["0x6015536101e0527f60606101a0527f601653600060175360f360185360196060"],"depth":1,"refund":0,"opName":"PUSH2"} +{"pc":822,"op":82,"gas":"0xaea2e","gasCost":"0x6","memSize":672,"stack":["0x6015536101e0527f60606101a0527f601653600060175360f360185360196060","0x2a0"],"depth":1,"refund":0,"opName":"MSTORE"} +{"pc":823,"op":127,"gas":"0xaea28","gasCost":"0x3","memSize":704,"stack":[],"depth":1,"refund":0,"opName":"PUSH32"} +{"pc":856,"op":97,"gas":"0xaea25","gasCost":"0x3","memSize":704,"stack":["0x610240527f605260006060e052610200527f610100527f7f806101c0527f5360"],"depth":1,"refund":0,"opName":"PUSH2"} +{"pc":859,"op":82,"gas":"0xaea22","gasCost":"0x7","memSize":704,"stack":["0x610240527f605260006060e052610200527f610100527f7f806101c0527f5360","0x2c0"],"depth":1,"refund":0,"opName":"MSTORE"} +{"pc":860,"op":127,"gas":"0xaea1b","gasCost":"0x3","memSize":736,"stack":[],"depth":1,"refund":0,"opName":"PUSH32"} +{"pc":893,"op":97,"gas":"0xaea18","gasCost":"0x3","memSize":736,"stack":["0xf360815360610260527f8260006000f060006000600060610220527e845af450"],"depth":1,"refund":0,"opName":"PUSH2"} +{"pc":896,"op":82,"gas":"0xaea15","gasCost":"0x6","memSize":736,"stack":["0xf360815360610260527f8260006000f060006000600060610220527e845af450","0x2e0"],"depth":1,"refund":0,"opName":"MSTORE"} +{"pc":897,"op":127,"gas":"0xaea0f","gasCost":"0x3","memSize":768,"stack":[],"depth":1,"refund":0,"opName":"PUSH32"} +{"pc":930,"op":97,"gas":"0xaea0c","gasCost":"0x3","memSize":768,"stack":["0x506000600061016101e0610280527f527f20527f60610100527e600060006003"],"depth":1,"refund":0,"opName":"PUSH2"} +{"pc":933,"op":82,"gas":"0xaea09","gasCost":"0x6","memSize":768,"stack":["0x506000600061016101e0610280527f527f20527f60610100527e600060006003","0x300"],"depth":1,"refund":0,"opName":"MSTORE"} +{"pc":934,"op":127,"gas":"0xaea03","gasCost":"0x3","memSize":800,"stack":[],"depth":1,"refund":0,"opName":"PUSH32"} +{"pc":967,"op":97,"gas":"0xaea00","gasCost":"0x3","memSize":800,"stack":["0x610240527f5af15060005450c760006102a0527f6002551309f562610200527f"],"depth":1,"refund":0,"opName":"PUSH2"} +{"pc":970,"op":82,"gas":"0xae9fd","gasCost":"0x6","memSize":800,"stack":["0x610240527f5af15060005450c760006102a0527f6002551309f562610200527f","0x320"],"depth":1,"refund":0,"opName":"MSTORE"} +{"pc":971,"op":127,"gas":"0xae9f7","gasCost":"0x3","memSize":832,"stack":[],"depth":1,"refund":0,"opName":"PUSH32"} +{"pc":1004,"op":97,"gas":"0xae9f4","gasCost":"0x3","memSize":832,"stack":["0x66a486610140527f6b00610260527f1d457161016102c0527f20527f60005450"],"depth":1,"refund":0,"opName":"PUSH2"} +{"pc":1007,"op":82,"gas":"0xae9f1","gasCost":"0x6","memSize":832,"stack":["0x66a486610140527f6b00610260527f1d457161016102c0527f20527f60005450","0x340"],"depth":1,"refund":0,"opName":"MSTORE"} +{"pc":1008,"op":127,"gas":"0xae9eb","gasCost":"0x3","memSize":864,"stack":[],"depth":1,"refund":0,"opName":"PUSH32"} +{"pc":1041,"op":97,"gas":"0xae9e8","gasCost":"0x3","memSize":864,"stack":["0x1c641d373c7f60045450610220527f6000600155610280527f6102e0527f6005"],"depth":1,"refund":0,"opName":"PUSH2"} +{"pc":1044,"op":82,"gas":"0xae9e5","gasCost":"0x6","memSize":864,"stack":["0x1c641d373c7f60045450610220527f6000600155610280527f6102e0527f6005","0x360"],"depth":1,"refund":0,"opName":"MSTORE"} +{"pc":1045,"op":127,"gas":"0xae9df","gasCost":"0x3","memSize":896,"stack":[],"depth":1,"refund":0,"opName":"PUSH32"} +{"pc":1078,"op":97,"gas":"0xae9dc","gasCost":"0x3","memSize":896,"stack":["0x54610160527f50600160025560085450610140527f60006002610240527f6103"],"depth":1,"refund":0,"opName":"PUSH2"} +{"pc":1081,"op":82,"gas":"0xae9d9","gasCost":"0x6","memSize":896,"stack":["0x54610160527f50600160025560085450610140527f60006002610240527f6103","0x380"],"depth":1,"refund":0,"opName":"MSTORE"} +{"pc":1082,"op":126,"gas":"0xae9d3","gasCost":"0x3","memSize":928,"stack":[],"depth":1,"refund":0,"opName":"PUSH31"} +{"pc":1114,"op":97,"gas":"0xae9d0","gasCost":"0x3","memSize":928,"stack":["0x527f6102a0527f557fd86000606000527e600060610180527e600060005af1"],"depth":1,"refund":0,"opName":"PUSH2"} +{"pc":1117,"op":82,"gas":"0xae9cd","gasCost":"0x6","memSize":928,"stack":["0x527f6102a0527f557fd86000606000527e600060610180527e600060005af1","0x3a0"],"depth":1,"refund":0,"opName":"MSTORE"} +{"pc":1118,"op":127,"gas":"0xae9c7","gasCost":"0x3","memSize":960,"stack":[],"depth":1,"refund":0,"opName":"PUSH32"} +{"pc":1151,"op":97,"gas":"0xae9c4","gasCost":"0x3","memSize":960,"stack":["0x508612610320527f17145147356102c0527f610260527f610160527f5198a37e"],"depth":1,"refund":0,"opName":"PUSH2"} +{"pc":1154,"op":82,"gas":"0xae9c1","gasCost":"0x6","memSize":960,"stack":["0x508612610320527f17145147356102c0527f610260527f610160527f5198a37e","0x3c0"],"depth":1,"refund":0,"opName":"MSTORE"} +{"pc":1155,"op":127,"gas":"0xae9bb","gasCost":"0x3","memSize":992,"stack":[],"depth":1,"refund":0,"opName":"PUSH32"} +{"pc":1188,"op":97,"gas":"0xae9b8","gasCost":"0x3","memSize":992,"stack":["0x127a7efa7c600052610340527f6101a0527f606020527f6102e0527f60602053"],"depth":1,"refund":0,"opName":"PUSH2"} +{"pc":1191,"op":82,"gas":"0xae9b5","gasCost":"0x7","memSize":992,"stack":["0x127a7efa7c600052610340527f6101a0527f606020527f6102e0527f60602053","0x3e0"],"depth":1,"refund":0,"opName":"MSTORE"} +{"pc":1192,"op":127,"gas":"0xae9ae","gasCost":"0x3","memSize":1024,"stack":[],"depth":1,"refund":0,"opName":"PUSH32"} +{"pc":1225,"op":97,"gas":"0xae9ab","gasCost":"0x3","memSize":1024,"stack":["0x60610280527ff760215360ff60610360527f225360610180527fdb6023536037"],"depth":1,"refund":0,"opName":"PUSH2"} +{"pc":1228,"op":82,"gas":"0xae9a8","gasCost":"0x6","memSize":1024,"stack":["0x60610280527ff760215360ff60610360527f225360610180527fdb6023536037","0x400"],"depth":1,"refund":0,"opName":"MSTORE"} +{"pc":1229,"op":127,"gas":"0xae9a2","gasCost":"0x3","memSize":1056,"stack":[],"depth":1,"refund":0,"opName":"PUSH32"} +{"pc":1262,"op":97,"gas":"0xae99f","gasCost":"0x3","memSize":1056,"stack":["0x60610300527f6101c0527f24536075606102610380527fa0527f2553609f6060"],"depth":1,"refund":0,"opName":"PUSH2"} +{"pc":1265,"op":82,"gas":"0xae99c","gasCost":"0x6","memSize":1056,"stack":["0x60610300527f6101c0527f24536075606102610380527fa0527f2553609f6060","0x420"],"depth":1,"refund":0,"opName":"MSTORE"} +{"pc":1266,"op":127,"gas":"0xae996","gasCost":"0x3","memSize":1088,"stack":[],"depth":1,"refund":0,"opName":"PUSH32"} +{"pc":1299,"op":97,"gas":"0xae993","gasCost":"0x3","memSize":1088,"stack":["0x40527f265360fe60275360610320527f8f60286101a0526103a0527f7f536061"],"depth":1,"refund":0,"opName":"PUSH2"} +{"pc":1302,"op":82,"gas":"0xae990","gasCost":"0x6","memSize":1088,"stack":["0x40527f265360fe60275360610320527f8f60286101a0526103a0527f7f536061","0x440"],"depth":1,"refund":0,"opName":"MSTORE"} +{"pc":1303,"op":127,"gas":"0xae98a","gasCost":"0x3","memSize":1120,"stack":[],"depth":1,"refund":0,"opName":"PUSH32"} +{"pc":1336,"op":97,"gas":"0xae987","gasCost":"0x3","memSize":1120,"stack":["0x1e0527f6102c0527f0b6029536060602a53600060610340527f2b536103c052"],"depth":1,"refund":0,"opName":"PUSH2"} +{"pc":1339,"op":82,"gas":"0xae984","gasCost":"0x6","memSize":1120,"stack":["0x1e0527f6102c0527f0b6029536060602a53600060610340527f2b536103c052","0x460"],"depth":1,"refund":0,"opName":"MSTORE"} +{"pc":1340,"op":127,"gas":"0xae97e","gasCost":"0x3","memSize":1152,"stack":[],"depth":1,"refund":0,"opName":"PUSH32"} +{"pc":1373,"op":97,"gas":"0xae97b","gasCost":"0x3","memSize":1152,"stack":["0x7f6060602c53606052606060805360006061016102e0527f610200527fc05261"],"depth":1,"refund":0,"opName":"PUSH2"} +{"pc":1376,"op":82,"gas":"0xae978","gasCost":"0x6","memSize":1152,"stack":["0x7f6060602c53606052606060805360006061016102e0527f610200527fc05261","0x480"],"depth":1,"refund":0,"opName":"MSTORE"} +{"pc":1377,"op":127,"gas":"0xae972","gasCost":"0x3","memSize":1184,"stack":[],"depth":1,"refund":0,"opName":"PUSH32"} +{"pc":1410,"op":97,"gas":"0xae96f","gasCost":"0x3","memSize":1184,"stack":["0x36103e0527f60527f7f81536060608253602d60835360536084536060608553"],"depth":1,"refund":0,"opName":"PUSH2"} +{"pc":1413,"op":82,"gas":"0xae96c","gasCost":"0x6","memSize":1184,"stack":["0x36103e0527f60527f7f81536060608253602d60835360536084536060608553","0x4a0"],"depth":1,"refund":0,"opName":"MSTORE"} +{"pc":1414,"op":127,"gas":"0xae966","gasCost":"0x3","memSize":1216,"stack":[],"depth":1,"refund":0,"opName":"PUSH32"} +{"pc":1447,"op":97,"gas":"0xae963","gasCost":"0x3","memSize":1216,"stack":["0x60fd61030052610400527f7f6086610380527f536060610220527f6087536101"],"depth":1,"refund":0,"opName":"PUSH2"} +{"pc":1450,"op":82,"gas":"0xae960","gasCost":"0x6","memSize":1216,"stack":["0x60fd61030052610400527f7f6086610380527f536060610220527f6087536101","0x4c0"],"depth":1,"refund":0,"opName":"MSTORE"} +{"pc":1451,"op":127,"gas":"0xae95a","gasCost":"0x3","memSize":1248,"stack":[],"depth":1,"refund":0,"opName":"PUSH32"} +{"pc":1484,"op":97,"gas":"0xae957","gasCost":"0x3","memSize":1248,"stack":["0xe0527f602e608853605360610420527f89536060608a61036103a0527f20527f"],"depth":1,"refund":0,"opName":"PUSH2"} +{"pc":1487,"op":82,"gas":"0xae954","gasCost":"0x7","memSize":1248,"stack":["0xe0527f602e608853605360610420527f89536060608a61036103a0527f20527f","0x4e0"],"depth":1,"refund":0,"opName":"MSTORE"} +{"pc":1488,"op":127,"gas":"0xae94d","gasCost":"0x3","memSize":1280,"stack":[],"depth":1,"refund":0,"opName":"PUSH32"} +{"pc":1521,"op":97,"gas":"0xae94a","gasCost":"0x3","memSize":1280,"stack":["0x53602f608b536060608c610240527f53610440527f6000608d5360f361020052"],"depth":1,"refund":0,"opName":"PUSH2"} +{"pc":1524,"op":82,"gas":"0xae947","gasCost":"0x6","memSize":1280,"stack":["0x53602f608b536060608c610240527f53610440527f6000608d5360f361020052","0x500"],"depth":1,"refund":0,"opName":"MSTORE"} +{"pc":1525,"op":127,"gas":"0xae941","gasCost":"0x3","memSize":1312,"stack":[],"depth":1,"refund":0,"opName":"PUSH32"} +{"pc":1558,"op":97,"gas":"0xae93e","gasCost":"0x3","memSize":1312,"stack":["0x60606103c0527f610220610340527f53608e610221610460527f536053610222"],"depth":1,"refund":0,"opName":"PUSH2"} +{"pc":1561,"op":82,"gas":"0xae93b","gasCost":"0x6","memSize":1312,"stack":["0x60606103c0527f610220610340527f53608e610221610460527f536053610222","0x520"],"depth":1,"refund":0,"opName":"MSTORE"} +{"pc":1562,"op":127,"gas":"0xae935","gasCost":"0x3","memSize":1344,"stack":[],"depth":1,"refund":0,"opName":"PUSH32"} +{"pc":1595,"op":97,"gas":"0xae932","gasCost":"0x3","memSize":1344,"stack":["0x536060610260527f610223536103e0527f600061022453606061610480527f03"],"depth":1,"refund":0,"opName":"PUSH2"} +{"pc":1598,"op":82,"gas":"0xae92f","gasCost":"0x6","memSize":1344,"stack":["0x536060610260527f610223536103e0527f600061022453606061610480527f03","0x540"],"depth":1,"refund":0,"opName":"MSTORE"} +{"pc":1599,"op":127,"gas":"0xae929","gasCost":"0x3","memSize":1376,"stack":[],"depth":1,"refund":0,"opName":"PUSH32"} +{"pc":1632,"op":97,"gas":"0xae926","gasCost":"0x3","memSize":1376,"stack":["0x60527f61022553608f61022653606061022753600061610400527f0261028061"],"depth":1,"refund":0,"opName":"PUSH2"} +{"pc":1635,"op":82,"gas":"0xae923","gasCost":"0x6","memSize":1376,"stack":["0x60527f61022553608f61022653606061022753600061610400527f0261028061","0x560"],"depth":1,"refund":0,"opName":"MSTORE"} +{"pc":1636,"op":127,"gas":"0xae91d","gasCost":"0x3","memSize":1408,"stack":[],"depth":1,"refund":0,"opName":"PUSH32"} +{"pc":1669,"op":97,"gas":"0xae91a","gasCost":"0x3","memSize":1408,"stack":["0x4a0527f527f28536060610229610380527f53600061022a5360f561022b5360"],"depth":1,"refund":0,"opName":"PUSH2"} +{"pc":1672,"op":82,"gas":"0xae917","gasCost":"0x6","memSize":1408,"stack":["0x4a0527f527f28536060610229610380527f53600061022a5360f561022b5360","0x580"],"depth":1,"refund":0,"opName":"MSTORE"} +{"pc":1673,"op":127,"gas":"0xae911","gasCost":"0x3","memSize":1440,"stack":[],"depth":1,"refund":0,"opName":"PUSH32"} +{"pc":1706,"op":97,"gas":"0xae90e","gasCost":"0x3","memSize":1440,"stack":["0x610420526104c0527f7f6061022c53600061022d536102a0527f60606103a052"],"depth":1,"refund":0,"opName":"PUSH2"} +{"pc":1709,"op":82,"gas":"0xae90b","gasCost":"0x7","memSize":1440,"stack":["0x610420526104c0527f7f6061022c53600061022d536102a0527f60606103a052","0x5a0"],"depth":1,"refund":0,"opName":"MSTORE"} +{"pc":1710,"op":127,"gas":"0xae904","gasCost":"0x3","memSize":1472,"stack":[],"depth":1,"refund":0,"opName":"PUSH32"} +{"pc":1743,"op":97,"gas":"0xae901","gasCost":"0x3","memSize":1472,"stack":["0x7f61022e53600061026104e0527f2f610440527f536060610230536000610231"],"depth":1,"refund":0,"opName":"PUSH2"} +{"pc":1746,"op":82,"gas":"0xae8fe","gasCost":"0x6","memSize":1472,"stack":["0x7f61022e53600061026104e0527f2f610440527f536060610230536000610231","0x5c0"],"depth":1,"refund":0,"opName":"MSTORE"} +{"pc":1747,"op":127,"gas":"0xae8f8","gasCost":"0x3","memSize":1504,"stack":[],"depth":1,"refund":0,"opName":"PUSH32"} +{"pc":1780,"op":97,"gas":"0xae8f5","gasCost":"0x3","memSize":1504,"stack":["0x53606061023253600061026103c0610500527f527fc0527f61610460527f0233"],"depth":1,"refund":0,"opName":"PUSH2"} +{"pc":1783,"op":82,"gas":"0xae8f2","gasCost":"0x6","memSize":1504,"stack":["0x53606061023253600061026103c0610500527f527fc0527f61610460527f0233","0x5e0"],"depth":1,"refund":0,"opName":"MSTORE"} +{"pc":1784,"op":127,"gas":"0xae8ec","gasCost":"0x3","memSize":1536,"stack":[],"depth":1,"refund":0,"opName":"PUSH32"} +{"pc":1817,"op":97,"gas":"0xae8e9","gasCost":"0x3","memSize":1536,"stack":["0x53606061023453600061023553608561023653610520527f605a610237536061"],"depth":1,"refund":0,"opName":"PUSH2"} +{"pc":1820,"op":82,"gas":"0xae8e6","gasCost":"0x6","memSize":1536,"stack":["0x53606061023453600061023553608561023653610520527f605a610237536061","0x600"],"depth":1,"refund":0,"opName":"MSTORE"} +{"pc":1821,"op":127,"gas":"0xae8e0","gasCost":"0x3","memSize":1568,"stack":[],"depth":1,"refund":0,"opName":"PUSH32"} +{"pc":1854,"op":97,"gas":"0xae8dd","gasCost":"0x3","memSize":1568,"stack":["0x3e052610480527f7ff261026102e0526038610300536053610540527f610301"],"depth":1,"refund":0,"opName":"PUSH2"} +{"pc":1857,"op":82,"gas":"0xae8da","gasCost":"0x6","memSize":1568,"stack":["0x3e052610480527f7ff261026102e0526038610300536053610540527f610301","0x620"],"depth":1,"refund":0,"opName":"MSTORE"} +{"pc":1858,"op":127,"gas":"0xae8d4","gasCost":"0x3","memSize":1600,"stack":[],"depth":1,"refund":0,"opName":"PUSH32"} +{"pc":1891,"op":97,"gas":"0xae8d1","gasCost":"0x3","memSize":1600,"stack":["0x536060610302536050610303536104a0527f60610400527f6161030453610560"],"depth":1,"refund":0,"opName":"PUSH2"} +{"pc":1894,"op":82,"gas":"0xae8ce","gasCost":"0x7","memSize":1600,"stack":["0x536060610302536050610303536104a0527f60610400527f6161030453610560","0x640"],"depth":1,"refund":0,"opName":"MSTORE"} +{"pc":1895,"op":127,"gas":"0xae8c7","gasCost":"0x3","memSize":1632,"stack":[],"depth":1,"refund":0,"opName":"PUSH32"} +{"pc":1928,"op":97,"gas":"0xae8c4","gasCost":"0x3","memSize":1632,"stack":["0x527f6002610305536039610306536053610307536060616104c0527f03085360"],"depth":1,"refund":0,"opName":"PUSH2"} +{"pc":1931,"op":82,"gas":"0xae8c1","gasCost":"0x6","memSize":1632,"stack":["0x527f6002610305536039610306536053610307536060616104c0527f03085360","0x660"],"depth":1,"refund":0,"opName":"MSTORE"} +{"pc":1932,"op":127,"gas":"0xae8bb","gasCost":"0x3","memSize":1664,"stack":[],"depth":1,"refund":0,"opName":"PUSH32"} +{"pc":1965,"op":97,"gas":"0xae8b8","gasCost":"0x3","memSize":1664,"stack":["0x5061610580527f610420527f030953606161030a53600261030b53603a61030c"],"depth":1,"refund":0,"opName":"PUSH2"} +{"pc":1968,"op":82,"gas":"0xae8b5","gasCost":"0x6","memSize":1664,"stack":["0x5061610580527f610420527f030953606161030a53600261030b53603a61030c","0x680"],"depth":1,"refund":0,"opName":"MSTORE"} +{"pc":1969,"op":127,"gas":"0xae8af","gasCost":"0x3","memSize":1696,"stack":[],"depth":1,"refund":0,"opName":"PUSH32"} +{"pc":2002,"op":97,"gas":"0xae8ac","gasCost":"0x3","memSize":1696,"stack":["0x536104e0527f606105a0527f5361030d53606161030e610440527f5360026103"],"depth":1,"refund":0,"opName":"PUSH2"} +{"pc":2005,"op":82,"gas":"0xae8a9","gasCost":"0x6","memSize":1696,"stack":["0x536104e0527f606105a0527f5361030d53606161030e610440527f5360026103","0x6a0"],"depth":1,"refund":0,"opName":"MSTORE"} +{"pc":2006,"op":127,"gas":"0xae8a3","gasCost":"0x3","memSize":1728,"stack":[],"depth":1,"refund":0,"opName":"PUSH32"} +{"pc":2039,"op":97,"gas":"0xae8a0","gasCost":"0x3","memSize":1728,"stack":["0xf53603b61031053606061616105c0527f0500527f03115360006103125360f3"],"depth":1,"refund":0,"opName":"PUSH2"} +{"pc":2042,"op":82,"gas":"0xae89d","gasCost":"0x6","memSize":1728,"stack":["0xf53603b61031053606061616105c0527f0500527f03115360006103125360f3","0x6c0"],"depth":1,"refund":0,"opName":"MSTORE"} +{"pc":2043,"op":127,"gas":"0xae897","gasCost":"0x3","memSize":1760,"stack":[],"depth":1,"refund":0,"opName":"PUSH32"} +{"pc":2076,"op":97,"gas":"0xae894","gasCost":"0x3","memSize":1760,"stack":["0x61031353616104605260036104805360146105e0527f61048153610520527f60"],"depth":1,"refund":0,"opName":"PUSH2"} +{"pc":2079,"op":82,"gas":"0xae891","gasCost":"0x7","memSize":1760,"stack":["0x61031353616104605260036104805360146105e0527f61048153610520527f60","0x6e0"],"depth":1,"refund":0,"opName":"MSTORE"} +{"pc":2080,"op":127,"gas":"0xae88a","gasCost":"0x3","memSize":1792,"stack":[],"depth":1,"refund":0,"opName":"PUSH32"} +{"pc":2113,"op":97,"gas":"0xae887","gasCost":"0x3","memSize":1792,"stack":["0x60610482536000610483536060610484536000610485610600527f5360f06104"],"depth":1,"refund":0,"opName":"PUSH2"} +{"pc":2116,"op":82,"gas":"0xae884","gasCost":"0x6","memSize":1792,"stack":["0x60610482536000610483536060610484536000610485610600527f5360f06104","0x700"],"depth":1,"refund":0,"opName":"MSTORE"} +{"pc":2117,"op":127,"gas":"0xae87e","gasCost":"0x3","memSize":1824,"stack":[],"depth":1,"refund":0,"opName":"PUSH32"} +{"pc":2150,"op":97,"gas":"0xae87b","gasCost":"0x3","memSize":1824,"stack":["0x86536060610540527f610487536000610488536060610489536000610620527f"],"depth":1,"refund":0,"opName":"PUSH2"} +{"pc":2153,"op":82,"gas":"0xae878","gasCost":"0x6","memSize":1824,"stack":["0x86536060610540527f610487536000610488536060610489536000610620527f","0x720"],"depth":1,"refund":0,"opName":"MSTORE"} +{"pc":2154,"op":127,"gas":"0xae872","gasCost":"0x3","memSize":1856,"stack":[],"depth":1,"refund":0,"opName":"PUSH32"} +{"pc":2187,"op":97,"gas":"0xae86f","gasCost":"0x3","memSize":1856,"stack":["0x61048a53606061048b5360006104610560527f8c53606061048d53600061048e"],"depth":1,"refund":0,"opName":"PUSH2"} +{"pc":2190,"op":82,"gas":"0xae86c","gasCost":"0x6","memSize":1856,"stack":["0x61048a53606061048b5360006104610560527f8c53606061048d53600061048e","0x740"],"depth":1,"refund":0,"opName":"MSTORE"} +{"pc":2191,"op":127,"gas":"0xae866","gasCost":"0x3","memSize":1888,"stack":[],"depth":1,"refund":0,"opName":"PUSH32"} +{"pc":2224,"op":97,"gas":"0xae863","gasCost":"0x3","memSize":1888,"stack":["0x610640527f53608461048f53605a6104905360f4610491536105805260606105"],"depth":1,"refund":0,"opName":"PUSH2"} +{"pc":2227,"op":82,"gas":"0xae860","gasCost":"0x7","memSize":1888,"stack":["0x610640527f53608461048f53605a6104905360f4610491536105805260606105","0x760"],"depth":1,"refund":0,"opName":"MSTORE"} +{"pc":2228,"op":127,"gas":"0xae859","gasCost":"0x3","memSize":1920,"stack":[],"depth":1,"refund":0,"opName":"PUSH32"} +{"pc":2261,"op":97,"gas":"0xae856","gasCost":"0x3","memSize":1920,"stack":["0xa053605061610660527f05a15360616105a25360046105a35360926105a45360"],"depth":1,"refund":0,"opName":"PUSH2"} +{"pc":2264,"op":82,"gas":"0xae853","gasCost":"0x6","memSize":1920,"stack":["0xa053605061610660527f05a15360616105a25360046105a35360926105a45360","0x780"],"depth":1,"refund":0,"opName":"MSTORE"} +{"pc":2265,"op":127,"gas":"0xae84d","gasCost":"0x3","memSize":1952,"stack":[],"depth":1,"refund":0,"opName":"PUSH32"} +{"pc":2298,"op":97,"gas":"0xae84a","gasCost":"0x3","memSize":1952,"stack":["0x536105a55360606105a6610680527f5360506105a75360616105a85360046105"],"depth":1,"refund":0,"opName":"PUSH2"} +{"pc":2301,"op":82,"gas":"0xae847","gasCost":"0x6","memSize":1952,"stack":["0x536105a55360606105a6610680527f5360506105a75360616105a85360046105","0x7a0"],"depth":1,"refund":0,"opName":"MSTORE"} +{"pc":2302,"op":127,"gas":"0xae841","gasCost":"0x3","memSize":1984,"stack":[],"depth":1,"refund":0,"opName":"PUSH32"} +{"pc":2335,"op":97,"gas":"0xae83e","gasCost":"0x3","memSize":1984,"stack":["0xa95360936105aa5360536105ab53606106a0527f616105ac5360046105ad5360"],"depth":1,"refund":0,"opName":"PUSH2"} +{"pc":2338,"op":82,"gas":"0xae83b","gasCost":"0x6","memSize":1984,"stack":["0xa95360936105aa5360536105ab53606106a0527f616105ac5360046105ad5360","0x7c0"],"depth":1,"refund":0,"opName":"MSTORE"} +{"pc":2339,"op":127,"gas":"0xae835","gasCost":"0x3","memSize":2016,"stack":[],"depth":1,"refund":0,"opName":"PUSH32"} +{"pc":2372,"op":97,"gas":"0xae832","gasCost":"0x3","memSize":2016,"stack":["0x946105ae5360606105af5360006105b05360f3616106c05260056106e05360b1"],"depth":1,"refund":0,"opName":"PUSH2"} +{"pc":2375,"op":82,"gas":"0xae82f","gasCost":"0x7","memSize":2016,"stack":["0x946105ae5360606105af5360006105b05360f3616106c05260056106e05360b1","0x7e0"],"depth":1,"refund":0,"opName":"MSTORE"} +{"pc":2376,"op":127,"gas":"0xae828","gasCost":"0x3","memSize":2048,"stack":[],"depth":1,"refund":0,"opName":"PUSH32"} +{"pc":2409,"op":97,"gas":"0xae825","gasCost":"0x3","memSize":2048,"stack":["0x6106e15360536106e25360606106e35360006106e45360616106e55360056106"],"depth":1,"refund":0,"opName":"PUSH2"} +{"pc":2412,"op":82,"gas":"0xae822","gasCost":"0x6","memSize":2048,"stack":["0x6106e15360536106e25360606106e35360006106e45360616106e55360056106","0x800"],"depth":1,"refund":0,"opName":"MSTORE"} +{"pc":2413,"op":127,"gas":"0xae81c","gasCost":"0x3","memSize":2080,"stack":[],"depth":1,"refund":0,"opName":"PUSH32"} +{"pc":2446,"op":97,"gas":"0xae819","gasCost":"0x3","memSize":2080,"stack":["0xe65360b26106e75360606106e85360006106e95360606106ea5360006106eb53"],"depth":1,"refund":0,"opName":"PUSH2"} +{"pc":2449,"op":82,"gas":"0xae816","gasCost":"0x6","memSize":2080,"stack":["0xe65360b26106e75360606106e85360006106e95360606106ea5360006106eb53","0x820"],"depth":1,"refund":0,"opName":"MSTORE"} +{"pc":2450,"op":127,"gas":"0xae810","gasCost":"0x3","memSize":2112,"stack":[],"depth":1,"refund":0,"opName":"PUSH32"} +{"pc":2483,"op":97,"gas":"0xae80d","gasCost":"0x3","memSize":2112,"stack":["0x60f56106ec5360606106ed5360006106ee5360606106ef5360006106f0536060"],"depth":1,"refund":0,"opName":"PUSH2"} +{"pc":2486,"op":82,"gas":"0xae80a","gasCost":"0x6","memSize":2112,"stack":["0x60f56106ec5360606106ed5360006106ee5360606106ef5360006106f0536060","0x840"],"depth":1,"refund":0,"opName":"MSTORE"} +{"pc":2487,"op":127,"gas":"0xae804","gasCost":"0x3","memSize":2144,"stack":[],"depth":1,"refund":0,"opName":"PUSH32"} +{"pc":2520,"op":97,"gas":"0xae801","gasCost":"0x3","memSize":2144,"stack":["0x6106f15360006106f25360606106f35360006106f45360606106f55360006106"],"depth":1,"refund":0,"opName":"PUSH2"} +{"pc":2523,"op":82,"gas":"0xae7fe","gasCost":"0x7","memSize":2144,"stack":["0x6106f15360006106f25360606106f35360006106f45360606106f55360006106","0x860"],"depth":1,"refund":0,"opName":"MSTORE"} +{"pc":2524,"op":127,"gas":"0xae7f7","gasCost":"0x3","memSize":2176,"stack":[],"depth":1,"refund":0,"opName":"PUSH32"} +{"pc":2557,"op":97,"gas":"0xae7f4","gasCost":"0x3","memSize":2176,"stack":["0xf65360856106f753605a6106f85360f16106f95360506106fa5360506106fb53"],"depth":1,"refund":0,"opName":"PUSH2"} +{"pc":2560,"op":82,"gas":"0xae7f1","gasCost":"0x6","memSize":2176,"stack":["0xf65360856106f753605a6106f85360f16106f95360506106fa5360506106fb53","0x880"],"depth":1,"refund":0,"opName":"MSTORE"} +{"pc":2561,"op":96,"gas":"0xae7eb","gasCost":"0x3","memSize":2208,"stack":[],"depth":1,"refund":0,"opName":"PUSH1"} +{"pc":2563,"op":97,"gas":"0xae7e8","gasCost":"0x3","memSize":2208,"stack":["0x61"],"depth":1,"refund":0,"opName":"PUSH2"} +{"pc":2566,"op":83,"gas":"0xae7e5","gasCost":"0x6","memSize":2208,"stack":["0x61","0x8a0"],"depth":1,"refund":0,"opName":"MSTORE8"} +{"pc":2567,"op":96,"gas":"0xae7df","gasCost":"0x3","memSize":2240,"stack":[],"depth":1,"refund":0,"opName":"PUSH1"} +{"pc":2569,"op":97,"gas":"0xae7dc","gasCost":"0x3","memSize":2240,"stack":["0x6"],"depth":1,"refund":0,"opName":"PUSH2"} +{"pc":2572,"op":83,"gas":"0xae7d9","gasCost":"0x3","memSize":2240,"stack":["0x6","0x8a1"],"depth":1,"refund":0,"opName":"MSTORE8"} +{"pc":2573,"op":96,"gas":"0xae7d6","gasCost":"0x3","memSize":2240,"stack":[],"depth":1,"refund":0,"opName":"PUSH1"} +{"pc":2575,"op":97,"gas":"0xae7d3","gasCost":"0x3","memSize":2240,"stack":["0xfc"],"depth":1,"refund":0,"opName":"PUSH2"} +{"pc":2578,"op":83,"gas":"0xae7d0","gasCost":"0x3","memSize":2240,"stack":["0xfc","0x8a2"],"depth":1,"refund":0,"opName":"MSTORE8"} +{"pc":2579,"op":96,"gas":"0xae7cd","gasCost":"0x3","memSize":2240,"stack":[],"depth":1,"refund":0,"opName":"PUSH1"} +{"pc":2581,"op":97,"gas":"0xae7ca","gasCost":"0x3","memSize":2240,"stack":["0x60"],"depth":1,"refund":0,"opName":"PUSH2"} +{"pc":2584,"op":83,"gas":"0xae7c7","gasCost":"0x3","memSize":2240,"stack":["0x60","0x8a3"],"depth":1,"refund":0,"opName":"MSTORE8"} +{"pc":2585,"op":96,"gas":"0xae7c4","gasCost":"0x3","memSize":2240,"stack":[],"depth":1,"refund":0,"opName":"PUSH1"} +{"pc":2587,"op":97,"gas":"0xae7c1","gasCost":"0x3","memSize":2240,"stack":["0x0"],"depth":1,"refund":0,"opName":"PUSH2"} +{"pc":2590,"op":83,"gas":"0xae7be","gasCost":"0x3","memSize":2240,"stack":["0x0","0x8a4"],"depth":1,"refund":0,"opName":"MSTORE8"} +{"pc":2591,"op":96,"gas":"0xae7bb","gasCost":"0x3","memSize":2240,"stack":[],"depth":1,"refund":0,"opName":"PUSH1"} +{"pc":2593,"op":97,"gas":"0xae7b8","gasCost":"0x3","memSize":2240,"stack":["0xf3"],"depth":1,"refund":0,"opName":"PUSH2"} +{"pc":2596,"op":83,"gas":"0xae7b5","gasCost":"0x3","memSize":2240,"stack":["0xf3","0x8a5"],"depth":1,"refund":0,"opName":"MSTORE8"} +{"pc":2597,"op":97,"gas":"0xae7b2","gasCost":"0x3","memSize":2240,"stack":[],"depth":1,"refund":0,"opName":"PUSH2"} +{"pc":2600,"op":96,"gas":"0xae7af","gasCost":"0x3","memSize":2240,"stack":["0x8a6"],"depth":1,"refund":0,"opName":"PUSH1"} +{"pc":2602,"op":96,"gas":"0xae7ac","gasCost":"0x3","memSize":2240,"stack":["0x8a6","0x0"],"depth":1,"refund":0,"opName":"PUSH1"} +{"pc":2604,"op":240,"gas":"0xae7a9","gasCost":"0x7d00","memSize":2240,"stack":["0x8a6","0x0","0x0"],"depth":1,"refund":0,"opName":"CREATE"} +{"pc":0,"op":96,"gas":"0xa40ff","gasCost":"0x3","memSize":0,"stack":[],"depth":2,"refund":0,"opName":"PUSH1"} +{"pc":2,"op":84,"gas":"0xa40fc","gasCost":"0x834","memSize":0,"stack":["0x2"],"depth":2,"refund":0,"opName":"SLOAD"} +{"pc":3,"op":80,"gas":"0xa38c8","gasCost":"0x2","memSize":0,"stack":["0x0"],"depth":2,"refund":0,"opName":"POP"} +{"pc":4,"op":96,"gas":"0xa38c6","gasCost":"0x3","memSize":0,"stack":[],"depth":2,"refund":0,"opName":"PUSH1"} +{"pc":6,"op":84,"gas":"0xa38c3","gasCost":"0x834","memSize":0,"stack":["0x3"],"depth":2,"refund":0,"opName":"SLOAD"} +{"pc":7,"op":80,"gas":"0xa308f","gasCost":"0x2","memSize":0,"stack":["0x0"],"depth":2,"refund":0,"opName":"POP"} +{"pc":8,"op":96,"gas":"0xa308d","gasCost":"0x3","memSize":0,"stack":[],"depth":2,"refund":0,"opName":"PUSH1"} +{"pc":10,"op":96,"gas":"0xa308a","gasCost":"0x3","memSize":0,"stack":["0x1"],"depth":2,"refund":0,"opName":"PUSH1"} +{"pc":12,"op":85,"gas":"0xa3087","gasCost":"0x4e20","memSize":0,"stack":["0x1","0x3"],"depth":2,"refund":0,"opName":"SSTORE"} +{"pc":13,"op":127,"gas":"0x9e267","gasCost":"0x3","memSize":0,"stack":[],"depth":2,"refund":0,"opName":"PUSH32"} +{"pc":46,"op":96,"gas":"0x9e264","gasCost":"0x3","memSize":0,"stack":["0x7f6008545060006004557f600160045560006004556000600060006000600060"],"depth":2,"refund":0,"opName":"PUSH1"} +{"pc":48,"op":82,"gas":"0x9e261","gasCost":"0x6","memSize":0,"stack":["0x7f6008545060006004557f600160045560006004556000600060006000600060","0x0"],"depth":2,"refund":0,"opName":"MSTORE"} +{"pc":49,"op":127,"gas":"0x9e25b","gasCost":"0x3","memSize":32,"stack":[],"depth":2,"refund":0,"opName":"PUSH32"} +{"pc":82,"op":96,"gas":"0x9e258","gasCost":"0x3","memSize":32,"stack":["0xf96000527f5af250600060006000606000527e60f45af4506000600060006000"],"depth":2,"refund":0,"opName":"PUSH1"} +{"pc":84,"op":82,"gas":"0x9e255","gasCost":"0x6","memSize":32,"stack":["0xf96000527f5af250600060006000606000527e60f45af4506000600060006000","0x20"],"depth":2,"refund":0,"opName":"MSTORE"} +{"pc":85,"op":127,"gas":"0x9e24f","gasCost":"0x3","memSize":64,"stack":[],"depth":2,"refund":0,"opName":"PUSH32"} +{"pc":118,"op":96,"gas":"0x9e24c","gasCost":"0x3","memSize":64,"stack":["0x600060f55a6020527ff150f001075205846a44a283446020527f8ca260006000"],"depth":2,"refund":0,"opName":"PUSH1"} +{"pc":120,"op":82,"gas":"0x9e249","gasCost":"0x6","memSize":64,"stack":["0x600060f55a6020527ff150f001075205846a44a283446020527f8ca260006000","0x40"],"depth":2,"refund":0,"opName":"MSTORE"} +{"pc":121,"op":127,"gas":"0x9e243","gasCost":"0x3","memSize":96,"stack":[],"depth":2,"refund":0,"opName":"PUSH32"} +{"pc":154,"op":96,"gas":"0x9e240","gasCost":"0x3","memSize":96,"stack":["0x6000600060045af4506040527f519930847f3b631c54a49b5f60035450326040"],"depth":2,"refund":0,"opName":"PUSH1"} +{"pc":156,"op":82,"gas":"0x9e23d","gasCost":"0x6","memSize":96,"stack":["0x6000600060045af4506040527f519930847f3b631c54a49b5f60035450326040","0x60"],"depth":2,"refund":0,"opName":"MSTORE"} +{"pc":157,"op":127,"gas":"0x9e237","gasCost":"0x3","memSize":128,"stack":[],"depth":2,"refund":0,"opName":"PUSH32"} +{"pc":190,"op":96,"gas":"0x9e234","gasCost":"0x3","memSize":128,"stack":["0x527f77306b60006000600060006060527f6000600c5af1506000600060006000"],"depth":2,"refund":0,"opName":"PUSH1"} +{"pc":192,"op":82,"gas":"0x9e231","gasCost":"0x6","memSize":128,"stack":["0x527f77306b60006000600060006060527f6000600c5af1506000600060006000","0x80"],"depth":2,"refund":0,"opName":"MSTORE"} +{"pc":193,"op":127,"gas":"0x9e22b","gasCost":"0x3","memSize":160,"stack":[],"depth":2,"refund":0,"opName":"PUSH32"} +{"pc":226,"op":96,"gas":"0x9e228","gasCost":"0x3","memSize":160,"stack":["0x60f85af450506060527f066001600255606080527f0354506000600055600060"],"depth":2,"refund":0,"opName":"PUSH1"} +{"pc":228,"op":82,"gas":"0x9e225","gasCost":"0x6","memSize":160,"stack":["0x60f85af450506060527f066001600255606080527f0354506000600055600060","0xa0"],"depth":2,"refund":0,"opName":"MSTORE"} +{"pc":229,"op":127,"gas":"0x9e21f","gasCost":"0x3","memSize":192,"stack":[],"depth":2,"refund":0,"opName":"PUSH32"} +{"pc":262,"op":96,"gas":"0x9e21c","gasCost":"0x3","memSize":192,"stack":["0x1556c3394fff4607f7f1684317b6080527f387b9f60a0527f1920700184809d"],"depth":2,"refund":0,"opName":"PUSH1"} +{"pc":264,"op":82,"gas":"0x9e219","gasCost":"0x6","memSize":192,"stack":["0x1556c3394fff4607f7f1684317b6080527f387b9f60a0527f1920700184809d","0xc0"],"depth":2,"refund":0,"opName":"MSTORE"} +{"pc":265,"op":127,"gas":"0x9e213","gasCost":"0x3","memSize":224,"stack":[],"depth":2,"refund":0,"opName":"PUSH32"} +{"pc":298,"op":96,"gas":"0x9e210","gasCost":"0x3","memSize":224,"stack":["0x60015450011899016e6009ff60026001556000527f9f60a05260c0527f7f6000"],"depth":2,"refund":0,"opName":"PUSH1"} +{"pc":300,"op":82,"gas":"0x9e20d","gasCost":"0x6","memSize":224,"stack":["0x60015450011899016e6009ff60026001556000527f9f60a05260c0527f7f6000","0xe0"],"depth":2,"refund":0,"opName":"MSTORE"} +{"pc":301,"op":127,"gas":"0x9e207","gasCost":"0x3","memSize":256,"stack":[],"depth":2,"refund":0,"opName":"PUSH32"} +{"pc":334,"op":97,"gas":"0x9e204","gasCost":"0x3","memSize":256,"stack":["0x527f9981600160045582600eff600060006000600060f65af45060006060e052"],"depth":2,"refund":0,"opName":"PUSH2"} +{"pc":337,"op":82,"gas":"0x9e201","gasCost":"0x6","memSize":256,"stack":["0x527f9981600160045582600eff600060006000600060f65af45060006060e052","0x100"],"depth":2,"refund":0,"opName":"MSTORE"} +{"pc":338,"op":127,"gas":"0x9e1fb","gasCost":"0x3","memSize":288,"stack":[],"depth":2,"refund":0,"opName":"PUSH32"} +{"pc":371,"op":97,"gas":"0x9e1f8","gasCost":"0x3","memSize":288,"stack":["0x7f6060c0527f20527e600060006020527f60f75af4501d7f1903166660006000"],"depth":2,"refund":0,"opName":"PUSH2"} +{"pc":374,"op":82,"gas":"0x9e1f5","gasCost":"0x6","memSize":288,"stack":["0x7f6060c0527f20527e600060006020527f60f75af4501d7f1903166660006000","0x120"],"depth":2,"refund":0,"opName":"MSTORE"} +{"pc":375,"op":127,"gas":"0x9e1ef","gasCost":"0x3","memSize":320,"stack":[],"depth":2,"refund":0,"opName":"PUSH32"} +{"pc":408,"op":97,"gas":"0x9e1ec","gasCost":"0x3","memSize":320,"stack":["0x60610100527e6000600060e0527f60046040527f5af150600060006000604052"],"depth":2,"refund":0,"opName":"PUSH2"} +{"pc":411,"op":82,"gas":"0x9e1e9","gasCost":"0x6","memSize":320,"stack":["0x60610100527e6000600060e0527f60046040527f5af150600060006000604052","0x140"],"depth":2,"refund":0,"opName":"MSTORE"} +{"pc":412,"op":127,"gas":"0x9e1e3","gasCost":"0x3","memSize":352,"stack":[],"depth":2,"refund":0,"opName":"PUSH32"} +{"pc":445,"op":97,"gas":"0x9e1e0","gasCost":"0x3","memSize":352,"stack":["0x7f60006009610120527f5af4503c95138e5b8f610100527f7f605a6000536060"],"depth":2,"refund":0,"opName":"PUSH2"} +{"pc":448,"op":82,"gas":"0x9e1dd","gasCost":"0x6","memSize":352,"stack":["0x7f60006009610120527f5af4503c95138e5b8f610100527f7f605a6000536060","0x160"],"depth":2,"refund":0,"opName":"MSTORE"} +{"pc":449,"op":127,"gas":"0x9e1d7","gasCost":"0x3","memSize":384,"stack":[],"depth":2,"refund":0,"opName":"PUSH32"} +{"pc":482,"op":97,"gas":"0x9e1d4","gasCost":"0x3","memSize":384,"stack":["0x527f6031600153606b60610140527f02536010606060527f0353604560610120"],"depth":2,"refund":0,"opName":"PUSH2"} +{"pc":485,"op":82,"gas":"0x9e1d1","gasCost":"0x6","memSize":384,"stack":["0x527f6031600153606b60610140527f02536010606060527f0353604560610120","0x180"],"depth":2,"refund":0,"opName":"MSTORE"} +{"pc":486,"op":127,"gas":"0x9e1cb","gasCost":"0x3","memSize":416,"stack":[],"depth":2,"refund":0,"opName":"PUSH32"} +{"pc":519,"op":97,"gas":"0x9e1c8","gasCost":"0x3","memSize":416,"stack":["0x527f04536060600553600160608052610160527f7e527f600653606060075360"],"depth":2,"refund":0,"opName":"PUSH2"} +{"pc":522,"op":82,"gas":"0x9e1c5","gasCost":"0x6","memSize":416,"stack":["0x527f04536060600553600160608052610160527f7e527f600653606060075360","0x1a0"],"depth":2,"refund":0,"opName":"MSTORE"} +{"pc":523,"op":127,"gas":"0x9e1bf","gasCost":"0x3","memSize":448,"stack":[],"depth":2,"refund":0,"opName":"PUSH32"} +{"pc":556,"op":97,"gas":"0x9e1bc","gasCost":"0x3","memSize":448,"stack":["0x2600853606080610140527f527f556009536060610180527f600a53600160a0"],"depth":2,"refund":0,"opName":"PUSH2"} +{"pc":559,"op":82,"gas":"0x9e1b9","gasCost":"0x6","memSize":448,"stack":["0x2600853606080610140527f527f556009536060610180527f600a53600160a0","0x1c0"],"depth":2,"refund":0,"opName":"MSTORE"} +{"pc":560,"op":127,"gas":"0x9e1b3","gasCost":"0x3","memSize":480,"stack":[],"depth":2,"refund":0,"opName":"PUSH32"} +{"pc":593,"op":97,"gas":"0x9e1b0","gasCost":"0x3","memSize":480,"stack":["0x527f600b536060600c6020527f53600060610160527f0d53606101a0527f5560"],"depth":2,"refund":0,"opName":"PUSH2"} +{"pc":596,"op":82,"gas":"0x9e1ad","gasCost":"0x6","memSize":480,"stack":["0x527f600b536060600c6020527f53600060610160527f0d53606101a0527f5560","0x1e0"],"depth":2,"refund":0,"opName":"MSTORE"} +{"pc":597,"op":127,"gas":"0x9e1a7","gasCost":"0x3","memSize":512,"stack":[],"depth":2,"refund":0,"opName":"PUSH32"} +{"pc":630,"op":97,"gas":"0x9e1a4","gasCost":"0x3","memSize":512,"stack":["0xe60a0527f536060600f536060c0527f01601053606060115360026101806101"],"depth":2,"refund":0,"opName":"PUSH2"} +{"pc":633,"op":82,"gas":"0x9e1a1","gasCost":"0x6","memSize":512,"stack":["0xe60a0527f536060600f536060c0527f01601053606060115360026101806101","0x200"],"depth":2,"refund":0,"opName":"MSTORE"} +{"pc":634,"op":127,"gas":"0x9e19b","gasCost":"0x3","memSize":544,"stack":[],"depth":2,"refund":0,"opName":"PUSH32"} +{"pc":667,"op":97,"gas":"0x9e198","gasCost":"0x3","memSize":544,"stack":["0xc0527f527f601253606040527f55601353606060c0527f60145360e0527f6000"],"depth":2,"refund":0,"opName":"PUSH2"} +{"pc":670,"op":82,"gas":"0x9e195","gasCost":"0x6","memSize":544,"stack":["0xc0527f527f601253606040527f55601353606060c0527f60145360e0527f6000","0x220"],"depth":2,"refund":0,"opName":"MSTORE"} +{"pc":671,"op":127,"gas":"0x9e18f","gasCost":"0x3","memSize":576,"stack":[],"depth":2,"refund":0,"opName":"PUSH32"} +{"pc":704,"op":97,"gas":"0x9e18c","gasCost":"0x3","memSize":576,"stack":["0x6015536101e0527f60606101a0527f601653600060175360f360185360196060"],"depth":2,"refund":0,"opName":"PUSH2"} +{"pc":707,"op":82,"gas":"0x9e189","gasCost":"0x6","memSize":576,"stack":["0x6015536101e0527f60606101a0527f601653600060175360f360185360196060","0x240"],"depth":2,"refund":0,"opName":"MSTORE"} +{"pc":708,"op":127,"gas":"0x9e183","gasCost":"0x3","memSize":608,"stack":[],"depth":2,"refund":0,"opName":"PUSH32"} +{"pc":741,"op":97,"gas":"0x9e180","gasCost":"0x3","memSize":608,"stack":["0x605260006060e052610200527f610100527f7f806101c0527f5360f360815360"],"depth":2,"refund":0,"opName":"PUSH2"} +{"pc":744,"op":82,"gas":"0x9e17d","gasCost":"0x6","memSize":608,"stack":["0x605260006060e052610200527f610100527f7f806101c0527f5360f360815360","0x260"],"depth":2,"refund":0,"opName":"MSTORE"} +{"pc":745,"op":127,"gas":"0x9e177","gasCost":"0x3","memSize":640,"stack":[],"depth":2,"refund":0,"opName":"PUSH32"} +{"pc":778,"op":97,"gas":"0x9e174","gasCost":"0x3","memSize":640,"stack":["0x8260006000f060006000600060610220527e845af450506000600061016101e0"],"depth":2,"refund":0,"opName":"PUSH2"} +{"pc":781,"op":82,"gas":"0x9e171","gasCost":"0x6","memSize":640,"stack":["0x8260006000f060006000600060610220527e845af450506000600061016101e0","0x280"],"depth":2,"refund":0,"opName":"MSTORE"} +{"pc":782,"op":127,"gas":"0x9e16b","gasCost":"0x3","memSize":672,"stack":[],"depth":2,"refund":0,"opName":"PUSH32"} +{"pc":815,"op":97,"gas":"0x9e168","gasCost":"0x3","memSize":672,"stack":["0x527f20527f60610100527e600060006003610240527f5af15060005450c76000"],"depth":2,"refund":0,"opName":"PUSH2"} +{"pc":818,"op":82,"gas":"0x9e165","gasCost":"0x6","memSize":672,"stack":["0x527f20527f60610100527e600060006003610240527f5af15060005450c76000","0x2a0"],"depth":2,"refund":0,"opName":"MSTORE"} +{"pc":819,"op":127,"gas":"0x9e15f","gasCost":"0x3","memSize":704,"stack":[],"depth":2,"refund":0,"opName":"PUSH32"} +{"pc":852,"op":97,"gas":"0x9e15c","gasCost":"0x3","memSize":704,"stack":["0x6002551309f562610200527f66a486610140527f6b00610260527f1d45716101"],"depth":2,"refund":0,"opName":"PUSH2"} +{"pc":855,"op":82,"gas":"0x9e159","gasCost":"0x7","memSize":704,"stack":["0x6002551309f562610200527f66a486610140527f6b00610260527f1d45716101","0x2c0"],"depth":2,"refund":0,"opName":"MSTORE"} +{"pc":856,"op":127,"gas":"0x9e152","gasCost":"0x3","memSize":736,"stack":[],"depth":2,"refund":0,"opName":"PUSH32"} +{"pc":889,"op":97,"gas":"0x9e14f","gasCost":"0x3","memSize":736,"stack":["0x20527f600054501c641d373c7f60045450610220527f6000600155610280527f"],"depth":2,"refund":0,"opName":"PUSH2"} +{"pc":892,"op":82,"gas":"0x9e14c","gasCost":"0x6","memSize":736,"stack":["0x20527f600054501c641d373c7f60045450610220527f6000600155610280527f","0x2e0"],"depth":2,"refund":0,"opName":"MSTORE"} +{"pc":893,"op":127,"gas":"0x9e146","gasCost":"0x3","memSize":768,"stack":[],"depth":2,"refund":0,"opName":"PUSH32"} +{"pc":926,"op":97,"gas":"0x9e143","gasCost":"0x3","memSize":768,"stack":["0x600554610160527f50600160025560085450610140527f60006002610240527f"],"depth":2,"refund":0,"opName":"PUSH2"} +{"pc":929,"op":82,"gas":"0x9e140","gasCost":"0x6","memSize":768,"stack":["0x600554610160527f50600160025560085450610140527f60006002610240527f","0x300"],"depth":2,"refund":0,"opName":"MSTORE"} +{"pc":930,"op":127,"gas":"0x9e13a","gasCost":"0x3","memSize":800,"stack":[],"depth":2,"refund":0,"opName":"PUSH32"} +{"pc":963,"op":97,"gas":"0x9e137","gasCost":"0x3","memSize":800,"stack":["0x6102a0527f557fd86000606000527e600060610180527e600060005af1508612"],"depth":2,"refund":0,"opName":"PUSH2"} +{"pc":966,"op":82,"gas":"0x9e134","gasCost":"0x6","memSize":800,"stack":["0x6102a0527f557fd86000606000527e600060610180527e600060005af1508612","0x320"],"depth":2,"refund":0,"opName":"MSTORE"} +{"pc":967,"op":127,"gas":"0x9e12e","gasCost":"0x3","memSize":832,"stack":[],"depth":2,"refund":0,"opName":"PUSH32"} +{"pc":1000,"op":97,"gas":"0x9e12b","gasCost":"0x3","memSize":832,"stack":["0x17145147356102c0527f610260527f610160527f5198a37e127a7efa7c600052"],"depth":2,"refund":0,"opName":"PUSH2"} +{"pc":1003,"op":82,"gas":"0x9e128","gasCost":"0x6","memSize":832,"stack":["0x17145147356102c0527f610260527f610160527f5198a37e127a7efa7c600052","0x340"],"depth":2,"refund":0,"opName":"MSTORE"} +{"pc":1004,"op":127,"gas":"0x9e122","gasCost":"0x3","memSize":864,"stack":[],"depth":2,"refund":0,"opName":"PUSH32"} +{"pc":1037,"op":97,"gas":"0x9e11f","gasCost":"0x3","memSize":864,"stack":["0x6101a0527f606020527f6102e0527f6060205360610280527ff760215360ff60"],"depth":2,"refund":0,"opName":"PUSH2"} +{"pc":1040,"op":82,"gas":"0x9e11c","gasCost":"0x6","memSize":864,"stack":["0x6101a0527f606020527f6102e0527f6060205360610280527ff760215360ff60","0x360"],"depth":2,"refund":0,"opName":"MSTORE"} +{"pc":1041,"op":127,"gas":"0x9e116","gasCost":"0x3","memSize":896,"stack":[],"depth":2,"refund":0,"opName":"PUSH32"} +{"pc":1074,"op":97,"gas":"0x9e113","gasCost":"0x3","memSize":896,"stack":["0x225360610180527fdb602353603760610300527f6101c0527f24536075606102"],"depth":2,"refund":0,"opName":"PUSH2"} +{"pc":1077,"op":82,"gas":"0x9e110","gasCost":"0x6","memSize":896,"stack":["0x225360610180527fdb602353603760610300527f6101c0527f24536075606102","0x380"],"depth":2,"refund":0,"opName":"MSTORE"} +{"pc":1078,"op":127,"gas":"0x9e10a","gasCost":"0x3","memSize":928,"stack":[],"depth":2,"refund":0,"opName":"PUSH32"} +{"pc":1111,"op":97,"gas":"0x9e107","gasCost":"0x3","memSize":928,"stack":["0xa0527f2553609f606040527f265360fe60275360610320527f8f60286101a052"],"depth":2,"refund":0,"opName":"PUSH2"} +{"pc":1114,"op":82,"gas":"0x9e104","gasCost":"0x6","memSize":928,"stack":["0xa0527f2553609f606040527f265360fe60275360610320527f8f60286101a052","0x3a0"],"depth":2,"refund":0,"opName":"MSTORE"} +{"pc":1115,"op":127,"gas":"0x9e0fe","gasCost":"0x3","memSize":960,"stack":[],"depth":2,"refund":0,"opName":"PUSH32"} +{"pc":1148,"op":97,"gas":"0x9e0fb","gasCost":"0x3","memSize":960,"stack":["0x7f53606101e0527f6102c0527f0b6029536060602a53600060610340527f2b53"],"depth":2,"refund":0,"opName":"PUSH2"} +{"pc":1151,"op":82,"gas":"0x9e0f8","gasCost":"0x6","memSize":960,"stack":["0x7f53606101e0527f6102c0527f0b6029536060602a53600060610340527f2b53","0x3c0"],"depth":2,"refund":0,"opName":"MSTORE"} +{"pc":1152,"op":127,"gas":"0x9e0f2","gasCost":"0x3","memSize":992,"stack":[],"depth":2,"refund":0,"opName":"PUSH32"} +{"pc":1185,"op":97,"gas":"0x9e0ef","gasCost":"0x3","memSize":992,"stack":["0x6060602c53606052606060805360006061016102e0527f610200527fc0526103"],"depth":2,"refund":0,"opName":"PUSH2"} +{"pc":1188,"op":82,"gas":"0x9e0ec","gasCost":"0x7","memSize":992,"stack":["0x6060602c53606052606060805360006061016102e0527f610200527fc0526103","0x3e0"],"depth":2,"refund":0,"opName":"MSTORE"} +{"pc":1189,"op":127,"gas":"0x9e0e5","gasCost":"0x3","memSize":1024,"stack":[],"depth":2,"refund":0,"opName":"PUSH32"} +{"pc":1222,"op":97,"gas":"0x9e0e2","gasCost":"0x3","memSize":1024,"stack":["0x60527f7f81536060608253602d6083536053608453606060855360fd61030052"],"depth":2,"refund":0,"opName":"PUSH2"} +{"pc":1225,"op":82,"gas":"0x9e0df","gasCost":"0x6","memSize":1024,"stack":["0x60527f7f81536060608253602d6083536053608453606060855360fd61030052","0x400"],"depth":2,"refund":0,"opName":"MSTORE"} +{"pc":1226,"op":127,"gas":"0x9e0d9","gasCost":"0x3","memSize":1056,"stack":[],"depth":2,"refund":0,"opName":"PUSH32"} +{"pc":1259,"op":97,"gas":"0x9e0d6","gasCost":"0x3","memSize":1056,"stack":["0x7f6086610380527f536060610220527f6087536101e0527f602e608853605360"],"depth":2,"refund":0,"opName":"PUSH2"} +{"pc":1262,"op":82,"gas":"0x9e0d3","gasCost":"0x6","memSize":1056,"stack":["0x7f6086610380527f536060610220527f6087536101e0527f602e608853605360","0x420"],"depth":2,"refund":0,"opName":"MSTORE"} +{"pc":1263,"op":127,"gas":"0x9e0cd","gasCost":"0x3","memSize":1088,"stack":[],"depth":2,"refund":0,"opName":"PUSH32"} +{"pc":1296,"op":97,"gas":"0x9e0ca","gasCost":"0x3","memSize":1088,"stack":["0x89536060608a61036103a0527f20527f53602f608b536060608c610240527f53"],"depth":2,"refund":0,"opName":"PUSH2"} +{"pc":1299,"op":82,"gas":"0x9e0c7","gasCost":"0x6","memSize":1088,"stack":["0x89536060608a61036103a0527f20527f53602f608b536060608c610240527f53","0x440"],"depth":2,"refund":0,"opName":"MSTORE"} +{"pc":1300,"op":127,"gas":"0x9e0c1","gasCost":"0x3","memSize":1120,"stack":[],"depth":2,"refund":0,"opName":"PUSH32"} +{"pc":1333,"op":97,"gas":"0x9e0be","gasCost":"0x3","memSize":1120,"stack":["0x6000608d5360f36102005260606103c0527f610220610340527f53608e610221"],"depth":2,"refund":0,"opName":"PUSH2"} +{"pc":1336,"op":82,"gas":"0x9e0bb","gasCost":"0x6","memSize":1120,"stack":["0x6000608d5360f36102005260606103c0527f610220610340527f53608e610221","0x460"],"depth":2,"refund":0,"opName":"MSTORE"} +{"pc":1337,"op":127,"gas":"0x9e0b5","gasCost":"0x3","memSize":1152,"stack":[],"depth":2,"refund":0,"opName":"PUSH32"} +{"pc":1370,"op":97,"gas":"0x9e0b2","gasCost":"0x3","memSize":1152,"stack":["0x536053610222536060610260527f610223536103e0527f600061022453606061"],"depth":2,"refund":0,"opName":"PUSH2"} +{"pc":1373,"op":82,"gas":"0x9e0af","gasCost":"0x6","memSize":1152,"stack":["0x536053610222536060610260527f610223536103e0527f600061022453606061","0x480"],"depth":2,"refund":0,"opName":"MSTORE"} +{"pc":1374,"op":127,"gas":"0x9e0a9","gasCost":"0x3","memSize":1184,"stack":[],"depth":2,"refund":0,"opName":"PUSH32"} +{"pc":1407,"op":97,"gas":"0x9e0a6","gasCost":"0x3","memSize":1184,"stack":["0x360527f61022553608f61022653606061022753600061610400527f02610280"],"depth":2,"refund":0,"opName":"PUSH2"} +{"pc":1410,"op":82,"gas":"0x9e0a3","gasCost":"0x6","memSize":1184,"stack":["0x360527f61022553608f61022653606061022753600061610400527f02610280","0x4a0"],"depth":2,"refund":0,"opName":"MSTORE"} +{"pc":1411,"op":127,"gas":"0x9e09d","gasCost":"0x3","memSize":1216,"stack":[],"depth":2,"refund":0,"opName":"PUSH32"} +{"pc":1444,"op":97,"gas":"0x9e09a","gasCost":"0x3","memSize":1216,"stack":["0x527f28536060610229610380527f53600061022a5360f561022b536061042052"],"depth":2,"refund":0,"opName":"PUSH2"} +{"pc":1447,"op":82,"gas":"0x9e097","gasCost":"0x6","memSize":1216,"stack":["0x527f28536060610229610380527f53600061022a5360f561022b536061042052","0x4c0"],"depth":2,"refund":0,"opName":"MSTORE"} +{"pc":1448,"op":127,"gas":"0x9e091","gasCost":"0x3","memSize":1248,"stack":[],"depth":2,"refund":0,"opName":"PUSH32"} +{"pc":1481,"op":97,"gas":"0x9e08e","gasCost":"0x3","memSize":1248,"stack":["0x7f6061022c53600061022d536102a0527f60606103a0527f61022e5360006102"],"depth":2,"refund":0,"opName":"PUSH2"} +{"pc":1484,"op":82,"gas":"0x9e08b","gasCost":"0x7","memSize":1248,"stack":["0x7f6061022c53600061022d536102a0527f60606103a0527f61022e5360006102","0x4e0"],"depth":2,"refund":0,"opName":"MSTORE"} +{"pc":1485,"op":127,"gas":"0x9e084","gasCost":"0x3","memSize":1280,"stack":[],"depth":2,"refund":0,"opName":"PUSH32"} +{"pc":1518,"op":97,"gas":"0x9e081","gasCost":"0x3","memSize":1280,"stack":["0x2f610440527f53606061023053600061023153606061023253600061026103c0"],"depth":2,"refund":0,"opName":"PUSH2"} +{"pc":1521,"op":82,"gas":"0x9e07e","gasCost":"0x6","memSize":1280,"stack":["0x2f610440527f53606061023053600061023153606061023253600061026103c0","0x500"],"depth":2,"refund":0,"opName":"MSTORE"} +{"pc":1522,"op":127,"gas":"0x9e078","gasCost":"0x3","memSize":1312,"stack":[],"depth":2,"refund":0,"opName":"PUSH32"} +{"pc":1555,"op":97,"gas":"0x9e075","gasCost":"0x3","memSize":1312,"stack":["0x527fc0527f61610460527f023353606061023453600061023553608561023653"],"depth":2,"refund":0,"opName":"PUSH2"} +{"pc":1558,"op":82,"gas":"0x9e072","gasCost":"0x6","memSize":1312,"stack":["0x527fc0527f61610460527f023353606061023453600061023553608561023653","0x520"],"depth":2,"refund":0,"opName":"MSTORE"} +{"pc":1559,"op":127,"gas":"0x9e06c","gasCost":"0x3","memSize":1344,"stack":[],"depth":2,"refund":0,"opName":"PUSH32"} +{"pc":1592,"op":97,"gas":"0x9e069","gasCost":"0x3","memSize":1344,"stack":["0x605a61023753606103e052610480527f7ff261026102e0526038610300536053"],"depth":2,"refund":0,"opName":"PUSH2"} +{"pc":1595,"op":82,"gas":"0x9e066","gasCost":"0x6","memSize":1344,"stack":["0x605a61023753606103e052610480527f7ff261026102e0526038610300536053","0x540"],"depth":2,"refund":0,"opName":"MSTORE"} +{"pc":1596,"op":127,"gas":"0x9e060","gasCost":"0x3","memSize":1376,"stack":[],"depth":2,"refund":0,"opName":"PUSH32"} +{"pc":1629,"op":97,"gas":"0x9e05d","gasCost":"0x3","memSize":1376,"stack":["0x610301536060610302536050610303536104a0527f60610400527f6161030453"],"depth":2,"refund":0,"opName":"PUSH2"} +{"pc":1632,"op":82,"gas":"0x9e05a","gasCost":"0x6","memSize":1376,"stack":["0x610301536060610302536050610303536104a0527f60610400527f6161030453","0x560"],"depth":2,"refund":0,"opName":"MSTORE"} +{"pc":1633,"op":127,"gas":"0x9e054","gasCost":"0x3","memSize":1408,"stack":[],"depth":2,"refund":0,"opName":"PUSH32"} +{"pc":1666,"op":97,"gas":"0x9e051","gasCost":"0x3","memSize":1408,"stack":["0x6002610305536039610306536053610307536060616104c0527f030853605061"],"depth":2,"refund":0,"opName":"PUSH2"} +{"pc":1669,"op":82,"gas":"0x9e04e","gasCost":"0x6","memSize":1408,"stack":["0x6002610305536039610306536053610307536060616104c0527f030853605061","0x580"],"depth":2,"refund":0,"opName":"MSTORE"} +{"pc":1670,"op":127,"gas":"0x9e048","gasCost":"0x3","memSize":1440,"stack":[],"depth":2,"refund":0,"opName":"PUSH32"} +{"pc":1703,"op":97,"gas":"0x9e045","gasCost":"0x3","memSize":1440,"stack":["0x610420527f030953606161030a53600261030b53603a61030c536104e0527f60"],"depth":2,"refund":0,"opName":"PUSH2"} +{"pc":1706,"op":82,"gas":"0x9e042","gasCost":"0x7","memSize":1440,"stack":["0x610420527f030953606161030a53600261030b53603a61030c536104e0527f60","0x5a0"],"depth":2,"refund":0,"opName":"MSTORE"} +{"pc":1707,"op":127,"gas":"0x9e03b","gasCost":"0x3","memSize":1472,"stack":[],"depth":2,"refund":0,"opName":"PUSH32"} +{"pc":1740,"op":97,"gas":"0x9e038","gasCost":"0x3","memSize":1472,"stack":["0x5361030d53606161030e610440527f53600261030f53603b6103105360606161"],"depth":2,"refund":0,"opName":"PUSH2"} +{"pc":1743,"op":82,"gas":"0x9e035","gasCost":"0x6","memSize":1472,"stack":["0x5361030d53606161030e610440527f53600261030f53603b6103105360606161","0x5c0"],"depth":2,"refund":0,"opName":"MSTORE"} +{"pc":1744,"op":127,"gas":"0x9e02f","gasCost":"0x3","memSize":1504,"stack":[],"depth":2,"refund":0,"opName":"PUSH32"} +{"pc":1777,"op":97,"gas":"0x9e02c","gasCost":"0x3","memSize":1504,"stack":["0x500527f03115360006103125360f36103135361610460526003610480536014"],"depth":2,"refund":0,"opName":"PUSH2"} +{"pc":1780,"op":82,"gas":"0x9e029","gasCost":"0x6","memSize":1504,"stack":["0x500527f03115360006103125360f36103135361610460526003610480536014","0x5e0"],"depth":2,"refund":0,"opName":"MSTORE"} +{"pc":1781,"op":127,"gas":"0x9e023","gasCost":"0x3","memSize":1536,"stack":[],"depth":2,"refund":0,"opName":"PUSH32"} +{"pc":1814,"op":97,"gas":"0x9e020","gasCost":"0x3","memSize":1536,"stack":["0x61048153610520527f6060610482536000610483536060610484536000610485"],"depth":2,"refund":0,"opName":"PUSH2"} +{"pc":1817,"op":82,"gas":"0x9e01d","gasCost":"0x6","memSize":1536,"stack":["0x61048153610520527f6060610482536000610483536060610484536000610485","0x600"],"depth":2,"refund":0,"opName":"MSTORE"} +{"pc":1818,"op":127,"gas":"0x9e017","gasCost":"0x3","memSize":1568,"stack":[],"depth":2,"refund":0,"opName":"PUSH32"} +{"pc":1851,"op":97,"gas":"0x9e014","gasCost":"0x3","memSize":1568,"stack":["0x5360f0610486536060610540527f610487536000610488536060610489536000"],"depth":2,"refund":0,"opName":"PUSH2"} +{"pc":1854,"op":82,"gas":"0x9e011","gasCost":"0x6","memSize":1568,"stack":["0x5360f0610486536060610540527f610487536000610488536060610489536000","0x620"],"depth":2,"refund":0,"opName":"MSTORE"} +{"pc":1855,"op":127,"gas":"0x9e00b","gasCost":"0x3","memSize":1600,"stack":[],"depth":2,"refund":0,"opName":"PUSH32"} +{"pc":1888,"op":97,"gas":"0x9e008","gasCost":"0x3","memSize":1600,"stack":["0x61048a53606061048b5360006104610560527f8c53606061048d53600061048e"],"depth":2,"refund":0,"opName":"PUSH2"} +{"pc":1891,"op":82,"gas":"0x9e005","gasCost":"0x7","memSize":1600,"stack":["0x61048a53606061048b5360006104610560527f8c53606061048d53600061048e","0x640"],"depth":2,"refund":0,"opName":"MSTORE"} +{"pc":1892,"op":127,"gas":"0x9dffe","gasCost":"0x3","memSize":1632,"stack":[],"depth":2,"refund":0,"opName":"PUSH32"} +{"pc":1925,"op":97,"gas":"0x9dffb","gasCost":"0x3","memSize":1632,"stack":["0x53608461048f53605a6104905360f4610491536105805260606105a053605061"],"depth":2,"refund":0,"opName":"PUSH2"} +{"pc":1928,"op":82,"gas":"0x9dff8","gasCost":"0x6","memSize":1632,"stack":["0x53608461048f53605a6104905360f4610491536105805260606105a053605061","0x660"],"depth":2,"refund":0,"opName":"MSTORE"} +{"pc":1929,"op":127,"gas":"0x9dff2","gasCost":"0x3","memSize":1664,"stack":[],"depth":2,"refund":0,"opName":"PUSH32"} +{"pc":1962,"op":97,"gas":"0x9dfef","gasCost":"0x3","memSize":1664,"stack":["0x5a15360616105a25360046105a35360926105a45360536105a55360606105a6"],"depth":2,"refund":0,"opName":"PUSH2"} +{"pc":1965,"op":82,"gas":"0x9dfec","gasCost":"0x6","memSize":1664,"stack":["0x5a15360616105a25360046105a35360926105a45360536105a55360606105a6","0x680"],"depth":2,"refund":0,"opName":"MSTORE"} +{"pc":1966,"op":127,"gas":"0x9dfe6","gasCost":"0x3","memSize":1696,"stack":[],"depth":2,"refund":0,"opName":"PUSH32"} +{"pc":1999,"op":97,"gas":"0x9dfe3","gasCost":"0x3","memSize":1696,"stack":["0x5360506105a75360616105a85360046105a95360936105aa5360536105ab5360"],"depth":2,"refund":0,"opName":"PUSH2"} +{"pc":2002,"op":82,"gas":"0x9dfe0","gasCost":"0x6","memSize":1696,"stack":["0x5360506105a75360616105a85360046105a95360936105aa5360536105ab5360","0x6a0"],"depth":2,"refund":0,"opName":"MSTORE"} +{"pc":2003,"op":127,"gas":"0x9dfda","gasCost":"0x3","memSize":1728,"stack":[],"depth":2,"refund":0,"opName":"PUSH32"} +{"pc":2036,"op":97,"gas":"0x9dfd7","gasCost":"0x3","memSize":1728,"stack":["0x616105ac5360046105ad5360946105ae5360606105af5360006105b05360f361"],"depth":2,"refund":0,"opName":"PUSH2"} +{"pc":2039,"op":82,"gas":"0x9dfd4","gasCost":"0x6","memSize":1728,"stack":["0x616105ac5360046105ad5360946105ae5360606105af5360006105b05360f361","0x6c0"],"depth":2,"refund":0,"opName":"MSTORE"} +{"pc":2040,"op":96,"gas":"0x9dfce","gasCost":"0x3","memSize":1760,"stack":[],"depth":2,"refund":0,"opName":"PUSH1"} +{"pc":2042,"op":97,"gas":"0x9dfcb","gasCost":"0x3","memSize":1760,"stack":["0x5"],"depth":2,"refund":0,"opName":"PUSH2"} +{"pc":2045,"op":83,"gas":"0x9dfc8","gasCost":"0x7","memSize":1760,"stack":["0x5","0x6e0"],"depth":2,"refund":0,"opName":"MSTORE8"} +{"pc":2046,"op":96,"gas":"0x9dfc1","gasCost":"0x3","memSize":1792,"stack":[],"depth":2,"refund":0,"opName":"PUSH1"} +{"pc":2048,"op":97,"gas":"0x9dfbe","gasCost":"0x3","memSize":1792,"stack":["0xb1"],"depth":2,"refund":0,"opName":"PUSH2"} +{"pc":2051,"op":83,"gas":"0x9dfbb","gasCost":"0x3","memSize":1792,"stack":["0xb1","0x6e1"],"depth":2,"refund":0,"opName":"MSTORE8"} +{"pc":2052,"op":96,"gas":"0x9dfb8","gasCost":"0x3","memSize":1792,"stack":[],"depth":2,"refund":0,"opName":"PUSH1"} +{"pc":2054,"op":97,"gas":"0x9dfb5","gasCost":"0x3","memSize":1792,"stack":["0x53"],"depth":2,"refund":0,"opName":"PUSH2"} +{"pc":2057,"op":83,"gas":"0x9dfb2","gasCost":"0x3","memSize":1792,"stack":["0x53","0x6e2"],"depth":2,"refund":0,"opName":"MSTORE8"} +{"pc":2058,"op":96,"gas":"0x9dfaf","gasCost":"0x3","memSize":1792,"stack":[],"depth":2,"refund":0,"opName":"PUSH1"} +{"pc":2060,"op":97,"gas":"0x9dfac","gasCost":"0x3","memSize":1792,"stack":["0x60"],"depth":2,"refund":0,"opName":"PUSH2"} +{"pc":2063,"op":83,"gas":"0x9dfa9","gasCost":"0x3","memSize":1792,"stack":["0x60","0x6e3"],"depth":2,"refund":0,"opName":"MSTORE8"} +{"pc":2064,"op":96,"gas":"0x9dfa6","gasCost":"0x3","memSize":1792,"stack":[],"depth":2,"refund":0,"opName":"PUSH1"} +{"pc":2066,"op":97,"gas":"0x9dfa3","gasCost":"0x3","memSize":1792,"stack":["0x0"],"depth":2,"refund":0,"opName":"PUSH2"} +{"pc":2069,"op":83,"gas":"0x9dfa0","gasCost":"0x3","memSize":1792,"stack":["0x0","0x6e4"],"depth":2,"refund":0,"opName":"MSTORE8"} +{"pc":2070,"op":96,"gas":"0x9df9d","gasCost":"0x3","memSize":1792,"stack":[],"depth":2,"refund":0,"opName":"PUSH1"} +{"pc":2072,"op":97,"gas":"0x9df9a","gasCost":"0x3","memSize":1792,"stack":["0x61"],"depth":2,"refund":0,"opName":"PUSH2"} +{"pc":2075,"op":83,"gas":"0x9df97","gasCost":"0x3","memSize":1792,"stack":["0x61","0x6e5"],"depth":2,"refund":0,"opName":"MSTORE8"} +{"pc":2076,"op":96,"gas":"0x9df94","gasCost":"0x3","memSize":1792,"stack":[],"depth":2,"refund":0,"opName":"PUSH1"} +{"pc":2078,"op":97,"gas":"0x9df91","gasCost":"0x3","memSize":1792,"stack":["0x5"],"depth":2,"refund":0,"opName":"PUSH2"} +{"pc":2081,"op":83,"gas":"0x9df8e","gasCost":"0x3","memSize":1792,"stack":["0x5","0x6e6"],"depth":2,"refund":0,"opName":"MSTORE8"} +{"pc":2082,"op":96,"gas":"0x9df8b","gasCost":"0x3","memSize":1792,"stack":[],"depth":2,"refund":0,"opName":"PUSH1"} +{"pc":2084,"op":97,"gas":"0x9df88","gasCost":"0x3","memSize":1792,"stack":["0xb2"],"depth":2,"refund":0,"opName":"PUSH2"} +{"pc":2087,"op":83,"gas":"0x9df85","gasCost":"0x3","memSize":1792,"stack":["0xb2","0x6e7"],"depth":2,"refund":0,"opName":"MSTORE8"} +{"pc":2088,"op":96,"gas":"0x9df82","gasCost":"0x3","memSize":1792,"stack":[],"depth":2,"refund":0,"opName":"PUSH1"} +{"pc":2090,"op":97,"gas":"0x9df7f","gasCost":"0x3","memSize":1792,"stack":["0x60"],"depth":2,"refund":0,"opName":"PUSH2"} +{"pc":2093,"op":83,"gas":"0x9df7c","gasCost":"0x3","memSize":1792,"stack":["0x60","0x6e8"],"depth":2,"refund":0,"opName":"MSTORE8"} +{"pc":2094,"op":96,"gas":"0x9df79","gasCost":"0x3","memSize":1792,"stack":[],"depth":2,"refund":0,"opName":"PUSH1"} +{"pc":2096,"op":97,"gas":"0x9df76","gasCost":"0x3","memSize":1792,"stack":["0x0"],"depth":2,"refund":0,"opName":"PUSH2"} +{"pc":2099,"op":83,"gas":"0x9df73","gasCost":"0x3","memSize":1792,"stack":["0x0","0x6e9"],"depth":2,"refund":0,"opName":"MSTORE8"} +{"pc":2100,"op":96,"gas":"0x9df70","gasCost":"0x3","memSize":1792,"stack":[],"depth":2,"refund":0,"opName":"PUSH1"} +{"pc":2102,"op":97,"gas":"0x9df6d","gasCost":"0x3","memSize":1792,"stack":["0x60"],"depth":2,"refund":0,"opName":"PUSH2"} +{"pc":2105,"op":83,"gas":"0x9df6a","gasCost":"0x3","memSize":1792,"stack":["0x60","0x6ea"],"depth":2,"refund":0,"opName":"MSTORE8"} +{"pc":2106,"op":96,"gas":"0x9df67","gasCost":"0x3","memSize":1792,"stack":[],"depth":2,"refund":0,"opName":"PUSH1"} +{"pc":2108,"op":97,"gas":"0x9df64","gasCost":"0x3","memSize":1792,"stack":["0x0"],"depth":2,"refund":0,"opName":"PUSH2"} +{"pc":2111,"op":83,"gas":"0x9df61","gasCost":"0x3","memSize":1792,"stack":["0x0","0x6eb"],"depth":2,"refund":0,"opName":"MSTORE8"} +{"pc":2112,"op":96,"gas":"0x9df5e","gasCost":"0x3","memSize":1792,"stack":[],"depth":2,"refund":0,"opName":"PUSH1"} +{"pc":2114,"op":97,"gas":"0x9df5b","gasCost":"0x3","memSize":1792,"stack":["0xf5"],"depth":2,"refund":0,"opName":"PUSH2"} +{"pc":2117,"op":83,"gas":"0x9df58","gasCost":"0x3","memSize":1792,"stack":["0xf5","0x6ec"],"depth":2,"refund":0,"opName":"MSTORE8"} +{"pc":2118,"op":96,"gas":"0x9df55","gasCost":"0x3","memSize":1792,"stack":[],"depth":2,"refund":0,"opName":"PUSH1"} +{"pc":2120,"op":97,"gas":"0x9df52","gasCost":"0x3","memSize":1792,"stack":["0x60"],"depth":2,"refund":0,"opName":"PUSH2"} +{"pc":2123,"op":83,"gas":"0x9df4f","gasCost":"0x3","memSize":1792,"stack":["0x60","0x6ed"],"depth":2,"refund":0,"opName":"MSTORE8"} +{"pc":2124,"op":96,"gas":"0x9df4c","gasCost":"0x3","memSize":1792,"stack":[],"depth":2,"refund":0,"opName":"PUSH1"} +{"pc":2126,"op":97,"gas":"0x9df49","gasCost":"0x3","memSize":1792,"stack":["0x0"],"depth":2,"refund":0,"opName":"PUSH2"} +{"pc":2129,"op":83,"gas":"0x9df46","gasCost":"0x3","memSize":1792,"stack":["0x0","0x6ee"],"depth":2,"refund":0,"opName":"MSTORE8"} +{"pc":2130,"op":96,"gas":"0x9df43","gasCost":"0x3","memSize":1792,"stack":[],"depth":2,"refund":0,"opName":"PUSH1"} +{"pc":2132,"op":97,"gas":"0x9df40","gasCost":"0x3","memSize":1792,"stack":["0x60"],"depth":2,"refund":0,"opName":"PUSH2"} +{"pc":2135,"op":83,"gas":"0x9df3d","gasCost":"0x3","memSize":1792,"stack":["0x60","0x6ef"],"depth":2,"refund":0,"opName":"MSTORE8"} +{"pc":2136,"op":96,"gas":"0x9df3a","gasCost":"0x3","memSize":1792,"stack":[],"depth":2,"refund":0,"opName":"PUSH1"} +{"pc":2138,"op":97,"gas":"0x9df37","gasCost":"0x3","memSize":1792,"stack":["0x0"],"depth":2,"refund":0,"opName":"PUSH2"} +{"pc":2141,"op":83,"gas":"0x9df34","gasCost":"0x3","memSize":1792,"stack":["0x0","0x6f0"],"depth":2,"refund":0,"opName":"MSTORE8"} +{"pc":2142,"op":96,"gas":"0x9df31","gasCost":"0x3","memSize":1792,"stack":[],"depth":2,"refund":0,"opName":"PUSH1"} +{"pc":2144,"op":97,"gas":"0x9df2e","gasCost":"0x3","memSize":1792,"stack":["0x60"],"depth":2,"refund":0,"opName":"PUSH2"} +{"pc":2147,"op":83,"gas":"0x9df2b","gasCost":"0x3","memSize":1792,"stack":["0x60","0x6f1"],"depth":2,"refund":0,"opName":"MSTORE8"} +{"pc":2148,"op":96,"gas":"0x9df28","gasCost":"0x3","memSize":1792,"stack":[],"depth":2,"refund":0,"opName":"PUSH1"} +{"pc":2150,"op":97,"gas":"0x9df25","gasCost":"0x3","memSize":1792,"stack":["0x0"],"depth":2,"refund":0,"opName":"PUSH2"} +{"pc":2153,"op":83,"gas":"0x9df22","gasCost":"0x3","memSize":1792,"stack":["0x0","0x6f2"],"depth":2,"refund":0,"opName":"MSTORE8"} +{"pc":2154,"op":96,"gas":"0x9df1f","gasCost":"0x3","memSize":1792,"stack":[],"depth":2,"refund":0,"opName":"PUSH1"} +{"pc":2156,"op":97,"gas":"0x9df1c","gasCost":"0x3","memSize":1792,"stack":["0x60"],"depth":2,"refund":0,"opName":"PUSH2"} +{"pc":2159,"op":83,"gas":"0x9df19","gasCost":"0x3","memSize":1792,"stack":["0x60","0x6f3"],"depth":2,"refund":0,"opName":"MSTORE8"} +{"pc":2160,"op":96,"gas":"0x9df16","gasCost":"0x3","memSize":1792,"stack":[],"depth":2,"refund":0,"opName":"PUSH1"} +{"pc":2162,"op":97,"gas":"0x9df13","gasCost":"0x3","memSize":1792,"stack":["0x0"],"depth":2,"refund":0,"opName":"PUSH2"} +{"pc":2165,"op":83,"gas":"0x9df10","gasCost":"0x3","memSize":1792,"stack":["0x0","0x6f4"],"depth":2,"refund":0,"opName":"MSTORE8"} +{"pc":2166,"op":96,"gas":"0x9df0d","gasCost":"0x3","memSize":1792,"stack":[],"depth":2,"refund":0,"opName":"PUSH1"} +{"pc":2168,"op":97,"gas":"0x9df0a","gasCost":"0x3","memSize":1792,"stack":["0x60"],"depth":2,"refund":0,"opName":"PUSH2"} +{"pc":2171,"op":83,"gas":"0x9df07","gasCost":"0x3","memSize":1792,"stack":["0x60","0x6f5"],"depth":2,"refund":0,"opName":"MSTORE8"} +{"pc":2172,"op":96,"gas":"0x9df04","gasCost":"0x3","memSize":1792,"stack":[],"depth":2,"refund":0,"opName":"PUSH1"} +{"pc":2174,"op":97,"gas":"0x9df01","gasCost":"0x3","memSize":1792,"stack":["0x0"],"depth":2,"refund":0,"opName":"PUSH2"} +{"pc":2177,"op":83,"gas":"0x9defe","gasCost":"0x3","memSize":1792,"stack":["0x0","0x6f6"],"depth":2,"refund":0,"opName":"MSTORE8"} +{"pc":2178,"op":96,"gas":"0x9defb","gasCost":"0x3","memSize":1792,"stack":[],"depth":2,"refund":0,"opName":"PUSH1"} +{"pc":2180,"op":97,"gas":"0x9def8","gasCost":"0x3","memSize":1792,"stack":["0x85"],"depth":2,"refund":0,"opName":"PUSH2"} +{"pc":2183,"op":83,"gas":"0x9def5","gasCost":"0x3","memSize":1792,"stack":["0x85","0x6f7"],"depth":2,"refund":0,"opName":"MSTORE8"} +{"pc":2184,"op":96,"gas":"0x9def2","gasCost":"0x3","memSize":1792,"stack":[],"depth":2,"refund":0,"opName":"PUSH1"} +{"pc":2186,"op":97,"gas":"0x9deef","gasCost":"0x3","memSize":1792,"stack":["0x5a"],"depth":2,"refund":0,"opName":"PUSH2"} +{"pc":2189,"op":83,"gas":"0x9deec","gasCost":"0x3","memSize":1792,"stack":["0x5a","0x6f8"],"depth":2,"refund":0,"opName":"MSTORE8"} +{"pc":2190,"op":96,"gas":"0x9dee9","gasCost":"0x3","memSize":1792,"stack":[],"depth":2,"refund":0,"opName":"PUSH1"} +{"pc":2192,"op":97,"gas":"0x9dee6","gasCost":"0x3","memSize":1792,"stack":["0xf1"],"depth":2,"refund":0,"opName":"PUSH2"} +{"pc":2195,"op":83,"gas":"0x9dee3","gasCost":"0x3","memSize":1792,"stack":["0xf1","0x6f9"],"depth":2,"refund":0,"opName":"MSTORE8"} +{"pc":2196,"op":96,"gas":"0x9dee0","gasCost":"0x3","memSize":1792,"stack":[],"depth":2,"refund":0,"opName":"PUSH1"} +{"pc":2198,"op":97,"gas":"0x9dedd","gasCost":"0x3","memSize":1792,"stack":["0x50"],"depth":2,"refund":0,"opName":"PUSH2"} +{"pc":2201,"op":83,"gas":"0x9deda","gasCost":"0x3","memSize":1792,"stack":["0x50","0x6fa"],"depth":2,"refund":0,"opName":"MSTORE8"} +{"pc":2202,"op":96,"gas":"0x9ded7","gasCost":"0x3","memSize":1792,"stack":[],"depth":2,"refund":0,"opName":"PUSH1"} +{"pc":2204,"op":97,"gas":"0x9ded4","gasCost":"0x3","memSize":1792,"stack":["0x50"],"depth":2,"refund":0,"opName":"PUSH2"} +{"pc":2207,"op":83,"gas":"0x9ded1","gasCost":"0x3","memSize":1792,"stack":["0x50","0x6fb"],"depth":2,"refund":0,"opName":"MSTORE8"} +{"pc":2208,"op":97,"gas":"0x9dece","gasCost":"0x3","memSize":1792,"stack":[],"depth":2,"refund":0,"opName":"PUSH2"} +{"pc":2211,"op":96,"gas":"0x9decb","gasCost":"0x3","memSize":1792,"stack":["0x6fc"],"depth":2,"refund":0,"opName":"PUSH1"} +{"pc":2213,"op":243,"gas":"0x9dec8","gasCost":"0x0","memSize":1792,"stack":["0x6fc","0x0"],"depth":2,"refund":0,"opName":"RETURN"} +{"output":"7f6008545060006004557f600160045560006004556000600060006000600060f96000527f5af250600060006000606000527e60f45af4506000600060006000600060f55a6020527ff150f001075205846a44a283446020527f8ca2600060006000600060045af4506040527f519930847f3b631c54a49b5f60035450326040527f77306b60006000600060006060527f6000600c5af150600060006000600060f85af450506060527f066001600255606080527f035450600060005560006001556c3394fff4607f7f1684317b6080527f387b9f60a0527f1920700184809d60015450011899016e6009ff60026001556000527f9f60a05260c0527f7f6000527f9981600160045582600eff600060006000600060f65af45060006060e0527f6060c0527f20527e600060006020527f60f75af4501d7f190316666000600060610100527e6000600060e0527f60046040527f5af1506000600060006040527f60006009610120527f5af4503c95138e5b8f610100527f7f605a6000536060527f6031600153606b60610140527f02536010606060527f0353604560610120527f04536060600553600160608052610160527f7e527f60065360606007536002600853606080610140527f527f556009536060610180527f600a53600160a0527f600b536060600c6020527f53600060610160527f0d53606101a0527f55600e60a0527f536060600f536060c0527f01601053606060115360026101806101c0527f527f601253606040527f55601353606060c0527f60145360e0527f60006015536101e0527f60606101a0527f601653600060175360f360185360196060605260006060e052610200527f610100527f7f806101c0527f5360f3608153608260006000f060006000600060610220527e845af450506000600061016101e0527f20527f60610100527e600060006003610240527f5af15060005450c760006002551309f562610200527f66a486610140527f6b00610260527f1d4571610120527f600054501c641d373c7f60045450610220527f6000600155610280527f600554610160527f50600160025560085450610140527f60006002610240527f6102a0527f557fd86000606000527e600060610180527e600060005af150861217145147356102c0527f610260527f610160527f5198a37e127a7efa7c6000526101a0527f606020527f6102e0527f6060205360610280527ff760215360ff60225360610180527fdb602353603760610300527f6101c0527f24536075606102a0527f2553609f606040527f265360fe60275360610320527f8f60286101a0527f53606101e0527f6102c0527f0b6029536060602a53600060610340527f2b536060602c53606052606060805360006061016102e0527f610200527fc052610360527f7f81536060608253602d6083536053608453606060855360fd610300527f6086610380527f536060610220527f6087536101e0527f602e60885360536089536060608a61036103a0527f20527f53602f608b536060608c610240527f536000608d5360f36102005260606103c0527f610220610340527f53608e610221536053610222536060610260527f610223536103e0527f6000610224536060610360527f61022553608f61022653606061022753600061610400527f02610280527f28536060610229610380527f53600061022a5360f561022b5360610420527f6061022c53600061022d536102a0527f60606103a0527f61022e53600061022f610440527f53606061023053600061023153606061023253600061026103c0527fc0527f61610460527f023353606061023453600061023553608561023653605a61023753606103e052610480527f7ff261026102e0526038610300536053610301536060610302536050610303536104a0527f60610400527f61610304536002610305536039610306536053610307536060616104c0527f030853605061610420527f030953606161030a53600261030b53603a61030c536104e0527f605361030d53606161030e610440527f53600261030f53603b61031053606061610500527f03115360006103125360f3610313536161046052600361048053601461048153610520527f60606104825360006104835360606104845360006104855360f0610486536060610540527f61048753600061048853606061048953600061048a53606061048b5360006104610560527f8c53606061048d53600061048e53608461048f53605a6104905360f4610491536105805260606105a05360506105a15360616105a25360046105a35360926105a45360536105a55360606105a65360506105a75360616105a85360046105a95360936105aa5360536105ab5360616105ac5360046105ad5360946105ae5360606105af5360006105b05360f36105b15360006105b260006000f560006000600060006000855af15050","gasUsed":"0x5d717"} +{"pc":2605,"op":96,"gas":"0x49392","gasCost":"0x3","memSize":2240,"stack":["0x7dce2faf43218578e3fcf2ad22df9918a89e2fba"],"depth":1,"refund":0,"opName":"PUSH1"} +{"pc":2607,"op":96,"gas":"0x4938f","gasCost":"0x3","memSize":2240,"stack":["0x7dce2faf43218578e3fcf2ad22df9918a89e2fba","0x0"],"depth":1,"refund":0,"opName":"PUSH1"} +{"pc":2609,"op":96,"gas":"0x4938c","gasCost":"0x3","memSize":2240,"stack":["0x7dce2faf43218578e3fcf2ad22df9918a89e2fba","0x0","0x0"],"depth":1,"refund":0,"opName":"PUSH1"} +{"pc":2611,"op":96,"gas":"0x49389","gasCost":"0x3","memSize":2240,"stack":["0x7dce2faf43218578e3fcf2ad22df9918a89e2fba","0x0","0x0","0x0"],"depth":1,"refund":0,"opName":"PUSH1"} +{"pc":2613,"op":96,"gas":"0x49386","gasCost":"0x3","memSize":2240,"stack":["0x7dce2faf43218578e3fcf2ad22df9918a89e2fba","0x0","0x0","0x0","0x0"],"depth":1,"refund":0,"opName":"PUSH1"} +{"pc":2615,"op":133,"gas":"0x49383","gasCost":"0x3","memSize":2240,"stack":["0x7dce2faf43218578e3fcf2ad22df9918a89e2fba","0x0","0x0","0x0","0x0","0x0"],"depth":1,"refund":0,"opName":"DUP6"} +{"pc":2616,"op":90,"gas":"0x49380","gasCost":"0x2","memSize":2240,"stack":["0x7dce2faf43218578e3fcf2ad22df9918a89e2fba","0x0","0x0","0x0","0x0","0x0","0x7dce2faf43218578e3fcf2ad22df9918a89e2fba"],"depth":1,"refund":0,"opName":"GAS"} +{"pc":2617,"op":241,"gas":"0x4937e","gasCost":"0x48132","memSize":2240,"stack":["0x7dce2faf43218578e3fcf2ad22df9918a89e2fba","0x0","0x0","0x0","0x0","0x0","0x7dce2faf43218578e3fcf2ad22df9918a89e2fba","0x4937e"],"depth":1,"refund":0,"opName":"CALL"} +{"pc":0,"op":127,"gas":"0x480ce","gasCost":"0x3","memSize":0,"stack":[],"depth":2,"refund":0,"opName":"PUSH32"} +{"pc":33,"op":96,"gas":"0x480cb","gasCost":"0x3","memSize":0,"stack":["0x6008545060006004557f600160045560006004556000600060006000600060f9"],"depth":2,"refund":0,"opName":"PUSH1"} +{"pc":35,"op":82,"gas":"0x480c8","gasCost":"0x6","memSize":0,"stack":["0x6008545060006004557f600160045560006004556000600060006000600060f9","0x0"],"depth":2,"refund":0,"opName":"MSTORE"} +{"pc":36,"op":127,"gas":"0x480c2","gasCost":"0x3","memSize":32,"stack":[],"depth":2,"refund":0,"opName":"PUSH32"} +{"pc":69,"op":96,"gas":"0x480bf","gasCost":"0x3","memSize":32,"stack":["0x5af250600060006000606000527e60f45af4506000600060006000600060f55a"],"depth":2,"refund":0,"opName":"PUSH1"} +{"pc":71,"op":82,"gas":"0x480bc","gasCost":"0x6","memSize":32,"stack":["0x5af250600060006000606000527e60f45af4506000600060006000600060f55a","0x20"],"depth":2,"refund":0,"opName":"MSTORE"} +{"pc":72,"op":127,"gas":"0x480b6","gasCost":"0x3","memSize":64,"stack":[],"depth":2,"refund":0,"opName":"PUSH32"} +{"pc":105,"op":96,"gas":"0x480b3","gasCost":"0x3","memSize":64,"stack":["0xf150f001075205846a44a283446020527f8ca2600060006000600060045af450"],"depth":2,"refund":0,"opName":"PUSH1"} +{"pc":107,"op":82,"gas":"0x480b0","gasCost":"0x6","memSize":64,"stack":["0xf150f001075205846a44a283446020527f8ca2600060006000600060045af450","0x40"],"depth":2,"refund":0,"opName":"MSTORE"} +{"pc":108,"op":127,"gas":"0x480aa","gasCost":"0x3","memSize":96,"stack":[],"depth":2,"refund":0,"opName":"PUSH32"} +{"pc":141,"op":96,"gas":"0x480a7","gasCost":"0x3","memSize":96,"stack":["0x519930847f3b631c54a49b5f60035450326040527f77306b6000600060006000"],"depth":2,"refund":0,"opName":"PUSH1"} +{"pc":143,"op":82,"gas":"0x480a4","gasCost":"0x6","memSize":96,"stack":["0x519930847f3b631c54a49b5f60035450326040527f77306b6000600060006000","0x60"],"depth":2,"refund":0,"opName":"MSTORE"} +{"pc":144,"op":127,"gas":"0x4809e","gasCost":"0x3","memSize":128,"stack":[],"depth":2,"refund":0,"opName":"PUSH32"} +{"pc":177,"op":96,"gas":"0x4809b","gasCost":"0x3","memSize":128,"stack":["0x6000600c5af150600060006000600060f85af450506060527f06600160025560"],"depth":2,"refund":0,"opName":"PUSH1"} +{"pc":179,"op":82,"gas":"0x48098","gasCost":"0x6","memSize":128,"stack":["0x6000600c5af150600060006000600060f85af450506060527f06600160025560","0x80"],"depth":2,"refund":0,"opName":"MSTORE"} +{"pc":180,"op":127,"gas":"0x48092","gasCost":"0x3","memSize":160,"stack":[],"depth":2,"refund":0,"opName":"PUSH32"} +{"pc":213,"op":96,"gas":"0x4808f","gasCost":"0x3","memSize":160,"stack":["0x35450600060005560006001556c3394fff4607f7f1684317b6080527f387b9f"],"depth":2,"refund":0,"opName":"PUSH1"} +{"pc":215,"op":82,"gas":"0x4808c","gasCost":"0x6","memSize":160,"stack":["0x35450600060005560006001556c3394fff4607f7f1684317b6080527f387b9f","0xa0"],"depth":2,"refund":0,"opName":"MSTORE"} +{"pc":216,"op":127,"gas":"0x48086","gasCost":"0x3","memSize":192,"stack":[],"depth":2,"refund":0,"opName":"PUSH32"} +{"pc":249,"op":96,"gas":"0x48083","gasCost":"0x3","memSize":192,"stack":["0x1920700184809d60015450011899016e6009ff60026001556000527f9f60a052"],"depth":2,"refund":0,"opName":"PUSH1"} +{"pc":251,"op":82,"gas":"0x48080","gasCost":"0x6","memSize":192,"stack":["0x1920700184809d60015450011899016e6009ff60026001556000527f9f60a052","0xc0"],"depth":2,"refund":0,"opName":"MSTORE"} +{"pc":252,"op":127,"gas":"0x4807a","gasCost":"0x3","memSize":224,"stack":[],"depth":2,"refund":0,"opName":"PUSH32"} +{"pc":285,"op":96,"gas":"0x48077","gasCost":"0x3","memSize":224,"stack":["0x7f6000527f9981600160045582600eff600060006000600060f65af450600060"],"depth":2,"refund":0,"opName":"PUSH1"} +{"pc":287,"op":82,"gas":"0x48074","gasCost":"0x6","memSize":224,"stack":["0x7f6000527f9981600160045582600eff600060006000600060f65af450600060","0xe0"],"depth":2,"refund":0,"opName":"MSTORE"} +{"pc":288,"op":127,"gas":"0x4806e","gasCost":"0x3","memSize":256,"stack":[],"depth":2,"refund":0,"opName":"PUSH32"} +{"pc":321,"op":97,"gas":"0x4806b","gasCost":"0x3","memSize":256,"stack":["0x6060c0527f20527e600060006020527f60f75af4501d7f190316666000600060"],"depth":2,"refund":0,"opName":"PUSH2"} +{"pc":324,"op":82,"gas":"0x48068","gasCost":"0x6","memSize":256,"stack":["0x6060c0527f20527e600060006020527f60f75af4501d7f190316666000600060","0x100"],"depth":2,"refund":0,"opName":"MSTORE"} +{"pc":325,"op":126,"gas":"0x48062","gasCost":"0x3","memSize":288,"stack":[],"depth":2,"refund":0,"opName":"PUSH31"} +{"pc":357,"op":97,"gas":"0x4805f","gasCost":"0x3","memSize":288,"stack":["0x6000600060e0527f60046040527f5af1506000600060006040527f60006009"],"depth":2,"refund":0,"opName":"PUSH2"} +{"pc":360,"op":82,"gas":"0x4805c","gasCost":"0x6","memSize":288,"stack":["0x6000600060e0527f60046040527f5af1506000600060006040527f60006009","0x120"],"depth":2,"refund":0,"opName":"MSTORE"} +{"pc":361,"op":127,"gas":"0x48056","gasCost":"0x3","memSize":320,"stack":[],"depth":2,"refund":0,"opName":"PUSH32"} +{"pc":394,"op":97,"gas":"0x48053","gasCost":"0x3","memSize":320,"stack":["0x5af4503c95138e5b8f610100527f7f605a6000536060527f6031600153606b60"],"depth":2,"refund":0,"opName":"PUSH2"} +{"pc":397,"op":82,"gas":"0x48050","gasCost":"0x6","memSize":320,"stack":["0x5af4503c95138e5b8f610100527f7f605a6000536060527f6031600153606b60","0x140"],"depth":2,"refund":0,"opName":"MSTORE"} +{"pc":398,"op":127,"gas":"0x4804a","gasCost":"0x3","memSize":352,"stack":[],"depth":2,"refund":0,"opName":"PUSH32"} +{"pc":431,"op":97,"gas":"0x48047","gasCost":"0x3","memSize":352,"stack":["0x2536010606060527f0353604560610120527f04536060600553600160608052"],"depth":2,"refund":0,"opName":"PUSH2"} +{"pc":434,"op":82,"gas":"0x48044","gasCost":"0x6","memSize":352,"stack":["0x2536010606060527f0353604560610120527f04536060600553600160608052","0x160"],"depth":2,"refund":0,"opName":"MSTORE"} +{"pc":435,"op":127,"gas":"0x4803e","gasCost":"0x3","memSize":384,"stack":[],"depth":2,"refund":0,"opName":"PUSH32"} +{"pc":468,"op":97,"gas":"0x4803b","gasCost":"0x3","memSize":384,"stack":["0x7e527f60065360606007536002600853606080610140527f527f556009536060"],"depth":2,"refund":0,"opName":"PUSH2"} +{"pc":471,"op":82,"gas":"0x48038","gasCost":"0x6","memSize":384,"stack":["0x7e527f60065360606007536002600853606080610140527f527f556009536060","0x180"],"depth":2,"refund":0,"opName":"MSTORE"} +{"pc":472,"op":127,"gas":"0x48032","gasCost":"0x3","memSize":416,"stack":[],"depth":2,"refund":0,"opName":"PUSH32"} +{"pc":505,"op":97,"gas":"0x4802f","gasCost":"0x3","memSize":416,"stack":["0x600a53600160a0527f600b536060600c6020527f53600060610160527f0d5360"],"depth":2,"refund":0,"opName":"PUSH2"} +{"pc":508,"op":82,"gas":"0x4802c","gasCost":"0x6","memSize":416,"stack":["0x600a53600160a0527f600b536060600c6020527f53600060610160527f0d5360","0x1a0"],"depth":2,"refund":0,"opName":"MSTORE"} +{"pc":509,"op":127,"gas":"0x48026","gasCost":"0x3","memSize":448,"stack":[],"depth":2,"refund":0,"opName":"PUSH32"} +{"pc":542,"op":97,"gas":"0x48023","gasCost":"0x3","memSize":448,"stack":["0x55600e60a0527f536060600f536060c0527f0160105360606011536002610180"],"depth":2,"refund":0,"opName":"PUSH2"} +{"pc":545,"op":82,"gas":"0x48020","gasCost":"0x6","memSize":448,"stack":["0x55600e60a0527f536060600f536060c0527f0160105360606011536002610180","0x1c0"],"depth":2,"refund":0,"opName":"MSTORE"} +{"pc":546,"op":127,"gas":"0x4801a","gasCost":"0x3","memSize":480,"stack":[],"depth":2,"refund":0,"opName":"PUSH32"} +{"pc":579,"op":97,"gas":"0x48017","gasCost":"0x3","memSize":480,"stack":["0x527f601253606040527f55601353606060c0527f60145360e0527f6000601553"],"depth":2,"refund":0,"opName":"PUSH2"} +{"pc":582,"op":82,"gas":"0x48014","gasCost":"0x6","memSize":480,"stack":["0x527f601253606040527f55601353606060c0527f60145360e0527f6000601553","0x1e0"],"depth":2,"refund":0,"opName":"MSTORE"} +{"pc":583,"op":127,"gas":"0x4800e","gasCost":"0x3","memSize":512,"stack":[],"depth":2,"refund":0,"opName":"PUSH32"} +{"pc":616,"op":97,"gas":"0x4800b","gasCost":"0x3","memSize":512,"stack":["0x60606101a0527f601653600060175360f360185360196060605260006060e052"],"depth":2,"refund":0,"opName":"PUSH2"} +{"pc":619,"op":82,"gas":"0x48008","gasCost":"0x6","memSize":512,"stack":["0x60606101a0527f601653600060175360f360185360196060605260006060e052","0x200"],"depth":2,"refund":0,"opName":"MSTORE"} +{"pc":620,"op":127,"gas":"0x48002","gasCost":"0x3","memSize":544,"stack":[],"depth":2,"refund":0,"opName":"PUSH32"} +{"pc":653,"op":97,"gas":"0x47fff","gasCost":"0x3","memSize":544,"stack":["0x610100527f7f806101c0527f5360f3608153608260006000f060006000600060"],"depth":2,"refund":0,"opName":"PUSH2"} +{"pc":656,"op":82,"gas":"0x47ffc","gasCost":"0x6","memSize":544,"stack":["0x610100527f7f806101c0527f5360f3608153608260006000f060006000600060","0x220"],"depth":2,"refund":0,"opName":"MSTORE"} +{"pc":657,"op":126,"gas":"0x47ff6","gasCost":"0x3","memSize":576,"stack":[],"depth":2,"refund":0,"opName":"PUSH31"} +{"pc":689,"op":97,"gas":"0x47ff3","gasCost":"0x3","memSize":576,"stack":["0x845af450506000600061016101e0527f20527f60610100527e600060006003"],"depth":2,"refund":0,"opName":"PUSH2"} +{"pc":692,"op":82,"gas":"0x47ff0","gasCost":"0x6","memSize":576,"stack":["0x845af450506000600061016101e0527f20527f60610100527e600060006003","0x240"],"depth":2,"refund":0,"opName":"MSTORE"} +{"pc":693,"op":127,"gas":"0x47fea","gasCost":"0x3","memSize":608,"stack":[],"depth":2,"refund":0,"opName":"PUSH32"} +{"pc":726,"op":97,"gas":"0x47fe7","gasCost":"0x3","memSize":608,"stack":["0x5af15060005450c760006002551309f562610200527f66a486610140527f6b00"],"depth":2,"refund":0,"opName":"PUSH2"} +{"pc":729,"op":82,"gas":"0x47fe4","gasCost":"0x6","memSize":608,"stack":["0x5af15060005450c760006002551309f562610200527f66a486610140527f6b00","0x260"],"depth":2,"refund":0,"opName":"MSTORE"} +{"pc":730,"op":127,"gas":"0x47fde","gasCost":"0x3","memSize":640,"stack":[],"depth":2,"refund":0,"opName":"PUSH32"} +{"pc":763,"op":97,"gas":"0x47fdb","gasCost":"0x3","memSize":640,"stack":["0x1d4571610120527f600054501c641d373c7f60045450610220527f6000600155"],"depth":2,"refund":0,"opName":"PUSH2"} +{"pc":766,"op":82,"gas":"0x47fd8","gasCost":"0x6","memSize":640,"stack":["0x1d4571610120527f600054501c641d373c7f60045450610220527f6000600155","0x280"],"depth":2,"refund":0,"opName":"MSTORE"} +{"pc":767,"op":127,"gas":"0x47fd2","gasCost":"0x3","memSize":672,"stack":[],"depth":2,"refund":0,"opName":"PUSH32"} +{"pc":800,"op":97,"gas":"0x47fcf","gasCost":"0x3","memSize":672,"stack":["0x600554610160527f50600160025560085450610140527f60006002610240527f"],"depth":2,"refund":0,"opName":"PUSH2"} +{"pc":803,"op":82,"gas":"0x47fcc","gasCost":"0x6","memSize":672,"stack":["0x600554610160527f50600160025560085450610140527f60006002610240527f","0x2a0"],"depth":2,"refund":0,"opName":"MSTORE"} +{"pc":804,"op":127,"gas":"0x47fc6","gasCost":"0x3","memSize":704,"stack":[],"depth":2,"refund":0,"opName":"PUSH32"} +{"pc":837,"op":97,"gas":"0x47fc3","gasCost":"0x3","memSize":704,"stack":["0x557fd86000606000527e600060610180527e600060005af15086121714514735"],"depth":2,"refund":0,"opName":"PUSH2"} +{"pc":840,"op":82,"gas":"0x47fc0","gasCost":"0x7","memSize":704,"stack":["0x557fd86000606000527e600060610180527e600060005af15086121714514735","0x2c0"],"depth":2,"refund":0,"opName":"MSTORE"} +{"pc":841,"op":127,"gas":"0x47fb9","gasCost":"0x3","memSize":736,"stack":[],"depth":2,"refund":0,"opName":"PUSH32"} +{"pc":874,"op":97,"gas":"0x47fb6","gasCost":"0x3","memSize":736,"stack":["0x610260527f610160527f5198a37e127a7efa7c6000526101a0527f606020527f"],"depth":2,"refund":0,"opName":"PUSH2"} +{"pc":877,"op":82,"gas":"0x47fb3","gasCost":"0x6","memSize":736,"stack":["0x610260527f610160527f5198a37e127a7efa7c6000526101a0527f606020527f","0x2e0"],"depth":2,"refund":0,"opName":"MSTORE"} +{"pc":878,"op":127,"gas":"0x47fad","gasCost":"0x3","memSize":768,"stack":[],"depth":2,"refund":0,"opName":"PUSH32"} +{"pc":911,"op":97,"gas":"0x47faa","gasCost":"0x3","memSize":768,"stack":["0x6060205360610280527ff760215360ff60225360610180527fdb602353603760"],"depth":2,"refund":0,"opName":"PUSH2"} +{"pc":914,"op":82,"gas":"0x47fa7","gasCost":"0x6","memSize":768,"stack":["0x6060205360610280527ff760215360ff60225360610180527fdb602353603760","0x300"],"depth":2,"refund":0,"opName":"MSTORE"} +{"pc":915,"op":127,"gas":"0x47fa1","gasCost":"0x3","memSize":800,"stack":[],"depth":2,"refund":0,"opName":"PUSH32"} +{"pc":948,"op":97,"gas":"0x47f9e","gasCost":"0x3","memSize":800,"stack":["0x6101c0527f24536075606102a0527f2553609f606040527f265360fe60275360"],"depth":2,"refund":0,"opName":"PUSH2"} +{"pc":951,"op":82,"gas":"0x47f9b","gasCost":"0x6","memSize":800,"stack":["0x6101c0527f24536075606102a0527f2553609f606040527f265360fe60275360","0x320"],"depth":2,"refund":0,"opName":"MSTORE"} +{"pc":952,"op":127,"gas":"0x47f95","gasCost":"0x3","memSize":832,"stack":[],"depth":2,"refund":0,"opName":"PUSH32"} +{"pc":985,"op":97,"gas":"0x47f92","gasCost":"0x3","memSize":832,"stack":["0x8f60286101a0527f53606101e0527f6102c0527f0b6029536060602a53600060"],"depth":2,"refund":0,"opName":"PUSH2"} +{"pc":988,"op":82,"gas":"0x47f8f","gasCost":"0x6","memSize":832,"stack":["0x8f60286101a0527f53606101e0527f6102c0527f0b6029536060602a53600060","0x340"],"depth":2,"refund":0,"opName":"MSTORE"} +{"pc":989,"op":127,"gas":"0x47f89","gasCost":"0x3","memSize":864,"stack":[],"depth":2,"refund":0,"opName":"PUSH32"} +{"pc":1022,"op":97,"gas":"0x47f86","gasCost":"0x3","memSize":864,"stack":["0x2b536060602c53606052606060805360006061016102e0527f610200527fc052"],"depth":2,"refund":0,"opName":"PUSH2"} +{"pc":1025,"op":82,"gas":"0x47f83","gasCost":"0x6","memSize":864,"stack":["0x2b536060602c53606052606060805360006061016102e0527f610200527fc052","0x360"],"depth":2,"refund":0,"opName":"MSTORE"} +{"pc":1026,"op":127,"gas":"0x47f7d","gasCost":"0x3","memSize":896,"stack":[],"depth":2,"refund":0,"opName":"PUSH32"} +{"pc":1059,"op":97,"gas":"0x47f7a","gasCost":"0x3","memSize":896,"stack":["0x7f81536060608253602d6083536053608453606060855360fd610300527f6086"],"depth":2,"refund":0,"opName":"PUSH2"} +{"pc":1062,"op":82,"gas":"0x47f77","gasCost":"0x6","memSize":896,"stack":["0x7f81536060608253602d6083536053608453606060855360fd610300527f6086","0x380"],"depth":2,"refund":0,"opName":"MSTORE"} +{"pc":1063,"op":127,"gas":"0x47f71","gasCost":"0x3","memSize":928,"stack":[],"depth":2,"refund":0,"opName":"PUSH32"} +{"pc":1096,"op":97,"gas":"0x47f6e","gasCost":"0x3","memSize":928,"stack":["0x536060610220527f6087536101e0527f602e60885360536089536060608a6103"],"depth":2,"refund":0,"opName":"PUSH2"} +{"pc":1099,"op":82,"gas":"0x47f6b","gasCost":"0x6","memSize":928,"stack":["0x536060610220527f6087536101e0527f602e60885360536089536060608a6103","0x3a0"],"depth":2,"refund":0,"opName":"MSTORE"} +{"pc":1100,"op":127,"gas":"0x47f65","gasCost":"0x3","memSize":960,"stack":[],"depth":2,"refund":0,"opName":"PUSH32"} +{"pc":1133,"op":97,"gas":"0x47f62","gasCost":"0x3","memSize":960,"stack":["0x20527f53602f608b536060608c610240527f536000608d5360f3610200526060"],"depth":2,"refund":0,"opName":"PUSH2"} +{"pc":1136,"op":82,"gas":"0x47f5f","gasCost":"0x6","memSize":960,"stack":["0x20527f53602f608b536060608c610240527f536000608d5360f3610200526060","0x3c0"],"depth":2,"refund":0,"opName":"MSTORE"} +{"pc":1137,"op":127,"gas":"0x47f59","gasCost":"0x3","memSize":992,"stack":[],"depth":2,"refund":0,"opName":"PUSH32"} +{"pc":1170,"op":97,"gas":"0x47f56","gasCost":"0x3","memSize":992,"stack":["0x610220610340527f53608e610221536053610222536060610260527f61022353"],"depth":2,"refund":0,"opName":"PUSH2"} +{"pc":1173,"op":82,"gas":"0x47f53","gasCost":"0x7","memSize":992,"stack":["0x610220610340527f53608e610221536053610222536060610260527f61022353","0x3e0"],"depth":2,"refund":0,"opName":"MSTORE"} +{"pc":1174,"op":127,"gas":"0x47f4c","gasCost":"0x3","memSize":1024,"stack":[],"depth":2,"refund":0,"opName":"PUSH32"} +{"pc":1207,"op":97,"gas":"0x47f49","gasCost":"0x3","memSize":1024,"stack":["0x6000610224536060610360527f61022553608f61022653606061022753600061"],"depth":2,"refund":0,"opName":"PUSH2"} +{"pc":1210,"op":82,"gas":"0x47f46","gasCost":"0x6","memSize":1024,"stack":["0x6000610224536060610360527f61022553608f61022653606061022753600061","0x400"],"depth":2,"refund":0,"opName":"MSTORE"} +{"pc":1211,"op":127,"gas":"0x47f40","gasCost":"0x3","memSize":1056,"stack":[],"depth":2,"refund":0,"opName":"PUSH32"} +{"pc":1244,"op":97,"gas":"0x47f3d","gasCost":"0x3","memSize":1056,"stack":["0x2610280527f28536060610229610380527f53600061022a5360f561022b5360"],"depth":2,"refund":0,"opName":"PUSH2"} +{"pc":1247,"op":82,"gas":"0x47f3a","gasCost":"0x6","memSize":1056,"stack":["0x2610280527f28536060610229610380527f53600061022a5360f561022b5360","0x420"],"depth":2,"refund":0,"opName":"MSTORE"} +{"pc":1248,"op":127,"gas":"0x47f34","gasCost":"0x3","memSize":1088,"stack":[],"depth":2,"refund":0,"opName":"PUSH32"} +{"pc":1281,"op":97,"gas":"0x47f31","gasCost":"0x3","memSize":1088,"stack":["0x6061022c53600061022d536102a0527f60606103a0527f61022e53600061022f"],"depth":2,"refund":0,"opName":"PUSH2"} +{"pc":1284,"op":82,"gas":"0x47f2e","gasCost":"0x6","memSize":1088,"stack":["0x6061022c53600061022d536102a0527f60606103a0527f61022e53600061022f","0x440"],"depth":2,"refund":0,"opName":"MSTORE"} +{"pc":1285,"op":127,"gas":"0x47f28","gasCost":"0x3","memSize":1120,"stack":[],"depth":2,"refund":0,"opName":"PUSH32"} +{"pc":1318,"op":97,"gas":"0x47f25","gasCost":"0x3","memSize":1120,"stack":["0x53606061023053600061023153606061023253600061026103c0527fc0527f61"],"depth":2,"refund":0,"opName":"PUSH2"} +{"pc":1321,"op":82,"gas":"0x47f22","gasCost":"0x6","memSize":1120,"stack":["0x53606061023053600061023153606061023253600061026103c0527fc0527f61","0x460"],"depth":2,"refund":0,"opName":"MSTORE"} +{"pc":1322,"op":127,"gas":"0x47f1c","gasCost":"0x3","memSize":1152,"stack":[],"depth":2,"refund":0,"opName":"PUSH32"} +{"pc":1355,"op":97,"gas":"0x47f19","gasCost":"0x3","memSize":1152,"stack":["0x23353606061023453600061023553608561023653605a61023753606103e052"],"depth":2,"refund":0,"opName":"PUSH2"} +{"pc":1358,"op":82,"gas":"0x47f16","gasCost":"0x6","memSize":1152,"stack":["0x23353606061023453600061023553608561023653605a61023753606103e052","0x480"],"depth":2,"refund":0,"opName":"MSTORE"} +{"pc":1359,"op":127,"gas":"0x47f10","gasCost":"0x3","memSize":1184,"stack":[],"depth":2,"refund":0,"opName":"PUSH32"} +{"pc":1392,"op":97,"gas":"0x47f0d","gasCost":"0x3","memSize":1184,"stack":["0x7ff261026102e052603861030053605361030153606061030253605061030353"],"depth":2,"refund":0,"opName":"PUSH2"} +{"pc":1395,"op":82,"gas":"0x47f0a","gasCost":"0x6","memSize":1184,"stack":["0x7ff261026102e052603861030053605361030153606061030253605061030353","0x4a0"],"depth":2,"refund":0,"opName":"MSTORE"} +{"pc":1396,"op":127,"gas":"0x47f04","gasCost":"0x3","memSize":1216,"stack":[],"depth":2,"refund":0,"opName":"PUSH32"} +{"pc":1429,"op":97,"gas":"0x47f01","gasCost":"0x3","memSize":1216,"stack":["0x60610400527f6161030453600261030553603961030653605361030753606061"],"depth":2,"refund":0,"opName":"PUSH2"} +{"pc":1432,"op":82,"gas":"0x47efe","gasCost":"0x6","memSize":1216,"stack":["0x60610400527f6161030453600261030553603961030653605361030753606061","0x4c0"],"depth":2,"refund":0,"opName":"MSTORE"} +{"pc":1433,"op":127,"gas":"0x47ef8","gasCost":"0x3","memSize":1248,"stack":[],"depth":2,"refund":0,"opName":"PUSH32"} +{"pc":1466,"op":97,"gas":"0x47ef5","gasCost":"0x3","memSize":1248,"stack":["0x30853605061610420527f030953606161030a53600261030b53603a61030c53"],"depth":2,"refund":0,"opName":"PUSH2"} +{"pc":1469,"op":82,"gas":"0x47ef2","gasCost":"0x7","memSize":1248,"stack":["0x30853605061610420527f030953606161030a53600261030b53603a61030c53","0x4e0"],"depth":2,"refund":0,"opName":"MSTORE"} +{"pc":1470,"op":127,"gas":"0x47eeb","gasCost":"0x3","memSize":1280,"stack":[],"depth":2,"refund":0,"opName":"PUSH32"} +{"pc":1503,"op":97,"gas":"0x47ee8","gasCost":"0x3","memSize":1280,"stack":["0x605361030d53606161030e610440527f53600261030f53603b61031053606061"],"depth":2,"refund":0,"opName":"PUSH2"} +{"pc":1506,"op":82,"gas":"0x47ee5","gasCost":"0x6","memSize":1280,"stack":["0x605361030d53606161030e610440527f53600261030f53603b61031053606061","0x500"],"depth":2,"refund":0,"opName":"MSTORE"} +{"pc":1507,"op":127,"gas":"0x47edf","gasCost":"0x3","memSize":1312,"stack":[],"depth":2,"refund":0,"opName":"PUSH32"} +{"pc":1540,"op":97,"gas":"0x47edc","gasCost":"0x3","memSize":1312,"stack":["0x3115360006103125360f3610313536161046052600361048053601461048153"],"depth":2,"refund":0,"opName":"PUSH2"} +{"pc":1543,"op":82,"gas":"0x47ed9","gasCost":"0x6","memSize":1312,"stack":["0x3115360006103125360f3610313536161046052600361048053601461048153","0x520"],"depth":2,"refund":0,"opName":"MSTORE"} +{"pc":1544,"op":127,"gas":"0x47ed3","gasCost":"0x3","memSize":1344,"stack":[],"depth":2,"refund":0,"opName":"PUSH32"} +{"pc":1577,"op":97,"gas":"0x47ed0","gasCost":"0x3","memSize":1344,"stack":["0x60606104825360006104835360606104845360006104855360f0610486536060"],"depth":2,"refund":0,"opName":"PUSH2"} +{"pc":1580,"op":82,"gas":"0x47ecd","gasCost":"0x6","memSize":1344,"stack":["0x60606104825360006104835360606104845360006104855360f0610486536060","0x540"],"depth":2,"refund":0,"opName":"MSTORE"} +{"pc":1581,"op":127,"gas":"0x47ec7","gasCost":"0x3","memSize":1376,"stack":[],"depth":2,"refund":0,"opName":"PUSH32"} +{"pc":1614,"op":97,"gas":"0x47ec4","gasCost":"0x3","memSize":1376,"stack":["0x61048753600061048853606061048953600061048a53606061048b5360006104"],"depth":2,"refund":0,"opName":"PUSH2"} +{"pc":1617,"op":82,"gas":"0x47ec1","gasCost":"0x6","memSize":1376,"stack":["0x61048753600061048853606061048953600061048a53606061048b5360006104","0x560"],"depth":2,"refund":0,"opName":"MSTORE"} +{"pc":1618,"op":127,"gas":"0x47ebb","gasCost":"0x3","memSize":1408,"stack":[],"depth":2,"refund":0,"opName":"PUSH32"} +{"pc":1651,"op":97,"gas":"0x47eb8","gasCost":"0x3","memSize":1408,"stack":["0x8c53606061048d53600061048e53608461048f53605a6104905360f461049153"],"depth":2,"refund":0,"opName":"PUSH2"} +{"pc":1654,"op":82,"gas":"0x47eb5","gasCost":"0x6","memSize":1408,"stack":["0x8c53606061048d53600061048e53608461048f53605a6104905360f461049153","0x580"],"depth":2,"refund":0,"opName":"MSTORE"} +{"pc":1655,"op":96,"gas":"0x47eaf","gasCost":"0x3","memSize":1440,"stack":[],"depth":2,"refund":0,"opName":"PUSH1"} +{"pc":1657,"op":97,"gas":"0x47eac","gasCost":"0x3","memSize":1440,"stack":["0x60"],"depth":2,"refund":0,"opName":"PUSH2"} +{"pc":1660,"op":83,"gas":"0x47ea9","gasCost":"0x7","memSize":1440,"stack":["0x60","0x5a0"],"depth":2,"refund":0,"opName":"MSTORE8"} +{"pc":1661,"op":96,"gas":"0x47ea2","gasCost":"0x3","memSize":1472,"stack":[],"depth":2,"refund":0,"opName":"PUSH1"} +{"pc":1663,"op":97,"gas":"0x47e9f","gasCost":"0x3","memSize":1472,"stack":["0x50"],"depth":2,"refund":0,"opName":"PUSH2"} +{"pc":1666,"op":83,"gas":"0x47e9c","gasCost":"0x3","memSize":1472,"stack":["0x50","0x5a1"],"depth":2,"refund":0,"opName":"MSTORE8"} +{"pc":1667,"op":96,"gas":"0x47e99","gasCost":"0x3","memSize":1472,"stack":[],"depth":2,"refund":0,"opName":"PUSH1"} +{"pc":1669,"op":97,"gas":"0x47e96","gasCost":"0x3","memSize":1472,"stack":["0x61"],"depth":2,"refund":0,"opName":"PUSH2"} +{"pc":1672,"op":83,"gas":"0x47e93","gasCost":"0x3","memSize":1472,"stack":["0x61","0x5a2"],"depth":2,"refund":0,"opName":"MSTORE8"} +{"pc":1673,"op":96,"gas":"0x47e90","gasCost":"0x3","memSize":1472,"stack":[],"depth":2,"refund":0,"opName":"PUSH1"} +{"pc":1675,"op":97,"gas":"0x47e8d","gasCost":"0x3","memSize":1472,"stack":["0x4"],"depth":2,"refund":0,"opName":"PUSH2"} +{"pc":1678,"op":83,"gas":"0x47e8a","gasCost":"0x3","memSize":1472,"stack":["0x4","0x5a3"],"depth":2,"refund":0,"opName":"MSTORE8"} +{"pc":1679,"op":96,"gas":"0x47e87","gasCost":"0x3","memSize":1472,"stack":[],"depth":2,"refund":0,"opName":"PUSH1"} +{"pc":1681,"op":97,"gas":"0x47e84","gasCost":"0x3","memSize":1472,"stack":["0x92"],"depth":2,"refund":0,"opName":"PUSH2"} +{"pc":1684,"op":83,"gas":"0x47e81","gasCost":"0x3","memSize":1472,"stack":["0x92","0x5a4"],"depth":2,"refund":0,"opName":"MSTORE8"} +{"pc":1685,"op":96,"gas":"0x47e7e","gasCost":"0x3","memSize":1472,"stack":[],"depth":2,"refund":0,"opName":"PUSH1"} +{"pc":1687,"op":97,"gas":"0x47e7b","gasCost":"0x3","memSize":1472,"stack":["0x53"],"depth":2,"refund":0,"opName":"PUSH2"} +{"pc":1690,"op":83,"gas":"0x47e78","gasCost":"0x3","memSize":1472,"stack":["0x53","0x5a5"],"depth":2,"refund":0,"opName":"MSTORE8"} +{"pc":1691,"op":96,"gas":"0x47e75","gasCost":"0x3","memSize":1472,"stack":[],"depth":2,"refund":0,"opName":"PUSH1"} +{"pc":1693,"op":97,"gas":"0x47e72","gasCost":"0x3","memSize":1472,"stack":["0x60"],"depth":2,"refund":0,"opName":"PUSH2"} +{"pc":1696,"op":83,"gas":"0x47e6f","gasCost":"0x3","memSize":1472,"stack":["0x60","0x5a6"],"depth":2,"refund":0,"opName":"MSTORE8"} +{"pc":1697,"op":96,"gas":"0x47e6c","gasCost":"0x3","memSize":1472,"stack":[],"depth":2,"refund":0,"opName":"PUSH1"} +{"pc":1699,"op":97,"gas":"0x47e69","gasCost":"0x3","memSize":1472,"stack":["0x50"],"depth":2,"refund":0,"opName":"PUSH2"} +{"pc":1702,"op":83,"gas":"0x47e66","gasCost":"0x3","memSize":1472,"stack":["0x50","0x5a7"],"depth":2,"refund":0,"opName":"MSTORE8"} +{"pc":1703,"op":96,"gas":"0x47e63","gasCost":"0x3","memSize":1472,"stack":[],"depth":2,"refund":0,"opName":"PUSH1"} +{"pc":1705,"op":97,"gas":"0x47e60","gasCost":"0x3","memSize":1472,"stack":["0x61"],"depth":2,"refund":0,"opName":"PUSH2"} +{"pc":1708,"op":83,"gas":"0x47e5d","gasCost":"0x3","memSize":1472,"stack":["0x61","0x5a8"],"depth":2,"refund":0,"opName":"MSTORE8"} +{"pc":1709,"op":96,"gas":"0x47e5a","gasCost":"0x3","memSize":1472,"stack":[],"depth":2,"refund":0,"opName":"PUSH1"} +{"pc":1711,"op":97,"gas":"0x47e57","gasCost":"0x3","memSize":1472,"stack":["0x4"],"depth":2,"refund":0,"opName":"PUSH2"} +{"pc":1714,"op":83,"gas":"0x47e54","gasCost":"0x3","memSize":1472,"stack":["0x4","0x5a9"],"depth":2,"refund":0,"opName":"MSTORE8"} +{"pc":1715,"op":96,"gas":"0x47e51","gasCost":"0x3","memSize":1472,"stack":[],"depth":2,"refund":0,"opName":"PUSH1"} +{"pc":1717,"op":97,"gas":"0x47e4e","gasCost":"0x3","memSize":1472,"stack":["0x93"],"depth":2,"refund":0,"opName":"PUSH2"} +{"pc":1720,"op":83,"gas":"0x47e4b","gasCost":"0x3","memSize":1472,"stack":["0x93","0x5aa"],"depth":2,"refund":0,"opName":"MSTORE8"} +{"pc":1721,"op":96,"gas":"0x47e48","gasCost":"0x3","memSize":1472,"stack":[],"depth":2,"refund":0,"opName":"PUSH1"} +{"pc":1723,"op":97,"gas":"0x47e45","gasCost":"0x3","memSize":1472,"stack":["0x53"],"depth":2,"refund":0,"opName":"PUSH2"} +{"pc":1726,"op":83,"gas":"0x47e42","gasCost":"0x3","memSize":1472,"stack":["0x53","0x5ab"],"depth":2,"refund":0,"opName":"MSTORE8"} +{"pc":1727,"op":96,"gas":"0x47e3f","gasCost":"0x3","memSize":1472,"stack":[],"depth":2,"refund":0,"opName":"PUSH1"} +{"pc":1729,"op":97,"gas":"0x47e3c","gasCost":"0x3","memSize":1472,"stack":["0x61"],"depth":2,"refund":0,"opName":"PUSH2"} +{"pc":1732,"op":83,"gas":"0x47e39","gasCost":"0x3","memSize":1472,"stack":["0x61","0x5ac"],"depth":2,"refund":0,"opName":"MSTORE8"} +{"pc":1733,"op":96,"gas":"0x47e36","gasCost":"0x3","memSize":1472,"stack":[],"depth":2,"refund":0,"opName":"PUSH1"} +{"pc":1735,"op":97,"gas":"0x47e33","gasCost":"0x3","memSize":1472,"stack":["0x4"],"depth":2,"refund":0,"opName":"PUSH2"} +{"pc":1738,"op":83,"gas":"0x47e30","gasCost":"0x3","memSize":1472,"stack":["0x4","0x5ad"],"depth":2,"refund":0,"opName":"MSTORE8"} +{"pc":1739,"op":96,"gas":"0x47e2d","gasCost":"0x3","memSize":1472,"stack":[],"depth":2,"refund":0,"opName":"PUSH1"} +{"pc":1741,"op":97,"gas":"0x47e2a","gasCost":"0x3","memSize":1472,"stack":["0x94"],"depth":2,"refund":0,"opName":"PUSH2"} +{"pc":1744,"op":83,"gas":"0x47e27","gasCost":"0x3","memSize":1472,"stack":["0x94","0x5ae"],"depth":2,"refund":0,"opName":"MSTORE8"} +{"pc":1745,"op":96,"gas":"0x47e24","gasCost":"0x3","memSize":1472,"stack":[],"depth":2,"refund":0,"opName":"PUSH1"} +{"pc":1747,"op":97,"gas":"0x47e21","gasCost":"0x3","memSize":1472,"stack":["0x60"],"depth":2,"refund":0,"opName":"PUSH2"} +{"pc":1750,"op":83,"gas":"0x47e1e","gasCost":"0x3","memSize":1472,"stack":["0x60","0x5af"],"depth":2,"refund":0,"opName":"MSTORE8"} +{"pc":1751,"op":96,"gas":"0x47e1b","gasCost":"0x3","memSize":1472,"stack":[],"depth":2,"refund":0,"opName":"PUSH1"} +{"pc":1753,"op":97,"gas":"0x47e18","gasCost":"0x3","memSize":1472,"stack":["0x0"],"depth":2,"refund":0,"opName":"PUSH2"} +{"pc":1756,"op":83,"gas":"0x47e15","gasCost":"0x3","memSize":1472,"stack":["0x0","0x5b0"],"depth":2,"refund":0,"opName":"MSTORE8"} +{"pc":1757,"op":96,"gas":"0x47e12","gasCost":"0x3","memSize":1472,"stack":[],"depth":2,"refund":0,"opName":"PUSH1"} +{"pc":1759,"op":97,"gas":"0x47e0f","gasCost":"0x3","memSize":1472,"stack":["0xf3"],"depth":2,"refund":0,"opName":"PUSH2"} +{"pc":1762,"op":83,"gas":"0x47e0c","gasCost":"0x3","memSize":1472,"stack":["0xf3","0x5b1"],"depth":2,"refund":0,"opName":"MSTORE8"} +{"pc":1763,"op":96,"gas":"0x47e09","gasCost":"0x3","memSize":1472,"stack":[],"depth":2,"refund":0,"opName":"PUSH1"} +{"pc":1765,"op":97,"gas":"0x47e06","gasCost":"0x3","memSize":1472,"stack":["0x0"],"depth":2,"refund":0,"opName":"PUSH2"} +{"pc":1768,"op":96,"gas":"0x47e03","gasCost":"0x3","memSize":1472,"stack":["0x0","0x5b2"],"depth":2,"refund":0,"opName":"PUSH1"} +{"pc":1770,"op":96,"gas":"0x47e00","gasCost":"0x3","memSize":1472,"stack":["0x0","0x5b2","0x0"],"depth":2,"refund":0,"opName":"PUSH1"} +{"pc":1772,"op":245,"gas":"0x47dfd","gasCost":"0x7e14","memSize":1472,"stack":["0x0","0x5b2","0x0","0x0"],"depth":2,"refund":0,"opName":"CREATE2"} +{"pc":0,"op":96,"gas":"0x3efea","gasCost":"0x3","memSize":0,"stack":[],"depth":3,"refund":0,"opName":"PUSH1"} +{"pc":2,"op":84,"gas":"0x3efe7","gasCost":"0x834","memSize":0,"stack":["0x8"],"depth":3,"refund":0,"opName":"SLOAD"} +{"pc":3,"op":80,"gas":"0x3e7b3","gasCost":"0x2","memSize":0,"stack":["0x0"],"depth":3,"refund":0,"opName":"POP"} +{"pc":4,"op":96,"gas":"0x3e7b1","gasCost":"0x3","memSize":0,"stack":[],"depth":3,"refund":0,"opName":"PUSH1"} +{"pc":6,"op":96,"gas":"0x3e7ae","gasCost":"0x3","memSize":0,"stack":["0x0"],"depth":3,"refund":0,"opName":"PUSH1"} +{"pc":8,"op":85,"gas":"0x3e7ab","gasCost":"0x898","memSize":0,"stack":["0x0","0x4"],"depth":3,"refund":0,"opName":"SSTORE"} +{"pc":9,"op":127,"gas":"0x3df13","gasCost":"0x3","memSize":0,"stack":[],"depth":3,"refund":0,"opName":"PUSH32"} +{"pc":42,"op":96,"gas":"0x3df10","gasCost":"0x3","memSize":0,"stack":["0x600160045560006004556000600060006000600060f95af25060006000600060"],"depth":3,"refund":0,"opName":"PUSH1"} +{"pc":44,"op":82,"gas":"0x3df0d","gasCost":"0x6","memSize":0,"stack":["0x600160045560006004556000600060006000600060f95af25060006000600060","0x0"],"depth":3,"refund":0,"opName":"MSTORE"} +{"pc":45,"op":126,"gas":"0x3df07","gasCost":"0x3","memSize":32,"stack":[],"depth":3,"refund":0,"opName":"PUSH31"} +{"pc":77,"op":96,"gas":"0x3df04","gasCost":"0x3","memSize":32,"stack":["0x60f45af4506000600060006000600060f55af150f001075205846a44a28344"],"depth":3,"refund":0,"opName":"PUSH1"} +{"pc":79,"op":82,"gas":"0x3df01","gasCost":"0x6","memSize":32,"stack":["0x60f45af4506000600060006000600060f55af150f001075205846a44a28344","0x20"],"depth":3,"refund":0,"opName":"MSTORE"} +{"pc":80,"op":127,"gas":"0x3defb","gasCost":"0x3","memSize":64,"stack":[],"depth":3,"refund":0,"opName":"PUSH32"} +{"pc":113,"op":96,"gas":"0x3def8","gasCost":"0x3","memSize":64,"stack":["0x8ca2600060006000600060045af450519930847f3b631c54a49b5f6003545032"],"depth":3,"refund":0,"opName":"PUSH1"} +{"pc":115,"op":82,"gas":"0x3def5","gasCost":"0x6","memSize":64,"stack":["0x8ca2600060006000600060045af450519930847f3b631c54a49b5f6003545032","0x40"],"depth":3,"refund":0,"opName":"MSTORE"} +{"pc":116,"op":127,"gas":"0x3deef","gasCost":"0x3","memSize":96,"stack":[],"depth":3,"refund":0,"opName":"PUSH32"} +{"pc":149,"op":96,"gas":"0x3deec","gasCost":"0x3","memSize":96,"stack":["0x77306b60006000600060006000600c5af150600060006000600060f85af45050"],"depth":3,"refund":0,"opName":"PUSH1"} +{"pc":151,"op":82,"gas":"0x3dee9","gasCost":"0x6","memSize":96,"stack":["0x77306b60006000600060006000600c5af150600060006000600060f85af45050","0x60"],"depth":3,"refund":0,"opName":"MSTORE"} +{"pc":152,"op":127,"gas":"0x3dee3","gasCost":"0x3","memSize":128,"stack":[],"depth":3,"refund":0,"opName":"PUSH32"} +{"pc":185,"op":96,"gas":"0x3dee0","gasCost":"0x3","memSize":128,"stack":["0x6600160025560035450600060005560006001556c3394fff4607f7f1684317b"],"depth":3,"refund":0,"opName":"PUSH1"} +{"pc":187,"op":82,"gas":"0x3dedd","gasCost":"0x6","memSize":128,"stack":["0x6600160025560035450600060005560006001556c3394fff4607f7f1684317b","0x80"],"depth":3,"refund":0,"opName":"MSTORE"} +{"pc":188,"op":127,"gas":"0x3ded7","gasCost":"0x3","memSize":160,"stack":[],"depth":3,"refund":0,"opName":"PUSH32"} +{"pc":221,"op":96,"gas":"0x3ded4","gasCost":"0x3","memSize":160,"stack":["0x387b9f1920700184809d60015450011899016e6009ff60026001556000527f9f"],"depth":3,"refund":0,"opName":"PUSH1"} +{"pc":223,"op":82,"gas":"0x3ded1","gasCost":"0x6","memSize":160,"stack":["0x387b9f1920700184809d60015450011899016e6009ff60026001556000527f9f","0xa0"],"depth":3,"refund":0,"opName":"MSTORE"} +{"pc":224,"op":127,"gas":"0x3decb","gasCost":"0x3","memSize":192,"stack":[],"depth":3,"refund":0,"opName":"PUSH32"} +{"pc":257,"op":96,"gas":"0x3dec8","gasCost":"0x3","memSize":192,"stack":["0x6000527f9981600160045582600eff600060006000600060f65af45060006060"],"depth":3,"refund":0,"opName":"PUSH1"} +{"pc":259,"op":82,"gas":"0x3dec5","gasCost":"0x6","memSize":192,"stack":["0x6000527f9981600160045582600eff600060006000600060f65af45060006060","0xc0"],"depth":3,"refund":0,"opName":"MSTORE"} +{"pc":260,"op":127,"gas":"0x3debf","gasCost":"0x3","memSize":224,"stack":[],"depth":3,"refund":0,"opName":"PUSH32"} +{"pc":293,"op":96,"gas":"0x3debc","gasCost":"0x3","memSize":224,"stack":["0x20527e600060006020527f60f75af4501d7f1903166660006000600060006000"],"depth":3,"refund":0,"opName":"PUSH1"} +{"pc":295,"op":82,"gas":"0x3deb9","gasCost":"0x6","memSize":224,"stack":["0x20527e600060006020527f60f75af4501d7f1903166660006000600060006000","0xe0"],"depth":3,"refund":0,"opName":"MSTORE"} +{"pc":296,"op":127,"gas":"0x3deb3","gasCost":"0x3","memSize":256,"stack":[],"depth":3,"refund":0,"opName":"PUSH32"} +{"pc":329,"op":97,"gas":"0x3deb0","gasCost":"0x3","memSize":256,"stack":["0x60046040527f5af1506000600060006040527f600060095af4503c95138e5b8f"],"depth":3,"refund":0,"opName":"PUSH2"} +{"pc":332,"op":82,"gas":"0x3dead","gasCost":"0x6","memSize":256,"stack":["0x60046040527f5af1506000600060006040527f600060095af4503c95138e5b8f","0x100"],"depth":3,"refund":0,"opName":"MSTORE"} +{"pc":333,"op":127,"gas":"0x3dea7","gasCost":"0x3","memSize":288,"stack":[],"depth":3,"refund":0,"opName":"PUSH32"} +{"pc":366,"op":97,"gas":"0x3dea4","gasCost":"0x3","memSize":288,"stack":["0x7f605a6000536060527f6031600153606b6002536010606060527f0353604560"],"depth":3,"refund":0,"opName":"PUSH2"} +{"pc":369,"op":82,"gas":"0x3dea1","gasCost":"0x6","memSize":288,"stack":["0x7f605a6000536060527f6031600153606b6002536010606060527f0353604560","0x120"],"depth":3,"refund":0,"opName":"MSTORE"} +{"pc":370,"op":127,"gas":"0x3de9b","gasCost":"0x3","memSize":320,"stack":[],"depth":3,"refund":0,"opName":"PUSH32"} +{"pc":403,"op":97,"gas":"0x3de98","gasCost":"0x3","memSize":320,"stack":["0x45360606005536001606080527e527f60065360606007536002600853606080"],"depth":3,"refund":0,"opName":"PUSH2"} +{"pc":406,"op":82,"gas":"0x3de95","gasCost":"0x6","memSize":320,"stack":["0x45360606005536001606080527e527f60065360606007536002600853606080","0x140"],"depth":3,"refund":0,"opName":"MSTORE"} +{"pc":407,"op":127,"gas":"0x3de8f","gasCost":"0x3","memSize":352,"stack":[],"depth":3,"refund":0,"opName":"PUSH32"} +{"pc":440,"op":97,"gas":"0x3de8c","gasCost":"0x3","memSize":352,"stack":["0x527f556009536060600a53600160a0527f600b536060600c6020527f53600060"],"depth":3,"refund":0,"opName":"PUSH2"} +{"pc":443,"op":82,"gas":"0x3de89","gasCost":"0x6","memSize":352,"stack":["0x527f556009536060600a53600160a0527f600b536060600c6020527f53600060","0x160"],"depth":3,"refund":0,"opName":"MSTORE"} +{"pc":444,"op":127,"gas":"0x3de83","gasCost":"0x3","memSize":384,"stack":[],"depth":3,"refund":0,"opName":"PUSH32"} +{"pc":477,"op":97,"gas":"0x3de80","gasCost":"0x3","memSize":384,"stack":["0xd536055600e60a0527f536060600f536060c0527f0160105360606011536002"],"depth":3,"refund":0,"opName":"PUSH2"} +{"pc":480,"op":82,"gas":"0x3de7d","gasCost":"0x6","memSize":384,"stack":["0xd536055600e60a0527f536060600f536060c0527f0160105360606011536002","0x180"],"depth":3,"refund":0,"opName":"MSTORE"} +{"pc":481,"op":127,"gas":"0x3de77","gasCost":"0x3","memSize":416,"stack":[],"depth":3,"refund":0,"opName":"PUSH32"} +{"pc":514,"op":97,"gas":"0x3de74","gasCost":"0x3","memSize":416,"stack":["0x601253606040527f55601353606060c0527f60145360e0527f60006015536060"],"depth":3,"refund":0,"opName":"PUSH2"} +{"pc":517,"op":82,"gas":"0x3de71","gasCost":"0x6","memSize":416,"stack":["0x601253606040527f55601353606060c0527f60145360e0527f60006015536060","0x1a0"],"depth":3,"refund":0,"opName":"MSTORE"} +{"pc":518,"op":127,"gas":"0x3de6b","gasCost":"0x3","memSize":448,"stack":[],"depth":3,"refund":0,"opName":"PUSH32"} +{"pc":551,"op":97,"gas":"0x3de68","gasCost":"0x3","memSize":448,"stack":["0x601653600060175360f360185360196060605260006060e052610100527f7f80"],"depth":3,"refund":0,"opName":"PUSH2"} +{"pc":554,"op":82,"gas":"0x3de65","gasCost":"0x6","memSize":448,"stack":["0x601653600060175360f360185360196060605260006060e052610100527f7f80","0x1c0"],"depth":3,"refund":0,"opName":"MSTORE"} +{"pc":555,"op":127,"gas":"0x3de5f","gasCost":"0x3","memSize":480,"stack":[],"depth":3,"refund":0,"opName":"PUSH32"} +{"pc":588,"op":97,"gas":"0x3de5c","gasCost":"0x3","memSize":480,"stack":["0x5360f3608153608260006000f06000600060006000845af45050600060006101"],"depth":3,"refund":0,"opName":"PUSH2"} +{"pc":591,"op":82,"gas":"0x3de59","gasCost":"0x6","memSize":480,"stack":["0x5360f3608153608260006000f06000600060006000845af45050600060006101","0x1e0"],"depth":3,"refund":0,"opName":"MSTORE"} +{"pc":592,"op":127,"gas":"0x3de53","gasCost":"0x3","memSize":512,"stack":[],"depth":3,"refund":0,"opName":"PUSH32"} +{"pc":625,"op":97,"gas":"0x3de50","gasCost":"0x3","memSize":512,"stack":["0x20527f60610100527e6000600060035af15060005450c760006002551309f562"],"depth":3,"refund":0,"opName":"PUSH2"} +{"pc":628,"op":82,"gas":"0x3de4d","gasCost":"0x6","memSize":512,"stack":["0x20527f60610100527e6000600060035af15060005450c760006002551309f562","0x200"],"depth":3,"refund":0,"opName":"MSTORE"} +{"pc":629,"op":127,"gas":"0x3de47","gasCost":"0x3","memSize":544,"stack":[],"depth":3,"refund":0,"opName":"PUSH32"} +{"pc":662,"op":97,"gas":"0x3de44","gasCost":"0x3","memSize":544,"stack":["0x66a486610140527f6b001d4571610120527f600054501c641d373c7f60045450"],"depth":3,"refund":0,"opName":"PUSH2"} +{"pc":665,"op":82,"gas":"0x3de41","gasCost":"0x6","memSize":544,"stack":["0x66a486610140527f6b001d4571610120527f600054501c641d373c7f60045450","0x220"],"depth":3,"refund":0,"opName":"MSTORE"} +{"pc":666,"op":127,"gas":"0x3de3b","gasCost":"0x3","memSize":576,"stack":[],"depth":3,"refund":0,"opName":"PUSH32"} +{"pc":699,"op":97,"gas":"0x3de38","gasCost":"0x3","memSize":576,"stack":["0x6000600155600554610160527f50600160025560085450610140527f60006002"],"depth":3,"refund":0,"opName":"PUSH2"} +{"pc":702,"op":82,"gas":"0x3de35","gasCost":"0x6","memSize":576,"stack":["0x6000600155600554610160527f50600160025560085450610140527f60006002","0x240"],"depth":3,"refund":0,"opName":"MSTORE"} +{"pc":703,"op":127,"gas":"0x3de2f","gasCost":"0x3","memSize":608,"stack":[],"depth":3,"refund":0,"opName":"PUSH32"} +{"pc":736,"op":97,"gas":"0x3de2c","gasCost":"0x3","memSize":608,"stack":["0x557fd86000606000527e600060610180527e600060005af15086121714514735"],"depth":3,"refund":0,"opName":"PUSH2"} +{"pc":739,"op":82,"gas":"0x3de29","gasCost":"0x6","memSize":608,"stack":["0x557fd86000606000527e600060610180527e600060005af15086121714514735","0x260"],"depth":3,"refund":0,"opName":"MSTORE"} +{"pc":740,"op":127,"gas":"0x3de23","gasCost":"0x3","memSize":640,"stack":[],"depth":3,"refund":0,"opName":"PUSH32"} +{"pc":773,"op":97,"gas":"0x3de20","gasCost":"0x3","memSize":640,"stack":["0x610160527f5198a37e127a7efa7c6000526101a0527f606020527f6060205360"],"depth":3,"refund":0,"opName":"PUSH2"} +{"pc":776,"op":82,"gas":"0x3de1d","gasCost":"0x6","memSize":640,"stack":["0x610160527f5198a37e127a7efa7c6000526101a0527f606020527f6060205360","0x280"],"depth":3,"refund":0,"opName":"MSTORE"} +{"pc":777,"op":127,"gas":"0x3de17","gasCost":"0x3","memSize":672,"stack":[],"depth":3,"refund":0,"opName":"PUSH32"} +{"pc":810,"op":97,"gas":"0x3de14","gasCost":"0x3","memSize":672,"stack":["0xf760215360ff60225360610180527fdb6023536037606101c0527f2453607560"],"depth":3,"refund":0,"opName":"PUSH2"} +{"pc":813,"op":82,"gas":"0x3de11","gasCost":"0x6","memSize":672,"stack":["0xf760215360ff60225360610180527fdb6023536037606101c0527f2453607560","0x2a0"],"depth":3,"refund":0,"opName":"MSTORE"} +{"pc":814,"op":127,"gas":"0x3de0b","gasCost":"0x3","memSize":704,"stack":[],"depth":3,"refund":0,"opName":"PUSH32"} +{"pc":847,"op":97,"gas":"0x3de08","gasCost":"0x3","memSize":704,"stack":["0x2553609f606040527f265360fe602753608f60286101a0527f53606101e0527f"],"depth":3,"refund":0,"opName":"PUSH2"} +{"pc":850,"op":82,"gas":"0x3de05","gasCost":"0x7","memSize":704,"stack":["0x2553609f606040527f265360fe602753608f60286101a0527f53606101e0527f","0x2c0"],"depth":3,"refund":0,"opName":"MSTORE"} +{"pc":851,"op":127,"gas":"0x3ddfe","gasCost":"0x3","memSize":736,"stack":[],"depth":3,"refund":0,"opName":"PUSH32"} +{"pc":884,"op":97,"gas":"0x3ddfb","gasCost":"0x3","memSize":736,"stack":["0xb6029536060602a536000602b536060602c5360605260606080536000606101"],"depth":3,"refund":0,"opName":"PUSH2"} +{"pc":887,"op":82,"gas":"0x3ddf8","gasCost":"0x6","memSize":736,"stack":["0xb6029536060602a536000602b536060602c5360605260606080536000606101","0x2e0"],"depth":3,"refund":0,"opName":"MSTORE"} +{"pc":888,"op":127,"gas":"0x3ddf2","gasCost":"0x3","memSize":768,"stack":[],"depth":3,"refund":0,"opName":"PUSH32"} +{"pc":921,"op":97,"gas":"0x3ddef","gasCost":"0x3","memSize":768,"stack":["0x610200527fc0527f81536060608253602d6083536053608453606060855360fd"],"depth":3,"refund":0,"opName":"PUSH2"} +{"pc":924,"op":82,"gas":"0x3ddec","gasCost":"0x6","memSize":768,"stack":["0x610200527fc0527f81536060608253602d6083536053608453606060855360fd","0x300"],"depth":3,"refund":0,"opName":"MSTORE"} +{"pc":925,"op":127,"gas":"0x3dde6","gasCost":"0x3","memSize":800,"stack":[],"depth":3,"refund":0,"opName":"PUSH32"} +{"pc":958,"op":97,"gas":"0x3dde3","gasCost":"0x3","memSize":800,"stack":["0x6086536060610220527f6087536101e0527f602e60885360536089536060608a"],"depth":3,"refund":0,"opName":"PUSH2"} +{"pc":961,"op":82,"gas":"0x3dde0","gasCost":"0x6","memSize":800,"stack":["0x6086536060610220527f6087536101e0527f602e60885360536089536060608a","0x320"],"depth":3,"refund":0,"opName":"MSTORE"} +{"pc":962,"op":127,"gas":"0x3ddda","gasCost":"0x3","memSize":832,"stack":[],"depth":3,"refund":0,"opName":"PUSH32"} +{"pc":995,"op":97,"gas":"0x3ddd7","gasCost":"0x3","memSize":832,"stack":["0x53602f608b536060608c610240527f536000608d5360f3610200526060610220"],"depth":3,"refund":0,"opName":"PUSH2"} +{"pc":998,"op":82,"gas":"0x3ddd4","gasCost":"0x6","memSize":832,"stack":["0x53602f608b536060608c610240527f536000608d5360f3610200526060610220","0x340"],"depth":3,"refund":0,"opName":"MSTORE"} +{"pc":999,"op":127,"gas":"0x3ddce","gasCost":"0x3","memSize":864,"stack":[],"depth":3,"refund":0,"opName":"PUSH32"} +{"pc":1032,"op":97,"gas":"0x3ddcb","gasCost":"0x3","memSize":864,"stack":["0x53608e610221536053610222536060610260527f610223536000610224536060"],"depth":3,"refund":0,"opName":"PUSH2"} +{"pc":1035,"op":82,"gas":"0x3ddc8","gasCost":"0x6","memSize":864,"stack":["0x53608e610221536053610222536060610260527f610223536000610224536060","0x360"],"depth":3,"refund":0,"opName":"MSTORE"} +{"pc":1036,"op":127,"gas":"0x3ddc2","gasCost":"0x3","memSize":896,"stack":[],"depth":3,"refund":0,"opName":"PUSH32"} +{"pc":1069,"op":97,"gas":"0x3ddbf","gasCost":"0x3","memSize":896,"stack":["0x61022553608f6102265360606102275360006102610280527f28536060610229"],"depth":3,"refund":0,"opName":"PUSH2"} +{"pc":1072,"op":82,"gas":"0x3ddbc","gasCost":"0x6","memSize":896,"stack":["0x61022553608f6102265360606102275360006102610280527f28536060610229","0x380"],"depth":3,"refund":0,"opName":"MSTORE"} +{"pc":1073,"op":127,"gas":"0x3ddb6","gasCost":"0x3","memSize":928,"stack":[],"depth":3,"refund":0,"opName":"PUSH32"} +{"pc":1106,"op":97,"gas":"0x3ddb3","gasCost":"0x3","memSize":928,"stack":["0x53600061022a5360f561022b53606061022c53600061022d536102a0527f6060"],"depth":3,"refund":0,"opName":"PUSH2"} +{"pc":1109,"op":82,"gas":"0x3ddb0","gasCost":"0x6","memSize":928,"stack":["0x53600061022a5360f561022b53606061022c53600061022d536102a0527f6060","0x3a0"],"depth":3,"refund":0,"opName":"MSTORE"} +{"pc":1110,"op":127,"gas":"0x3ddaa","gasCost":"0x3","memSize":960,"stack":[],"depth":3,"refund":0,"opName":"PUSH32"} +{"pc":1143,"op":97,"gas":"0x3dda7","gasCost":"0x3","memSize":960,"stack":["0x61022e53600061022f5360606102305360006102315360606102325360006102"],"depth":3,"refund":0,"opName":"PUSH2"} +{"pc":1146,"op":82,"gas":"0x3dda4","gasCost":"0x6","memSize":960,"stack":["0x61022e53600061022f5360606102305360006102315360606102325360006102","0x3c0"],"depth":3,"refund":0,"opName":"MSTORE"} +{"pc":1147,"op":127,"gas":"0x3dd9e","gasCost":"0x3","memSize":992,"stack":[],"depth":3,"refund":0,"opName":"PUSH32"} +{"pc":1180,"op":97,"gas":"0x3dd9b","gasCost":"0x3","memSize":992,"stack":["0xc0527f61023353606061023453600061023553608561023653605a6102375360"],"depth":3,"refund":0,"opName":"PUSH2"} +{"pc":1183,"op":82,"gas":"0x3dd98","gasCost":"0x7","memSize":992,"stack":["0xc0527f61023353606061023453600061023553608561023653605a6102375360","0x3e0"],"depth":3,"refund":0,"opName":"MSTORE"} +{"pc":1184,"op":127,"gas":"0x3dd91","gasCost":"0x3","memSize":1024,"stack":[],"depth":3,"refund":0,"opName":"PUSH32"} +{"pc":1217,"op":97,"gas":"0x3dd8e","gasCost":"0x3","memSize":1024,"stack":["0xf261026102e05260386103005360536103015360606103025360506103035360"],"depth":3,"refund":0,"opName":"PUSH2"} +{"pc":1220,"op":82,"gas":"0x3dd8b","gasCost":"0x6","memSize":1024,"stack":["0xf261026102e05260386103005360536103015360606103025360506103035360","0x400"],"depth":3,"refund":0,"opName":"MSTORE"} +{"pc":1221,"op":127,"gas":"0x3dd85","gasCost":"0x3","memSize":1056,"stack":[],"depth":3,"refund":0,"opName":"PUSH32"} +{"pc":1254,"op":97,"gas":"0x3dd82","gasCost":"0x3","memSize":1056,"stack":["0x6161030453600261030553603961030653605361030753606061030853605061"],"depth":3,"refund":0,"opName":"PUSH2"} +{"pc":1257,"op":82,"gas":"0x3dd7f","gasCost":"0x6","memSize":1056,"stack":["0x6161030453600261030553603961030653605361030753606061030853605061","0x420"],"depth":3,"refund":0,"opName":"MSTORE"} +{"pc":1258,"op":127,"gas":"0x3dd79","gasCost":"0x3","memSize":1088,"stack":[],"depth":3,"refund":0,"opName":"PUSH32"} +{"pc":1291,"op":97,"gas":"0x3dd76","gasCost":"0x3","memSize":1088,"stack":["0x30953606161030a53600261030b53603a61030c53605361030d53606161030e"],"depth":3,"refund":0,"opName":"PUSH2"} +{"pc":1294,"op":82,"gas":"0x3dd73","gasCost":"0x6","memSize":1088,"stack":["0x30953606161030a53600261030b53603a61030c53605361030d53606161030e","0x440"],"depth":3,"refund":0,"opName":"MSTORE"} +{"pc":1295,"op":127,"gas":"0x3dd6d","gasCost":"0x3","memSize":1120,"stack":[],"depth":3,"refund":0,"opName":"PUSH32"} +{"pc":1328,"op":97,"gas":"0x3dd6a","gasCost":"0x3","memSize":1120,"stack":["0x53600261030f53603b6103105360606103115360006103125360f36103135361"],"depth":3,"refund":0,"opName":"PUSH2"} +{"pc":1331,"op":82,"gas":"0x3dd67","gasCost":"0x6","memSize":1120,"stack":["0x53600261030f53603b6103105360606103115360006103125360f36103135361","0x460"],"depth":3,"refund":0,"opName":"MSTORE"} +{"pc":1332,"op":96,"gas":"0x3dd61","gasCost":"0x3","memSize":1152,"stack":[],"depth":3,"refund":0,"opName":"PUSH1"} +{"pc":1334,"op":97,"gas":"0x3dd5e","gasCost":"0x3","memSize":1152,"stack":["0x3"],"depth":3,"refund":0,"opName":"PUSH2"} +{"pc":1337,"op":83,"gas":"0x3dd5b","gasCost":"0x6","memSize":1152,"stack":["0x3","0x480"],"depth":3,"refund":0,"opName":"MSTORE8"} +{"pc":1338,"op":96,"gas":"0x3dd55","gasCost":"0x3","memSize":1184,"stack":[],"depth":3,"refund":0,"opName":"PUSH1"} +{"pc":1340,"op":97,"gas":"0x3dd52","gasCost":"0x3","memSize":1184,"stack":["0x14"],"depth":3,"refund":0,"opName":"PUSH2"} +{"pc":1343,"op":83,"gas":"0x3dd4f","gasCost":"0x3","memSize":1184,"stack":["0x14","0x481"],"depth":3,"refund":0,"opName":"MSTORE8"} +{"pc":1344,"op":96,"gas":"0x3dd4c","gasCost":"0x3","memSize":1184,"stack":[],"depth":3,"refund":0,"opName":"PUSH1"} +{"pc":1346,"op":97,"gas":"0x3dd49","gasCost":"0x3","memSize":1184,"stack":["0x60"],"depth":3,"refund":0,"opName":"PUSH2"} +{"pc":1349,"op":83,"gas":"0x3dd46","gasCost":"0x3","memSize":1184,"stack":["0x60","0x482"],"depth":3,"refund":0,"opName":"MSTORE8"} +{"pc":1350,"op":96,"gas":"0x3dd43","gasCost":"0x3","memSize":1184,"stack":[],"depth":3,"refund":0,"opName":"PUSH1"} +{"pc":1352,"op":97,"gas":"0x3dd40","gasCost":"0x3","memSize":1184,"stack":["0x0"],"depth":3,"refund":0,"opName":"PUSH2"} +{"pc":1355,"op":83,"gas":"0x3dd3d","gasCost":"0x3","memSize":1184,"stack":["0x0","0x483"],"depth":3,"refund":0,"opName":"MSTORE8"} +{"pc":1356,"op":96,"gas":"0x3dd3a","gasCost":"0x3","memSize":1184,"stack":[],"depth":3,"refund":0,"opName":"PUSH1"} +{"pc":1358,"op":97,"gas":"0x3dd37","gasCost":"0x3","memSize":1184,"stack":["0x60"],"depth":3,"refund":0,"opName":"PUSH2"} +{"pc":1361,"op":83,"gas":"0x3dd34","gasCost":"0x3","memSize":1184,"stack":["0x60","0x484"],"depth":3,"refund":0,"opName":"MSTORE8"} +{"pc":1362,"op":96,"gas":"0x3dd31","gasCost":"0x3","memSize":1184,"stack":[],"depth":3,"refund":0,"opName":"PUSH1"} +{"pc":1364,"op":97,"gas":"0x3dd2e","gasCost":"0x3","memSize":1184,"stack":["0x0"],"depth":3,"refund":0,"opName":"PUSH2"} +{"pc":1367,"op":83,"gas":"0x3dd2b","gasCost":"0x3","memSize":1184,"stack":["0x0","0x485"],"depth":3,"refund":0,"opName":"MSTORE8"} +{"pc":1368,"op":96,"gas":"0x3dd28","gasCost":"0x3","memSize":1184,"stack":[],"depth":3,"refund":0,"opName":"PUSH1"} +{"pc":1370,"op":97,"gas":"0x3dd25","gasCost":"0x3","memSize":1184,"stack":["0xf0"],"depth":3,"refund":0,"opName":"PUSH2"} +{"pc":1373,"op":83,"gas":"0x3dd22","gasCost":"0x3","memSize":1184,"stack":["0xf0","0x486"],"depth":3,"refund":0,"opName":"MSTORE8"} +{"pc":1374,"op":96,"gas":"0x3dd1f","gasCost":"0x3","memSize":1184,"stack":[],"depth":3,"refund":0,"opName":"PUSH1"} +{"pc":1376,"op":97,"gas":"0x3dd1c","gasCost":"0x3","memSize":1184,"stack":["0x60"],"depth":3,"refund":0,"opName":"PUSH2"} +{"pc":1379,"op":83,"gas":"0x3dd19","gasCost":"0x3","memSize":1184,"stack":["0x60","0x487"],"depth":3,"refund":0,"opName":"MSTORE8"} +{"pc":1380,"op":96,"gas":"0x3dd16","gasCost":"0x3","memSize":1184,"stack":[],"depth":3,"refund":0,"opName":"PUSH1"} +{"pc":1382,"op":97,"gas":"0x3dd13","gasCost":"0x3","memSize":1184,"stack":["0x0"],"depth":3,"refund":0,"opName":"PUSH2"} +{"pc":1385,"op":83,"gas":"0x3dd10","gasCost":"0x3","memSize":1184,"stack":["0x0","0x488"],"depth":3,"refund":0,"opName":"MSTORE8"} +{"pc":1386,"op":96,"gas":"0x3dd0d","gasCost":"0x3","memSize":1184,"stack":[],"depth":3,"refund":0,"opName":"PUSH1"} +{"pc":1388,"op":97,"gas":"0x3dd0a","gasCost":"0x3","memSize":1184,"stack":["0x60"],"depth":3,"refund":0,"opName":"PUSH2"} +{"pc":1391,"op":83,"gas":"0x3dd07","gasCost":"0x3","memSize":1184,"stack":["0x60","0x489"],"depth":3,"refund":0,"opName":"MSTORE8"} +{"pc":1392,"op":96,"gas":"0x3dd04","gasCost":"0x3","memSize":1184,"stack":[],"depth":3,"refund":0,"opName":"PUSH1"} +{"pc":1394,"op":97,"gas":"0x3dd01","gasCost":"0x3","memSize":1184,"stack":["0x0"],"depth":3,"refund":0,"opName":"PUSH2"} +{"pc":1397,"op":83,"gas":"0x3dcfe","gasCost":"0x3","memSize":1184,"stack":["0x0","0x48a"],"depth":3,"refund":0,"opName":"MSTORE8"} +{"pc":1398,"op":96,"gas":"0x3dcfb","gasCost":"0x3","memSize":1184,"stack":[],"depth":3,"refund":0,"opName":"PUSH1"} +{"pc":1400,"op":97,"gas":"0x3dcf8","gasCost":"0x3","memSize":1184,"stack":["0x60"],"depth":3,"refund":0,"opName":"PUSH2"} +{"pc":1403,"op":83,"gas":"0x3dcf5","gasCost":"0x3","memSize":1184,"stack":["0x60","0x48b"],"depth":3,"refund":0,"opName":"MSTORE8"} +{"pc":1404,"op":96,"gas":"0x3dcf2","gasCost":"0x3","memSize":1184,"stack":[],"depth":3,"refund":0,"opName":"PUSH1"} +{"pc":1406,"op":97,"gas":"0x3dcef","gasCost":"0x3","memSize":1184,"stack":["0x0"],"depth":3,"refund":0,"opName":"PUSH2"} +{"pc":1409,"op":83,"gas":"0x3dcec","gasCost":"0x3","memSize":1184,"stack":["0x0","0x48c"],"depth":3,"refund":0,"opName":"MSTORE8"} +{"pc":1410,"op":96,"gas":"0x3dce9","gasCost":"0x3","memSize":1184,"stack":[],"depth":3,"refund":0,"opName":"PUSH1"} +{"pc":1412,"op":97,"gas":"0x3dce6","gasCost":"0x3","memSize":1184,"stack":["0x60"],"depth":3,"refund":0,"opName":"PUSH2"} +{"pc":1415,"op":83,"gas":"0x3dce3","gasCost":"0x3","memSize":1184,"stack":["0x60","0x48d"],"depth":3,"refund":0,"opName":"MSTORE8"} +{"pc":1416,"op":96,"gas":"0x3dce0","gasCost":"0x3","memSize":1184,"stack":[],"depth":3,"refund":0,"opName":"PUSH1"} +{"pc":1418,"op":97,"gas":"0x3dcdd","gasCost":"0x3","memSize":1184,"stack":["0x0"],"depth":3,"refund":0,"opName":"PUSH2"} +{"pc":1421,"op":83,"gas":"0x3dcda","gasCost":"0x3","memSize":1184,"stack":["0x0","0x48e"],"depth":3,"refund":0,"opName":"MSTORE8"} +{"pc":1422,"op":96,"gas":"0x3dcd7","gasCost":"0x3","memSize":1184,"stack":[],"depth":3,"refund":0,"opName":"PUSH1"} +{"pc":1424,"op":97,"gas":"0x3dcd4","gasCost":"0x3","memSize":1184,"stack":["0x84"],"depth":3,"refund":0,"opName":"PUSH2"} +{"pc":1427,"op":83,"gas":"0x3dcd1","gasCost":"0x3","memSize":1184,"stack":["0x84","0x48f"],"depth":3,"refund":0,"opName":"MSTORE8"} +{"pc":1428,"op":96,"gas":"0x3dcce","gasCost":"0x3","memSize":1184,"stack":[],"depth":3,"refund":0,"opName":"PUSH1"} +{"pc":1430,"op":97,"gas":"0x3dccb","gasCost":"0x3","memSize":1184,"stack":["0x5a"],"depth":3,"refund":0,"opName":"PUSH2"} +{"pc":1433,"op":83,"gas":"0x3dcc8","gasCost":"0x3","memSize":1184,"stack":["0x5a","0x490"],"depth":3,"refund":0,"opName":"MSTORE8"} +{"pc":1434,"op":96,"gas":"0x3dcc5","gasCost":"0x3","memSize":1184,"stack":[],"depth":3,"refund":0,"opName":"PUSH1"} +{"pc":1436,"op":97,"gas":"0x3dcc2","gasCost":"0x3","memSize":1184,"stack":["0xf4"],"depth":3,"refund":0,"opName":"PUSH2"} +{"pc":1439,"op":83,"gas":"0x3dcbf","gasCost":"0x3","memSize":1184,"stack":["0xf4","0x491"],"depth":3,"refund":0,"opName":"MSTORE8"} +{"pc":1440,"op":96,"gas":"0x3dcbc","gasCost":"0x3","memSize":1184,"stack":[],"depth":3,"refund":0,"opName":"PUSH1"} +{"pc":1442,"op":97,"gas":"0x3dcb9","gasCost":"0x3","memSize":1184,"stack":["0x50"],"depth":3,"refund":0,"opName":"PUSH2"} +{"pc":1445,"op":83,"gas":"0x3dcb6","gasCost":"0x3","memSize":1184,"stack":["0x50","0x492"],"depth":3,"refund":0,"opName":"MSTORE8"} +{"pc":1446,"op":96,"gas":"0x3dcb3","gasCost":"0x3","memSize":1184,"stack":[],"depth":3,"refund":0,"opName":"PUSH1"} +{"pc":1448,"op":97,"gas":"0x3dcb0","gasCost":"0x3","memSize":1184,"stack":["0x50"],"depth":3,"refund":0,"opName":"PUSH2"} +{"pc":1451,"op":83,"gas":"0x3dcad","gasCost":"0x3","memSize":1184,"stack":["0x50","0x493"],"depth":3,"refund":0,"opName":"MSTORE8"} +{"pc":1452,"op":97,"gas":"0x3dcaa","gasCost":"0x3","memSize":1184,"stack":[],"depth":3,"refund":0,"opName":"PUSH2"} +{"pc":1455,"op":96,"gas":"0x3dca7","gasCost":"0x3","memSize":1184,"stack":["0x494"],"depth":3,"refund":0,"opName":"PUSH1"} +{"pc":1457,"op":243,"gas":"0x3dca4","gasCost":"0x0","memSize":1184,"stack":["0x494","0x0"],"depth":3,"refund":0,"opName":"RETURN"} +{"output":"600160045560006004556000600060006000600060f95af250600060006000600060f45af4506000600060006000600060f55af150f001075205846a44a283448ca2600060006000600060045af450519930847f3b631c54a49b5f600354503277306b60006000600060006000600c5af150600060006000600060f85af4505006600160025560035450600060005560006001556c3394fff4607f7f1684317b387b9f1920700184809d60015450011899016e6009ff60026001556000527f9f6000527f9981600160045582600eff600060006000600060f65af4506000606020527e600060006020527f60f75af4501d7f190316666000600060006000600060046040527f5af1506000600060006040527f600060095af4503c95138e5b8f7f605a6000536060527f6031600153606b6002536010606060527f0353604560045360606005536001606080527e527f60065360606007536002600853606080527f556009536060600a53600160a0527f600b536060600c6020527f536000600d536055600e60a0527f536060600f536060c0527f0160105360606011536002601253606040527f55601353606060c0527f60145360e0527f60006015536060601653600060175360f360185360196060605260006060e052610100527f7f805360f3608153608260006000f06000600060006000845af4505060006000610120527f60610100527e6000600060035af15060005450c760006002551309f56266a486610140527f6b001d4571610120527f600054501c641d373c7f600454506000600155600554610160527f50600160025560085450610140527f60006002557fd86000606000527e600060610180527e600060005af15086121714514735610160527f5198a37e127a7efa7c6000526101a0527f606020527f6060205360f760215360ff60225360610180527fdb6023536037606101c0527f24536075602553609f606040527f265360fe602753608f60286101a0527f53606101e0527f0b6029536060602a536000602b536060602c5360605260606080536000606101610200527fc0527f81536060608253602d6083536053608453606060855360fd6086536060610220527f6087536101e0527f602e60885360536089536060608a53602f608b536060608c610240527f536000608d5360f361020052606061022053608e610221536053610222536060610260527f61022353600061022453606061022553608f6102265360606102275360006102610280527f2853606061022953600061022a5360f561022b53606061022c53600061022d536102a0527f606061022e53600061022f5360606102305360006102315360606102325360006102c0527f61023353606061023453600061023553608561023653605a6102375360f261026102e052603861030053605361030153606061030253605061030353606161030453600261030553603961030653605361030753606061030853605061030953606161030a53600261030b53603a61030c53605361030d53606161030e53600261030f53603b6103105360606103115360006103125360f36103135361031460006000f06000600060006000845af45050","gasUsed":"0x3a6e6"} +{"pc":1773,"op":96,"gas":"0x5903","gasCost":"0x3","memSize":1472,"stack":["0x94a843a7335fc63be036fbdecc40b1365f3c5f26"],"depth":2,"refund":0,"opName":"PUSH1"} +{"pc":1775,"op":96,"gas":"0x5900","gasCost":"0x3","memSize":1472,"stack":["0x94a843a7335fc63be036fbdecc40b1365f3c5f26","0x0"],"depth":2,"refund":0,"opName":"PUSH1"} +{"pc":1777,"op":96,"gas":"0x58fd","gasCost":"0x3","memSize":1472,"stack":["0x94a843a7335fc63be036fbdecc40b1365f3c5f26","0x0","0x0"],"depth":2,"refund":0,"opName":"PUSH1"} +{"pc":1779,"op":96,"gas":"0x58fa","gasCost":"0x3","memSize":1472,"stack":["0x94a843a7335fc63be036fbdecc40b1365f3c5f26","0x0","0x0","0x0"],"depth":2,"refund":0,"opName":"PUSH1"} +{"pc":1781,"op":96,"gas":"0x58f7","gasCost":"0x3","memSize":1472,"stack":["0x94a843a7335fc63be036fbdecc40b1365f3c5f26","0x0","0x0","0x0","0x0"],"depth":2,"refund":0,"opName":"PUSH1"} +{"pc":1783,"op":133,"gas":"0x58f4","gasCost":"0x3","memSize":1472,"stack":["0x94a843a7335fc63be036fbdecc40b1365f3c5f26","0x0","0x0","0x0","0x0","0x0"],"depth":2,"refund":0,"opName":"DUP6"} +{"pc":1784,"op":90,"gas":"0x58f1","gasCost":"0x2","memSize":1472,"stack":["0x94a843a7335fc63be036fbdecc40b1365f3c5f26","0x0","0x0","0x0","0x0","0x0","0x94a843a7335fc63be036fbdecc40b1365f3c5f26"],"depth":2,"refund":0,"opName":"GAS"} +{"pc":1785,"op":241,"gas":"0x58ef","gasCost":"0x578d","memSize":1472,"stack":["0x94a843a7335fc63be036fbdecc40b1365f3c5f26","0x0","0x0","0x0","0x0","0x0","0x94a843a7335fc63be036fbdecc40b1365f3c5f26","0x58ef"],"depth":2,"refund":0,"opName":"CALL"} +{"pc":0,"op":96,"gas":"0x5729","gasCost":"0x3","memSize":0,"stack":[],"depth":3,"refund":0,"opName":"PUSH1"} +{"pc":2,"op":96,"gas":"0x5726","gasCost":"0x3","memSize":0,"stack":["0x1"],"depth":3,"refund":0,"opName":"PUSH1"} +{"pc":4,"op":85,"gas":"0x5723","gasCost":"0x4e20","memSize":0,"stack":["0x1","0x4"],"depth":3,"refund":0,"opName":"SSTORE"} +{"pc":5,"op":96,"gas":"0x903","gasCost":"0x3","memSize":0,"stack":[],"depth":3,"refund":0,"opName":"PUSH1"} +{"pc":7,"op":96,"gas":"0x900","gasCost":"0x3","memSize":0,"stack":["0x0"],"depth":3,"refund":0,"opName":"PUSH1"} +{"pc":9,"op":85,"gas":"0x8fd","gasCost":"0x64","memSize":0,"stack":["0x0","0x4"],"depth":3,"refund":19900,"opName":"SSTORE"} +{"pc":10,"op":96,"gas":"0x899","gasCost":"0x3","memSize":0,"stack":[],"depth":3,"refund":19900,"opName":"PUSH1"} +{"pc":12,"op":96,"gas":"0x896","gasCost":"0x3","memSize":0,"stack":["0x0"],"depth":3,"refund":19900,"opName":"PUSH1"} +{"pc":14,"op":96,"gas":"0x893","gasCost":"0x3","memSize":0,"stack":["0x0","0x0"],"depth":3,"refund":19900,"opName":"PUSH1"} +{"pc":16,"op":96,"gas":"0x890","gasCost":"0x3","memSize":0,"stack":["0x0","0x0","0x0"],"depth":3,"refund":19900,"opName":"PUSH1"} +{"pc":18,"op":96,"gas":"0x88d","gasCost":"0x3","memSize":0,"stack":["0x0","0x0","0x0","0x0"],"depth":3,"refund":19900,"opName":"PUSH1"} +{"pc":20,"op":96,"gas":"0x88a","gasCost":"0x3","memSize":0,"stack":["0x0","0x0","0x0","0x0","0x0"],"depth":3,"refund":19900,"opName":"PUSH1"} +{"pc":22,"op":90,"gas":"0x887","gasCost":"0x2","memSize":0,"stack":["0x0","0x0","0x0","0x0","0x0","0xf9"],"depth":3,"refund":19900,"opName":"GAS"} +{"pc":23,"op":242,"gas":"0x885","gasCost":"0x64","memSize":0,"stack":["0x0","0x0","0x0","0x0","0x0","0xf9","0x885"],"depth":3,"refund":19900,"opName":"CALLCODE","error":"out of gas: out of gas"} +{"output":"","gasUsed":"0x5729","error":"out of gas: out of gas"} +{"pc":1786,"op":80,"gas":"0x162","gasCost":"0x2","memSize":1472,"stack":["0x94a843a7335fc63be036fbdecc40b1365f3c5f26","0x0"],"depth":2,"refund":0,"opName":"POP"} +{"pc":1787,"op":80,"gas":"0x160","gasCost":"0x2","memSize":1472,"stack":["0x94a843a7335fc63be036fbdecc40b1365f3c5f26"],"depth":2,"refund":0,"opName":"POP"} +{"pc":1788,"op":0,"gas":"0x15e","gasCost":"0x0","memSize":1472,"stack":[],"depth":2,"refund":0,"opName":"STOP"} +{"output":"","gasUsed":"0x47f70"} +{"pc":2618,"op":80,"gas":"0x13aa","gasCost":"0x2","memSize":2240,"stack":["0x7dce2faf43218578e3fcf2ad22df9918a89e2fba","0x1"],"depth":1,"refund":0,"opName":"POP"} +{"pc":2619,"op":80,"gas":"0x13a8","gasCost":"0x2","memSize":2240,"stack":["0x7dce2faf43218578e3fcf2ad22df9918a89e2fba"],"depth":1,"refund":0,"opName":"POP"} +{"pc":2620,"op":0,"gas":"0x13a6","gasCost":"0x0","memSize":2240,"stack":[],"depth":1,"refund":0,"opName":"STOP"} +{"output":"","gasUsed":"0xb2e6d"} +{"stateRoot": "0xad1024c87b5548e77c937aa50f72b6cb620d278f4dd79bae7f78f71ff75af458"} diff --git a/cmd/evm/testdata/statetest.json b/cmd/evm/testdata/statetest.json new file mode 100755 index 0000000000..3b2a91987a --- /dev/null +++ b/cmd/evm/testdata/statetest.json @@ -0,0 +1,57 @@ +{ + "00000006-naivefuzz-0": { + "env": { + "currentCoinbase": "b94f5374fce5edbc8e2a8697c15331677e6ebf0b", + "currentDifficulty": "0x200000", + "currentGasLimit": "0x26e1f476fe1e22", + "currentNumber": "0x1", + "currentTimestamp": "0x3e8", + "previousHash": "0x0000000000000000000000000000000000000000000000000000000000000000", + "currentBaseFee": "0x10" + }, + "pre": { + "0x00000000000000000000000000000000000000f1": { + "code": "0x60026003556000600060006000600060045af2507f600254506003545060016003557f7f6008545060006004557f600160045560006000527f60045560006000600060006000606000527ff96000527f5af2506000600060006020527f606000527e60f45af45060006000600060006020527f600060f55a6020527ff16040527f50f001075205846a44a283446020527f8ca2600060006040527f6000600060046060527f5af4506040527f519930847f3b631c54a49b5f600354503260406060527f527f6080527f77306b60006000600060006060527f6000600c5af1506000600060006000608060a0527f527f60f85af450506060527f066001600255606080527f03545060006000556060c0527e6060a0527f01556c3394fff4607f7f1684317b6080527f387b9f60a0527f1960e0527f20700184809d60c0527f60015450011899016e6009ff60026001556000527f9f610100527f60a05260c0527f7f600060e0527f527f9981600160045582600eff6000600060610120527e600060f65af45060006060e052610100527f7f6060c0527f20527e60006000610140527f6020527f60f75af4501d7f1903166660006000610120527f60610100527e6000610160527f600060e0527f60046040527f5af150600060006000604052610140527f7f6000610180527f6009610120527f5af4503c95138e5b8f610100527f7f605a60005360606101606101a0527f527f527f6031600153606b60610140527f02536010606060527f0353604560616101c0527f0120610180527f527f04536060600553600160608052610160527f7e527f60066101e0527f536060600753606101a0527f02600853606080610140527f527f556009536060610200527f610180527f600a53600160a06101c0527f527f600b536060600c6020527f5360610220527e60610160527f0d53606101a0527f55606101e0527f0e60a0527f536060600f610240527f536060c0527f01601053606060115360026101806101610200527fc0527f527f610260527f601253606040527f55601353606060c0527f60145360e0527f6000610220527f610280527f6015536101e0527f60606101a0527f601653600060175360f3601853601960606102a0527f610240527f605260006060e052610200527f610100527f7f806101c0527f53606102c0527ff360815360610260527f8260006000f060006000600060610220527e845af4506102e0527f506000600061016101e0610280527f527f20527f60610100527e600060006003610300527f610240527f5af15060005450c760006102a0527f6002551309f562610200527f610320527f66a486610140527f6b00610260527f1d457161016102c0527f20527f60005450610340527f1c641d373c7f60045450610220527f6000600155610280527f6102e0527f6005610360527f54610160527f50600160025560085450610140527f60006002610240527f6103610380527e527f6102a0527f557fd86000606000527e600060610180527e600060005af16103a0527f508612610320527f17145147356102c0527f610260527f610160527f5198a37e6103c0527f127a7efa7c600052610340527f6101a0527f606020527f6102e0527f606020536103e0527f60610280527ff760215360ff60610360527f225360610180527fdb6023536037610400527f60610300527f6101c0527f24536075606102610380527fa0527f2553609f6060610420527f40527f265360fe60275360610320527f8f60286101a0526103a0527f7f536061610440527f01e0527f6102c0527f0b6029536060602a53600060610340527f2b536103c052610460527f7f6060602c53606052606060805360006061016102e0527f610200527fc05261610480527f036103e0527f60527f7f81536060608253602d608353605360845360606085536104a0527f60fd61030052610400527f7f6086610380527f536060610220527f60875361016104c0527fe0527f602e608853605360610420527f89536060608a61036103a0527f20527f6104e0527f53602f608b536060608c610240527f53610440527f6000608d5360f361020052610500527f60606103c0527f610220610340527f53608e610221610460527f536053610222610520527f536060610260527f610223536103e0527f600061022453606061610480527f03610540527f60527f61022553608f61022653606061022753600061610400527f0261028061610560527f04a0527f527f28536060610229610380527f53600061022a5360f561022b5360610580527f610420526104c0527f7f6061022c53600061022d536102a0527f60606103a0526105a0527f7f61022e53600061026104e0527f2f610440527f5360606102305360006102316105c0527f53606061023253600061026103c0610500527f527fc0527f61610460527f02336105e0527f53606061023453600061023553608561023653610520527f605a610237536061610600527f03e052610480527f7ff261026102e0526038610300536053610540527f610301610620527f536060610302536050610303536104a0527f60610400527f6161030453610560610640527f527f6002610305536039610306536053610307536060616104c0527f03085360610660527f5061610580527f610420527f030953606161030a53600261030b53603a61030c610680527f536104e0527f606105a0527f5361030d53606161030e610440527f53600261036106a0527f0f53603b61031053606061616105c0527f0500527f03115360006103125360f36106c0527f61031353616104605260036104805360146105e0527f61048153610520527f606106e0527f60610482536000610483536060610484536000610485610600527f5360f06104610700527f86536060610540527f610487536000610488536060610489536000610620527f610720527f61048a53606061048b5360006104610560527f8c53606061048d53600061048e610740527f610640527f53608461048f53605a6104905360f4610491536105805260606105610760527fa053605061610660527f05a15360616105a25360046105a35360926105a45360610780527f536105a55360606105a6610680527f5360506105a75360616105a853600461056107a0527fa95360936105aa5360536105ab53606106a0527f616105ac5360046105ad53606107c0527f946105ae5360606105af5360006105b05360f3616106c05260056106e05360b16107e0527f6106e15360536106e25360606106e35360006106e45360616106e55360056106610800527fe65360b26106e75360606106e85360006106e95360606106ea5360006106eb53610820527f60f56106ec5360606106ed5360006106ee5360606106ef5360006106f0536060610840527f6106f15360006106f25360606106f35360006106f45360606106f55360006106610860527ff65360856106f753605a6106f85360f16106f95360506106fa5360506106fb536108805260616108a05360066108a15360fc6108a25360606108a35360006108a45360f36108a5536108a660006000f060006000600060006000855af15050", + "storage": {}, + "balance": "0x0", + "nonce": "0x0" + }, + "0xa94f5374fce5edbc8e2a8697c15331677e6ebf0b": { + "code": "0x", + "storage": {}, + "balance": "0xffffffffff", + "nonce": "0x0" + } + }, + "transaction": { + "sender": "0xa94f5374fce5edbc8e2a8697c15331677e6ebf0b", + "gasPrice": "0x10", + "nonce": "0x0", + "to": "0x00000000000000000000000000000000000000f1", + "data": [ + "0x81fbe24d1e33d7944b2e62ee0ff24811bbbcf8cb311e5617c80623dec4477cc14849fc042b9bbaebca9f03f66cca76c46353c5a68c2e134ef75f8c2425d9702f3a4bd3c5527e93d27579bdbd7d237eaa1c0278fce26479aaf11fb8d00e7478" + ], + "gasLimit": [ + "0xb9a0b" + ], + "value": [ + "0x01" + ], + "secretKey": "0x45a915e4d060149eb4365960e6a7a45f334393093061116b197e3240065ff2d8" + }, + "out": "0x", + "post": { + "London": [ + { + "hash": "0x0000000000000000000000000000000000000000000000000000000000000000", + "logs": "0x0000000000000000000000000000000000000000000000000000000000000000", + "indexes": { + "data": 0, + "gas": 0, + "value": 0 + } + } + ] + } + } +} \ No newline at end of file diff --git a/eth/tracers/api.go b/eth/tracers/api.go index c15c5c4eb2..c9bf5a4223 100644 --- a/eth/tracers/api.go +++ b/eth/tracers/api.go @@ -758,7 +758,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 ( diff --git a/eth/tracers/logger/logger.go b/eth/tracers/logger/logger.go index 51b1512d03..dc9e6e62b7 100644 --- a/eth/tracers/logger/logger.go +++ b/eth/tracers/logger/logger.go @@ -45,9 +45,7 @@ type Config struct { 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 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"` } @@ -324,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) { diff --git a/eth/tracers/tracers_test.go b/eth/tracers/tracers_test.go index 13c36379e7..a72dbf6ee6 100644 --- a/eth/tracers/tracers_test.go +++ b/eth/tracers/tracers_test.go @@ -88,7 +88,7 @@ func BenchmarkTransactionTraceV2(b *testing.B) { b.ReportAllocs() for i := 0; i < b.N; i++ { - tracer := logger.NewStructLogger(&logger.Config{Debug: false}).Hooks() + tracer := logger.NewStructLogger(&logger.Config{}).Hooks() tracer.OnTxStart(evm.GetVMContext(), tx, msg.From) evm.Config.Tracer = tracer From 4ecf08584c413dd759c3241703f69c95bda6f84f Mon Sep 17 00:00:00 2001 From: Zheyuan He Date: Tue, 10 Dec 2024 20:10:17 +0800 Subject: [PATCH 05/14] core/vm: remove unnecessary comment (#30887) --- core/vm/instructions.go | 1 - 1 file changed, 1 deletion(-) diff --git a/core/vm/instructions.go b/core/vm/instructions.go index 47eb62be08..9b9a31a855 100644 --- a/core/vm/instructions.go +++ b/core/vm/instructions.go @@ -501,7 +501,6 @@ func opMload(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]by } func opMstore(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) { - // pop value of the stack mStart, val := scope.Stack.pop(), scope.Stack.pop() scope.Memory.Set32(mStart.Uint64(), &val) return nil, nil From 9045b79bc266a547c5b9e923e8db4286e74c240c Mon Sep 17 00:00:00 2001 From: Martin HS Date: Tue, 10 Dec 2024 13:27:29 +0100 Subject: [PATCH 06/14] metrics, cmd/geth: change init-process of metrics (#30814) This PR modifies how the metrics library handles `Enabled`: previously, the package `init` decided whether to serve real metrics or just dummy-types. This has several drawbacks: - During pkg init, we need to determine whether metrics are enabled or not. So we first hacked in a check if certain geth-specific commandline-flags were enabled. Then we added a similar check for geth-env-vars. Then we almost added a very elaborate check for toml-config-file, plus toml parsing. - Using "real" types and dummy types interchangeably means that everything is hidden behind interfaces. This has a performance penalty, and also it just adds a lot of code. This PR removes the interface stuff, uses concrete types, and allows for the setting of Enabled to happen later. It is still assumed that `metrics.Enable()` is invoked early on. The somewhat 'heavy' operations, such as ticking meters and exp-decay, now checks the enable-flag to prevent resource leak. The change may be large, but it's mostly pretty trivial, and from the last time I gutted the metrics, I ensured that we have fairly good test coverage. --------- Co-authored-by: Felix Lange --- cmd/geth/chaincmd.go | 11 +- cmd/geth/config.go | 24 ++ cmd/geth/main.go | 7 - cmd/utils/flags.go | 105 ++++---- core/rawdb/freezer_table.go | 12 +- core/state/trie_prefetcher.go | 28 +-- core/txpool/txpool.go | 4 +- eth/downloader/queue.go | 2 +- eth/handler.go | 2 +- eth/protocols/eth/handler.go | 2 +- eth/protocols/eth/handshake.go | 2 +- eth/protocols/eth/metrics.go | 12 +- eth/protocols/snap/handler.go | 2 +- ethdb/leveldb/leveldb.go | 28 +-- ethdb/pebble/pebble.go | 28 +-- metrics/counter.go | 94 ++----- metrics/counter_float64.go | 103 ++------ metrics/counter_float_64_test.go | 68 ++--- metrics/counter_test.go | 38 +-- metrics/debug.go | 8 +- metrics/ewma.go | 64 ++--- metrics/ewma_test.go | 14 +- metrics/exp/exp.go | 22 +- metrics/gauge.go | 84 +++---- metrics/gauge_float64.go | 65 ++--- metrics/gauge_info.go | 50 ++-- metrics/graphite.go | 117 --------- metrics/graphite_test.go | 22 -- metrics/healthcheck.go | 44 +--- metrics/histogram.go | 23 +- metrics/inactive.go | 48 ---- metrics/influxdb/influxdb.go | 16 +- metrics/influxdb/influxdb_test.go | 2 +- metrics/init_test.go | 2 +- metrics/log.go | 18 +- metrics/meter.go | 156 +++++------- metrics/meter_test.go | 12 +- metrics/metrics.go | 59 ++--- metrics/metrics_test.go | 71 ++---- metrics/opentsdb.go | 14 +- metrics/prometheus/collector.go | 22 +- metrics/prometheus/collector_test.go | 2 +- metrics/registry.go | 39 ++- metrics/registry_test.go | 18 +- metrics/resetting_sample.go | 2 +- metrics/resetting_timer.go | 90 +++---- metrics/sample.go | 356 +++++++++++++-------------- metrics/sample_test.go | 14 +- metrics/syslog.go | 16 +- metrics/timer.go | 104 +++----- metrics/writer.go | 16 +- p2p/discover/metrics.go | 4 +- p2p/discover/table.go | 4 +- p2p/metrics.go | 35 +-- p2p/peer.go | 2 +- p2p/tracker/tracker.go | 4 +- p2p/transport.go | 2 +- triedb/pathdb/states.go | 2 +- 58 files changed, 779 insertions(+), 1436 deletions(-) delete mode 100644 metrics/graphite.go delete mode 100644 metrics/graphite_test.go delete mode 100644 metrics/inactive.go diff --git a/cmd/geth/chaincmd.go b/cmd/geth/chaincmd.go index f6dc1cf4bf..48564eb5eb 100644 --- a/cmd/geth/chaincmd.go +++ b/cmd/geth/chaincmd.go @@ -38,7 +38,6 @@ import ( "github.com/ethereum/go-ethereum/ethdb" "github.com/ethereum/go-ethereum/internal/era" "github.com/ethereum/go-ethereum/log" - "github.com/ethereum/go-ethereum/metrics" "github.com/ethereum/go-ethereum/params" "github.com/urfave/cli/v2" ) @@ -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 17ed9fb606..ecee2bfd80 100644 --- a/cmd/geth/config.go +++ b/cmd/geth/config.go @@ -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 @@ -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/main.go b/cmd/geth/main.go index 1527e180fb..9d9256862b 100644 --- a/cmd/geth/main.go +++ b/cmd/geth/main.go @@ -34,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" @@ -325,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. diff --git a/cmd/utils/flags.go b/cmd/utils/flags.go index 9f309286bd..8ef39e88de 100644 --- a/cmd/utils/flags.go +++ b/cmd/utils/flags.go @@ -1968,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{} 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/state/trie_prefetcher.go b/core/state/trie_prefetcher.go index 5b64583432..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,7 +111,7 @@ 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 { diff --git a/core/txpool/txpool.go b/core/txpool/txpool.go index ce455e806e..d54cf4968b 100644 --- a/core/txpool/txpool.go +++ b/core/txpool/txpool.go @@ -126,7 +126,7 @@ func (p *TxPool) reserver(id int, subpool SubPool) AddressReserver { return ErrAlreadyReserved } p.reservations[addr] = subpool - if metrics.Enabled { + if metrics.Enabled() { m := fmt.Sprintf("%s/%d", reservationsGaugeName, id) metrics.GetOrRegisterGauge(m, nil).Inc(1) } @@ -143,7 +143,7 @@ func (p *TxPool) reserver(id int, subpool SubPool) AddressReserver { return errors.New("address not owned") } delete(p.reservations, addr) - if metrics.Enabled { + if metrics.Enabled() { m := fmt.Sprintf("%s/%d", reservationsGaugeName, id) metrics.GetOrRegisterGauge(m, nil).Dec(1) } diff --git a/eth/downloader/queue.go b/eth/downloader/queue.go index 6c9175a95a..a1c114f057 100644 --- a/eth/downloader/queue.go +++ b/eth/downloader/queue.go @@ -882,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/handler.go b/eth/handler.go index 583dc2835d..9820118173 100644 --- a/eth/handler.go +++ b/eth/handler.go @@ -366,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 { diff --git a/eth/protocols/eth/handler.go b/eth/protocols/eth/handler.go index dc32559c47..28db93a209 100644 --- a/eth/protocols/eth/handler.go +++ b/eth/protocols/eth/handler.go @@ -190,7 +190,7 @@ func handleMessage(backend Backend, peer *Peer) error { var handlers = eth68 // Track the amount of time it takes to serve the request and run the handler - if metrics.Enabled { + if metrics.Enabled() { h := fmt.Sprintf("%s/%s/%d/%#02x", p2p.HandleHistName, ProtocolName, peer.Version(), msg.Code) defer func(start time.Time) { sampler := func() metrics.Sample { diff --git a/eth/protocols/eth/handshake.go b/eth/protocols/eth/handshake.go index 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 8164973c20..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 { diff --git a/ethdb/leveldb/leveldb.go b/ethdb/leveldb/leveldb.go index ce7d823561..f18503c941 100644 --- a/ethdb/leveldb/leveldb.go +++ b/ethdb/leveldb/leveldb.go @@ -62,21 +62,21 @@ type Database struct { fn string // filename for reporting db *leveldb.DB // LevelDB instance - compTimeMeter metrics.Meter // Meter for measuring the total time spent in database compaction - compReadMeter metrics.Meter // Meter for measuring the data read during compaction - compWriteMeter metrics.Meter // Meter for measuring the data written during compaction - writeDelayNMeter metrics.Meter // Meter for measuring the write delay number due to database compaction - writeDelayMeter metrics.Meter // Meter for measuring the write delay duration due to database compaction - diskSizeGauge metrics.Gauge // Gauge for tracking the size of all the levels in the database - diskReadMeter metrics.Meter // Meter for measuring the effective amount of data read - diskWriteMeter metrics.Meter // Meter for measuring the effective amount of data written - memCompGauge metrics.Gauge // Gauge for tracking the number of memory compaction - level0CompGauge metrics.Gauge // Gauge for tracking the number of table compaction in level0 - nonlevel0CompGauge metrics.Gauge // Gauge for tracking the number of table compaction in non0 level - seekCompGauge metrics.Gauge // Gauge for tracking the number of table compaction caused by read opt - manualMemAllocGauge metrics.Gauge // Gauge to track the amount of memory that has been manually allocated (not a part of runtime/GC) + 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 diff --git a/ethdb/pebble/pebble.go b/ethdb/pebble/pebble.go index a9151a3bb5..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 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 c855671ae2..6619eb1e9e 100644 --- a/metrics/sample_test.go +++ b/metrics/sample_test.go @@ -86,7 +86,7 @@ func TestExpDecaySample(t *testing.T) { if have, want := snap.Size(), min(tc.updates, tc.reservoirSize); have != want { t.Errorf("unexpected size: have %d want %d", have, want) } - values := snap.(*sampleSnapshot).values + values := snap.values if have, want := len(values), min(tc.updates, tc.reservoirSize); have != want { t.Errorf("unexpected values length: have %d want %d", have, want) } @@ -111,8 +111,7 @@ func TestExpDecaySampleNanosecondRegression(t *testing.T) { for i := 0; i < 1000; i++ { sw.Update(20) } - s := sw.Snapshot() - v := s.(*sampleSnapshot).values + v := sw.Snapshot().values avg := float64(0) for i := 0; i < len(v); i++ { avg += float64(v[i]) @@ -166,7 +165,7 @@ func TestUniformSample(t *testing.T) { if size := s.Size(); size != 100 { t.Errorf("s.Size(): 100 != %v\n", size) } - values := s.(*sampleSnapshot).values + values := s.values if l := len(values); l != 100 { t.Errorf("len(s.Values()): 100 != %v\n", l) @@ -184,8 +183,7 @@ func TestUniformSampleIncludesTail(t *testing.T) { for i := 0; i < max; i++ { sw.Update(int64(i)) } - s := sw.Snapshot() - v := s.(*sampleSnapshot).values + v := sw.Snapshot().values sum := 0 exp := (max - 1) * max / 2 for i := 0; i < len(v); i++ { @@ -220,7 +218,7 @@ func benchmarkSample(b *testing.B, s Sample) { } } -func testExpDecaySampleStatistics(t *testing.T, s SampleSnapshot) { +func testExpDecaySampleStatistics(t *testing.T, s *sampleSnapshot) { if sum := s.Sum(); sum != 496598 { t.Errorf("s.Sum(): 496598 != %v\n", sum) } @@ -251,7 +249,7 @@ func testExpDecaySampleStatistics(t *testing.T, s SampleSnapshot) { } } -func testUniformSampleStatistics(t *testing.T, s SampleSnapshot) { +func testUniformSampleStatistics(t *testing.T, s *sampleSnapshot) { if count := s.Count(); count != 10000 { t.Errorf("s.Count(): 10000 != %v\n", count) } diff --git a/metrics/syslog.go b/metrics/syslog.go index fd856d6973..0bc4ed0da5 100644 --- a/metrics/syslog.go +++ b/metrics/syslog.go @@ -15,17 +15,17 @@ func Syslog(r Registry, d time.Duration, w *syslog.Writer) { for range time.Tick(d) { r.Each(func(name string, i interface{}) { switch metric := i.(type) { - case Counter: + case *Counter: w.Info(fmt.Sprintf("counter %s: count: %d", name, metric.Snapshot().Count())) - case CounterFloat64: + case *CounterFloat64: w.Info(fmt.Sprintf("counter %s: count: %f", name, metric.Snapshot().Count())) - case Gauge: + case *Gauge: w.Info(fmt.Sprintf("gauge %s: value: %d", name, metric.Snapshot().Value())) - case GaugeFloat64: + case *GaugeFloat64: w.Info(fmt.Sprintf("gauge %s: value: %f", name, metric.Snapshot().Value())) - case GaugeInfo: + case *GaugeInfo: w.Info(fmt.Sprintf("gauge %s: value: %s", name, metric.Snapshot().Value())) - case Healthcheck: + case *Healthcheck: metric.Check() w.Info(fmt.Sprintf("healthcheck %s: error: %v", name, metric.Error())) case Histogram: @@ -45,7 +45,7 @@ func Syslog(r Registry, d time.Duration, w *syslog.Writer) { ps[3], ps[4], )) - case Meter: + case *Meter: m := metric.Snapshot() w.Info(fmt.Sprintf( "meter %s: count: %d 1-min: %.2f 5-min: %.2f 15-min: %.2f mean: %.2f", @@ -56,7 +56,7 @@ func Syslog(r Registry, d time.Duration, w *syslog.Writer) { m.Rate15(), m.RateMean(), )) - case Timer: + case *Timer: t := metric.Snapshot() ps := t.Percentiles([]float64{0.5, 0.75, 0.95, 0.99, 0.999}) w.Info(fmt.Sprintf( diff --git a/metrics/timer.go b/metrics/timer.go index fc2a88f508..9df15c967a 100644 --- a/metrics/timer.go +++ b/metrics/timer.go @@ -5,47 +5,30 @@ import ( "time" ) -type TimerSnapshot interface { - HistogramSnapshot - MeterSnapshot -} - -// Timer capture the duration and rate of events. -type Timer interface { - Snapshot() TimerSnapshot - Stop() - Time(func()) - UpdateSince(time.Time) - Update(time.Duration) -} - // GetOrRegisterTimer returns an existing Timer or constructs and registers a -// new StandardTimer. +// new Timer. // Be sure to unregister the meter from the registry once it is of no use to // allow for garbage collection. -func GetOrRegisterTimer(name string, r Registry) Timer { +func GetOrRegisterTimer(name string, r Registry) *Timer { if nil == r { r = DefaultRegistry } - return r.GetOrRegister(name, NewTimer).(Timer) + return r.GetOrRegister(name, NewTimer).(*Timer) } -// NewCustomTimer constructs a new StandardTimer from a Histogram and a Meter. +// NewCustomTimer constructs a new Timer from a Histogram and a Meter. // Be sure to call Stop() once the timer is of no use to allow for garbage collection. -func NewCustomTimer(h Histogram, m Meter) Timer { - if !Enabled { - return NilTimer{} - } - return &StandardTimer{ +func NewCustomTimer(h Histogram, m *Meter) *Timer { + return &Timer{ histogram: h, meter: m, } } -// NewRegisteredTimer constructs and registers a new StandardTimer. +// NewRegisteredTimer constructs and registers a new Timer. // Be sure to unregister the meter from the registry once it is of no use to // allow for garbage collection. -func NewRegisteredTimer(name string, r Registry) Timer { +func NewRegisteredTimer(name string, r Registry) *Timer { c := NewTimer() if nil == r { r = DefaultRegistry @@ -54,60 +37,47 @@ func NewRegisteredTimer(name string, r Registry) Timer { return c } -// NewTimer constructs a new StandardTimer using an exponentially-decaying +// NewTimer constructs a new Timer using an exponentially-decaying // sample with the same reservoir size and alpha as UNIX load averages. // Be sure to call Stop() once the timer is of no use to allow for garbage collection. -func NewTimer() Timer { - if !Enabled { - return NilTimer{} - } - return &StandardTimer{ +func NewTimer() *Timer { + return &Timer{ histogram: NewHistogram(NewExpDecaySample(1028, 0.015)), meter: NewMeter(), } } -// NilTimer is a no-op Timer. -type NilTimer struct{} - -func (NilTimer) Snapshot() TimerSnapshot { return (*emptySnapshot)(nil) } -func (NilTimer) Stop() {} -func (NilTimer) Time(f func()) { f() } -func (NilTimer) Update(time.Duration) {} -func (NilTimer) UpdateSince(time.Time) {} - -// StandardTimer is the standard implementation of a Timer and uses a Histogram -// and Meter. -type StandardTimer struct { +// Timer captures the duration and rate of events, using a Histogram and a Meter. +type Timer struct { histogram Histogram - meter Meter + meter *Meter mutex sync.Mutex } // Snapshot returns a read-only copy of the timer. -func (t *StandardTimer) Snapshot() TimerSnapshot { +func (t *Timer) Snapshot() *TimerSnapshot { t.mutex.Lock() defer t.mutex.Unlock() - return &timerSnapshot{ + return &TimerSnapshot{ histogram: t.histogram.Snapshot(), meter: t.meter.Snapshot(), } } // Stop stops the meter. -func (t *StandardTimer) Stop() { +func (t *Timer) Stop() { t.meter.Stop() } // Time record the duration of the execution of the given function. -func (t *StandardTimer) Time(f func()) { +func (t *Timer) Time(f func()) { ts := time.Now() f() t.Update(time.Since(ts)) } // Update the duration of an event, in nanoseconds. -func (t *StandardTimer) Update(d time.Duration) { +func (t *Timer) Update(d time.Duration) { t.mutex.Lock() defer t.mutex.Unlock() t.histogram.Update(d.Nanoseconds()) @@ -116,67 +86,67 @@ func (t *StandardTimer) Update(d time.Duration) { // UpdateSince update the duration of an event that started at a time and ends now. // The record uses nanoseconds. -func (t *StandardTimer) UpdateSince(ts time.Time) { +func (t *Timer) UpdateSince(ts time.Time) { t.Update(time.Since(ts)) } -// timerSnapshot is a read-only copy of another Timer. -type timerSnapshot struct { +// TimerSnapshot is a read-only copy of another Timer. +type TimerSnapshot struct { histogram HistogramSnapshot - meter MeterSnapshot + meter *MeterSnapshot } // Count returns the number of events recorded at the time the snapshot was // taken. -func (t *timerSnapshot) Count() int64 { return t.histogram.Count() } +func (t *TimerSnapshot) Count() int64 { return t.histogram.Count() } // Max returns the maximum value at the time the snapshot was taken. -func (t *timerSnapshot) Max() int64 { return t.histogram.Max() } +func (t *TimerSnapshot) Max() int64 { return t.histogram.Max() } // Size returns the size of the sample at the time the snapshot was taken. -func (t *timerSnapshot) Size() int { return t.histogram.Size() } +func (t *TimerSnapshot) Size() int { return t.histogram.Size() } // Mean returns the mean value at the time the snapshot was taken. -func (t *timerSnapshot) Mean() float64 { return t.histogram.Mean() } +func (t *TimerSnapshot) Mean() float64 { return t.histogram.Mean() } // Min returns the minimum value at the time the snapshot was taken. -func (t *timerSnapshot) Min() int64 { return t.histogram.Min() } +func (t *TimerSnapshot) Min() int64 { return t.histogram.Min() } // Percentile returns an arbitrary percentile of sampled values at the time the // snapshot was taken. -func (t *timerSnapshot) Percentile(p float64) float64 { +func (t *TimerSnapshot) Percentile(p float64) float64 { return t.histogram.Percentile(p) } // Percentiles returns a slice of arbitrary percentiles of sampled values at // the time the snapshot was taken. -func (t *timerSnapshot) Percentiles(ps []float64) []float64 { +func (t *TimerSnapshot) Percentiles(ps []float64) []float64 { return t.histogram.Percentiles(ps) } // Rate1 returns the one-minute moving average rate of events per second at the // time the snapshot was taken. -func (t *timerSnapshot) Rate1() float64 { return t.meter.Rate1() } +func (t *TimerSnapshot) Rate1() float64 { return t.meter.Rate1() } // Rate5 returns the five-minute moving average rate of events per second at // the time the snapshot was taken. -func (t *timerSnapshot) Rate5() float64 { return t.meter.Rate5() } +func (t *TimerSnapshot) Rate5() float64 { return t.meter.Rate5() } // Rate15 returns the fifteen-minute moving average rate of events per second // at the time the snapshot was taken. -func (t *timerSnapshot) Rate15() float64 { return t.meter.Rate15() } +func (t *TimerSnapshot) Rate15() float64 { return t.meter.Rate15() } // RateMean returns the meter's mean rate of events per second at the time the // snapshot was taken. -func (t *timerSnapshot) RateMean() float64 { return t.meter.RateMean() } +func (t *TimerSnapshot) RateMean() float64 { return t.meter.RateMean() } // StdDev returns the standard deviation of the values at the time the snapshot // was taken. -func (t *timerSnapshot) StdDev() float64 { return t.histogram.StdDev() } +func (t *TimerSnapshot) StdDev() float64 { return t.histogram.StdDev() } // Sum returns the sum at the time the snapshot was taken. -func (t *timerSnapshot) Sum() int64 { return t.histogram.Sum() } +func (t *TimerSnapshot) Sum() int64 { return t.histogram.Sum() } // Variance returns the variance of the values at the time the snapshot was // taken. -func (t *timerSnapshot) Variance() float64 { return t.histogram.Variance() } +func (t *TimerSnapshot) Variance() float64 { return t.histogram.Variance() } diff --git a/metrics/writer.go b/metrics/writer.go index c211c5046b..2a41f8e1fe 100644 --- a/metrics/writer.go +++ b/metrics/writer.go @@ -26,22 +26,22 @@ func WriteOnce(r Registry, w io.Writer) { slices.SortFunc(namedMetrics, namedMetric.cmp) for _, namedMetric := range namedMetrics { switch metric := namedMetric.m.(type) { - case Counter: + case *Counter: fmt.Fprintf(w, "counter %s\n", namedMetric.name) fmt.Fprintf(w, " count: %9d\n", metric.Snapshot().Count()) - case CounterFloat64: + case *CounterFloat64: fmt.Fprintf(w, "counter %s\n", namedMetric.name) fmt.Fprintf(w, " count: %f\n", metric.Snapshot().Count()) - case Gauge: + case *Gauge: fmt.Fprintf(w, "gauge %s\n", namedMetric.name) fmt.Fprintf(w, " value: %9d\n", metric.Snapshot().Value()) - case GaugeFloat64: + case *GaugeFloat64: fmt.Fprintf(w, "gauge %s\n", namedMetric.name) fmt.Fprintf(w, " value: %f\n", metric.Snapshot().Value()) - case GaugeInfo: + case *GaugeInfo: fmt.Fprintf(w, "gauge %s\n", namedMetric.name) fmt.Fprintf(w, " value: %s\n", metric.Snapshot().Value().String()) - case Healthcheck: + case *Healthcheck: metric.Check() fmt.Fprintf(w, "healthcheck %s\n", namedMetric.name) fmt.Fprintf(w, " error: %v\n", metric.Error()) @@ -59,7 +59,7 @@ func WriteOnce(r Registry, w io.Writer) { fmt.Fprintf(w, " 95%%: %12.2f\n", ps[2]) fmt.Fprintf(w, " 99%%: %12.2f\n", ps[3]) fmt.Fprintf(w, " 99.9%%: %12.2f\n", ps[4]) - case Meter: + case *Meter: m := metric.Snapshot() fmt.Fprintf(w, "meter %s\n", namedMetric.name) fmt.Fprintf(w, " count: %9d\n", m.Count()) @@ -67,7 +67,7 @@ func WriteOnce(r Registry, w io.Writer) { fmt.Fprintf(w, " 5-min rate: %12.2f\n", m.Rate5()) fmt.Fprintf(w, " 15-min rate: %12.2f\n", m.Rate15()) fmt.Fprintf(w, " mean rate: %12.2f\n", m.RateMean()) - case Timer: + case *Timer: t := metric.Snapshot() ps := t.Percentiles([]float64{0.5, 0.75, 0.95, 0.99, 0.999}) fmt.Fprintf(w, "timer %s\n", namedMetric.name) diff --git a/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/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/peer.go b/p2p/peer.go index c3834965cc..30be151a2f 100644 --- a/p2p/peer.go +++ b/p2p/peer.go @@ -357,7 +357,7 @@ func (p *Peer) handle(msg Msg) error { if err != nil { return fmt.Errorf("msg code out of range: %v", msg.Code) } - if metrics.Enabled { + if metrics.Enabled() { m := fmt.Sprintf("%s/%s/%d/%#02x", ingressMeterName, proto.Name, proto.Version, msg.Code-proto.offset) metrics.GetOrRegisterMeter(m, nil).Mark(int64(msg.meterSize)) metrics.GetOrRegisterMeter(m+"/packets", nil).Mark(1) diff --git a/p2p/tracker/tracker.go b/p2p/tracker/tracker.go index 6a733b9ba5..5b72eb2b88 100644 --- a/p2p/tracker/tracker.go +++ b/p2p/tracker/tracker.go @@ -84,7 +84,7 @@ func New(protocol string, timeout time.Duration) *Tracker { // Track adds a network request to the tracker to wait for a response to arrive // or until the request it cancelled or times out. func (t *Tracker) Track(peer string, version uint, reqCode uint64, resCode uint64, id uint64) { - if !metrics.Enabled { + if !metrics.Enabled() { return } t.lock.Lock() @@ -163,7 +163,7 @@ func (t *Tracker) schedule() { // Fulfil fills a pending request, if any is available, reporting on various metrics. func (t *Tracker) Fulfil(peer string, version uint, code uint64, id uint64) { - if !metrics.Enabled { + if !metrics.Enabled() { return } t.lock.Lock() diff --git a/p2p/transport.go b/p2p/transport.go index 5fc7686feb..360e73a0de 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) diff --git a/triedb/pathdb/states.go b/triedb/pathdb/states.go index e1611b3ebc..6e633db272 100644 --- a/triedb/pathdb/states.go +++ b/triedb/pathdb/states.go @@ -43,7 +43,7 @@ func (c *counter) add(size int) { } // report uploads the cached statistics to meters. -func (c *counter) report(count metrics.Meter, size metrics.Meter) { +func (c *counter) report(count, size *metrics.Meter) { count.Mark(int64(c.n)) size.Mark(int64(c.size)) } From 4ed36ea1f877fc992db0fbd0b10a129bf0dca64d Mon Sep 17 00:00:00 2001 From: Hteev Oli Date: Tue, 10 Dec 2024 21:22:43 +0800 Subject: [PATCH 07/14] build: update to Go 1.23.4 (#30872) --- build/checksums.txt | 98 ++++++++++++++++++++++----------------------- 1 file changed, 49 insertions(+), 49 deletions(-) diff --git a/build/checksums.txt b/build/checksums.txt index b835215850..b8061c9244 100644 --- a/build/checksums.txt +++ b/build/checksums.txt @@ -5,56 +5,56 @@ # https://github.com/ethereum/execution-spec-tests/releases/download/v2.1.0/ ca89c76851b0900bfcc3cbb9a26cbece1f3d7c64a3bed38723e914713290df6c fixtures_develop.tar.gz -# version:golang 1.23.3 +# version:golang 1.23.4 # https://go.dev/dl/ -8d6a77332487557c6afa2421131b50f83db4ae3c579c3bc72e670ee1f6968599 go1.23.3.src.tar.gz -bdbf2a243ed4a121c9988684e5b15989cb244c1ff9e41ca823d0187b5c859114 go1.23.3.aix-ppc64.tar.gz -b79c77bbdf61e6e486aa6bea9286f3f7969c28e2ff7686ce10c334f746bfb724 go1.23.3.darwin-amd64.pkg -c7e024d5c0bc81845070f23598caf02f05b8ae88fd4ad2cd3e236ddbea833ad2 go1.23.3.darwin-amd64.tar.gz -3e764df0db8f3c7470b9ff641954a380510a4822613c06bd5a195fd083f4731d go1.23.3.darwin-arm64.pkg -31e119fe9bde6e105407a32558d5b5fa6ca11e2bd17f8b7b2f8a06aba16a0632 go1.23.3.darwin-arm64.tar.gz -3872c9a98331050a242afe63fa6abc8fc313aca83dcaefda318e903309ac0c8d go1.23.3.dragonfly-amd64.tar.gz -69479fa016ec5b4605885643ce0c2dd5c583e02353978feb6de38c961863b9cc go1.23.3.freebsd-386.tar.gz -bf1de22a900646ef4f79480ed88337856d47089cc610f87e6fef46f6b8db0e1f go1.23.3.freebsd-amd64.tar.gz -e461f866479bc36bdd4cfec32bfecb1bb243152268a1b3223de109410dec3407 go1.23.3.freebsd-arm.tar.gz -24154b4018a45540aefeb6b5b9ffdcc8d9a8cdb78cd7fec262787b89fed19997 go1.23.3.freebsd-arm64.tar.gz -218f3f1532e61dd65c330c2a5fc85bec18cc3690489763e62ffa9bb9fc85a68e go1.23.3.freebsd-riscv64.tar.gz -24e3f34858b8687c31f5e5ab9e46d27fb613b0d50a94261c500cebb2d79c0672 go1.23.3.illumos-amd64.tar.gz -3d7b00191a43c50d28e0903a0c576104bc7e171a8670de419d41111c08dfa299 go1.23.3.linux-386.tar.gz -a0afb9744c00648bafb1b90b4aba5bdb86f424f02f9275399ce0c20b93a2c3a8 go1.23.3.linux-amd64.tar.gz -1f7cbd7f668ea32a107ecd41b6488aaee1f5d77a66efd885b175494439d4e1ce go1.23.3.linux-arm64.tar.gz -5f0332754beffc65af65a7b2da76e9dd997567d0d81b6f4f71d3588dc7b4cb00 go1.23.3.linux-armv6l.tar.gz -1d0161a8946c7d99f717bad23631738408511f9f87e78d852224a023d8882ad8 go1.23.3.linux-loong64.tar.gz -e924a7c9027f521f8a3563541ed0f89a4db3ef005b6b71263415b38e0b46e63a go1.23.3.linux-mips.tar.gz -4cdf8c38165627f032c2b17cdd95e4aafff40d75fc873824d4c94914284098ca go1.23.3.linux-mips64.tar.gz -5e49347e7325d2e268fb14040529b704e66eed77154cc73a919e9167d8527a2f go1.23.3.linux-mips64le.tar.gz -142eabc17cee99403e895383ed7a6b7b40e740e8c2f73b79352bb9d1242fbd98 go1.23.3.linux-mipsle.tar.gz -96ad61ba6b6cc0f5adfd75e65231c61e7db26d8236f01117023899528164d1b0 go1.23.3.linux-ppc64.tar.gz -e3b926c81e8099d3cee6e6e270b85b39c3bd44263f8d3df29aacb4d7e00507c8 go1.23.3.linux-ppc64le.tar.gz -324e03b6f59be841dfbaeabc466224b0f0905f5ad3a225b7c0703090e6c4b1a5 go1.23.3.linux-riscv64.tar.gz -6bd72fcef72b046b6282c2d1f2c38f31600e4fe9361fcd8341500c754fb09c38 go1.23.3.linux-s390x.tar.gz -5df382337fe2e4ea6adaafa823da5e083513a97534a38f89d691dd6f599084ca go1.23.3.netbsd-386.tar.gz -9ae7cb6095a3e91182ac03547167e230fddd4941ed02dbdb6af663b2a53d9db7 go1.23.3.netbsd-amd64.tar.gz -4a452c4134a9bea6213d8925d322f26b01c0eccda1330585bb2b241c76a0c3ea go1.23.3.netbsd-arm.tar.gz -8ff3b5184d840148dbca061c04dca35a7070dc894255d3b755066bd76a7094dc go1.23.3.netbsd-arm64.tar.gz -5b6940922e68ac1162a704a8b583fb4f039f955bfe97c35a56c40269cbcff9b1 go1.23.3.openbsd-386.tar.gz -6ae4aeb6a88f3754b10ecec90422a30fb8bf86c3187be2be9408d67a5a235ace go1.23.3.openbsd-amd64.tar.gz -e5eae226391b60c4d1ea1022663f55b225c6d7bab67f31fbafd5dd7a04684006 go1.23.3.openbsd-arm.tar.gz -e12b2c04535e0bf5561d54831122b410d708519c1ec2c56b0c2350b15243c331 go1.23.3.openbsd-arm64.tar.gz -599818e4062166d7a112f6f3fcca2dd4e2cdd3111fe48f9757bd8debf38c7f52 go1.23.3.openbsd-ppc64.tar.gz -9ca4db8cab2a07d561f5b2a9397793684ab3b22326add1fe8cda8a545a1693db go1.23.3.openbsd-riscv64.tar.gz -8fca1ec2aced936e0170605378ee7f0acb38f002490321f67fc83728ee281967 go1.23.3.plan9-386.tar.gz -22d663692224fc1933a97f61d9fe49815e3b9ef1c2be97046505683fdf2e23c7 go1.23.3.plan9-amd64.tar.gz -d0417a702d0e776d57e450fa2ce1ce7efa199a636644776862dbf946c409a462 go1.23.3.plan9-arm.tar.gz -b5d9db1c02e0ca266a142eb687bd7749890c30872b09a4a0ffcd491425039754 go1.23.3.solaris-amd64.tar.gz -14b7baf4af2046013b74dfac6e9a0a7403f15ee9940a16890bc028dfd32c49ac go1.23.3.windows-386.msi -23da9089ea6c5612d718f13c26e9bfc9aaaabe222838075346a8191d48f9dfe5 go1.23.3.windows-386.zip -614f0e3eed82245dfb4356d4e8d5b96abecca6a4c4f0168c0e389e4dd6284db8 go1.23.3.windows-amd64.msi -81968b563642096b8a7521171e2be6e77ff6f44032f7493b7bdec9d33f44f31d go1.23.3.windows-amd64.zip -c9951eecad732c59dfde6dc4803fa9253eb074663c61035c8d856f4d2eb146cb go1.23.3.windows-arm.msi -1a7db02be47deada42082d21d63eba0013f93375cfa0e7768962f1295a469022 go1.23.3.windows-arm.zip -a74e3e195219af4330b93c71cd4b736b709a5654a07cc37eebe181c4984afb82 go1.23.3.windows-arm64.msi -dbdfa868b1a3f8c62950373e4975d83f90dd8b869a3907319af8384919bcaffe go1.23.3.windows-arm64.zip +ad345ac421e90814293a9699cca19dd5238251c3f687980bbcae28495b263531 go1.23.4.src.tar.gz +459a09504f7ebf2cbcee6ac282c8f34f97651217b1feae64557dcdd392b9bb62 go1.23.4.aix-ppc64.tar.gz +0f4e569b2d38cb75cb2efcaf56beb42778ab5e46d89318fef39060fe36d7b9b7 go1.23.4.darwin-amd64.pkg +6700067389a53a1607d30aa8d6e01d198230397029faa0b109e89bc871ab5a0e go1.23.4.darwin-amd64.tar.gz +19c054eaf40c5fac65b027f7443c01382e493d3c8c42cf8b2504832ebddce037 go1.23.4.darwin-arm64.pkg +87d2bb0ad4fe24d2a0685a55df321e0efe4296419a9b3de03369dbe60b8acd3a go1.23.4.darwin-arm64.tar.gz +5e73dc89b44626677ec9d9aa4257d6d2ef1245502bc36a99385284910d0ade0a go1.23.4.dragonfly-amd64.tar.gz +8df26b1e71234756c1f0e82cfffba3f427c5a91a251844ada2c7694a6986c546 go1.23.4.freebsd-386.tar.gz +7de078d94d2af50ee9506ef7df85e4d12d4018b23e0b2cbcbc61d686f549b41a go1.23.4.freebsd-amd64.tar.gz +3f23e0a01cfe24e4160124cd7ab02bdd188264652074abdbce401c93f41e58a4 go1.23.4.freebsd-arm.tar.gz +986a20e7c94431f03b44b3c415abc698c7b4edc2ae8431f7ecae1c2429d4cfa2 go1.23.4.freebsd-arm64.tar.gz +25e39f005f977778ce963fc43089510fe7514f3cfc0358eab584de4ce9f181fb go1.23.4.freebsd-riscv64.tar.gz +7e1d52f93da68c3bab39e3d83f89944d7d151208e54fdc30b0eda2a3812661d7 go1.23.4.illumos-amd64.tar.gz +4a4a0e7587ef8c8a326439b957027f2791795e2d29d4ae3885b4091a48f843bc go1.23.4.linux-386.tar.gz +6924efde5de86fe277676e929dc9917d466efa02fb934197bc2eba35d5680971 go1.23.4.linux-amd64.tar.gz +16e5017863a7f6071363782b1b8042eb12c6ca4f4cd71528b2123f0a1275b13e go1.23.4.linux-arm64.tar.gz +1f1dda0dc7ce0b2295f57258ec5ef0803fd31b9ed0aa20e2e9222334e5755de1 go1.23.4.linux-armv6l.tar.gz +4f469179a335a1a7bb9f991ad0c567f3d3eeb9b412ecd192206ab5c3e1a52b5a go1.23.4.linux-loong64.tar.gz +86b68185bcc43ea07190e95137c3442f062acc7ae10c3f1cf900fbe23e07df24 go1.23.4.linux-mips.tar.gz +3a19245eec76533b3d01c90f3a40a38d63684028f0fd54d442dc9a9d03197891 go1.23.4.linux-mips64.tar.gz +b53a06fc8455f6a875329e8d2e24d39db298122c9cce6e948117022191f6c613 go1.23.4.linux-mips64le.tar.gz +66120a8105b8ba6559f4e6a13b1e39b433fb8032df9d1744e4486876fa1723ce go1.23.4.linux-mipsle.tar.gz +33be2bfb27f2821a65e9f6aba744c85ea7c5e233e16bac27bb3ec253bcd4e970 go1.23.4.linux-ppc64.tar.gz +65a303ef51e48ff77e004a6a5b4db6ce59495cd59c6af51b54bf4f786c01a1b9 go1.23.4.linux-ppc64le.tar.gz +7c40e9e0d722cef14ede765159ba297f4c6e3093bb106f10fbccf8564780049a go1.23.4.linux-riscv64.tar.gz +74aab82bf4eca7c26c830a5b0e2a31d193a4d5ba47045526b92473cc7188d7d7 go1.23.4.linux-s390x.tar.gz +dba009d8bf9928cb5a1e31fcbe0eb41335cce4fe63755d95cef6b5987df4ed5a go1.23.4.netbsd-386.tar.gz +54b081cc36355aa5ecb6db9544cf7e77366a7b08ce96cb98a45d043e393660c7 go1.23.4.netbsd-amd64.tar.gz +f05fec348c7c9f07e1ad4e436db4122e98de99ebcfbf6ac6176869785f334a02 go1.23.4.netbsd-arm.tar.gz +317878da2bface5a57a8eaf5c1fe2b40b1c82d8172a10453ad3eea36f6946bdb go1.23.4.netbsd-arm64.tar.gz +0d84350dfd72c505c6ad474e51676b04e95ffb748c614bd5bf8510026873059b go1.23.4.openbsd-386.tar.gz +cc62f5a14ea3d573d8edbce1833f70a8f99ca048a9db0fcc9e738fd48e950505 go1.23.4.openbsd-amd64.tar.gz +326aba6cf5bb9348fa3e41217abd2c84eac92608684e2fe8c5474fdab23a0db9 go1.23.4.openbsd-arm.tar.gz +51ea2a2588bf3da8e1476f3e2bd4d6724d74126e99f9c6ea9af4ebe389e64de6 go1.23.4.openbsd-arm64.tar.gz +44c5c82ab23e40225b2ba1e7d19150a5973ea58e93b4931e426e6e6f0d108872 go1.23.4.openbsd-ppc64.tar.gz +5fa31fc13d1e3c123a5e96ba38683fa2c947baed23ac9c7c341afcfe007c8993 go1.23.4.openbsd-riscv64.tar.gz +e5952fc93eeaa0094ef09a0e72a9f06f0621ce841a39f9637fb5b9062e77d67a go1.23.4.plan9-386.tar.gz +fb2a9ee3ae5a77e734862e257a9395b43e707ac45e060dfa84c5a40688e73170 go1.23.4.plan9-amd64.tar.gz +e1b95563b19fdebd6ea0d20c07641e69580976fa754e586c831ad7a3ae987140 go1.23.4.plan9-arm.tar.gz +088c282509fc9e1a8f29fc0dd16fe486854d05b8ceba08d077d17d11d6979a41 go1.23.4.solaris-amd64.tar.gz +e5865c1bfc3fee5d003819b2e2c800f598fe9994931bac63f573e8d05a10d91f go1.23.4.windows-386.msi +e544e0e356147ba998e267002bd0f2c4bf3370d495467a55baf2c63595a2026d go1.23.4.windows-386.zip +5f8cc5991eb8f4f96b6c611d839453cd11c9a2c3f23672a4188342c97ee159fa go1.23.4.windows-amd64.msi +16c59ac9196b63afb872ce9b47f945b9821a3e1542ec125f16f6085a1c0f3c39 go1.23.4.windows-amd64.zip +fc77c0531406d092c5356167e45c05a22d16bea84e3fa555e0f03af085c11763 go1.23.4.windows-arm.msi +1012cfd8ca7241c2beecb5c345dd61f01897c6f6baca80ea1bfed357035c868a go1.23.4.windows-arm.zip +8347c1aa4e1e67954d12830f88dbe44bd7ac0ec134bb472783dbfb5a3a8865d0 go1.23.4.windows-arm64.msi +db69cae5006753c785345c3215ad941f8b6224e2f81fec471c42d6857bee0e6f go1.23.4.windows-arm64.zip # version:golangci 1.61.0 # https://github.com/golangci/golangci-lint/releases/ From 330190e476e2a2de4aac712551629a4134f802d5 Mon Sep 17 00:00:00 2001 From: Darren Kelly <107671032+darrenvechain@users.noreply.github.com> Date: Tue, 10 Dec 2024 13:30:24 +0000 Subject: [PATCH 08/14] accounts/abi: support unpacking solidity errors (#30738) This PR adds the error fragments to `func (abi ABI) getArguments` which allows typed decoding of errors. --- accounts/abi/abi.go | 7 +++++-- accounts/abi/abi_test.go | 33 +++++++++++++++++++++++++++++++++ 2 files changed, 38 insertions(+), 2 deletions(-) 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)) From c7e740f40ca93cd2b8681b97af71e0b20207ca09 Mon Sep 17 00:00:00 2001 From: gitglorythegreat Date: Wed, 11 Dec 2024 18:05:59 +0800 Subject: [PATCH 09/14] core/state: remove pointless wrapper functions (#30891) --- core/state/access_list.go | 5 +---- core/state/statedb_test.go | 14 +++----------- 2 files changed, 4 insertions(+), 15 deletions(-) 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/statedb_test.go b/core/state/statedb_test.go index 57886e6e03..37141e90b0 100644 --- a/core/state/statedb_test.go +++ b/core/state/statedb_test.go @@ -650,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()) @@ -1034,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) From c1c250714842768687e8a4abe0dd9f57ca9dcc53 Mon Sep 17 00:00:00 2001 From: lorenzo <31852651+lorenzo-dev1@users.noreply.github.com> Date: Thu, 12 Dec 2024 12:33:42 +0100 Subject: [PATCH 10/14] p2p: fix DiscReason encoding/decoding (#30855) This fixes an issue where the disconnect message was not wrapped in a list. The specification requires it to be a list like any other message. In order to remain compatible with legacy geth versions, we now accept both encodings when parsing a disconnect message. --------- Co-authored-by: Felix Lange --- p2p/peer.go | 25 ++++++++++++++++++++++--- p2p/peer_error.go | 5 ++++- p2p/transport.go | 24 ++++++++++-------------- p2p/transport_test.go | 10 ++++++++-- 4 files changed, 44 insertions(+), 20 deletions(-) diff --git a/p2p/peer.go b/p2p/peer.go index 30be151a2f..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() @@ -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/transport.go b/p2p/transport.go index 360e73a0de..87d3013f11 100644 --- a/p2p/transport.go +++ b/p2p/transport.go @@ -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, }, { From 88cbfab332c96edfbe99d161d9df6a40721bd786 Mon Sep 17 00:00:00 2001 From: Antony Denyer Date: Thu, 12 Dec 2024 11:39:03 +0000 Subject: [PATCH 11/14] internal/ethapi: add block override to estimateGas (#30695) Add block overrides to `eth_estimateGas` to align consistency with `eth_call`. https://github.com/ethereum/go-ethereum/issues/27800#issuecomment-1658186166 Fixes https://github.com/ethereum/go-ethereum/issues/28175 --------- Co-authored-by: Sina Mahmoodi --- .gitignore | 1 + eth/gasestimator/gasestimator.go | 13 +- eth/tracers/api.go | 5 +- eth/tracers/api_test.go | 35 +-- graphql/graphql.go | 4 +- internal/ethapi/api.go | 191 +------------ internal/ethapi/api_test.go | 323 +++++++++++----------- internal/ethapi/override/override.go | 195 +++++++++++++ internal/ethapi/override/override_test.go | 125 +++++++++ internal/ethapi/simulate.go | 9 +- internal/ethapi/simulate_test.go | 13 +- internal/ethapi/transaction_args.go | 2 +- internal/web3ext/web3ext.go | 4 +- 13 files changed, 545 insertions(+), 375 deletions(-) create mode 100644 internal/ethapi/override/override.go create mode 100644 internal/ethapi/override/override_test.go diff --git a/.gitignore b/.gitignore index ac0f4efdfb..7000fedd25 100644 --- a/.gitignore +++ b/.gitignore @@ -37,6 +37,7 @@ profile.cov # IdeaIDE .idea +*.iml # VS Code .vscode diff --git a/eth/gasestimator/gasestimator.go b/eth/gasestimator/gasestimator.go index 9b24bfbf96..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 } @@ -220,6 +222,9 @@ func run(ctx context.Context, call *core.Message, opts *Options) (*core.Executio 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 call.GasPrice.Sign() == 0 { diff --git a/eth/tracers/api.go b/eth/tracers/api.go index c9bf5a4223..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 } diff --git a/eth/tracers/api_test.go b/eth/tracers/api_test.go index e786853ede..13a7b0aaae 100644 --- a/eth/tracers/api_test.go +++ b/eth/tracers/api_test.go @@ -44,6 +44,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/params" "github.com/ethereum/go-ethereum/rpc" ) @@ -454,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":[ @@ -698,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":""}`, @@ -740,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))}), }, @@ -757,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"}`, }, @@ -777,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"}`, }, @@ -807,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")), }, }, @@ -823,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{{}}), }, @@ -841,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, @@ -876,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, @@ -914,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/graphql/graphql.go b/graphql/graphql.go index 57dfd14ecd..6e364de6d9 100644 --- a/graphql/graphql.go +++ b/graphql/graphql.go @@ -1208,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 { @@ -1272,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/internal/ethapi/api.go b/internal/ethapi/api.go index 067d07ba7a..de75c697ef 100644 --- a/internal/ethapi/api.go +++ b/internal/ethapi/api.go @@ -35,19 +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" ) // estimateGasErrorRatio is the amount of overestimation eth_estimateGas is @@ -621,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 @@ -818,7 +652,7 @@ 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) @@ -897,7 +731,7 @@ func applyMessageWithEVM(ctx context.Context, evm *vm.EVM, msg *core.Message, ti 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) @@ -913,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 @@ -972,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 { @@ -983,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. @@ -1016,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 . diff --git a/internal/ethapi/api_test.go b/internal/ethapi/api_test.go index 1f5f2dd1d5..ae2ec9f0f0 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" ) @@ -640,6 +641,17 @@ func TestEstimateGas(t *testing.T) { 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 @@ -648,14 +660,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{ @@ -687,8 +701,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, @@ -700,8 +714,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, }, @@ -758,16 +772,65 @@ 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")), + }, } 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 } @@ -818,9 +881,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 }{ @@ -880,8 +943,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", }, @@ -920,27 +983,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 @@ -963,8 +1059,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{}, }, }, @@ -991,7 +1087,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"), }, @@ -1017,8 +1113,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{}, }, }, @@ -1172,8 +1268,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, @@ -1215,8 +1311,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{ { @@ -1230,8 +1326,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{ { @@ -1289,8 +1385,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, @@ -1316,7 +1412,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, }, @@ -1331,7 +1427,7 @@ func TestSimulateV1(t *testing.T) { }, }, }, { - BlockOverrides: &BlockOverrides{ + BlockOverrides: &override.BlockOverrides{ Number: (*hexutil.Big)(big.NewInt(12)), }, Calls: []TransactionArgs{{ @@ -1374,7 +1470,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{{ @@ -1386,7 +1482,7 @@ func TestSimulateV1(t *testing.T) { }, }}, }, { - BlockOverrides: &BlockOverrides{ + BlockOverrides: &override.BlockOverrides{ Number: (*hexutil.Big)(big.NewInt(11)), }, Calls: []TransactionArgs{{ @@ -1406,8 +1502,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"), }, }, @@ -1448,8 +1544,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 { @@ -1490,8 +1586,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 { @@ -1518,7 +1614,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 { @@ -1555,8 +1651,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 { @@ -1610,8 +1706,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" { @@ -1753,8 +1849,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), @@ -1788,11 +1884,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, @@ -1821,7 +1917,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{ @@ -1843,7 +1939,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)), @@ -1890,10 +1986,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"), }, @@ -1915,7 +2011,7 @@ func TestSimulateV1(t *testing.T) { Input: uint256ToBytes(uint256.NewInt(10)), }}, }, { - BlockOverrides: &BlockOverrides{ + BlockOverrides: &override.BlockOverrides{ Number: (*hexutil.Big)(big.NewInt(16)), }, Calls: []TransactionArgs{{ @@ -2005,7 +2101,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" { @@ -2040,7 +2136,7 @@ func TestSimulateV1(t *testing.T) { }, }, }, { - BlockOverrides: &BlockOverrides{ + BlockOverrides: &override.BlockOverrides{ BaseFeePerGas: (*hexutil.Big)(big.NewInt(1)), }, Calls: []TransactionArgs{{ @@ -2113,7 +2209,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" { @@ -3286,97 +3382,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/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 f6647c7ba4..5bd0079a93 100644 --- a/internal/ethapi/simulate.go +++ b/internal/ethapi/simulate.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/internal/ethapi/override" "github.com/ethereum/go-ethereum/params" "github.com/ethereum/go-ethereum/rpc" "github.com/ethereum/go-ethereum/trie" @@ -49,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 } @@ -303,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)) @@ -323,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/transaction_args.go b/internal/ethapi/transaction_args.go index 91e05544dc..2a0508b147 100644 --- a/internal/ethapi/transaction_args.go +++ b/internal/ethapi/transaction_args.go @@ -160,7 +160,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 } diff --git a/internal/web3ext/web3ext.go b/internal/web3ext/web3ext.go index 0c346bbf79..8ac8f44958 100644 --- a/internal/web3ext/web3ext.go +++ b/internal/web3ext/web3ext.go @@ -503,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({ From 804d45cc2eddd92ee4b9940875e4a6914ec5256f Mon Sep 17 00:00:00 2001 From: Lucas Date: Fri, 13 Dec 2024 08:46:12 -0300 Subject: [PATCH 12/14] p2p: DNS resolution for static nodes (#30822) Closes #23210 # Context When deploying Geth in Kubernetes with ReplicaSets, we encountered two DNS-related issues affecting node connectivity. First, during startup, Geth tries to resolve DNS names for static nodes too early in the config unmarshaling phase. If peer nodes aren't ready yet (which is common in Kubernetes rolling deployments), this causes an immediate failure: ``` INFO [11-26|10:03:42.816] Starting Geth on Ethereum mainnet... INFO [11-26|10:03:42.817] Bumping default cache on mainnet provided=1024 updated=4096 Fatal: config.toml, line 81: (p2p.Config.StaticNodes) lookup idontexist.geth.node: no such host ``` The second issue comes up when pods get rescheduled to different nodes - their IPs change but peers keep using the initially resolved IP, never updating the DNS mapping. This PR adds proper DNS support for enode:// URLs by deferring resolution to connection time. It also handles DNS failures gracefully instead of failing fatally during startup, making it work better in container environments where IPs are dynamic and peers come and go during rollouts. --------- Co-authored-by: Felix Lange --- p2p/dial.go | 143 ++++++++++++++++++++++++++++++++-------- p2p/dial_test.go | 29 ++++++++ p2p/enode/node.go | 22 +++++++ p2p/enode/node_test.go | 19 ++++++ p2p/enode/urlv4.go | 38 ++++++----- p2p/enode/urlv4_test.go | 23 +++---- p2p/peer_test.go | 30 ++++----- 7 files changed, 228 insertions(+), 76 deletions(-) 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/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/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)) From f808d7357ed4076b224a8c6fe47893ce022f9409 Mon Sep 17 00:00:00 2001 From: lightclient <14004106+lightclient@users.noreply.github.com> Date: Mon, 16 Dec 2024 03:29:37 -0700 Subject: [PATCH 13/14] all: implement eip-7702 set code tx (#30078) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This PR implements EIP-7702: "Set EOA account code". Specification: https://eips.ethereum.org/EIPS/eip-7702 > Add a new transaction type that adds a list of `[chain_id, address, nonce, y_parity, r, s]` authorization tuples. For each tuple, write a delegation designator `(0xef0100 ++ address)` to the signing account’s code. All code reading operations must load the code pointed to by the designator. --------- Co-authored-by: Mario Vega Co-authored-by: Martin Holst Swende Co-authored-by: Felix Lange --- accounts/external/backend.go | 2 +- cmd/evm/eofparse.go | 2 +- cmd/evm/eofparse_test.go | 2 +- cmd/evm/internal/t8ntool/execution.go | 4 +- cmd/evm/internal/t8ntool/gen_execresult.go | 4 +- cmd/evm/internal/t8ntool/transaction.go | 2 +- cmd/evm/t8n_test.go | 8 + cmd/evm/testdata/1/exp.json | 3 +- cmd/evm/testdata/13/exp2.json | 3 +- cmd/evm/testdata/14/exp.json | 3 +- cmd/evm/testdata/14/exp2.json | 3 +- cmd/evm/testdata/14/exp_berlin.json | 3 +- cmd/evm/testdata/19/exp_arrowglacier.json | 3 +- cmd/evm/testdata/19/exp_grayglacier.json | 25 +- cmd/evm/testdata/19/exp_london.json | 3 +- cmd/evm/testdata/23/exp.json | 3 +- cmd/evm/testdata/24/exp.json | 3 +- cmd/evm/testdata/25/exp.json | 3 +- cmd/evm/testdata/26/exp.json | 3 +- cmd/evm/testdata/28/exp.json | 3 +- cmd/evm/testdata/29/exp.json | 3 +- cmd/evm/testdata/3/exp.json | 3 +- cmd/evm/testdata/30/exp.json | 5 +- cmd/evm/testdata/33/README.md | 1 + cmd/evm/testdata/33/alloc.json | 30 +++ cmd/evm/testdata/33/env.json | 14 ++ cmd/evm/testdata/33/exp.json | 62 +++++ cmd/evm/testdata/33/txs.json | 37 +++ cmd/evm/testdata/5/exp.json | 3 +- core/bench_test.go | 2 +- core/blockchain_test.go | 94 ++++++++ core/error.go | 18 ++ core/state/journal.go | 19 +- core/state/state_object.go | 7 +- core/state/statedb.go | 5 +- core/state/statedb_hooked.go | 11 +- core/state_processor_test.go | 25 +- core/state_transition.go | 107 ++++++++- core/txpool/validation.go | 2 +- core/types/gen_authorization.go | 75 ++++++ core/types/receipt.go | 4 +- core/types/transaction.go | 12 + core/types/transaction_marshalling.go | 92 +++++++ core/types/transaction_signing.go | 77 +++++- core/types/tx_setcode.go | 226 ++++++++++++++++++ core/types/tx_setcode_test.go | 70 ++++++ core/verkle_witness_test.go | 4 +- core/vm/eips.go | 68 ++++++ core/vm/eof_validation_test.go | 16 +- core/vm/evm.go | 39 ++- core/vm/interface.go | 4 +- core/vm/interpreter.go | 2 + core/vm/jump_table.go | 17 +- core/vm/operations_acl.go | 68 ++++++ core/vm/runtime/runtime_test.go | 80 +++++++ .../testdata/prestate_tracer/setcode_tx.json | 82 +++++++ eth/tracers/native/prestate.go | 9 + graphql/graphql.go | 4 +- internal/ethapi/api.go | 61 +++-- internal/ethapi/api_test.go | 23 +- internal/ethapi/transaction_args.go | 27 +++ params/protocol_params.go | 9 +- tests/gen_stauthorization.go | 74 ++++++ tests/gen_sttransaction.go | 6 + tests/state_test_util.go | 37 +++ tests/transaction_test_util.go | 2 +- 66 files changed, 1606 insertions(+), 115 deletions(-) create mode 100644 cmd/evm/testdata/33/README.md create mode 100644 cmd/evm/testdata/33/alloc.json create mode 100644 cmd/evm/testdata/33/env.json create mode 100644 cmd/evm/testdata/33/exp.json create mode 100644 cmd/evm/testdata/33/txs.json create mode 100644 core/types/gen_authorization.go create mode 100644 core/types/tx_setcode.go create mode 100644 core/types/tx_setcode_test.go create mode 100644 eth/tracers/internal/tracetest/testdata/prestate_tracer/setcode_tx.json create mode 100644 tests/gen_stauthorization.go 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/cmd/evm/eofparse.go b/cmd/evm/eofparse.go index df8581146a..92182a53b3 100644 --- a/cmd/evm/eofparse.go +++ b/cmd/evm/eofparse.go @@ -36,7 +36,7 @@ var jt vm.JumpTable const initcode = "INITCODE" func init() { - jt = vm.NewPragueEOFInstructionSetForTesting() + jt = vm.NewEOFInstructionSetForTesting() } var ( 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/t8ntool/execution.go b/cmd/evm/internal/t8ntool/execution.go index 7a0de86a11..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 { 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..64e21b24fd 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.AuthList(), 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/t8n_test.go b/cmd/evm/t8n_test.go index 85caf010b3..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()...) 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..9c4db138c8 --- /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", + "v": "0x1", + "r": "0xf7e3e597fc097e71ed6c26b14b25e5395bc8510d58b9136af439e12715f2d721", + "s": "0x6cf7c3d7939bfdb784373effc0ebb0bd7549691a513f395e3cdabf8602724987" + }, + { + "chainId": "0x0", + "address": "0x000000000000000000000000000000000000bbbb", + "nonce": "0x0", + "v": "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/core/bench_test.go b/core/bench_test.go index 6d518e8d3b..d376830318 100644 --- a/core/bench_test.go +++ b/core/bench_test.go @@ -90,7 +90,7 @@ func genValueTx(nbytes int) func(int, *BlockGen) { data := make([]byte, nbytes) return func(i int, gen *BlockGen) { toaddr := common.Address{} - 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 { diff --git a/core/blockchain_test.go b/core/blockchain_test.go index dc391bb520..a54a907766 100644 --- a/core/blockchain_test.go +++ b/core/blockchain_test.go @@ -17,6 +17,7 @@ package core import ( + "bytes" "errors" "fmt" gomath "math" @@ -36,6 +37,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/core/vm/program" "github.com/ethereum/go-ethereum/crypto" "github.com/ethereum/go-ethereum/eth/tracers/logger" "github.com/ethereum/go-ethereum/ethdb" @@ -4232,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.SignAuth(types.Authorization{ + ChainID: gspec.Config.ChainID.Uint64(), + Address: aa, + Nonce: 1, + }, key1) + auth2, _ := types.SignAuth(types.Authorization{ + ChainID: 0, + Address: bb, + Nonce: 0, + }, key2) + + _, 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.Authorization{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/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/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/state_object.go b/core/state/state_object.go index 2d542e5005..76a3aba92c 100644 --- a/core/state/state_object.go +++ b/core/state/state_object.go @@ -20,6 +20,7 @@ import ( "bytes" "fmt" "maps" + "slices" "time" "github.com/ethereum/go-ethereum/common" @@ -541,9 +542,11 @@ func (s *stateObject) CodeSize() int { return size } -func (s *stateObject) SetCode(codeHash common.Hash, code []byte) { - s.db.journal.setCode(s.address) +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) { diff --git a/core/state/statedb.go b/core/state/statedb.go index b0603db7f0..d279ccfdfe 100644 --- a/core/state/statedb.go +++ b/core/state/statedb.go @@ -439,11 +439,12 @@ 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) common.Hash { diff --git a/core/state/statedb_hooked.go b/core/state/statedb_hooked.go index 26d0217099..31bdd06b46 100644 --- a/core/state/statedb_hooked.go +++ b/core/state/statedb_hooked.go @@ -182,11 +182,16 @@ func (s *hookedStateDB) SetNonce(address common.Address, nonce uint64) { } } -func (s *hookedStateDB) SetCode(address common.Address, code []byte) { - s.inner.SetCode(address, code) +func (s *hookedStateDB) SetCode(address common.Address, code []byte) []byte { + prev := s.inner.SetCode(address, code) if s.hooks.OnCodeChange != nil { - s.hooks.OnCodeChange(address, types.EmptyCodeHash, nil, crypto.Keccak256Hash(code), code) + 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 { diff --git a/core/state_processor_test.go b/core/state_processor_test.go index f3d2304690..e8d8c2ca2e 100644 --- a/core/state_processor_test.go +++ b/core/state_processor_test.go @@ -63,6 +63,7 @@ func TestStateProcessorErrors(t *testing.T) { TerminalTotalDifficulty: big.NewInt(0), ShanghaiTime: new(uint64), CancunTime: new(uint64), + PragueTime: new(uint64), } signer = types.LatestSigner(config) key1, _ = crypto.HexToECDSA("b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291") @@ -110,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.Authorization) *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 ( @@ -251,6 +267,13 @@ 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, false) _, err := blockchain.InsertChain(types.Blocks{block}) @@ -337,7 +360,7 @@ 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, false) diff --git a/core/state_transition.go b/core/state_transition.go index ea7e3df2ff..58728e470e 100644 --- a/core/state_transition.go +++ b/core/state_transition.go @@ -67,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.Authorization, isContractCreation, isHomestead, isEIP2028, isEIP3860 bool) (uint64, error) { // Set the starting gas for the raw transaction var gas uint64 if isContractCreation && isHomestead { @@ -113,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 } @@ -140,6 +143,7 @@ type Message struct { AccessList types.AccessList BlobGasFeeCap *big.Int BlobHashes []common.Hash + AuthList []types.Authorization // When SkipNonceChecks is true, the message nonce is not checked against the // account nonce in state. @@ -162,6 +166,7 @@ func TransactionToMessage(tx *types.Transaction, s types.Signer, baseFee *big.In Value: tx.Value(), Data: tx.Data(), AccessList: tx.AccessList(), + AuthList: tx.AuthList(), SkipNonceChecks: false, SkipFromEOACheck: false, BlobHashes: tx.BlobHashes(), @@ -303,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) @@ -366,6 +371,15 @@ func (st *stateTransition) preCheck() error { } } } + // Check that EIP-7702 authorization list signatures are well formed. + if msg.AuthList != nil { + if msg.To == nil { + return fmt.Errorf("%w (sender %v)", ErrSetCodeTxCreate, msg.From) + } + if len(msg.AuthList) == 0 { + return fmt.Errorf("%w (sender %v)", ErrEmptyAuthList, msg.From) + } + } return st.buyGas() } @@ -403,7 +417,7 @@ func (st *stateTransition) execute() (*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.AuthList, contractCreation, rules.IsHomestead, rules.IsIstanbul, rules.IsShanghai) if err != nil { return nil, err } @@ -449,8 +463,27 @@ func (st *stateTransition) execute() (*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.AuthList != nil { + for _, auth := range msg.AuthList { + // 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) } @@ -494,6 +527,64 @@ func (st *stateTransition) execute() (*ExecutionResult, error) { }, nil } +// validateAuthorization validates an EIP-7702 authorization against the state. +func (st *stateTransition) validateAuthorization(auth *types.Authorization) (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.Authorization) 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 diff --git a/core/txpool/validation.go b/core/txpool/validation.go index 33b383d5cf..908945471d 100644 --- a/core/txpool/validation.go +++ b/core/txpool/validation.go @@ -108,7 +108,7 @@ func ValidateTransaction(tx *types.Transaction, head *types.Header, signer types } // 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.AuthList(), 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/gen_authorization.go b/core/types/gen_authorization.go new file mode 100644 index 0000000000..b598b64ff7 --- /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 (a Authorization) MarshalJSON() ([]byte, error) { + type Authorization 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:"v" gencodec:"required"` + R uint256.Int `json:"r" gencodec:"required"` + S uint256.Int `json:"s" gencodec:"required"` + } + var enc Authorization + enc.ChainID = hexutil.Uint64(a.ChainID) + enc.Address = a.Address + enc.Nonce = hexutil.Uint64(a.Nonce) + enc.V = hexutil.Uint64(a.V) + enc.R = a.R + enc.S = a.S + return json.Marshal(&enc) +} + +// UnmarshalJSON unmarshals from JSON. +func (a *Authorization) UnmarshalJSON(input []byte) error { + type Authorization 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:"v" gencodec:"required"` + R *uint256.Int `json:"r" gencodec:"required"` + S *uint256.Int `json:"s" gencodec:"required"` + } + var dec Authorization + if err := json.Unmarshal(input, &dec); err != nil { + return err + } + if dec.ChainID == nil { + return errors.New("missing required field 'chainId' for Authorization") + } + a.ChainID = uint64(*dec.ChainID) + if dec.Address == nil { + return errors.New("missing required field 'address' for Authorization") + } + a.Address = *dec.Address + if dec.Nonce == nil { + return errors.New("missing required field 'nonce' for Authorization") + } + a.Nonce = uint64(*dec.Nonce) + if dec.V == nil { + return errors.New("missing required field 'v' for Authorization") + } + a.V = uint8(*dec.V) + if dec.R == nil { + return errors.New("missing required field 'r' for Authorization") + } + a.R = *dec.R + if dec.S == nil { + return errors.New("missing required field 's' for Authorization") + } + a.S = *dec.S + return nil +} 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 ea6f5ad6b9..b5fb3e2db2 100644 --- a/core/types/transaction.go +++ b/core/types/transaction.go @@ -48,6 +48,7 @@ const ( AccessListTxType = 0x01 DynamicFeeTxType = 0x02 BlobTxType = 0x03 + SetCodeTxType = 0x04 ) // Transaction is an Ethereum transaction. @@ -205,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 } @@ -471,6 +474,15 @@ func (tx *Transaction) WithBlobTxSidecar(sideCar *BlobTxSidecar) *Transaction { return cpy } +// AuthList returns the authorizations list of the transaction. +func (tx *Transaction) AuthList() []Authorization { + 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..4176a8220c 100644 --- a/core/types/transaction_marshalling.go +++ b/core/types/transaction_marshalling.go @@ -43,6 +43,7 @@ type txJSON struct { Input *hexutil.Bytes `json:"input"` AccessList *AccessList `json:"accessList,omitempty"` BlobVersionedHashes []common.Hash `json:"blobVersionedHashes,omitempty"` + AuthorizationList []Authorization `json:"authorizationList,omitempty"` V *hexutil.Big `json:"v"` R *hexutil.Big `json:"r"` S *hexutil.Big `json:"s"` @@ -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..78fa2fc8ef 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.AuthList(), + }) +} + 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..0fea9a63a7 --- /dev/null +++ b/core/types/tx_setcode.go @@ -0,0 +1,226 @@ +// 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 []Authorization + + // 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 Authorization -field-override authorizationMarshaling -out gen_authorization.go + +// Authorization is an authorization from an account to deploy code at its address. +type Authorization struct { + ChainID uint64 `json:"chainId" gencodec:"required"` + Address common.Address `json:"address" gencodec:"required"` + Nonce uint64 `json:"nonce" gencodec:"required"` + V uint8 `json:"v" 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 +} + +// SignAuth signs the provided authorization. +func SignAuth(auth Authorization, prv *ecdsa.PrivateKey) (Authorization, error) { + sighash := auth.sigHash() + sig, err := crypto.Sign(sighash[:], prv) + if err != nil { + return Authorization{}, err + } + return auth.withSignature(sig), nil +} + +// withSignature updates the signature of an Authorization to be equal the +// decoded signature provided in sig. +func (a *Authorization) withSignature(sig []byte) Authorization { + r, s, _ := decodeSignature(sig) + return Authorization{ + ChainID: a.ChainID, + Address: a.Address, + Nonce: a.Nonce, + V: sig[64], + R: *uint256.MustFromBig(r), + S: *uint256.MustFromBig(s), + } +} + +func (a *Authorization) sigHash() common.Hash { + return prefixedRlpHash(0x05, []any{ + a.ChainID, + a.Address, + a.Nonce, + }) +} + +// Authority recovers the the authorizing account of an authorization. +func (a *Authorization) 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([]Authorization, 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 index 45b317d3c0..5088231207 100644 --- a/core/verkle_witness_test.go +++ b/core/verkle_witness_test.go @@ -83,12 +83,12 @@ var ( func TestProcessVerkle(t *testing.T) { var ( code = common.FromHex(`6060604052600a8060106000396000f360606040526008565b00`) - intrinsicContractCreationGas, _ = IntrinsicGas(code, nil, true, true, true, true) + 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, true, true, true, true) + intrinsicCodeWithExtCodeCopyGas, _ = IntrinsicGas(codeWithExtCodeCopy, nil, nil, true, true, true, true) signer = types.LatestSigner(testVerkleChainConfig) testKey, _ = crypto.HexToECDSA("b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291") bcdb = rawdb.NewMemoryDatabase() // Database for the blockchain 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_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 07e4a272fa..1a0215459c 100644 --- a/core/vm/evm.go +++ b/core/vm/evm.go @@ -217,7 +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) + code := evm.resolveCode(addr) if len(code) == 0 { ret, err = nil, nil // gas is unchanged } else { @@ -225,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 } @@ -285,7 +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) - 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 } @@ -332,7 +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() - 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 } @@ -387,7 +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) - 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. @@ -567,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 } diff --git a/core/vm/interface.go b/core/vm/interface.go index 9229f4d2cd..011541dde3 100644 --- a/core/vm/interface.go +++ b/core/vm/interface.go @@ -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) diff --git a/core/vm/interpreter.go b/core/vm/interpreter.go index c408994401..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: 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/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/runtime/runtime_test.go b/core/vm/runtime/runtime_test.go index 0e774a01c2..6074e9a096 100644 --- a/core/vm/runtime/runtime_test.go +++ b/core/vm/runtime/runtime_test.go @@ -866,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/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/native/prestate.go b/eth/tracers/native/prestate.go index 9706eb43f6..5776275c2b 100644 --- a/eth/tracers/native/prestate.go +++ b/eth/tracers/native/prestate.go @@ -159,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.AuthList() { + addr, err := auth.Authority() + if err != nil { + continue + } + t.lookupAccount(addr) + } } func (t *prestateTracer) OnTxEnd(receipt *types.Receipt, err error) { diff --git a/graphql/graphql.go b/graphql/graphql.go index 6e364de6d9..7af1adbb4a 100644 --- a/graphql/graphql.go +++ b/graphql/graphql.go @@ -318,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 @@ -331,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 diff --git a/internal/ethapi/api.go b/internal/ethapi/api.go index de75c697ef..3f9ce46ddb 100644 --- a/internal/ethapi/api.go +++ b/internal/ethapi/api.go @@ -937,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.Authorization `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 @@ -1033,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.AuthList() } return result } diff --git a/internal/ethapi/api_test.go b/internal/ethapi/api_test.go index ae2ec9f0f0..0303a0a6ea 100644 --- a/internal/ethapi/api_test.go +++ b/internal/ethapi/api_test.go @@ -629,12 +629,13 @@ 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 @@ -819,6 +820,26 @@ func TestEstimateGas(t *testing.T) { 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, &tc.blockOverrides) diff --git a/internal/ethapi/transaction_args.go b/internal/ethapi/transaction_args.go index 2a0508b147..3942540b04 100644 --- a/internal/ethapi/transaction_args.go +++ b/internal/ethapi/transaction_args.go @@ -72,6 +72,9 @@ type TransactionArgs struct { Commitments []kzg4844.Commitment `json:"commitments"` Proofs []kzg4844.Proof `json:"proofs"` + // For SetCodeTxType + AuthorizationList []types.Authorization `json:"authorizationList"` + // This configures whether blobs are allowed to be passed. blobSidecarAllowed bool } @@ -473,6 +476,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: @@ -486,6 +491,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.Authorization{} + 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/params/protocol_params.go b/params/protocol_params.go index 90e7487cff..4d2baf8054 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. 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_util.go b/tests/state_test_util.go index 6884ae7ed5..e735ce2fb8 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`, @@ -419,6 +441,20 @@ func (tx *stTransaction) toMessage(ps stPostState, baseFee *big.Int) (*core.Mess if gasPrice == nil { return nil, errors.New("no gas price provided") } + var authList []types.Authorization + if tx.AuthorizationList != nil { + authList = make([]types.Authorization, len(tx.AuthorizationList)) + for i, auth := range tx.AuthorizationList { + authList[i] = types.Authorization{ + 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, @@ -433,6 +469,7 @@ func (tx *stTransaction) toMessage(ps stPostState, baseFee *big.Int) (*core.Mess AccessList: accessList, BlobHashes: tx.BlobVersionedHashes, BlobGasFeeCap: tx.BlobGasFeeCap, + AuthList: authList, } return msg, nil } diff --git a/tests/transaction_test_util.go b/tests/transaction_test_util.go index d3dbbd5db2..4da27ff943 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.AuthList(), tx.To() == nil, isHomestead, isIstanbul, false) if err != nil { return nil, nil, err } From bc1ec69008174aa2f5fd3642adeafb0459d2464b Mon Sep 17 00:00:00 2001 From: rjl493456442 Date: Mon, 16 Dec 2024 21:10:08 +0800 Subject: [PATCH 14/14] trie/pathdb: state iterator (snapshot integration pt 4) (#30654) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit In this pull request, the state iterator is implemented. It's mostly a copy-paste from the original state snapshot package, but still has some important changes to highlight here: (a) The iterator for the disk layer consists of a diff iterator and a disk iterator. Originally, the disk layer in the state snapshot was a wrapper around the disk, and its corresponding iterator was also a wrapper around the disk iterator. However, due to structural differences, the disk layer iterator is divided into two parts: - The disk iterator, which traverses the content stored on disk. - The diff iterator, which traverses the aggregated state buffer. Checkout `BinaryIterator` and `FastIterator` for more details. (b) The staleness management is improved in the diffAccountIterator and diffStorageIterator Originally, in the `diffAccountIterator`, the layer’s staleness had to be checked within the Next function to ensure the iterator remained usable. Additionally, a read lock on the associated diff layer was required to first retrieve the account blob. This read lock protection is essential to prevent concurrent map read/write. Afterward, a staleness check was performed to ensure the retrieved data was not outdated. The entire logic can be simplified as follows: a loadAccount callback is provided to retrieve account data. If the corresponding state is immutable (e.g., diff layers in the path database), the staleness check can be skipped, and a single account data retrieval is sufficient. However, if the corresponding state is mutable (e.g., the disk layer in the path database), the callback can operate as follows: ```go 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) } ``` The callback solution can eliminate the complexity for managing concurrency with the read lock for atomic operation. --- triedb/pathdb/database.go | 12 + triedb/pathdb/holdable_iterator.go | 97 ++ triedb/pathdb/holdable_iterator_test.go | 176 ++++ triedb/pathdb/iterator.go | 369 +++++++ triedb/pathdb/iterator_binary.go | 344 +++++++ triedb/pathdb/iterator_fast.go | 380 ++++++++ triedb/pathdb/iterator_test.go | 1162 +++++++++++++++++++++++ triedb/pathdb/reader.go | 11 + triedb/pathdb/states.go | 28 +- triedb/pathdb/states_test.go | 118 +-- 10 files changed, 2634 insertions(+), 63 deletions(-) create mode 100644 triedb/pathdb/holdable_iterator.go create mode 100644 triedb/pathdb/holdable_iterator_test.go create mode 100644 triedb/pathdb/iterator.go create mode 100644 triedb/pathdb/iterator_binary.go create mode 100644 triedb/pathdb/iterator_fast.go create mode 100644 triedb/pathdb/iterator_test.go diff --git a/triedb/pathdb/database.go b/triedb/pathdb/database.go index cfbdb01c49..914b17de5b 100644 --- a/triedb/pathdb/database.go +++ b/triedb/pathdb/database.go @@ -555,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/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/reader.go b/triedb/pathdb/reader.go index 4bba813b14..a404409035 100644 --- a/triedb/pathdb/reader.go +++ b/triedb/pathdb/reader.go @@ -86,6 +86,17 @@ func (r *reader) Node(owner common.Hash, path []byte, hash common.Hash) ([]byte, return blob, nil } +// 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. diff --git a/triedb/pathdb/states.go b/triedb/pathdb/states.go index 6e633db272..81d34da5df 100644 --- a/triedb/pathdb/states.go +++ b/triedb/pathdb/states.go @@ -98,6 +98,17 @@ func (s *stateSet) account(hash common.Hash) ([]byte, bool) { 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) { @@ -110,6 +121,19 @@ func (s *stateSet) storage(accountHash, storageHash common.Hash) ([]byte, bool) 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 { @@ -132,8 +156,6 @@ func (s *stateSet) check() uint64 { // the deleted ones. // // Note, the returned slice is not a copy, so do not modify it. -// -// nolint:unused func (s *stateSet) accountList() []common.Hash { // If an old list already exists, return it s.listLock.RLock() @@ -160,8 +182,6 @@ func (s *stateSet) accountList() []common.Hash { // storage slot. // // Note, the returned slice is not a copy, so do not modify it. -// -// nolint:unused func (s *stateSet) storageList(accountHash common.Hash) []common.Hash { s.listLock.RLock() if _, ok := s.storageData[accountHash]; !ok { diff --git a/triedb/pathdb/states_test.go b/triedb/pathdb/states_test.go index 4557fa958d..f097e90e81 100644 --- a/triedb/pathdb/states_test.go +++ b/triedb/pathdb/states_test.go @@ -28,39 +28,39 @@ import ( func TestStatesMerge(t *testing.T) { a := newStates( map[common.Hash][]byte{ - common.Hash{0xa}: {0xa0}, - common.Hash{0xb}: {0xb0}, - common.Hash{0xc}: {0xc0}, + {0xa}: {0xa0}, + {0xb}: {0xb0}, + {0xc}: {0xc0}, }, map[common.Hash]map[common.Hash][]byte{ - common.Hash{0xa}: { + {0xa}: { common.Hash{0x1}: {0x10}, common.Hash{0x2}: {0x20}, }, - common.Hash{0xb}: { + {0xb}: { common.Hash{0x1}: {0x10}, }, - common.Hash{0xc}: { + {0xc}: { common.Hash{0x1}: {0x10}, }, }, ) b := newStates( map[common.Hash][]byte{ - common.Hash{0xa}: {0xa1}, - common.Hash{0xb}: {0xb1}, - common.Hash{0xc}: nil, // delete account + {0xa}: {0xa1}, + {0xb}: {0xb1}, + {0xc}: nil, // delete account }, map[common.Hash]map[common.Hash][]byte{ - common.Hash{0xa}: { + {0xa}: { common.Hash{0x1}: {0x11}, common.Hash{0x2}: nil, // delete slot common.Hash{0x3}: {0x31}, }, - common.Hash{0xb}: { + {0xb}: { common.Hash{0x1}: {0x11}, }, - common.Hash{0xc}: { + {0xc}: { common.Hash{0x1}: nil, // delete slot }, }, @@ -116,39 +116,39 @@ func TestStatesMerge(t *testing.T) { func TestStatesRevert(t *testing.T) { a := newStates( map[common.Hash][]byte{ - common.Hash{0xa}: {0xa0}, - common.Hash{0xb}: {0xb0}, - common.Hash{0xc}: {0xc0}, + {0xa}: {0xa0}, + {0xb}: {0xb0}, + {0xc}: {0xc0}, }, map[common.Hash]map[common.Hash][]byte{ - common.Hash{0xa}: { + {0xa}: { common.Hash{0x1}: {0x10}, common.Hash{0x2}: {0x20}, }, - common.Hash{0xb}: { + {0xb}: { common.Hash{0x1}: {0x10}, }, - common.Hash{0xc}: { + {0xc}: { common.Hash{0x1}: {0x10}, }, }, ) b := newStates( map[common.Hash][]byte{ - common.Hash{0xa}: {0xa1}, - common.Hash{0xb}: {0xb1}, - common.Hash{0xc}: nil, + {0xa}: {0xa1}, + {0xb}: {0xb1}, + {0xc}: nil, }, map[common.Hash]map[common.Hash][]byte{ - common.Hash{0xa}: { + {0xa}: { common.Hash{0x1}: {0x11}, common.Hash{0x2}: nil, common.Hash{0x3}: {0x31}, }, - common.Hash{0xb}: { + {0xb}: { common.Hash{0x1}: {0x11}, }, - common.Hash{0xc}: { + {0xc}: { common.Hash{0x1}: nil, }, }, @@ -156,20 +156,20 @@ func TestStatesRevert(t *testing.T) { a.merge(b) a.revertTo( map[common.Hash][]byte{ - common.Hash{0xa}: {0xa0}, - common.Hash{0xb}: {0xb0}, - common.Hash{0xc}: {0xc0}, + {0xa}: {0xa0}, + {0xb}: {0xb0}, + {0xc}: {0xc0}, }, map[common.Hash]map[common.Hash][]byte{ - common.Hash{0xa}: { + {0xa}: { common.Hash{0x1}: {0x10}, common.Hash{0x2}: {0x20}, common.Hash{0x3}: nil, }, - common.Hash{0xb}: { + {0xb}: { common.Hash{0x1}: {0x10}, }, - common.Hash{0xc}: { + {0xc}: { common.Hash{0x1}: {0x10}, }, }, @@ -227,14 +227,14 @@ func TestStateRevertAccountNullMarker(t *testing.T) { a := newStates(nil, nil) // empty initial state b := newStates( map[common.Hash][]byte{ - common.Hash{0xa}: {0xa}, + {0xa}: {0xa}, }, nil, ) a.merge(b) // create account 0xa a.revertTo( map[common.Hash][]byte{ - common.Hash{0xa}: nil, + {0xa}: nil, }, nil, ) // revert the transition b @@ -253,13 +253,13 @@ func TestStateRevertAccountNullMarker(t *testing.T) { // entry in the set. func TestStateRevertStorageNullMarker(t *testing.T) { a := newStates(map[common.Hash][]byte{ - common.Hash{0xa}: {0xa}, + {0xa}: {0xa}, }, nil) // initial state with account 0xa b := newStates( nil, map[common.Hash]map[common.Hash][]byte{ - common.Hash{0xa}: { + {0xa}: { common.Hash{0x1}: {0x1}, }, }, @@ -268,7 +268,7 @@ func TestStateRevertStorageNullMarker(t *testing.T) { a.revertTo( nil, map[common.Hash]map[common.Hash][]byte{ - common.Hash{0xa}: { + {0xa}: { common.Hash{0x1}: nil, }, }, @@ -286,10 +286,10 @@ func TestStateRevertStorageNullMarker(t *testing.T) { func TestStatesEncode(t *testing.T) { s := newStates( map[common.Hash][]byte{ - common.Hash{0x1}: {0x1}, + {0x1}: {0x1}, }, map[common.Hash]map[common.Hash][]byte{ - common.Hash{0x1}: { + {0x1}: { common.Hash{0x1}: {0x1}, }, }, @@ -313,18 +313,18 @@ func TestStatesEncode(t *testing.T) { func TestStateWithOriginEncode(t *testing.T) { s := NewStateSetWithOrigin( map[common.Hash][]byte{ - common.Hash{0x1}: {0x1}, + {0x1}: {0x1}, }, map[common.Hash]map[common.Hash][]byte{ - common.Hash{0x1}: { + {0x1}: { common.Hash{0x1}: {0x1}, }, }, map[common.Address][]byte{ - common.Address{0x1}: {0x1}, + {0x1}: {0x1}, }, map[common.Address]map[common.Hash][]byte{ - common.Address{0x1}: { + {0x1}: { common.Hash{0x1}: {0x1}, }, }, @@ -359,19 +359,19 @@ func TestStateSizeTracking(t *testing.T) { a := newStates( map[common.Hash][]byte{ - common.Hash{0xa}: {0xa0}, // common.HashLength+1 - common.Hash{0xb}: {0xb0}, // common.HashLength+1 - common.Hash{0xc}: {0xc0}, // common.HashLength+1 + {0xa}: {0xa0}, // common.HashLength+1 + {0xb}: {0xb0}, // common.HashLength+1 + {0xc}: {0xc0}, // common.HashLength+1 }, map[common.Hash]map[common.Hash][]byte{ - common.Hash{0xa}: { + {0xa}: { common.Hash{0x1}: {0x10}, // 2*common.HashLength+1 common.Hash{0x2}: {0x20}, // 2*common.HashLength+1 }, - common.Hash{0xb}: { + {0xb}: { common.Hash{0x1}: {0x10, 0x11, 0x12}, // 2*common.HashLength+3 }, - common.Hash{0xc}: { + {0xc}: { common.Hash{0x1}: {0x10}, // 2*common.HashLength+1 }, }, @@ -386,21 +386,21 @@ func TestStateSizeTracking(t *testing.T) { 3*2*common.HashLength /* storage data of 0xc */ b := newStates( map[common.Hash][]byte{ - common.Hash{0xa}: {0xa1, 0xa1}, // common.HashLength+2 - common.Hash{0xb}: {0xb1, 0xb1, 0xb1}, // common.HashLength+3 - common.Hash{0xc}: nil, // common.HashLength, account deletion + {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{ - common.Hash{0xa}: { + {0xa}: { common.Hash{0x1}: {0x11, 0x11, 0x11}, // 2*common.HashLength+3 common.Hash{0x3}: {0x31, 0x31}, // 2*common.HashLength+2, slot creation }, - common.Hash{0xb}: { + {0xb}: { common.Hash{0x1}: {0x11, 0x11}, // 2*common.HashLength+2 common.Hash{0x2}: {0x22, 0x22}, // 2*common.HashLength+2, slot creation }, // The storage of 0xc is entirely removed - common.Hash{0xc}: { + {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 @@ -424,21 +424,21 @@ func TestStateSizeTracking(t *testing.T) { // Revert the set to original status a.revertTo( map[common.Hash][]byte{ - common.Hash{0xa}: {0xa0}, - common.Hash{0xb}: {0xb0}, - common.Hash{0xc}: {0xc0}, + {0xa}: {0xa0}, + {0xb}: {0xb0}, + {0xc}: {0xc0}, }, map[common.Hash]map[common.Hash][]byte{ - common.Hash{0xa}: { + {0xa}: { common.Hash{0x1}: {0x10}, common.Hash{0x2}: {0x20}, common.Hash{0x3}: nil, // revert slot creation }, - common.Hash{0xb}: { + {0xb}: { common.Hash{0x1}: {0x10, 0x11, 0x12}, common.Hash{0x2}: nil, // revert slot creation }, - common.Hash{0xc}: { + {0xc}: { common.Hash{0x1}: {0x10}, common.Hash{0x2}: {0x20}, // resurrected slot common.Hash{0x3}: {0x30}, // resurrected slot