From d9261b6e2f2e8a7c672afa07001c72d90c837af6 Mon Sep 17 00:00:00 2001 From: Ian Davis Date: Mon, 23 Jan 2023 14:37:43 +0000 Subject: [PATCH 01/30] itests: validate responses from ethereum json-rpc api --- go.mod | 21 +- go.sum | 113 +- itests/eth_conformance_test.go | 202 ++++ itests/specs/eth_openrpc.json | 1786 ++++++++++++++++++++++++++++++++ node/impl/full/eth.go | 17 +- 5 files changed, 2126 insertions(+), 13 deletions(-) create mode 100644 itests/eth_conformance_test.go create mode 100644 itests/specs/eth_openrpc.json diff --git a/go.mod b/go.mod index 485bfb5a4..8fa2d0350 100644 --- a/go.mod +++ b/go.mod @@ -62,10 +62,15 @@ require ( github.com/gbrlsnchs/jwt/v3 v3.0.1 github.com/gdamore/tcell/v2 v2.2.0 github.com/go-kit/kit v0.12.0 + github.com/go-openapi/errors v0.19.9 + github.com/go-openapi/spec v0.20.8 + github.com/go-openapi/strfmt v0.21.1 + github.com/go-openapi/validate v0.22.1 github.com/golang/mock v1.6.0 github.com/google/uuid v1.3.0 github.com/gorilla/mux v1.8.0 github.com/gorilla/websocket v1.5.0 + github.com/gregdhill/go-openrpc v0.0.0-20220114144539-ae6f44720487 github.com/hako/durafmt v0.0.0-20200710122514-c0fb7b4da026 github.com/hannahhoward/go-pubsub v0.0.0-20200423002714-8d62886cc36e github.com/hashicorp/go-multierror v1.1.1 @@ -168,13 +173,12 @@ require ( require ( github.com/GeertJohan/go.incremental v1.0.0 // indirect - github.com/PuerkitoBio/purell v1.1.1 // indirect - github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578 // indirect github.com/StackExchange/wmi v1.2.1 // indirect github.com/Stebalien/go-bitfield v0.0.1 // indirect github.com/akavel/rsrc v0.8.0 // indirect github.com/alecthomas/units v0.0.0-20210927113745-59d0afb8317a // indirect github.com/armon/go-metrics v0.3.9 // indirect + github.com/asaskevich/govalidator v0.0.0-20200907205600-7a23bdc65eef // indirect github.com/benbjohnson/clock v1.3.0 // indirect github.com/beorn7/perks v1.0.1 // indirect github.com/bep/debounce v1.2.1 // indirect @@ -211,10 +215,12 @@ require ( github.com/go-logr/logr v1.2.3 // indirect github.com/go-logr/stdr v1.2.2 // indirect github.com/go-ole/go-ole v1.2.5 // indirect - github.com/go-openapi/jsonpointer v0.19.3 // indirect - github.com/go-openapi/jsonreference v0.19.4 // indirect - github.com/go-openapi/spec v0.19.11 // indirect - github.com/go-openapi/swag v0.19.11 // indirect + github.com/go-openapi/analysis v0.21.2 // indirect + github.com/go-openapi/jsonpointer v0.19.5 // indirect + github.com/go-openapi/jsonreference v0.20.0 // indirect + github.com/go-openapi/loads v0.21.1 // indirect + github.com/go-openapi/swag v0.21.1 // indirect + github.com/go-stack/stack v1.8.0 // indirect github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0 // indirect github.com/godbus/dbus/v5 v5.1.0 // indirect github.com/gogo/protobuf v1.3.2 // indirect @@ -289,6 +295,7 @@ require ( github.com/mikioh/tcpinfo v0.0.0-20190314235526-30a79bb1804b // indirect github.com/mikioh/tcpopt v0.0.0-20190314235656-172688c1accc // indirect github.com/minio/sha256-simd v1.0.0 // indirect + github.com/mitchellh/mapstructure v1.4.2 // indirect github.com/mr-tron/base58 v1.2.0 // indirect github.com/multiformats/go-base36 v0.1.0 // indirect github.com/multiformats/go-multiaddr-fmt v0.1.0 // indirect @@ -297,6 +304,7 @@ require ( github.com/nikkolasg/hexjson v0.0.0-20181101101858-78e39397e00c // indirect github.com/nkovacs/streamquote v1.0.0 // indirect github.com/nxadm/tail v1.4.8 // indirect + github.com/oklog/ulid v1.3.1 // indirect github.com/onsi/ginkgo v1.16.5 // indirect github.com/opencontainers/runtime-spec v1.0.2 // indirect github.com/opentracing/opentracing-go v1.2.0 // indirect @@ -327,6 +335,7 @@ require ( github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673 // indirect github.com/zondax/hid v0.9.1 // indirect github.com/zondax/ledger-go v0.12.1 // indirect + go.mongodb.org/mongo-driver v1.7.5 // indirect go.opentelemetry.io/otel/metric v0.33.0 // indirect go.opentelemetry.io/otel/sdk/metric v0.33.0 // indirect go.opentelemetry.io/otel/trace v1.11.1 // indirect diff --git a/go.sum b/go.sum index 6ae2f7c4b..e8dfd68ee 100644 --- a/go.sum +++ b/go.sum @@ -69,9 +69,7 @@ github.com/Masterminds/semver v1.4.2/go.mod h1:MB6lktGJrhw8PrUyiEoblNEGEQ+RzHPF0 github.com/Masterminds/vcs v1.13.0/go.mod h1:N09YCmOQr6RLxC6UNHzuVwAdodYbbnycGHSmwVJjcKA= github.com/OneOfOne/xxhash v1.2.2 h1:KMrpdQIwFcEqXDklaen+P1axHaj9BSKzvpUUfnHldSE= github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU= -github.com/PuerkitoBio/purell v1.1.1 h1:WEQqlqaGbrPkxLJWfBwQmfEAE1Z7ONdDLqrN38tNFfI= github.com/PuerkitoBio/purell v1.1.1/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0= -github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578 h1:d+Bc7a5rLufV/sSk/8dngufqelfh6jnri85riMAaF/M= github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578/go.mod h1:uGdkoq3SwY9Y+13GIhn11/XLaGBb4BfwItxLd5jeuXE= github.com/Shopify/sarama v1.19.0/go.mod h1:FVkBWblsNy7DGZRfXLU0O9RCGt5g3g3yEuWXgklEdEo= github.com/Shopify/toxiproxy v2.1.4+incompatible/go.mod h1:OXgGpZ6Cli1/URJOF1DMxUHB2q5Ap20/P/eIdh4G0pI= @@ -108,6 +106,8 @@ github.com/armon/go-metrics v0.3.9/go.mod h1:4O98XIr/9W0sxpJ8UaYkvjk10Iff7SnFrb4 github.com/armon/go-radix v0.0.0-20180808171621-7fddfc383310/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8= github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5/go.mod h1:wHh0iHkYZB8zMSxRWpUBQtwG5a7fFgvEO+odwuTv2gs= github.com/aryann/difflib v0.0.0-20170710044230-e206f873d14a/go.mod h1:DAHtR1m6lCRdSC2Tm3DSWRPvIPr6xNKyeHdqDQSQT+A= +github.com/asaskevich/govalidator v0.0.0-20200907205600-7a23bdc65eef h1:46PFijGLmAjMPwCCCo7Jf0W6f9slllCkkv7vyc1yOSg= +github.com/asaskevich/govalidator v0.0.0-20200907205600-7a23bdc65eef/go.mod h1:WaHUgvxTVq04UNunO+XhnAqY/wQc+bxr74GqbsZ/Jqw= github.com/aws/aws-lambda-go v1.13.3/go.mod h1:4UKl9IzQMoD+QF79YdCuzCwp8VbmG4VAQwij/eHl5CU= github.com/aws/aws-sdk-go v1.27.0/go.mod h1:KmX6BPdI08NWTb3/sm4ZGu5ShLoqVDhKgpiN924inxo= github.com/aws/aws-sdk-go v1.32.11/go.mod h1:5zCpMtNQVjRREroY7sYe8lOMRSxkhG6MZveU8YkpAk0= @@ -444,26 +444,74 @@ github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag= github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE= github.com/go-ole/go-ole v1.2.5 h1:t4MGB5xEDZvXI+0rMjjsfBsD7yAgp/s9ZDkL1JndXwY= github.com/go-ole/go-ole v1.2.5/go.mod h1:pprOEPIfldk/42T2oK7lQ4v4JSDwmV0As9GaiUsvbm0= +github.com/go-openapi/analysis v0.21.2 h1:hXFrOYFHUAMQdu6zwAiKKJHJQ8kqZs1ux/ru1P1wLJU= +github.com/go-openapi/analysis v0.21.2/go.mod h1:HZwRk4RRisyG8vx2Oe6aqeSQcoxRp47Xkp3+K6q+LdY= +github.com/go-openapi/errors v0.19.8/go.mod h1:cM//ZKUKyO06HSwqAelJ5NsEMMcpa6VpXe8DOa1Mi1M= +github.com/go-openapi/errors v0.19.9 h1:9SnKdGhiPZHF3ttwFMiCBEb8jQ4IDdrK+5+a0oTygA4= +github.com/go-openapi/errors v0.19.9/go.mod h1:cM//ZKUKyO06HSwqAelJ5NsEMMcpa6VpXe8DOa1Mi1M= github.com/go-openapi/jsonpointer v0.19.2/go.mod h1:3akKfEdA7DF1sugOqz1dVQHBcuDBPKZGEoHC/NkiQRg= -github.com/go-openapi/jsonpointer v0.19.3 h1:gihV7YNZK1iK6Tgwwsxo2rJbD1GTbdm72325Bq8FI3w= github.com/go-openapi/jsonpointer v0.19.3/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg= +github.com/go-openapi/jsonpointer v0.19.5 h1:gZr+CIYByUqjcgeLXnQu2gHYQC9o73G2XUeOFYEICuY= +github.com/go-openapi/jsonpointer v0.19.5/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg= github.com/go-openapi/jsonreference v0.19.2/go.mod h1:jMjeRr2HHw6nAVajTXJ4eiUwohSTlpa0o73RUL1owJc= github.com/go-openapi/jsonreference v0.19.3/go.mod h1:rjx6GuL8TTa9VaixXglHmQmIL98+wF9xc8zWvFonSJ8= -github.com/go-openapi/jsonreference v0.19.4 h1:3Vw+rh13uq2JFNxgnMTGE1rnoieU9FmyE1gvnyylsYg= github.com/go-openapi/jsonreference v0.19.4/go.mod h1:RdybgQwPxbL4UEjuAruzK1x3nE69AqPYEJeo/TWfEeg= +github.com/go-openapi/jsonreference v0.19.6/go.mod h1:diGHMEHg2IqXZGKxqyvWdfWU/aim5Dprw5bqpKkTvns= +github.com/go-openapi/jsonreference v0.20.0 h1:MYlu0sBgChmCfJxxUKZ8g1cPWFOB37YSZqewK7OKeyA= +github.com/go-openapi/jsonreference v0.20.0/go.mod h1:Ag74Ico3lPc+zR+qjn4XBUmXymS4zJbYVCZmcgkasdo= +github.com/go-openapi/loads v0.21.1 h1:Wb3nVZpdEzDTcly8S4HMkey6fjARRzb7iEaySimlDW0= +github.com/go-openapi/loads v0.21.1/go.mod h1:/DtAMXXneXFjbQMGEtbamCZb+4x7eGwkvZCvBmwUG+g= +github.com/go-openapi/spec v0.19.2/go.mod h1:sCxk3jxKgioEJikev4fgkNmwS+3kuYdJtcsZsD5zxMY= github.com/go-openapi/spec v0.19.7/go.mod h1:Hm2Jr4jv8G1ciIAo+frC/Ft+rR2kQDh8JHKHb3gWUSk= -github.com/go-openapi/spec v0.19.11 h1:ogU5q8dtp3MMPn59a9VRrPKVxvJHEs5P7yNMR5sNnis= github.com/go-openapi/spec v0.19.11/go.mod h1:vqK/dIdLGCosfvYsQV3WfC7N3TiZSnGY2RZKoFK7X28= +github.com/go-openapi/spec v0.20.4/go.mod h1:faYFR1CvsJZ0mNsmsphTMSoRrNV3TEDoAM7FOEWeq8I= +github.com/go-openapi/spec v0.20.8 h1:ubHmXNY3FCIOinT8RNrrPfGc9t7I1qhPtdOGoG2AxRU= +github.com/go-openapi/spec v0.20.8/go.mod h1:2OpW+JddWPrpXSCIX8eOx7lZ5iyuWj3RYR6VaaBKcWA= +github.com/go-openapi/strfmt v0.21.0/go.mod h1:ZRQ409bWMj+SOgXofQAGTIo2Ebu72Gs+WaRADcS5iNg= +github.com/go-openapi/strfmt v0.21.1 h1:G6s2t5V5kGCHLVbSdZ/6lI8Wm4OzoPFkc3/cjAsKQrM= +github.com/go-openapi/strfmt v0.21.1/go.mod h1:I/XVKeLc5+MM5oPNN7P6urMOpuLXEcNrCX/rPGuWb0k= github.com/go-openapi/swag v0.19.2/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh66Z9tfKk= github.com/go-openapi/swag v0.19.5/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh66Z9tfKk= github.com/go-openapi/swag v0.19.8/go.mod h1:ao+8BpOPyKdpQz3AOJfbeEVpLmWAvlT1IfTe5McPyhY= -github.com/go-openapi/swag v0.19.11 h1:RFTu/dlFySpyVvJDfp/7674JY4SDglYWKztbiIGFpmc= github.com/go-openapi/swag v0.19.11/go.mod h1:Uc0gKkdR+ojzsEpjh39QChyu92vPgIr72POcgHMAgSY= +github.com/go-openapi/swag v0.19.15/go.mod h1:QYRuS/SOXUCsnplDa677K7+DxSOj6IPNl/eQntq43wQ= +github.com/go-openapi/swag v0.21.1 h1:wm0rhTb5z7qpJRHBdPOMuY4QjVUMbF6/kwoYeRAOrKU= +github.com/go-openapi/swag v0.21.1/go.mod h1:QYRuS/SOXUCsnplDa677K7+DxSOj6IPNl/eQntq43wQ= +github.com/go-openapi/validate v0.22.1 h1:G+c2ub6q47kfX1sOBLwIQwzBVt8qmOAARyo/9Fqs9NU= +github.com/go-openapi/validate v0.22.1/go.mod h1:rjnrwK57VJ7A8xqfpAOEKRH8yQSGUriMu5/zuPSQ1hg= github.com/go-sql-driver/mysql v1.4.0/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG5ZlKdlhCg5w= github.com/go-sql-driver/mysql v1.5.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg= +github.com/go-stack/stack v1.8.0 h1:5SgMzNM5HxrEjV0ww2lTmX6E2Izsfxas4+YHWRs3Lsk= github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0 h1:p104kn46Q8WdvHunIJ9dAyjPVtrBPhSr3KT2yUst43I= github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0/go.mod h1:fyg7847qk6SyHyPtNmDHnmrv/HOrqktSC+C9fM+CJOE= +github.com/gobuffalo/attrs v0.0.0-20190224210810-a9411de4debd/go.mod h1:4duuawTqi2wkkpB4ePgWMaai6/Kc6WEz83bhFwpHzj0= +github.com/gobuffalo/depgen v0.0.0-20190329151759-d478694a28d3/go.mod h1:3STtPUQYuzV0gBVOY3vy6CfMm/ljR4pABfrTeHNLHUY= +github.com/gobuffalo/depgen v0.1.0/go.mod h1:+ifsuy7fhi15RWncXQQKjWS9JPkdah5sZvtHc2RXGlg= +github.com/gobuffalo/envy v1.6.15/go.mod h1:n7DRkBerg/aorDM8kbduw5dN3oXGswK5liaSCx4T5NI= +github.com/gobuffalo/envy v1.7.0/go.mod h1:n7DRkBerg/aorDM8kbduw5dN3oXGswK5liaSCx4T5NI= +github.com/gobuffalo/flect v0.1.0/go.mod h1:d2ehjJqGOH/Kjqcoz+F7jHTBbmDb38yXA598Hb50EGs= +github.com/gobuffalo/flect v0.1.1/go.mod h1:8JCgGVbRjJhVgD6399mQr4fx5rRfGKVzFjbj6RE/9UI= +github.com/gobuffalo/flect v0.1.3/go.mod h1:8JCgGVbRjJhVgD6399mQr4fx5rRfGKVzFjbj6RE/9UI= +github.com/gobuffalo/genny v0.0.0-20190329151137-27723ad26ef9/go.mod h1:rWs4Z12d1Zbf19rlsn0nurr75KqhYp52EAGGxTbBhNk= +github.com/gobuffalo/genny v0.0.0-20190403191548-3ca520ef0d9e/go.mod h1:80lIj3kVJWwOrXWWMRzzdhW3DsrdjILVil/SFKBzF28= +github.com/gobuffalo/genny v0.1.0/go.mod h1:XidbUqzak3lHdS//TPu2OgiFB+51Ur5f7CSnXZ/JDvo= +github.com/gobuffalo/genny v0.1.1/go.mod h1:5TExbEyY48pfunL4QSXxlDOmdsD44RRq4mVZ0Ex28Xk= +github.com/gobuffalo/gitgen v0.0.0-20190315122116-cc086187d211/go.mod h1:vEHJk/E9DmhejeLeNt7UVvlSGv3ziL+djtTr3yyzcOw= +github.com/gobuffalo/gogen v0.0.0-20190315121717-8f38393713f5/go.mod h1:V9QVDIxsgKNZs6L2IYiGR8datgMhB577vzTDqypH360= +github.com/gobuffalo/gogen v0.1.0/go.mod h1:8NTelM5qd8RZ15VjQTFkAW6qOMx5wBbW4dSCS3BY8gg= +github.com/gobuffalo/gogen v0.1.1/go.mod h1:y8iBtmHmGc4qa3urIyo1shvOD8JftTtfcKi+71xfDNE= +github.com/gobuffalo/logger v0.0.0-20190315122211-86e12af44bc2/go.mod h1:QdxcLw541hSGtBnhUc4gaNIXRjiDppFGaDqzbrBd3v8= +github.com/gobuffalo/logger v1.0.0/go.mod h1:2zbswyIUa45I+c+FLXuWl9zSWEiVuthsk8ze5s8JvPs= +github.com/gobuffalo/mapi v1.0.1/go.mod h1:4VAGh89y6rVOvm5A8fKFxYG+wIW6LO1FMTG9hnKStFc= +github.com/gobuffalo/mapi v1.0.2/go.mod h1:4VAGh89y6rVOvm5A8fKFxYG+wIW6LO1FMTG9hnKStFc= +github.com/gobuffalo/packd v0.0.0-20190315124812-a385830c7fc0/go.mod h1:M2Juc+hhDXf/PnmBANFCqx4DM3wRbgDvnVWeG2RIxq4= +github.com/gobuffalo/packd v0.1.0/go.mod h1:M2Juc+hhDXf/PnmBANFCqx4DM3wRbgDvnVWeG2RIxq4= +github.com/gobuffalo/packd v0.3.0/go.mod h1:zC7QkmNkYVGKPw4tHpBQ+ml7W/3tIebgeo1b36chA3Q= +github.com/gobuffalo/packr/v2 v2.0.9/go.mod h1:emmyGweYTm6Kdper+iywB6YK5YzuKchGtJQZ0Odn4pQ= +github.com/gobuffalo/packr/v2 v2.2.0/go.mod h1:CaAwI0GPIAv+5wKLtv8Afwl+Cm78K/I/VCm/3ptBN+0= +github.com/gobuffalo/packr/v2 v2.6.0/go.mod h1:sgEE1xNZ6G0FNN5xn9pevVu4nywaxHvgup67xisti08= +github.com/gobuffalo/syncx v0.0.0-20190224160051-33c29581e754/go.mod h1:HhnNqWY95UYwwW3uSASeV7vtgYkT2t16hJgV3AEPUpw= github.com/godbus/dbus v0.0.0-20190402143921-271e53dc4968/go.mod h1:/YcGZj5zSblfDWMMoOzV4fas9FZnQYTkDnsGvmh2Grw= github.com/godbus/dbus/v5 v5.0.3/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= @@ -586,6 +634,8 @@ github.com/gorilla/websocket v1.4.1/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/ad github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= github.com/gorilla/websocket v1.5.0 h1:PPwGk2jz7EePpoHN/+ClbZu8SPxiqlu12wZP/3sWmnc= github.com/gorilla/websocket v1.5.0/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= +github.com/gregdhill/go-openrpc v0.0.0-20220114144539-ae6f44720487 h1:NyaWOSkqFK1d9o+HLfnMIGzrHuUUPeBNIZyi5Zoe/lY= +github.com/gregdhill/go-openrpc v0.0.0-20220114144539-ae6f44720487/go.mod h1:a1eRkbhd3DYpRH2lnuUsVG+QMTI+v0hGnsis8C9hMrA= github.com/gregjones/httpcache v0.0.0-20180305231024-9cad4c3443a7/go.mod h1:FecbI9+v66THATjSRHfNgh1IVFe/9kFxbXtjV0ctIMA= github.com/grpc-ecosystem/go-grpc-middleware v1.0.1-0.20190118093823-f849b5445de4/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs= github.com/grpc-ecosystem/go-grpc-middleware v1.1.0/go.mod h1:f5nM7jw/oeRSadq3xCzHAvxcr8HZnzsqU6ILg/0NiiE= @@ -665,6 +715,7 @@ github.com/icza/backscanner v0.0.0-20210726202459-ac2ffc679f94 h1:9tcYMdi+7Rb1y0 github.com/icza/backscanner v0.0.0-20210726202459-ac2ffc679f94/go.mod h1:GYeBD1CF7AqnKZK+UCytLcY3G+UKo0ByXX/3xfdNyqQ= github.com/icza/mighty v0.0.0-20180919140131-cfd07d671de6 h1:8UsGZ2rr2ksmEru6lToqnXgA8Mz1DP11X4zSJ159C3k= github.com/icza/mighty v0.0.0-20180919140131-cfd07d671de6/go.mod h1:xQig96I1VNBDIWGCdTt54nHt6EeI639SmHycLYL7FkA= +github.com/imdario/mergo v0.3.7/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA= github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8= github.com/influxdata/influxdb1-client v0.0.0-20191209144304-8bf82d3c094d/go.mod h1:qj24IKcXYK6Iy9ceXlo3Tc+vtHo9lIhSX5JddghvEPo= github.com/influxdata/influxdb1-client v0.0.0-20200827194710-b269163b24ab h1:HqW4xhhynfjrtEiiSGcQUd6vrK23iMam1FO8rI7mwig= @@ -924,6 +975,7 @@ github.com/jmespath/go-jmespath v0.0.0-20180206201540-c2b33e8439af/go.mod h1:Nht github.com/jmespath/go-jmespath v0.3.0/go.mod h1:9QtRXoHjLGCJ5IBSaohpXITPlowMeeYCZ7fLUTSywik= github.com/joeshaw/multierror v0.0.0-20140124173710-69b34d4ec901 h1:rp+c0RAYOWj8l6qbCUTSiRLG/iKnW3K3/QfPPuSsBt4= github.com/joeshaw/multierror v0.0.0-20140124173710-69b34d4ec901/go.mod h1:Z86h9688Y0wesXCyonoVr47MasHilkuLMqGhRZ4Hpak= +github.com/joho/godotenv v1.3.0/go.mod h1:7hK45KPybAkOC6peb+G5yklZfMxEjkZhHbwpqxOKXbg= github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo= github.com/jonboulle/clockwork v0.1.1-0.20190114141812-62fb9bc030d1/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo= github.com/jonboulle/clockwork v0.2.2 h1:UOGuzwb1PwsrDAObMuhUnj0p5ULPj8V/xJ7Kx9qUBdQ= @@ -952,6 +1004,9 @@ github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7V github.com/julienschmidt/httprouter v1.3.0/go.mod h1:JR6WtHb+2LUe8TCKY3cZOxFyyO8IZAc4RVcycCCAKdM= github.com/kabukky/httpscerts v0.0.0-20150320125433-617593d7dcb3/go.mod h1:BYpt4ufZiIGv2nXn4gMxnfKV306n3mWXgNu/d2TqdTU= github.com/kami-zh/go-capturer v0.0.0-20171211120116-e492ea43421d/go.mod h1:P2viExyCEfeWGU259JnaQ34Inuec4R38JCyBx2edgD0= +github.com/karrick/godirwalk v1.8.0/go.mod h1:H5KPZjojv4lE+QYImBI8xVtrBRgYrIVsaRPx4tDPEn4= +github.com/karrick/godirwalk v1.10.3/go.mod h1:RoGL9dQei4vP9ilrpETWE8CLOZ1kiN0LhBygSwrAsHA= +github.com/karrick/godirwalk v1.10.12/go.mod h1:RoGL9dQei4vP9ilrpETWE8CLOZ1kiN0LhBygSwrAsHA= github.com/kelseyhightower/envconfig v1.4.0 h1:Im6hONhd3pLkfDFsbRgu68RDNkGF1r3dvMUtDTo2cv8= github.com/kelseyhightower/envconfig v1.4.0/go.mod h1:cccZRl6mQpaq41TPp5QxidR+Sa3axMbJDNb//FQX6Gg= github.com/kilic/bls12-381 v0.0.0-20200607163746-32e1441c8a9f/go.mod h1:XXfR6YFCRSrkEXbNlIyDsgXVNJWVUV30m/ebkVy9n6s= @@ -977,6 +1032,7 @@ github.com/klauspost/cpuid/v2 v2.1.1/go.mod h1:RVVoqg1df56z8g3pUjL/3lE5UfnlrJX8t github.com/koalacxr/quantile v0.0.1 h1:wAW+SQ286Erny9wOjVww96t8ws+x5Zj6AKHDULUK+o0= github.com/koalacxr/quantile v0.0.1/go.mod h1:bGN/mCZLZ4lrSDHRQ6Lglj9chowGux8sGUIND+DQeD0= github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= +github.com/konsorten/go-windows-terminal-sequences v1.0.2/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= github.com/konsorten/go-windows-terminal-sequences v1.0.3/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= github.com/koron/go-ssdp v0.0.0-20180514024734-4a0ed625a78b/go.mod h1:5Ky9EC2xfoUKUor0Hjgi2BJhCSXJfMOFlmyYrVKGQMk= github.com/koron/go-ssdp v0.0.0-20191105050749-2e1c40ed0b5d/go.mod h1:5Ky9EC2xfoUKUor0Hjgi2BJhCSXJfMOFlmyYrVKGQMk= @@ -991,6 +1047,7 @@ github.com/kr/pretty v0.3.0 h1:WgNl7dwNpEZ6jJ9k1snq4pZsg7DOEN8hP9Xw0Tsjwk0= github.com/kr/pretty v0.3.0/go.mod h1:640gp4NfQd8pI5XOwp5fnNeVWj67G7CFk/SaSQn7NBk= github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= github.com/kr/pty v1.1.3/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= +github.com/kr/pty v1.1.5/go.mod h1:9r2w37qlBe7rQ6e1fg1S/9xpWHSnaqNdHD3WcMdbPDA= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= @@ -1368,6 +1425,8 @@ github.com/mailru/easyjson v0.0.0-20190626092158-b2ccc519800e/go.mod h1:C1wdFJiN github.com/mailru/easyjson v0.7.1/go.mod h1:KAzv3t3aY1NaHWoQz1+4F1ccyAH66Jk7yos7ldAVICs= github.com/mailru/easyjson v0.7.6 h1:8yTIVnZgCoiM1TgqoeTl+LfU5Jg6/xL3QhGQnimLYnA= github.com/mailru/easyjson v0.7.6/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc= +github.com/markbates/oncer v0.0.0-20181203154359-bf2de49a0be2/go.mod h1:Ld9puTsIW75CHf65OeIOkyKbteujpZVXDpWK6YGZbxE= +github.com/markbates/safe v1.0.1/go.mod h1:nAqgmRi7cY2nqMc92/bSEeQA+R4OheNU2T1kNSCBdG0= github.com/marten-seemann/qpack v0.2.1 h1:jvTsT/HpCn2UZJdP+UUB53FfUUgeOyG5K1ns0OJOGVs= github.com/marten-seemann/qpack v0.2.1/go.mod h1:F7Gl5L1jIgN1D11ucXefiuJS9UMVP2opoCp2jDKb7wc= github.com/marten-seemann/qtls v0.10.0/go.mod h1:UvMd1oaYDACI99/oZUYLzMCkBXQVT0aGm99sJhbT8hs= @@ -1454,11 +1513,16 @@ github.com/mitchellh/gox v0.4.0/go.mod h1:Sd9lOJ0+aimLBi73mGofS1ycjY8lL3uZM3JPS4 github.com/mitchellh/iochan v1.0.0/go.mod h1:JwYml1nuB7xOzsp52dPpHFffvOCDupsG0QubkSMEySY= github.com/mitchellh/mapstructure v0.0.0-20160808181253-ca63d7c062ee/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= +github.com/mitchellh/mapstructure v1.3.3/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= +github.com/mitchellh/mapstructure v1.4.1/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= +github.com/mitchellh/mapstructure v1.4.2 h1:6h7AQ0yhTcIsmFmnAwQls75jp2Gzs4iB8W7pjMO+rqo= +github.com/mitchellh/mapstructure v1.4.2/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= +github.com/montanaflynn/stats v0.0.0-20171201202039-1bf9dbcd8cbe/go.mod h1:wL8QJuTMNUDYhXwkmfOly8iTdp5TEcJFWZD2D7SIkUc= github.com/mr-tron/base58 v1.1.0/go.mod h1:xcD2VGqlgYjBdcBLw+TuYLr8afG+Hj8g2eTVqeSzSU8= github.com/mr-tron/base58 v1.1.1/go.mod h1:xcD2VGqlgYjBdcBLw+TuYLr8afG+Hj8g2eTVqeSzSU8= github.com/mr-tron/base58 v1.1.2/go.mod h1:BinMc/sQntlIE1frQmRFPUoPA1Zkr8VRgBdjWI2mNwc= @@ -1552,6 +1616,7 @@ github.com/nats-io/nuid v1.0.1/go.mod h1:19wcPz3Ph3q0Jbyiqsd0kePYG7A95tJPxeL+1OS github.com/neelance/astrewrite v0.0.0-20160511093645-99348263ae86/go.mod h1:kHJEU3ofeGjhHklVoIGuVj85JJwZ6kWPaJwCIxgnFmo= github.com/neelance/sourcemap v0.0.0-20151028013722-8c68805598ab/go.mod h1:Qr6/a/Q4r9LP1IltGz7tA7iOK1WonHEYhu1HRBA7ZiM= github.com/ngdinhtoan/glide-cleanup v0.2.0/go.mod h1:UQzsmiDOb8YV3nOsCxK/c9zPpCZVNoHScRE3EO9pVMM= +github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno= github.com/nikkolasg/hexjson v0.0.0-20181101101858-78e39397e00c h1:5bFTChQxSKNwy8ALwOebjekYExl9HTT9urdawqC95tA= github.com/nikkolasg/hexjson v0.0.0-20181101101858-78e39397e00c/go.mod h1:7qN3Y0BvzRUf4LofcoJplQL10lsFDb4PYlePTVwrP28= github.com/nkovacs/streamquote v1.0.0 h1:PmVIV08Zlx2lZK5fFZlMZ04eHcDTIFJCv/5/0twVUow= @@ -1561,6 +1626,8 @@ github.com/nxadm/tail v1.4.8 h1:nPr65rt6Y5JFSKQO7qToXr7pePgD6Gwiw05lkbyAQTE= github.com/nxadm/tail v1.4.8/go.mod h1:+ncqLTQzXmGhMZNUePPaPqPvBxHAIsmXswZKocGu+AU= github.com/oklog/oklog v0.3.2/go.mod h1:FCV+B7mhrz4o+ueLpx+KqkyXRGMWOYEvfiXtdGtbWGs= github.com/oklog/run v1.0.0/go.mod h1:dlhp/R75TPv97u0XWUtDeV/lRKWPKSdTuV0TZvrmrQA= +github.com/oklog/ulid v1.3.1 h1:EGfNDEx6MqHz8B3uNV6QAib1UR2Lm97sHi3ocA6ESJ4= +github.com/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn4U= github.com/olekukonko/tablewriter v0.0.0-20170122224234-a0225b3f23b5/go.mod h1:vsDQFd/mU46D+Z4whnwzcISnGGzXWMclvtLoiIKAKIo= github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= github.com/onsi/ginkgo v1.7.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= @@ -1610,6 +1677,7 @@ github.com/pbnjay/memory v0.0.0-20210728143218-7b4eea64cf58 h1:onHthvaw9LFnH4t2D github.com/pbnjay/memory v0.0.0-20210728143218-7b4eea64cf58/go.mod h1:DXv8WO4yhMYhSNPKjeNKa5WY9YCIEBRbNzFFPJbWO6Y= github.com/pborman/uuid v1.2.0/go.mod h1:X/NO0urCmaxf9VXbdlT7C2Yzkj2IKimNn4k+gtPdI/k= github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic= +github.com/pelletier/go-toml v1.7.0/go.mod h1:vwGMzjaWMwyfHwgIBhI2YUM4fB6nL6lVAvS1LBMMhTE= github.com/performancecopilot/speed v3.0.0+incompatible/go.mod h1:/CLtqpZ5gBg1M9iaPbIdPPGyKcA8hKdoy6hAWba7Yac= github.com/petar/GoLLRB v0.0.0-20210522233825-ae3b015fd3e9 h1:1/WtZae0yGtPq+TI6+Tv1WTxkukpXeMlviSxvL7SRgk= github.com/petar/GoLLRB v0.0.0-20210522233825-ae3b015fd3e9/go.mod h1:x3N5drFsm2uilKKuuYo6LdyD8vZAW55sH/9w+pbo1sw= @@ -1694,6 +1762,8 @@ github.com/rivo/uniseg v0.1.0 h1:+2KBaVoUmb9XzDsrx/Ct0W/EYOSFf/nWTauy++DprtY= github.com/rivo/uniseg v0.1.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc= github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg= github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ= +github.com/rogpeppe/go-internal v1.1.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= +github.com/rogpeppe/go-internal v1.2.2/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= 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.8.1 h1:geMPLpDpQOgVyCg5z5GoRwLHepNdb71NXb67XFkP+Eg= @@ -1740,6 +1810,8 @@ github.com/shurcooL/users v0.0.0-20180125191416-49c67e49c537/go.mod h1:QJTqeLYED github.com/shurcooL/webdavfs v0.0.0-20170829043945-18c3829fa133/go.mod h1:hKmq5kWdCj2z2KEozexVbfEZIWiTjhE0+UjmZgPqehw= github.com/siebenmann/go-kstat v0.0.0-20160321171754-d34789b79745/go.mod h1:G81aIFAMS9ECrwBYR9YxhlPjWgrItd+Kje78O6+uqm8= github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo= +github.com/sirupsen/logrus v1.4.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo= +github.com/sirupsen/logrus v1.4.1/go.mod h1:ni0Sbl8bgC9z8RoU9G6nDWqqs/fq4eDPysMBDgk/93Q= github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE= github.com/sirupsen/logrus v1.6.0/go.mod h1:7uNnSEd1DgxDLC74fIahvMZmmYsHGZGEOFrfsX/uA88= github.com/sirupsen/logrus v1.7.0/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0= @@ -1780,6 +1852,7 @@ github.com/streadway/amqp v0.0.0-20190827072141-edfb9018d271/go.mod h1:AZpEONHx3 github.com/streadway/handy v0.0.0-20190108123426-d5acb3125c2a/go.mod h1:qNTQ5P5JnDBl6z3cMAg/SywNDC5ABu5ApDIw6lUbRmI= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/objx v0.2.0/go.mod h1:qt09Ya8vawLte6SNmTgCsAVtYtaKzEcn8ATUoHMkEqE= github.com/stretchr/objx v0.4.0 h1:M2gUjqZET1qApGOWNSnZ49BAIMX4F/1plDv3+l31EJ4= github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= @@ -1796,6 +1869,8 @@ github.com/syndtr/goleveldb v1.0.0/go.mod h1:ZVVdQEZoIme9iO1Ch2Jdy24qqXrMMOU6lpP github.com/syndtr/goleveldb v1.0.1-0.20210819022825-2ae1ddf74ef7 h1:epCh84lMvA70Z7CTTCmYQn2CKbY8j86K7/FAIr141uY= github.com/syndtr/goleveldb v1.0.1-0.20210819022825-2ae1ddf74ef7/go.mod h1:q4W45IWZaF22tdD+VEXcAWRA037jwmWEB5VWYORlTpc= github.com/tarm/serial v0.0.0-20180830185346-98f6abe2eb07/go.mod h1:kDXzergiv9cbyO7IOYJZWg1U88JhDg3PB6klq9Hg2pA= +github.com/test-go/testify v1.1.4 h1:Tf9lntrKUMHiXQ07qBScBTSA0dhYQlu83hswqelv1iE= +github.com/test-go/testify v1.1.4/go.mod h1:rH7cfJo/47vWGdi4GPj16x3/t1xGOj2YxzmNQzk2ghU= github.com/texttheater/golang-levenshtein v0.0.0-20180516184445-d188e65d659e/go.mod h1:XDKHRm5ThF8YJjx001LtgelzsoaEcvnA7lVWz9EeX3g= github.com/tidwall/gjson v1.6.0 h1:9VEQWz6LLMUsUl6PueE49ir4Ka6CzLymOAZDxpFsTDc= github.com/tidwall/gjson v1.6.0/go.mod h1:P256ACg0Mn+j1RXIDXoss50DeIABTYK1PULOJHhxOls= @@ -1881,6 +1956,9 @@ github.com/whyrusleeping/multiaddr-filter v0.0.0-20160516205228-e903e4adabd7/go. github.com/whyrusleeping/timecache v0.0.0-20160911033111-cfcb2f1abfee h1:lYbXeSvJi5zk5GLKVuid9TVjS9a0OmLIDKTfoZBL6Ow= github.com/whyrusleeping/timecache v0.0.0-20160911033111-cfcb2f1abfee/go.mod h1:m2aV4LZI4Aez7dP5PMyVKEHhUyEJ/RjmPEDOpDvudHg= github.com/x-cray/logrus-prefixed-formatter v0.5.2/go.mod h1:2duySbKsL6M18s5GU7VPsoEPHyzalCE06qoARUCeBBE= +github.com/xdg-go/pbkdf2 v1.0.0/go.mod h1:jrpuAogTd400dnrH08LKmI/xc1MbPOebTwRqcT5RDeI= +github.com/xdg-go/scram v1.0.2/go.mod h1:1WAq6h33pAW+iRreB34OORO2Nf7qel3VV3fjBj+hCSs= +github.com/xdg-go/stringprep v1.0.2/go.mod h1:8F9zXuvzgwmyT5DUm4GUfZGDdT3W+LCvS6+da4O5kxM= github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU= github.com/xorcare/golden v0.6.0/go.mod h1:7T39/ZMvaSEZlBPoYfVFmsBLmUl3uz9IuzWj/U6FtvQ= github.com/xorcare/golden v0.6.1-0.20191112154924-b87f686d7542 h1:oWgZJmC1DorFZDpfMfWg7xk29yEOZiXmo/wZl+utTI8= @@ -1888,6 +1966,7 @@ github.com/xorcare/golden v0.6.1-0.20191112154924-b87f686d7542/go.mod h1:7T39/ZM github.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77/go.mod h1:aYKd//L2LvnjZzWKhF00oedf4jCCReLcmhLdhm1A27Q= github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673 h1:bAn7/zixMGCfxrRTfdpNzjtPYqr8smhKouy9mxVdGPU= github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673/go.mod h1:N3UwUGtsrSj3ccvlPHLoLsHnpR27oXr4ZE984MbSER8= +github.com/youmark/pkcs8 v0.0.0-20181117223130-1be2e3e5546d/go.mod h1:rHwXgn7JulP+udvsHwJoVG1YGAP6VLg4y9I5dyZdqmA= github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= @@ -1911,6 +1990,9 @@ go.etcd.io/bbolt v1.3.3/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU= go.etcd.io/bbolt v1.3.4 h1:hi1bXHMVrlQh6WwxAy+qZCV/SYIlqo+Ushwdpa4tAKg= go.etcd.io/bbolt v1.3.4/go.mod h1:G5EMThwa9y8QZGBClrRx5EY+Yw9kAhnjy3bSjsnlVTQ= go.etcd.io/etcd v0.0.0-20191023171146-3cf2f69b5738/go.mod h1:dnLIgRNXwCJa5e+c6mIZCrds/GIG4ncV9HhK5PX7jPg= +go.mongodb.org/mongo-driver v1.7.3/go.mod h1:NqaYOwnXWr5Pm7AOpO5QFxKJ503nbMse/R79oO62zWg= +go.mongodb.org/mongo-driver v1.7.5 h1:ny3p0reEpgsR2cfA5cjgwFZg3Cv/ofFh/8jbhGtz9VI= +go.mongodb.org/mongo-driver v1.7.5/go.mod h1:VXEWRZ6URJIkUq2SCAyapmhH0ZLRBP+FT4xhp5Zvxng= go.opencensus.io v0.18.0/go.mod h1:vKdFvxhtzZ9onBp9VKHK8z/sRpBMnKAsufL7wlDrCOA= go.opencensus.io v0.20.1/go.mod h1:6WKK9ahsWS3RSO+PY9ZHZUfv2irvY6gN279GOPZjmmk= go.opencensus.io v0.20.2/go.mod h1:6WKK9ahsWS3RSO+PY9ZHZUfv2irvY6gN279GOPZjmmk= @@ -1998,6 +2080,7 @@ golang.org/x/crypto v0.0.0-20190225124518-7f87c0fbb88b/go.mod h1:6SG95UA2DQfeDnf golang.org/x/crypto v0.0.0-20190228161510-8dd112bcdc25/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20190313024323-a1f597ede03a/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/crypto v0.0.0-20190422162423-af44ce270edf/go.mod h1:WFFai1msRO1wXaEeE5yQxYXgSfI8pQAWXbQop6sCtWE= golang.org/x/crypto v0.0.0-20190426145343-a29dc8fdc734/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20190513172903-22d7a77e9e5f/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= @@ -2005,6 +2088,7 @@ golang.org/x/crypto v0.0.0-20190530122614-20be4c3c3ed5/go.mod h1:yigFU9vqHzYiE8U golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20190611184440-5c40567a22f8/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20190618222545-ea8f1a30c443/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20190621222207-cc06ce4a13d4/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20190701094942-4def268fd1a4/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20190927123631-a832865fa7ad/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= @@ -2012,6 +2096,7 @@ golang.org/x/crypto v0.0.0-20191206172530-e9b2fee46413/go.mod h1:LzIPMQfyMNhhGPh golang.org/x/crypto v0.0.0-20200115085410-6d4e4cb37c7d/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20200128174031-69ecbb4d6d5d/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20200221231518-2aa609cf4a9d/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/crypto v0.0.0-20200302210943-78000ba7a073/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20200423211502-4bdfaf469ed5/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20200510223506-06a226fb4e37/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20200602180216-279210d13fed/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= @@ -2130,6 +2215,7 @@ 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-20210316092652-d523dce5a7f4/go.mod h1:RBQZq4jEuRlivfhVLdyRGr576XBO4/greRjx4P4O3yc= golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM= +golang.org/x/net v0.0.0-20210421230115-4e50805a0758/go.mod h1:72T/g9IO56b78aLF+1Kcs5dz7/ng1VjMUvfKvpfy+jM= golang.org/x/net v0.0.0-20210423184538-5f58ad60dda6/go.mod h1:OJAsFXCWl8Ukc7SiCT/9KSuxbyM7479/AVlXFRxuMCk= golang.org/x/net v0.0.0-20210428140749-89ef3d95e781/go.mod h1:OJAsFXCWl8Ukc7SiCT/9KSuxbyM7479/AVlXFRxuMCk= golang.org/x/net v0.0.0-20210525063256-abc453219eb5/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= @@ -2156,6 +2242,7 @@ golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJ golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20190412183630-56d357773e84/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20200317015054-43a5402ce75a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= @@ -2186,18 +2273,23 @@ golang.org/x/sys v0.0.0-20190228124157-a34e9553db1e/go.mod h1:STP8DvDyc/dI5b8T5h golang.org/x/sys v0.0.0-20190302025703-b6889370fb10/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190316082340-a2f829d7f35f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190403152447-81d4e9dc473e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190405154228-4b34438f7a67/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190411185658-b44545bcd369/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190419153524-e8e3143a4f4a/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190515120540-06a5c4944438/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190523142557-0e01d883c5c5/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190524122548-abf6ff778158/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190524152521-dbbf3f1254d4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190526052359-791d8a0f4d09/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190531175056-4c3a928424d2/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190610200419-93c9922d18ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190616124812-15dcb6c0061f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190626221950-04f50cda93cb/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -2253,6 +2345,7 @@ golang.org/x/sys v0.0.0-20210315160823-c6e025ad8005/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20210317225723-c4fcb01b228e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210331175145-43e1dd70ce54/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210420072515-93ed5bcd2bfe/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210423185535-09eb48e85fd7/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210426080607-c94f62235c83/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -2305,10 +2398,15 @@ golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3 golang.org/x/tools v0.0.0-20190312151545-0bb0c0a6e846/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= golang.org/x/tools v0.0.0-20190312170243-e65039ee4138/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= golang.org/x/tools v0.0.0-20190328211700-ab21143f2384/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +golang.org/x/tools v0.0.0-20190329151228-23e29df326fe/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +golang.org/x/tools v0.0.0-20190416151739-9c9e1878f421/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +golang.org/x/tools v0.0.0-20190420181800-aa740d480789/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= golang.org/x/tools v0.0.0-20190425150028-36563e24a262/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= golang.org/x/tools v0.0.0-20190506145303-2d16b83fe98c/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= +golang.org/x/tools v0.0.0-20190531172133-b3315ee88b7d/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= golang.org/x/tools v0.0.0-20190606124116-d0a3d012864b/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= +golang.org/x/tools v0.0.0-20190614205625-5aca471b1d59/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= golang.org/x/tools v0.0.0-20190628153133-6cdbf07be9d0/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= golang.org/x/tools v0.0.0-20190816200558-6889da9d5479/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= @@ -2479,6 +2577,7 @@ gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLks gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= gopkg.in/cheggaaa/pb.v1 v1.0.25/go.mod h1:V/YB90LKu/1FcN3WVnfiiE5oMCibMjukxqG/qStrOgw= @@ -2505,6 +2604,8 @@ gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gopkg.in/yaml.v3 v3.0.0-20200605160147-a5ece683394c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gopkg.in/yaml.v3 v3.0.0-20200615113413-eeeca48fe776/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/itests/eth_conformance_test.go b/itests/eth_conformance_test.go new file mode 100644 index 000000000..0a399adbc --- /dev/null +++ b/itests/eth_conformance_test.go @@ -0,0 +1,202 @@ +package itests + +import ( + "context" + "encoding/json" + "errors" + "os" + "path" + "testing" + "time" + + "github.com/filecoin-project/go-jsonrpc" + "github.com/filecoin-project/lotus/chain/types/ethtypes" + "github.com/filecoin-project/lotus/itests/kit" + oaerrors "github.com/go-openapi/errors" + "github.com/go-openapi/spec" + "github.com/go-openapi/strfmt" + "github.com/go-openapi/validate" + "github.com/ipfs/go-cid" + manet "github.com/multiformats/go-multiaddr/net" + "github.com/stretchr/testify/require" + + "github.com/gregdhill/go-openrpc/parse" + "github.com/gregdhill/go-openrpc/types" + "github.com/gregdhill/go-openrpc/util" +) + +// TODO generate this using reflection +type ethAPIRaw struct { + EthAccounts func(context.Context) (json.RawMessage, error) + EthBlockNumber func(context.Context) (json.RawMessage, error) + EthCall func(context.Context, ethtypes.EthCall, string) (json.RawMessage, error) + EthChainId func(context.Context) (json.RawMessage, error) + EthEstimateGas func(context.Context, ethtypes.EthCall) (json.RawMessage, error) + EthFeeHistory func(context.Context, ethtypes.EthUint64, string, []float64) (json.RawMessage, error) + EthGasPrice func(context.Context) (json.RawMessage, error) + EthGetBalance func(context.Context, ethtypes.EthAddress, string) (json.RawMessage, error) + EthGetBlockByHash func(context.Context, ethtypes.EthHash, bool) (json.RawMessage, error) + EthGetBlockByNumber func(context.Context, string, bool) (json.RawMessage, error) + EthGetBlockTransactionCountByHash func(context.Context, ethtypes.EthHash) (json.RawMessage, error) + EthGetBlockTransactionCountByNumber func(context.Context, ethtypes.EthUint64) (json.RawMessage, error) + EthGetCode func(context.Context, ethtypes.EthAddress, string) (json.RawMessage, error) + EthGetFilterChanges func(context.Context, ethtypes.EthFilterID) (json.RawMessage, error) + EthGetFilterLogs func(context.Context, ethtypes.EthFilterID) (json.RawMessage, error) + EthGetLogs func(context.Context, *ethtypes.EthFilterSpec) (json.RawMessage, error) + EthGetStorageAt func(context.Context, ethtypes.EthAddress, ethtypes.EthBytes, string) (json.RawMessage, error) + EthGetTransactionByBlockHashAndIndex func(context.Context, ethtypes.EthHash, ethtypes.EthUint64) (json.RawMessage, error) + EthGetTransactionByBlockNumberAndIndex func(context.Context, ethtypes.EthUint64, ethtypes.EthUint64) (json.RawMessage, error) + EthGetTransactionByHash func(context.Context, *ethtypes.EthHash) (json.RawMessage, error) + EthGetTransactionCount func(context.Context, ethtypes.EthAddress, string) (json.RawMessage, error) + EthGetTransactionHashByCid func(context.Context, cid.Cid) (json.RawMessage, error) + EthGetTransactionReceipt func(context.Context, ethtypes.EthHash) (json.RawMessage, error) + EthMaxPriorityFeePerGas func(context.Context) (json.RawMessage, error) + EthNewBlockFilter func(context.Context) (json.RawMessage, error) + EthNewFilter func(context.Context, *ethtypes.EthFilterSpec) (json.RawMessage, error) + EthNewPendingTransactionFilter func(context.Context) (json.RawMessage, error) + EthProtocolVersion func(context.Context) (json.RawMessage, error) + EthSendRawTransaction func(context.Context, ethtypes.EthBytes) (json.RawMessage, error) + EthSubscribe func(context.Context, string, *ethtypes.EthSubscriptionParams) (json.RawMessage, error) + EthUninstallFilter func(context.Context, ethtypes.EthFilterID) (json.RawMessage, error) + EthUnsubscribe func(context.Context, ethtypes.EthSubscriptionID) (json.RawMessage, error) +} + +func TestEthOpenRPC(t *testing.T) { + kit.QuietAllLogsExcept("events", "messagepool") + + specJSON, err := os.ReadFile("specs/eth_openrpc.json") + require.NoError(t, err) + + specParsed := types.NewOpenRPCSpec1() + err = json.Unmarshal([]byte(specJSON), specParsed) + require.NoError(t, err) + parse.GetTypes(specParsed, specParsed.Objects) + + schemas := make(map[string]spec.Schema) + for _, method := range specParsed.Methods { + if method.Result != nil { + schema := resolveSchema(specParsed, method.Result.Schema) + schemas[method.Name] = schema + } + } + + ctx, cancel := context.WithTimeout(context.Background(), time.Minute) + defer cancel() + + client, _, ens := kit.EnsembleMinimal(t, kit.MockProofs(), kit.ThroughRPC(), kit.WithEthRPC()) + ens.InterconnectAll().BeginMining(10 * time.Millisecond) + + // send a message that exercises event logs + sender, contract := client.EVM().DeployContractFromFilename(ctx, EventMatrixContract.Filename) + messages := invokeAndWaitUntilAllOnChain(t, client, []Invocation{ + { + Sender: sender, + Target: contract, + Selector: EventMatrixContract.Fn["logEventThreeIndexedWithData"], + Data: packUint64Values(44, 27, 19, 12), + }, + }) + + require.NotEmpty(t, messages) + + var messageWithEvents ethtypes.EthHash + for k := range messages { + messageWithEvents = k + break + } + + netAddr, err := manet.ToNetAddr(client.ListenAddr) + require.NoError(t, err) + rpcAddr := "ws://" + netAddr.String() + "/rpc/v1" + + var ethapi ethAPIRaw + closer, err := jsonrpc.NewClient(ctx, rpcAddr, "Filecoin", ðapi, nil) + require.NoError(t, err) + defer closer() + + testCases := []struct { + method string + call func(*ethAPIRaw) (json.RawMessage, error) + }{ + { + method: "eth_blockNumber", + call: func(a *ethAPIRaw) (json.RawMessage, error) { + return ethapi.EthBlockNumber(context.Background()) + }, + }, + + { + method: "eth_getTransactionReceipt", + call: func(a *ethAPIRaw) (json.RawMessage, error) { + return ethapi.EthGetTransactionReceipt(context.Background(), messageWithEvents) + }, + }, + } + + for _, tc := range testCases { + t.Run(tc.method, func(t *testing.T) { + schema, ok := schemas[tc.method] + require.True(t, ok, "method not found in openrpc spec") + + resp, err := tc.call(ðapi) + require.NoError(t, err) + + respJson, err := json.MarshalIndent(resp, "", " ") + require.NoError(t, err) + + err = validate.AgainstSchema(&schema, respJson, strfmt.Default) + if err != nil { + t.Logf("response was %s", respJson) + + ce := &oaerrors.CompositeError{} + if errors.As(err, &ce) { + for _, e := range ce.Errors { + t.Logf("error: %v", e) + } + } + } + require.NoError(t, err) + }) + } +} + +func resolveSchema(openrpc *types.OpenRPCSpec1, sch spec.Schema) spec.Schema { + doc, _, _ := sch.Ref.GetPointer().Get(openrpc) + + if s, ok := doc.(spec.Schema); ok { + sch = persistFields(sch, s) + } else if cd, ok := doc.(*types.ContentDescriptor); ok { + sch = persistFields(sch, cd.Schema) + } + + for i := range sch.OneOf { + sch.OneOf[i] = resolveSchema(openrpc, sch.OneOf[i]) + } + + for i := range sch.AllOf { + sch.AllOf[i] = resolveSchema(openrpc, sch.AllOf[i]) + } + + for i := range sch.AnyOf { + sch.AnyOf[i] = resolveSchema(openrpc, sch.AnyOf[i]) + } + + if sch.Not != nil { + s := resolveSchema(openrpc, *sch.Not) + sch.Not = &s + } + + if sch.Ref.GetURL() != nil { + return resolveSchema(openrpc, sch) + } + return sch +} + +func persistFields(prev, next spec.Schema) spec.Schema { + next.Title = util.FirstOf(next.Title, prev.Title, path.Base(prev.Ref.String())) + next.Description = util.FirstOf(next.Description, prev.Description) + if next.Items == nil { + next.Items = prev.Items + } + return next +} diff --git a/itests/specs/eth_openrpc.json b/itests/specs/eth_openrpc.json new file mode 100644 index 000000000..c9f83d07e --- /dev/null +++ b/itests/specs/eth_openrpc.json @@ -0,0 +1,1786 @@ +{ + "openrpc": "1.0.0", + "info": { + "version": "1.0.10", + "title": "Ethereum JSON-RPC", + "description": "This API lets you interact with an EVM-based client via JSON-RPC", + "license": { + "name": "Apache 2.0", + "url": "https://www.apache.org/licenses/LICENSE-2.0.html" + } + }, + "methods": [ + { + "name": "web3_clientVersion", + "description": "Returns the version of the current client", + "summary": "current client version", + "params": [], + "result": { + "name": "clientVersion", + "description": "client version", + "schema": { + "title": "clientVersion", + "type": "string" + } + } + }, + { + "name": "web3_sha3", + "summary": "Hashes data", + "description": "Hashes data using the Keccak-256 algorithm", + "params": [ + { + "name": "data", + "description": "data to hash using the Keccak-256 algorithm", + "summary": "data to hash", + "schema": { + "title": "data", + "type": "string", + "pattern": "^0x[a-fA-F\\d]+$" + } + } + ], + "result": { + "name": "hashedData", + "description": "Keccak-256 hash of the given data", + "schema": { + "$ref": "#/components/schemas/Keccak" + } + }, + "examples": [ + { + "name": "sha3Example", + "params": [ + { + "name": "sha3ParamExample", + "value": "0x68656c6c6f20776f726c64" + } + ], + "result": { + "name": "sha3ResultExample", + "value": "0x47173285a8d7341e5e972fc677286384f802f8ef42a5ec5f03bbfa254cb01fad" + } + } + ] + }, + { + "name": "net_listening", + "summary": "returns listening status", + "description": "Determines if this client is listening for new network connections.", + "params": [], + "result": { + "name": "netListeningResult", + "description": "`true` if listening is active or `false` if listening is not active", + "schema": { + "title": "isNetListening", + "type": "boolean" + } + }, + "examples": [ + { + "name": "netListeningTrueExample", + "description": "example of true result for net_listening", + "params": [], + "result": { + "name": "netListeningExampleFalseResult", + "value": true + } + } + ] + }, + { + "name": "net_peerCount", + "summary": "number of peers", + "description": "Returns the number of peers currently connected to this client.", + "params": [], + "result": { + "name": "quantity", + "description": "number of connected peers.", + "schema": { + "title": "numConnectedPeers", + "description": "Hex representation of number of connected peers", + "type": "string" + } + } + }, + { + "name": "net_version", + "summary": "Network identifier associated with network", + "description": "Returns the network ID associated with the current network.", + "params": [], + "result": { + "name": "networkId", + "description": "Network ID associated with the current network", + "schema": { + "title": "networkId", + "type": "string", + "pattern": "^[\\d]+$" + } + } + }, + { + "name": "eth_blockNumber", + "summary": "Returns the number of most recent block.", + "params": [], + "result": { + "$ref": "#/components/contentDescriptors/BlockNumber" + } + }, + { + "name": "eth_call", + "summary": "Executes a new message call (locally) immediately without creating a transaction on the block chain.", + "params": [ + { + "$ref": "#/components/contentDescriptors/Transaction" + }, + { + "$ref": "#/components/contentDescriptors/BlockNumber" + } + ], + "result": { + "name": "returnValue", + "description": "The return value of the executed contract", + "schema": { + "$ref": "#/components/schemas/Bytes" + } + } + }, + { + "name": "eth_chainId", + "summary": "Returns the currently configured chain id", + "description": "Returns the currently configured chain id, a value used in replay-protected transaction signing as introduced by [EIP-155](https://github.com/ethereum/EIPs/blob/master/EIPS/eip-155.md).", + "params": [], + "result": { + "name": "chainId", + "description": "hex format integer of the current chain id. Defaults are ETC=61, ETH=1, Morden=62.", + "schema": { + "title": "chainId", + "type": "string", + "pattern": "^0x[a-fA-F\\d]+$" + } + } + }, + { + "name": "eth_coinbase", + "summary": "Returns the client coinbase address.", + "params": [], + "result": { + "name": "address", + "description": "The address owned by the client that is used as default for things like the mining reward", + "schema": { + "$ref": "#/components/schemas/Address" + } + } + }, + { + "name": "eth_estimateGas", + "summary": "Generates and returns an estimate of how much gas is necessary to allow the transaction to complete. The transaction will not be added to the blockchain. Note that the estimate may be significantly more than the amount of gas actually used by the transaction, for a variety of reasons including EVM mechanics and node performance.", + "params": [ + { + "$ref": "#/components/contentDescriptors/Transaction" + } + ], + "result": { + "name": "gasUsed", + "description": "The amount of gas used", + "schema": { + "$ref": "#/components/schemas/Integer" + } + } + }, + { + "name": "eth_gasPrice", + "summary": "Returns the current price per gas in wei", + "params": [], + "result": { + "$ref": "#/components/contentDescriptors/GasPrice" + } + }, + { + "name": "eth_getBalance", + "summary": "Returns Ether balance of a given or account or contract", + "params": [ + { + "name": "address", + "required": true, + "description": "The address of the account or contract", + "schema": { + "$ref": "#/components/schemas/Address" + } + }, + { + "name": "blockNumber", + "description": "A BlockNumber at which to request the balance", + "schema": { + "$ref": "#/components/schemas/BlockNumber" + } + } + ], + "result": { + "name": "getBalanceResult", + "schema": { + "$ref": "#/components/schemas/IntegerOrNull" + } + } + }, + { + "name": "eth_getBlockByHash", + "summary": "Gets a block for a given hash", + "params": [ + { + "name": "blockHash", + "required": true, + "schema": { + "$ref": "#/components/schemas/BlockHash" + } + }, + { + "name": "includeTransactions", + "description": "If `true` it returns the full transaction objects, if `false` only the hashes of the transactions.", + "required": true, + "schema": { + "title": "isTransactionsIncluded", + "type": "boolean" + } + } + ], + "result": { + "name": "getBlockByHashResult", + "schema": { + "$ref": "#/components/schemas/BlockOrNull" + } + } + }, + { + "name": "eth_getBlockByNumber", + "summary": "Gets a block for a given number", + "params": [ + { + "$ref": "#/components/contentDescriptors/BlockNumber" + }, + { + "name": "includeTransactions", + "description": "If `true` it returns the full transaction objects, if `false` only the hashes of the transactions.", + "required": true, + "schema": { + "title": "isTransactionsIncluded", + "type": "boolean" + } + } + ], + "result": { + "name": "getBlockByNumberResult", + "schema": { + "$ref": "#/components/schemas/BlockOrNull" + } + } + }, + { + "name": "eth_getBlockTransactionCountByHash", + "summary": "Returns the number of transactions in a block from a block matching the given block hash.", + "params": [ + { + "$ref": "#/components/contentDescriptors/BlockHash" + } + ], + "result": { + "name": "blockTransactionCountByHash", + "description": "The Number of total transactions in the given block", + "schema": { + "$ref": "#/components/schemas/IntegerOrNull" + } + } + }, + { + "name": "eth_getBlockTransactionCountByNumber", + "summary": "Returns the number of transactions in a block from a block matching the given block number.", + "params": [ + { + "$ref": "#/components/contentDescriptors/BlockNumber" + } + ], + "result": { + "name": "blockTransactionCountByHash", + "description": "The Number of total transactions in the given block", + "schema": { + "$ref": "#/components/schemas/IntegerOrNull" + } + } + }, + { + "name": "eth_getCode", + "summary": "Returns code at a given contract address", + "params": [ + { + "name": "address", + "required": true, + "description": "The address of the contract", + "schema": { + "$ref": "#/components/schemas/Address" + } + }, + { + "name": "blockNumber", + "description": "A BlockNumber of which the code existed", + "schema": { + "$ref": "#/components/schemas/BlockNumber" + } + } + ], + "result": { + "name": "bytes", + "schema": { + "$ref": "#/components/schemas/Bytes" + } + } + }, + { + "name": "eth_getFilterChanges", + "summary": "Polling method for a filter, which returns an array of logs which occurred since last poll.", + "params": [ + { + "name": "filterId", + "required": true, + "schema": { + "$ref": "#/components/schemas/FilterId" + } + } + ], + "result": { + "name": "logResult", + "schema": { + "title": "logResult", + "type": "array", + "items": { + "$ref": "#/components/schemas/Log" + } + } + } + }, + { + "name": "eth_getFilterLogs", + "summary": "Returns an array of all logs matching filter with given id.", + "params": [ + { + "name": "filterId", + "required": true, + "schema": { + "$ref": "#/components/schemas/FilterId" + } + } + ], + "result": { + "$ref": "#/components/contentDescriptors/Logs" + } + }, + { + "name": "eth_getRawTransactionByHash", + "summary": "Returns raw transaction data of a transaction with the given hash.", + "params": [ + { + "$ref": "#/components/contentDescriptors/TransactionHash" + } + ], + "result": { + "name": "rawTransactionByHash", + "description": "The raw transaction data", + "schema": { + "$ref": "#/components/schemas/Bytes" + } + } + }, + { + "name": "eth_getRawTransactionByBlockHashAndIndex", + "summary": "Returns raw transaction data of a transaction with the block hash and index of which it was mined.", + "params": [ + { + "$ref": "#/components/contentDescriptors/BlockHash" + }, + { + "name": "index", + "description": "The ordering in which a transaction is mined within its block.", + "required": true, + "schema": { + "$ref": "#/components/schemas/Integer" + } + } + ], + "result": { + "name": "rawTransaction", + "description": "The raw transaction data", + "schema": { + "$ref": "#/components/schemas/Bytes" + } + } + }, + { + "name": "eth_getRawTransactionByBlockNumberAndIndex", + "summary": "Returns raw transaction data of a transaction with the block number and index of which it was mined.", + "params": [ + { + "$ref": "#/components/contentDescriptors/BlockNumber" + }, + { + "name": "index", + "description": "The ordering in which a transaction is mined within its block.", + "required": true, + "schema": { + "$ref": "#/components/schemas/Integer" + } + } + ], + "result": { + "name": "rawTransaction", + "description": "The raw transaction data", + "schema": { + "$ref": "#/components/schemas/Bytes" + } + } + }, + { + "name": "eth_getLogs", + "summary": "Returns an array of all logs matching a given filter object.", + "params": [ + { + "$ref": "#/components/contentDescriptors/Filter" + } + ], + "result": { + "$ref": "#/components/contentDescriptors/Logs" + } + }, + { + "name": "eth_getStorageAt", + "summary": "Gets a storage value from a contract address, a position, and an optional blockNumber", + "params": [ + { + "$ref": "#/components/contentDescriptors/Address" + }, + { + "$ref": "#/components/contentDescriptors/Position" + }, + { + "$ref": "#/components/contentDescriptors/BlockNumber" + } + ], + "result": { + "name": "dataWord", + "schema": { + "$ref": "#/components/schemas/DataWord" + } + } + }, + { + "name": "eth_getTransactionByBlockHashAndIndex", + "summary": "Returns the information about a transaction requested by the block hash and index of which it was mined.", + "params": [ + { + "$ref": "#/components/contentDescriptors/BlockHash" + }, + { + "name": "index", + "description": "The ordering in which a transaction is mined within its block.", + "required": true, + "schema": { + "$ref": "#/components/schemas/Integer" + } + } + ], + "result": { + "$ref": "#/components/contentDescriptors/TransactionResult" + }, + "examples": [ + { + "name": "nullExample", + "params": [ + { + "name": "blockHashExample", + "value": "0x1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef" + }, + { + "name": "indexExample", + "value": "0x0" + } + ], + "result": { + "name": "nullResultExample", + "value": null + } + } + ] + }, + { + "name": "eth_getTransactionByBlockNumberAndIndex", + "summary": "Returns the information about a transaction requested by the block number and index of which it was mined.", + "params": [ + { + "$ref": "#/components/contentDescriptors/BlockNumber" + }, + { + "name": "index", + "description": "The ordering in which a transaction is mined within its block.", + "required": true, + "schema": { + "$ref": "#/components/schemas/Integer" + } + } + ], + "result": { + "$ref": "#/components/contentDescriptors/TransactionResult" + } + }, + { + "name": "eth_getTransactionByHash", + "summary": "Returns the information about a transaction requested by transaction hash.", + "params": [ + { + "$ref": "#/components/contentDescriptors/TransactionHash" + } + ], + "result": { + "$ref": "#/components/contentDescriptors/TransactionResult" + } + }, + { + "name": "eth_getTransactionCount", + "summary": "Returns the number of transactions sent from an address", + "params": [ + { + "$ref": "#/components/contentDescriptors/Address" + }, + { + "$ref": "#/components/contentDescriptors/BlockNumber" + } + ], + "result": { + "name": "transactionCount", + "schema": { + "title": "nonceOrNull", + "oneOf": [ + { + "$ref": "#/components/schemas/Nonce" + }, + { + "$ref": "#/components/schemas/Null" + } + ] + } + } + }, + { + "name": "eth_getTransactionReceipt", + "summary": "Returns the receipt information of a transaction by its hash.", + "params": [ + { + "$ref": "#/components/contentDescriptors/TransactionHash" + } + ], + "result": { + "name": "transactionReceiptResult", + "description": "returns either a receipt or null", + "schema": { + "title": "transactionReceiptOrNull", + "oneOf": [ + { + "$ref": "#/components/schemas/Receipt" + }, + { + "$ref": "#/components/schemas/Null" + } + ] + } + } + }, + { + "name": "eth_getUncleByBlockHashAndIndex", + "summary": "Returns information about a uncle of a block by hash and uncle index position.", + "params": [ + { + "$ref": "#/components/contentDescriptors/BlockHash" + }, + { + "name": "index", + "description": "The ordering in which a uncle is included within its block.", + "required": true, + "schema": { + "$ref": "#/components/schemas/Integer" + } + } + ], + "result": { + "name": "uncle", + "schema": { + "$ref": "#/components/schemas/BlockOrNull" + } + } + }, + { + "name": "eth_getUncleByBlockNumberAndIndex", + "summary": "Returns information about a uncle of a block by hash and uncle index position.", + "params": [ + { + "name": "uncleBlockNumber", + "description": "The block in which the uncle was included", + "required": true, + "schema": { + "$ref": "#/components/schemas/BlockNumber" + } + }, + { + "name": "index", + "description": "The ordering in which a uncle is included within its block.", + "required": true, + "schema": { + "$ref": "#/components/schemas/Integer" + } + } + ], + "result": { + "name": "uncleResult", + "description": "returns an uncle block or null", + "schema": { + "$ref": "#/components/schemas/BlockOrNull" + } + }, + "examples": [ + { + "name": "nullResultExample", + "params": [ + { + "name": "uncleBlockNumberExample", + "value": "0x0" + }, + { + "name": "uncleBlockNumberIndexExample", + "value": "0x0" + } + ], + "result": { + "name": "nullResultExample", + "value": null + } + } + ] + }, + { + "name": "eth_getUncleCountByBlockHash", + "summary": "Returns the number of uncles in a block from a block matching the given block hash.", + "params": [ + { + "$ref": "#/components/contentDescriptors/BlockHash" + } + ], + "result": { + "name": "uncleCountResult", + "description": "The Number of total uncles in the given block", + "schema": { + "$ref": "#/components/schemas/IntegerOrNull" + } + } + }, + { + "name": "eth_getUncleCountByBlockNumber", + "summary": "Returns the number of uncles in a block from a block matching the given block number.", + "params": [ + { + "$ref": "#/components/contentDescriptors/BlockNumber" + } + ], + "result": { + "$ref": "#/components/contentDescriptors/UncleCountResult" + } + }, + { + "name": "eth_getProof", + "summary": "Returns the account- and storage-values of the specified account including the Merkle-proof.", + "params": [ + { + "name": "address", + "description": "The address of the account or contract", + "required": true, + "schema": { + "$ref": "#/components/schemas/Address" + } + }, + { + "name": "storageKeys", + "required": true, + "schema": { + "title": "storageKeys", + "description": "A storage key is indexed from the solidity compiler by the order it is declared. For mappings it uses the keccak of the mapping key with its position (and recursively for X-dimensional mappings)", + "items": { + "$ref": "#/components/schemas/StorageProofKey" + } + } + }, + { + "$ref": "#/components/contentDescriptors/BlockNumber" + } + ], + "result": { + "name": "account", + "schema": { + "title": "proofAccountOrNull", + "oneOf": [ + { + "title": "proofAccount", + "type": "object", + "description": "The merkle proofs of the specified account connecting them to the blockhash of the block specified", + "properties": { + "address": { + "title": "proofAccountAddress", + "description": "The address of the account or contract of the request", + "$ref": "#/components/schemas/Address" + }, + "accountProof": { + "$ref": "#/components/schemas/ProofNodes" + }, + "balance": { + "title": "proofAccountBalance", + "description": "The Ether balance of the account or contract of the request", + "$ref": "#/components/schemas/Integer" + }, + "codeHash": { + "title": "proofAccountCodeHash", + "description": "The code hash of the contract of the request (keccak(NULL) if external account)", + "$ref": "#/components/schemas/Keccak" + }, + "nonce": { + "title": "proofAccountNonce", + "description": "The transaction count of the account or contract of the request", + "$ref": "#/components/schemas/Nonce" + }, + "storageHash": { + "title": "proofAccountStorageHash", + "description": "The storage hash of the contract of the request (keccak(rlp(NULL)) if external account)", + "$ref": "#/components/schemas/Keccak" + }, + "storageProof": { + "$ref": "#/components/schemas/StorageProof" + } + } + }, + { + "$ref": "#/components/schemas/Null" + } + ] + } + } + }, + { + "name": "eth_getWork", + "summary": "Returns the hash of the current block, the seedHash, and the boundary condition to be met ('target').", + "params": [], + "result": { + "name": "work", + "schema": { + "title": "getWorkResults", + "type": "array", + "items": [ + { + "$ref": "#/components/schemas/PowHash" + }, + { + "$ref": "#/components/schemas/SeedHash" + }, + { + "$ref": "#/components/schemas/Difficulty" + } + ] + } + } + }, + { + "name": "eth_hashrate", + "summary": "Returns the number of hashes per second that the node is mining with.", + "params": [], + "result": { + "name": "hashesPerSecond", + "description": "Integer of the number of hashes per second", + "schema": { + "$ref": "#/components/schemas/Integer" + } + } + }, + { + "name": "eth_mining", + "summary": "Returns true if client is actively mining new blocks.", + "params": [], + "result": { + "name": "mining", + "description": "Whether or not the client is mining", + "schema": { + "type": "boolean" + } + } + }, + { + "name": "eth_newBlockFilter", + "summary": "Creates a filter in the node, to notify when a new block arrives. To check if the state has changed, call eth_getFilterChanges.", + "params": [], + "result": { + "$ref": "#/components/contentDescriptors/FilterId" + } + }, + { + "name": "eth_newFilter", + "summary": "Creates a filter object, based on filter options, to notify when the state changes (logs). To check if the state has changed, call eth_getFilterChanges.", + "params": [ + { + "$ref": "#/components/contentDescriptors/Filter" + } + ], + "result": { + "name": "filterId", + "description": "The filter ID for use in `eth_getFilterChanges`", + "schema": { + "$ref": "#/components/schemas/Integer" + } + } + }, + { + "name": "eth_newPendingTransactionFilter", + "summary": "Creates a filter in the node, to notify when new pending transactions arrive. To check if the state has changed, call eth_getFilterChanges.", + "params": [], + "result": { + "$ref": "#/components/contentDescriptors/FilterId" + } + }, + { + "name": "eth_pendingTransactions", + "summary": "Returns the transactions that are pending in the transaction pool and have a from address that is one of the accounts this node manages.", + "params": [], + "result": { + "name": "pendingTransactions", + "schema": { + "$ref": "#/components/schemas/Transactions" + } + } + }, + { + "name": "eth_protocolVersion", + "summary": "Returns the current ethereum protocol version.", + "params": [], + "result": { + "name": "protocolVersion", + "description": "The current ethereum protocol version", + "schema": { + "$ref": "#/components/schemas/Integer" + } + } + }, + { + "name": "eth_sendRawTransaction", + "summary": "Creates new message call transaction or a contract creation for signed transactions.", + "params": [ + { + "name": "signedTransactionData", + "required": true, + "description": "The signed transaction data", + "schema": { + "$ref": "#/components/schemas/Bytes" + } + } + ], + "result": { + "name": "transactionHash", + "description": "The transaction hash, or the zero hash if the transaction is not yet available.", + "schema": { + "$ref": "#/components/schemas/Keccak" + } + } + }, + { + "name": "eth_submitHashrate", + "deprecated": true, + "summary": "Used for submitting mining hashrate.", + "params": [ + { + "name": "hashRate", + "required": true, + "schema": { + "$ref": "#/components/schemas/DataWord" + } + }, + { + "name": "id", + "required": true, + "description": "String identifying the client", + "schema": { + "$ref": "#/components/schemas/DataWord" + } + } + ], + "result": { + "name": "submitHashRateSuccess", + "description": "whether of not submitting went through successfully", + "schema": { + "type": "boolean" + } + } + }, + { + "name": "eth_submitWork", + "summary": "Used for submitting a proof-of-work solution.", + "params": [ + { + "$ref": "#/components/contentDescriptors/Nonce" + }, + { + "name": "powHash", + "required": true, + "schema": { + "$ref": "#/components/schemas/PowHash" + } + }, + { + "name": "mixHash", + "required": true, + "schema": { + "$ref": "#/components/schemas/MixHash" + } + } + ], + "result": { + "name": "solutionValid", + "description": "returns true if the provided solution is valid, otherwise false.", + "schema": { + "type": "boolean" + } + }, + "examples": [ + { + "name": "submitWorkExample", + "params": [ + { + "name": "nonceExample", + "description": "example of a number only used once", + "value": "0x0000000000000001" + }, + { + "name": "powHashExample", + "description": "proof of work to submit", + "value": "0x6bf2cAE0dE3ec3ecA5E194a6C6e02cf42aADfe1C2c4Fff12E5D36C3Cf7297F22" + }, + { + "name": "mixHashExample", + "description": "the mix digest example", + "value": "0xD1FE5700000000000000000000000000D1FE5700000000000000000000000000" + } + ], + "result": { + "name": "solutionInvalidExample", + "description": "this example should return `false` as it is not a valid pow to submit", + "value": false + } + } + ] + }, + { + "name": "eth_syncing", + "summary": "Returns an object with data about the sync status or false.", + "params": [], + "result": { + "name": "syncing", + "schema": { + "title": "isSyncingResult", + "oneOf": [ + { + "title": "syncingData", + "description": "An object with sync status data", + "type": "object", + "properties": { + "startingBlock": { + "title": "syncingDataStartingBlock", + "description": "Block at which the import started (will only be reset, after the sync reached his head)", + "$ref": "#/components/schemas/Integer" + }, + "currentBlock": { + "title": "syncingDataCurrentBlock", + "description": "The current block, same as eth_blockNumber", + "$ref": "#/components/schemas/Integer" + }, + "highestBlock": { + "title": "syncingDataHighestBlock", + "description": "The estimated highest block", + "$ref": "#/components/schemas/Integer" + }, + "knownStates": { + "title": "syncingDataKnownStates", + "description": "The known states", + "$ref": "#/components/schemas/Integer" + }, + "pulledStates": { + "title": "syncingDataPulledStates", + "description": "The pulled states", + "$ref": "#/components/schemas/Integer" + } + } + }, + { + "type": "boolean" + } + ] + } + } + }, + { + "name": "eth_uninstallFilter", + "summary": "Uninstalls a filter with given id. Should always be called when watch is no longer needed. Additionally Filters timeout when they aren't requested with eth_getFilterChanges for a period of time.", + "params": [ + { + "name": "filterId", + "required": true, + "schema": { + "$ref": "#/components/schemas/FilterId" + } + } + ], + "result": { + "name": "filterUninstalledSuccess", + "description": "returns true if the filter was successfully uninstalled, false otherwise.", + "schema": { + "type": "boolean" + } + } + } + ], + "components": { + "schemas": { + "ProofNode": { + "title": "proofNode", + "description": "An individual node used to prove a path down a merkle-patricia-tree", + "$ref": "#/components/schemas/Bytes" + }, + "StorageProofKey": { + "title": "storageProofKey", + "description": "The key used to get the storage slot in its account tree.", + "$ref": "#/components/schemas/Integer" + }, + "StorageProof": { + "title": "storageProofSet", + "type": "array", + "description": "Current block header PoW hash.", + "items": { + "title": "storageProof", + "type": "object", + "description": "Object proving a relationship of a storage value to an account's storageHash.", + "properties": { + "key": { + "$ref": "#/components/schemas/StorageProofKey" + }, + "value": { + "title": "storageProofValue", + "description": "The value of the storage slot in its account tree", + "$ref": "#/components/schemas/Integer" + }, + "proof": { + "$ref": "#/components/schemas/ProofNodes" + } + } + } + }, + "ProofNodes": { + "title": "proofNodes", + "type": "array", + "description": "The set of node values needed to traverse a patricia merkle tree (from root to leaf) to retrieve a value", + "items": { + "$ref": "#/components/schemas/ProofNode" + } + }, + "PowHash": { + "title": "powHash", + "description": "Current block header PoW hash.", + "$ref": "#/components/schemas/DataWord" + }, + "SeedHash": { + "title": "seedHash", + "description": "The seed hash used for the DAG.", + "$ref": "#/components/schemas/DataWord" + }, + "MixHash": { + "title": "mixHash", + "description": "The mix digest.", + "$ref": "#/components/schemas/DataWord" + }, + "Difficulty": { + "title": "difficulty", + "description": "The boundary condition ('target'), 2^256 / difficulty.", + "$ref": "#/components/schemas/DataWord" + }, + "FilterId": { + "title": "filterId", + "type": "string", + "description": "An identifier used to reference the filter." + }, + "BlockHash": { + "title": "blockHash", + "type": "string", + "pattern": "^0x[a-fA-F\\d]{64}$", + "description": "The hex representation of the Keccak 256 of the RLP encoded block" + }, + "BlockNumber": { + "title": "blockNumber", + "type": "string", + "description": "The hex representation of the block's height", + "$ref": "#/components/schemas/Integer" + }, + "BlockNumberTag": { + "title": "blockNumberTag", + "type": "string", + "description": "The optional block height description", + "enum": [ + "earliest", + "latest", + "pending" + ] + }, + "BlockOrNull": { + "title": "blockOrNull", + "oneOf": [ + { + "$ref": "#/components/schemas/Block" + }, + { + "$ref": "#/components/schemas/Null" + } + ] + }, + "IntegerOrNull": { + "title": "integerOrNull", + "oneOf": [ + { + "$ref": "#/components/schemas/Integer" + }, + { + "$ref": "#/components/schemas/Null" + } + ] + }, + "AddressOrNull": { + "title": "addressOrNull", + "oneOf": [ + { + "$ref": "#/components/schemas/Address" + }, + { + "$ref": "#/components/schemas/Null" + } + ] + }, + "Receipt": { + "title": "receipt", + "type": "object", + "description": "The receipt of a transaction", + "required": [ + "blockHash", + "blockNumber", + "contractAddress", + "cumulativeGasUsed", + "from", + "gasUsed", + "logs", + "logsBloom", + "to", + "transactionHash", + "transactionIndex" + ], + "properties": { + "blockHash": { + "$ref": "#/components/schemas/BlockHash" + }, + "blockNumber": { + "$ref": "#/components/schemas/BlockNumber" + }, + "contractAddress": { + "title": "ReceiptContractAddress", + "description": "The contract address created, if the transaction was a contract creation, otherwise null", + "$ref": "#/components/schemas/AddressOrNull" + }, + "cumulativeGasUsed": { + "title": "ReceiptCumulativeGasUsed", + "description": "The gas units used by the transaction", + "$ref": "#/components/schemas/Integer" + }, + "from": { + "$ref": "#/components/schemas/From" + }, + "gasUsed": { + "title": "ReceiptGasUsed", + "description": "The total gas used by the transaction", + "$ref": "#/components/schemas/Integer" + }, + "logs": { + "title": "logs", + "type": "array", + "description": "An array of all the logs triggered during the transaction", + "items": { + "$ref": "#/components/schemas/Log" + } + }, + "logsBloom": { + "$ref": "#/components/schemas/BloomFilter" + }, + "to": { + "$ref": "#/components/schemas/To" + }, + "transactionHash": { + "$ref": "#/components/schemas/TransactionHash" + }, + "transactionIndex": { + "$ref": "#/components/schemas/TransactionIndex" + }, + "postTransactionState": { + "title": "ReceiptPostTransactionState", + "description": "The intermediate stateRoot directly after transaction execution.", + "$ref": "#/components/schemas/Keccak" + }, + "status": { + "title": "ReceiptStatus", + "description": "Whether or not the transaction threw an error.", + "type": "boolean" + } + } + }, + "BloomFilter": { + "title": "bloomFilter", + "type": "string", + "description": "A 2048 bit bloom filter from the logs of the transaction. Each log sets 3 bits though taking the low-order 11 bits of each of the first three pairs of bytes in a Keccak 256 hash of the log's byte series" + }, + "Log": { + "title": "log", + "type": "object", + "description": "An indexed event generated during a transaction", + "properties": { + "address": { + "title": "LogAddress", + "description": "Sender of the transaction", + "$ref": "#/components/schemas/Address" + }, + "blockHash": { + "$ref": "#/components/schemas/BlockHash" + }, + "blockNumber": { + "$ref": "#/components/schemas/BlockNumber" + }, + "data": { + "title": "LogData", + "description": "The data/input string sent along with the transaction", + "$ref": "#/components/schemas/Bytes" + }, + "logIndex": { + "title": "LogIndex", + "description": "The index of the event within its transaction, null when its pending", + "$ref": "#/components/schemas/Integer" + }, + "removed": { + "title": "logIsRemoved", + "description": "Whether or not the log was orphaned off the main chain", + "type": "boolean" + }, + "topics": { + "$ref": "#/components/schemas/Topics" + }, + "transactionHash": { + "$ref": "#/components/schemas/TransactionHash" + }, + "transactionIndex": { + "$ref": "#/components/schemas/TransactionIndex" + } + } + }, + "Topics": { + "title": "LogTopics", + "description": "Topics are order-dependent. Each topic can also be an array of DATA with 'or' options.", + "type": "array", + "items": { + "$ref": "#/components/schemas/Topic" + } + }, + "Topic": { + "title": "topic", + "description": "32 Bytes DATA of indexed log arguments. (In solidity: The first topic is the hash of the signature of the event (e.g. Deposit(address,bytes32,uint256))", + "$ref": "#/components/schemas/DataWord" + }, + "TransactionIndex": { + "title": "transactionIndex", + "description": "The index of the transaction. null when its pending", + "$ref": "#/components/schemas/IntegerOrNull" + }, + "BlockNumberOrNull": { + "title": "blockNumberOrNull", + "description": "The block number or null when its the pending block", + "oneOf": [ + { + "$ref": "#/components/schemas/BlockNumber" + }, + { + "$ref": "#/components/schemas/Null" + } + ] + }, + "BlockHashOrNull": { + "title": "blockHashOrNull", + "description": "The block hash or null when its the pending block", + "$ref": "#/components/schemas/KeccakOrPending" + }, + "NonceOrNull": { + "title": "nonceOrNull", + "description": "Randomly selected number to satisfy the proof-of-work or null when its the pending block", + "oneOf": [ + { + "$ref": "#/components/schemas/Nonce" + }, + { + "$ref": "#/components/schemas/Null" + } + ] + }, + "From": { + "title": "From", + "description": "The sender of the transaction", + "$ref": "#/components/schemas/Address" + }, + "To": { + "title": "To", + "description": "Destination address of the transaction. Null if it was a contract create.", + "oneOf": [ + { + "$ref": "#/components/schemas/Address" + }, + { + "$ref": "#/components/schemas/Null" + } + ] + }, + "Block": { + "title": "Block", + "description": "The Block is the collection of relevant pieces of information (known as the block header), together with information corresponding to the comprised transactions, and a set of other block headers that are known to have a parent equal to the present block’s parent’s parent.", + "type": "object", + "properties": { + "number": { + "$ref": "#/components/schemas/BlockNumberOrNull" + }, + "hash": { + "$ref": "#/components/schemas/BlockHashOrNull" + }, + "parentHash": { + "$ref": "#/components/schemas/BlockHash" + }, + "nonce": { + "$ref": "#/components/schemas/NonceOrNull" + }, + "sha3Uncles": { + "title": "blockShaUncles", + "description": "Keccak hash of the uncles data in the block", + "$ref": "#/components/schemas/Keccak" + }, + "logsBloom": { + "title": "blockLogsBloom", + "type": "string", + "description": "The bloom filter for the logs of the block or null when its the pending block", + "pattern": "^0x[a-fA-F\\d]+$" + }, + "transactionsRoot": { + "title": "blockTransactionsRoot", + "description": "The root of the transactions trie of the block.", + "$ref": "#/components/schemas/Keccak" + }, + "stateRoot": { + "title": "blockStateRoot", + "description": "The root of the final state trie of the block", + "$ref": "#/components/schemas/Keccak" + }, + "receiptsRoot": { + "title": "blockReceiptsRoot", + "description": "The root of the receipts trie of the block", + "$ref": "#/components/schemas/Keccak" + }, + "miner": { + "$ref": "#/components/schemas/AddressOrNull" + }, + "difficulty": { + "title": "blockDifficulty", + "type": "string", + "description": "Integer of the difficulty for this block" + }, + "totalDifficulty": { + "title": "blockTotalDifficulty", + "description": "Integer of the total difficulty of the chain until this block", + "$ref": "#/components/schemas/IntegerOrNull" + }, + "extraData": { + "title": "blockExtraData", + "type": "string", + "description": "The 'extra data' field of this block" + }, + "size": { + "title": "blockSize", + "type": "string", + "description": "Integer the size of this block in bytes" + }, + "gasLimit": { + "title": "blockGasLimit", + "type": "string", + "description": "The maximum gas allowed in this block" + }, + "gasUsed": { + "title": "blockGasUsed", + "type": "string", + "description": "The total used gas by all transactions in this block" + }, + "timestamp": { + "title": "blockTimeStamp", + "type": "string", + "description": "The unix timestamp for when the block was collated" + }, + "transactions": { + "title": "transactionsOrHashes", + "description": "Array of transaction objects, or 32 Bytes transaction hashes depending on the last given parameter", + "type": "array", + "items": { + "title": "transactionOrTransactionHash", + "oneOf": [ + { + "$ref": "#/components/schemas/Transaction" + }, + { + "$ref": "#/components/schemas/TransactionHash" + } + ] + } + }, + "uncles": { + "title": "uncleHashes", + "description": "Array of uncle hashes", + "type": "array", + "items": { + "title": "uncleHash", + "description": "Block hash of the RLP encoding of an uncle block", + "$ref": "#/components/schemas/Keccak" + } + } + } + }, + "Transaction": { + "title": "transaction", + "type": "object", + "required": [ + "gas", + "gasPrice", + "nonce" + ], + "properties": { + "blockHash": { + "$ref": "#/components/schemas/BlockHashOrNull" + }, + "blockNumber": { + "$ref": "#/components/schemas/BlockNumberOrNull" + }, + "from": { + "$ref": "#/components/schemas/From" + }, + "gas": { + "title": "transactionGas", + "type": "string", + "description": "The gas limit provided by the sender in Wei" + }, + "gasPrice": { + "title": "transactionGasPrice", + "type": "string", + "description": "The gas price willing to be paid by the sender in Wei" + }, + "hash": { + "$ref": "#/components/schemas/TransactionHash" + }, + "input": { + "title": "transactionInput", + "type": "string", + "description": "The data field sent with the transaction" + }, + "nonce": { + "title": "transactionNonce", + "description": "The total number of prior transactions made by the sender", + "$ref": "#/components/schemas/Nonce" + }, + "to": { + "$ref": "#/components/schemas/To" + }, + "transactionIndex": { + "$ref": "#/components/schemas/TransactionIndex" + }, + "value": { + "title": "transactionValue", + "description": "Value of Ether being transferred in Wei", + "$ref": "#/components/schemas/Keccak" + }, + "v": { + "title": "transactionSigV", + "type": "string", + "description": "ECDSA recovery id" + }, + "r": { + "title": "transactionSigR", + "type": "string", + "description": "ECDSA signature r" + }, + "s": { + "title": "transactionSigS", + "type": "string", + "description": "ECDSA signature s" + } + } + }, + "Transactions": { + "title": "transactions", + "description": "An array of transactions", + "type": "array", + "items": { + "$ref": "#/components/schemas/Transaction" + } + }, + "TransactionHash": { + "title": "transactionHash", + "type": "string", + "description": "Keccak 256 Hash of the RLP encoding of a transaction", + "$ref": "#/components/schemas/Keccak" + }, + "KeccakOrPending": { + "title": "keccakOrPending", + "oneOf": [ + { + "$ref": "#/components/schemas/Keccak" + }, + { + "$ref": "#/components/schemas/Null" + } + ] + }, + "Keccak": { + "title": "keccak", + "type": "string", + "description": "Hex representation of a Keccak 256 hash", + "pattern": "^0x[a-fA-F\\d]{64}$" + }, + "Nonce": { + "title": "nonce", + "description": "A number only to be used once", + "$ref": "#/components/schemas/Integer" + }, + "Null": { + "title": "null", + "type": "null", + "description": "Null" + }, + "Integer": { + "title": "integer", + "type": "string", + "pattern": "^0x[a-fA-F0-9]+$", + "description": "Hex representation of the integer" + }, + "Address": { + "title": "address", + "type": "string", + "pattern": "^0x[a-fA-F\\d]{40}$" + }, + "Addresses": { + "title": "addresses", + "type": "array", + "description": "List of contract addresses from which to monitor events", + "items": { + "$ref": "#/components/schemas/Address" + } + }, + "Position": { + "title": "position", + "type": "string", + "description": "Hex representation of the storage slot where the variable exists", + "pattern": "^0x([a-fA-F0-9]?)+$" + }, + "DataWord": { + "title": "dataWord", + "type": "string", + "description": "Hex representation of a 256 bit unit of data", + "pattern": "^0x([a-fA-F\\d]{64})?$" + }, + "Bytes": { + "title": "bytes", + "type": "string", + "description": "Hex representation of a variable length byte array", + "pattern": "^0x([a-fA-F0-9]?)+$" + } + }, + "contentDescriptors": { + "Block": { + "name": "block", + "summary": "A block", + "description": "A block object", + "schema": { + "$ref": "#/components/schemas/Block" + } + }, + "Null": { + "name": "Null", + "description": "JSON Null value", + "summary": "Null value", + "schema": { + "$ref": "#/components/schemas/Null" + } + }, + "Signature": { + "name": "signature", + "summary": "The signature.", + "required": true, + "schema": { + "title": "signatureBytes", + "type": "string", + "description": "Hex representation of byte array between 2 and 65 chars long", + "pattern": "0x^([A-Fa-f0-9]{2}){65}$" + } + }, + "GasPrice": { + "name": "gasPrice", + "required": true, + "schema": { + "title": "gasPriceResult", + "description": "Integer of the current gas price", + "$ref": "#/components/schemas/Integer" + } + }, + "Transaction": { + "required": true, + "name": "transaction", + "schema": { + "$ref": "#/components/schemas/Transaction" + } + }, + "TransactionResult": { + "name": "transactionResult", + "description": "Returns a transaction or null", + "schema": { + "title": "TransactionOrNull", + "oneOf": [ + { + "$ref": "#/components/schemas/Transaction" + }, + { + "$ref": "#/components/schemas/Null" + } + ] + } + }, + "UncleCountResult": { + "name": "uncleCountResult", + "description": "The Number of total uncles in the given block", + "schema": { + "$ref": "#/components/schemas/IntegerOrNull" + } + }, + "Message": { + "name": "message", + "required": true, + "schema": { + "$ref": "#/components/schemas/Bytes" + } + }, + "Filter": { + "name": "filter", + "required": true, + "schema": { + "title": "filter", + "type": "object", + "description": "A filter used to monitor the blockchain for log/events", + "properties": { + "fromBlock": { + "$ref": "#/components/schemas/BlockNumber" + }, + "toBlock": { + "$ref": "#/components/schemas/BlockNumber" + }, + "address": { + "title": "oneOrArrayOfAddresses", + "oneOf": [ + { + "$ref": "#/components/schemas/Address" + }, + { + "$ref": "#/components/schemas/Addresses" + } + ] + }, + "topics": { + "$ref": "#/components/schemas/Topics" + } + } + } + }, + "Address": { + "name": "address", + "required": true, + "schema": { + "$ref": "#/components/schemas/Address" + } + }, + "BlockHash": { + "name": "blockHash", + "required": true, + "schema": { + "$ref": "#/components/schemas/BlockHash" + } + }, + "Nonce": { + "name": "nonce", + "required": true, + "schema": { + "$ref": "#/components/schemas/Nonce" + } + }, + "Position": { + "name": "key", + "required": true, + "schema": { + "$ref": "#/components/schemas/Position" + } + }, + "Logs": { + "name": "logs", + "description": "An array of all logs matching filter with given id.", + "schema": { + "title": "setOfLogs", + "type": "array", + "items": { + "$ref": "#/components/schemas/Log" + } + } + }, + "FilterId": { + "name": "filterId", + "schema": { + "$ref": "#/components/schemas/FilterId" + } + }, + "BlockNumber": { + "name": "blockNumber", + "required": true, + "schema": { + "title": "blockNumberOrTag", + "oneOf": [ + { + "$ref": "#/components/schemas/BlockNumber" + }, + { + "$ref": "#/components/schemas/BlockNumberTag" + } + ] + } + }, + "TransactionHash": { + "name": "transactionHash", + "required": true, + "schema": { + "$ref": "#/components/schemas/TransactionHash" + } + } + } + } +} diff --git a/node/impl/full/eth.go b/node/impl/full/eth.go index 7eb992a22..2814c2cee 100644 --- a/node/impl/full/eth.go +++ b/node/impl/full/eth.go @@ -1776,7 +1776,11 @@ func newEthTxReceipt(ctx context.Context, tx ethtypes.EthTx, lookup *api.MsgLook } for _, entry := range evt.Entries { - value := ethtypes.EthBytes(leftpad32(entry.Value)) // value has already been cbor-decoded but see https://github.com/filecoin-project/ref-fvm/issues/1345 + value, err := cborDecodeTopicValue(entry.Value) + if err != nil { + return api.EthTxReceipt{}, xerrors.Errorf("failed to decode entry topic value: %w", err) + } + if entry.Key == ethtypes.EthTopic1 || entry.Key == ethtypes.EthTopic2 || entry.Key == ethtypes.EthTopic3 || entry.Key == ethtypes.EthTopic4 { l.Topics = append(l.Topics, value) } else { @@ -1900,3 +1904,14 @@ func leftpad32(orig []byte) []byte { copy(ret[needed:], orig) return ret } + +func cborDecodeTopicValue(orig []byte) ([]byte, error) { + if len(orig) == 0 { + return orig, nil + } + decoded, err := cbg.ReadByteArray(bytes.NewReader(orig), uint64(len(orig))) + if err != nil { + return nil, err + } + return leftpad32(decoded), nil +} From 7d1bd1d5aba8d339c29a7eef24579a81c3683d76 Mon Sep 17 00:00:00 2001 From: Ian Davis Date: Mon, 23 Jan 2023 16:50:40 +0000 Subject: [PATCH 02/30] Use gojsonschema --- chain/types/ethtypes/eth_types.go | 42 + go.mod | 13 +- go.sum | 81 +- itests/eth_conformance_test.go | 262 +- itests/specs/eth_openrpc.json | 6174 ++++++++++++++++++++--------- node/impl/full/eth.go | 79 +- 6 files changed, 4680 insertions(+), 1971 deletions(-) diff --git a/chain/types/ethtypes/eth_types.go b/chain/types/ethtypes/eth_types.go index 235cc7c79..a9c537f06 100644 --- a/chain/types/ethtypes/eth_types.go +++ b/chain/types/ethtypes/eth_types.go @@ -441,9 +441,51 @@ type EthFeeHistory struct { type EthFilterID EthHash +func (id EthFilterID) String() string { + return "0x" + hex.EncodeToString(id[:]) +} + +func (id EthFilterID) MarshalJSON() ([]byte, error) { + return json.Marshal(id.String()) +} + +func (id *EthFilterID) UnmarshalJSON(b []byte) error { + var s string + if err := json.Unmarshal(b, &s); err != nil { + return err + } + hash, err := ParseEthHash(s) + if err != nil { + return err + } + copy(id[:], hash[:]) + return nil +} + // An opaque identifier generated by the Lotus node to refer to an active subscription. type EthSubscriptionID EthHash +func (id EthSubscriptionID) String() string { + return "0x" + hex.EncodeToString(id[:]) +} + +func (id EthSubscriptionID) MarshalJSON() ([]byte, error) { + return json.Marshal(id.String()) +} + +func (id *EthSubscriptionID) UnmarshalJSON(b []byte) error { + var s string + if err := json.Unmarshal(b, &s); err != nil { + return err + } + hash, err := ParseEthHash(s) + if err != nil { + return err + } + copy(id[:], hash[:]) + return nil +} + type EthFilterSpec struct { // Interpreted as an epoch or one of "latest" for last mined block, "earliest" for first, // "pending" for not yet committed messages. diff --git a/go.mod b/go.mod index 8fa2d0350..91a6ba2d4 100644 --- a/go.mod +++ b/go.mod @@ -62,10 +62,7 @@ require ( github.com/gbrlsnchs/jwt/v3 v3.0.1 github.com/gdamore/tcell/v2 v2.2.0 github.com/go-kit/kit v0.12.0 - github.com/go-openapi/errors v0.19.9 github.com/go-openapi/spec v0.20.8 - github.com/go-openapi/strfmt v0.21.1 - github.com/go-openapi/validate v0.22.1 github.com/golang/mock v1.6.0 github.com/google/uuid v1.3.0 github.com/gorilla/mux v1.8.0 @@ -150,6 +147,7 @@ require ( github.com/whyrusleeping/cbor-gen v0.0.0-20221021053955-c138aae13722 github.com/whyrusleeping/ledger-filecoin-go v0.9.1-0.20201010031517-c3dcc1bddce4 github.com/whyrusleeping/multiaddr-filter v0.0.0-20160516205228-e903e4adabd7 + github.com/xeipuuv/gojsonschema v1.2.0 github.com/xorcare/golden v0.6.1-0.20191112154924-b87f686d7542 go.opencensus.io v0.23.0 go.opentelemetry.io/otel v1.11.1 @@ -178,7 +176,6 @@ require ( github.com/akavel/rsrc v0.8.0 // indirect github.com/alecthomas/units v0.0.0-20210927113745-59d0afb8317a // indirect github.com/armon/go-metrics v0.3.9 // indirect - github.com/asaskevich/govalidator v0.0.0-20200907205600-7a23bdc65eef // indirect github.com/benbjohnson/clock v1.3.0 // indirect github.com/beorn7/perks v1.0.1 // indirect github.com/bep/debounce v1.2.1 // indirect @@ -215,12 +212,9 @@ require ( github.com/go-logr/logr v1.2.3 // indirect github.com/go-logr/stdr v1.2.2 // indirect github.com/go-ole/go-ole v1.2.5 // indirect - github.com/go-openapi/analysis v0.21.2 // indirect github.com/go-openapi/jsonpointer v0.19.5 // indirect github.com/go-openapi/jsonreference v0.20.0 // indirect - github.com/go-openapi/loads v0.21.1 // indirect github.com/go-openapi/swag v0.21.1 // indirect - github.com/go-stack/stack v1.8.0 // indirect github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0 // indirect github.com/godbus/dbus/v5 v5.1.0 // indirect github.com/gogo/protobuf v1.3.2 // indirect @@ -295,7 +289,6 @@ require ( github.com/mikioh/tcpinfo v0.0.0-20190314235526-30a79bb1804b // indirect github.com/mikioh/tcpopt v0.0.0-20190314235656-172688c1accc // indirect github.com/minio/sha256-simd v1.0.0 // indirect - github.com/mitchellh/mapstructure v1.4.2 // indirect github.com/mr-tron/base58 v1.2.0 // indirect github.com/multiformats/go-base36 v0.1.0 // indirect github.com/multiformats/go-multiaddr-fmt v0.1.0 // indirect @@ -304,7 +297,6 @@ require ( github.com/nikkolasg/hexjson v0.0.0-20181101101858-78e39397e00c // indirect github.com/nkovacs/streamquote v1.0.0 // indirect github.com/nxadm/tail v1.4.8 // indirect - github.com/oklog/ulid v1.3.1 // indirect github.com/onsi/ginkgo v1.16.5 // indirect github.com/opencontainers/runtime-spec v1.0.2 // indirect github.com/opentracing/opentracing-go v1.2.0 // indirect @@ -332,10 +324,11 @@ require ( github.com/whyrusleeping/chunker v0.0.0-20181014151217-fe64bd25879f // indirect github.com/whyrusleeping/go-keyspace v0.0.0-20160322163242-5b898ac5add1 // indirect github.com/whyrusleeping/timecache v0.0.0-20160911033111-cfcb2f1abfee // indirect + github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f // indirect + github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415 // indirect github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673 // indirect github.com/zondax/hid v0.9.1 // indirect github.com/zondax/ledger-go v0.12.1 // indirect - go.mongodb.org/mongo-driver v1.7.5 // indirect go.opentelemetry.io/otel/metric v0.33.0 // indirect go.opentelemetry.io/otel/sdk/metric v0.33.0 // indirect go.opentelemetry.io/otel/trace v1.11.1 // indirect diff --git a/go.sum b/go.sum index e8dfd68ee..f83c152a5 100644 --- a/go.sum +++ b/go.sum @@ -106,8 +106,6 @@ github.com/armon/go-metrics v0.3.9/go.mod h1:4O98XIr/9W0sxpJ8UaYkvjk10Iff7SnFrb4 github.com/armon/go-radix v0.0.0-20180808171621-7fddfc383310/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8= github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5/go.mod h1:wHh0iHkYZB8zMSxRWpUBQtwG5a7fFgvEO+odwuTv2gs= github.com/aryann/difflib v0.0.0-20170710044230-e206f873d14a/go.mod h1:DAHtR1m6lCRdSC2Tm3DSWRPvIPr6xNKyeHdqDQSQT+A= -github.com/asaskevich/govalidator v0.0.0-20200907205600-7a23bdc65eef h1:46PFijGLmAjMPwCCCo7Jf0W6f9slllCkkv7vyc1yOSg= -github.com/asaskevich/govalidator v0.0.0-20200907205600-7a23bdc65eef/go.mod h1:WaHUgvxTVq04UNunO+XhnAqY/wQc+bxr74GqbsZ/Jqw= github.com/aws/aws-lambda-go v1.13.3/go.mod h1:4UKl9IzQMoD+QF79YdCuzCwp8VbmG4VAQwij/eHl5CU= github.com/aws/aws-sdk-go v1.27.0/go.mod h1:KmX6BPdI08NWTb3/sm4ZGu5ShLoqVDhKgpiN924inxo= github.com/aws/aws-sdk-go v1.32.11/go.mod h1:5zCpMtNQVjRREroY7sYe8lOMRSxkhG6MZveU8YkpAk0= @@ -444,11 +442,6 @@ github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag= github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE= github.com/go-ole/go-ole v1.2.5 h1:t4MGB5xEDZvXI+0rMjjsfBsD7yAgp/s9ZDkL1JndXwY= github.com/go-ole/go-ole v1.2.5/go.mod h1:pprOEPIfldk/42T2oK7lQ4v4JSDwmV0As9GaiUsvbm0= -github.com/go-openapi/analysis v0.21.2 h1:hXFrOYFHUAMQdu6zwAiKKJHJQ8kqZs1ux/ru1P1wLJU= -github.com/go-openapi/analysis v0.21.2/go.mod h1:HZwRk4RRisyG8vx2Oe6aqeSQcoxRp47Xkp3+K6q+LdY= -github.com/go-openapi/errors v0.19.8/go.mod h1:cM//ZKUKyO06HSwqAelJ5NsEMMcpa6VpXe8DOa1Mi1M= -github.com/go-openapi/errors v0.19.9 h1:9SnKdGhiPZHF3ttwFMiCBEb8jQ4IDdrK+5+a0oTygA4= -github.com/go-openapi/errors v0.19.9/go.mod h1:cM//ZKUKyO06HSwqAelJ5NsEMMcpa6VpXe8DOa1Mi1M= github.com/go-openapi/jsonpointer v0.19.2/go.mod h1:3akKfEdA7DF1sugOqz1dVQHBcuDBPKZGEoHC/NkiQRg= github.com/go-openapi/jsonpointer v0.19.3/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg= github.com/go-openapi/jsonpointer v0.19.5 h1:gZr+CIYByUqjcgeLXnQu2gHYQC9o73G2XUeOFYEICuY= @@ -456,20 +449,13 @@ github.com/go-openapi/jsonpointer v0.19.5/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34 github.com/go-openapi/jsonreference v0.19.2/go.mod h1:jMjeRr2HHw6nAVajTXJ4eiUwohSTlpa0o73RUL1owJc= github.com/go-openapi/jsonreference v0.19.3/go.mod h1:rjx6GuL8TTa9VaixXglHmQmIL98+wF9xc8zWvFonSJ8= github.com/go-openapi/jsonreference v0.19.4/go.mod h1:RdybgQwPxbL4UEjuAruzK1x3nE69AqPYEJeo/TWfEeg= -github.com/go-openapi/jsonreference v0.19.6/go.mod h1:diGHMEHg2IqXZGKxqyvWdfWU/aim5Dprw5bqpKkTvns= github.com/go-openapi/jsonreference v0.20.0 h1:MYlu0sBgChmCfJxxUKZ8g1cPWFOB37YSZqewK7OKeyA= github.com/go-openapi/jsonreference v0.20.0/go.mod h1:Ag74Ico3lPc+zR+qjn4XBUmXymS4zJbYVCZmcgkasdo= -github.com/go-openapi/loads v0.21.1 h1:Wb3nVZpdEzDTcly8S4HMkey6fjARRzb7iEaySimlDW0= -github.com/go-openapi/loads v0.21.1/go.mod h1:/DtAMXXneXFjbQMGEtbamCZb+4x7eGwkvZCvBmwUG+g= github.com/go-openapi/spec v0.19.2/go.mod h1:sCxk3jxKgioEJikev4fgkNmwS+3kuYdJtcsZsD5zxMY= github.com/go-openapi/spec v0.19.7/go.mod h1:Hm2Jr4jv8G1ciIAo+frC/Ft+rR2kQDh8JHKHb3gWUSk= github.com/go-openapi/spec v0.19.11/go.mod h1:vqK/dIdLGCosfvYsQV3WfC7N3TiZSnGY2RZKoFK7X28= -github.com/go-openapi/spec v0.20.4/go.mod h1:faYFR1CvsJZ0mNsmsphTMSoRrNV3TEDoAM7FOEWeq8I= github.com/go-openapi/spec v0.20.8 h1:ubHmXNY3FCIOinT8RNrrPfGc9t7I1qhPtdOGoG2AxRU= github.com/go-openapi/spec v0.20.8/go.mod h1:2OpW+JddWPrpXSCIX8eOx7lZ5iyuWj3RYR6VaaBKcWA= -github.com/go-openapi/strfmt v0.21.0/go.mod h1:ZRQ409bWMj+SOgXofQAGTIo2Ebu72Gs+WaRADcS5iNg= -github.com/go-openapi/strfmt v0.21.1 h1:G6s2t5V5kGCHLVbSdZ/6lI8Wm4OzoPFkc3/cjAsKQrM= -github.com/go-openapi/strfmt v0.21.1/go.mod h1:I/XVKeLc5+MM5oPNN7P6urMOpuLXEcNrCX/rPGuWb0k= github.com/go-openapi/swag v0.19.2/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh66Z9tfKk= github.com/go-openapi/swag v0.19.5/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh66Z9tfKk= github.com/go-openapi/swag v0.19.8/go.mod h1:ao+8BpOPyKdpQz3AOJfbeEVpLmWAvlT1IfTe5McPyhY= @@ -477,41 +463,15 @@ github.com/go-openapi/swag v0.19.11/go.mod h1:Uc0gKkdR+ojzsEpjh39QChyu92vPgIr72P github.com/go-openapi/swag v0.19.15/go.mod h1:QYRuS/SOXUCsnplDa677K7+DxSOj6IPNl/eQntq43wQ= github.com/go-openapi/swag v0.21.1 h1:wm0rhTb5z7qpJRHBdPOMuY4QjVUMbF6/kwoYeRAOrKU= github.com/go-openapi/swag v0.21.1/go.mod h1:QYRuS/SOXUCsnplDa677K7+DxSOj6IPNl/eQntq43wQ= -github.com/go-openapi/validate v0.22.1 h1:G+c2ub6q47kfX1sOBLwIQwzBVt8qmOAARyo/9Fqs9NU= -github.com/go-openapi/validate v0.22.1/go.mod h1:rjnrwK57VJ7A8xqfpAOEKRH8yQSGUriMu5/zuPSQ1hg= github.com/go-sql-driver/mysql v1.4.0/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG5ZlKdlhCg5w= github.com/go-sql-driver/mysql v1.5.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg= -github.com/go-stack/stack v1.8.0 h1:5SgMzNM5HxrEjV0ww2lTmX6E2Izsfxas4+YHWRs3Lsk= github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0 h1:p104kn46Q8WdvHunIJ9dAyjPVtrBPhSr3KT2yUst43I= github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0/go.mod h1:fyg7847qk6SyHyPtNmDHnmrv/HOrqktSC+C9fM+CJOE= -github.com/gobuffalo/attrs v0.0.0-20190224210810-a9411de4debd/go.mod h1:4duuawTqi2wkkpB4ePgWMaai6/Kc6WEz83bhFwpHzj0= -github.com/gobuffalo/depgen v0.0.0-20190329151759-d478694a28d3/go.mod h1:3STtPUQYuzV0gBVOY3vy6CfMm/ljR4pABfrTeHNLHUY= -github.com/gobuffalo/depgen v0.1.0/go.mod h1:+ifsuy7fhi15RWncXQQKjWS9JPkdah5sZvtHc2RXGlg= -github.com/gobuffalo/envy v1.6.15/go.mod h1:n7DRkBerg/aorDM8kbduw5dN3oXGswK5liaSCx4T5NI= github.com/gobuffalo/envy v1.7.0/go.mod h1:n7DRkBerg/aorDM8kbduw5dN3oXGswK5liaSCx4T5NI= -github.com/gobuffalo/flect v0.1.0/go.mod h1:d2ehjJqGOH/Kjqcoz+F7jHTBbmDb38yXA598Hb50EGs= -github.com/gobuffalo/flect v0.1.1/go.mod h1:8JCgGVbRjJhVgD6399mQr4fx5rRfGKVzFjbj6RE/9UI= -github.com/gobuffalo/flect v0.1.3/go.mod h1:8JCgGVbRjJhVgD6399mQr4fx5rRfGKVzFjbj6RE/9UI= -github.com/gobuffalo/genny v0.0.0-20190329151137-27723ad26ef9/go.mod h1:rWs4Z12d1Zbf19rlsn0nurr75KqhYp52EAGGxTbBhNk= -github.com/gobuffalo/genny v0.0.0-20190403191548-3ca520ef0d9e/go.mod h1:80lIj3kVJWwOrXWWMRzzdhW3DsrdjILVil/SFKBzF28= -github.com/gobuffalo/genny v0.1.0/go.mod h1:XidbUqzak3lHdS//TPu2OgiFB+51Ur5f7CSnXZ/JDvo= -github.com/gobuffalo/genny v0.1.1/go.mod h1:5TExbEyY48pfunL4QSXxlDOmdsD44RRq4mVZ0Ex28Xk= -github.com/gobuffalo/gitgen v0.0.0-20190315122116-cc086187d211/go.mod h1:vEHJk/E9DmhejeLeNt7UVvlSGv3ziL+djtTr3yyzcOw= -github.com/gobuffalo/gogen v0.0.0-20190315121717-8f38393713f5/go.mod h1:V9QVDIxsgKNZs6L2IYiGR8datgMhB577vzTDqypH360= -github.com/gobuffalo/gogen v0.1.0/go.mod h1:8NTelM5qd8RZ15VjQTFkAW6qOMx5wBbW4dSCS3BY8gg= -github.com/gobuffalo/gogen v0.1.1/go.mod h1:y8iBtmHmGc4qa3urIyo1shvOD8JftTtfcKi+71xfDNE= -github.com/gobuffalo/logger v0.0.0-20190315122211-86e12af44bc2/go.mod h1:QdxcLw541hSGtBnhUc4gaNIXRjiDppFGaDqzbrBd3v8= github.com/gobuffalo/logger v1.0.0/go.mod h1:2zbswyIUa45I+c+FLXuWl9zSWEiVuthsk8ze5s8JvPs= -github.com/gobuffalo/mapi v1.0.1/go.mod h1:4VAGh89y6rVOvm5A8fKFxYG+wIW6LO1FMTG9hnKStFc= -github.com/gobuffalo/mapi v1.0.2/go.mod h1:4VAGh89y6rVOvm5A8fKFxYG+wIW6LO1FMTG9hnKStFc= -github.com/gobuffalo/packd v0.0.0-20190315124812-a385830c7fc0/go.mod h1:M2Juc+hhDXf/PnmBANFCqx4DM3wRbgDvnVWeG2RIxq4= -github.com/gobuffalo/packd v0.1.0/go.mod h1:M2Juc+hhDXf/PnmBANFCqx4DM3wRbgDvnVWeG2RIxq4= github.com/gobuffalo/packd v0.3.0/go.mod h1:zC7QkmNkYVGKPw4tHpBQ+ml7W/3tIebgeo1b36chA3Q= -github.com/gobuffalo/packr/v2 v2.0.9/go.mod h1:emmyGweYTm6Kdper+iywB6YK5YzuKchGtJQZ0Odn4pQ= -github.com/gobuffalo/packr/v2 v2.2.0/go.mod h1:CaAwI0GPIAv+5wKLtv8Afwl+Cm78K/I/VCm/3ptBN+0= github.com/gobuffalo/packr/v2 v2.6.0/go.mod h1:sgEE1xNZ6G0FNN5xn9pevVu4nywaxHvgup67xisti08= -github.com/gobuffalo/syncx v0.0.0-20190224160051-33c29581e754/go.mod h1:HhnNqWY95UYwwW3uSASeV7vtgYkT2t16hJgV3AEPUpw= github.com/godbus/dbus v0.0.0-20190402143921-271e53dc4968/go.mod h1:/YcGZj5zSblfDWMMoOzV4fas9FZnQYTkDnsGvmh2Grw= github.com/godbus/dbus/v5 v5.0.3/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= @@ -1004,8 +964,6 @@ github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7V github.com/julienschmidt/httprouter v1.3.0/go.mod h1:JR6WtHb+2LUe8TCKY3cZOxFyyO8IZAc4RVcycCCAKdM= github.com/kabukky/httpscerts v0.0.0-20150320125433-617593d7dcb3/go.mod h1:BYpt4ufZiIGv2nXn4gMxnfKV306n3mWXgNu/d2TqdTU= github.com/kami-zh/go-capturer v0.0.0-20171211120116-e492ea43421d/go.mod h1:P2viExyCEfeWGU259JnaQ34Inuec4R38JCyBx2edgD0= -github.com/karrick/godirwalk v1.8.0/go.mod h1:H5KPZjojv4lE+QYImBI8xVtrBRgYrIVsaRPx4tDPEn4= -github.com/karrick/godirwalk v1.10.3/go.mod h1:RoGL9dQei4vP9ilrpETWE8CLOZ1kiN0LhBygSwrAsHA= github.com/karrick/godirwalk v1.10.12/go.mod h1:RoGL9dQei4vP9ilrpETWE8CLOZ1kiN0LhBygSwrAsHA= github.com/kelseyhightower/envconfig v1.4.0 h1:Im6hONhd3pLkfDFsbRgu68RDNkGF1r3dvMUtDTo2cv8= github.com/kelseyhightower/envconfig v1.4.0/go.mod h1:cccZRl6mQpaq41TPp5QxidR+Sa3axMbJDNb//FQX6Gg= @@ -1425,8 +1383,6 @@ github.com/mailru/easyjson v0.0.0-20190626092158-b2ccc519800e/go.mod h1:C1wdFJiN github.com/mailru/easyjson v0.7.1/go.mod h1:KAzv3t3aY1NaHWoQz1+4F1ccyAH66Jk7yos7ldAVICs= github.com/mailru/easyjson v0.7.6 h1:8yTIVnZgCoiM1TgqoeTl+LfU5Jg6/xL3QhGQnimLYnA= github.com/mailru/easyjson v0.7.6/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc= -github.com/markbates/oncer v0.0.0-20181203154359-bf2de49a0be2/go.mod h1:Ld9puTsIW75CHf65OeIOkyKbteujpZVXDpWK6YGZbxE= -github.com/markbates/safe v1.0.1/go.mod h1:nAqgmRi7cY2nqMc92/bSEeQA+R4OheNU2T1kNSCBdG0= github.com/marten-seemann/qpack v0.2.1 h1:jvTsT/HpCn2UZJdP+UUB53FfUUgeOyG5K1ns0OJOGVs= github.com/marten-seemann/qpack v0.2.1/go.mod h1:F7Gl5L1jIgN1D11ucXefiuJS9UMVP2opoCp2jDKb7wc= github.com/marten-seemann/qtls v0.10.0/go.mod h1:UvMd1oaYDACI99/oZUYLzMCkBXQVT0aGm99sJhbT8hs= @@ -1513,16 +1469,11 @@ github.com/mitchellh/gox v0.4.0/go.mod h1:Sd9lOJ0+aimLBi73mGofS1ycjY8lL3uZM3JPS4 github.com/mitchellh/iochan v1.0.0/go.mod h1:JwYml1nuB7xOzsp52dPpHFffvOCDupsG0QubkSMEySY= github.com/mitchellh/mapstructure v0.0.0-20160808181253-ca63d7c062ee/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= -github.com/mitchellh/mapstructure v1.3.3/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= -github.com/mitchellh/mapstructure v1.4.1/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= -github.com/mitchellh/mapstructure v1.4.2 h1:6h7AQ0yhTcIsmFmnAwQls75jp2Gzs4iB8W7pjMO+rqo= -github.com/mitchellh/mapstructure v1.4.2/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= -github.com/montanaflynn/stats v0.0.0-20171201202039-1bf9dbcd8cbe/go.mod h1:wL8QJuTMNUDYhXwkmfOly8iTdp5TEcJFWZD2D7SIkUc= github.com/mr-tron/base58 v1.1.0/go.mod h1:xcD2VGqlgYjBdcBLw+TuYLr8afG+Hj8g2eTVqeSzSU8= github.com/mr-tron/base58 v1.1.1/go.mod h1:xcD2VGqlgYjBdcBLw+TuYLr8afG+Hj8g2eTVqeSzSU8= github.com/mr-tron/base58 v1.1.2/go.mod h1:BinMc/sQntlIE1frQmRFPUoPA1Zkr8VRgBdjWI2mNwc= @@ -1626,8 +1577,6 @@ github.com/nxadm/tail v1.4.8 h1:nPr65rt6Y5JFSKQO7qToXr7pePgD6Gwiw05lkbyAQTE= github.com/nxadm/tail v1.4.8/go.mod h1:+ncqLTQzXmGhMZNUePPaPqPvBxHAIsmXswZKocGu+AU= github.com/oklog/oklog v0.3.2/go.mod h1:FCV+B7mhrz4o+ueLpx+KqkyXRGMWOYEvfiXtdGtbWGs= github.com/oklog/run v1.0.0/go.mod h1:dlhp/R75TPv97u0XWUtDeV/lRKWPKSdTuV0TZvrmrQA= -github.com/oklog/ulid v1.3.1 h1:EGfNDEx6MqHz8B3uNV6QAib1UR2Lm97sHi3ocA6ESJ4= -github.com/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn4U= github.com/olekukonko/tablewriter v0.0.0-20170122224234-a0225b3f23b5/go.mod h1:vsDQFd/mU46D+Z4whnwzcISnGGzXWMclvtLoiIKAKIo= github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= github.com/onsi/ginkgo v1.7.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= @@ -1677,7 +1626,6 @@ github.com/pbnjay/memory v0.0.0-20210728143218-7b4eea64cf58 h1:onHthvaw9LFnH4t2D github.com/pbnjay/memory v0.0.0-20210728143218-7b4eea64cf58/go.mod h1:DXv8WO4yhMYhSNPKjeNKa5WY9YCIEBRbNzFFPJbWO6Y= github.com/pborman/uuid v1.2.0/go.mod h1:X/NO0urCmaxf9VXbdlT7C2Yzkj2IKimNn4k+gtPdI/k= github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic= -github.com/pelletier/go-toml v1.7.0/go.mod h1:vwGMzjaWMwyfHwgIBhI2YUM4fB6nL6lVAvS1LBMMhTE= github.com/performancecopilot/speed v3.0.0+incompatible/go.mod h1:/CLtqpZ5gBg1M9iaPbIdPPGyKcA8hKdoy6hAWba7Yac= github.com/petar/GoLLRB v0.0.0-20210522233825-ae3b015fd3e9 h1:1/WtZae0yGtPq+TI6+Tv1WTxkukpXeMlviSxvL7SRgk= github.com/petar/GoLLRB v0.0.0-20210522233825-ae3b015fd3e9/go.mod h1:x3N5drFsm2uilKKuuYo6LdyD8vZAW55sH/9w+pbo1sw= @@ -1763,7 +1711,6 @@ github.com/rivo/uniseg v0.1.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJ github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg= github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ= github.com/rogpeppe/go-internal v1.1.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= -github.com/rogpeppe/go-internal v1.2.2/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= 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.8.1 h1:geMPLpDpQOgVyCg5z5GoRwLHepNdb71NXb67XFkP+Eg= @@ -1810,8 +1757,6 @@ github.com/shurcooL/users v0.0.0-20180125191416-49c67e49c537/go.mod h1:QJTqeLYED github.com/shurcooL/webdavfs v0.0.0-20170829043945-18c3829fa133/go.mod h1:hKmq5kWdCj2z2KEozexVbfEZIWiTjhE0+UjmZgPqehw= github.com/siebenmann/go-kstat v0.0.0-20160321171754-d34789b79745/go.mod h1:G81aIFAMS9ECrwBYR9YxhlPjWgrItd+Kje78O6+uqm8= github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo= -github.com/sirupsen/logrus v1.4.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo= -github.com/sirupsen/logrus v1.4.1/go.mod h1:ni0Sbl8bgC9z8RoU9G6nDWqqs/fq4eDPysMBDgk/93Q= github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE= github.com/sirupsen/logrus v1.6.0/go.mod h1:7uNnSEd1DgxDLC74fIahvMZmmYsHGZGEOFrfsX/uA88= github.com/sirupsen/logrus v1.7.0/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0= @@ -1956,9 +1901,12 @@ github.com/whyrusleeping/multiaddr-filter v0.0.0-20160516205228-e903e4adabd7/go. github.com/whyrusleeping/timecache v0.0.0-20160911033111-cfcb2f1abfee h1:lYbXeSvJi5zk5GLKVuid9TVjS9a0OmLIDKTfoZBL6Ow= github.com/whyrusleeping/timecache v0.0.0-20160911033111-cfcb2f1abfee/go.mod h1:m2aV4LZI4Aez7dP5PMyVKEHhUyEJ/RjmPEDOpDvudHg= github.com/x-cray/logrus-prefixed-formatter v0.5.2/go.mod h1:2duySbKsL6M18s5GU7VPsoEPHyzalCE06qoARUCeBBE= -github.com/xdg-go/pbkdf2 v1.0.0/go.mod h1:jrpuAogTd400dnrH08LKmI/xc1MbPOebTwRqcT5RDeI= -github.com/xdg-go/scram v1.0.2/go.mod h1:1WAq6h33pAW+iRreB34OORO2Nf7qel3VV3fjBj+hCSs= -github.com/xdg-go/stringprep v1.0.2/go.mod h1:8F9zXuvzgwmyT5DUm4GUfZGDdT3W+LCvS6+da4O5kxM= +github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f h1:J9EGpcZtP0E/raorCMxlFGSTBrsSlaDGf3jU/qvAE2c= +github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f/go.mod h1:N2zxlSyiKSe5eX1tZViRH5QA0qijqEDrYZiPEAiq3wU= +github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415 h1:EzJWgHovont7NscjpAxXsDA8S8BMYve8Y5+7cuRE7R0= +github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415/go.mod h1:GwrjFmJcFw6At/Gs6z4yjiIwzuJ1/+UwLxMQDVQXShQ= +github.com/xeipuuv/gojsonschema v1.2.0 h1:LhYJRs+L4fBtjZUfuSZIKGeVu0QRy8e5Xi7D17UxZ74= +github.com/xeipuuv/gojsonschema v1.2.0/go.mod h1:anYRn/JVcOK2ZgGU+IjEV4nwlhoK5sQluxsYJ78Id3Y= github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU= github.com/xorcare/golden v0.6.0/go.mod h1:7T39/ZMvaSEZlBPoYfVFmsBLmUl3uz9IuzWj/U6FtvQ= github.com/xorcare/golden v0.6.1-0.20191112154924-b87f686d7542 h1:oWgZJmC1DorFZDpfMfWg7xk29yEOZiXmo/wZl+utTI8= @@ -1966,7 +1914,6 @@ github.com/xorcare/golden v0.6.1-0.20191112154924-b87f686d7542/go.mod h1:7T39/ZM github.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77/go.mod h1:aYKd//L2LvnjZzWKhF00oedf4jCCReLcmhLdhm1A27Q= github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673 h1:bAn7/zixMGCfxrRTfdpNzjtPYqr8smhKouy9mxVdGPU= github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673/go.mod h1:N3UwUGtsrSj3ccvlPHLoLsHnpR27oXr4ZE984MbSER8= -github.com/youmark/pkcs8 v0.0.0-20181117223130-1be2e3e5546d/go.mod h1:rHwXgn7JulP+udvsHwJoVG1YGAP6VLg4y9I5dyZdqmA= github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= @@ -1990,9 +1937,6 @@ go.etcd.io/bbolt v1.3.3/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU= go.etcd.io/bbolt v1.3.4 h1:hi1bXHMVrlQh6WwxAy+qZCV/SYIlqo+Ushwdpa4tAKg= go.etcd.io/bbolt v1.3.4/go.mod h1:G5EMThwa9y8QZGBClrRx5EY+Yw9kAhnjy3bSjsnlVTQ= go.etcd.io/etcd v0.0.0-20191023171146-3cf2f69b5738/go.mod h1:dnLIgRNXwCJa5e+c6mIZCrds/GIG4ncV9HhK5PX7jPg= -go.mongodb.org/mongo-driver v1.7.3/go.mod h1:NqaYOwnXWr5Pm7AOpO5QFxKJ503nbMse/R79oO62zWg= -go.mongodb.org/mongo-driver v1.7.5 h1:ny3p0reEpgsR2cfA5cjgwFZg3Cv/ofFh/8jbhGtz9VI= -go.mongodb.org/mongo-driver v1.7.5/go.mod h1:VXEWRZ6URJIkUq2SCAyapmhH0ZLRBP+FT4xhp5Zvxng= go.opencensus.io v0.18.0/go.mod h1:vKdFvxhtzZ9onBp9VKHK8z/sRpBMnKAsufL7wlDrCOA= go.opencensus.io v0.20.1/go.mod h1:6WKK9ahsWS3RSO+PY9ZHZUfv2irvY6gN279GOPZjmmk= go.opencensus.io v0.20.2/go.mod h1:6WKK9ahsWS3RSO+PY9ZHZUfv2irvY6gN279GOPZjmmk= @@ -2080,7 +2024,6 @@ golang.org/x/crypto v0.0.0-20190225124518-7f87c0fbb88b/go.mod h1:6SG95UA2DQfeDnf golang.org/x/crypto v0.0.0-20190228161510-8dd112bcdc25/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20190313024323-a1f597ede03a/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= -golang.org/x/crypto v0.0.0-20190422162423-af44ce270edf/go.mod h1:WFFai1msRO1wXaEeE5yQxYXgSfI8pQAWXbQop6sCtWE= golang.org/x/crypto v0.0.0-20190426145343-a29dc8fdc734/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20190513172903-22d7a77e9e5f/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= @@ -2096,7 +2039,6 @@ golang.org/x/crypto v0.0.0-20191206172530-e9b2fee46413/go.mod h1:LzIPMQfyMNhhGPh golang.org/x/crypto v0.0.0-20200115085410-6d4e4cb37c7d/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20200128174031-69ecbb4d6d5d/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20200221231518-2aa609cf4a9d/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= -golang.org/x/crypto v0.0.0-20200302210943-78000ba7a073/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20200423211502-4bdfaf469ed5/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20200510223506-06a226fb4e37/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20200602180216-279210d13fed/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= @@ -2215,7 +2157,6 @@ 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-20210316092652-d523dce5a7f4/go.mod h1:RBQZq4jEuRlivfhVLdyRGr576XBO4/greRjx4P4O3yc= golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM= -golang.org/x/net v0.0.0-20210421230115-4e50805a0758/go.mod h1:72T/g9IO56b78aLF+1Kcs5dz7/ng1VjMUvfKvpfy+jM= golang.org/x/net v0.0.0-20210423184538-5f58ad60dda6/go.mod h1:OJAsFXCWl8Ukc7SiCT/9KSuxbyM7479/AVlXFRxuMCk= golang.org/x/net v0.0.0-20210428140749-89ef3d95e781/go.mod h1:OJAsFXCWl8Ukc7SiCT/9KSuxbyM7479/AVlXFRxuMCk= golang.org/x/net v0.0.0-20210525063256-abc453219eb5/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= @@ -2242,7 +2183,6 @@ golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJ golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20190412183630-56d357773e84/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20200317015054-43a5402ce75a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= @@ -2273,11 +2213,9 @@ golang.org/x/sys v0.0.0-20190228124157-a34e9553db1e/go.mod h1:STP8DvDyc/dI5b8T5h golang.org/x/sys v0.0.0-20190302025703-b6889370fb10/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190316082340-a2f829d7f35f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190403152447-81d4e9dc473e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190405154228-4b34438f7a67/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190411185658-b44545bcd369/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190419153524-e8e3143a4f4a/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -2286,7 +2224,6 @@ golang.org/x/sys v0.0.0-20190523142557-0e01d883c5c5/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20190524122548-abf6ff778158/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190524152521-dbbf3f1254d4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190526052359-791d8a0f4d09/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190531175056-4c3a928424d2/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190610200419-93c9922d18ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190616124812-15dcb6c0061f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -2345,7 +2282,6 @@ golang.org/x/sys v0.0.0-20210315160823-c6e025ad8005/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20210317225723-c4fcb01b228e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210331175145-43e1dd70ce54/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210420072515-93ed5bcd2bfe/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210423185535-09eb48e85fd7/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210426080607-c94f62235c83/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -2398,13 +2334,9 @@ golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3 golang.org/x/tools v0.0.0-20190312151545-0bb0c0a6e846/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= golang.org/x/tools v0.0.0-20190312170243-e65039ee4138/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= golang.org/x/tools v0.0.0-20190328211700-ab21143f2384/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= -golang.org/x/tools v0.0.0-20190329151228-23e29df326fe/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= -golang.org/x/tools v0.0.0-20190416151739-9c9e1878f421/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= -golang.org/x/tools v0.0.0-20190420181800-aa740d480789/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= golang.org/x/tools v0.0.0-20190425150028-36563e24a262/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= golang.org/x/tools v0.0.0-20190506145303-2d16b83fe98c/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= -golang.org/x/tools v0.0.0-20190531172133-b3315ee88b7d/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= golang.org/x/tools v0.0.0-20190606124116-d0a3d012864b/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= golang.org/x/tools v0.0.0-20190614205625-5aca471b1d59/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= @@ -2604,7 +2536,6 @@ gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= -gopkg.in/yaml.v3 v3.0.0-20200605160147-a5ece683394c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.0-20200615113413-eeeca48fe776/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= diff --git a/itests/eth_conformance_test.go b/itests/eth_conformance_test.go index 0a399adbc..7cd69bfe6 100644 --- a/itests/eth_conformance_test.go +++ b/itests/eth_conformance_test.go @@ -1,28 +1,27 @@ package itests import ( + "bytes" "context" + "encoding/hex" "encoding/json" - "errors" "os" - "path" "testing" "time" - "github.com/filecoin-project/go-jsonrpc" - "github.com/filecoin-project/lotus/chain/types/ethtypes" - "github.com/filecoin-project/lotus/itests/kit" - oaerrors "github.com/go-openapi/errors" "github.com/go-openapi/spec" - "github.com/go-openapi/strfmt" - "github.com/go-openapi/validate" + "github.com/gregdhill/go-openrpc/parse" + orpctypes "github.com/gregdhill/go-openrpc/types" "github.com/ipfs/go-cid" manet "github.com/multiformats/go-multiaddr/net" "github.com/stretchr/testify/require" + "github.com/xeipuuv/gojsonschema" - "github.com/gregdhill/go-openrpc/parse" - "github.com/gregdhill/go-openrpc/types" - "github.com/gregdhill/go-openrpc/util" + "github.com/filecoin-project/go-address" + "github.com/filecoin-project/go-jsonrpc" + + "github.com/filecoin-project/lotus/chain/types/ethtypes" + "github.com/filecoin-project/lotus/itests/kit" ) // TODO generate this using reflection @@ -48,35 +47,34 @@ type ethAPIRaw struct { EthGetTransactionByBlockNumberAndIndex func(context.Context, ethtypes.EthUint64, ethtypes.EthUint64) (json.RawMessage, error) EthGetTransactionByHash func(context.Context, *ethtypes.EthHash) (json.RawMessage, error) EthGetTransactionCount func(context.Context, ethtypes.EthAddress, string) (json.RawMessage, error) - EthGetTransactionHashByCid func(context.Context, cid.Cid) (json.RawMessage, error) EthGetTransactionReceipt func(context.Context, ethtypes.EthHash) (json.RawMessage, error) EthMaxPriorityFeePerGas func(context.Context) (json.RawMessage, error) EthNewBlockFilter func(context.Context) (json.RawMessage, error) EthNewFilter func(context.Context, *ethtypes.EthFilterSpec) (json.RawMessage, error) EthNewPendingTransactionFilter func(context.Context) (json.RawMessage, error) - EthProtocolVersion func(context.Context) (json.RawMessage, error) EthSendRawTransaction func(context.Context, ethtypes.EthBytes) (json.RawMessage, error) EthSubscribe func(context.Context, string, *ethtypes.EthSubscriptionParams) (json.RawMessage, error) EthUninstallFilter func(context.Context, ethtypes.EthFilterID) (json.RawMessage, error) EthUnsubscribe func(context.Context, ethtypes.EthSubscriptionID) (json.RawMessage, error) } -func TestEthOpenRPC(t *testing.T) { +func TestEthOpenRPCConformance(t *testing.T) { kit.QuietAllLogsExcept("events", "messagepool") + // specs/eth_openrpc.json is built from https://github.com/ethereum/execution-apis specJSON, err := os.ReadFile("specs/eth_openrpc.json") require.NoError(t, err) - specParsed := types.NewOpenRPCSpec1() + specParsed := orpctypes.NewOpenRPCSpec1() err = json.Unmarshal([]byte(specJSON), specParsed) require.NoError(t, err) parse.GetTypes(specParsed, specParsed.Objects) schemas := make(map[string]spec.Schema) for _, method := range specParsed.Methods { + t.Logf("method: %s", method.Name) if method.Result != nil { - schema := resolveSchema(specParsed, method.Result.Schema) - schemas[method.Name] = schema + schemas[method.Name] = method.Result.Schema } } @@ -86,12 +84,29 @@ func TestEthOpenRPC(t *testing.T) { client, _, ens := kit.EnsembleMinimal(t, kit.MockProofs(), kit.ThroughRPC(), kit.WithEthRPC()) ens.InterconnectAll().BeginMining(10 * time.Millisecond) + contractHex, err := os.ReadFile(EventMatrixContract.Filename) + require.NoError(t, err) + + // strip any trailing newlines from the file + contractHex = bytes.TrimRight(contractHex, "\n") + + contractBin, err := hex.DecodeString(string(contractHex)) + require.NoError(t, err) + + senderAddr, err := client.EVM().WalletDefaultAddress(ctx) + require.NoError(t, err) + // senderEthAddr := getContractEthAddress(ctx, t, client, senderAddr) + + result := client.EVM().DeployContract(ctx, senderAddr, contractBin) + + contractAddr, err := address.NewIDAddress(result.ActorID) + require.NoError(t, err) + // send a message that exercises event logs - sender, contract := client.EVM().DeployContractFromFilename(ctx, EventMatrixContract.Filename) messages := invokeAndWaitUntilAllOnChain(t, client, []Invocation{ { - Sender: sender, - Target: contract, + Sender: senderAddr, + Target: contractAddr, Selector: EventMatrixContract.Fn["logEventThreeIndexedWithData"], Data: packUint64Values(44, 27, 19, 12), }, @@ -100,24 +115,49 @@ func TestEthOpenRPC(t *testing.T) { require.NotEmpty(t, messages) var messageWithEvents ethtypes.EthHash - for k := range messages { + var blockHashWithMessage ethtypes.EthHash + var blockNumberWithMessage ethtypes.EthUint64 + + for k, mts := range messages { messageWithEvents = k + ts := mts.ts + blockNumberWithMessage = ethtypes.EthUint64(ts.Height()) + + tsCid, err := ts.Key().Cid() + require.NoError(t, err) + + blockHashWithMessage, err = ethtypes.EthHashFromCid(tsCid) + require.NoError(t, err) break } + // create a json-rpc client that returns raw json responses + var ethapi ethAPIRaw + netAddr, err := manet.ToNetAddr(client.ListenAddr) require.NoError(t, err) rpcAddr := "ws://" + netAddr.String() + "/rpc/v1" - var ethapi ethAPIRaw closer, err := jsonrpc.NewClient(ctx, rpcAddr, "Filecoin", ðapi, nil) require.NoError(t, err) defer closer() + // maxPriorityFeePerGas, err := client.EthMaxPriorityFeePerGas(ctx) + testCases := []struct { - method string - call func(*ethAPIRaw) (json.RawMessage, error) + method string + variant string // suffix applied to the test name to distinguish different variants of a method call + call func(*ethAPIRaw) (json.RawMessage, error) }{ + // Simple no-argument calls first + + { + method: "eth_accounts", + call: func(a *ethAPIRaw) (json.RawMessage, error) { + return ethapi.EthAccounts(context.Background()) + }, + }, + { method: "eth_blockNumber", call: func(a *ethAPIRaw) (json.RawMessage, error) { @@ -125,78 +165,148 @@ func TestEthOpenRPC(t *testing.T) { }, }, + { + method: "eth_chainId", + call: func(a *ethAPIRaw) (json.RawMessage, error) { + return ethapi.EthChainId(context.Background()) + }, + }, + + { + method: "eth_gasPrice", + call: func(a *ethAPIRaw) (json.RawMessage, error) { + return ethapi.EthGasPrice(context.Background()) + }, + }, + + { + method: "eth_maxPriorityFeePerGas", + call: func(a *ethAPIRaw) (json.RawMessage, error) { + return ethapi.EthMaxPriorityFeePerGas(context.Background()) + }, + }, + + { + method: "eth_newBlockFilter", + call: func(a *ethAPIRaw) (json.RawMessage, error) { + return ethapi.EthNewBlockFilter(context.Background()) + }, + }, + + { + method: "eth_newPendingTransactionFilter", + call: func(a *ethAPIRaw) (json.RawMessage, error) { + return ethapi.EthNewPendingTransactionFilter(context.Background()) + }, + }, + { method: "eth_getTransactionReceipt", call: func(a *ethAPIRaw) (json.RawMessage, error) { return ethapi.EthGetTransactionReceipt(context.Background(), messageWithEvents) }, }, + + { + method: "eth_getBlockByHash", + variant: "txhashes", + call: func(a *ethAPIRaw) (json.RawMessage, error) { + return ethapi.EthGetBlockByHash(context.Background(), blockHashWithMessage, false) + }, + }, + + { + method: "eth_getBlockByHash", + variant: "txfull", + call: func(a *ethAPIRaw) (json.RawMessage, error) { + return ethapi.EthGetBlockByHash(context.Background(), blockHashWithMessage, true) + }, + }, + + { + method: "eth_getBlockByNumber", + variant: "earliest", + call: func(a *ethAPIRaw) (json.RawMessage, error) { + return ethapi.EthGetBlockByNumber(context.Background(), "earliest", true) + }, + }, + + { + method: "eth_getBlockByNumber", + variant: "pending", + call: func(a *ethAPIRaw) (json.RawMessage, error) { + return ethapi.EthGetBlockByNumber(context.Background(), "pending", true) + }, + }, + + { + method: "eth_getBlockByNumber", + variant: "latest", + call: func(a *ethAPIRaw) (json.RawMessage, error) { + return ethapi.EthGetBlockByNumber(context.Background(), blockNumberWithMessage.Hex(), true) + }, + }, + + // { + // method: "eth_call", + // call: func(a *ethAPIRaw) (json.RawMessage, error) { + // return ethapi.EthCall(context.Background(), ethtypes.EthCall{ + // From: &senderEthAddr, + // Data: contractBin, + // }, "logEventZeroData") + // }, + // }, + + // { + // method: "eth_estimateGas", + // call: func(a *ethAPIRaw) (json.RawMessage, error) { + // return ethapi.EthEstimateGas(context.Background(), ethtypes.EthCall{ + // From: &senderEthAddr, + // Data: contractBin, + // }) + // }, + // }, } for _, tc := range testCases { - t.Run(tc.method, func(t *testing.T) { + name := tc.method + if tc.variant != "" { + name += "_" + tc.variant + } + t.Run(name, func(t *testing.T) { schema, ok := schemas[tc.method] require.True(t, ok, "method not found in openrpc spec") resp, err := tc.call(ðapi) require.NoError(t, err) - respJson, err := json.MarshalIndent(resp, "", " ") + respJson, err := json.Marshal(resp) require.NoError(t, err) - err = validate.AgainstSchema(&schema, respJson, strfmt.Default) - if err != nil { - t.Logf("response was %s", respJson) + loader := gojsonschema.NewGoLoader(schema) + resploader := gojsonschema.NewBytesLoader(respJson) + result, err := gojsonschema.Validate(loader, resploader) + require.NoError(t, err) - ce := &oaerrors.CompositeError{} - if errors.As(err, &ce) { - for _, e := range ce.Errors { - t.Logf("error: %v", e) - } + if !result.Valid() { + niceRespJson, err := json.MarshalIndent(resp, "", " ") + if err == nil { + t.Logf("response was %s", niceRespJson) } + + schemaJson, err := json.MarshalIndent(schema, "", " ") + if err == nil { + t.Logf("schema was %s", schemaJson) + } + + // check against https://www.jsonschemavalidator.net/ + + for _, desc := range result.Errors() { + t.Logf("- %s\n", desc) + } + + t.Errorf("response did not validate") } - require.NoError(t, err) }) } } - -func resolveSchema(openrpc *types.OpenRPCSpec1, sch spec.Schema) spec.Schema { - doc, _, _ := sch.Ref.GetPointer().Get(openrpc) - - if s, ok := doc.(spec.Schema); ok { - sch = persistFields(sch, s) - } else if cd, ok := doc.(*types.ContentDescriptor); ok { - sch = persistFields(sch, cd.Schema) - } - - for i := range sch.OneOf { - sch.OneOf[i] = resolveSchema(openrpc, sch.OneOf[i]) - } - - for i := range sch.AllOf { - sch.AllOf[i] = resolveSchema(openrpc, sch.AllOf[i]) - } - - for i := range sch.AnyOf { - sch.AnyOf[i] = resolveSchema(openrpc, sch.AnyOf[i]) - } - - if sch.Not != nil { - s := resolveSchema(openrpc, *sch.Not) - sch.Not = &s - } - - if sch.Ref.GetURL() != nil { - return resolveSchema(openrpc, sch) - } - return sch -} - -func persistFields(prev, next spec.Schema) spec.Schema { - next.Title = util.FirstOf(next.Title, prev.Title, path.Base(prev.Ref.String())) - next.Description = util.FirstOf(next.Description, prev.Description) - if next.Items == nil { - next.Items = prev.Items - } - return next -} diff --git a/itests/specs/eth_openrpc.json b/itests/specs/eth_openrpc.json index c9f83d07e..ed13614e9 100644 --- a/itests/specs/eth_openrpc.json +++ b/itests/specs/eth_openrpc.json @@ -1,1786 +1,4392 @@ { - "openrpc": "1.0.0", - "info": { - "version": "1.0.10", - "title": "Ethereum JSON-RPC", - "description": "This API lets you interact with an EVM-based client via JSON-RPC", - "license": { - "name": "Apache 2.0", - "url": "https://www.apache.org/licenses/LICENSE-2.0.html" - } - }, - "methods": [ - { - "name": "web3_clientVersion", - "description": "Returns the version of the current client", - "summary": "current client version", - "params": [], - "result": { - "name": "clientVersion", - "description": "client version", - "schema": { - "title": "clientVersion", - "type": "string" - } - } - }, - { - "name": "web3_sha3", - "summary": "Hashes data", - "description": "Hashes data using the Keccak-256 algorithm", - "params": [ - { - "name": "data", - "description": "data to hash using the Keccak-256 algorithm", - "summary": "data to hash", - "schema": { - "title": "data", - "type": "string", - "pattern": "^0x[a-fA-F\\d]+$" - } - } - ], - "result": { - "name": "hashedData", - "description": "Keccak-256 hash of the given data", - "schema": { - "$ref": "#/components/schemas/Keccak" - } - }, - "examples": [ - { - "name": "sha3Example", - "params": [ - { - "name": "sha3ParamExample", - "value": "0x68656c6c6f20776f726c64" - } - ], - "result": { - "name": "sha3ResultExample", - "value": "0x47173285a8d7341e5e972fc677286384f802f8ef42a5ec5f03bbfa254cb01fad" - } - } - ] - }, - { - "name": "net_listening", - "summary": "returns listening status", - "description": "Determines if this client is listening for new network connections.", - "params": [], - "result": { - "name": "netListeningResult", - "description": "`true` if listening is active or `false` if listening is not active", - "schema": { - "title": "isNetListening", - "type": "boolean" - } - }, - "examples": [ - { - "name": "netListeningTrueExample", - "description": "example of true result for net_listening", - "params": [], - "result": { - "name": "netListeningExampleFalseResult", - "value": true - } - } - ] - }, - { - "name": "net_peerCount", - "summary": "number of peers", - "description": "Returns the number of peers currently connected to this client.", - "params": [], - "result": { - "name": "quantity", - "description": "number of connected peers.", - "schema": { - "title": "numConnectedPeers", - "description": "Hex representation of number of connected peers", - "type": "string" - } - } - }, - { - "name": "net_version", - "summary": "Network identifier associated with network", - "description": "Returns the network ID associated with the current network.", - "params": [], - "result": { - "name": "networkId", - "description": "Network ID associated with the current network", - "schema": { - "title": "networkId", - "type": "string", - "pattern": "^[\\d]+$" - } - } - }, - { - "name": "eth_blockNumber", - "summary": "Returns the number of most recent block.", - "params": [], - "result": { - "$ref": "#/components/contentDescriptors/BlockNumber" - } - }, - { - "name": "eth_call", - "summary": "Executes a new message call (locally) immediately without creating a transaction on the block chain.", - "params": [ - { - "$ref": "#/components/contentDescriptors/Transaction" - }, - { - "$ref": "#/components/contentDescriptors/BlockNumber" - } - ], - "result": { - "name": "returnValue", - "description": "The return value of the executed contract", - "schema": { - "$ref": "#/components/schemas/Bytes" - } - } - }, - { - "name": "eth_chainId", - "summary": "Returns the currently configured chain id", - "description": "Returns the currently configured chain id, a value used in replay-protected transaction signing as introduced by [EIP-155](https://github.com/ethereum/EIPs/blob/master/EIPS/eip-155.md).", - "params": [], - "result": { - "name": "chainId", - "description": "hex format integer of the current chain id. Defaults are ETC=61, ETH=1, Morden=62.", - "schema": { - "title": "chainId", - "type": "string", - "pattern": "^0x[a-fA-F\\d]+$" - } - } - }, - { - "name": "eth_coinbase", - "summary": "Returns the client coinbase address.", - "params": [], - "result": { - "name": "address", - "description": "The address owned by the client that is used as default for things like the mining reward", - "schema": { - "$ref": "#/components/schemas/Address" - } - } - }, - { - "name": "eth_estimateGas", - "summary": "Generates and returns an estimate of how much gas is necessary to allow the transaction to complete. The transaction will not be added to the blockchain. Note that the estimate may be significantly more than the amount of gas actually used by the transaction, for a variety of reasons including EVM mechanics and node performance.", - "params": [ - { - "$ref": "#/components/contentDescriptors/Transaction" - } - ], - "result": { - "name": "gasUsed", - "description": "The amount of gas used", - "schema": { - "$ref": "#/components/schemas/Integer" - } - } - }, - { - "name": "eth_gasPrice", - "summary": "Returns the current price per gas in wei", - "params": [], - "result": { - "$ref": "#/components/contentDescriptors/GasPrice" - } - }, - { - "name": "eth_getBalance", - "summary": "Returns Ether balance of a given or account or contract", - "params": [ - { - "name": "address", - "required": true, - "description": "The address of the account or contract", - "schema": { - "$ref": "#/components/schemas/Address" - } - }, - { - "name": "blockNumber", - "description": "A BlockNumber at which to request the balance", - "schema": { - "$ref": "#/components/schemas/BlockNumber" - } - } - ], - "result": { - "name": "getBalanceResult", - "schema": { - "$ref": "#/components/schemas/IntegerOrNull" - } - } - }, - { - "name": "eth_getBlockByHash", - "summary": "Gets a block for a given hash", - "params": [ - { - "name": "blockHash", - "required": true, - "schema": { - "$ref": "#/components/schemas/BlockHash" - } - }, - { - "name": "includeTransactions", - "description": "If `true` it returns the full transaction objects, if `false` only the hashes of the transactions.", - "required": true, - "schema": { - "title": "isTransactionsIncluded", - "type": "boolean" - } - } - ], - "result": { - "name": "getBlockByHashResult", - "schema": { - "$ref": "#/components/schemas/BlockOrNull" - } - } - }, - { - "name": "eth_getBlockByNumber", - "summary": "Gets a block for a given number", - "params": [ - { - "$ref": "#/components/contentDescriptors/BlockNumber" - }, - { - "name": "includeTransactions", - "description": "If `true` it returns the full transaction objects, if `false` only the hashes of the transactions.", - "required": true, - "schema": { - "title": "isTransactionsIncluded", - "type": "boolean" - } - } - ], - "result": { - "name": "getBlockByNumberResult", - "schema": { - "$ref": "#/components/schemas/BlockOrNull" - } - } - }, - { - "name": "eth_getBlockTransactionCountByHash", - "summary": "Returns the number of transactions in a block from a block matching the given block hash.", - "params": [ - { - "$ref": "#/components/contentDescriptors/BlockHash" - } - ], - "result": { - "name": "blockTransactionCountByHash", - "description": "The Number of total transactions in the given block", - "schema": { - "$ref": "#/components/schemas/IntegerOrNull" - } - } - }, - { - "name": "eth_getBlockTransactionCountByNumber", - "summary": "Returns the number of transactions in a block from a block matching the given block number.", - "params": [ - { - "$ref": "#/components/contentDescriptors/BlockNumber" - } - ], - "result": { - "name": "blockTransactionCountByHash", - "description": "The Number of total transactions in the given block", - "schema": { - "$ref": "#/components/schemas/IntegerOrNull" - } - } - }, - { - "name": "eth_getCode", - "summary": "Returns code at a given contract address", - "params": [ - { - "name": "address", - "required": true, - "description": "The address of the contract", - "schema": { - "$ref": "#/components/schemas/Address" - } - }, - { - "name": "blockNumber", - "description": "A BlockNumber of which the code existed", - "schema": { - "$ref": "#/components/schemas/BlockNumber" - } - } - ], - "result": { - "name": "bytes", - "schema": { - "$ref": "#/components/schemas/Bytes" - } - } - }, - { - "name": "eth_getFilterChanges", - "summary": "Polling method for a filter, which returns an array of logs which occurred since last poll.", - "params": [ - { - "name": "filterId", - "required": true, - "schema": { - "$ref": "#/components/schemas/FilterId" - } - } - ], - "result": { - "name": "logResult", - "schema": { - "title": "logResult", - "type": "array", - "items": { - "$ref": "#/components/schemas/Log" - } - } - } - }, - { - "name": "eth_getFilterLogs", - "summary": "Returns an array of all logs matching filter with given id.", - "params": [ - { - "name": "filterId", - "required": true, - "schema": { - "$ref": "#/components/schemas/FilterId" - } - } - ], - "result": { - "$ref": "#/components/contentDescriptors/Logs" - } - }, - { - "name": "eth_getRawTransactionByHash", - "summary": "Returns raw transaction data of a transaction with the given hash.", - "params": [ - { - "$ref": "#/components/contentDescriptors/TransactionHash" - } - ], - "result": { - "name": "rawTransactionByHash", - "description": "The raw transaction data", - "schema": { - "$ref": "#/components/schemas/Bytes" - } - } - }, - { - "name": "eth_getRawTransactionByBlockHashAndIndex", - "summary": "Returns raw transaction data of a transaction with the block hash and index of which it was mined.", - "params": [ - { - "$ref": "#/components/contentDescriptors/BlockHash" - }, - { - "name": "index", - "description": "The ordering in which a transaction is mined within its block.", - "required": true, - "schema": { - "$ref": "#/components/schemas/Integer" - } - } - ], - "result": { - "name": "rawTransaction", - "description": "The raw transaction data", - "schema": { - "$ref": "#/components/schemas/Bytes" - } - } - }, - { - "name": "eth_getRawTransactionByBlockNumberAndIndex", - "summary": "Returns raw transaction data of a transaction with the block number and index of which it was mined.", - "params": [ - { - "$ref": "#/components/contentDescriptors/BlockNumber" - }, - { - "name": "index", - "description": "The ordering in which a transaction is mined within its block.", - "required": true, - "schema": { - "$ref": "#/components/schemas/Integer" - } - } - ], - "result": { - "name": "rawTransaction", - "description": "The raw transaction data", - "schema": { - "$ref": "#/components/schemas/Bytes" - } - } - }, - { - "name": "eth_getLogs", - "summary": "Returns an array of all logs matching a given filter object.", - "params": [ - { - "$ref": "#/components/contentDescriptors/Filter" - } - ], - "result": { - "$ref": "#/components/contentDescriptors/Logs" - } - }, - { - "name": "eth_getStorageAt", - "summary": "Gets a storage value from a contract address, a position, and an optional blockNumber", - "params": [ - { - "$ref": "#/components/contentDescriptors/Address" - }, - { - "$ref": "#/components/contentDescriptors/Position" - }, - { - "$ref": "#/components/contentDescriptors/BlockNumber" - } - ], - "result": { - "name": "dataWord", - "schema": { - "$ref": "#/components/schemas/DataWord" - } - } - }, - { - "name": "eth_getTransactionByBlockHashAndIndex", - "summary": "Returns the information about a transaction requested by the block hash and index of which it was mined.", - "params": [ - { - "$ref": "#/components/contentDescriptors/BlockHash" - }, - { - "name": "index", - "description": "The ordering in which a transaction is mined within its block.", - "required": true, - "schema": { - "$ref": "#/components/schemas/Integer" - } - } - ], - "result": { - "$ref": "#/components/contentDescriptors/TransactionResult" - }, - "examples": [ - { - "name": "nullExample", - "params": [ - { - "name": "blockHashExample", - "value": "0x1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef" - }, - { - "name": "indexExample", - "value": "0x0" - } - ], - "result": { - "name": "nullResultExample", - "value": null - } - } - ] - }, - { - "name": "eth_getTransactionByBlockNumberAndIndex", - "summary": "Returns the information about a transaction requested by the block number and index of which it was mined.", - "params": [ - { - "$ref": "#/components/contentDescriptors/BlockNumber" - }, - { - "name": "index", - "description": "The ordering in which a transaction is mined within its block.", - "required": true, - "schema": { - "$ref": "#/components/schemas/Integer" - } - } - ], - "result": { - "$ref": "#/components/contentDescriptors/TransactionResult" - } - }, - { - "name": "eth_getTransactionByHash", - "summary": "Returns the information about a transaction requested by transaction hash.", - "params": [ - { - "$ref": "#/components/contentDescriptors/TransactionHash" - } - ], - "result": { - "$ref": "#/components/contentDescriptors/TransactionResult" - } - }, - { - "name": "eth_getTransactionCount", - "summary": "Returns the number of transactions sent from an address", - "params": [ - { - "$ref": "#/components/contentDescriptors/Address" - }, - { - "$ref": "#/components/contentDescriptors/BlockNumber" - } - ], - "result": { - "name": "transactionCount", - "schema": { - "title": "nonceOrNull", - "oneOf": [ - { - "$ref": "#/components/schemas/Nonce" - }, - { - "$ref": "#/components/schemas/Null" - } - ] - } - } - }, - { - "name": "eth_getTransactionReceipt", - "summary": "Returns the receipt information of a transaction by its hash.", - "params": [ - { - "$ref": "#/components/contentDescriptors/TransactionHash" - } - ], - "result": { - "name": "transactionReceiptResult", - "description": "returns either a receipt or null", - "schema": { - "title": "transactionReceiptOrNull", - "oneOf": [ - { - "$ref": "#/components/schemas/Receipt" - }, - { - "$ref": "#/components/schemas/Null" - } - ] - } - } - }, - { - "name": "eth_getUncleByBlockHashAndIndex", - "summary": "Returns information about a uncle of a block by hash and uncle index position.", - "params": [ - { - "$ref": "#/components/contentDescriptors/BlockHash" - }, - { - "name": "index", - "description": "The ordering in which a uncle is included within its block.", - "required": true, - "schema": { - "$ref": "#/components/schemas/Integer" - } - } - ], - "result": { - "name": "uncle", - "schema": { - "$ref": "#/components/schemas/BlockOrNull" - } - } - }, - { - "name": "eth_getUncleByBlockNumberAndIndex", - "summary": "Returns information about a uncle of a block by hash and uncle index position.", - "params": [ - { - "name": "uncleBlockNumber", - "description": "The block in which the uncle was included", - "required": true, - "schema": { - "$ref": "#/components/schemas/BlockNumber" - } - }, - { - "name": "index", - "description": "The ordering in which a uncle is included within its block.", - "required": true, - "schema": { - "$ref": "#/components/schemas/Integer" - } - } - ], - "result": { - "name": "uncleResult", - "description": "returns an uncle block or null", - "schema": { - "$ref": "#/components/schemas/BlockOrNull" - } - }, - "examples": [ - { - "name": "nullResultExample", - "params": [ - { - "name": "uncleBlockNumberExample", - "value": "0x0" - }, - { - "name": "uncleBlockNumberIndexExample", - "value": "0x0" - } - ], - "result": { - "name": "nullResultExample", - "value": null - } - } - ] - }, - { - "name": "eth_getUncleCountByBlockHash", - "summary": "Returns the number of uncles in a block from a block matching the given block hash.", - "params": [ - { - "$ref": "#/components/contentDescriptors/BlockHash" - } - ], - "result": { - "name": "uncleCountResult", - "description": "The Number of total uncles in the given block", - "schema": { - "$ref": "#/components/schemas/IntegerOrNull" - } - } - }, - { - "name": "eth_getUncleCountByBlockNumber", - "summary": "Returns the number of uncles in a block from a block matching the given block number.", - "params": [ - { - "$ref": "#/components/contentDescriptors/BlockNumber" - } - ], - "result": { - "$ref": "#/components/contentDescriptors/UncleCountResult" - } - }, - { - "name": "eth_getProof", - "summary": "Returns the account- and storage-values of the specified account including the Merkle-proof.", - "params": [ - { - "name": "address", - "description": "The address of the account or contract", - "required": true, - "schema": { - "$ref": "#/components/schemas/Address" - } - }, - { - "name": "storageKeys", - "required": true, - "schema": { - "title": "storageKeys", - "description": "A storage key is indexed from the solidity compiler by the order it is declared. For mappings it uses the keccak of the mapping key with its position (and recursively for X-dimensional mappings)", - "items": { - "$ref": "#/components/schemas/StorageProofKey" - } - } - }, - { - "$ref": "#/components/contentDescriptors/BlockNumber" - } - ], - "result": { - "name": "account", - "schema": { - "title": "proofAccountOrNull", - "oneOf": [ - { - "title": "proofAccount", - "type": "object", - "description": "The merkle proofs of the specified account connecting them to the blockhash of the block specified", - "properties": { - "address": { - "title": "proofAccountAddress", - "description": "The address of the account or contract of the request", - "$ref": "#/components/schemas/Address" - }, - "accountProof": { - "$ref": "#/components/schemas/ProofNodes" - }, - "balance": { - "title": "proofAccountBalance", - "description": "The Ether balance of the account or contract of the request", - "$ref": "#/components/schemas/Integer" - }, - "codeHash": { - "title": "proofAccountCodeHash", - "description": "The code hash of the contract of the request (keccak(NULL) if external account)", - "$ref": "#/components/schemas/Keccak" - }, - "nonce": { - "title": "proofAccountNonce", - "description": "The transaction count of the account or contract of the request", - "$ref": "#/components/schemas/Nonce" - }, - "storageHash": { - "title": "proofAccountStorageHash", - "description": "The storage hash of the contract of the request (keccak(rlp(NULL)) if external account)", - "$ref": "#/components/schemas/Keccak" - }, - "storageProof": { - "$ref": "#/components/schemas/StorageProof" - } - } - }, - { - "$ref": "#/components/schemas/Null" - } - ] - } - } - }, - { - "name": "eth_getWork", - "summary": "Returns the hash of the current block, the seedHash, and the boundary condition to be met ('target').", - "params": [], - "result": { - "name": "work", - "schema": { - "title": "getWorkResults", - "type": "array", - "items": [ - { - "$ref": "#/components/schemas/PowHash" - }, - { - "$ref": "#/components/schemas/SeedHash" - }, - { - "$ref": "#/components/schemas/Difficulty" - } - ] - } - } - }, - { - "name": "eth_hashrate", - "summary": "Returns the number of hashes per second that the node is mining with.", - "params": [], - "result": { - "name": "hashesPerSecond", - "description": "Integer of the number of hashes per second", - "schema": { - "$ref": "#/components/schemas/Integer" - } - } - }, - { - "name": "eth_mining", - "summary": "Returns true if client is actively mining new blocks.", - "params": [], - "result": { - "name": "mining", - "description": "Whether or not the client is mining", - "schema": { - "type": "boolean" - } - } - }, - { - "name": "eth_newBlockFilter", - "summary": "Creates a filter in the node, to notify when a new block arrives. To check if the state has changed, call eth_getFilterChanges.", - "params": [], - "result": { - "$ref": "#/components/contentDescriptors/FilterId" - } - }, - { - "name": "eth_newFilter", - "summary": "Creates a filter object, based on filter options, to notify when the state changes (logs). To check if the state has changed, call eth_getFilterChanges.", - "params": [ - { - "$ref": "#/components/contentDescriptors/Filter" - } - ], - "result": { - "name": "filterId", - "description": "The filter ID for use in `eth_getFilterChanges`", - "schema": { - "$ref": "#/components/schemas/Integer" - } - } - }, - { - "name": "eth_newPendingTransactionFilter", - "summary": "Creates a filter in the node, to notify when new pending transactions arrive. To check if the state has changed, call eth_getFilterChanges.", - "params": [], - "result": { - "$ref": "#/components/contentDescriptors/FilterId" - } - }, - { - "name": "eth_pendingTransactions", - "summary": "Returns the transactions that are pending in the transaction pool and have a from address that is one of the accounts this node manages.", - "params": [], - "result": { - "name": "pendingTransactions", - "schema": { - "$ref": "#/components/schemas/Transactions" - } - } - }, - { - "name": "eth_protocolVersion", - "summary": "Returns the current ethereum protocol version.", - "params": [], - "result": { - "name": "protocolVersion", - "description": "The current ethereum protocol version", - "schema": { - "$ref": "#/components/schemas/Integer" - } - } - }, - { - "name": "eth_sendRawTransaction", - "summary": "Creates new message call transaction or a contract creation for signed transactions.", - "params": [ - { - "name": "signedTransactionData", - "required": true, - "description": "The signed transaction data", - "schema": { - "$ref": "#/components/schemas/Bytes" - } - } - ], - "result": { - "name": "transactionHash", - "description": "The transaction hash, or the zero hash if the transaction is not yet available.", - "schema": { - "$ref": "#/components/schemas/Keccak" - } - } - }, - { - "name": "eth_submitHashrate", - "deprecated": true, - "summary": "Used for submitting mining hashrate.", - "params": [ - { - "name": "hashRate", - "required": true, - "schema": { - "$ref": "#/components/schemas/DataWord" - } - }, - { - "name": "id", - "required": true, - "description": "String identifying the client", - "schema": { - "$ref": "#/components/schemas/DataWord" - } - } - ], - "result": { - "name": "submitHashRateSuccess", - "description": "whether of not submitting went through successfully", - "schema": { - "type": "boolean" - } - } - }, - { - "name": "eth_submitWork", - "summary": "Used for submitting a proof-of-work solution.", - "params": [ - { - "$ref": "#/components/contentDescriptors/Nonce" - }, - { - "name": "powHash", - "required": true, - "schema": { - "$ref": "#/components/schemas/PowHash" - } - }, - { - "name": "mixHash", - "required": true, - "schema": { - "$ref": "#/components/schemas/MixHash" - } - } - ], - "result": { - "name": "solutionValid", - "description": "returns true if the provided solution is valid, otherwise false.", - "schema": { - "type": "boolean" - } - }, - "examples": [ - { - "name": "submitWorkExample", - "params": [ - { - "name": "nonceExample", - "description": "example of a number only used once", - "value": "0x0000000000000001" - }, - { - "name": "powHashExample", - "description": "proof of work to submit", - "value": "0x6bf2cAE0dE3ec3ecA5E194a6C6e02cf42aADfe1C2c4Fff12E5D36C3Cf7297F22" - }, - { - "name": "mixHashExample", - "description": "the mix digest example", - "value": "0xD1FE5700000000000000000000000000D1FE5700000000000000000000000000" - } - ], - "result": { - "name": "solutionInvalidExample", - "description": "this example should return `false` as it is not a valid pow to submit", - "value": false - } - } - ] - }, - { - "name": "eth_syncing", - "summary": "Returns an object with data about the sync status or false.", - "params": [], - "result": { - "name": "syncing", - "schema": { - "title": "isSyncingResult", - "oneOf": [ - { - "title": "syncingData", - "description": "An object with sync status data", - "type": "object", - "properties": { - "startingBlock": { - "title": "syncingDataStartingBlock", - "description": "Block at which the import started (will only be reset, after the sync reached his head)", - "$ref": "#/components/schemas/Integer" - }, - "currentBlock": { - "title": "syncingDataCurrentBlock", - "description": "The current block, same as eth_blockNumber", - "$ref": "#/components/schemas/Integer" - }, - "highestBlock": { - "title": "syncingDataHighestBlock", - "description": "The estimated highest block", - "$ref": "#/components/schemas/Integer" - }, - "knownStates": { - "title": "syncingDataKnownStates", - "description": "The known states", - "$ref": "#/components/schemas/Integer" - }, - "pulledStates": { - "title": "syncingDataPulledStates", - "description": "The pulled states", - "$ref": "#/components/schemas/Integer" - } - } - }, - { - "type": "boolean" - } - ] - } - } - }, - { - "name": "eth_uninstallFilter", - "summary": "Uninstalls a filter with given id. Should always be called when watch is no longer needed. Additionally Filters timeout when they aren't requested with eth_getFilterChanges for a period of time.", - "params": [ - { - "name": "filterId", - "required": true, - "schema": { - "$ref": "#/components/schemas/FilterId" - } - } - ], - "result": { - "name": "filterUninstalledSuccess", - "description": "returns true if the filter was successfully uninstalled, false otherwise.", - "schema": { - "type": "boolean" - } - } - } - ], - "components": { - "schemas": { - "ProofNode": { - "title": "proofNode", - "description": "An individual node used to prove a path down a merkle-patricia-tree", - "$ref": "#/components/schemas/Bytes" - }, - "StorageProofKey": { - "title": "storageProofKey", - "description": "The key used to get the storage slot in its account tree.", - "$ref": "#/components/schemas/Integer" - }, - "StorageProof": { - "title": "storageProofSet", - "type": "array", - "description": "Current block header PoW hash.", - "items": { - "title": "storageProof", - "type": "object", - "description": "Object proving a relationship of a storage value to an account's storageHash.", - "properties": { - "key": { - "$ref": "#/components/schemas/StorageProofKey" - }, - "value": { - "title": "storageProofValue", - "description": "The value of the storage slot in its account tree", - "$ref": "#/components/schemas/Integer" - }, - "proof": { - "$ref": "#/components/schemas/ProofNodes" - } - } - } - }, - "ProofNodes": { - "title": "proofNodes", - "type": "array", - "description": "The set of node values needed to traverse a patricia merkle tree (from root to leaf) to retrieve a value", - "items": { - "$ref": "#/components/schemas/ProofNode" - } - }, - "PowHash": { - "title": "powHash", - "description": "Current block header PoW hash.", - "$ref": "#/components/schemas/DataWord" - }, - "SeedHash": { - "title": "seedHash", - "description": "The seed hash used for the DAG.", - "$ref": "#/components/schemas/DataWord" - }, - "MixHash": { - "title": "mixHash", - "description": "The mix digest.", - "$ref": "#/components/schemas/DataWord" - }, - "Difficulty": { - "title": "difficulty", - "description": "The boundary condition ('target'), 2^256 / difficulty.", - "$ref": "#/components/schemas/DataWord" - }, - "FilterId": { - "title": "filterId", - "type": "string", - "description": "An identifier used to reference the filter." - }, - "BlockHash": { - "title": "blockHash", - "type": "string", - "pattern": "^0x[a-fA-F\\d]{64}$", - "description": "The hex representation of the Keccak 256 of the RLP encoded block" - }, - "BlockNumber": { - "title": "blockNumber", - "type": "string", - "description": "The hex representation of the block's height", - "$ref": "#/components/schemas/Integer" - }, - "BlockNumberTag": { - "title": "blockNumberTag", - "type": "string", - "description": "The optional block height description", - "enum": [ - "earliest", - "latest", - "pending" - ] - }, - "BlockOrNull": { - "title": "blockOrNull", - "oneOf": [ - { - "$ref": "#/components/schemas/Block" - }, - { - "$ref": "#/components/schemas/Null" - } - ] - }, - "IntegerOrNull": { - "title": "integerOrNull", - "oneOf": [ - { - "$ref": "#/components/schemas/Integer" - }, - { - "$ref": "#/components/schemas/Null" - } - ] - }, - "AddressOrNull": { - "title": "addressOrNull", - "oneOf": [ - { - "$ref": "#/components/schemas/Address" - }, - { - "$ref": "#/components/schemas/Null" - } - ] - }, - "Receipt": { - "title": "receipt", - "type": "object", - "description": "The receipt of a transaction", - "required": [ - "blockHash", - "blockNumber", - "contractAddress", - "cumulativeGasUsed", - "from", - "gasUsed", - "logs", - "logsBloom", - "to", - "transactionHash", - "transactionIndex" - ], - "properties": { - "blockHash": { - "$ref": "#/components/schemas/BlockHash" - }, - "blockNumber": { - "$ref": "#/components/schemas/BlockNumber" - }, - "contractAddress": { - "title": "ReceiptContractAddress", - "description": "The contract address created, if the transaction was a contract creation, otherwise null", - "$ref": "#/components/schemas/AddressOrNull" - }, - "cumulativeGasUsed": { - "title": "ReceiptCumulativeGasUsed", - "description": "The gas units used by the transaction", - "$ref": "#/components/schemas/Integer" - }, - "from": { - "$ref": "#/components/schemas/From" - }, - "gasUsed": { - "title": "ReceiptGasUsed", - "description": "The total gas used by the transaction", - "$ref": "#/components/schemas/Integer" - }, - "logs": { - "title": "logs", - "type": "array", - "description": "An array of all the logs triggered during the transaction", - "items": { - "$ref": "#/components/schemas/Log" - } - }, - "logsBloom": { - "$ref": "#/components/schemas/BloomFilter" - }, - "to": { - "$ref": "#/components/schemas/To" - }, - "transactionHash": { - "$ref": "#/components/schemas/TransactionHash" - }, - "transactionIndex": { - "$ref": "#/components/schemas/TransactionIndex" - }, - "postTransactionState": { - "title": "ReceiptPostTransactionState", - "description": "The intermediate stateRoot directly after transaction execution.", - "$ref": "#/components/schemas/Keccak" - }, - "status": { - "title": "ReceiptStatus", - "description": "Whether or not the transaction threw an error.", - "type": "boolean" - } - } - }, - "BloomFilter": { - "title": "bloomFilter", - "type": "string", - "description": "A 2048 bit bloom filter from the logs of the transaction. Each log sets 3 bits though taking the low-order 11 bits of each of the first three pairs of bytes in a Keccak 256 hash of the log's byte series" - }, - "Log": { - "title": "log", - "type": "object", - "description": "An indexed event generated during a transaction", - "properties": { - "address": { - "title": "LogAddress", - "description": "Sender of the transaction", - "$ref": "#/components/schemas/Address" - }, - "blockHash": { - "$ref": "#/components/schemas/BlockHash" - }, - "blockNumber": { - "$ref": "#/components/schemas/BlockNumber" - }, - "data": { - "title": "LogData", - "description": "The data/input string sent along with the transaction", - "$ref": "#/components/schemas/Bytes" - }, - "logIndex": { - "title": "LogIndex", - "description": "The index of the event within its transaction, null when its pending", - "$ref": "#/components/schemas/Integer" - }, - "removed": { - "title": "logIsRemoved", - "description": "Whether or not the log was orphaned off the main chain", - "type": "boolean" - }, - "topics": { - "$ref": "#/components/schemas/Topics" - }, - "transactionHash": { - "$ref": "#/components/schemas/TransactionHash" - }, - "transactionIndex": { - "$ref": "#/components/schemas/TransactionIndex" - } - } - }, - "Topics": { - "title": "LogTopics", - "description": "Topics are order-dependent. Each topic can also be an array of DATA with 'or' options.", - "type": "array", - "items": { - "$ref": "#/components/schemas/Topic" - } - }, - "Topic": { - "title": "topic", - "description": "32 Bytes DATA of indexed log arguments. (In solidity: The first topic is the hash of the signature of the event (e.g. Deposit(address,bytes32,uint256))", - "$ref": "#/components/schemas/DataWord" - }, - "TransactionIndex": { - "title": "transactionIndex", - "description": "The index of the transaction. null when its pending", - "$ref": "#/components/schemas/IntegerOrNull" - }, - "BlockNumberOrNull": { - "title": "blockNumberOrNull", - "description": "The block number or null when its the pending block", - "oneOf": [ - { - "$ref": "#/components/schemas/BlockNumber" - }, - { - "$ref": "#/components/schemas/Null" - } - ] - }, - "BlockHashOrNull": { - "title": "blockHashOrNull", - "description": "The block hash or null when its the pending block", - "$ref": "#/components/schemas/KeccakOrPending" - }, - "NonceOrNull": { - "title": "nonceOrNull", - "description": "Randomly selected number to satisfy the proof-of-work or null when its the pending block", - "oneOf": [ - { - "$ref": "#/components/schemas/Nonce" - }, - { - "$ref": "#/components/schemas/Null" - } - ] - }, - "From": { - "title": "From", - "description": "The sender of the transaction", - "$ref": "#/components/schemas/Address" - }, - "To": { - "title": "To", - "description": "Destination address of the transaction. Null if it was a contract create.", - "oneOf": [ - { - "$ref": "#/components/schemas/Address" - }, - { - "$ref": "#/components/schemas/Null" - } - ] - }, - "Block": { - "title": "Block", - "description": "The Block is the collection of relevant pieces of information (known as the block header), together with information corresponding to the comprised transactions, and a set of other block headers that are known to have a parent equal to the present block’s parent’s parent.", - "type": "object", - "properties": { - "number": { - "$ref": "#/components/schemas/BlockNumberOrNull" - }, - "hash": { - "$ref": "#/components/schemas/BlockHashOrNull" - }, - "parentHash": { - "$ref": "#/components/schemas/BlockHash" - }, - "nonce": { - "$ref": "#/components/schemas/NonceOrNull" - }, - "sha3Uncles": { - "title": "blockShaUncles", - "description": "Keccak hash of the uncles data in the block", - "$ref": "#/components/schemas/Keccak" - }, - "logsBloom": { - "title": "blockLogsBloom", - "type": "string", - "description": "The bloom filter for the logs of the block or null when its the pending block", - "pattern": "^0x[a-fA-F\\d]+$" - }, - "transactionsRoot": { - "title": "blockTransactionsRoot", - "description": "The root of the transactions trie of the block.", - "$ref": "#/components/schemas/Keccak" - }, - "stateRoot": { - "title": "blockStateRoot", - "description": "The root of the final state trie of the block", - "$ref": "#/components/schemas/Keccak" - }, - "receiptsRoot": { - "title": "blockReceiptsRoot", - "description": "The root of the receipts trie of the block", - "$ref": "#/components/schemas/Keccak" - }, - "miner": { - "$ref": "#/components/schemas/AddressOrNull" - }, - "difficulty": { - "title": "blockDifficulty", - "type": "string", - "description": "Integer of the difficulty for this block" - }, - "totalDifficulty": { - "title": "blockTotalDifficulty", - "description": "Integer of the total difficulty of the chain until this block", - "$ref": "#/components/schemas/IntegerOrNull" - }, - "extraData": { - "title": "blockExtraData", - "type": "string", - "description": "The 'extra data' field of this block" - }, - "size": { - "title": "blockSize", - "type": "string", - "description": "Integer the size of this block in bytes" - }, - "gasLimit": { - "title": "blockGasLimit", - "type": "string", - "description": "The maximum gas allowed in this block" - }, - "gasUsed": { - "title": "blockGasUsed", - "type": "string", - "description": "The total used gas by all transactions in this block" - }, - "timestamp": { - "title": "blockTimeStamp", - "type": "string", - "description": "The unix timestamp for when the block was collated" - }, - "transactions": { - "title": "transactionsOrHashes", - "description": "Array of transaction objects, or 32 Bytes transaction hashes depending on the last given parameter", - "type": "array", - "items": { - "title": "transactionOrTransactionHash", - "oneOf": [ - { - "$ref": "#/components/schemas/Transaction" - }, - { - "$ref": "#/components/schemas/TransactionHash" - } - ] - } - }, - "uncles": { - "title": "uncleHashes", - "description": "Array of uncle hashes", - "type": "array", - "items": { - "title": "uncleHash", - "description": "Block hash of the RLP encoding of an uncle block", - "$ref": "#/components/schemas/Keccak" - } - } - } - }, - "Transaction": { - "title": "transaction", - "type": "object", - "required": [ - "gas", - "gasPrice", - "nonce" - ], - "properties": { - "blockHash": { - "$ref": "#/components/schemas/BlockHashOrNull" - }, - "blockNumber": { - "$ref": "#/components/schemas/BlockNumberOrNull" - }, - "from": { - "$ref": "#/components/schemas/From" - }, - "gas": { - "title": "transactionGas", - "type": "string", - "description": "The gas limit provided by the sender in Wei" - }, - "gasPrice": { - "title": "transactionGasPrice", - "type": "string", - "description": "The gas price willing to be paid by the sender in Wei" - }, - "hash": { - "$ref": "#/components/schemas/TransactionHash" - }, - "input": { - "title": "transactionInput", - "type": "string", - "description": "The data field sent with the transaction" - }, - "nonce": { - "title": "transactionNonce", - "description": "The total number of prior transactions made by the sender", - "$ref": "#/components/schemas/Nonce" - }, - "to": { - "$ref": "#/components/schemas/To" - }, - "transactionIndex": { - "$ref": "#/components/schemas/TransactionIndex" - }, - "value": { - "title": "transactionValue", - "description": "Value of Ether being transferred in Wei", - "$ref": "#/components/schemas/Keccak" - }, - "v": { - "title": "transactionSigV", - "type": "string", - "description": "ECDSA recovery id" - }, - "r": { - "title": "transactionSigR", - "type": "string", - "description": "ECDSA signature r" - }, - "s": { - "title": "transactionSigS", - "type": "string", - "description": "ECDSA signature s" - } - } - }, - "Transactions": { - "title": "transactions", - "description": "An array of transactions", - "type": "array", - "items": { - "$ref": "#/components/schemas/Transaction" - } - }, - "TransactionHash": { - "title": "transactionHash", - "type": "string", - "description": "Keccak 256 Hash of the RLP encoding of a transaction", - "$ref": "#/components/schemas/Keccak" - }, - "KeccakOrPending": { - "title": "keccakOrPending", - "oneOf": [ - { - "$ref": "#/components/schemas/Keccak" - }, - { - "$ref": "#/components/schemas/Null" - } - ] - }, - "Keccak": { - "title": "keccak", - "type": "string", - "description": "Hex representation of a Keccak 256 hash", - "pattern": "^0x[a-fA-F\\d]{64}$" - }, - "Nonce": { - "title": "nonce", - "description": "A number only to be used once", - "$ref": "#/components/schemas/Integer" - }, - "Null": { - "title": "null", - "type": "null", - "description": "Null" - }, - "Integer": { - "title": "integer", - "type": "string", - "pattern": "^0x[a-fA-F0-9]+$", - "description": "Hex representation of the integer" - }, - "Address": { - "title": "address", - "type": "string", - "pattern": "^0x[a-fA-F\\d]{40}$" - }, - "Addresses": { - "title": "addresses", - "type": "array", - "description": "List of contract addresses from which to monitor events", - "items": { - "$ref": "#/components/schemas/Address" - } - }, - "Position": { - "title": "position", - "type": "string", - "description": "Hex representation of the storage slot where the variable exists", - "pattern": "^0x([a-fA-F0-9]?)+$" - }, - "DataWord": { - "title": "dataWord", - "type": "string", - "description": "Hex representation of a 256 bit unit of data", - "pattern": "^0x([a-fA-F\\d]{64})?$" - }, - "Bytes": { - "title": "bytes", - "type": "string", - "description": "Hex representation of a variable length byte array", - "pattern": "^0x([a-fA-F0-9]?)+$" - } - }, - "contentDescriptors": { - "Block": { - "name": "block", - "summary": "A block", - "description": "A block object", - "schema": { - "$ref": "#/components/schemas/Block" - } - }, - "Null": { - "name": "Null", - "description": "JSON Null value", - "summary": "Null value", - "schema": { - "$ref": "#/components/schemas/Null" - } - }, - "Signature": { - "name": "signature", - "summary": "The signature.", - "required": true, - "schema": { - "title": "signatureBytes", - "type": "string", - "description": "Hex representation of byte array between 2 and 65 chars long", - "pattern": "0x^([A-Fa-f0-9]{2}){65}$" - } - }, - "GasPrice": { - "name": "gasPrice", - "required": true, - "schema": { - "title": "gasPriceResult", - "description": "Integer of the current gas price", - "$ref": "#/components/schemas/Integer" - } - }, - "Transaction": { - "required": true, - "name": "transaction", - "schema": { - "$ref": "#/components/schemas/Transaction" - } - }, - "TransactionResult": { - "name": "transactionResult", - "description": "Returns a transaction or null", - "schema": { - "title": "TransactionOrNull", - "oneOf": [ - { - "$ref": "#/components/schemas/Transaction" - }, - { - "$ref": "#/components/schemas/Null" - } - ] - } - }, - "UncleCountResult": { - "name": "uncleCountResult", - "description": "The Number of total uncles in the given block", - "schema": { - "$ref": "#/components/schemas/IntegerOrNull" - } - }, - "Message": { - "name": "message", - "required": true, - "schema": { - "$ref": "#/components/schemas/Bytes" - } - }, - "Filter": { - "name": "filter", - "required": true, - "schema": { - "title": "filter", - "type": "object", - "description": "A filter used to monitor the blockchain for log/events", - "properties": { - "fromBlock": { - "$ref": "#/components/schemas/BlockNumber" - }, - "toBlock": { - "$ref": "#/components/schemas/BlockNumber" - }, - "address": { - "title": "oneOrArrayOfAddresses", - "oneOf": [ - { - "$ref": "#/components/schemas/Address" - }, - { - "$ref": "#/components/schemas/Addresses" - } - ] - }, - "topics": { - "$ref": "#/components/schemas/Topics" - } - } - } - }, - "Address": { - "name": "address", - "required": true, - "schema": { - "$ref": "#/components/schemas/Address" - } - }, - "BlockHash": { - "name": "blockHash", - "required": true, - "schema": { - "$ref": "#/components/schemas/BlockHash" - } - }, - "Nonce": { - "name": "nonce", - "required": true, - "schema": { - "$ref": "#/components/schemas/Nonce" - } - }, - "Position": { - "name": "key", - "required": true, - "schema": { - "$ref": "#/components/schemas/Position" - } - }, - "Logs": { - "name": "logs", - "description": "An array of all logs matching filter with given id.", - "schema": { - "title": "setOfLogs", - "type": "array", - "items": { - "$ref": "#/components/schemas/Log" - } - } - }, - "FilterId": { - "name": "filterId", - "schema": { - "$ref": "#/components/schemas/FilterId" - } - }, - "BlockNumber": { - "name": "blockNumber", - "required": true, - "schema": { - "title": "blockNumberOrTag", - "oneOf": [ - { - "$ref": "#/components/schemas/BlockNumber" - }, - { - "$ref": "#/components/schemas/BlockNumberTag" - } - ] - } - }, - "TransactionHash": { - "name": "transactionHash", - "required": true, - "schema": { - "$ref": "#/components/schemas/TransactionHash" - } - } - } - } + "openrpc": "1.2.4", + "info": { + "title": "Ethereum JSON-RPC Specification", + "description": "A specification of the standard interface for Ethereum clients.", + "license": { + "name": "CC0-1.0", + "url": "https://creativecommons.org/publicdomain/zero/1.0/legalcode" + }, + "version": "0.0.0" + }, + "methods": [ + { + "name": "eth_getBlockByHash", + "summary": "Returns information about a block by hash.", + "params": [ + { + "name": "Block hash", + "required": true, + "schema": { + "title": "32 byte hex value", + "type": "string", + "pattern": "^0x[0-9a-f]{64}$" + } + }, + { + "name": "Hydrated transactions", + "required": true, + "schema": { + "title": "hydrated", + "type": "boolean" + } + } + ], + "result": { + "name": "Block information", + "schema": { + "title": "Block object", + "type": "object", + "required": [ + "parentHash", + "sha3Uncles", + "miner", + "stateRoot", + "transactionsRoot", + "receiptsRoot", + "logsBloom", + "number", + "gasLimit", + "gasUsed", + "timestamp", + "extraData", + "mixHash", + "nonce", + "size", + "transactions", + "uncles" + ], + "properties": { + "parentHash": { + "title": "Parent block hash", + "type": "string", + "pattern": "^0x[0-9a-f]{64}$" + }, + "sha3Uncles": { + "title": "Ommers hash", + "type": "string", + "pattern": "^0x[0-9a-f]{64}$" + }, + "miner": { + "title": "Coinbase", + "type": "string", + "pattern": "^0x[0-9,a-f,A-F]{40}$" + }, + "stateRoot": { + "title": "State root", + "type": "string", + "pattern": "^0x[0-9a-f]{64}$" + }, + "transactionsRoot": { + "title": "Transactions root", + "type": "string", + "pattern": "^0x[0-9a-f]{64}$" + }, + "receiptsRoot": { + "title": "Receipts root", + "type": "string", + "pattern": "^0x[0-9a-f]{64}$" + }, + "logsBloom": { + "title": "Bloom filter", + "type": "string", + "pattern": "^0x[0-9a-f]{512}$" + }, + "difficulty": { + "title": "Difficulty", + "type": "string", + "pattern": "^0x[0-9a-f]*$" + }, + "number": { + "title": "Number", + "type": "string", + "pattern": "^0x([1-9a-f]+[0-9a-f]*|0)$" + }, + "gasLimit": { + "title": "Gas limit", + "type": "string", + "pattern": "^0x([1-9a-f]+[0-9a-f]*|0)$" + }, + "gasUsed": { + "title": "Gas used", + "type": "string", + "pattern": "^0x([1-9a-f]+[0-9a-f]*|0)$" + }, + "timestamp": { + "title": "Timestamp", + "type": "string", + "pattern": "^0x([1-9a-f]+[0-9a-f]*|0)$" + }, + "extraData": { + "title": "Extra data", + "type": "string", + "pattern": "^0x[0-9a-f]*$" + }, + "mixHash": { + "title": "Mix hash", + "type": "string", + "pattern": "^0x[0-9a-f]{64}$" + }, + "nonce": { + "title": "Nonce", + "type": "string", + "pattern": "^0x[0-9a-f]{16}$" + }, + "totalDifficulty": { + "title": "Total difficult", + "type": "string", + "pattern": "^0x([1-9a-f]+[0-9a-f]*|0)$" + }, + "baseFeePerGas": { + "title": "Base fee per gas", + "type": "string", + "pattern": "^0x([1-9a-f]+[0-9a-f]*|0)$" + }, + "size": { + "title": "Block size", + "type": "string", + "pattern": "^0x([1-9a-f]+[0-9a-f]*|0)$" + }, + "transactions": { + "anyOf": [ + { + "title": "Transaction hashes", + "type": "array", + "items": { + "title": "32 byte hex value", + "type": "string", + "pattern": "^0x[0-9a-f]{64}$" + } + }, + { + "title": "Full transactions", + "type": "array", + "items": { + "oneOf": [ + { + "title": "Signed 1559 Transaction", + "type": "object", + "required": [ + "accessList", + "chainId", + "gas", + "input", + "maxFeePerGas", + "maxPriorityFeePerGas", + "nonce", + "r", + "s", + "type", + "value", + "yParity" + ], + "properties": { + "type": { + "title": "type", + "type": "string", + "pattern": "^0x([0-9,a-f,A-F]?){1,2}$" + }, + "nonce": { + "title": "nonce", + "type": "string", + "pattern": "^0x([1-9a-f]+[0-9a-f]*|0)$" + }, + "to": { + "title": "to address", + "type": "string", + "pattern": "^0x[0-9,a-f,A-F]{40}$" + }, + "gas": { + "title": "gas limit", + "type": "string", + "pattern": "^0x([1-9a-f]+[0-9a-f]*|0)$" + }, + "value": { + "title": "value", + "type": "string", + "pattern": "^0x([1-9a-f]+[0-9a-f]*|0)$" + }, + "input": { + "title": "input data", + "type": "string", + "pattern": "^0x[0-9a-f]*$" + }, + "maxPriorityFeePerGas": { + "title": "max priority fee per gas", + "type": "string", + "pattern": "^0x([1-9a-f]+[0-9a-f]*|0)$", + "description": "Maximum fee per gas the sender is willing to pay to miners in wei" + }, + "maxFeePerGas": { + "title": "max fee per gas", + "type": "string", + "pattern": "^0x([1-9a-f]+[0-9a-f]*|0)$", + "description": "The maximum total fee per gas the sender is willing to pay (includes the network / base fee and miner / priority fee) in wei" + }, + "accessList": { + "title": "accessList", + "type": "array", + "description": "EIP-2930 access list", + "items": { + "title": "Access list entry", + "type": "object", + "properties": { + "address": { + "title": "hex encoded address", + "type": "string", + "pattern": "^0x[0-9,a-f,A-F]{40}$" + }, + "storageKeys": { + "type": "array", + "items": { + "title": "32 byte hex value", + "type": "string", + "pattern": "^0x[0-9a-f]{64}$" + } + } + } + } + }, + "chainId": { + "title": "chainId", + "type": "string", + "pattern": "^0x([1-9a-f]+[0-9a-f]*|0)$", + "description": "Chain ID that this transaction is valid on." + }, + "yParity": { + "title": "yParity", + "type": "string", + "pattern": "^0x([1-9a-f]+[0-9a-f]*|0)$", + "description": "The parity (0 for even, 1 for odd) of the y-value of the secp256k1 signature." + }, + "r": { + "title": "r", + "type": "string", + "pattern": "^0x([1-9a-f]+[0-9a-f]*|0)$" + }, + "s": { + "title": "s", + "type": "string", + "pattern": "^0x([1-9a-f]+[0-9a-f]*|0)$" + } + } + }, + { + "title": "Signed 2930 Transaction", + "type": "object", + "required": [ + "accessList", + "chainId", + "gas", + "gasPrice", + "input", + "nonce", + "r", + "s", + "type", + "value", + "yParity" + ], + "properties": { + "type": { + "title": "type", + "type": "string", + "pattern": "^0x([0-9,a-f,A-F]?){1,2}$" + }, + "nonce": { + "title": "nonce", + "type": "string", + "pattern": "^0x([1-9a-f]+[0-9a-f]*|0)$" + }, + "to": { + "title": "to address", + "type": "string", + "pattern": "^0x[0-9,a-f,A-F]{40}$" + }, + "gas": { + "title": "gas limit", + "type": "string", + "pattern": "^0x([1-9a-f]+[0-9a-f]*|0)$" + }, + "value": { + "title": "value", + "type": "string", + "pattern": "^0x([1-9a-f]+[0-9a-f]*|0)$" + }, + "input": { + "title": "input data", + "type": "string", + "pattern": "^0x[0-9a-f]*$" + }, + "gasPrice": { + "title": "gas price", + "type": "string", + "pattern": "^0x([1-9a-f]+[0-9a-f]*|0)$", + "description": "The gas price willing to be paid by the sender in wei" + }, + "accessList": { + "title": "accessList", + "type": "array", + "description": "EIP-2930 access list", + "items": { + "title": "Access list entry", + "type": "object", + "properties": { + "address": { + "title": "hex encoded address", + "type": "string", + "pattern": "^0x[0-9,a-f,A-F]{40}$" + }, + "storageKeys": { + "type": "array", + "items": { + "title": "32 byte hex value", + "type": "string", + "pattern": "^0x[0-9a-f]{64}$" + } + } + } + } + }, + "chainId": { + "title": "chainId", + "type": "string", + "pattern": "^0x([1-9a-f]+[0-9a-f]*|0)$", + "description": "Chain ID that this transaction is valid on." + }, + "yParity": { + "title": "yParity", + "type": "string", + "pattern": "^0x([1-9a-f]+[0-9a-f]*|0)$", + "description": "The parity (0 for even, 1 for odd) of the y-value of the secp256k1 signature." + }, + "r": { + "title": "r", + "type": "string", + "pattern": "^0x([1-9a-f]+[0-9a-f]*|0)$" + }, + "s": { + "title": "s", + "type": "string", + "pattern": "^0x([1-9a-f]+[0-9a-f]*|0)$" + } + } + }, + { + "title": "Signed Legacy Transaction", + "type": "object", + "required": [ + "gas", + "gasPrice", + "input", + "nonce", + "r", + "s", + "type", + "v", + "value" + ], + "properties": { + "type": { + "title": "type", + "type": "string", + "pattern": "^0x([0-9,a-f,A-F]?){1,2}$" + }, + "nonce": { + "title": "nonce", + "type": "string", + "pattern": "^0x([1-9a-f]+[0-9a-f]*|0)$" + }, + "to": { + "title": "to address", + "type": "string", + "pattern": "^0x[0-9,a-f,A-F]{40}$" + }, + "gas": { + "title": "gas limit", + "type": "string", + "pattern": "^0x([1-9a-f]+[0-9a-f]*|0)$" + }, + "value": { + "title": "value", + "type": "string", + "pattern": "^0x([1-9a-f]+[0-9a-f]*|0)$" + }, + "input": { + "title": "input data", + "type": "string", + "pattern": "^0x[0-9a-f]*$" + }, + "gasPrice": { + "title": "gas price", + "type": "string", + "pattern": "^0x([1-9a-f]+[0-9a-f]*|0)$", + "description": "The gas price willing to be paid by the sender in wei" + }, + "chainId": { + "title": "chainId", + "type": "string", + "pattern": "^0x([1-9a-f]+[0-9a-f]*|0)$", + "description": "Chain ID that this transaction is valid on." + }, + "v": { + "title": "v", + "type": "string", + "pattern": "^0x([1-9a-f]+[0-9a-f]*|0)$" + }, + "r": { + "title": "r", + "type": "string", + "pattern": "^0x([1-9a-f]+[0-9a-f]*|0)$" + }, + "s": { + "title": "s", + "type": "string", + "pattern": "^0x([1-9a-f]+[0-9a-f]*|0)$" + } + } + } + ] + } + } + ] + }, + "uncles": { + "title": "Uncles", + "type": "array", + "items": { + "title": "32 byte hex value", + "type": "string", + "pattern": "^0x[0-9a-f]{64}$" + } + } + } + } + } + }, + { + "name": "eth_getBlockByNumber", + "summary": "Returns information about a block by number.", + "params": [ + { + "name": "Block", + "required": true, + "schema": { + "title": "Block number or tag", + "oneOf": [ + { + "title": "Block number", + "type": "string", + "pattern": "^0x([1-9a-f]+[0-9a-f]*|0)$" + }, + { + "title": "Block tag", + "type": "string", + "enum": [ + "earliest", + "finalized", + "safe", + "latest", + "pending" + ], + "description": "`earliest`: The lowest numbered block the client has available; `finalized`: The most recent crypto-economically secure block, cannot be re-orged outside of manual intervention driven by community coordination; `safe`: The most recent block that is safe from re-orgs under honest majority and certain synchronicity assumptions; `latest`: The most recent block in the canonical chain observed by the client, this block may be re-orged out of the canonical chain even under healthy/normal conditions; `pending`: A sample next block built by the client on top of `latest` and containing the set of transactions usually taken from local mempool. Before the merge transition is finalized, any call querying for `finalized` or `safe` block MUST be responded to with `-39001: Unknown block` error" + } + ] + } + }, + { + "name": "Hydrated transactions", + "required": true, + "schema": { + "title": "hydrated", + "type": "boolean" + } + } + ], + "result": { + "name": "Block information", + "schema": { + "title": "Block object", + "type": "object", + "required": [ + "parentHash", + "sha3Uncles", + "miner", + "stateRoot", + "transactionsRoot", + "receiptsRoot", + "logsBloom", + "number", + "gasLimit", + "gasUsed", + "timestamp", + "extraData", + "mixHash", + "nonce", + "size", + "transactions", + "uncles" + ], + "properties": { + "parentHash": { + "title": "Parent block hash", + "type": "string", + "pattern": "^0x[0-9a-f]{64}$" + }, + "sha3Uncles": { + "title": "Ommers hash", + "type": "string", + "pattern": "^0x[0-9a-f]{64}$" + }, + "miner": { + "title": "Coinbase", + "type": "string", + "pattern": "^0x[0-9,a-f,A-F]{40}$" + }, + "stateRoot": { + "title": "State root", + "type": "string", + "pattern": "^0x[0-9a-f]{64}$" + }, + "transactionsRoot": { + "title": "Transactions root", + "type": "string", + "pattern": "^0x[0-9a-f]{64}$" + }, + "receiptsRoot": { + "title": "Receipts root", + "type": "string", + "pattern": "^0x[0-9a-f]{64}$" + }, + "logsBloom": { + "title": "Bloom filter", + "type": "string", + "pattern": "^0x[0-9a-f]{512}$" + }, + "difficulty": { + "title": "Difficulty", + "type": "string", + "pattern": "^0x[0-9a-f]*$" + }, + "number": { + "title": "Number", + "type": "string", + "pattern": "^0x([1-9a-f]+[0-9a-f]*|0)$" + }, + "gasLimit": { + "title": "Gas limit", + "type": "string", + "pattern": "^0x([1-9a-f]+[0-9a-f]*|0)$" + }, + "gasUsed": { + "title": "Gas used", + "type": "string", + "pattern": "^0x([1-9a-f]+[0-9a-f]*|0)$" + }, + "timestamp": { + "title": "Timestamp", + "type": "string", + "pattern": "^0x([1-9a-f]+[0-9a-f]*|0)$" + }, + "extraData": { + "title": "Extra data", + "type": "string", + "pattern": "^0x[0-9a-f]*$" + }, + "mixHash": { + "title": "Mix hash", + "type": "string", + "pattern": "^0x[0-9a-f]{64}$" + }, + "nonce": { + "title": "Nonce", + "type": "string", + "pattern": "^0x[0-9a-f]{16}$" + }, + "totalDifficulty": { + "title": "Total difficult", + "type": "string", + "pattern": "^0x([1-9a-f]+[0-9a-f]*|0)$" + }, + "baseFeePerGas": { + "title": "Base fee per gas", + "type": "string", + "pattern": "^0x([1-9a-f]+[0-9a-f]*|0)$" + }, + "size": { + "title": "Block size", + "type": "string", + "pattern": "^0x([1-9a-f]+[0-9a-f]*|0)$" + }, + "transactions": { + "anyOf": [ + { + "title": "Transaction hashes", + "type": "array", + "items": { + "title": "32 byte hex value", + "type": "string", + "pattern": "^0x[0-9a-f]{64}$" + } + }, + { + "title": "Full transactions", + "type": "array", + "items": { + "oneOf": [ + { + "title": "Signed 1559 Transaction", + "type": "object", + "required": [ + "accessList", + "chainId", + "gas", + "input", + "maxFeePerGas", + "maxPriorityFeePerGas", + "nonce", + "r", + "s", + "type", + "value", + "yParity" + ], + "properties": { + "type": { + "title": "type", + "type": "string", + "pattern": "^0x([0-9,a-f,A-F]?){1,2}$" + }, + "nonce": { + "title": "nonce", + "type": "string", + "pattern": "^0x([1-9a-f]+[0-9a-f]*|0)$" + }, + "to": { + "title": "to address", + "type": "string", + "pattern": "^0x[0-9,a-f,A-F]{40}$" + }, + "gas": { + "title": "gas limit", + "type": "string", + "pattern": "^0x([1-9a-f]+[0-9a-f]*|0)$" + }, + "value": { + "title": "value", + "type": "string", + "pattern": "^0x([1-9a-f]+[0-9a-f]*|0)$" + }, + "input": { + "title": "input data", + "type": "string", + "pattern": "^0x[0-9a-f]*$" + }, + "maxPriorityFeePerGas": { + "title": "max priority fee per gas", + "type": "string", + "pattern": "^0x([1-9a-f]+[0-9a-f]*|0)$", + "description": "Maximum fee per gas the sender is willing to pay to miners in wei" + }, + "maxFeePerGas": { + "title": "max fee per gas", + "type": "string", + "pattern": "^0x([1-9a-f]+[0-9a-f]*|0)$", + "description": "The maximum total fee per gas the sender is willing to pay (includes the network / base fee and miner / priority fee) in wei" + }, + "accessList": { + "title": "accessList", + "type": "array", + "description": "EIP-2930 access list", + "items": { + "title": "Access list entry", + "type": "object", + "properties": { + "address": { + "title": "hex encoded address", + "type": "string", + "pattern": "^0x[0-9,a-f,A-F]{40}$" + }, + "storageKeys": { + "type": "array", + "items": { + "title": "32 byte hex value", + "type": "string", + "pattern": "^0x[0-9a-f]{64}$" + } + } + } + } + }, + "chainId": { + "title": "chainId", + "type": "string", + "pattern": "^0x([1-9a-f]+[0-9a-f]*|0)$", + "description": "Chain ID that this transaction is valid on." + }, + "yParity": { + "title": "yParity", + "type": "string", + "pattern": "^0x([1-9a-f]+[0-9a-f]*|0)$", + "description": "The parity (0 for even, 1 for odd) of the y-value of the secp256k1 signature." + }, + "r": { + "title": "r", + "type": "string", + "pattern": "^0x([1-9a-f]+[0-9a-f]*|0)$" + }, + "s": { + "title": "s", + "type": "string", + "pattern": "^0x([1-9a-f]+[0-9a-f]*|0)$" + } + } + }, + { + "title": "Signed 2930 Transaction", + "type": "object", + "required": [ + "accessList", + "chainId", + "gas", + "gasPrice", + "input", + "nonce", + "r", + "s", + "type", + "value", + "yParity" + ], + "properties": { + "type": { + "title": "type", + "type": "string", + "pattern": "^0x([0-9,a-f,A-F]?){1,2}$" + }, + "nonce": { + "title": "nonce", + "type": "string", + "pattern": "^0x([1-9a-f]+[0-9a-f]*|0)$" + }, + "to": { + "title": "to address", + "type": "string", + "pattern": "^0x[0-9,a-f,A-F]{40}$" + }, + "gas": { + "title": "gas limit", + "type": "string", + "pattern": "^0x([1-9a-f]+[0-9a-f]*|0)$" + }, + "value": { + "title": "value", + "type": "string", + "pattern": "^0x([1-9a-f]+[0-9a-f]*|0)$" + }, + "input": { + "title": "input data", + "type": "string", + "pattern": "^0x[0-9a-f]*$" + }, + "gasPrice": { + "title": "gas price", + "type": "string", + "pattern": "^0x([1-9a-f]+[0-9a-f]*|0)$", + "description": "The gas price willing to be paid by the sender in wei" + }, + "accessList": { + "title": "accessList", + "type": "array", + "description": "EIP-2930 access list", + "items": { + "title": "Access list entry", + "type": "object", + "properties": { + "address": { + "title": "hex encoded address", + "type": "string", + "pattern": "^0x[0-9,a-f,A-F]{40}$" + }, + "storageKeys": { + "type": "array", + "items": { + "title": "32 byte hex value", + "type": "string", + "pattern": "^0x[0-9a-f]{64}$" + } + } + } + } + }, + "chainId": { + "title": "chainId", + "type": "string", + "pattern": "^0x([1-9a-f]+[0-9a-f]*|0)$", + "description": "Chain ID that this transaction is valid on." + }, + "yParity": { + "title": "yParity", + "type": "string", + "pattern": "^0x([1-9a-f]+[0-9a-f]*|0)$", + "description": "The parity (0 for even, 1 for odd) of the y-value of the secp256k1 signature." + }, + "r": { + "title": "r", + "type": "string", + "pattern": "^0x([1-9a-f]+[0-9a-f]*|0)$" + }, + "s": { + "title": "s", + "type": "string", + "pattern": "^0x([1-9a-f]+[0-9a-f]*|0)$" + } + } + }, + { + "title": "Signed Legacy Transaction", + "type": "object", + "required": [ + "gas", + "gasPrice", + "input", + "nonce", + "r", + "s", + "type", + "v", + "value" + ], + "properties": { + "type": { + "title": "type", + "type": "string", + "pattern": "^0x([0-9,a-f,A-F]?){1,2}$" + }, + "nonce": { + "title": "nonce", + "type": "string", + "pattern": "^0x([1-9a-f]+[0-9a-f]*|0)$" + }, + "to": { + "title": "to address", + "type": "string", + "pattern": "^0x[0-9,a-f,A-F]{40}$" + }, + "gas": { + "title": "gas limit", + "type": "string", + "pattern": "^0x([1-9a-f]+[0-9a-f]*|0)$" + }, + "value": { + "title": "value", + "type": "string", + "pattern": "^0x([1-9a-f]+[0-9a-f]*|0)$" + }, + "input": { + "title": "input data", + "type": "string", + "pattern": "^0x[0-9a-f]*$" + }, + "gasPrice": { + "title": "gas price", + "type": "string", + "pattern": "^0x([1-9a-f]+[0-9a-f]*|0)$", + "description": "The gas price willing to be paid by the sender in wei" + }, + "chainId": { + "title": "chainId", + "type": "string", + "pattern": "^0x([1-9a-f]+[0-9a-f]*|0)$", + "description": "Chain ID that this transaction is valid on." + }, + "v": { + "title": "v", + "type": "string", + "pattern": "^0x([1-9a-f]+[0-9a-f]*|0)$" + }, + "r": { + "title": "r", + "type": "string", + "pattern": "^0x([1-9a-f]+[0-9a-f]*|0)$" + }, + "s": { + "title": "s", + "type": "string", + "pattern": "^0x([1-9a-f]+[0-9a-f]*|0)$" + } + } + } + ] + } + } + ] + }, + "uncles": { + "title": "Uncles", + "type": "array", + "items": { + "title": "32 byte hex value", + "type": "string", + "pattern": "^0x[0-9a-f]{64}$" + } + } + } + } + } + }, + { + "name": "eth_getBlockTransactionCountByHash", + "summary": "Returns the number of transactions in a block from a block matching the given block hash.", + "params": [ + { + "name": "Block hash", + "schema": { + "title": "32 byte hex value", + "type": "string", + "pattern": "^0x[0-9a-f]{64}$" + } + } + ], + "result": { + "name": "Transaction count", + "schema": { + "title": "hex encoded unsigned integer", + "type": "string", + "pattern": "^0x([1-9a-f]+[0-9a-f]*|0)$" + } + } + }, + { + "name": "eth_getBlockTransactionCountByNumber", + "summary": "Returns the number of transactions in a block matching the given block number.", + "params": [ + { + "name": "Block", + "schema": { + "title": "Block number or tag", + "oneOf": [ + { + "title": "Block number", + "type": "string", + "pattern": "^0x([1-9a-f]+[0-9a-f]*|0)$" + }, + { + "title": "Block tag", + "type": "string", + "enum": [ + "earliest", + "finalized", + "safe", + "latest", + "pending" + ], + "description": "`earliest`: The lowest numbered block the client has available; `finalized`: The most recent crypto-economically secure block, cannot be re-orged outside of manual intervention driven by community coordination; `safe`: The most recent block that is safe from re-orgs under honest majority and certain synchronicity assumptions; `latest`: The most recent block in the canonical chain observed by the client, this block may be re-orged out of the canonical chain even under healthy/normal conditions; `pending`: A sample next block built by the client on top of `latest` and containing the set of transactions usually taken from local mempool. Before the merge transition is finalized, any call querying for `finalized` or `safe` block MUST be responded to with `-39001: Unknown block` error" + } + ] + } + } + ], + "result": { + "name": "Transaction count", + "schema": { + "title": "hex encoded unsigned integer", + "type": "string", + "pattern": "^0x([1-9a-f]+[0-9a-f]*|0)$" + } + } + }, + { + "name": "eth_getUncleCountByBlockHash", + "summary": "Returns the number of uncles in a block from a block matching the given block hash.", + "params": [ + { + "name": "Block hash", + "schema": { + "title": "32 byte hex value", + "type": "string", + "pattern": "^0x[0-9a-f]{64}$" + } + } + ], + "result": { + "name": "Uncle count", + "schema": { + "title": "hex encoded unsigned integer", + "type": "string", + "pattern": "^0x([1-9a-f]+[0-9a-f]*|0)$" + } + } + }, + { + "name": "eth_getUncleCountByBlockNumber", + "summary": "Returns the number of transactions in a block matching the given block number.", + "params": [ + { + "name": "Block", + "schema": { + "title": "Block number or tag", + "oneOf": [ + { + "title": "Block number", + "type": "string", + "pattern": "^0x([1-9a-f]+[0-9a-f]*|0)$" + }, + { + "title": "Block tag", + "type": "string", + "enum": [ + "earliest", + "finalized", + "safe", + "latest", + "pending" + ], + "description": "`earliest`: The lowest numbered block the client has available; `finalized`: The most recent crypto-economically secure block, cannot be re-orged outside of manual intervention driven by community coordination; `safe`: The most recent block that is safe from re-orgs under honest majority and certain synchronicity assumptions; `latest`: The most recent block in the canonical chain observed by the client, this block may be re-orged out of the canonical chain even under healthy/normal conditions; `pending`: A sample next block built by the client on top of `latest` and containing the set of transactions usually taken from local mempool. Before the merge transition is finalized, any call querying for `finalized` or `safe` block MUST be responded to with `-39001: Unknown block` error" + } + ] + } + } + ], + "result": { + "name": "Uncle count", + "schema": { + "title": "hex encoded unsigned integer", + "type": "string", + "pattern": "^0x([1-9a-f]+[0-9a-f]*|0)$" + } + } + }, + { + "name": "eth_chainId", + "summary": "Returns the chain ID of the current network.", + "params": [], + "result": { + "name": "Chain ID", + "schema": { + "title": "hex encoded unsigned integer", + "type": "string", + "pattern": "^0x([1-9a-f]+[0-9a-f]*|0)$" + } + } + }, + { + "name": "eth_syncing", + "summary": "Returns an object with data about the sync status or false.", + "params": [], + "result": { + "name": "Syncing status", + "schema": { + "title": "Syncing status", + "oneOf": [ + { + "title": "Syncing progress", + "type": "object", + "properties": { + "startingBlock": { + "title": "Starting block", + "type": "string", + "pattern": "^0x([1-9a-f]+[0-9a-f]*|0)$" + }, + "currentBlock": { + "title": "Current block", + "type": "string", + "pattern": "^0x([1-9a-f]+[0-9a-f]*|0)$" + }, + "highestBlock": { + "title": "Highest block", + "type": "string", + "pattern": "^0x([1-9a-f]+[0-9a-f]*|0)$" + } + } + }, + { + "title": "Not syncing", + "description": "Should always return false if not syncing.", + "type": "boolean" + } + ] + } + } + }, + { + "name": "eth_coinbase", + "summary": "Returns the client coinbase address.", + "params": [], + "result": { + "name": "Coinbase address", + "schema": { + "title": "hex encoded address", + "type": "string", + "pattern": "^0x[0-9,a-f,A-F]{40}$" + } + } + }, + { + "name": "eth_accounts", + "summary": "Returns a list of addresses owned by client.", + "params": [], + "result": { + "name": "Accounts", + "schema": { + "title": "Accounts", + "type": "array", + "items": { + "title": "hex encoded address", + "type": "string", + "pattern": "^0x[0-9,a-f,A-F]{40}$" + } + } + } + }, + { + "name": "eth_blockNumber", + "summary": "Returns the number of most recent block.", + "params": [], + "result": { + "name": "Block number", + "schema": { + "title": "hex encoded unsigned integer", + "type": "string", + "pattern": "^0x([1-9a-f]+[0-9a-f]*|0)$" + } + } + }, + { + "name": "eth_call", + "summary": "Executes a new message call immediately without creating a transaction on the block chain.", + "params": [ + { + "name": "Transaction", + "required": true, + "schema": { + "type": "object", + "title": "Transaction object generic to all types", + "properties": { + "type": { + "title": "type", + "type": "string", + "pattern": "^0x([0-9,a-f,A-F]?){1,2}$" + }, + "nonce": { + "title": "nonce", + "type": "string", + "pattern": "^0x([1-9a-f]+[0-9a-f]*|0)$" + }, + "to": { + "title": "to address", + "type": "string", + "pattern": "^0x[0-9,a-f,A-F]{40}$" + }, + "from": { + "title": "from address", + "type": "string", + "pattern": "^0x[0-9,a-f,A-F]{40}$" + }, + "gas": { + "title": "gas limit", + "type": "string", + "pattern": "^0x([1-9a-f]+[0-9a-f]*|0)$" + }, + "value": { + "title": "value", + "type": "string", + "pattern": "^0x([1-9a-f]+[0-9a-f]*|0)$" + }, + "input": { + "title": "input data", + "type": "string", + "pattern": "^0x[0-9a-f]*$" + }, + "gasPrice": { + "title": "gas price", + "type": "string", + "pattern": "^0x([1-9a-f]+[0-9a-f]*|0)$", + "description": "The gas price willing to be paid by the sender in wei" + }, + "maxPriorityFeePerGas": { + "title": "max priority fee per gas", + "type": "string", + "pattern": "^0x([1-9a-f]+[0-9a-f]*|0)$", + "description": "Maximum fee per gas the sender is willing to pay to miners in wei" + }, + "maxFeePerGas": { + "title": "max fee per gas", + "type": "string", + "pattern": "^0x([1-9a-f]+[0-9a-f]*|0)$", + "description": "The maximum total fee per gas the sender is willing to pay (includes the network / base fee and miner / priority fee) in wei" + }, + "accessList": { + "title": "accessList", + "type": "array", + "description": "EIP-2930 access list", + "items": { + "title": "Access list entry", + "type": "object", + "properties": { + "address": { + "title": "hex encoded address", + "type": "string", + "pattern": "^0x[0-9,a-f,A-F]{40}$" + }, + "storageKeys": { + "type": "array", + "items": { + "title": "32 byte hex value", + "type": "string", + "pattern": "^0x[0-9a-f]{64}$" + } + } + } + } + }, + "chainId": { + "title": "chainId", + "type": "string", + "pattern": "^0x([1-9a-f]+[0-9a-f]*|0)$", + "description": "Chain ID that this transaction is valid on." + } + } + } + }, + { + "name": "Block", + "required": false, + "schema": { + "title": "Block number, tag, or block hash", + "anyOf": [ + { + "title": "Block number", + "type": "string", + "pattern": "^0x([1-9a-f]+[0-9a-f]*|0)$" + }, + { + "title": "Block tag", + "type": "string", + "enum": [ + "earliest", + "finalized", + "safe", + "latest", + "pending" + ], + "description": "`earliest`: The lowest numbered block the client has available; `finalized`: The most recent crypto-economically secure block, cannot be re-orged outside of manual intervention driven by community coordination; `safe`: The most recent block that is safe from re-orgs under honest majority and certain synchronicity assumptions; `latest`: The most recent block in the canonical chain observed by the client, this block may be re-orged out of the canonical chain even under healthy/normal conditions; `pending`: A sample next block built by the client on top of `latest` and containing the set of transactions usually taken from local mempool. Before the merge transition is finalized, any call querying for `finalized` or `safe` block MUST be responded to with `-39001: Unknown block` error" + }, + { + "title": "Block hash", + "type": "string", + "pattern": "^0x[0-9a-f]{64}$" + } + ] + } + } + ], + "result": { + "name": "Return data", + "schema": { + "title": "hex encoded bytes", + "type": "string", + "pattern": "^0x[0-9a-f]*$" + } + } + }, + { + "name": "eth_estimateGas", + "summary": "Generates and returns an estimate of how much gas is necessary to allow the transaction to complete.", + "params": [ + { + "name": "Transaction", + "required": true, + "schema": { + "type": "object", + "title": "Transaction object generic to all types", + "properties": { + "type": { + "title": "type", + "type": "string", + "pattern": "^0x([0-9,a-f,A-F]?){1,2}$" + }, + "nonce": { + "title": "nonce", + "type": "string", + "pattern": "^0x([1-9a-f]+[0-9a-f]*|0)$" + }, + "to": { + "title": "to address", + "type": "string", + "pattern": "^0x[0-9,a-f,A-F]{40}$" + }, + "from": { + "title": "from address", + "type": "string", + "pattern": "^0x[0-9,a-f,A-F]{40}$" + }, + "gas": { + "title": "gas limit", + "type": "string", + "pattern": "^0x([1-9a-f]+[0-9a-f]*|0)$" + }, + "value": { + "title": "value", + "type": "string", + "pattern": "^0x([1-9a-f]+[0-9a-f]*|0)$" + }, + "input": { + "title": "input data", + "type": "string", + "pattern": "^0x[0-9a-f]*$" + }, + "gasPrice": { + "title": "gas price", + "type": "string", + "pattern": "^0x([1-9a-f]+[0-9a-f]*|0)$", + "description": "The gas price willing to be paid by the sender in wei" + }, + "maxPriorityFeePerGas": { + "title": "max priority fee per gas", + "type": "string", + "pattern": "^0x([1-9a-f]+[0-9a-f]*|0)$", + "description": "Maximum fee per gas the sender is willing to pay to miners in wei" + }, + "maxFeePerGas": { + "title": "max fee per gas", + "type": "string", + "pattern": "^0x([1-9a-f]+[0-9a-f]*|0)$", + "description": "The maximum total fee per gas the sender is willing to pay (includes the network / base fee and miner / priority fee) in wei" + }, + "accessList": { + "title": "accessList", + "type": "array", + "description": "EIP-2930 access list", + "items": { + "title": "Access list entry", + "type": "object", + "properties": { + "address": { + "title": "hex encoded address", + "type": "string", + "pattern": "^0x[0-9,a-f,A-F]{40}$" + }, + "storageKeys": { + "type": "array", + "items": { + "title": "32 byte hex value", + "type": "string", + "pattern": "^0x[0-9a-f]{64}$" + } + } + } + } + }, + "chainId": { + "title": "chainId", + "type": "string", + "pattern": "^0x([1-9a-f]+[0-9a-f]*|0)$", + "description": "Chain ID that this transaction is valid on." + } + } + } + }, + { + "name": "Block", + "required": false, + "schema": { + "title": "Block number or tag", + "oneOf": [ + { + "title": "Block number", + "type": "string", + "pattern": "^0x([1-9a-f]+[0-9a-f]*|0)$" + }, + { + "title": "Block tag", + "type": "string", + "enum": [ + "earliest", + "finalized", + "safe", + "latest", + "pending" + ], + "description": "`earliest`: The lowest numbered block the client has available; `finalized`: The most recent crypto-economically secure block, cannot be re-orged outside of manual intervention driven by community coordination; `safe`: The most recent block that is safe from re-orgs under honest majority and certain synchronicity assumptions; `latest`: The most recent block in the canonical chain observed by the client, this block may be re-orged out of the canonical chain even under healthy/normal conditions; `pending`: A sample next block built by the client on top of `latest` and containing the set of transactions usually taken from local mempool. Before the merge transition is finalized, any call querying for `finalized` or `safe` block MUST be responded to with `-39001: Unknown block` error" + } + ] + } + } + ], + "result": { + "name": "Gas used", + "schema": { + "title": "hex encoded unsigned integer", + "type": "string", + "pattern": "^0x([1-9a-f]+[0-9a-f]*|0)$" + } + } + }, + { + "name": "eth_createAccessList", + "summary": "Generates an access list for a transaction.", + "params": [ + { + "name": "Transaction", + "required": true, + "schema": { + "type": "object", + "title": "Transaction object generic to all types", + "properties": { + "type": { + "title": "type", + "type": "string", + "pattern": "^0x([0-9,a-f,A-F]?){1,2}$" + }, + "nonce": { + "title": "nonce", + "type": "string", + "pattern": "^0x([1-9a-f]+[0-9a-f]*|0)$" + }, + "to": { + "title": "to address", + "type": "string", + "pattern": "^0x[0-9,a-f,A-F]{40}$" + }, + "from": { + "title": "from address", + "type": "string", + "pattern": "^0x[0-9,a-f,A-F]{40}$" + }, + "gas": { + "title": "gas limit", + "type": "string", + "pattern": "^0x([1-9a-f]+[0-9a-f]*|0)$" + }, + "value": { + "title": "value", + "type": "string", + "pattern": "^0x([1-9a-f]+[0-9a-f]*|0)$" + }, + "input": { + "title": "input data", + "type": "string", + "pattern": "^0x[0-9a-f]*$" + }, + "gasPrice": { + "title": "gas price", + "type": "string", + "pattern": "^0x([1-9a-f]+[0-9a-f]*|0)$", + "description": "The gas price willing to be paid by the sender in wei" + }, + "maxPriorityFeePerGas": { + "title": "max priority fee per gas", + "type": "string", + "pattern": "^0x([1-9a-f]+[0-9a-f]*|0)$", + "description": "Maximum fee per gas the sender is willing to pay to miners in wei" + }, + "maxFeePerGas": { + "title": "max fee per gas", + "type": "string", + "pattern": "^0x([1-9a-f]+[0-9a-f]*|0)$", + "description": "The maximum total fee per gas the sender is willing to pay (includes the network / base fee and miner / priority fee) in wei" + }, + "accessList": { + "title": "accessList", + "type": "array", + "description": "EIP-2930 access list", + "items": { + "title": "Access list entry", + "type": "object", + "properties": { + "address": { + "title": "hex encoded address", + "type": "string", + "pattern": "^0x[0-9,a-f,A-F]{40}$" + }, + "storageKeys": { + "type": "array", + "items": { + "title": "32 byte hex value", + "type": "string", + "pattern": "^0x[0-9a-f]{64}$" + } + } + } + } + }, + "chainId": { + "title": "chainId", + "type": "string", + "pattern": "^0x([1-9a-f]+[0-9a-f]*|0)$", + "description": "Chain ID that this transaction is valid on." + } + } + } + }, + { + "name": "Block", + "required": false, + "schema": { + "title": "Block number or tag", + "oneOf": [ + { + "title": "Block number", + "type": "string", + "pattern": "^0x([1-9a-f]+[0-9a-f]*|0)$" + }, + { + "title": "Block tag", + "type": "string", + "enum": [ + "earliest", + "finalized", + "safe", + "latest", + "pending" + ], + "description": "`earliest`: The lowest numbered block the client has available; `finalized`: The most recent crypto-economically secure block, cannot be re-orged outside of manual intervention driven by community coordination; `safe`: The most recent block that is safe from re-orgs under honest majority and certain synchronicity assumptions; `latest`: The most recent block in the canonical chain observed by the client, this block may be re-orged out of the canonical chain even under healthy/normal conditions; `pending`: A sample next block built by the client on top of `latest` and containing the set of transactions usually taken from local mempool. Before the merge transition is finalized, any call querying for `finalized` or `safe` block MUST be responded to with `-39001: Unknown block` error" + } + ] + } + } + ], + "result": { + "name": "Gas used", + "schema": { + "title": "Access list result", + "type": "object", + "properties": { + "accessList": { + "title": "accessList", + "type": "array", + "items": { + "title": "Access list entry", + "type": "object", + "properties": { + "address": { + "title": "hex encoded address", + "type": "string", + "pattern": "^0x[0-9,a-f,A-F]{40}$" + }, + "storageKeys": { + "type": "array", + "items": { + "title": "32 byte hex value", + "type": "string", + "pattern": "^0x[0-9a-f]{64}$" + } + } + } + } + }, + "error": { + "title": "error", + "type": "string" + }, + "gasUsed": { + "title": "Gas used", + "type": "string", + "pattern": "^0x([1-9a-f]+[0-9a-f]*|0)$" + } + } + } + } + }, + { + "name": "eth_gasPrice", + "summary": "Returns the current price per gas in wei.", + "params": [], + "result": { + "name": "Gas price", + "schema": { + "title": "Gas price", + "type": "string", + "pattern": "^0x([1-9a-f]+[0-9a-f]*|0)$" + } + } + }, + { + "name": "eth_maxPriorityFeePerGas", + "summary": "Returns the current maxPriorityFeePerGas per gas in wei.", + "params": [], + "result": { + "name": "Max priority fee per gas", + "schema": { + "title": "Max priority fee per gas", + "type": "string", + "pattern": "^0x([1-9a-f]+[0-9a-f]*|0)$" + } + } + }, + { + "name": "eth_feeHistory", + "summary": "Transaction fee history", + "description": "Returns transaction base fee per gas and effective priority fee per gas for the requested/supported block range.", + "params": [ + { + "name": "blockCount", + "description": "Requested range of blocks. Clients will return less than the requested range if not all blocks are available.", + "required": true, + "schema": { + "title": "hex encoded unsigned integer", + "type": "string", + "pattern": "^0x([1-9a-f]+[0-9a-f]*|0)$" + } + }, + { + "name": "newestBlock", + "description": "Highest block of the requested range.", + "required": true, + "schema": { + "title": "Block number or tag", + "oneOf": [ + { + "title": "Block number", + "type": "string", + "pattern": "^0x([1-9a-f]+[0-9a-f]*|0)$" + }, + { + "title": "Block tag", + "type": "string", + "enum": [ + "earliest", + "finalized", + "safe", + "latest", + "pending" + ], + "description": "`earliest`: The lowest numbered block the client has available; `finalized`: The most recent crypto-economically secure block, cannot be re-orged outside of manual intervention driven by community coordination; `safe`: The most recent block that is safe from re-orgs under honest majority and certain synchronicity assumptions; `latest`: The most recent block in the canonical chain observed by the client, this block may be re-orged out of the canonical chain even under healthy/normal conditions; `pending`: A sample next block built by the client on top of `latest` and containing the set of transactions usually taken from local mempool. Before the merge transition is finalized, any call querying for `finalized` or `safe` block MUST be responded to with `-39001: Unknown block` error" + } + ] + } + }, + { + "name": "rewardPercentiles", + "description": "A monotonically increasing list of percentile values. For each block in the requested range, the transactions will be sorted in ascending order by effective tip per gas and the coresponding effective tip for the percentile will be determined, accounting for gas consumed.", + "required": true, + "schema": { + "title": "rewardPercentiles", + "type": "array", + "items": { + "title": "rewardPercentile", + "description": "Floating point value between 0 and 100.", + "type": "number" + } + } + } + ], + "result": { + "name": "feeHistoryResult", + "description": "Fee history for the returned block range. This can be a subsection of the requested range if not all blocks are available.", + "schema": { + "title": "feeHistoryResults", + "description": "Fee history results.", + "type": "object", + "required": [ + "oldestBlock", + "baseFeePerGas", + "gasUsedRatio" + ], + "properties": { + "oldestBlock": { + "title": "oldestBlock", + "type": "string", + "pattern": "^0x([1-9a-f]+[0-9a-f]*|0)$", + "description": "Lowest number block of returned range." + }, + "baseFeePerGas": { + "title": "baseFeePerGasArray", + "description": "An array of block base fees per gas. This includes the next block after the newest of the returned range, because this value can be derived from the newest block. Zeroes are returned for pre-EIP-1559 blocks.", + "type": "array", + "items": { + "title": "hex encoded unsigned integer", + "type": "string", + "pattern": "^0x([1-9a-f]+[0-9a-f]*|0)$" + } + }, + "reward": { + "title": "rewardArray", + "description": "A two-dimensional array of effective priority fees per gas at the requested block percentiles.", + "type": "array", + "items": { + "title": "rewardPercentile", + "description": "An array of effective priority fee per gas data points from a single block. All zeroes are returned if the block is empty.", + "type": "array", + "items": { + "title": "rewardPercentile", + "type": "string", + "pattern": "^0x([1-9a-f]+[0-9a-f]*|0)$", + "description": "A given percentile sample of effective priority fees per gas from a single block in ascending order, weighted by gas used. Zeroes are returned if the block is empty." + } + } + } + } + } + } + }, + { + "name": "eth_newFilter", + "summary": "Creates a filter object, based on filter options, to notify when the state changes (logs).", + "params": [ + { + "name": "Filter", + "schema": { + "title": "filter", + "type": "object", + "properties": { + "fromBlock": { + "title": "from block", + "type": "string", + "pattern": "^0x([1-9a-f]+[0-9a-f]*|0)$" + }, + "toBlock": { + "title": "to block", + "type": "string", + "pattern": "^0x([1-9a-f]+[0-9a-f]*|0)$" + }, + "address": { + "title": "Address(es)", + "oneOf": [ + { + "title": "Address", + "type": "string", + "pattern": "^0x[0-9,a-f,A-F]{40}$" + }, + { + "title": "Addresses", + "type": "array", + "items": { + "title": "hex encoded address", + "type": "string", + "pattern": "^0x[0-9,a-f,A-F]{40}$" + } + } + ] + }, + "topics": { + "title": "Topics", + "type": "array", + "items": { + "title": "Filter Topic List Entry", + "oneOf": [ + { + "title": "Any Topic Match", + "type": "null" + }, + { + "title": "Single Topic Match", + "type": "string", + "pattern": "^0x[0-9a-f]{64}$" + }, + { + "title": "Multiple Topic Match", + "type": "array", + "items": { + "title": "32 hex encoded bytes", + "type": "string", + "pattern": "^0x[0-9a-f]{64}$" + } + } + ] + } + } + } + } + } + ], + "result": { + "name": "Filter Identifier", + "schema": { + "title": "hex encoded unsigned integer", + "type": "string", + "pattern": "^0x([1-9a-f]+[0-9a-f]*|0)$" + } + } + }, + { + "name": "eth_newBlockFilter", + "summary": "Creates a filter in the node, to notify when a new block arrives.", + "params": [], + "result": { + "name": "Filter Identifier", + "schema": { + "title": "hex encoded unsigned integer", + "type": "string", + "pattern": "^0x([1-9a-f]+[0-9a-f]*|0)$" + } + } + }, + { + "name": "eth_newPendingTransactionFilter", + "summary": "Creates a filter in the node, to notify when new pending transactions arrive.", + "params": [], + "result": { + "name": "Filter Identifier", + "schema": { + "title": "hex encoded unsigned integer", + "type": "string", + "pattern": "^0x([1-9a-f]+[0-9a-f]*|0)$" + } + } + }, + { + "name": "eth_uninstallFilter", + "summary": "Uninstalls a filter with given id.", + "params": [ + { + "name": "Filter Identifier", + "schema": { + "title": "hex encoded unsigned integer", + "type": "string", + "pattern": "^0x([1-9a-f]+[0-9a-f]*|0)$" + } + } + ], + "result": { + "name": "Success", + "schema": { + "type": "boolean" + } + } + }, + { + "name": "eth_getFilterChanges", + "summary": "Polling method for a filter, which returns an array of logs which occurred since last poll.", + "params": [ + { + "name": "Filter Identifier", + "schema": { + "title": "hex encoded unsigned integer", + "type": "string", + "pattern": "^0x([1-9a-f]+[0-9a-f]*|0)$" + } + } + ], + "result": { + "name": "Log objects", + "schema": { + "title": "Filter results", + "oneOf": [ + { + "title": "new block hashes", + "type": "array", + "items": { + "title": "32 byte hex value", + "type": "string", + "pattern": "^0x[0-9a-f]{64}$" + } + }, + { + "title": "new transaction hashes", + "type": "array", + "items": { + "title": "32 byte hex value", + "type": "string", + "pattern": "^0x[0-9a-f]{64}$" + } + }, + { + "title": "new logs", + "type": "array", + "items": { + "title": "log", + "type": "object", + "required": [ + "transactionHash" + ], + "properties": { + "removed": { + "title": "removed", + "type": "boolean" + }, + "logIndex": { + "title": "log index", + "type": "string", + "pattern": "^0x([1-9a-f]+[0-9a-f]*|0)$" + }, + "transactionIndex": { + "title": "transaction index", + "type": "string", + "pattern": "^0x([1-9a-f]+[0-9a-f]*|0)$" + }, + "transactionHash": { + "title": "transaction hash", + "type": "string", + "pattern": "^0x[0-9a-f]{64}$" + }, + "blockHash": { + "title": "block hash", + "type": "string", + "pattern": "^0x[0-9a-f]{64}$" + }, + "blockNumber": { + "title": "block number", + "type": "string", + "pattern": "^0x([1-9a-f]+[0-9a-f]*|0)$" + }, + "address": { + "title": "address", + "type": "string", + "pattern": "^0x[0-9,a-f,A-F]{40}$" + }, + "data": { + "title": "data", + "type": "string", + "pattern": "^0x[0-9a-f]*$" + }, + "topics": { + "title": "topics", + "type": "array", + "items": { + "title": "32 hex encoded bytes", + "type": "string", + "pattern": "^0x[0-9a-f]{64}$" + } + } + } + } + } + ] + } + } + }, + { + "name": "eth_getFilterLogs", + "summary": "Returns an array of all logs matching filter with given id.", + "params": [ + { + "name": "Filter Identifier", + "schema": { + "title": "hex encoded unsigned integer", + "type": "string", + "pattern": "^0x([1-9a-f]+[0-9a-f]*|0)$" + } + } + ], + "result": { + "name": "Log objects", + "schema": { + "title": "Filter results", + "oneOf": [ + { + "title": "new block hashes", + "type": "array", + "items": { + "title": "32 byte hex value", + "type": "string", + "pattern": "^0x[0-9a-f]{64}$" + } + }, + { + "title": "new transaction hashes", + "type": "array", + "items": { + "title": "32 byte hex value", + "type": "string", + "pattern": "^0x[0-9a-f]{64}$" + } + }, + { + "title": "new logs", + "type": "array", + "items": { + "title": "log", + "type": "object", + "required": [ + "transactionHash" + ], + "properties": { + "removed": { + "title": "removed", + "type": "boolean" + }, + "logIndex": { + "title": "log index", + "type": "string", + "pattern": "^0x([1-9a-f]+[0-9a-f]*|0)$" + }, + "transactionIndex": { + "title": "transaction index", + "type": "string", + "pattern": "^0x([1-9a-f]+[0-9a-f]*|0)$" + }, + "transactionHash": { + "title": "transaction hash", + "type": "string", + "pattern": "^0x[0-9a-f]{64}$" + }, + "blockHash": { + "title": "block hash", + "type": "string", + "pattern": "^0x[0-9a-f]{64}$" + }, + "blockNumber": { + "title": "block number", + "type": "string", + "pattern": "^0x([1-9a-f]+[0-9a-f]*|0)$" + }, + "address": { + "title": "address", + "type": "string", + "pattern": "^0x[0-9,a-f,A-F]{40}$" + }, + "data": { + "title": "data", + "type": "string", + "pattern": "^0x[0-9a-f]*$" + }, + "topics": { + "title": "topics", + "type": "array", + "items": { + "title": "32 hex encoded bytes", + "type": "string", + "pattern": "^0x[0-9a-f]{64}$" + } + } + } + } + } + ] + } + } + }, + { + "name": "eth_getLogs", + "summary": "Returns an array of all logs matching filter with given id.", + "params": [ + { + "name": "Filter", + "schema": { + "title": "filter", + "type": "object", + "properties": { + "fromBlock": { + "title": "from block", + "type": "string", + "pattern": "^0x([1-9a-f]+[0-9a-f]*|0)$" + }, + "toBlock": { + "title": "to block", + "type": "string", + "pattern": "^0x([1-9a-f]+[0-9a-f]*|0)$" + }, + "address": { + "title": "Address(es)", + "oneOf": [ + { + "title": "Address", + "type": "string", + "pattern": "^0x[0-9,a-f,A-F]{40}$" + }, + { + "title": "Addresses", + "type": "array", + "items": { + "title": "hex encoded address", + "type": "string", + "pattern": "^0x[0-9,a-f,A-F]{40}$" + } + } + ] + }, + "topics": { + "title": "Topics", + "type": "array", + "items": { + "title": "Filter Topic List Entry", + "oneOf": [ + { + "title": "Any Topic Match", + "type": "null" + }, + { + "title": "Single Topic Match", + "type": "string", + "pattern": "^0x[0-9a-f]{64}$" + }, + { + "title": "Multiple Topic Match", + "type": "array", + "items": { + "title": "32 hex encoded bytes", + "type": "string", + "pattern": "^0x[0-9a-f]{64}$" + } + } + ] + } + } + } + } + } + ], + "result": { + "name": "Log objects", + "schema": { + "title": "Filter results", + "oneOf": [ + { + "title": "new block hashes", + "type": "array", + "items": { + "title": "32 byte hex value", + "type": "string", + "pattern": "^0x[0-9a-f]{64}$" + } + }, + { + "title": "new transaction hashes", + "type": "array", + "items": { + "title": "32 byte hex value", + "type": "string", + "pattern": "^0x[0-9a-f]{64}$" + } + }, + { + "title": "new logs", + "type": "array", + "items": { + "title": "log", + "type": "object", + "required": [ + "transactionHash" + ], + "properties": { + "removed": { + "title": "removed", + "type": "boolean" + }, + "logIndex": { + "title": "log index", + "type": "string", + "pattern": "^0x([1-9a-f]+[0-9a-f]*|0)$" + }, + "transactionIndex": { + "title": "transaction index", + "type": "string", + "pattern": "^0x([1-9a-f]+[0-9a-f]*|0)$" + }, + "transactionHash": { + "title": "transaction hash", + "type": "string", + "pattern": "^0x[0-9a-f]{64}$" + }, + "blockHash": { + "title": "block hash", + "type": "string", + "pattern": "^0x[0-9a-f]{64}$" + }, + "blockNumber": { + "title": "block number", + "type": "string", + "pattern": "^0x([1-9a-f]+[0-9a-f]*|0)$" + }, + "address": { + "title": "address", + "type": "string", + "pattern": "^0x[0-9,a-f,A-F]{40}$" + }, + "data": { + "title": "data", + "type": "string", + "pattern": "^0x[0-9a-f]*$" + }, + "topics": { + "title": "topics", + "type": "array", + "items": { + "title": "32 hex encoded bytes", + "type": "string", + "pattern": "^0x[0-9a-f]{64}$" + } + } + } + } + } + ] + } + } + }, + { + "name": "eth_mining", + "summary": "Returns whether the client is actively mining new blocks.", + "params": [], + "result": { + "name": "Mining status", + "schema": { + "title": "miningStatus", + "type": "boolean" + } + } + }, + { + "name": "eth_hashrate", + "summary": "Returns the number of hashes per second that the node is mining with.", + "params": [], + "result": { + "name": "Mining status", + "schema": { + "title": "Hashrate", + "type": "string", + "pattern": "^0x([1-9a-f]+[0-9a-f]*|0)$" + } + } + }, + { + "name": "eth_getWork", + "summary": "Returns the hash of the current block, the seedHash, and the boundary condition to be met (“target”).", + "params": [], + "result": { + "name": "Current work", + "schema": { + "type": "array", + "items": [ + { + "title": "Proof-of-work hash", + "type": "string", + "pattern": "^0x[0-9a-f]{64}$" + }, + { + "title": "seed hash", + "type": "string", + "pattern": "^0x[0-9a-f]{64}$" + }, + { + "title": "difficulty", + "type": "string", + "pattern": "^0x[0-9a-f]{64}$" + } + ] + } + } + }, + { + "name": "eth_submitWork", + "summary": "Used for submitting a proof-of-work solution.", + "params": [ + { + "name": "nonce", + "required": true, + "schema": { + "title": "8 hex encoded bytes", + "type": "string", + "pattern": "^0x[0-9a-f]{16}$" + } + }, + { + "name": "hash", + "required": true, + "schema": { + "title": "32 hex encoded bytes", + "type": "string", + "pattern": "^0x[0-9a-f]{64}$" + } + }, + { + "name": "digest", + "required": true, + "schema": { + "title": "32 hex encoded bytes", + "type": "string", + "pattern": "^0x[0-9a-f]{64}$" + } + } + ], + "result": { + "name": "Success", + "schema": { + "type": "boolean" + } + } + }, + { + "name": "eth_submitHashrate", + "summary": "Used for submitting mining hashrate.", + "params": [ + { + "name": "Hashrate", + "required": true, + "schema": { + "title": "32 hex encoded bytes", + "type": "string", + "pattern": "^0x[0-9a-f]{64}$" + } + }, + { + "name": "ID", + "required": true, + "schema": { + "title": "32 hex encoded bytes", + "type": "string", + "pattern": "^0x[0-9a-f]{64}$" + } + } + ], + "result": { + "name": "Success", + "schema": { + "type": "boolean" + } + } + }, + { + "name": "eth_sign", + "summary": "Returns an EIP-191 signature over the provided data.", + "params": [ + { + "name": "Address", + "required": true, + "schema": { + "title": "hex encoded address", + "type": "string", + "pattern": "^0x[0-9,a-f,A-F]{40}$" + } + }, + { + "name": "Message", + "required": true, + "schema": { + "title": "hex encoded bytes", + "type": "string", + "pattern": "^0x[0-9a-f]*$" + } + } + ], + "result": { + "name": "Signature", + "schema": { + "title": "65 hex encoded bytes", + "type": "string", + "pattern": "^0x[0-9a-f]{65}$" + } + } + }, + { + "name": "eth_signTransaction", + "summary": "Returns an RLP encoded transaction signed by the specified account.", + "params": [ + { + "name": "Transaction", + "required": true, + "schema": { + "type": "object", + "title": "Transaction object generic to all types", + "properties": { + "type": { + "title": "type", + "type": "string", + "pattern": "^0x([0-9,a-f,A-F]?){1,2}$" + }, + "nonce": { + "title": "nonce", + "type": "string", + "pattern": "^0x([1-9a-f]+[0-9a-f]*|0)$" + }, + "to": { + "title": "to address", + "type": "string", + "pattern": "^0x[0-9,a-f,A-F]{40}$" + }, + "from": { + "title": "from address", + "type": "string", + "pattern": "^0x[0-9,a-f,A-F]{40}$" + }, + "gas": { + "title": "gas limit", + "type": "string", + "pattern": "^0x([1-9a-f]+[0-9a-f]*|0)$" + }, + "value": { + "title": "value", + "type": "string", + "pattern": "^0x([1-9a-f]+[0-9a-f]*|0)$" + }, + "input": { + "title": "input data", + "type": "string", + "pattern": "^0x[0-9a-f]*$" + }, + "gasPrice": { + "title": "gas price", + "type": "string", + "pattern": "^0x([1-9a-f]+[0-9a-f]*|0)$", + "description": "The gas price willing to be paid by the sender in wei" + }, + "maxPriorityFeePerGas": { + "title": "max priority fee per gas", + "type": "string", + "pattern": "^0x([1-9a-f]+[0-9a-f]*|0)$", + "description": "Maximum fee per gas the sender is willing to pay to miners in wei" + }, + "maxFeePerGas": { + "title": "max fee per gas", + "type": "string", + "pattern": "^0x([1-9a-f]+[0-9a-f]*|0)$", + "description": "The maximum total fee per gas the sender is willing to pay (includes the network / base fee and miner / priority fee) in wei" + }, + "accessList": { + "title": "accessList", + "type": "array", + "description": "EIP-2930 access list", + "items": { + "title": "Access list entry", + "type": "object", + "properties": { + "address": { + "title": "hex encoded address", + "type": "string", + "pattern": "^0x[0-9,a-f,A-F]{40}$" + }, + "storageKeys": { + "type": "array", + "items": { + "title": "32 byte hex value", + "type": "string", + "pattern": "^0x[0-9a-f]{64}$" + } + } + } + } + }, + "chainId": { + "title": "chainId", + "type": "string", + "pattern": "^0x([1-9a-f]+[0-9a-f]*|0)$", + "description": "Chain ID that this transaction is valid on." + } + } + } + } + ], + "result": { + "name": "Encoded transaction", + "schema": { + "title": "hex encoded bytes", + "type": "string", + "pattern": "^0x[0-9a-f]*$" + } + } + }, + { + "name": "eth_getBalance", + "summary": "Returns the balance of the account of given address.", + "params": [ + { + "name": "Address", + "required": true, + "schema": { + "title": "hex encoded address", + "type": "string", + "pattern": "^0x[0-9,a-f,A-F]{40}$" + } + }, + { + "name": "Block", + "required": false, + "schema": { + "title": "Block number, tag, or block hash", + "anyOf": [ + { + "title": "Block number", + "type": "string", + "pattern": "^0x([1-9a-f]+[0-9a-f]*|0)$" + }, + { + "title": "Block tag", + "type": "string", + "enum": [ + "earliest", + "finalized", + "safe", + "latest", + "pending" + ], + "description": "`earliest`: The lowest numbered block the client has available; `finalized`: The most recent crypto-economically secure block, cannot be re-orged outside of manual intervention driven by community coordination; `safe`: The most recent block that is safe from re-orgs under honest majority and certain synchronicity assumptions; `latest`: The most recent block in the canonical chain observed by the client, this block may be re-orged out of the canonical chain even under healthy/normal conditions; `pending`: A sample next block built by the client on top of `latest` and containing the set of transactions usually taken from local mempool. Before the merge transition is finalized, any call querying for `finalized` or `safe` block MUST be responded to with `-39001: Unknown block` error" + }, + { + "title": "Block hash", + "type": "string", + "pattern": "^0x[0-9a-f]{64}$" + } + ] + } + } + ], + "result": { + "name": "Balance", + "schema": { + "title": "hex encoded unsigned integer", + "type": "string", + "pattern": "^0x([1-9a-f]+[0-9a-f]*|0)$" + } + } + }, + { + "name": "eth_getStorageAt", + "summary": "Returns the value from a storage position at a given address.", + "params": [ + { + "name": "Address", + "required": true, + "schema": { + "title": "hex encoded address", + "type": "string", + "pattern": "^0x[0-9,a-f,A-F]{40}$" + } + }, + { + "name": "Storage slot", + "required": true, + "schema": { + "title": "hex encoded 256 bit unsigned integer", + "type": "string", + "pattern": "^0x([1-9a-f]+[0-9a-f]{0,31})|0$" + } + }, + { + "name": "Block", + "required": false, + "schema": { + "title": "Block number, tag, or block hash", + "anyOf": [ + { + "title": "Block number", + "type": "string", + "pattern": "^0x([1-9a-f]+[0-9a-f]*|0)$" + }, + { + "title": "Block tag", + "type": "string", + "enum": [ + "earliest", + "finalized", + "safe", + "latest", + "pending" + ], + "description": "`earliest`: The lowest numbered block the client has available; `finalized`: The most recent crypto-economically secure block, cannot be re-orged outside of manual intervention driven by community coordination; `safe`: The most recent block that is safe from re-orgs under honest majority and certain synchronicity assumptions; `latest`: The most recent block in the canonical chain observed by the client, this block may be re-orged out of the canonical chain even under healthy/normal conditions; `pending`: A sample next block built by the client on top of `latest` and containing the set of transactions usually taken from local mempool. Before the merge transition is finalized, any call querying for `finalized` or `safe` block MUST be responded to with `-39001: Unknown block` error" + }, + { + "title": "Block hash", + "type": "string", + "pattern": "^0x[0-9a-f]{64}$" + } + ] + } + } + ], + "result": { + "name": "Value", + "schema": { + "title": "hex encoded bytes", + "type": "string", + "pattern": "^0x[0-9a-f]*$" + } + } + }, + { + "name": "eth_getTransactionCount", + "summary": "Returns the number of transactions sent from an address.", + "params": [ + { + "name": "Address", + "required": true, + "schema": { + "title": "hex encoded address", + "type": "string", + "pattern": "^0x[0-9,a-f,A-F]{40}$" + } + }, + { + "name": "Block", + "required": false, + "schema": { + "title": "Block number, tag, or block hash", + "anyOf": [ + { + "title": "Block number", + "type": "string", + "pattern": "^0x([1-9a-f]+[0-9a-f]*|0)$" + }, + { + "title": "Block tag", + "type": "string", + "enum": [ + "earliest", + "finalized", + "safe", + "latest", + "pending" + ], + "description": "`earliest`: The lowest numbered block the client has available; `finalized`: The most recent crypto-economically secure block, cannot be re-orged outside of manual intervention driven by community coordination; `safe`: The most recent block that is safe from re-orgs under honest majority and certain synchronicity assumptions; `latest`: The most recent block in the canonical chain observed by the client, this block may be re-orged out of the canonical chain even under healthy/normal conditions; `pending`: A sample next block built by the client on top of `latest` and containing the set of transactions usually taken from local mempool. Before the merge transition is finalized, any call querying for `finalized` or `safe` block MUST be responded to with `-39001: Unknown block` error" + }, + { + "title": "Block hash", + "type": "string", + "pattern": "^0x[0-9a-f]{64}$" + } + ] + } + } + ], + "result": { + "name": "Transaction count", + "schema": { + "title": "hex encoded unsigned integer", + "type": "string", + "pattern": "^0x([1-9a-f]+[0-9a-f]*|0)$" + } + } + }, + { + "name": "eth_getCode", + "summary": "Returns code at a given address.", + "params": [ + { + "name": "Address", + "required": true, + "schema": { + "title": "hex encoded address", + "type": "string", + "pattern": "^0x[0-9,a-f,A-F]{40}$" + } + }, + { + "name": "Block", + "required": false, + "schema": { + "title": "Block number, tag, or block hash", + "anyOf": [ + { + "title": "Block number", + "type": "string", + "pattern": "^0x([1-9a-f]+[0-9a-f]*|0)$" + }, + { + "title": "Block tag", + "type": "string", + "enum": [ + "earliest", + "finalized", + "safe", + "latest", + "pending" + ], + "description": "`earliest`: The lowest numbered block the client has available; `finalized`: The most recent crypto-economically secure block, cannot be re-orged outside of manual intervention driven by community coordination; `safe`: The most recent block that is safe from re-orgs under honest majority and certain synchronicity assumptions; `latest`: The most recent block in the canonical chain observed by the client, this block may be re-orged out of the canonical chain even under healthy/normal conditions; `pending`: A sample next block built by the client on top of `latest` and containing the set of transactions usually taken from local mempool. Before the merge transition is finalized, any call querying for `finalized` or `safe` block MUST be responded to with `-39001: Unknown block` error" + }, + { + "title": "Block hash", + "type": "string", + "pattern": "^0x[0-9a-f]{64}$" + } + ] + } + } + ], + "result": { + "name": "Bytecode", + "schema": { + "title": "hex encoded bytes", + "type": "string", + "pattern": "^0x[0-9a-f]*$" + } + } + }, + { + "name": "eth_getProof", + "summary": "Returns the merkle proof for a given account and optionally some storage keys.", + "params": [ + { + "name": "Address", + "required": true, + "schema": { + "title": "hex encoded address", + "type": "string", + "pattern": "^0x[0-9,a-f,A-F]{40}$" + } + }, + { + "name": "StorageKeys", + "required": true, + "schema": { + "title": "Storage keys", + "type": "array", + "items": { + "title": "32 hex encoded bytes", + "type": "string", + "pattern": "^0x[0-9a-f]{0,64}$" + } + } + }, + { + "name": "Block", + "required": true, + "schema": { + "title": "Block number, tag, or block hash", + "anyOf": [ + { + "title": "Block number", + "type": "string", + "pattern": "^0x([1-9a-f]+[0-9a-f]*|0)$" + }, + { + "title": "Block tag", + "type": "string", + "enum": [ + "earliest", + "finalized", + "safe", + "latest", + "pending" + ], + "description": "`earliest`: The lowest numbered block the client has available; `finalized`: The most recent crypto-economically secure block, cannot be re-orged outside of manual intervention driven by community coordination; `safe`: The most recent block that is safe from re-orgs under honest majority and certain synchronicity assumptions; `latest`: The most recent block in the canonical chain observed by the client, this block may be re-orged out of the canonical chain even under healthy/normal conditions; `pending`: A sample next block built by the client on top of `latest` and containing the set of transactions usually taken from local mempool. Before the merge transition is finalized, any call querying for `finalized` or `safe` block MUST be responded to with `-39001: Unknown block` error" + }, + { + "title": "Block hash", + "type": "string", + "pattern": "^0x[0-9a-f]{64}$" + } + ] + } + } + ], + "result": { + "name": "Account", + "schema": { + "title": "Account proof", + "type": "object", + "required": [ + "address", + "accountProof", + "balance", + "codeHash", + "nonce", + "storageHash", + "storageProof" + ], + "properties": { + "address": { + "title": "address", + "type": "string", + "pattern": "^0x[0-9,a-f,A-F]{40}$" + }, + "accountProof": { + "title": "accountProof", + "type": "array", + "items": { + "title": "hex encoded bytes", + "type": "string", + "pattern": "^0x[0-9a-f]*$" + } + }, + "balance": { + "title": "balance", + "type": "string", + "pattern": "^0x([1-9a-f]+[0-9a-f]{0,31})|0$" + }, + "codeHash": { + "title": "codeHash", + "type": "string", + "pattern": "^0x[0-9a-f]{64}$" + }, + "nonce": { + "title": "nonce", + "type": "string", + "pattern": "^0x([1-9a-f]+[0-9a-f]{0,15})|0$" + }, + "storageHash": { + "title": "storageHash", + "type": "string", + "pattern": "^0x[0-9a-f]{64}$" + }, + "storageProof": { + "title": "Storage proofs", + "type": "array", + "items": { + "title": "Storage proof", + "type": "object", + "required": [ + "key", + "value", + "proof" + ], + "properties": { + "key": { + "title": "key", + "type": "string", + "pattern": "^0x[0-9a-f]{0,64}$" + }, + "value": { + "title": "value", + "type": "string", + "pattern": "^0x([1-9a-f]+[0-9a-f]{0,31})|0$" + }, + "proof": { + "title": "proof", + "type": "array", + "items": { + "title": "hex encoded bytes", + "type": "string", + "pattern": "^0x[0-9a-f]*$" + } + } + } + } + } + } + } + } + }, + { + "name": "eth_sendTransaction", + "summary": "Signs and submits a transaction.", + "params": [ + { + "name": "Transaction", + "required": true, + "schema": { + "type": "object", + "title": "Transaction object generic to all types", + "properties": { + "type": { + "title": "type", + "type": "string", + "pattern": "^0x([0-9,a-f,A-F]?){1,2}$" + }, + "nonce": { + "title": "nonce", + "type": "string", + "pattern": "^0x([1-9a-f]+[0-9a-f]*|0)$" + }, + "to": { + "title": "to address", + "type": "string", + "pattern": "^0x[0-9,a-f,A-F]{40}$" + }, + "from": { + "title": "from address", + "type": "string", + "pattern": "^0x[0-9,a-f,A-F]{40}$" + }, + "gas": { + "title": "gas limit", + "type": "string", + "pattern": "^0x([1-9a-f]+[0-9a-f]*|0)$" + }, + "value": { + "title": "value", + "type": "string", + "pattern": "^0x([1-9a-f]+[0-9a-f]*|0)$" + }, + "input": { + "title": "input data", + "type": "string", + "pattern": "^0x[0-9a-f]*$" + }, + "gasPrice": { + "title": "gas price", + "type": "string", + "pattern": "^0x([1-9a-f]+[0-9a-f]*|0)$", + "description": "The gas price willing to be paid by the sender in wei" + }, + "maxPriorityFeePerGas": { + "title": "max priority fee per gas", + "type": "string", + "pattern": "^0x([1-9a-f]+[0-9a-f]*|0)$", + "description": "Maximum fee per gas the sender is willing to pay to miners in wei" + }, + "maxFeePerGas": { + "title": "max fee per gas", + "type": "string", + "pattern": "^0x([1-9a-f]+[0-9a-f]*|0)$", + "description": "The maximum total fee per gas the sender is willing to pay (includes the network / base fee and miner / priority fee) in wei" + }, + "accessList": { + "title": "accessList", + "type": "array", + "description": "EIP-2930 access list", + "items": { + "title": "Access list entry", + "type": "object", + "properties": { + "address": { + "title": "hex encoded address", + "type": "string", + "pattern": "^0x[0-9,a-f,A-F]{40}$" + }, + "storageKeys": { + "type": "array", + "items": { + "title": "32 byte hex value", + "type": "string", + "pattern": "^0x[0-9a-f]{64}$" + } + } + } + } + }, + "chainId": { + "title": "chainId", + "type": "string", + "pattern": "^0x([1-9a-f]+[0-9a-f]*|0)$", + "description": "Chain ID that this transaction is valid on." + } + } + } + } + ], + "result": { + "name": "Transaction hash", + "schema": { + "title": "32 byte hex value", + "type": "string", + "pattern": "^0x[0-9a-f]{64}$" + } + } + }, + { + "name": "eth_sendRawTransaction", + "summary": "Submits a raw transaction.", + "params": [ + { + "name": "Transaction", + "required": true, + "schema": { + "title": "hex encoded bytes", + "type": "string", + "pattern": "^0x[0-9a-f]*$" + } + } + ], + "result": { + "name": "Transaction hash", + "schema": { + "title": "32 byte hex value", + "type": "string", + "pattern": "^0x[0-9a-f]{64}$" + } + } + }, + { + "name": "eth_getTransactionByHash", + "summary": "Returns the information about a transaction requested by transaction hash.", + "params": [ + { + "name": "Transaction hash", + "required": true, + "schema": { + "title": "32 byte hex value", + "type": "string", + "pattern": "^0x[0-9a-f]{64}$" + } + } + ], + "result": { + "name": "Transaction information", + "schema": { + "type": "object", + "title": "Transaction information", + "required": [ + "blockHash", + "blockNumber", + "from", + "hash", + "transactionIndex" + ], + "oneOf": [ + { + "title": "Signed 1559 Transaction", + "type": "object", + "required": [ + "accessList", + "chainId", + "gas", + "input", + "maxFeePerGas", + "maxPriorityFeePerGas", + "nonce", + "r", + "s", + "type", + "value", + "yParity" + ], + "properties": { + "type": { + "title": "type", + "type": "string", + "pattern": "^0x([0-9,a-f,A-F]?){1,2}$" + }, + "nonce": { + "title": "nonce", + "type": "string", + "pattern": "^0x([1-9a-f]+[0-9a-f]*|0)$" + }, + "to": { + "title": "to address", + "type": "string", + "pattern": "^0x[0-9,a-f,A-F]{40}$" + }, + "gas": { + "title": "gas limit", + "type": "string", + "pattern": "^0x([1-9a-f]+[0-9a-f]*|0)$" + }, + "value": { + "title": "value", + "type": "string", + "pattern": "^0x([1-9a-f]+[0-9a-f]*|0)$" + }, + "input": { + "title": "input data", + "type": "string", + "pattern": "^0x[0-9a-f]*$" + }, + "maxPriorityFeePerGas": { + "title": "max priority fee per gas", + "type": "string", + "pattern": "^0x([1-9a-f]+[0-9a-f]*|0)$", + "description": "Maximum fee per gas the sender is willing to pay to miners in wei" + }, + "maxFeePerGas": { + "title": "max fee per gas", + "type": "string", + "pattern": "^0x([1-9a-f]+[0-9a-f]*|0)$", + "description": "The maximum total fee per gas the sender is willing to pay (includes the network / base fee and miner / priority fee) in wei" + }, + "accessList": { + "title": "accessList", + "type": "array", + "description": "EIP-2930 access list", + "items": { + "title": "Access list entry", + "type": "object", + "properties": { + "address": { + "title": "hex encoded address", + "type": "string", + "pattern": "^0x[0-9,a-f,A-F]{40}$" + }, + "storageKeys": { + "type": "array", + "items": { + "title": "32 byte hex value", + "type": "string", + "pattern": "^0x[0-9a-f]{64}$" + } + } + } + } + }, + "chainId": { + "title": "chainId", + "type": "string", + "pattern": "^0x([1-9a-f]+[0-9a-f]*|0)$", + "description": "Chain ID that this transaction is valid on." + }, + "yParity": { + "title": "yParity", + "type": "string", + "pattern": "^0x([1-9a-f]+[0-9a-f]*|0)$", + "description": "The parity (0 for even, 1 for odd) of the y-value of the secp256k1 signature." + }, + "r": { + "title": "r", + "type": "string", + "pattern": "^0x([1-9a-f]+[0-9a-f]*|0)$" + }, + "s": { + "title": "s", + "type": "string", + "pattern": "^0x([1-9a-f]+[0-9a-f]*|0)$" + } + } + }, + { + "title": "Signed 2930 Transaction", + "type": "object", + "required": [ + "accessList", + "chainId", + "gas", + "gasPrice", + "input", + "nonce", + "r", + "s", + "type", + "value", + "yParity" + ], + "properties": { + "type": { + "title": "type", + "type": "string", + "pattern": "^0x([0-9,a-f,A-F]?){1,2}$" + }, + "nonce": { + "title": "nonce", + "type": "string", + "pattern": "^0x([1-9a-f]+[0-9a-f]*|0)$" + }, + "to": { + "title": "to address", + "type": "string", + "pattern": "^0x[0-9,a-f,A-F]{40}$" + }, + "gas": { + "title": "gas limit", + "type": "string", + "pattern": "^0x([1-9a-f]+[0-9a-f]*|0)$" + }, + "value": { + "title": "value", + "type": "string", + "pattern": "^0x([1-9a-f]+[0-9a-f]*|0)$" + }, + "input": { + "title": "input data", + "type": "string", + "pattern": "^0x[0-9a-f]*$" + }, + "gasPrice": { + "title": "gas price", + "type": "string", + "pattern": "^0x([1-9a-f]+[0-9a-f]*|0)$", + "description": "The gas price willing to be paid by the sender in wei" + }, + "accessList": { + "title": "accessList", + "type": "array", + "description": "EIP-2930 access list", + "items": { + "title": "Access list entry", + "type": "object", + "properties": { + "address": { + "title": "hex encoded address", + "type": "string", + "pattern": "^0x[0-9,a-f,A-F]{40}$" + }, + "storageKeys": { + "type": "array", + "items": { + "title": "32 byte hex value", + "type": "string", + "pattern": "^0x[0-9a-f]{64}$" + } + } + } + } + }, + "chainId": { + "title": "chainId", + "type": "string", + "pattern": "^0x([1-9a-f]+[0-9a-f]*|0)$", + "description": "Chain ID that this transaction is valid on." + }, + "yParity": { + "title": "yParity", + "type": "string", + "pattern": "^0x([1-9a-f]+[0-9a-f]*|0)$", + "description": "The parity (0 for even, 1 for odd) of the y-value of the secp256k1 signature." + }, + "r": { + "title": "r", + "type": "string", + "pattern": "^0x([1-9a-f]+[0-9a-f]*|0)$" + }, + "s": { + "title": "s", + "type": "string", + "pattern": "^0x([1-9a-f]+[0-9a-f]*|0)$" + } + } + }, + { + "title": "Signed Legacy Transaction", + "type": "object", + "required": [ + "gas", + "gasPrice", + "input", + "nonce", + "r", + "s", + "type", + "v", + "value" + ], + "properties": { + "type": { + "title": "type", + "type": "string", + "pattern": "^0x([0-9,a-f,A-F]?){1,2}$" + }, + "nonce": { + "title": "nonce", + "type": "string", + "pattern": "^0x([1-9a-f]+[0-9a-f]*|0)$" + }, + "to": { + "title": "to address", + "type": "string", + "pattern": "^0x[0-9,a-f,A-F]{40}$" + }, + "gas": { + "title": "gas limit", + "type": "string", + "pattern": "^0x([1-9a-f]+[0-9a-f]*|0)$" + }, + "value": { + "title": "value", + "type": "string", + "pattern": "^0x([1-9a-f]+[0-9a-f]*|0)$" + }, + "input": { + "title": "input data", + "type": "string", + "pattern": "^0x[0-9a-f]*$" + }, + "gasPrice": { + "title": "gas price", + "type": "string", + "pattern": "^0x([1-9a-f]+[0-9a-f]*|0)$", + "description": "The gas price willing to be paid by the sender in wei" + }, + "chainId": { + "title": "chainId", + "type": "string", + "pattern": "^0x([1-9a-f]+[0-9a-f]*|0)$", + "description": "Chain ID that this transaction is valid on." + }, + "v": { + "title": "v", + "type": "string", + "pattern": "^0x([1-9a-f]+[0-9a-f]*|0)$" + }, + "r": { + "title": "r", + "type": "string", + "pattern": "^0x([1-9a-f]+[0-9a-f]*|0)$" + }, + "s": { + "title": "s", + "type": "string", + "pattern": "^0x([1-9a-f]+[0-9a-f]*|0)$" + } + } + } + ], + "properties": { + "blockHash": { + "title": "block hash", + "type": "string", + "pattern": "^0x[0-9a-f]{64}$" + }, + "blockNumber": { + "title": "block number", + "type": "string", + "pattern": "^0x([1-9a-f]+[0-9a-f]*|0)$" + }, + "from": { + "title": "from address", + "type": "string", + "pattern": "^0x[0-9,a-f,A-F]{40}$" + }, + "hash": { + "title": "transaction hash", + "type": "string", + "pattern": "^0x[0-9a-f]{64}$" + }, + "transactionIndex": { + "title": "transaction index", + "type": "string", + "pattern": "^0x([1-9a-f]+[0-9a-f]*|0)$" + } + } + } + } + }, + { + "name": "eth_getTransactionByBlockHashAndIndex", + "summary": "Returns information about a transaction by block hash and transaction index position.", + "params": [ + { + "name": "Block hash", + "required": true, + "schema": { + "title": "32 byte hex value", + "type": "string", + "pattern": "^0x[0-9a-f]{64}$" + } + }, + { + "name": "Transaction index", + "required": true, + "schema": { + "title": "hex encoded unsigned integer", + "type": "string", + "pattern": "^0x([1-9a-f]+[0-9a-f]*|0)$" + } + } + ], + "result": { + "name": "Transaction information", + "schema": { + "type": "object", + "title": "Transaction information", + "required": [ + "blockHash", + "blockNumber", + "from", + "hash", + "transactionIndex" + ], + "oneOf": [ + { + "title": "Signed 1559 Transaction", + "type": "object", + "required": [ + "accessList", + "chainId", + "gas", + "input", + "maxFeePerGas", + "maxPriorityFeePerGas", + "nonce", + "r", + "s", + "type", + "value", + "yParity" + ], + "properties": { + "type": { + "title": "type", + "type": "string", + "pattern": "^0x([0-9,a-f,A-F]?){1,2}$" + }, + "nonce": { + "title": "nonce", + "type": "string", + "pattern": "^0x([1-9a-f]+[0-9a-f]*|0)$" + }, + "to": { + "title": "to address", + "type": "string", + "pattern": "^0x[0-9,a-f,A-F]{40}$" + }, + "gas": { + "title": "gas limit", + "type": "string", + "pattern": "^0x([1-9a-f]+[0-9a-f]*|0)$" + }, + "value": { + "title": "value", + "type": "string", + "pattern": "^0x([1-9a-f]+[0-9a-f]*|0)$" + }, + "input": { + "title": "input data", + "type": "string", + "pattern": "^0x[0-9a-f]*$" + }, + "maxPriorityFeePerGas": { + "title": "max priority fee per gas", + "type": "string", + "pattern": "^0x([1-9a-f]+[0-9a-f]*|0)$", + "description": "Maximum fee per gas the sender is willing to pay to miners in wei" + }, + "maxFeePerGas": { + "title": "max fee per gas", + "type": "string", + "pattern": "^0x([1-9a-f]+[0-9a-f]*|0)$", + "description": "The maximum total fee per gas the sender is willing to pay (includes the network / base fee and miner / priority fee) in wei" + }, + "accessList": { + "title": "accessList", + "type": "array", + "description": "EIP-2930 access list", + "items": { + "title": "Access list entry", + "type": "object", + "properties": { + "address": { + "title": "hex encoded address", + "type": "string", + "pattern": "^0x[0-9,a-f,A-F]{40}$" + }, + "storageKeys": { + "type": "array", + "items": { + "title": "32 byte hex value", + "type": "string", + "pattern": "^0x[0-9a-f]{64}$" + } + } + } + } + }, + "chainId": { + "title": "chainId", + "type": "string", + "pattern": "^0x([1-9a-f]+[0-9a-f]*|0)$", + "description": "Chain ID that this transaction is valid on." + }, + "yParity": { + "title": "yParity", + "type": "string", + "pattern": "^0x([1-9a-f]+[0-9a-f]*|0)$", + "description": "The parity (0 for even, 1 for odd) of the y-value of the secp256k1 signature." + }, + "r": { + "title": "r", + "type": "string", + "pattern": "^0x([1-9a-f]+[0-9a-f]*|0)$" + }, + "s": { + "title": "s", + "type": "string", + "pattern": "^0x([1-9a-f]+[0-9a-f]*|0)$" + } + } + }, + { + "title": "Signed 2930 Transaction", + "type": "object", + "required": [ + "accessList", + "chainId", + "gas", + "gasPrice", + "input", + "nonce", + "r", + "s", + "type", + "value", + "yParity" + ], + "properties": { + "type": { + "title": "type", + "type": "string", + "pattern": "^0x([0-9,a-f,A-F]?){1,2}$" + }, + "nonce": { + "title": "nonce", + "type": "string", + "pattern": "^0x([1-9a-f]+[0-9a-f]*|0)$" + }, + "to": { + "title": "to address", + "type": "string", + "pattern": "^0x[0-9,a-f,A-F]{40}$" + }, + "gas": { + "title": "gas limit", + "type": "string", + "pattern": "^0x([1-9a-f]+[0-9a-f]*|0)$" + }, + "value": { + "title": "value", + "type": "string", + "pattern": "^0x([1-9a-f]+[0-9a-f]*|0)$" + }, + "input": { + "title": "input data", + "type": "string", + "pattern": "^0x[0-9a-f]*$" + }, + "gasPrice": { + "title": "gas price", + "type": "string", + "pattern": "^0x([1-9a-f]+[0-9a-f]*|0)$", + "description": "The gas price willing to be paid by the sender in wei" + }, + "accessList": { + "title": "accessList", + "type": "array", + "description": "EIP-2930 access list", + "items": { + "title": "Access list entry", + "type": "object", + "properties": { + "address": { + "title": "hex encoded address", + "type": "string", + "pattern": "^0x[0-9,a-f,A-F]{40}$" + }, + "storageKeys": { + "type": "array", + "items": { + "title": "32 byte hex value", + "type": "string", + "pattern": "^0x[0-9a-f]{64}$" + } + } + } + } + }, + "chainId": { + "title": "chainId", + "type": "string", + "pattern": "^0x([1-9a-f]+[0-9a-f]*|0)$", + "description": "Chain ID that this transaction is valid on." + }, + "yParity": { + "title": "yParity", + "type": "string", + "pattern": "^0x([1-9a-f]+[0-9a-f]*|0)$", + "description": "The parity (0 for even, 1 for odd) of the y-value of the secp256k1 signature." + }, + "r": { + "title": "r", + "type": "string", + "pattern": "^0x([1-9a-f]+[0-9a-f]*|0)$" + }, + "s": { + "title": "s", + "type": "string", + "pattern": "^0x([1-9a-f]+[0-9a-f]*|0)$" + } + } + }, + { + "title": "Signed Legacy Transaction", + "type": "object", + "required": [ + "gas", + "gasPrice", + "input", + "nonce", + "r", + "s", + "type", + "v", + "value" + ], + "properties": { + "type": { + "title": "type", + "type": "string", + "pattern": "^0x([0-9,a-f,A-F]?){1,2}$" + }, + "nonce": { + "title": "nonce", + "type": "string", + "pattern": "^0x([1-9a-f]+[0-9a-f]*|0)$" + }, + "to": { + "title": "to address", + "type": "string", + "pattern": "^0x[0-9,a-f,A-F]{40}$" + }, + "gas": { + "title": "gas limit", + "type": "string", + "pattern": "^0x([1-9a-f]+[0-9a-f]*|0)$" + }, + "value": { + "title": "value", + "type": "string", + "pattern": "^0x([1-9a-f]+[0-9a-f]*|0)$" + }, + "input": { + "title": "input data", + "type": "string", + "pattern": "^0x[0-9a-f]*$" + }, + "gasPrice": { + "title": "gas price", + "type": "string", + "pattern": "^0x([1-9a-f]+[0-9a-f]*|0)$", + "description": "The gas price willing to be paid by the sender in wei" + }, + "chainId": { + "title": "chainId", + "type": "string", + "pattern": "^0x([1-9a-f]+[0-9a-f]*|0)$", + "description": "Chain ID that this transaction is valid on." + }, + "v": { + "title": "v", + "type": "string", + "pattern": "^0x([1-9a-f]+[0-9a-f]*|0)$" + }, + "r": { + "title": "r", + "type": "string", + "pattern": "^0x([1-9a-f]+[0-9a-f]*|0)$" + }, + "s": { + "title": "s", + "type": "string", + "pattern": "^0x([1-9a-f]+[0-9a-f]*|0)$" + } + } + } + ], + "properties": { + "blockHash": { + "title": "block hash", + "type": "string", + "pattern": "^0x[0-9a-f]{64}$" + }, + "blockNumber": { + "title": "block number", + "type": "string", + "pattern": "^0x([1-9a-f]+[0-9a-f]*|0)$" + }, + "from": { + "title": "from address", + "type": "string", + "pattern": "^0x[0-9,a-f,A-F]{40}$" + }, + "hash": { + "title": "transaction hash", + "type": "string", + "pattern": "^0x[0-9a-f]{64}$" + }, + "transactionIndex": { + "title": "transaction index", + "type": "string", + "pattern": "^0x([1-9a-f]+[0-9a-f]*|0)$" + } + } + } + } + }, + { + "name": "eth_getTransactionByBlockNumberAndIndex", + "summary": "Returns information about a transaction by block number and transaction index position.", + "params": [ + { + "name": "Block", + "required": true, + "schema": { + "title": "Block number or tag", + "oneOf": [ + { + "title": "Block number", + "type": "string", + "pattern": "^0x([1-9a-f]+[0-9a-f]*|0)$" + }, + { + "title": "Block tag", + "type": "string", + "enum": [ + "earliest", + "finalized", + "safe", + "latest", + "pending" + ], + "description": "`earliest`: The lowest numbered block the client has available; `finalized`: The most recent crypto-economically secure block, cannot be re-orged outside of manual intervention driven by community coordination; `safe`: The most recent block that is safe from re-orgs under honest majority and certain synchronicity assumptions; `latest`: The most recent block in the canonical chain observed by the client, this block may be re-orged out of the canonical chain even under healthy/normal conditions; `pending`: A sample next block built by the client on top of `latest` and containing the set of transactions usually taken from local mempool. Before the merge transition is finalized, any call querying for `finalized` or `safe` block MUST be responded to with `-39001: Unknown block` error" + } + ] + } + }, + { + "name": "Transaction index", + "required": true, + "schema": { + "title": "hex encoded unsigned integer", + "type": "string", + "pattern": "^0x([1-9a-f]+[0-9a-f]*|0)$" + } + } + ], + "result": { + "name": "Transaction information", + "schema": { + "type": "object", + "title": "Transaction information", + "required": [ + "blockHash", + "blockNumber", + "from", + "hash", + "transactionIndex" + ], + "oneOf": [ + { + "title": "Signed 1559 Transaction", + "type": "object", + "required": [ + "accessList", + "chainId", + "gas", + "input", + "maxFeePerGas", + "maxPriorityFeePerGas", + "nonce", + "r", + "s", + "type", + "value", + "yParity" + ], + "properties": { + "type": { + "title": "type", + "type": "string", + "pattern": "^0x([0-9,a-f,A-F]?){1,2}$" + }, + "nonce": { + "title": "nonce", + "type": "string", + "pattern": "^0x([1-9a-f]+[0-9a-f]*|0)$" + }, + "to": { + "title": "to address", + "type": "string", + "pattern": "^0x[0-9,a-f,A-F]{40}$" + }, + "gas": { + "title": "gas limit", + "type": "string", + "pattern": "^0x([1-9a-f]+[0-9a-f]*|0)$" + }, + "value": { + "title": "value", + "type": "string", + "pattern": "^0x([1-9a-f]+[0-9a-f]*|0)$" + }, + "input": { + "title": "input data", + "type": "string", + "pattern": "^0x[0-9a-f]*$" + }, + "maxPriorityFeePerGas": { + "title": "max priority fee per gas", + "type": "string", + "pattern": "^0x([1-9a-f]+[0-9a-f]*|0)$", + "description": "Maximum fee per gas the sender is willing to pay to miners in wei" + }, + "maxFeePerGas": { + "title": "max fee per gas", + "type": "string", + "pattern": "^0x([1-9a-f]+[0-9a-f]*|0)$", + "description": "The maximum total fee per gas the sender is willing to pay (includes the network / base fee and miner / priority fee) in wei" + }, + "accessList": { + "title": "accessList", + "type": "array", + "description": "EIP-2930 access list", + "items": { + "title": "Access list entry", + "type": "object", + "properties": { + "address": { + "title": "hex encoded address", + "type": "string", + "pattern": "^0x[0-9,a-f,A-F]{40}$" + }, + "storageKeys": { + "type": "array", + "items": { + "title": "32 byte hex value", + "type": "string", + "pattern": "^0x[0-9a-f]{64}$" + } + } + } + } + }, + "chainId": { + "title": "chainId", + "type": "string", + "pattern": "^0x([1-9a-f]+[0-9a-f]*|0)$", + "description": "Chain ID that this transaction is valid on." + }, + "yParity": { + "title": "yParity", + "type": "string", + "pattern": "^0x([1-9a-f]+[0-9a-f]*|0)$", + "description": "The parity (0 for even, 1 for odd) of the y-value of the secp256k1 signature." + }, + "r": { + "title": "r", + "type": "string", + "pattern": "^0x([1-9a-f]+[0-9a-f]*|0)$" + }, + "s": { + "title": "s", + "type": "string", + "pattern": "^0x([1-9a-f]+[0-9a-f]*|0)$" + } + } + }, + { + "title": "Signed 2930 Transaction", + "type": "object", + "required": [ + "accessList", + "chainId", + "gas", + "gasPrice", + "input", + "nonce", + "r", + "s", + "type", + "value", + "yParity" + ], + "properties": { + "type": { + "title": "type", + "type": "string", + "pattern": "^0x([0-9,a-f,A-F]?){1,2}$" + }, + "nonce": { + "title": "nonce", + "type": "string", + "pattern": "^0x([1-9a-f]+[0-9a-f]*|0)$" + }, + "to": { + "title": "to address", + "type": "string", + "pattern": "^0x[0-9,a-f,A-F]{40}$" + }, + "gas": { + "title": "gas limit", + "type": "string", + "pattern": "^0x([1-9a-f]+[0-9a-f]*|0)$" + }, + "value": { + "title": "value", + "type": "string", + "pattern": "^0x([1-9a-f]+[0-9a-f]*|0)$" + }, + "input": { + "title": "input data", + "type": "string", + "pattern": "^0x[0-9a-f]*$" + }, + "gasPrice": { + "title": "gas price", + "type": "string", + "pattern": "^0x([1-9a-f]+[0-9a-f]*|0)$", + "description": "The gas price willing to be paid by the sender in wei" + }, + "accessList": { + "title": "accessList", + "type": "array", + "description": "EIP-2930 access list", + "items": { + "title": "Access list entry", + "type": "object", + "properties": { + "address": { + "title": "hex encoded address", + "type": "string", + "pattern": "^0x[0-9,a-f,A-F]{40}$" + }, + "storageKeys": { + "type": "array", + "items": { + "title": "32 byte hex value", + "type": "string", + "pattern": "^0x[0-9a-f]{64}$" + } + } + } + } + }, + "chainId": { + "title": "chainId", + "type": "string", + "pattern": "^0x([1-9a-f]+[0-9a-f]*|0)$", + "description": "Chain ID that this transaction is valid on." + }, + "yParity": { + "title": "yParity", + "type": "string", + "pattern": "^0x([1-9a-f]+[0-9a-f]*|0)$", + "description": "The parity (0 for even, 1 for odd) of the y-value of the secp256k1 signature." + }, + "r": { + "title": "r", + "type": "string", + "pattern": "^0x([1-9a-f]+[0-9a-f]*|0)$" + }, + "s": { + "title": "s", + "type": "string", + "pattern": "^0x([1-9a-f]+[0-9a-f]*|0)$" + } + } + }, + { + "title": "Signed Legacy Transaction", + "type": "object", + "required": [ + "gas", + "gasPrice", + "input", + "nonce", + "r", + "s", + "type", + "v", + "value" + ], + "properties": { + "type": { + "title": "type", + "type": "string", + "pattern": "^0x([0-9,a-f,A-F]?){1,2}$" + }, + "nonce": { + "title": "nonce", + "type": "string", + "pattern": "^0x([1-9a-f]+[0-9a-f]*|0)$" + }, + "to": { + "title": "to address", + "type": "string", + "pattern": "^0x[0-9,a-f,A-F]{40}$" + }, + "gas": { + "title": "gas limit", + "type": "string", + "pattern": "^0x([1-9a-f]+[0-9a-f]*|0)$" + }, + "value": { + "title": "value", + "type": "string", + "pattern": "^0x([1-9a-f]+[0-9a-f]*|0)$" + }, + "input": { + "title": "input data", + "type": "string", + "pattern": "^0x[0-9a-f]*$" + }, + "gasPrice": { + "title": "gas price", + "type": "string", + "pattern": "^0x([1-9a-f]+[0-9a-f]*|0)$", + "description": "The gas price willing to be paid by the sender in wei" + }, + "chainId": { + "title": "chainId", + "type": "string", + "pattern": "^0x([1-9a-f]+[0-9a-f]*|0)$", + "description": "Chain ID that this transaction is valid on." + }, + "v": { + "title": "v", + "type": "string", + "pattern": "^0x([1-9a-f]+[0-9a-f]*|0)$" + }, + "r": { + "title": "r", + "type": "string", + "pattern": "^0x([1-9a-f]+[0-9a-f]*|0)$" + }, + "s": { + "title": "s", + "type": "string", + "pattern": "^0x([1-9a-f]+[0-9a-f]*|0)$" + } + } + } + ], + "properties": { + "blockHash": { + "title": "block hash", + "type": "string", + "pattern": "^0x[0-9a-f]{64}$" + }, + "blockNumber": { + "title": "block number", + "type": "string", + "pattern": "^0x([1-9a-f]+[0-9a-f]*|0)$" + }, + "from": { + "title": "from address", + "type": "string", + "pattern": "^0x[0-9,a-f,A-F]{40}$" + }, + "hash": { + "title": "transaction hash", + "type": "string", + "pattern": "^0x[0-9a-f]{64}$" + }, + "transactionIndex": { + "title": "transaction index", + "type": "string", + "pattern": "^0x([1-9a-f]+[0-9a-f]*|0)$" + } + } + } + } + }, + { + "name": "eth_getTransactionReceipt", + "summary": "Returns the receipt of a transaction by transaction hash.", + "params": [ + { + "name": "Transaction hash", + "schema": { + "title": "32 byte hex value", + "type": "string", + "pattern": "^0x[0-9a-f]{64}$" + } + } + ], + "result": { + "name": "Receipt Information", + "schema": { + "type": "object", + "title": "Receipt info", + "required": [ + "blockHash", + "blockNumber", + "from", + "cumulativeGasUsed", + "gasUsed", + "logs", + "logsBloom", + "transactionHash", + "transactionIndex", + "effectiveGasPrice" + ], + "properties": { + "transactionHash": { + "title": "transaction hash", + "type": "string", + "pattern": "^0x[0-9a-f]{64}$" + }, + "transactionIndex": { + "title": "transaction index", + "type": "string", + "pattern": "^0x([1-9a-f]+[0-9a-f]*|0)$" + }, + "blockHash": { + "title": "block hash", + "type": "string", + "pattern": "^0x[0-9a-f]{64}$" + }, + "blockNumber": { + "title": "block number", + "type": "string", + "pattern": "^0x([1-9a-f]+[0-9a-f]*|0)$" + }, + "from": { + "title": "from", + "type": "string", + "pattern": "^0x[0-9,a-f,A-F]{40}$" + }, + "to": { + "title": "to", + "type": "string", + "pattern": "^0x[0-9,a-f,A-F]{40}$", + "description": "Address of the receiver or null in a contract creation transaction." + }, + "cumulativeGasUsed": { + "title": "cumulative gas used", + "type": "string", + "pattern": "^0x([1-9a-f]+[0-9a-f]*|0)$", + "description": "The sum of gas used by this transaction and all preceding transactions in the same block." + }, + "gasUsed": { + "title": "gas used", + "type": "string", + "pattern": "^0x([1-9a-f]+[0-9a-f]*|0)$", + "description": "The amount of gas used for this specific transaction alone." + }, + "contractAddress": { + "title": "contract address", + "description": "The contract address created, if the transaction was a contract creation, otherwise null.", + "oneOf": [ + { + "title": "hex encoded address", + "type": "string", + "pattern": "^0x[0-9,a-f,A-F]{40}$" + }, + { + "name": null, + "type": "null" + } + ] + }, + "logs": { + "title": "logs", + "type": "array", + "items": { + "title": "log", + "type": "object", + "required": [ + "transactionHash" + ], + "properties": { + "removed": { + "title": "removed", + "type": "boolean" + }, + "logIndex": { + "title": "log index", + "type": "string", + "pattern": "^0x([1-9a-f]+[0-9a-f]*|0)$" + }, + "transactionIndex": { + "title": "transaction index", + "type": "string", + "pattern": "^0x([1-9a-f]+[0-9a-f]*|0)$" + }, + "transactionHash": { + "title": "transaction hash", + "type": "string", + "pattern": "^0x[0-9a-f]{64}$" + }, + "blockHash": { + "title": "block hash", + "type": "string", + "pattern": "^0x[0-9a-f]{64}$" + }, + "blockNumber": { + "title": "block number", + "type": "string", + "pattern": "^0x([1-9a-f]+[0-9a-f]*|0)$" + }, + "address": { + "title": "address", + "type": "string", + "pattern": "^0x[0-9,a-f,A-F]{40}$" + }, + "data": { + "title": "data", + "type": "string", + "pattern": "^0x[0-9a-f]*$" + }, + "topics": { + "title": "topics", + "type": "array", + "items": { + "title": "32 hex encoded bytes", + "type": "string", + "pattern": "^0x[0-9a-f]{64}$" + } + } + } + } + }, + "logsBloom": { + "title": "logs bloom", + "type": "string", + "pattern": "^0x[0-9a-f]{512}$" + }, + "root": { + "title": "state root", + "type": "string", + "pattern": "^0x[0-9a-f]{64}$", + "description": "The post-transaction state root. Only specified for transactions included before the Byzantium upgrade." + }, + "status": { + "title": "status", + "type": "string", + "pattern": "^0x([1-9a-f]+[0-9a-f]*|0)$", + "description": "Either 1 (success) or 0 (failure). Only specified for transactions included after the Byzantium upgrade." + }, + "effectiveGasPrice": { + "title": "effective gas price", + "type": "string", + "pattern": "^0x([1-9a-f]+[0-9a-f]*|0)$", + "description": "The actual value per gas deducted from the senders account. Before EIP-1559, this is equal to the transaction's gas price. After, it is equal to baseFeePerGas + min(maxFeePerGas - baseFeePerGas, maxPriorityFeePerGas)." + } + } + } + } + }, + { + "name": "debug_getRawHeader", + "summary": "Returns an RLP-encoded header.", + "params": [ + { + "name": "Block", + "required": true, + "schema": { + "title": "Block number or tag", + "oneOf": [ + { + "title": "Block number", + "type": "string", + "pattern": "^0x([1-9a-f]+[0-9a-f]*|0)$" + }, + { + "title": "Block tag", + "type": "string", + "enum": [ + "earliest", + "finalized", + "safe", + "latest", + "pending" + ], + "description": "`earliest`: The lowest numbered block the client has available; `finalized`: The most recent crypto-economically secure block, cannot be re-orged outside of manual intervention driven by community coordination; `safe`: The most recent block that is safe from re-orgs under honest majority and certain synchronicity assumptions; `latest`: The most recent block in the canonical chain observed by the client, this block may be re-orged out of the canonical chain even under healthy/normal conditions; `pending`: A sample next block built by the client on top of `latest` and containing the set of transactions usually taken from local mempool. Before the merge transition is finalized, any call querying for `finalized` or `safe` block MUST be responded to with `-39001: Unknown block` error" + } + ] + } + } + ], + "result": { + "name": "Header RLP", + "schema": { + "title": "hex encoded bytes", + "type": "string", + "pattern": "^0x[0-9a-f]*$" + } + } + }, + { + "name": "debug_getRawBlock", + "summary": "Returns an RLP-encoded block.", + "params": [ + { + "name": "Block", + "required": true, + "schema": { + "title": "Block number or tag", + "oneOf": [ + { + "title": "Block number", + "type": "string", + "pattern": "^0x([1-9a-f]+[0-9a-f]*|0)$" + }, + { + "title": "Block tag", + "type": "string", + "enum": [ + "earliest", + "finalized", + "safe", + "latest", + "pending" + ], + "description": "`earliest`: The lowest numbered block the client has available; `finalized`: The most recent crypto-economically secure block, cannot be re-orged outside of manual intervention driven by community coordination; `safe`: The most recent block that is safe from re-orgs under honest majority and certain synchronicity assumptions; `latest`: The most recent block in the canonical chain observed by the client, this block may be re-orged out of the canonical chain even under healthy/normal conditions; `pending`: A sample next block built by the client on top of `latest` and containing the set of transactions usually taken from local mempool. Before the merge transition is finalized, any call querying for `finalized` or `safe` block MUST be responded to with `-39001: Unknown block` error" + } + ] + } + } + ], + "result": { + "name": "Block RLP", + "schema": { + "title": "hex encoded bytes", + "type": "string", + "pattern": "^0x[0-9a-f]*$" + } + } + }, + { + "name": "debug_getRawTransaction", + "summary": "Returns an array of EIP-2718 binary-encoded transactions.", + "params": [ + { + "name": "Transaction hash", + "required": true, + "schema": { + "title": "32 byte hex value", + "type": "string", + "pattern": "^0x[0-9a-f]{64}$" + } + } + ], + "result": { + "name": "EIP-2718 binary-encoded transaction", + "schema": { + "title": "hex encoded bytes", + "type": "string", + "pattern": "^0x[0-9a-f]*$" + } + } + }, + { + "name": "debug_getRawReceipts", + "summary": "Returns an array of EIP-2718 binary-encoded receipts.", + "params": [ + { + "name": "Block", + "required": true, + "schema": { + "title": "Block number or tag", + "oneOf": [ + { + "title": "Block number", + "type": "string", + "pattern": "^0x([1-9a-f]+[0-9a-f]*|0)$" + }, + { + "title": "Block tag", + "type": "string", + "enum": [ + "earliest", + "finalized", + "safe", + "latest", + "pending" + ], + "description": "`earliest`: The lowest numbered block the client has available; `finalized`: The most recent crypto-economically secure block, cannot be re-orged outside of manual intervention driven by community coordination; `safe`: The most recent block that is safe from re-orgs under honest majority and certain synchronicity assumptions; `latest`: The most recent block in the canonical chain observed by the client, this block may be re-orged out of the canonical chain even under healthy/normal conditions; `pending`: A sample next block built by the client on top of `latest` and containing the set of transactions usually taken from local mempool. Before the merge transition is finalized, any call querying for `finalized` or `safe` block MUST be responded to with `-39001: Unknown block` error" + } + ] + } + } + ], + "result": { + "name": "Receipts", + "schema": { + "title": "Receipt array", + "type": "array", + "items": { + "title": "hex encoded bytes", + "type": "string", + "pattern": "^0x[0-9a-f]*$" + } + } + } + }, + { + "name": "debug_getBadBlocks", + "summary": "Returns an array of recent bad blocks that the client has seen on the network.", + "params": [], + "result": { + "name": "Blocks", + "schema": { + "title": "Bad block array", + "type": "array", + "items": { + "title": "Bad block", + "type": "object", + "required": [ + "block", + "hash", + "rlp" + ], + "properties": { + "block": { + "title": "Block", + "type": "string", + "pattern": "^0x[0-9a-f]*$" + }, + "hash": { + "title": "Hash", + "type": "string", + "pattern": "^0x[0-9a-f]{64}$" + }, + "rlp": { + "title": "RLP", + "type": "string", + "pattern": "^0x[0-9a-f]*$" + } + } + } + } + } + } + ], + "components": {} } diff --git a/node/impl/full/eth.go b/node/impl/full/eth.go index 2814c2cee..1fb33c790 100644 --- a/node/impl/full/eth.go +++ b/node/impl/full/eth.go @@ -980,17 +980,9 @@ func (e *EthEvent) installEthFilterSpec(ctx context.Context, filterSpec *ethtype addresses = append(addresses, a) } - for idx, vals := range filterSpec.Topics { - if len(vals) == 0 { - continue - } - // Ethereum topics are emitted using `LOG{0..4}` opcodes resulting in topics1..4 - key := fmt.Sprintf("topic%d", idx+1) - for _, v := range vals { - buf := make([]byte, len(v[:])) - copy(buf, v[:]) - keys[key] = append(keys[key], buf) - } + keys, err := parseEthTopics(filterSpec.Topics) + if err != nil { + return nil, err } return e.EventFilterManager.Install(ctx, minHeight, maxHeight, tipsetCid, addresses, keys) @@ -1016,6 +1008,8 @@ func (e *EthEvent) EthNewFilter(ctx context.Context, filterSpec *ethtypes.EthFil return ethtypes.EthFilterID{}, err } + fmt.Printf("REMOVEME: EthNewFilter.f=%+v\n", f) + return ethtypes.EthFilterID(f.ID()), nil } @@ -1139,14 +1133,12 @@ func (e *EthEvent) EthSubscribe(ctx context.Context, eventType string, params *e case EthSubscribeEventTypeLogs: keys := map[string][][]byte{} if params != nil { - for idx, vals := range params.Topics { - // Ethereum topics are emitted using `LOG{0..4}` opcodes resulting in topics1..4 - key := fmt.Sprintf("topic%d", idx+1) - keyvals := make([][]byte, len(vals)) - for i, v := range vals { - keyvals[i] = v[:] - } - keys[key] = keyvals + var err error + keys, err = parseEthTopics(params.Topics) + if err != nil { + // clean up any previous filters added and stop the sub + _, _ = e.EthUnsubscribe(ctx, sub.id) + return nil, err } } @@ -1233,7 +1225,10 @@ func ethFilterResultFromEvents(evs []*filter.CollectedEvent, sa StateAPI) (*etht var err error for _, entry := range ev.Entries { - value := ethtypes.EthBytes(leftpad32(entry.Value)) // value has already been cbor-decoded but see https://github.com/filecoin-project/ref-fvm/issues/1345 + value, err := cborDecodeTopicValue(entry.Value) + if err != nil { + return nil, err + } if entry.Key == ethtypes.EthTopic1 || entry.Key == ethtypes.EthTopic2 || entry.Key == ethtypes.EthTopic3 || entry.Key == ethtypes.EthTopic4 { log.Topics = append(log.Topics, value) } else { @@ -1778,9 +1773,8 @@ func newEthTxReceipt(ctx context.Context, tx ethtypes.EthTx, lookup *api.MsgLook for _, entry := range evt.Entries { value, err := cborDecodeTopicValue(entry.Value) if err != nil { - return api.EthTxReceipt{}, xerrors.Errorf("failed to decode entry topic value: %w", err) + return api.EthTxReceipt{}, xerrors.Errorf("failed to decode event log value: %w", err) } - if entry.Key == ethtypes.EthTopic1 || entry.Key == ethtypes.EthTopic2 || entry.Key == ethtypes.EthTopic3 || entry.Key == ethtypes.EthTopic4 { l.Topics = append(l.Topics, value) } else { @@ -1891,10 +1885,6 @@ func EthTxHashGC(ctx context.Context, retentionDays int, manager *EthTxHashManag } } -// TODO we could also emit full EVM words from the EVM runtime, but not doing so -// makes the contract slightly cheaper (and saves storage bytes), at the expense -// of having to left pad in the API, which is a pretty acceptable tradeoff at -// face value. There may be other protocol implications to consider. func leftpad32(orig []byte) []byte { needed := 32 - len(orig) if needed <= 0 { @@ -1905,6 +1895,24 @@ func leftpad32(orig []byte) []byte { return ret } +func trimLeadingZeros(b []byte) []byte { + for i := range b { + if b[i] != 0 { + return b[i:] + } + } + return []byte{} +} + +func cborEncodeTopicValue(orig []byte) ([]byte, error) { + var buf bytes.Buffer + err := cbg.WriteByteArray(&buf, trimLeadingZeros(orig)) + if err != nil { + return nil, err + } + return buf.Bytes(), nil +} + func cborDecodeTopicValue(orig []byte) ([]byte, error) { if len(orig) == 0 { return orig, nil @@ -1915,3 +1923,22 @@ func cborDecodeTopicValue(orig []byte) ([]byte, error) { } return leftpad32(decoded), nil } + +func parseEthTopics(topics ethtypes.EthTopicSpec) (map[string][][]byte, error) { + keys := map[string][][]byte{} + for idx, vals := range topics { + if len(vals) == 0 { + continue + } + // Ethereum topics are emitted using `LOG{0..4}` opcodes resulting in topics1..4 + key := fmt.Sprintf("topic%d", idx+1) + for _, v := range vals { + encodedVal, err := cborEncodeTopicValue(v[:]) + if err != nil { + return nil, xerrors.Errorf("failed to encode topic value") + } + keys[key] = append(keys[key], encodedVal) + } + } + return keys, nil +} From 9fed750f9ddd98152dd41c99b7c307e274176a44 Mon Sep 17 00:00:00 2001 From: Ian Davis Date: Mon, 23 Jan 2023 17:09:35 +0000 Subject: [PATCH 03/30] Add further tests --- itests/eth_conformance_test.go | 37 ++++++++++++++++++++++++++++++++-- 1 file changed, 35 insertions(+), 2 deletions(-) diff --git a/itests/eth_conformance_test.go b/itests/eth_conformance_test.go index 7cd69bfe6..c0c254d79 100644 --- a/itests/eth_conformance_test.go +++ b/itests/eth_conformance_test.go @@ -12,7 +12,6 @@ import ( "github.com/go-openapi/spec" "github.com/gregdhill/go-openrpc/parse" orpctypes "github.com/gregdhill/go-openrpc/types" - "github.com/ipfs/go-cid" manet "github.com/multiformats/go-multiaddr/net" "github.com/stretchr/testify/require" "github.com/xeipuuv/gojsonschema" @@ -72,7 +71,6 @@ func TestEthOpenRPCConformance(t *testing.T) { schemas := make(map[string]spec.Schema) for _, method := range specParsed.Methods { - t.Logf("method: %s", method.Name) if method.Result != nil { schemas[method.Name] = method.Result.Schema } @@ -247,6 +245,41 @@ func TestEthOpenRPCConformance(t *testing.T) { }, }, + { + method: "eth_getBlockTransactionCountByHash", + call: func(a *ethAPIRaw) (json.RawMessage, error) { + return ethapi.EthGetBlockTransactionCountByHash(context.Background(), blockHashWithMessage) + }, + }, + + { + method: "eth_getBlockTransactionCountByNumber", + call: func(a *ethAPIRaw) (json.RawMessage, error) { + return ethapi.EthGetBlockTransactionCountByNumber(context.Background(), blockNumberWithMessage) + }, + }, + + { + method: "eth_getTransactionByBlockHashAndIndex", + call: func(a *ethAPIRaw) (json.RawMessage, error) { + return ethapi.EthGetTransactionByBlockHashAndIndex(context.Background(), blockHashWithMessage, ethtypes.EthUint64(0)) + }, + }, + + { + method: "eth_getTransactionByBlockNumberAndIndex", + call: func(a *ethAPIRaw) (json.RawMessage, error) { + return ethapi.EthGetTransactionByBlockNumberAndIndex(context.Background(), blockNumberWithMessage, ethtypes.EthUint64(0)) + }, + }, + + { + method: "eth_getTransactionByHash", + call: func(a *ethAPIRaw) (json.RawMessage, error) { + return ethapi.EthGetTransactionByHash(context.Background(), &messageWithEvents) + }, + }, + // { // method: "eth_call", // call: func(a *ethAPIRaw) (json.RawMessage, error) { From 7eca3e4853d13986a020579d2bfcce45e31756f7 Mon Sep 17 00:00:00 2001 From: Ian Davis Date: Mon, 23 Jan 2023 17:22:25 +0000 Subject: [PATCH 04/30] Start eth_sendRawTransaction test --- itests/eth_conformance_test.go | 53 ++++++++++++++++++++++++++++++++-- 1 file changed, 51 insertions(+), 2 deletions(-) diff --git a/itests/eth_conformance_test.go b/itests/eth_conformance_test.go index c0c254d79..6d155d712 100644 --- a/itests/eth_conformance_test.go +++ b/itests/eth_conformance_test.go @@ -18,7 +18,10 @@ import ( "github.com/filecoin-project/go-address" "github.com/filecoin-project/go-jsonrpc" + "github.com/filecoin-project/go-state-types/big" + "github.com/filecoin-project/lotus/build" + "github.com/filecoin-project/lotus/chain/types" "github.com/filecoin-project/lotus/chain/types/ethtypes" "github.com/filecoin-project/lotus/itests/kit" ) @@ -97,6 +100,8 @@ func TestEthOpenRPCConformance(t *testing.T) { result := client.EVM().DeployContract(ctx, senderAddr, contractBin) + // set up some standard values for tests + contractAddr, err := address.NewIDAddress(result.ActorID) require.NoError(t, err) @@ -129,6 +134,45 @@ func TestEthOpenRPCConformance(t *testing.T) { break } + // install contract + contract2Hex, err := os.ReadFile("./contracts/SimpleCoin.hex") + require.NoError(t, err) + + contract, err := hex.DecodeString(string(contract2Hex)) + require.NoError(t, err) + + // create a new Ethereum account + key, ethAddr, deployer := client.EVM().NewAccount() + _, ethAddr2, _ := client.EVM().NewAccount() + + kit.SendFunds(ctx, t, client, deployer, types.FromFil(1000)) + + gaslimit, err := client.EthEstimateGas(ctx, ethtypes.EthCall{ + From: ðAddr, + Data: contract, + }) + require.NoError(t, err) + + maxPriorityFeePerGas, err := client.EthMaxPriorityFeePerGas(ctx) + require.NoError(t, err) + + tx := ethtypes.EthTxArgs{ + ChainID: build.Eip155ChainId, + Value: big.NewInt(100), + Nonce: 0, + To: ðAddr2, + MaxFeePerGas: types.NanoFil, + MaxPriorityFeePerGas: big.Int(maxPriorityFeePerGas), + GasLimit: int(gaslimit), + V: big.Zero(), + R: big.Zero(), + S: big.Zero(), + } + + client.EVM().SignTransaction(&tx, key.PrivateKey) + signed, err := tx.ToRlpSignedMsg() + require.NoError(t, err) + // create a json-rpc client that returns raw json responses var ethapi ethAPIRaw @@ -140,8 +184,6 @@ func TestEthOpenRPCConformance(t *testing.T) { require.NoError(t, err) defer closer() - // maxPriorityFeePerGas, err := client.EthMaxPriorityFeePerGas(ctx) - testCases := []struct { method string variant string // suffix applied to the test name to distinguish different variants of a method call @@ -280,6 +322,13 @@ func TestEthOpenRPCConformance(t *testing.T) { }, }, + { + method: "eth_sendRawTransaction", + call: func(a *ethAPIRaw) (json.RawMessage, error) { + return ethapi.EthSendRawTransaction(context.Background(), signed) + }, + }, + // { // method: "eth_call", // call: func(a *ethAPIRaw) (json.RawMessage, error) { From c0c8ad5510e13b286d20d0b6fc31a968cdd499b6 Mon Sep 17 00:00:00 2001 From: Ian Davis Date: Tue, 24 Jan 2023 11:10:38 +0000 Subject: [PATCH 05/30] Test more functions --- itests/eth_conformance_test.go | 69 +++++++++++++++++++++++++++++++++- 1 file changed, 67 insertions(+), 2 deletions(-) diff --git a/itests/eth_conformance_test.go b/itests/eth_conformance_test.go index 6d155d712..9294a39dc 100644 --- a/itests/eth_conformance_test.go +++ b/itests/eth_conformance_test.go @@ -6,6 +6,7 @@ import ( "encoding/hex" "encoding/json" "os" + "strings" "testing" "time" @@ -105,6 +106,20 @@ func TestEthOpenRPCConformance(t *testing.T) { contractAddr, err := address.NewIDAddress(result.ActorID) require.NoError(t, err) + pendingTransactionFilterID, err := client.EthNewPendingTransactionFilter(ctx) + require.NoError(t, err) + + blockFilterID, err := client.EthNewBlockFilter(ctx) + require.NoError(t, err) + + filterAllLogs := newEthFilterBuilder().FromBlockEpoch(0).Filter() + + logFilterID, err := client.EthNewFilter(ctx, filterAllLogs) + require.NoError(t, err) + + uninstallableFilterID, err := client.EthNewFilter(ctx, filterAllLogs) + require.NoError(t, err) + // send a message that exercises event logs messages := invokeAndWaitUntilAllOnChain(t, client, []Invocation{ { @@ -280,8 +295,7 @@ func TestEthOpenRPCConformance(t *testing.T) { }, { - method: "eth_getBlockByNumber", - variant: "latest", + method: "eth_getBlockByNumber", call: func(a *ethAPIRaw) (json.RawMessage, error) { return ethapi.EthGetBlockByNumber(context.Background(), blockNumberWithMessage.Hex(), true) }, @@ -329,6 +343,51 @@ func TestEthOpenRPCConformance(t *testing.T) { }, }, + { + method: "eth_getLogs", + call: func(a *ethAPIRaw) (json.RawMessage, error) { + return ethapi.EthGetLogs(context.Background(), filterAllLogs) + }, + }, + + { + method: "eth_getFilterChanges", + variant: "pendingtransaction", + call: func(a *ethAPIRaw) (json.RawMessage, error) { + return a.EthGetFilterChanges(ctx, pendingTransactionFilterID) + }, + }, + + { + method: "eth_getFilterChanges", + variant: "block", + call: func(a *ethAPIRaw) (json.RawMessage, error) { + return a.EthGetFilterChanges(ctx, blockFilterID) + }, + }, + + { + method: "eth_getFilterChanges", + variant: "logs", + call: func(a *ethAPIRaw) (json.RawMessage, error) { + return a.EthGetFilterChanges(ctx, logFilterID) + }, + }, + + { + method: "eth_getFilterLogs", + call: func(a *ethAPIRaw) (json.RawMessage, error) { + return a.EthGetFilterLogs(ctx, logFilterID) + }, + }, + + { + method: "eth_uninstallFilter", + call: func(a *ethAPIRaw) (json.RawMessage, error) { + return a.EthUninstallFilter(ctx, uninstallableFilterID) + }, + }, + // { // method: "eth_call", // call: func(a *ethAPIRaw) (json.RawMessage, error) { @@ -371,6 +430,12 @@ func TestEthOpenRPCConformance(t *testing.T) { require.NoError(t, err) if !result.Valid() { + if len(result.Errors()) == 1 && strings.Contains(result.Errors()[0].String(), "Must validate one and only one schema (oneOf)") { + // Ignore this error, since it seems the openrpc spec can't handle it + // New transaction and block filters have the same schema: an array of 32 byte hashes + return + } + niceRespJson, err := json.MarshalIndent(resp, "", " ") if err == nil { t.Logf("response was %s", niceRespJson) From 8daf22c3ff150e6c8d196778b3dae4dc9bad7167 Mon Sep 17 00:00:00 2001 From: Ian Davis Date: Tue, 24 Jan 2023 11:27:18 +0000 Subject: [PATCH 06/30] Test and fix eth_FeeHistory --- chain/types/ethtypes/eth_types.go | 2 +- itests/eth_conformance_test.go | 25 ++++++++++++++++--------- node/impl/full/eth.go | 2 +- 3 files changed, 18 insertions(+), 11 deletions(-) diff --git a/chain/types/ethtypes/eth_types.go b/chain/types/ethtypes/eth_types.go index a9c537f06..c9df41063 100644 --- a/chain/types/ethtypes/eth_types.go +++ b/chain/types/ethtypes/eth_types.go @@ -433,7 +433,7 @@ func (h EthHash) ToCid() cid.Cid { } type EthFeeHistory struct { - OldestBlock uint64 `json:"oldestBlock"` + OldestBlock EthUint64 `json:"oldestBlock"` BaseFeePerGas []EthBigInt `json:"baseFeePerGas"` GasUsedRatio []float64 `json:"gasUsedRatio"` Reward *[][]EthBigInt `json:"reward,omitempty"` diff --git a/itests/eth_conformance_test.go b/itests/eth_conformance_test.go index 9294a39dc..9e5541786 100644 --- a/itests/eth_conformance_test.go +++ b/itests/eth_conformance_test.go @@ -398,15 +398,22 @@ func TestEthOpenRPCConformance(t *testing.T) { // }, // }, - // { - // method: "eth_estimateGas", - // call: func(a *ethAPIRaw) (json.RawMessage, error) { - // return ethapi.EthEstimateGas(context.Background(), ethtypes.EthCall{ - // From: &senderEthAddr, - // Data: contractBin, - // }) - // }, - // }, + { + method: "eth_estimateGas", + call: func(a *ethAPIRaw) (json.RawMessage, error) { + return ethapi.EthEstimateGas(context.Background(), ethtypes.EthCall{ + From: ðAddr, + Data: contractBin, + }) + }, + }, + + { + method: "eth_feeHistory", + call: func(a *ethAPIRaw) (json.RawMessage, error) { + return ethapi.EthFeeHistory(context.Background(), ethtypes.EthUint64(2), "", nil) + }, + }, } for _, tc := range testCases { diff --git a/node/impl/full/eth.go b/node/impl/full/eth.go index 1fb33c790..11088cdeb 100644 --- a/node/impl/full/eth.go +++ b/node/impl/full/eth.go @@ -640,7 +640,7 @@ func (a *EthModule) EthFeeHistory(ctx context.Context, blkCount ethtypes.EthUint } return ethtypes.EthFeeHistory{ - OldestBlock: oldestBlkHeight, + OldestBlock: ethtypes.EthUint64(oldestBlkHeight), BaseFeePerGas: baseFeeArray, GasUsedRatio: gasUsedRatioArray, }, nil From 2a73e1c2e1578d5e71c3bdd4e1e720e9f1dcd448 Mon Sep 17 00:00:00 2001 From: Ian Davis Date: Tue, 24 Jan 2023 13:12:31 +0000 Subject: [PATCH 07/30] Add test for eth_getStorageAt --- itests/eth_conformance_test.go | 167 ++++++++++++++++++-------------- itests/eth_filter_test.go | 10 +- itests/eth_transactions_test.go | 3 - 3 files changed, 99 insertions(+), 81 deletions(-) diff --git a/itests/eth_conformance_test.go b/itests/eth_conformance_test.go index 9e5541786..ce920092f 100644 --- a/itests/eth_conformance_test.go +++ b/itests/eth_conformance_test.go @@ -24,6 +24,7 @@ import ( "github.com/filecoin-project/lotus/build" "github.com/filecoin-project/lotus/chain/types" "github.com/filecoin-project/lotus/chain/types/ethtypes" + "github.com/filecoin-project/lotus/chain/wallet/key" "github.com/filecoin-project/lotus/itests/kit" ) @@ -95,15 +96,11 @@ func TestEthOpenRPCConformance(t *testing.T) { contractBin, err := hex.DecodeString(string(contractHex)) require.NoError(t, err) - senderAddr, err := client.EVM().WalletDefaultAddress(ctx) - require.NoError(t, err) - // senderEthAddr := getContractEthAddress(ctx, t, client, senderAddr) + senderKey, senderEthAddr, senderFilAddr := client.EVM().NewAccount() + _, receiverEthAddr, _ := client.EVM().NewAccount() + kit.SendFunds(ctx, t, client, senderFilAddr, types.FromFil(1000)) - result := client.EVM().DeployContract(ctx, senderAddr, contractBin) - - // set up some standard values for tests - - contractAddr, err := address.NewIDAddress(result.ActorID) + deployerAddr, err := client.EVM().WalletDefaultAddress(ctx) require.NoError(t, err) pendingTransactionFilterID, err := client.EthNewPendingTransactionFilter(ctx) @@ -120,73 +117,15 @@ func TestEthOpenRPCConformance(t *testing.T) { uninstallableFilterID, err := client.EthNewFilter(ctx, filterAllLogs) require.NoError(t, err) - // send a message that exercises event logs - messages := invokeAndWaitUntilAllOnChain(t, client, []Invocation{ - { - Sender: senderAddr, - Target: contractAddr, - Selector: EventMatrixContract.Fn["logEventThreeIndexedWithData"], - Data: packUint64Values(44, 27, 19, 12), - }, - }) + rawSignedEthTx := createRawSignedEthTx(ctx, t, client, senderEthAddr, receiverEthAddr, senderKey, contractBin) - require.NotEmpty(t, messages) - - var messageWithEvents ethtypes.EthHash - var blockHashWithMessage ethtypes.EthHash - var blockNumberWithMessage ethtypes.EthUint64 - - for k, mts := range messages { - messageWithEvents = k - ts := mts.ts - blockNumberWithMessage = ethtypes.EthUint64(ts.Height()) - - tsCid, err := ts.Key().Cid() - require.NoError(t, err) - - blockHashWithMessage, err = ethtypes.EthHashFromCid(tsCid) - require.NoError(t, err) - break - } - - // install contract - contract2Hex, err := os.ReadFile("./contracts/SimpleCoin.hex") + result := client.EVM().DeployContract(ctx, deployerAddr, contractBin) + contractAddr, err := address.NewIDAddress(result.ActorID) require.NoError(t, err) - contract, err := hex.DecodeString(string(contract2Hex)) - require.NoError(t, err) + contractEthAddr := ethtypes.EthAddress(result.EthAddress) - // create a new Ethereum account - key, ethAddr, deployer := client.EVM().NewAccount() - _, ethAddr2, _ := client.EVM().NewAccount() - - kit.SendFunds(ctx, t, client, deployer, types.FromFil(1000)) - - gaslimit, err := client.EthEstimateGas(ctx, ethtypes.EthCall{ - From: ðAddr, - Data: contract, - }) - require.NoError(t, err) - - maxPriorityFeePerGas, err := client.EthMaxPriorityFeePerGas(ctx) - require.NoError(t, err) - - tx := ethtypes.EthTxArgs{ - ChainID: build.Eip155ChainId, - Value: big.NewInt(100), - Nonce: 0, - To: ðAddr2, - MaxFeePerGas: types.NanoFil, - MaxPriorityFeePerGas: big.Int(maxPriorityFeePerGas), - GasLimit: int(gaslimit), - V: big.Zero(), - R: big.Zero(), - S: big.Zero(), - } - - client.EVM().SignTransaction(&tx, key.PrivateKey) - signed, err := tx.ToRlpSignedMsg() - require.NoError(t, err) + messageWithEvents, blockHashWithMessage, blockNumberWithMessage := waitForMessageWithEvents(ctx, t, client, deployerAddr, contractAddr) // create a json-rpc client that returns raw json responses var ethapi ethAPIRaw @@ -339,7 +278,7 @@ func TestEthOpenRPCConformance(t *testing.T) { { method: "eth_sendRawTransaction", call: func(a *ethAPIRaw) (json.RawMessage, error) { - return ethapi.EthSendRawTransaction(context.Background(), signed) + return ethapi.EthSendRawTransaction(context.Background(), rawSignedEthTx) }, }, @@ -402,7 +341,7 @@ func TestEthOpenRPCConformance(t *testing.T) { method: "eth_estimateGas", call: func(a *ethAPIRaw) (json.RawMessage, error) { return ethapi.EthEstimateGas(context.Background(), ethtypes.EthCall{ - From: ðAddr, + From: &senderEthAddr, Data: contractBin, }) }, @@ -414,6 +353,29 @@ func TestEthOpenRPCConformance(t *testing.T) { return ethapi.EthFeeHistory(context.Background(), ethtypes.EthUint64(2), "", nil) }, }, + { + method: "eth_getTransactionCount", + variant: "blocknumber", + call: func(a *ethAPIRaw) (json.RawMessage, error) { + return ethapi.EthGetTransactionCount(context.Background(), senderEthAddr, "0x0") + }, + }, + + { + method: "eth_getCode", + variant: "blocknumber", + call: func(a *ethAPIRaw) (json.RawMessage, error) { + return ethapi.EthGetCode(context.Background(), contractEthAddr, "0x0") + }, + }, + + { + method: "eth_getStorageAt", + variant: "blocknumber", + call: func(a *ethAPIRaw) (json.RawMessage, error) { + return ethapi.EthGetStorageAt(context.Background(), contractEthAddr, ethtypes.EthBytes{0}, "0x0") + }, + }, } for _, tc := range testCases { @@ -464,3 +426,62 @@ func TestEthOpenRPCConformance(t *testing.T) { }) } } + +func createRawSignedEthTx(ctx context.Context, t *testing.T, client *kit.TestFullNode, senderEthAddr ethtypes.EthAddress, receiverEthAddr ethtypes.EthAddress, senderKey *key.Key, contractBin []byte) []byte { + gaslimit, err := client.EthEstimateGas(ctx, ethtypes.EthCall{ + From: &senderEthAddr, + Data: contractBin, + }) + require.NoError(t, err) + + maxPriorityFeePerGas, err := client.EthMaxPriorityFeePerGas(ctx) + require.NoError(t, err) + + tx := ethtypes.EthTxArgs{ + ChainID: build.Eip155ChainId, + Value: big.NewInt(100), + Nonce: 0, + To: &receiverEthAddr, + MaxFeePerGas: types.NanoFil, + MaxPriorityFeePerGas: big.Int(maxPriorityFeePerGas), + GasLimit: int(gaslimit), + V: big.Zero(), + R: big.Zero(), + S: big.Zero(), + } + + client.EVM().SignTransaction(&tx, senderKey.PrivateKey) + signed, err := tx.ToRlpSignedMsg() + require.NoError(t, err) + return signed +} + +func waitForMessageWithEvents(ctx context.Context, t *testing.T, client *kit.TestFullNode, sender address.Address, target address.Address) (ethtypes.EthHash, ethtypes.EthHash, ethtypes.EthUint64) { + // send a message that exercises event logs + messages := invokeAndWaitUntilAllOnChain(t, client, []Invocation{ + { + Sender: sender, + Target: target, + Selector: EventMatrixContract.Fn["logEventThreeIndexedWithData"], + Data: packUint64Values(44, 27, 19, 12), + }, + }) + + require.NotEmpty(t, messages) + + for msgHash, mts := range messages { + ts := mts.ts + blockNumber := ethtypes.EthUint64(ts.Height()) + + tsCid, err := ts.Key().Cid() + require.NoError(t, err) + + blockHash, err := ethtypes.EthHashFromCid(tsCid) + require.NoError(t, err) + return msgHash, blockHash, blockNumber + } + + // should not get here + t.FailNow() + return ethtypes.EthHash{}, ethtypes.EthHash{}, 0 +} diff --git a/itests/eth_filter_test.go b/itests/eth_filter_test.go index aba61f934..bc12bbb30 100644 --- a/itests/eth_filter_test.go +++ b/itests/eth_filter_test.go @@ -1091,7 +1091,7 @@ func TestEthGetLogsWithBlockRanges(t *testing.T) { // Select events for partitioning for _, m := range messages { if bytes.Equal(m.invocation.Selector, EventMatrixContract.Fn["logEventTwoIndexedWithData"]) { - addr := getContractEthAddress(ctx, t, client, m.invocation.Target) + addr := getEthAddress(ctx, t, client, m.invocation.Target) args := unpackUint64Values(m.invocation.Data) require.Equal(3, len(args), "logEventTwoIndexedWithData should have 3 arguments") @@ -1295,7 +1295,7 @@ type msgInTipset struct { reverted bool } -func getContractEthAddress(ctx context.Context, t *testing.T, client *kit.TestFullNode, addr address.Address) ethtypes.EthAddress { +func getEthAddress(ctx context.Context, t *testing.T, client *kit.TestFullNode, addr address.Address) ethtypes.EthAddress { head, err := client.ChainHead(ctx) require.NoError(t, err) @@ -1446,7 +1446,7 @@ func invokeLogFourData(t *testing.T, client *kit.TestFullNode, iterations int) ( messages := invokeAndWaitUntilAllOnChain(t, client, invocations) - ethAddr := getContractEthAddress(ctx, t, client, idAddr) + ethAddr := getEthAddress(ctx, t, client, idAddr) return ethAddr, messages } @@ -1641,8 +1641,8 @@ func invokeEventMatrix(ctx context.Context, t *testing.T, client *kit.TestFullNo } messages := invokeAndWaitUntilAllOnChain(t, client, invocations) - ethAddr1 := getContractEthAddress(ctx, t, client, contract1) - ethAddr2 := getContractEthAddress(ctx, t, client, contract2) + ethAddr1 := getEthAddress(ctx, t, client, contract1) + ethAddr2 := getEthAddress(ctx, t, client, contract2) return ethAddr1, ethAddr2, messages } diff --git a/itests/eth_transactions_test.go b/itests/eth_transactions_test.go index 0c8f1baa5..ab46378f0 100644 --- a/itests/eth_transactions_test.go +++ b/itests/eth_transactions_test.go @@ -100,11 +100,9 @@ func TestLegacyTransaction(t *testing.T) { require.NoError(t, err) _, err = client.EVM().EthSendRawTransaction(ctx, txBytes) require.ErrorContains(t, err, "legacy transaction is not supported") - } func TestContractDeploymentValidSignature(t *testing.T) { - blockTime := 100 * time.Millisecond client, _, ens := kit.EnsembleMinimal(t, kit.MockProofs(), kit.ThroughRPC()) @@ -253,7 +251,6 @@ func TestContractInvocation(t *testing.T) { // Success. require.EqualValues(t, ethtypes.EthUint64(0x1), receipt.Status) - } func deployContractTx(ctx context.Context, client *kit.TestFullNode, ethAddr ethtypes.EthAddress, contract []byte) (*ethtypes.EthTxArgs, error) { From 7acdb49074b03df1964eb00a240b583350a94264 Mon Sep 17 00:00:00 2001 From: Ian Davis Date: Tue, 24 Jan 2023 14:03:31 +0000 Subject: [PATCH 08/30] Add test for eth_call --- itests/eth_conformance_test.go | 27 ++++++++++++++++++--------- 1 file changed, 18 insertions(+), 9 deletions(-) diff --git a/itests/eth_conformance_test.go b/itests/eth_conformance_test.go index ce920092f..10ab1956b 100644 --- a/itests/eth_conformance_test.go +++ b/itests/eth_conformance_test.go @@ -327,15 +327,16 @@ func TestEthOpenRPCConformance(t *testing.T) { }, }, - // { - // method: "eth_call", - // call: func(a *ethAPIRaw) (json.RawMessage, error) { - // return ethapi.EthCall(context.Background(), ethtypes.EthCall{ - // From: &senderEthAddr, - // Data: contractBin, - // }, "logEventZeroData") - // }, - // }, + { + method: "eth_call", + variant: "latest", + call: func(a *ethAPIRaw) (json.RawMessage, error) { + return ethapi.EthCall(context.Background(), ethtypes.EthCall{ + From: &senderEthAddr, + Data: contractBin, + }, "latest") + }, + }, { method: "eth_estimateGas", @@ -376,6 +377,14 @@ func TestEthOpenRPCConformance(t *testing.T) { return ethapi.EthGetStorageAt(context.Background(), contractEthAddr, ethtypes.EthBytes{0}, "0x0") }, }, + + { + method: "eth_getBalance", + variant: "blocknumber", + call: func(a *ethAPIRaw) (json.RawMessage, error) { + return ethapi.EthGetBalance(context.Background(), contractEthAddr, "0x0") + }, + }, } for _, tc := range testCases { From 58cd226d6bbaccd4991e915dd193e44b9394980c Mon Sep 17 00:00:00 2001 From: Ian Davis Date: Tue, 24 Jan 2023 14:46:16 +0000 Subject: [PATCH 09/30] Change EthBlock.Extradata type to EthBytes --- chain/types/ethtypes/eth_types.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/chain/types/ethtypes/eth_types.go b/chain/types/ethtypes/eth_types.go index c9df41063..dfafbd62e 100644 --- a/chain/types/ethtypes/eth_types.go +++ b/chain/types/ethtypes/eth_types.go @@ -144,7 +144,7 @@ type EthBlock struct { GasLimit EthUint64 `json:"gasLimit"` GasUsed EthUint64 `json:"gasUsed"` Timestamp EthUint64 `json:"timestamp"` - Extradata []byte `json:"extraData"` + Extradata EthBytes `json:"extraData"` MixHash EthHash `json:"mixHash"` Nonce EthNonce `json:"nonce"` BaseFeePerGas EthBigInt `json:"baseFeePerGas"` From 7586710395e3cfe2104e4c8197475cd43aef9ba4 Mon Sep 17 00:00:00 2001 From: Ian Davis Date: Thu, 26 Jan 2023 12:10:42 +0000 Subject: [PATCH 10/30] Fix panic in EthGetCode --- itests/eth_conformance_test.go | 209 +++++++++++++++++---------------- node/impl/full/eth.go | 6 +- 2 files changed, 113 insertions(+), 102 deletions(-) diff --git a/itests/eth_conformance_test.go b/itests/eth_conformance_test.go index 10ab1956b..344c8ef65 100644 --- a/itests/eth_conformance_test.go +++ b/itests/eth_conformance_test.go @@ -143,7 +143,7 @@ func TestEthOpenRPCConformance(t *testing.T) { variant string // suffix applied to the test name to distinguish different variants of a method call call func(*ethAPIRaw) (json.RawMessage, error) }{ - // Simple no-argument calls first + // Alphabetical order { method: "eth_accounts", @@ -159,6 +159,17 @@ func TestEthOpenRPCConformance(t *testing.T) { }, }, + { + method: "eth_call", + variant: "latest", + call: func(a *ethAPIRaw) (json.RawMessage, error) { + return ethapi.EthCall(context.Background(), ethtypes.EthCall{ + From: &senderEthAddr, + Data: contractBin, + }, "latest") + }, + }, + { method: "eth_chainId", call: func(a *ethAPIRaw) (json.RawMessage, error) { @@ -166,6 +177,23 @@ func TestEthOpenRPCConformance(t *testing.T) { }, }, + { + method: "eth_estimateGas", + call: func(a *ethAPIRaw) (json.RawMessage, error) { + return ethapi.EthEstimateGas(context.Background(), ethtypes.EthCall{ + From: &senderEthAddr, + Data: contractBin, + }) + }, + }, + + { + method: "eth_feeHistory", + call: func(a *ethAPIRaw) (json.RawMessage, error) { + return ethapi.EthFeeHistory(context.Background(), ethtypes.EthUint64(2), "", nil) + }, + }, + { method: "eth_gasPrice", call: func(a *ethAPIRaw) (json.RawMessage, error) { @@ -174,30 +202,10 @@ func TestEthOpenRPCConformance(t *testing.T) { }, { - method: "eth_maxPriorityFeePerGas", + method: "eth_getBalance", + variant: "blocknumber", call: func(a *ethAPIRaw) (json.RawMessage, error) { - return ethapi.EthMaxPriorityFeePerGas(context.Background()) - }, - }, - - { - method: "eth_newBlockFilter", - call: func(a *ethAPIRaw) (json.RawMessage, error) { - return ethapi.EthNewBlockFilter(context.Background()) - }, - }, - - { - method: "eth_newPendingTransactionFilter", - call: func(a *ethAPIRaw) (json.RawMessage, error) { - return ethapi.EthNewPendingTransactionFilter(context.Background()) - }, - }, - - { - method: "eth_getTransactionReceipt", - call: func(a *ethAPIRaw) (json.RawMessage, error) { - return ethapi.EthGetTransactionReceipt(context.Background(), messageWithEvents) + return ethapi.EthGetBalance(context.Background(), contractEthAddr, "0x0") }, }, @@ -255,37 +263,10 @@ func TestEthOpenRPCConformance(t *testing.T) { }, { - method: "eth_getTransactionByBlockHashAndIndex", + method: "eth_getCode", + variant: "blocknumber", call: func(a *ethAPIRaw) (json.RawMessage, error) { - return ethapi.EthGetTransactionByBlockHashAndIndex(context.Background(), blockHashWithMessage, ethtypes.EthUint64(0)) - }, - }, - - { - method: "eth_getTransactionByBlockNumberAndIndex", - call: func(a *ethAPIRaw) (json.RawMessage, error) { - return ethapi.EthGetTransactionByBlockNumberAndIndex(context.Background(), blockNumberWithMessage, ethtypes.EthUint64(0)) - }, - }, - - { - method: "eth_getTransactionByHash", - call: func(a *ethAPIRaw) (json.RawMessage, error) { - return ethapi.EthGetTransactionByHash(context.Background(), &messageWithEvents) - }, - }, - - { - method: "eth_sendRawTransaction", - call: func(a *ethAPIRaw) (json.RawMessage, error) { - return ethapi.EthSendRawTransaction(context.Background(), rawSignedEthTx) - }, - }, - - { - method: "eth_getLogs", - call: func(a *ethAPIRaw) (json.RawMessage, error) { - return ethapi.EthGetLogs(context.Background(), filterAllLogs) + return ethapi.EthGetCode(context.Background(), contractEthAddr, blockNumberWithMessage.Hex()) }, }, @@ -321,52 +302,9 @@ func TestEthOpenRPCConformance(t *testing.T) { }, { - method: "eth_uninstallFilter", + method: "eth_getLogs", call: func(a *ethAPIRaw) (json.RawMessage, error) { - return a.EthUninstallFilter(ctx, uninstallableFilterID) - }, - }, - - { - method: "eth_call", - variant: "latest", - call: func(a *ethAPIRaw) (json.RawMessage, error) { - return ethapi.EthCall(context.Background(), ethtypes.EthCall{ - From: &senderEthAddr, - Data: contractBin, - }, "latest") - }, - }, - - { - method: "eth_estimateGas", - call: func(a *ethAPIRaw) (json.RawMessage, error) { - return ethapi.EthEstimateGas(context.Background(), ethtypes.EthCall{ - From: &senderEthAddr, - Data: contractBin, - }) - }, - }, - - { - method: "eth_feeHistory", - call: func(a *ethAPIRaw) (json.RawMessage, error) { - return ethapi.EthFeeHistory(context.Background(), ethtypes.EthUint64(2), "", nil) - }, - }, - { - method: "eth_getTransactionCount", - variant: "blocknumber", - call: func(a *ethAPIRaw) (json.RawMessage, error) { - return ethapi.EthGetTransactionCount(context.Background(), senderEthAddr, "0x0") - }, - }, - - { - method: "eth_getCode", - variant: "blocknumber", - call: func(a *ethAPIRaw) (json.RawMessage, error) { - return ethapi.EthGetCode(context.Background(), contractEthAddr, "0x0") + return ethapi.EthGetLogs(context.Background(), filterAllLogs) }, }, @@ -379,10 +317,79 @@ func TestEthOpenRPCConformance(t *testing.T) { }, { - method: "eth_getBalance", + method: "eth_getTransactionByBlockHashAndIndex", + call: func(a *ethAPIRaw) (json.RawMessage, error) { + return ethapi.EthGetTransactionByBlockHashAndIndex(context.Background(), blockHashWithMessage, ethtypes.EthUint64(0)) + }, + }, + + { + method: "eth_getTransactionByBlockNumberAndIndex", + call: func(a *ethAPIRaw) (json.RawMessage, error) { + return ethapi.EthGetTransactionByBlockNumberAndIndex(context.Background(), blockNumberWithMessage, ethtypes.EthUint64(0)) + }, + }, + + { + method: "eth_getTransactionByHash", + call: func(a *ethAPIRaw) (json.RawMessage, error) { + return ethapi.EthGetTransactionByHash(context.Background(), &messageWithEvents) + }, + }, + + { + method: "eth_getTransactionCount", variant: "blocknumber", call: func(a *ethAPIRaw) (json.RawMessage, error) { - return ethapi.EthGetBalance(context.Background(), contractEthAddr, "0x0") + return ethapi.EthGetTransactionCount(context.Background(), senderEthAddr, blockNumberWithMessage.Hex()) + }, + }, + + { + method: "eth_getTransactionReceipt", + call: func(a *ethAPIRaw) (json.RawMessage, error) { + return ethapi.EthGetTransactionReceipt(context.Background(), messageWithEvents) + }, + }, + + { + method: "eth_maxPriorityFeePerGas", + call: func(a *ethAPIRaw) (json.RawMessage, error) { + return ethapi.EthMaxPriorityFeePerGas(context.Background()) + }, + }, + + { + method: "eth_newBlockFilter", + call: func(a *ethAPIRaw) (json.RawMessage, error) { + return ethapi.EthNewBlockFilter(context.Background()) + }, + }, + + { + method: "eth_newFilter", + call: func(a *ethAPIRaw) (json.RawMessage, error) { + return ethapi.EthNewFilter(context.Background(), filterAllLogs) + }, + }, + + { + method: "eth_newPendingTransactionFilter", + call: func(a *ethAPIRaw) (json.RawMessage, error) { + return ethapi.EthNewPendingTransactionFilter(context.Background()) + }, + }, + + { + method: "eth_sendRawTransaction", + call: func(a *ethAPIRaw) (json.RawMessage, error) { + return ethapi.EthSendRawTransaction(context.Background(), rawSignedEthTx) + }, + }, + { + method: "eth_uninstallFilter", + call: func(a *ethAPIRaw) (json.RawMessage, error) { + return a.EthUninstallFilter(ctx, uninstallableFilterID) }, }, } diff --git a/node/impl/full/eth.go b/node/impl/full/eth.go index f6ebf613c..c65a930f0 100644 --- a/node/impl/full/eth.go +++ b/node/impl/full/eth.go @@ -441,6 +441,11 @@ func (a *EthModule) EthGetCode(ctx context.Context, ethAddr ethtypes.EthAddress, return nil, xerrors.Errorf("cannot parse block param: %s", blkParam) } + // StateManager.Call will panic if there is no parent + if ts.Height() == 0 { + return nil, xerrors.Errorf("block param must not specify genesis block") + } + // Try calling until we find a height with no migration. var res *api.InvocResult for { @@ -838,7 +843,6 @@ func (a *EthModule) EthCall(ctx context.Context, tx ethtypes.EthCall, blkParam s if msg.To == builtintypes.EthereumAddressManagerActorAddr { // As far as I can tell, the Eth API always returns empty on contract deployment return ethtypes.EthBytes{}, nil - } else if len(invokeResult.MsgRct.Return) > 0 { return cbg.ReadByteArray(bytes.NewReader(invokeResult.MsgRct.Return), uint64(len(invokeResult.MsgRct.Return))) } From daf90ff402836e735133dd184a7dec4f1b8b3ce4 Mon Sep 17 00:00:00 2001 From: Ian Davis Date: Thu, 26 Jan 2023 17:01:39 +0000 Subject: [PATCH 11/30] Fix lint errors --- itests/eth_conformance_test.go | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/itests/eth_conformance_test.go b/itests/eth_conformance_test.go index 344c8ef65..4ab8e848c 100644 --- a/itests/eth_conformance_test.go +++ b/itests/eth_conformance_test.go @@ -70,7 +70,7 @@ func TestEthOpenRPCConformance(t *testing.T) { require.NoError(t, err) specParsed := orpctypes.NewOpenRPCSpec1() - err = json.Unmarshal([]byte(specJSON), specParsed) + err = json.Unmarshal(specJSON, specParsed) require.NoError(t, err) parse.GetTypes(specParsed, specParsed.Objects) @@ -395,6 +395,7 @@ func TestEthOpenRPCConformance(t *testing.T) { } for _, tc := range testCases { + tc := tc name := tc.method if tc.variant != "" { name += "_" + tc.variant From 916b8001b4cb9ba40dd259c0580f17c57999b48d Mon Sep 17 00:00:00 2001 From: Ian Davis Date: Thu, 26 Jan 2023 17:07:17 +0000 Subject: [PATCH 12/30] Skip some rpc conformance tests --- itests/eth_conformance_test.go | 20 +++++++++++++++++--- 1 file changed, 17 insertions(+), 3 deletions(-) diff --git a/itests/eth_conformance_test.go b/itests/eth_conformance_test.go index 4ab8e848c..6e21101d2 100644 --- a/itests/eth_conformance_test.go +++ b/itests/eth_conformance_test.go @@ -138,10 +138,13 @@ func TestEthOpenRPCConformance(t *testing.T) { require.NoError(t, err) defer closer() + const skipUntilIssue10106 = "Skipped until https://github.com/filecoin-project/lotus/issues/10106 is addressed" + testCases := []struct { - method string - variant string // suffix applied to the test name to distinguish different variants of a method call - call func(*ethAPIRaw) (json.RawMessage, error) + method string + variant string // suffix applied to the test name to distinguish different variants of a method call + call func(*ethAPIRaw) (json.RawMessage, error) + skipReason string }{ // Alphabetical order @@ -223,6 +226,7 @@ func TestEthOpenRPCConformance(t *testing.T) { call: func(a *ethAPIRaw) (json.RawMessage, error) { return ethapi.EthGetBlockByHash(context.Background(), blockHashWithMessage, true) }, + skipReason: skipUntilIssue10106, }, { @@ -231,6 +235,7 @@ func TestEthOpenRPCConformance(t *testing.T) { call: func(a *ethAPIRaw) (json.RawMessage, error) { return ethapi.EthGetBlockByNumber(context.Background(), "earliest", true) }, + skipReason: skipUntilIssue10106, }, { @@ -239,6 +244,7 @@ func TestEthOpenRPCConformance(t *testing.T) { call: func(a *ethAPIRaw) (json.RawMessage, error) { return ethapi.EthGetBlockByNumber(context.Background(), "pending", true) }, + skipReason: skipUntilIssue10106, }, { @@ -246,6 +252,7 @@ func TestEthOpenRPCConformance(t *testing.T) { call: func(a *ethAPIRaw) (json.RawMessage, error) { return ethapi.EthGetBlockByNumber(context.Background(), blockNumberWithMessage.Hex(), true) }, + skipReason: skipUntilIssue10106, }, { @@ -321,6 +328,7 @@ func TestEthOpenRPCConformance(t *testing.T) { call: func(a *ethAPIRaw) (json.RawMessage, error) { return ethapi.EthGetTransactionByBlockHashAndIndex(context.Background(), blockHashWithMessage, ethtypes.EthUint64(0)) }, + skipReason: skipUntilIssue10106, }, { @@ -328,6 +336,7 @@ func TestEthOpenRPCConformance(t *testing.T) { call: func(a *ethAPIRaw) (json.RawMessage, error) { return ethapi.EthGetTransactionByBlockNumberAndIndex(context.Background(), blockNumberWithMessage, ethtypes.EthUint64(0)) }, + skipReason: skipUntilIssue10106, }, { @@ -335,6 +344,7 @@ func TestEthOpenRPCConformance(t *testing.T) { call: func(a *ethAPIRaw) (json.RawMessage, error) { return ethapi.EthGetTransactionByHash(context.Background(), &messageWithEvents) }, + skipReason: skipUntilIssue10106, }, { @@ -401,6 +411,10 @@ func TestEthOpenRPCConformance(t *testing.T) { name += "_" + tc.variant } t.Run(name, func(t *testing.T) { + if tc.skipReason != "" { + t.Skipf(tc.skipReason) + } + schema, ok := schemas[tc.method] require.True(t, ok, "method not found in openrpc spec") From 17ef4888e4bd307b6c916e7961d4053bc8d8071c Mon Sep 17 00:00:00 2001 From: Ian Davis Date: Thu, 26 Jan 2023 17:09:15 +0000 Subject: [PATCH 13/30] make gen --- .circleci/config.yml | 5 + build/openrpc/full.json.gz | Bin 32994 -> 32839 bytes build/openrpc/gateway.json.gz | Bin 8494 -> 8387 bytes documentation/en/api-v1-unstable-methods.md | 295 +------------------- 4 files changed, 16 insertions(+), 284 deletions(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index b67a7a7d0..00b8b96d6 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -619,6 +619,11 @@ workflows: suite: itest-eth_block_hash target: "./itests/eth_block_hash_test.go" + - test: + name: test-itest-eth_conformance + suite: itest-eth_conformance + target: "./itests/eth_conformance_test.go" + - test: name: test-itest-eth_deploy suite: itest-eth_deploy diff --git a/build/openrpc/full.json.gz b/build/openrpc/full.json.gz index 4fc58a93a6f9db32082e1c3941745e4fd50f2902..f5d66dbee6f3471d9d0c919197e95e4e3f3541b3 100644 GIT binary patch literal 32839 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%0Di6a^A#g*kgj36Cg?;82tn zLGD19IZ+8%u!u+2m6wctQ(&366D{52`Ke6b;8H(X|9YJ}6)&c$e#ErJLVc)vl)ve8 z*;v%uT27i+sO&wz@`67uG~ujEGwirTEsu`EGQXQ9s2Epqh)a+EkO~v200xZWGxXE)?!A%uuRzxTHg<2X-g}Ym zxj6_fX+t!TYLdFPKL36>N8UOQj07zd> zW7q7hefx=O(Kp*z|Eh%k;7kc+!dv&gIj2-Qq2keHoC%g!m$5d=TbGa;?m4|S5LP7- zNI!U^j&cC+mcsORBs*l^U$2UEkl>zwwDj8*bAxktUmG9dcN7GeqozfA7odXk{)!J; zx5j`LG$b13St1WNqZcUs@N4yq094o|Xvfyh6Ln+BtttA}O?|XTWW-;{;fo_wC^R2{lEo02C6=BRROfmIf7 zOP2Y|tCbE6&s2oorTUt>!FkhsHVp5r&7%sI}(xoYZ*UPXedcYSa(>j~U0S4Nd? z%IJKnM6%5Q4!uY)=l6T6A8+inGv}v1bf3ogARGQZE?uj237%Kmz0xww{y;CyA+d!njMf_%b6Y*NV62WS(K{~OCp&js+(S~adB(9 zyGSPLaHFT0VCej{AsO~3_bvNN+<;ykG2^bFmO^4vx@wkKm+D$Q+PNk8^zRPyQ7Hl! zi#{KxxFOj@7;MlPUi;20vX6za2lLmg#*S!BHgBvXQlH1yXg7q@j$vf9Pk~1R$@hRO zY@6Z3fwzs?P|d|oouKpbb0gv*Q!&Ng30m1grh3ODSDzRz9_c`a=75Gye!St4d!!)- zpM1G_4O$6O1a>JHpOIIlMs!hxUPhS{$7x&X2#kqqMNAHWX|b#z@z9STSGdryqbE!q zA`hQLnDFhH_WKSfDX*?7Z(lsav^-kyNzT-yd{s7IcgAJc8*z7RB5QA4QT4jc+`QZ1 z)^F16GlgJhP>D%@KD6xW@9}4Mto|uuti+v3djIX1^$&)8%;YwgoTbxlun;U6<(Xd{ z$RMBHIV~>v=PeS3K0y zx!HzrR=0I9mCc9XM~rRmT5o`j*m-U81vQx`#0I%^*HpAuD&%sV|LCE1imFx3<{c1l zdl&>Io+e7GLm3&2kF{bG6@uo4`OgAfpsfM!FF6#@G91U)+UdkHj?aADJX1T>hbG-& z4^4k{Fh(?x;!dw)KH4E}B;IRynK~_8(HRa`U2CDjYzj|_M9U438KXoa)KTQlR&{d| zMvhv${Ql^dZO_27S28AWZEzG4YtJKLg?>xRwg@TR(2^&tUUx*G5_&sNK?m(SPa+Pz zmcgHo8i{a%fqypN+8maK8yj=${R_j{@eyH}xjxs};F!zXkO_B0jnFh>uV*TbdjbB& z=f5Kf`ul>iB6>XKcIKs&8?Q8u2CVE1<<1!+P*%xsd|$34k4nAc>M8aGLpn~zbFAw0 zIDL00TK?&#nROPYJCy0|4wi+a)-w4$M=v2}-q=94z4;4dyzUxjwz?d{=dkN6Sry-?E%_rgXRJB_H zNhrpxvJ9o_lW$Oz{=5auX9FUBi$>E~CkL{A1F#FYpzI)I9xC};3(rXD-B%J{KBP)> z8vr{Wjno6s<&V&#F_71b?f8B4ezE+6i;MU3{j~oR3s)=mbF+W@PAqT;=k+&S_b>OU zA&28{lyKlaS;|ae2YXoo;upV#u$|E+$#8t{8wpXDcW{fQ{590GD=>T>okxGs0=9tn z1*UiK&U)JXRbhT}dQ^m;Lv zu8Hgta)>!Hn0dzKs_ltpyUQs3+|l08SUScpP_(&NOgpoi>Qpoh88HeyS==$kC#%kX zE>p5Dxl9+0HS+3M)j(_2!MKugqf(K@<%CEwGFFK^wgZ<3C@tua1#7OU6nV?}BsY-6 zCS#*Zh;1+DHBa(lOU_&|sI3g?L??o!k&|a1;Br|hA!-dOBUfB}-iPD^0(35shtePHREzgv+lbn^$ZMvwO zyN79V3T$(zCZVzlYA;*jQIMW2_vGw0@f8EsjrnLcx1n6XUMQI(l60Ax^~Gtr_yXZG z#8I?K$%hy0pIN0PwARhSO4bPjo0mhmjT!Og;jdE|rTw9K8bL`}iTo}D1aeDe* zcM~C|buRM$VwNp{`{+puJ}0;SC=8K{b(&XqZM5+TAwMH=#2N(Y*wJGq*jR%ndE4c9 zq2s7=6$}-1>XEPZm&Zr8)VB9f1^p*F99IxLF%+4-q8d0SQ5_b=zQAJox@>xR=Eh_d z&qum=rTu{GXxt{y zQgT4jGQ6}+R2OmBnk7M@yoa~ruadr~wHo}3>x3O+o>G$YAXd(o~D^pAxyjrF)AH7$%Z9yQaaAv0g~p@2gL? zGtd7*-|7YD(;mtJ3n-Mr^1=cu?k4nYL~@7tK*y$OJt$+azDYJ zb*BfMl;vCNk*lXmaU7cq)JF$8Nx*NQ zFcgRs%tOgkPnF@cv=Vqo$t59fi@Ysz?_}~qN=-QOC0*KPC>^S$C~aHkAZZD2jPRY| zaCwh7z~07Rg7IAnw;l!)2O}tr+uz$3juus8dlA2ez zsc!LTG!l(DN2ZjZls(UwLAVE=d<_2P$|_!>e+Y{uhLwR_F-Ii+D&@qSQeSwI8+X#; zJXgz7J&RXp1wE#Q9mNuq(ZD$U8ebU&$>gHDX8vv z4FPcxA^yNVU^?d<+U$p_f1RJV2VnrwJ(yK#?iv5ckerW&0{j<0_oBD`mwihtnTB`i zL5e~3W^XgDW@p>=wk}@5cLZf$+4=qGw)XO0KEHfyb#GnI__p?TZ(PBG%G2pm<3}#*JSk>ORH_0T^Oml z6zP=c9SzvBO&EdyYV98e^t2MPol(JcU&H|kZQ*W{UZojsvRQ@S|e|V z{1bgu=wb9@6ajk&6{&SDeHDJ8BimNjkG8Dt(yEpt$;>Wd7WZ$H!^{rHT2T&;DhXYe z@w^|BYpRl)w;W@+wv$z@j`$cZ^&dl&hUQ(3X2Ya>BEW)8ZnABz?MRP`>WWmO;Ojm+ z#K+)7v^*q&8w)?w^rEm)E+RHB3(CW?i1ks37KVn)SDnU+`a@wkKn*^Y#7_vZVS@xO zr`5^|;suUDv&605$*SXZb8P9Xl6rwiDN>=b@27gExdC!d0~%z-++3h=sM2l-J>JQE z$m18(1TZmX-w>>Zw*rfY>>v!^;jG-u?qEn*dDuG7&pqAyWE8x1Melh|pbk8`*}3sK zPF)09johBObHdPK?vM(z%TXzVa%0c+*~b2nVycugkP`8ko6or{QXv1GDS^jZxyza? z&m5(Px}OBzi)%Y8=+{PayXJkFid^!Fp9fn+lM>;B>*>lv8(?fJ(4cQH&3xl^D?ou4X$Ai*;p7pVUlxSKUO{ z@||wORQvohJ%{860{(E&8%L~E?Xa|(H<%`Z7jrwrUEZ+JK?#|;8Ur-W*e&jRe7hd0 z*A^63TtYFFp8HxPZY}6)&#k!8LZ6w<`aKf5C@s%l{M_(8u01+EB)IIN6S=b@t$7mP z;C8IR^wSgRTW<5x7v+u%D72^*QTDfF&9PF$UOJkZYlH%5!Um_gP9-oDuwM98)cr-0o&w3cy}CA^N@^!s5+BEXs)&sTxB7( zxEl-_{t()(QvNVZ^fC6(4UO<7Cgb?)UL-7czMJEbFv*WKeb*8ys?;?t)VcpqE@?Sy zMe%$+1DSg@Y@gCc%o_|hEXp3;gYP+nS*MuG1O;K@)*bG;G~59)?m7U#55NaDlqkD${Ouj6dpk(bqor;3)VCZ|w<86;h{3jH2H!eDyqn6+MZM-0@i76Gf{{{A zHgZv|uS4?BKe4O`#bA0JM7YUCKwq&J^zgMb86>Y9>GBaKM_Zts1~#=?B5Kc89+G2! zjt)tAqy1YU-Y1N3|U(RGkn}o-Y-N z3vz6w=0&5r8Riy-0(r#&8?}AqKB@bWAlblIF_Bnnv9J}-Y2qo;!6JtvaIQQTh-W?8 zPs1nP=Xqn=Cx@nv^sU=zqDXLaT{+>F2Zw>DwWY$h7)tg%q$9rWfvNN(@S}&)L&Vh8 z$SW=+!l*ShSn}wJ3&{Tcg_rE9G!fo>T`Ob;=;y_HTj~&nd5zev5)mm1n}mQOrck4% z2<=WZ#>UU{J7?wzg6aMb5^6RTjTJgmOUe2=A0hZd!ajAy0dl|f%djAP;7;oYso#(9 z??&r+N9`T zL3ij}yy{Br|DL~@Wx@xfe-%%YDq1|+;u&S0`UUp(!!ji1|1Y34*~kt4Wp^UvQt?2) z|M>0W!qPnUBQwwc{rw`>>g4S@pedVcjs451$X<65Znu=hU9US|%e&&);cx!Lj_>-~ zd^K}-V#}C6rfkJshH1anUAg%#Z_0Vq8iiem;*h^@p&m?P^p?wQbN@^5k_dp40@r<=#-s@LhAh`)b8&`$0>h zHNAtGggBkCirV5x)SQV7@c#qGG`=1WtvpL5L6w*gSc}efm#wuM_L7LOU8<{LvLx>=4mC3;>4M}2!gY@uJ=4|2x4}s-$cwsbFV>^!YVHk~q zyW^lH!egkuR2HHIIl-hbYK$19vyL!?I(`F+i2w!}?I+2wFC4w<5wLomNsjw4Yp8{PAD81?E-gn{XmS1qWPNT&orf$OHVcE)}w&GiqLNXL>}Z17=#520zh z7$wQ00L~dLSx<{-n!_(Sy`)yhOcp_453R4tsy#Q4PxRDG7?d7Hm;KekN9YvcyT4ZW zszh-_VObW8kxkb@u3U?!JQw#M@X9HMH0p`JcOa{&$Ujhcl_vzA6YhiJfk!-^3P4xG zQkGvyFm7m<>UXc)(3|fhH8(t(>Jotq%1sOb!3 zwowPSL}l0;$0u}-N;$W{i-~e4lCr-JT!s+R3>wxXQR4%b`^Jg%Oal;{7b`yCagwZ4 zceG&T*Tt3&OdC?h^peC=GrBhAD!0BZepMPE`io061GEOgMXw@P#oRjbk9lRgM9b@g zls!l);R&{}{Hp&W^dCXhw@L13@E zs0fK&fz;wf%1O;<7}#YDFBodVwDM1iP&kjJNVcn6jCnNxW;nrW|Bl>GiSm`%wh5_N zt?zZ>!LS%ykX0L&{enu=ZQwFL8Wx}76I0q`VT~OMbqkEiMJL4kt{h>SVRP&BSOtz>FhxU zI8JOiNfvFcQd8)bDYWw^rqa>KQ==3_8#Ff%%wDLV{=X)<)DsLJ@?AG31%tDSb@ zpQK5nsJ`17^LS^ROP9~J$Swh@g{R=G<{RzIlCU*$b7EbGR*x)t@vvUl^fzX>v8i^A z^2r)kWF%w3AgnNuQY!Khi=&B{N;d3<8sgy;{)%n{#28AgM$t%k@<^yL!b0e0GIw1y zR%D4vAS`NXLO;zbPpl>P2s+6jA2(9` zL_I7iB8+U)U{PcaGoJeR9X01v%}>5tJKqeazJU{x(?)zwe6JpKCgY%#CNsrs6t!V< z;~8>w_-uS8885({?z>znRam0_wY`=zIZCrBw#4ei@{o4uSJ1f*%F2zX9 z>Rg~w;q2_MEL5Rqr3a-uZp5eAGBk=U?=iH$05Z@{L4uu)ahC<`t+#B6EL&=#T>y)P z=lr6}pq!HJi}V5qbpJOXB&LtpNYnbZW}rZId?eyFSB{WOFmxLY_7J%YLwfKd`gJ|2 zyQO^`?NP^oUYB^$ey#; zuasN#94_}jaX3$T-Z#x79m}T4cE(@hl_%l`7N#y5oRX5!>p~mzcv)*(dU!J4h;e#2 zv?{ooAYj2(h{PmP+Z+r~7AB#*UnaS%(A3NvfUH!yz4VWThL>WT?R&gwQgK*axZgM||UV=tp%mSIMWI>>EJp2sEu~S znbfSlJ}2hM1|>?7X2f%wAj-P)uqU47`9~@Z{k2NcY}kk^Zf`RdOS?Xmd!16(&21{P zW=ETB7oxzE50(eI+1)K!@l-4yJH!s9L-BM3*KTvq8gRyS!j6B}fA{w)3Wr7~BAs+X znZHTJSco`gGXBBx3;vE@*=XPdY7osWdSIhURB~HYxVKMy%GDl_cZ%7pC|>RmpTjAL z_8cIZ-xaVFfJ$487nL_t3pFDUN1#(6_-@ zb^cO;ebc9^`uVViR7&##nuned+7QaXOk4b)YL|q4%M01JqGVu|P&?$s_&BqJvsOa~ ztJcf!c!?){f&o(n=rSr{$sfK1Q-9Tx`OyLOx1RI9Dw;-93uzJpH=Nyxd&KQTI7SGv zSGa^!kGF8d2eBz^#$40~FVoZsk%AMXcDN=5Xd^yhN%k}wNzPo$r}{(ho<=T5zDb)9 z)okRumo(V`a>ywJL$bUnkiVG5ujv?rBTcMnqXs#n{X91lHM2-oYk~~D3#t30nwr6!B0*?Bt@=jHk=iou>npn;_>i>g11G9R=OK+NIw+;wi*BB# zt3Y?~?|}8jQX?F>^{Un_;>*xx-rnm$S!M;BLTz}8Hsr{%DdUkY2(2_{ue+!qXf7xI zvHDS@c{Ps7(gAL6(d5l)BBIBeLm0`Vv$E*(5um-d3{MQ1`2Si`?^<>CdgJ>7@$XEi z{+jQvb9Sr8{iSte@r#=yHA%Za(H^ur8Gwf1Dp{&O<2}QgG53(@LW zNHZdt6t#Ema0~`qTU|&p2y|%XJY|P8$4-H!@tSGu%E?Xx0Ev9V&=o%D8aN7?1LDGC zx~j47)BSk=c(@R4pr!S9jX!QiW>w2vwJ7h_hs4yqW|cf@wnX$i!Ze zMuH`JyICh?64{+xr-Cyw{izOw+47H%FExO6VJa8$SZDp!l0T~sozt$@>p(eYv2sBd zrCrIi{H9>cW16cJ8{&NWi{`7u8nLGTX*6bBj7~otP>*tNsIHHv$jOu>B+1YdYci|P z7O0rWwp3V=V@o>{@;1)>IR<#gp@EzwMBmX3$o{NJ8$!%c5;zYnieHi;dD^26M>2;4 z>go9HK>Z~eztAf27)HkGeE@hxzDJDw?qS;rcifB9GU718Y`X2Xt-a%L!DWCBKL9#( zj65-zKt>?85aZzH}n9Uf?iwD0iVtdX@fFgthCGy=1oL4{G%v9&iWU<+BJtGj2&Hsw@18qt`tElD}8`y;B8Hf$*EU1N&OEPYo9lF}82#Za+T zZB*(Nhexh#H@BsYOSUR#Ow`BrlMXk& zA-lX0ch^Alu(E6%8X&u91WdeCBc^InxPiS?|A4Wf1|#6_=s1IYR=gwF*qWd+xAifj zvR@J(kDl}_8>;tNTq{)Jx77p{Zz;U+RL!_{aXwnXb@R>St0&R!de=SF(4eQqk%zVT zoOa-KTO-sZSJZfnsA<%_nF}N2bh)%b>)oL6Y^avA>m4D9s$=z0mTW}aPs9paT;2QL z!QH_*gh~fHXAo2TOwt;x3cIC&^)Q3$q$%L9@6NS^tnUx!>zTtaxJMOP;46>jzijHI zAiV)>f^=`sA@LF7%NO7zd{UlQwBDCFyvAy8Wu+K_PJK#txDUy;)&6lUfZB{#fvj!g z47@&?2~YL)|F?NDb#;r0 zn;+Xgga4^_!np0Bws~EnxSv9(|MAcc0kHmhZiEKt!S+v5!ih@;b|9;65&i3}G+O0eA*h zREtoAy=HAt8_*5*jJ39b@(eejBre6^O2{auyjfX65AFRh+mb5S8Cl7;XSX>|vt2#Z zK}ZfI_1;yA(^OIBMh`O$Wq1LlSYt#8=mZ+7_kjf>;a|9cF%|SwBc@C`sCP=#O)1o#D)_+HlVMy1XWa=;W~; zrTS5o^d4@a@*@eAOpk^u(KD)p6BNHmZ?N8)4LZ|jRR6Ba(6|=1syeg8KY}w<55d*+ zjZZvZrS;SLe_nwA8!mW@-RA8R+o)MoCk#UN7EUWFi#C`eWUOrE^^!5N9T9A2a+(Dm z`0^EzEM;1o|3P+AXbwqsan{X_0UdC2D%F2TPB*5`70!#pw$bbuWnph_i zv-Agz@`@Jorn!G+(al}MEiP&Ia^#=#O#6yYviw{*Y>MG78VW5&^lb$WoiGo&dOdM@ z`(}9whn&({J$jq(0I-2aF*rv07{TPE!DjR{XI$X&ctJE9E$lR#r-~wE;s9U23vYRL zAnbp`4zg0Z=8|_d*#-q-C$YM%99-mBp)VVkNMz|x@(u@oZJd|6KM0FW$_0b2cW>O5 z941w;n5RT?Xcf{(OrSvf3ctKW0C}QSXuN*U5C}62E<+$(D}R%t+1U3HOfuwSz=dI@ zZQ(v=Uv}4@1Z`YT%q3!#^4x#i|JAI$B!EZMcNN*qR_ki_-+~K$!j&lhj4lxXVfZ1GLSTsJUbJn+1uy2(KUV;ax zsr1E{!5w8iZMo1j`c#IDU6GP)%jM*3P^TP#FF(g&Wmt8TCWg1)yk^Q9K`$T*@UL`4 zmmwWH>Foki*i$C)D;WcIE~xn*sF>gU1g?a&3f5Gds!vBC$1nKy9PJnGtBS%l6)Baq z6_fF{M9c?xRuL2N)e1tP>Pw_8Do3m?I?n%H_>CZuSP*Xj?Qv+=)Ij!xhz2I-yD@R& zEav<#<$(Z&BSk*kLOAV!@HB`V;v^?Fj)~TF=GQZ;M%juNDb7*MQ>U6$pK4%Qj zzLZg-PhdojkG*WIQhUu7gvnxz%oj_9t+EN34?;(lL~R{;*=KpFIvKAyBX-DwDkw{v zV$mU#O?t|uest{*=oo>2OVckQrzEEG+z*BoYG4HYrJ{W0o zHfbVdPbuq37Fxm4s*IA=+&R=9-@;s$S}L!PBb7Xx-;<$?Pt7@(Fxz1#=bZL=VI48g zPJS@w1c5dMu9V)0NMhK@l7$VZhgzU zsQE=5ue2!wkRz=2eRh*a+|5PxKRP?7CQ-U3O1DnC`?PJ_wr$(CZQHhOoVIP-w(aTn zo0vZ^ceO7oDxz|)%(b2@pIR+!Tyz$5hy~I>aY)bg7~sE!-X?HI(}x@b0hDMLXTmbH z7qJBoJxK_VkR8WNJUTGYD?$@=Q9ZI!t~?0QGcbgLj8$@@M|c9rmzv$>hU14+{QIVO zz8Y>BkL3}|WfVAvB^O`=?nFi!u*S61i4mWAzMx@hlluW^L1kWNq)64K z82|Nij?+G&m7P@9vyXWgIlcKWu9kkze;LozL+;!&I*|+c_%SxSq_ZrjjIz_GkqP;0 zU-P&U@dT<$-OkhWSw$9@awSI7vGnt|gZKM0GmT5#&hrYqhZ$!j^F@^Zwh`uWjzLHr zEyq#y)r#6dnrX=j6z0>}Ji$O%7>-RLr`H;ytyj~*>G~Ns^8^7(^)>+1ZqV9S zHx>RCqF{qflF&L(hY31m=miVEbb{)<{`(vP6Z#hBb94jU&~cF>OL)GsFe41O+~T$PwYOxrRnS6cCd)?4~L3hX@7n-HzC-^ zg6#L;4b~&rfX(qxykNEF+qr5B;r-nR)8Pcvp|;_Q=q1*B)y~CT|F4vo+jvRi18Z!f zy00Y3nr_l5G(j)qeNbyixX^kdh)cma!O%j9?L+~fc}<&wGx4BULN{Pua#~%IL5x4K z8=$-H@DX?EfMRPY35^kCA32FzeDOafGxOm8?e_1L__pHmAvMt4h9VMv|iHSp}c>qR4B@C8Dr-U!}%~ z@8exnl_-qZUXC#bFCpPW{#a5UJ)i%^Blr`ddCl~s<6Xn!qU8!OpY#djy_OxPi$`j0 zu1st;E?>$tdEfjqm!-X*LxdU_i59s9uHiwat-c+i63J+vdr3?Lww^KBZ6it6@557b z8!a|z#6?u{{>B`8Rp>6TC>e=ahGW0~TQnUVuUIrIs9aj3U{x;WsgUISDeFPri=c)QE?@Q$4ly-+cj#W?J5Gzu zrM=}xj__ytAK1b1AQBt*|8X6pNF1IA(G}xi?1ZeLvgrMWc;=)`cmw#XJzW4nMixa- z+?;ubmXXR1CF~tkBYV6r1TIk4=vFmT_xvL!;**U zdNp@UVhM&1~PZ{*+|1D_LLySwgr4WCpHcL0inZ4n9; z$Z4P&*j|Ka$->`i8{H)vgs!xPbxep55TYTJ`wtwNWWi;6Fr`TW65Cdj4+?34(6Z={ z0HQkM-AcP%z1w#Tqx)7~l6QYfRFge5}e}shY+0ifZmkVco~U z^B&ql;}JZ<{TxBequRks*^T8#;`9bl^{1mK+B^JL70 zqECcH8NrNK7$KzL<(x?hi~{XNv>%I!)8tK@V69{)bM_2^9SwNbMe5IQ(GJ zCkgwpWUiD>C^wkbTjWb`fER!Wcy3Cpj|qxIwq1-r;V`&LI3mwe6HS@Cf+9|$=1crN`VO&(Xy{*`oT!ZS=bMd4e& zus*gj?X%!wb;1{}aV`fFf2n!s+QRU< z8okBck(PPr@o;QrI{)^xj4#@fOYmf|{VPb^3Bvplnn!4cN8cehjgR+y>U67<>y^lFsFDbHtwr^S#;1g`=8OuIm_=9{T908d+fbHkvIIN$kB)N zsoYYzH`><2x-1$J<~m4h3Ul);)zZjLRG+e3rEOGPILMr4iqgnE%sc42i7kHE0jhZe z-ZW(pe^BT>HVH1AWJ@%NR1pOV=1_WZ6a{Wi_YqPSJgNQ2^h9Gag1>mtjY?Pe)88k= z;9R~)$$|zAUX^J7P-z}sW$)AS8RBVUPR34Q5&MZRqF?tcLkOb*qm!&L8~}vd{TZk3{Em&ImO|cqJ+c~k_mXW72Q6^#VOKa5D#Y49GrbP4Ma;m z+J!{o^(?nB8`dU1yeEHpw4EK|Ov{U#CX&5b;+FX|fX!iwnE&rXF6cZDgy``-2(?tZW-=WKM8#k+@x*~V zzll24yGC}-B7bH%Fe=?{FhT7YuzVY54QvbwIsdIe-08wVq zUkndS*mK3aPB0u_(UKZ0Gz5aPpPa|ZDmIY2eXr&1xpc&F@oUgJ4!eA5mtT_A%cOjv zKwJ|~_dSK8gLQ2QS=1M~+8=j24J|RStPr{9-rYqEw&snt429U}`~xO&pscdZTO?6! z0E@T={&7(99;Twsa)|tw9cAN9MS=!**f0>lFTb-J-iVjfCxDB7CHP?2ff6>Ar%54I z!o3mNyDQ_ZuV`(*VKB7`u-Da*QMaBv3b1)CNrQ1{aV!o&u4^~ zHTD3Pz7}1O(SwSiHB;@N&g#ZQ3MA~rGQODz8S2NZeQV-26J*sk`N&+$(a`T*qgT%2 z9iGpp+m)N2?-lI#SJ`8qRqn^G+8;+FZ_ih^$8U?XTAg#K}!;5&vB(*G-EnI3Z|Hmd#ixT{*IB^SM=KnqG zx|xLpf=ZodA~pBk2WL1+(_1ED8s{TYeb>ENAZRlQkIq2PUvQm~pxQ09FNC3aoMpBS zZ08Xf=fG8jrU(P~`-nc(LX9of;zqkgZ&40bE_a9#EnG*`z>mGuyf=xYP>Ro?zIe6u zjrO6m3O}36mL_mAEICJ}w1dW5T(l$>4zw|26Ww&&}SO#hJgt|DZ*01-|6rlIr5L^a$lJ$bGVRS)Ixz zr52ym7kA-n9!7tPDUXhskvaeEZ6}f?XA(-mb(C&jr3irk##qpudL*eo1r2*hchrLG zCKGK4Xcw?4W4*}rsLAPvfH^>C?FVn<{jftI{5nID66#--20*S4>6%H&9d-4L`zF6! ztw9-2c#UK7`w1{gkZol#czMb$&D3M)CnM?Uo1yaD}saGj6_*o{DNgi@qHPYJ!V5u|s zc{-6373Ic%a$27-5O?J~I0Kfc`Mly7ju{4OV%0L|Y47+|NZ&lZbb88f8Ce`Wf_EkDFuuc z`~by{dyiI!O|fdF@YC^pFG4os(iohy!3f26W8goi+rI*k{-E0Y2#4uPeLR*)U&o>3Ap3CwMqOBm*NG+)5&T4x%$<31$oFXJ{m z?{0)9i`JD8@DuM!HhnA{X}!>|`o;hz*yI>Dp(!IC4^6bMUE1eHo97OvG54(_-%oR- zPri*#_MZE`ZqlHyN0xOv8aw)&L~Pf-Mtxd?|8DqPiAgq=o3k))K=d)DnU#CWB1DkW zp3l=dgKUxd_DA0=*K~d!%Cgm?;_(XW(o)~q%iz*+mav>-J+@~{=DS3qzXJUF#((-h zwx-Xp2_}1rlA4)672t3bbBzmB*WdoZKNyV0{?BMm^f8|6)h4)IK649w|CZ*4KYR0s zU{?R(u+S3AXI7F4R!rhW?k__pP^j)%iE99fMq3Y*hqt&Q$Vy zB=x6FQTAr>fb%J<9y<3FADXiRQUt{*OH3qV%!FivP>KbCZMqhhxkC$aY=p^4uFBxh z6esUE_#Tli{&RI|8h7kXo^L(ll%BNPP&^W0?FgVH&3kG!8S$UrmRkWzXA)w2cA{eU zaVy=HLk7uq_BB}B!qKfvtZ?;#QG>Lh9A==$8`+*3*owVKDU)qBS-6eBlBEnx`HeGV zXm9?UC?#}$Op-AbpCeR5nL$581$`AcWocgmlxW2@WQ0_Uhc1J)`H~F_!}$3!6~ov0 zJh*jl`xC;k!N6DY<2qx&Ciwj_NSi~=lGcz2U=q2XG}M`=YC@2P%|N&}yCz5pbCjca zJlO_9^#1cE|Env9kGjX{pZKAU{4ta#gEYj$i_lIZ&$34Q331@S1J2=OGLUQ^Pf|Ai@Me{AhV3C*IxFpZ$Os_s@eN`lj?pFvF-7jCOY8kF2 zoEc*GEP0P0DILX4X@@sp)Q{Y~aBE~4BZWRs&Nj)>uetJw0-r8vMc+BbqU>D4EZqV4 zkNk8cKoQ#gJ7EOvx0j1nx6ja<8hnZtqzlTI0=2u{4et)pR*0vT)fnQebVQoe=?PT(^YiQk1fzIveDYn)9N&C&Hzm@xt z_%V0u;hOZRWhjcU!FDMo6o(c~G~l>PZ5~MC`5FE?KIb_cV)QD2)7}`jvdh2ChLGhFJRd7#HMx5?Z5be zM)r~b(0(UIPk_32qsNdG*ELq>Y)uQ)u@!=Dot0Vr`{Orl7y-*lDbUQSS^=r-NRagC z`BkG8^WBkDu%DM@{oIdM!gGT6BU+P@aetna=rp$@Zvph#um{2KXnk2^ShL<46uHs~ zbl?-bu4-QG9KX@sIzaGw_!V@={`Qn-VC(@DX-0(*YOB>0g<uOFERG*>pj?$t(Yb5|_JewR>IEO93MXp|q>1dZb? z*#-h0;Aph$_5BV_or=I!5RzwDGrTVX=;jBB>U*pQH8n8nFLQ}Njr%SR!f|EoU}4c7j#$+vPfNo(jXgJdm&7^^QWNi5sOJph$ZOWjTU~S{@)sOvCAJ#W*0QdMd z)E>7g`J)%>n=X`ha__I>>j3zDG{WKW?MMWyZ&E-#3{=kLi=>URBxq(9A1LODr_4f` zBP}@AUbQ4sUzV$pEzzKnIu z2c8$*FNiPboRM$DFQJ8Da71L*y}TtTX)-g}x{6iCplf%1)-B}1dgRLLFWA}FF$wm> zlhNDN@Fz@>Nyl6fv+uH~@3M0CUkRInBv%p%dACsOLu|zne`xnv_5#3qOq}nk0ySi9 zFFc)$bI(eZ6L&B&sg>?&k&};c7ID zuK-nLc)BCMp<#sJYY*1J01&;Mzj12+ptmc^Ok&XdVt*3Sy>H@315bb5-Mhf2BlQkG z9r?EDZFT*xEqi*zd$wNt7Upy^+A}dJJjm0{!>|=F^v0pes+u`?F-%FeW%)IcsR zpT+t61}W>DyoqOKFow)Se~qB0w9wbBBT>%;i4^#q`oQ1q?K(oKE)D#dhQfnyNu=^O z2k|J@whLfAT!yWgGF3_`RgLEF-D_EU+inw4=1I{wozT0|;iP_}H9ZTR>~{8Y10iH3 zR~48-!G#{=LrFz>iGHNDao-1u4bBiZOT&4s4iXh*e;~=$oQ|9ovoyRZDHa%|E6InH z6rk`ERs=dPB#?tiXAVIFZmTk&IWYIC)|7%X)LT3W4V{ zAy`)Ex>VX&fBC7|LlRJLAuQ`$^Y}V*$Sp=3N?ic5{ zMi8Es(W%Iw%H^PB2m*s<)bBee?K3|0`V^ zV!7h)Va0OdTp7&_?;?%Y5&8=GJ9o1Hh8h}Lzi2($%;dF7o2@rnm+D-oXb9W9A6Zzx zL{2>-QEJ5GupWG8VY{fMXm?w4E^?BT<00DxC1fQ~h*fwUkky;o;_W?4lcIO33Iv&I6qnq9PTNNf=WX7~v} zII8}%Rryf=4XGont$$;^1V7cdOg_v{#Yt`#(F!w-3o*J(sLE!%b%h#F`jack@(+WQk2?s;sq@CtLd#t=} zd-Yqb2fnMSS3_*hytxZMZ{P4NsEYwX`lFfbt<*-TZ*H+It!TT@_3p8R&G>CA~!6t>w$HV*hiHEd@3 zia&MYFD^XqtuV{-FW+a@lZI5t$cL$yK!2S%HIAZZEjTph%qnE<4v7Pn0Fmfagrwi| ze9I@fWo_tHcNxA)KfAd42A3o!pr&aPu!JSKP$RXNk$6F;YyFKbg9rJyaS}RT+C*vNT7ARsuya zy5j6eyU@Ucda*n$Ti5xVWJMy{qnur(Qpcrwu`DY&hPlFoNIg0OJzulXM5bV|EE5IN znW6;5FggO=UbECBo}f&gw>r~8NouT@;|W2bS*E#2rdFP|T=zmr^1OoMDIudmJ>?3i ziK6wh2npR>T~XMN(%w08dhcUMHP6Zc=%UX>dAas)n6`ky0y=;9&vqeWo8;yyI0b=9 zhQm;1N0d$WEc)OfqKzP-#`~%0ZGJGR)_mC5CbXF+wfFfa_ z8v4d+EbRk|i!6iOuj77KxOv@?CdIce+4`gU7^K%R?3*rqfMq#3GlgpCZ9TD;ndg44 z>$C16zSTtA`9MZ_zdoz1Yj~O3V2V2^crhp3MQM|##3KsK4E+m@L8l7u$QVw97vsz9 zv2ZR{gxBQpKu3AM$?oT}Lq}wXFgdSZ0}8YO;1(ir0e~ zV&1DjH_Z5bHX}BxlR`{>*^v^HCN+YV)E! zBfC{!6I+Q3B^Qvz$54c>(vdLvsHCP_@FLOA#*D(U^4DadP0528<0!$wOX>_x5e^J; zKL}Lq=eWBoqNLCN3)iTFS#DWW6W<<7=nSTuuEFykQ0~?sTI8NZ*nnL;lb~5{xvk+laj#0A6&`tDVOFJEK{Y^fT*M3Gu8q%2 zh4%7NGdmiB-Ek>jkli&?D+xQHyn`D4-)lkb1P*K^g4JlPM?l^J90w`*X$Q-1F{1ez z<=q|xVJdDhv*f>y;<_sl;JRD`Vl_=@hJ!O?DA|@WsSG8=pgTH6a=%ffEQ(&OO!Xt+ z)rDk~MnYMD52|8a#lD7pbKFKg7Zm-Bjt~BAudPc05R;KK@_qSpaSe~6Ojg)KJSYr$ z4%_2|zpk-k;fp>?5b4K#1j@g=8Ibhoqo{6lBKI+b0B?Z=N}=Y#d=@o7%oCwybZGzKkqr_{FTJy!_p z$((LuW(GTFNyu7ZM$8y|Xt4o3$e==*9BXGvcu=@N(%VBQ->~oy79~X%P2vDg*dK_V z3#gzMeUOym9g(_<7q8ODzE3CaQgTU){^PTu{AN%D+2MyXfZ~F0?tw!jTM7?tWiU*N zxik=1o%WR8Z2>rdR4E>6-fT^KSAeO?QfH5?Z`i!2C0|G4M8#aXF~BHmWMNwcywJGZAm@qpxjk$Ter-Y2FpVQFJ9B?Vw~6UV7tkg3{I|Fiqy?;a*6Y_2-Ej zWSIK)(Z=6H$K3E8b73d#duIJ{2QIEnGx?hzL>E^BdI$f_KLg{Ponyj!)A#32MCipz z+(I{uEyzJ+*oppevR(oGMrTsWY?G$f;Ye3wrg08$)CmimTF1bA_;zUeN$@Q3_ zjUzp6^BlB*26lxu^W%^G2eNRph?+`LiR6i1?gj81C&x8bXJSv>7DYv}ib_8of+SV8 z6RYb~ebuw{5hRiuW4VeV?KJqWY(s@@AQj{0^tOv1it`3KNj=5!a5;7_J42{XT273* zg-eGPg?l2D(t1_A%vJeJljvVU%X`76{w~HUOHn;mjW=VzRaCs8Ew=u1&QE^0>AyPJtB!-en3JMp*c0&@LNfGllzA;4fDu9wrZ02QJ3SPY`Xx<`^|llNGt; zctvh|KtoB$P{@-~qQ0)Az_Et{Dq8De4|{Mz_z^W;LndyVRMgEuB&`H|s!4YbSq#DU z9BlMOMYG!kT{=6zAZN9#z!0E#^D`4yqxH~co;-0m@Io?cK z<}<-RO2@+MVLOrot$C|b0)Zgq86x0cp!hYLrqY?W1rFD-+U%UiUk>SqW1!F zD!t*@p|*gEgf3ZKA;OvEXYebN!kpMpaLl}23^JPtVr`SiZNU2_1=>{IjiE%4XT!$N z+*Ov|4h)*=NB`D;H~-@FRG0nMqTc#rm9*&;ctnMy1ny%MNPzn3Ap!DFqN-GoHjn|J^Zr#L*XV7f z&Wk-x{3JW4gVjE5q-U?Qw!e`M-nXdhhre(4Pi4OX{7YOGTm9SZ*9KHv$U+NuKYcmP zU;6(;1eIwabjY|5*Y*${W|gw?@a3;nb5hTR)PvizCg8Jg<=$M|!R*Go?v_;c!$H)g zjcSt69iqycDhJli_07Ir*AtpW(^P|XV&VxOSs6w2xA_4l3#8!Lt&F`qywOSn9(nyQ z8VgD0E%HJ<^_!FxZ^)hV<_8$cpSZFy!kto7Is}Q*^(oQ9!bdihd*Rq7I%?;f`=eds z2wHMH-GNcpE=9t#*HCIAagugSJe;1K20mN1Qfx&e;!sZDhu1E#Y^kVCqAk!l~Zo`!$e@W7vym)S+s?XXYnU64u89<1XFoL8_6m8z2EQ4+Cv-Ax4} z4_M?J5o#_iU>jz{6*Jv-%xoAwT5G03-+*v+>}t~=b@7V%Yw}u7mXM@}K|ied%SS$Q zg610eVWBrtl`UBhhNFj8nk(+bx%#8XHi?SbZe6mc+M49NAEf!76tL7|biRM=Le|W% zV4dyEi#_u~MA7YZ!v4^DQbZOwpf#Jl05(WUIpDYJL_}(RP*n2y@ZrQsf_pxsZxUVF z;El@O4PcKMDxT%Cm~%$(Bh;EA{F%x$fi!i;_tFV+s+yEMPUI4(46=w6DhA%iRH zc5(Emp1hoY>SXU!KHPL{ZdN|H!h7V{PW3;l3s)|MV7!fTu#0h0zz{{{cbGcmztk*& z71y&G?4lsXtd6IMXi z_cN4G%Z;oitNfSaTOr zPYJp70xblGr=(9;63r#ek|8;3yqRZ~aY+bfmz>>cUdUn-I1ltHMCx{^uXK0kKl6eP zbjJy2B@4?-D06sqgY;X5%kDH-`egIY(1c1wX&ZC4k)wyV3GX|EwR=;#f-8NM_pD@Z z6I-ro^3`k%&eBYAsq&oojM?VhnM~DWv_)T-lY#ilfai(;o$3682xQ=B=$vdt1>FKR zJY3FP`#0u_IE9Dpd3x-$v9Mab)oFSXb{-7{?+s3gZCFtAK(X)OR&Bf)>hD;o%SX06o5d!K09!TOlon!=^w z%-KZ-v?MB!!qW5T`wg1~@~`!<7onC;0Wms|SK0y_IF151t{J?H z*|P)s36B7*zrz2i1)rBKVPUkA1@1NzdBT z+Y0x&o7Y?-tLWquGeTV%pmt_?h3=fD;%I|ZYkyO~9H3@Iz<1k@!Ggogo2vmn0^XXJ z7<6Ehe=gLpc!&Q3bo{PClH1UC%)aMv@iO^7x%0W>7Ym8qV`jb8Uix^duE>)}2_mQ* z(TPkhk$H1^{;82lcCwM~v~KMBJ@rh6RfUZX`qpVqf`bv!_t~$c-sP<<^VBpu0LH%Y zZA`3togE1?fE1-j5$=dl0ILj-5Uc9zSqj6#_O>kp^PZE+6Kx`GYGIAzT6fd9P%?;i zKi^|6`5>$*TCa%k2@pEqhy^L|6JKR!NBq|hxEK}NS^SuJUxrEN8GFK4P30Mv9<Jg;s^9M;eiD) zc#X(pGlM_AQ*i;)3BnHUJ^l*A7(uf_%J{NyuLUb-Y|(clC^@+ zLO71!6Eqm(nphQD*ew3~x8QWDlSd z(6%6`3Y`R8Vu!v^!wgyu0vSjiAR4JwU^oHa8}yJxHk|5GggYJL*3Y;Z!f&?+-vA4- zTopSCJfDaUc*ze2V88FS;dEFd&ejgHuY1m)C5VKP7WL_ zujRo!Eh{5CIrDDJ*i`(Cn*GvOqCDv8H>-h?8JlC)i5`-}F%N{#7pHm;2F+P^-PnOl z_D*(vg{t2#U&%)^y@5HIEab_=)|yaNh2j4a=bfBPpaToLT{Fnu`a9l8DdcytlSvP9 zD=+hKwag@-E&YUMc!bSoWLCL5j75&vVTeIZ$kC)#YN8{M;}9^8t+``h9+N>Zz?)za zv_PT|UeFx7rz2jWS4xfc$=teP@uWPQd9)}w3<&J&%N$%4lZ~DKSuDxwbE`0)CKVos zv?jzDZP^!4^zrnE;Ba&PJUrXo)fNDfzIDJA=ip2>gk9v7`f8k)Reisc;63T|D~;Mn zQr)E;QwvKZkG^Ep>{;{aHz5>9`g#~}9PLhbN5JI;jTRGz$zhQ11}3^3a-l9v8WFmR zg`DkMgkeX%yvmQ8@2JIYw-`{#%d}pMNVO33d_`tON;imk=-2LXj55=0qZu7X9!SQF zph+xH51J8>NZG4oX-*S~EXjClos6D}_VQ$inke@{UOG1n#RAWO_(x?%!uIb*jOpRo z#)K%?C6n@?k4O2@A!|jpI;Y-ar21~DZ9N+vOM_a&IKClC-2|k`39k}EgO!jLIMjK< z-2QM<>MbYl@%*?Y;Vm=8wdJgMvSUzpXlZ6gUw#YFdqaI?XiIVPIjEELP#fVM#U0%f zbhZ}&CzJWbUGe;w7JRr*Dy~QcbgRi7DzgUKDbSEB$)A*AWf_1+~4S)WoC z{}_6ZjL)rHAWN9gnoD~|E-DIUP3=;|NQmdl{_7bzPGw)hP^%BLDk{_%^EQkxj)p(u zt_}K5jz_i!O9#cOA*J3{XqvYh&bUJ?Lava*HxQ^eQC_I2@nH_x9Sh5AHs9O&e%(iG0H|fhTcgsx zYU#?>4>_X1D)S9pXEJ7!yW|0Xhh3M+cv{uIS*6u+qS6#+oVrN(ll#}*ZKTbvCH4T* z-kF*ra+Ri49F*8*SdsM>XSt{CPIMQ|L%emDtsaq;4jXtx4&@I}@AQ}TkIFDx&2-Q6Gkt|qc ziINn(68VE>b$L6&lqFd5zOJHa1772#nIHt;OB8uKD)S@Jj%wO<@c9>f1?Arl6z!y^^hmz!h|AD&??(`#Y^^#La&;wfX8S;z%yZk4pAK}B$X^f# zf5jvHgWogZ){=%oedG6eKfg)9sS%J~sW*`E>kJ^8Or4-9zo2k()P+zybxKVX5@Rw! z_*E3DO$pTARtIO~8tLNCiNiiIGLx#Aivr|^)%UV<2d`!Zke*|u9Tc^N_aygA$;dAo z-{LE>nde;I9b6;!aY79Z4RHj8(eP*+;%hJfTZxvbd{)RAnC;f1_1YE(x!N7>otg^P ztoqr@y(AR!yU{TZObP~Z%^*@N$hMQ`D#gb*F=0qf^$E+yy#QhH7^G+yqcK5toqPaL zTqJaS0%E}ukT7Pe`jFc?dG@Gg5*tPRfjytWk-hh@qB}-Sk;gMPc242pUk_B_5HmhH zvwAi?|2U>`bnrv=kv#&}*utgXDP(DKov$PxBGsDBY(>@z%5%C=s3#hifx^_MzF2Is zn3g2z|K&Y^6ZQ`k78%D6P>6Aw*ru*1Sm2FDuoK$OTa{4MmM)Qc64xZ~8x?__-_HlFu88a%i5`l?VMU6-7ynT&K6` zP+Q!!NYstaZj((T>1}!o_D@qt@90c@yD2D>4PV=IHF1(PpEp;W!s)s?fGz5Ml{xBp zO(E)O4W2}Onb)Dc+A^G9m-O6{TRxFCw(l04)GU>skG?Q7PoX=55Tn~GCEYrbnb&p| z6>F?_JCtTnYnsYnT0}N;8k_;ptD!ifi-U_P)Vjhz8*vLxO9p~SfRl=s03s^4REej~ zGYSwC0s;|eI&9R>H7J*@Uo2H?KOs+fx8i>x)Ra@VQ~bPG?u?ho*^J;+$UP{Vv0pr4 zJ9or-Vvl*p58whNzunjnh{0!XvoHet zUZzD}xIBNsP0UJrp-_=-_^&7}35taS5o>L8EvIZ>B?CMrNLAWF0}eAV8@Np#8*08E zx?VqE5adh}3D)RSQe#a`4okvk51?9(N@gFcmwD4qTnZK7VH@g}W^p+;q3klb5?8~B zql-d#Zh&WRCiVhB*;>pF;$Znc$fiR%%ZzIQLkty+W1#enDU)VGR9ylJhSaSS@K@rX ze?29eYooUB(I=p17ri&265~{p)w?Dgm}`sOg@DX=&qvsdrXVXFu-;zWoPAiQ{(X~Y zT_%Pq_9)1IZu%u`rU=G|&jrOBX4{{s=Oa{l6q zF(dzZ7ag(48z@zQHf>)f=>ZEnP2|kf6XkOxrnREarM%+Ya?@j1Zp}_(=5`pV_CAb@ zjg340QyUwjGxqcIqGxxjlB`K*J|18jIuw2?mtvX#(2P__-AqWCKggs=M*B?AQ zGi+-PLEtNY!4_o+7c-F-SEH8Z=d z0Z=@xL`T4MIHP7n1F<|=L~D&q5akeakKog z<@k<8we&OU%o1Je*VKo)Tt6P0X!<#|Yqwa=rd`&Mq+B(ZhFCJ4Gvhcutd3-`CzPbv zf=F-#&@BM71U5H}JTor~^RZ?w_Ul-QUQ1+qnq&;#@{6dabz2kmd}NtLjrJo7EcwB8$J0pz#_Xe|kN7Pq|uN$A4ew1F^N z3bVizuDp)o$IBs4sf+C@`g+coP;V$i6VG|0$o>@44KpUVRARmHH#!IQ`W0zs04F>Yc zyb)JFNKEK-uoY`xoF~91{-Hi>iiEO@CQOZ;ngIG&iOU!5d=XE#Qp&tUxpemmuo01o zn`a5c3$`Q=*+3W_AHB~4iSJRp{P*Z4(5>f8ur`exyE-vr6}`EZhsH&3wBioM)lxcY zWAg&(Q?qNPSa8(^fSHCKGES-nl%GNs?LY*MIN!_g&IbFD7LyUr^6YF*V{m59=IONVG&6EAO5*lmUx74;UM%>Wg=^5xb<*P zRqNaNIEe|lr{KV&-^#}eIxK!JOWML`IiT68@`lBI$!Mo>@KC7og{aZO1(ZA6zV`iQ z+tijK;^>R=0a0z6+(WV1La*u{3~6jY(NzB4keMv0DnL7+NPoTeODg8a8UvZ%@1`<6 zLPis{0ziXIkzsN=8}p7%}sk52Fq1@SFYgsC24_bCJxI0plu$-7`9~=?TBW>`Jr+$K3CB{ zO8%3&w*Qoxn7njSUjH`<4ICIk(cE}k7!&8h-(qsLa(M-2Y`(fx5>#tl z_lVPH2=#j9lNx{eg{A2#KI{)ICU?DplJMO`r<7&VAVX9Nykj^FRa8CdBsQiN(uJHpuNu5w}6Q6e8850JZ4<^ z7N-HKvzNklz;kpP-w8H#ZDNLAW7jAjS8Mznfx&Xdvp%L*|h#;=K_!#J6X=8h1MNX;$sX-W1k$R$ZX47u4ps{D+8$#(~Y8(oC)) z&9Re!wy{!myg$ z>8=V4*ioOAA`wbGqkdX?2Q-rlaB{wUsS1=lpWsm-Odp2uTz-u4V&Lhe%I$(;*Neut zCfD3Z^!ILqQ-v?JRY!eKjDm_Z)mc~;_l8nSHb+Y_VZ9|W3ywiBaOgu8>Kg!G2VG`V zWeHRfQpWm7$vh{Gb<(gjtmxr7WG^_mK||d>bd40iOSjwX7H_keVSIzm$C{_Gi*Q%& zaxi(`-NJvj-ssLN>HbK#imudusVo$L=Ox|p$UaL*Z{soyG@5?N77dM>3cApx?w&bV ZxStO*vA*BD002KfiTr{^RJou4{|6Ct7%Kn( diff --git a/build/openrpc/gateway.json.gz b/build/openrpc/gateway.json.gz index d145c8939094c574aac4b8fbf5b83121686d0a13..f73254379ea88e1c21dbf41e9f6fd0940a267e64 100644 GIT binary patch literal 8387 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 EAF)`6^#A|> literal 8494 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 Date: Thu, 26 Jan 2023 17:20:47 +0000 Subject: [PATCH 14/30] Changed skip message --- itests/eth_conformance_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/itests/eth_conformance_test.go b/itests/eth_conformance_test.go index 6e21101d2..f37d03942 100644 --- a/itests/eth_conformance_test.go +++ b/itests/eth_conformance_test.go @@ -138,7 +138,7 @@ func TestEthOpenRPCConformance(t *testing.T) { require.NoError(t, err) defer closer() - const skipUntilIssue10106 = "Skipped until https://github.com/filecoin-project/lotus/issues/10106 is addressed" + const skipUntilIssue10106 = "Skipped until EthTx is updated, see https://github.com/filecoin-project/lotus/issues/10106" testCases := []struct { method string From 98d6d4eb17d854a90c6c2fd785e7f0ceebdeb246 Mon Sep 17 00:00:00 2001 From: Ian Davis Date: Thu, 26 Jan 2023 17:27:55 +0000 Subject: [PATCH 15/30] Remove stm: #integration comment --- itests/eth_filter_test.go | 1 - 1 file changed, 1 deletion(-) diff --git a/itests/eth_filter_test.go b/itests/eth_filter_test.go index 1263696b3..a15f6d09a 100644 --- a/itests/eth_filter_test.go +++ b/itests/eth_filter_test.go @@ -1,4 +1,3 @@ -// stm: #integration package itests import ( From 6efe08dd61861486ef50ddb9fc5e282ee338e6d3 Mon Sep 17 00:00:00 2001 From: Ian Davis Date: Thu, 26 Jan 2023 17:39:50 +0000 Subject: [PATCH 16/30] Fix comment --- itests/eth_conformance_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/itests/eth_conformance_test.go b/itests/eth_conformance_test.go index f37d03942..700427ce4 100644 --- a/itests/eth_conformance_test.go +++ b/itests/eth_conformance_test.go @@ -28,7 +28,7 @@ import ( "github.com/filecoin-project/lotus/itests/kit" ) -// TODO generate this using reflection +// TODO generate this using reflection. It's the same as the EthAPI except every return value is a json.RawMessage type ethAPIRaw struct { EthAccounts func(context.Context) (json.RawMessage, error) EthBlockNumber func(context.Context) (json.RawMessage, error) From 67805fd25afaf21e3a984c59a984f664980f5d6c Mon Sep 17 00:00:00 2001 From: Ian Davis Date: Fri, 27 Jan 2023 15:13:38 +0000 Subject: [PATCH 17/30] Refactor to ensure conformance test can run in circleci --- itests/eth_conformance_test.go | 55 ++--- itests/eth_filter_test.go | 364 +++++++++++---------------------- itests/kit/evm.go | 75 +++++++ itests/kit/solidity.go | 64 ++++++ 4 files changed, 283 insertions(+), 275 deletions(-) create mode 100644 itests/kit/solidity.go diff --git a/itests/eth_conformance_test.go b/itests/eth_conformance_test.go index 700427ce4..5d04b5948 100644 --- a/itests/eth_conformance_test.go +++ b/itests/eth_conformance_test.go @@ -3,6 +3,7 @@ package itests import ( "bytes" "context" + "encoding/binary" "encoding/hex" "encoding/json" "os" @@ -87,7 +88,7 @@ func TestEthOpenRPCConformance(t *testing.T) { client, _, ens := kit.EnsembleMinimal(t, kit.MockProofs(), kit.ThroughRPC(), kit.WithEthRPC()) ens.InterconnectAll().BeginMining(10 * time.Millisecond) - contractHex, err := os.ReadFile(EventMatrixContract.Filename) + contractHex, err := os.ReadFile("contracts/EventMatrix.hex") require.NoError(t, err) // strip any trailing newlines from the file @@ -109,7 +110,7 @@ func TestEthOpenRPCConformance(t *testing.T) { blockFilterID, err := client.EthNewBlockFilter(ctx) require.NoError(t, err) - filterAllLogs := newEthFilterBuilder().FromBlockEpoch(0).Filter() + filterAllLogs := kit.NewEthFilterBuilder().FromBlockEpoch(0).Filter() logFilterID, err := client.EthNewFilter(ctx, filterAllLogs) require.NoError(t, err) @@ -488,31 +489,31 @@ func createRawSignedEthTx(ctx context.Context, t *testing.T, client *kit.TestFul } func waitForMessageWithEvents(ctx context.Context, t *testing.T, client *kit.TestFullNode, sender address.Address, target address.Address) (ethtypes.EthHash, ethtypes.EthHash, ethtypes.EthUint64) { - // send a message that exercises event logs - messages := invokeAndWaitUntilAllOnChain(t, client, []Invocation{ - { - Sender: sender, - Target: target, - Selector: EventMatrixContract.Fn["logEventThreeIndexedWithData"], - Data: packUint64Values(44, 27, 19, 12), - }, - }) - - require.NotEmpty(t, messages) - - for msgHash, mts := range messages { - ts := mts.ts - blockNumber := ethtypes.EthUint64(ts.Height()) - - tsCid, err := ts.Key().Cid() - require.NoError(t, err) - - blockHash, err := ethtypes.EthHashFromCid(tsCid) - require.NoError(t, err) - return msgHash, blockHash, blockNumber + vals := []uint64{44, 27, 19, 12} + inputData := []byte{} + for _, v := range vals { + buf := make([]byte, 32) + binary.BigEndian.PutUint64(buf[24:], v) + inputData = append(inputData, buf...) } - // should not get here - t.FailNow() - return ethtypes.EthHash{}, ethtypes.EthHash{}, 0 + // send a message that exercises event logs + ret := client.EVM().InvokeSolidity(ctx, sender, target, kit.EventMatrixContract.Fn["logEventThreeIndexedWithData"], inputData) + require.True(t, ret.Receipt.ExitCode.IsSuccess(), "contract execution failed") + + msgHash, err := client.EthGetTransactionHashByCid(ctx, ret.Message) + require.NoError(t, err) + require.NotNil(t, msgHash) + + ts, err := client.ChainGetTipSet(ctx, ret.TipSet) + require.NoError(t, err) + + blockNumber := ethtypes.EthUint64(ts.Height()) + + tsCid, err := ts.Key().Cid() + require.NoError(t, err) + + blockHash, err := ethtypes.EthHashFromCid(tsCid) + require.NoError(t, err) + return *msgHash, blockHash, blockNumber } diff --git a/itests/eth_filter_test.go b/itests/eth_filter_test.go index a15f6d09a..028126a78 100644 --- a/itests/eth_filter_test.go +++ b/itests/eth_filter_test.go @@ -16,7 +16,6 @@ import ( "github.com/ipfs/go-cid" "github.com/stretchr/testify/require" cbg "github.com/whyrusleeping/cbor-gen" - "golang.org/x/crypto/sha3" "golang.org/x/xerrors" "github.com/filecoin-project/go-address" @@ -30,53 +29,6 @@ import ( "github.com/filecoin-project/lotus/itests/kit" ) -// SolidityContractDef holds information about one of the test contracts -type SolidityContractDef struct { - Filename string // filename of the hex of the contract, e.g. contracts/EventMatrix.hex - Fn map[string][]byte // mapping of function names to 32-bit selector - Ev map[string][]byte // mapping of event names to 256-bit signature hashes -} - -var EventMatrixContract = SolidityContractDef{ - Filename: "contracts/EventMatrix.hex", - Fn: map[string][]byte{ - "logEventZeroData": ethFunctionHash("logEventZeroData()"), - "logEventOneData": ethFunctionHash("logEventOneData(uint256)"), - "logEventTwoData": ethFunctionHash("logEventTwoData(uint256,uint256)"), - "logEventThreeData": ethFunctionHash("logEventThreeData(uint256,uint256,uint256)"), - "logEventFourData": ethFunctionHash("logEventFourData(uint256,uint256,uint256,uint256)"), - "logEventOneIndexed": ethFunctionHash("logEventOneIndexed(uint256)"), - "logEventTwoIndexed": ethFunctionHash("logEventTwoIndexed(uint256,uint256)"), - "logEventThreeIndexed": ethFunctionHash("logEventThreeIndexed(uint256,uint256,uint256)"), - "logEventOneIndexedWithData": ethFunctionHash("logEventOneIndexedWithData(uint256,uint256)"), - "logEventTwoIndexedWithData": ethFunctionHash("logEventTwoIndexedWithData(uint256,uint256,uint256)"), - "logEventThreeIndexedWithData": ethFunctionHash("logEventThreeIndexedWithData(uint256,uint256,uint256,uint256)"), - }, - Ev: map[string][]byte{ - "EventZeroData": ethTopicHash("EventZeroData()"), - "EventOneData": ethTopicHash("EventOneData(uint256)"), - "EventTwoData": ethTopicHash("EventTwoData(uint256,uint256)"), - "EventThreeData": ethTopicHash("EventThreeData(uint256,uint256,uint256)"), - "EventFourData": ethTopicHash("EventFourData(uint256,uint256,uint256,uint256)"), - "EventOneIndexed": ethTopicHash("EventOneIndexed(uint256)"), - "EventTwoIndexed": ethTopicHash("EventTwoIndexed(uint256,uint256)"), - "EventThreeIndexed": ethTopicHash("EventThreeIndexed(uint256,uint256,uint256)"), - "EventOneIndexedWithData": ethTopicHash("EventOneIndexedWithData(uint256,uint256)"), - "EventTwoIndexedWithData": ethTopicHash("EventTwoIndexedWithData(uint256,uint256,uint256)"), - "EventThreeIndexedWithData": ethTopicHash("EventThreeIndexedWithData(uint256,uint256,uint256,uint256)"), - }, -} - -var EventsContract = SolidityContractDef{ - Filename: "contracts/events.bin", - Fn: map[string][]byte{ - "log_zero_data": {0x00, 0x00, 0x00, 0x00}, - "log_zero_nodata": {0x00, 0x00, 0x00, 0x01}, - "log_four_data": {0x00, 0x00, 0x00, 0x02}, - }, - Ev: map[string][]byte{}, -} - func TestEthNewPendingTransactionFilter(t *testing.T) { ctx, cancel := context.WithTimeout(context.Background(), time.Minute) defer cancel() @@ -381,7 +333,7 @@ func TestEthGetLogsBasic(t *testing.T) { ethContractAddr, received := invokeLogFourData(t, client, invocations) // Build filter spec - spec := newEthFilterBuilder(). + spec := kit.NewEthFilterBuilder(). FromBlockEpoch(0). Topic1OneOf(paddedEthHash([]byte{0x11, 0x11})). Filter() @@ -699,7 +651,7 @@ func TestEthGetLogsWithBlockRanges(t *testing.T) { // Select events for partitioning for _, m := range messages { - if bytes.Equal(m.invocation.Selector, EventMatrixContract.Fn["logEventTwoIndexedWithData"]) { + if bytes.Equal(m.invocation.Selector, kit.EventMatrixContract.Fn["logEventTwoIndexedWithData"]) { addr := getEthAddress(ctx, t, client, m.invocation.Target) args := unpackUint64Values(m.invocation.Data) require.Equal(3, len(args), "logEventTwoIndexedWithData should have 3 arguments") @@ -708,7 +660,7 @@ func TestEthGetLogsWithBlockRanges(t *testing.T) { expectedByHeight[m.ts.Height()] = append(expectedByHeight[m.ts.Height()], ExpectedEthLog{ Address: addr, Topics: []ethtypes.EthBytes{ - EventMatrixContract.Ev["EventTwoIndexedWithData"], + kit.EventMatrixContract.Ev["EventTwoIndexedWithData"], paddedUint64(args[0]), paddedUint64(args[1]), }, @@ -773,7 +725,7 @@ func TestEthGetLogsWithBlockRanges(t *testing.T) { require.True(len(partition3.expected) > 0, "partition should have events") // these are the topics we selected for partitioning earlier - topics := []ethtypes.EthHash{paddedEthHash(EventMatrixContract.Ev["EventTwoIndexedWithData"])} + topics := []ethtypes.EthHash{paddedEthHash(kit.EventMatrixContract.Ev["EventTwoIndexedWithData"])} union := func(lists ...[]ExpectedEthLog) []ExpectedEthLog { ret := []ExpectedEthLog{} @@ -790,91 +742,91 @@ func TestEthGetLogsWithBlockRanges(t *testing.T) { }{ { name: "find all events from genesis", - spec: newEthFilterBuilder().FromBlockEpoch(0).Topic1OneOf(topics...).Filter(), + spec: kit.NewEthFilterBuilder().FromBlockEpoch(0).Topic1OneOf(topics...).Filter(), expected: union(partition1.expected, partition2.expected, partition3.expected), }, { name: "find all from start of partition1", - spec: newEthFilterBuilder().FromBlockEpoch(partition1.start).Topic1OneOf(topics...).Filter(), + spec: kit.NewEthFilterBuilder().FromBlockEpoch(partition1.start).Topic1OneOf(topics...).Filter(), expected: union(partition1.expected, partition2.expected, partition3.expected), }, { name: "find all from start of partition2", - spec: newEthFilterBuilder().FromBlockEpoch(partition2.start).Topic1OneOf(topics...).Filter(), + spec: kit.NewEthFilterBuilder().FromBlockEpoch(partition2.start).Topic1OneOf(topics...).Filter(), expected: union(partition2.expected, partition3.expected), }, { name: "find all from start of partition3", - spec: newEthFilterBuilder().FromBlockEpoch(partition3.start).Topic1OneOf(topics...).Filter(), + spec: kit.NewEthFilterBuilder().FromBlockEpoch(partition3.start).Topic1OneOf(topics...).Filter(), expected: union(partition3.expected), }, { name: "find none after end of partition3", - spec: newEthFilterBuilder().FromBlockEpoch(partition3.end + 1).Topic1OneOf(topics...).Filter(), + spec: kit.NewEthFilterBuilder().FromBlockEpoch(partition3.end + 1).Topic1OneOf(topics...).Filter(), expected: nil, }, { name: "find all events from genesis to end of partition1", - spec: newEthFilterBuilder().FromBlockEpoch(0).ToBlockEpoch(partition1.end).Topic1OneOf(topics...).Filter(), + spec: kit.NewEthFilterBuilder().FromBlockEpoch(0).ToBlockEpoch(partition1.end).Topic1OneOf(topics...).Filter(), expected: union(partition1.expected), }, { name: "find all events from genesis to end of partition2", - spec: newEthFilterBuilder().FromBlockEpoch(0).ToBlockEpoch(partition2.end).Topic1OneOf(topics...).Filter(), + spec: kit.NewEthFilterBuilder().FromBlockEpoch(0).ToBlockEpoch(partition2.end).Topic1OneOf(topics...).Filter(), expected: union(partition1.expected, partition2.expected), }, { name: "find all events from genesis to end of partition3", - spec: newEthFilterBuilder().FromBlockEpoch(0).ToBlockEpoch(partition3.end).Topic1OneOf(topics...).Filter(), + spec: kit.NewEthFilterBuilder().FromBlockEpoch(0).ToBlockEpoch(partition3.end).Topic1OneOf(topics...).Filter(), expected: union(partition1.expected, partition2.expected, partition3.expected), }, { name: "find none from genesis to start of partition1", - spec: newEthFilterBuilder().FromBlockEpoch(0).ToBlockEpoch(partition1.start - 1).Topic1OneOf(topics...).Filter(), + spec: kit.NewEthFilterBuilder().FromBlockEpoch(0).ToBlockEpoch(partition1.start - 1).Topic1OneOf(topics...).Filter(), expected: nil, }, { name: "find all events in partition1", - spec: newEthFilterBuilder().FromBlockEpoch(partition1.start).ToBlockEpoch(partition1.end).Topic1OneOf(topics...).Filter(), + spec: kit.NewEthFilterBuilder().FromBlockEpoch(partition1.start).ToBlockEpoch(partition1.end).Topic1OneOf(topics...).Filter(), expected: union(partition1.expected), }, { name: "find all events in partition2", - spec: newEthFilterBuilder().FromBlockEpoch(partition2.start).ToBlockEpoch(partition2.end).Topic1OneOf(topics...).Filter(), + spec: kit.NewEthFilterBuilder().FromBlockEpoch(partition2.start).ToBlockEpoch(partition2.end).Topic1OneOf(topics...).Filter(), expected: union(partition2.expected), }, { name: "find all events in partition3", - spec: newEthFilterBuilder().FromBlockEpoch(partition3.start).ToBlockEpoch(partition3.end).Topic1OneOf(topics...).Filter(), + spec: kit.NewEthFilterBuilder().FromBlockEpoch(partition3.start).ToBlockEpoch(partition3.end).Topic1OneOf(topics...).Filter(), expected: union(partition3.expected), }, { name: "find all events from earliest to end of partition1", - spec: newEthFilterBuilder().FromBlock("earliest").ToBlockEpoch(partition1.end).Topic1OneOf(topics...).Filter(), + spec: kit.NewEthFilterBuilder().FromBlock("earliest").ToBlockEpoch(partition1.end).Topic1OneOf(topics...).Filter(), expected: union(partition1.expected), }, { name: "find all events from start of partition3 to latest", - spec: newEthFilterBuilder().FromBlockEpoch(partition3.start).ToBlock("latest").Topic1OneOf(topics...).Filter(), + spec: kit.NewEthFilterBuilder().FromBlockEpoch(partition3.start).ToBlock("latest").Topic1OneOf(topics...).Filter(), expected: union(partition3.expected), }, { name: "find all events from earliest to latest", - spec: newEthFilterBuilder().FromBlock("earliest").ToBlock("latest").Topic1OneOf(topics...).Filter(), + spec: kit.NewEthFilterBuilder().FromBlock("earliest").ToBlock("latest").Topic1OneOf(topics...).Filter(), expected: union(partition1.expected, partition2.expected, partition3.expected), }, } @@ -904,20 +856,20 @@ func TestEthNewFilterMergesHistoricWithRealtime(t *testing.T) { ctx, cancel := context.WithTimeout(context.Background(), time.Minute) defer cancel() - sender, contract := client.EVM().DeployContractFromFilename(ctx, EventMatrixContract.Filename) + sender, contract := client.EVM().DeployContractFromFilename(ctx, kit.EventMatrixContract.Filename) // generate some events before the creation of the filter preInvocations := []Invocation{ { Sender: sender, Target: contract, - Selector: EventMatrixContract.Fn["logEventOneData"], + Selector: kit.EventMatrixContract.Fn["logEventOneData"], Data: packUint64Values(1), }, { Sender: sender, Target: contract, - Selector: EventMatrixContract.Fn["logEventOneIndexed"], + Selector: kit.EventMatrixContract.Fn["logEventOneIndexed"], Data: packUint64Values(2), }, } @@ -925,7 +877,7 @@ func TestEthNewFilterMergesHistoricWithRealtime(t *testing.T) { messages := invokeAndWaitUntilAllOnChain(t, client, preInvocations) // now install filter - spec := newEthFilterBuilder().FromBlock("earliest").Filter() + spec := kit.NewEthFilterBuilder().FromBlock("earliest").Filter() filterID, err := client.EthNewFilter(ctx, spec) require.NoError(err) @@ -935,13 +887,13 @@ func TestEthNewFilterMergesHistoricWithRealtime(t *testing.T) { { Sender: sender, Target: contract, - Selector: EventMatrixContract.Fn["logEventOneData"], + Selector: kit.EventMatrixContract.Fn["logEventOneData"], Data: packUint64Values(3), }, { Sender: sender, Target: contract, - Selector: EventMatrixContract.Fn["logEventOneIndexed"], + Selector: kit.EventMatrixContract.Fn["logEventOneIndexed"], Data: packUint64Values(4), }, } @@ -962,28 +914,28 @@ func TestEthNewFilterMergesHistoricWithRealtime(t *testing.T) { { Address: ethContractAddr, Topics: []ethtypes.EthBytes{ - EventMatrixContract.Ev["EventOneData"], + kit.EventMatrixContract.Ev["EventOneData"], }, Data: paddedUint64(1), }, { Address: ethContractAddr, Topics: []ethtypes.EthBytes{ - EventMatrixContract.Ev["EventOneIndexed"], + kit.EventMatrixContract.Ev["EventOneIndexed"], paddedUint64(2), }, }, { Address: ethContractAddr, Topics: []ethtypes.EthBytes{ - EventMatrixContract.Ev["EventOneData"], + kit.EventMatrixContract.Ev["EventOneData"], }, Data: paddedUint64(3), }, { Address: ethContractAddr, Topics: []ethtypes.EthBytes{ - EventMatrixContract.Ev["EventOneIndexed"], + kit.EventMatrixContract.Ev["EventOneIndexed"], paddedUint64(4), }, }, @@ -1143,14 +1095,14 @@ func invokeLogFourData(t *testing.T, client *kit.TestFullNode, iterations int) ( ctx, cancel := context.WithTimeout(context.Background(), time.Minute) defer cancel() - fromAddr, idAddr := client.EVM().DeployContractFromFilename(ctx, EventsContract.Filename) + fromAddr, idAddr := client.EVM().DeployContractFromFilename(ctx, kit.EventsContract.Filename) invocations := make([]Invocation, iterations) for i := range invocations { invocations[i] = Invocation{ Sender: fromAddr, Target: idAddr, - Selector: EventsContract.Fn["log_four_data"], + Selector: kit.EventsContract.Fn["log_four_data"], Data: nil, } } @@ -1163,8 +1115,8 @@ func invokeLogFourData(t *testing.T, client *kit.TestFullNode, iterations int) ( } func prepareEventMatrixInvocations(ctx context.Context, t *testing.T, client *kit.TestFullNode) (ethtypes.EthAddress, ethtypes.EthAddress, []Invocation) { - sender1, contract1 := client.EVM().DeployContractFromFilename(ctx, EventMatrixContract.Filename) - sender2, contract2 := client.EVM().DeployContractFromFilename(ctx, EventMatrixContract.Filename) + sender1, contract1 := client.EVM().DeployContractFromFilename(ctx, kit.EventMatrixContract.Filename) + sender2, contract2 := client.EVM().DeployContractFromFilename(ctx, kit.EventMatrixContract.Filename) invocations := []Invocation{ // log EventZeroData() @@ -1172,7 +1124,7 @@ func prepareEventMatrixInvocations(ctx context.Context, t *testing.T, client *ki { Sender: sender1, Target: contract1, - Selector: EventMatrixContract.Fn["logEventZeroData"], + Selector: kit.EventMatrixContract.Fn["logEventZeroData"], Data: nil, }, @@ -1182,7 +1134,7 @@ func prepareEventMatrixInvocations(ctx context.Context, t *testing.T, client *ki { Sender: sender1, Target: contract1, - Selector: EventMatrixContract.Fn["logEventOneData"], + Selector: kit.EventMatrixContract.Fn["logEventOneData"], Data: packUint64Values(23), }, @@ -1192,7 +1144,7 @@ func prepareEventMatrixInvocations(ctx context.Context, t *testing.T, client *ki { Sender: sender1, Target: contract1, - Selector: EventMatrixContract.Fn["logEventOneIndexed"], + Selector: kit.EventMatrixContract.Fn["logEventOneIndexed"], Data: packUint64Values(44), }, @@ -1203,7 +1155,7 @@ func prepareEventMatrixInvocations(ctx context.Context, t *testing.T, client *ki { Sender: sender2, Target: contract2, - Selector: EventMatrixContract.Fn["logEventTwoIndexed"], + Selector: kit.EventMatrixContract.Fn["logEventTwoIndexed"], Data: packUint64Values(44, 19), }, @@ -1213,7 +1165,7 @@ func prepareEventMatrixInvocations(ctx context.Context, t *testing.T, client *ki { Sender: sender1, Target: contract1, - Selector: EventMatrixContract.Fn["logEventOneData"], + Selector: kit.EventMatrixContract.Fn["logEventOneData"], Data: packUint64Values(44), }, @@ -1223,7 +1175,7 @@ func prepareEventMatrixInvocations(ctx context.Context, t *testing.T, client *ki { Sender: sender1, Target: contract1, - Selector: EventMatrixContract.Fn["logEventTwoData"], + Selector: kit.EventMatrixContract.Fn["logEventTwoData"], Data: packUint64Values(555, 666), }, @@ -1232,7 +1184,7 @@ func prepareEventMatrixInvocations(ctx context.Context, t *testing.T, client *ki { Sender: sender2, Target: contract2, - Selector: EventMatrixContract.Fn["logEventZeroData"], + Selector: kit.EventMatrixContract.Fn["logEventZeroData"], Data: nil, }, @@ -1242,7 +1194,7 @@ func prepareEventMatrixInvocations(ctx context.Context, t *testing.T, client *ki { Sender: sender1, Target: contract1, - Selector: EventMatrixContract.Fn["logEventThreeData"], + Selector: kit.EventMatrixContract.Fn["logEventThreeData"], Data: packUint64Values(1, 2, 3), }, @@ -1254,7 +1206,7 @@ func prepareEventMatrixInvocations(ctx context.Context, t *testing.T, client *ki { Sender: sender1, Target: contract2, - Selector: EventMatrixContract.Fn["logEventThreeIndexed"], + Selector: kit.EventMatrixContract.Fn["logEventThreeIndexed"], Data: packUint64Values(44, 27, 19), }, @@ -1265,7 +1217,7 @@ func prepareEventMatrixInvocations(ctx context.Context, t *testing.T, client *ki { Sender: sender1, Target: contract1, - Selector: EventMatrixContract.Fn["logEventOneIndexedWithData"], + Selector: kit.EventMatrixContract.Fn["logEventOneIndexedWithData"], Data: packUint64Values(44, 19), }, @@ -1276,7 +1228,7 @@ func prepareEventMatrixInvocations(ctx context.Context, t *testing.T, client *ki { Sender: sender1, Target: contract1, - Selector: EventMatrixContract.Fn["logEventOneIndexedWithData"], + Selector: kit.EventMatrixContract.Fn["logEventOneIndexedWithData"], Data: packUint64Values(46, 12), }, @@ -1288,7 +1240,7 @@ func prepareEventMatrixInvocations(ctx context.Context, t *testing.T, client *ki { Sender: sender1, Target: contract1, - Selector: EventMatrixContract.Fn["logEventTwoIndexedWithData"], + Selector: kit.EventMatrixContract.Fn["logEventTwoIndexedWithData"], Data: packUint64Values(44, 27, 19), }, @@ -1301,7 +1253,7 @@ func prepareEventMatrixInvocations(ctx context.Context, t *testing.T, client *ki { Sender: sender1, Target: contract1, - Selector: EventMatrixContract.Fn["logEventThreeIndexedWithData"], + Selector: kit.EventMatrixContract.Fn["logEventThreeIndexedWithData"], Data: packUint64Values(44, 27, 19, 12), }, @@ -1312,7 +1264,7 @@ func prepareEventMatrixInvocations(ctx context.Context, t *testing.T, client *ki { Sender: sender2, Target: contract2, - Selector: EventMatrixContract.Fn["logEventOneIndexedWithData"], + Selector: kit.EventMatrixContract.Fn["logEventOneIndexedWithData"], Data: packUint64Values(50, 9), }, @@ -1324,7 +1276,7 @@ func prepareEventMatrixInvocations(ctx context.Context, t *testing.T, client *ki { Sender: sender1, Target: contract1, - Selector: EventMatrixContract.Fn["logEventTwoIndexedWithData"], + Selector: kit.EventMatrixContract.Fn["logEventTwoIndexedWithData"], Data: packUint64Values(46, 27, 19), }, @@ -1336,7 +1288,7 @@ func prepareEventMatrixInvocations(ctx context.Context, t *testing.T, client *ki { Sender: sender1, Target: contract1, - Selector: EventMatrixContract.Fn["logEventTwoIndexedWithData"], + Selector: kit.EventMatrixContract.Fn["logEventTwoIndexedWithData"], Data: packUint64Values(46, 14, 19), }, // log EventTwoIndexed(44,19) from contract1 @@ -1346,7 +1298,7 @@ func prepareEventMatrixInvocations(ctx context.Context, t *testing.T, client *ki { Sender: sender1, Target: contract1, - Selector: EventMatrixContract.Fn["logEventTwoIndexed"], + Selector: kit.EventMatrixContract.Fn["logEventTwoIndexed"], Data: packUint64Values(40, 20), }, } @@ -1374,20 +1326,20 @@ func getTopicFilterTestCases(contract1, contract2 ethtypes.EthAddress, fromBlock return []filterTestCase{ { name: "find all EventZeroData events", - spec: newEthFilterBuilder().FromBlock(fromBlock).Topic1OneOf(paddedEthHash(EventMatrixContract.Ev["EventZeroData"])).Filter(), + spec: kit.NewEthFilterBuilder().FromBlock(fromBlock).Topic1OneOf(paddedEthHash(kit.EventMatrixContract.Ev["EventZeroData"])).Filter(), expected: []ExpectedEthLog{ { Address: contract1, Topics: []ethtypes.EthBytes{ - EventMatrixContract.Ev["EventZeroData"], + kit.EventMatrixContract.Ev["EventZeroData"], }, Data: nil, }, { Address: contract2, Topics: []ethtypes.EthBytes{ - EventMatrixContract.Ev["EventZeroData"], + kit.EventMatrixContract.Ev["EventZeroData"], }, Data: nil, }, @@ -1395,20 +1347,20 @@ func getTopicFilterTestCases(contract1, contract2 ethtypes.EthAddress, fromBlock }, { name: "find all EventOneData events", - spec: newEthFilterBuilder().FromBlock(fromBlock).Topic1OneOf(paddedEthHash(EventMatrixContract.Ev["EventOneData"])).Filter(), + spec: kit.NewEthFilterBuilder().FromBlock(fromBlock).Topic1OneOf(paddedEthHash(kit.EventMatrixContract.Ev["EventOneData"])).Filter(), expected: []ExpectedEthLog{ { Address: contract1, Topics: []ethtypes.EthBytes{ - EventMatrixContract.Ev["EventOneData"], + kit.EventMatrixContract.Ev["EventOneData"], }, Data: packUint64Values(23), }, { Address: contract1, Topics: []ethtypes.EthBytes{ - EventMatrixContract.Ev["EventOneData"], + kit.EventMatrixContract.Ev["EventOneData"], }, Data: packUint64Values(44), }, @@ -1416,13 +1368,13 @@ func getTopicFilterTestCases(contract1, contract2 ethtypes.EthAddress, fromBlock }, { name: "find all EventTwoData events", - spec: newEthFilterBuilder().FromBlock(fromBlock).Topic1OneOf(paddedEthHash(EventMatrixContract.Ev["EventTwoData"])).Filter(), + spec: kit.NewEthFilterBuilder().FromBlock(fromBlock).Topic1OneOf(paddedEthHash(kit.EventMatrixContract.Ev["EventTwoData"])).Filter(), expected: []ExpectedEthLog{ { Address: contract1, Topics: []ethtypes.EthBytes{ - EventMatrixContract.Ev["EventTwoData"], + kit.EventMatrixContract.Ev["EventTwoData"], }, Data: packUint64Values(555, 666), }, @@ -1430,13 +1382,13 @@ func getTopicFilterTestCases(contract1, contract2 ethtypes.EthAddress, fromBlock }, { name: "find all EventThreeData events", - spec: newEthFilterBuilder().FromBlock(fromBlock).Topic1OneOf(paddedEthHash(EventMatrixContract.Ev["EventThreeData"])).Filter(), + spec: kit.NewEthFilterBuilder().FromBlock(fromBlock).Topic1OneOf(paddedEthHash(kit.EventMatrixContract.Ev["EventThreeData"])).Filter(), expected: []ExpectedEthLog{ { Address: contract1, Topics: []ethtypes.EthBytes{ - EventMatrixContract.Ev["EventThreeData"], + kit.EventMatrixContract.Ev["EventThreeData"], }, Data: packUint64Values(1, 2, 3), }, @@ -1444,13 +1396,13 @@ func getTopicFilterTestCases(contract1, contract2 ethtypes.EthAddress, fromBlock }, { name: "find all EventOneIndexed events", - spec: newEthFilterBuilder().FromBlock(fromBlock).Topic1OneOf(paddedEthHash(EventMatrixContract.Ev["EventOneIndexed"])).Filter(), + spec: kit.NewEthFilterBuilder().FromBlock(fromBlock).Topic1OneOf(paddedEthHash(kit.EventMatrixContract.Ev["EventOneIndexed"])).Filter(), expected: []ExpectedEthLog{ { Address: contract1, Topics: []ethtypes.EthBytes{ - EventMatrixContract.Ev["EventOneIndexed"], + kit.EventMatrixContract.Ev["EventOneIndexed"], paddedUint64(44), }, Data: nil, @@ -1459,13 +1411,13 @@ func getTopicFilterTestCases(contract1, contract2 ethtypes.EthAddress, fromBlock }, { name: "find all EventTwoIndexed events", - spec: newEthFilterBuilder().FromBlock(fromBlock).Topic1OneOf(paddedEthHash(EventMatrixContract.Ev["EventTwoIndexed"])).Filter(), + spec: kit.NewEthFilterBuilder().FromBlock(fromBlock).Topic1OneOf(paddedEthHash(kit.EventMatrixContract.Ev["EventTwoIndexed"])).Filter(), expected: []ExpectedEthLog{ { Address: contract2, Topics: []ethtypes.EthBytes{ - EventMatrixContract.Ev["EventTwoIndexed"], + kit.EventMatrixContract.Ev["EventTwoIndexed"], paddedUint64(44), paddedUint64(19), }, @@ -1474,7 +1426,7 @@ func getTopicFilterTestCases(contract1, contract2 ethtypes.EthAddress, fromBlock { Address: contract1, Topics: []ethtypes.EthBytes{ - EventMatrixContract.Ev["EventTwoIndexed"], + kit.EventMatrixContract.Ev["EventTwoIndexed"], paddedUint64(40), paddedUint64(20), }, @@ -1484,13 +1436,13 @@ func getTopicFilterTestCases(contract1, contract2 ethtypes.EthAddress, fromBlock }, { name: "find all EventThreeIndexed events", - spec: newEthFilterBuilder().FromBlock(fromBlock).Topic1OneOf(paddedEthHash(EventMatrixContract.Ev["EventThreeIndexed"])).Filter(), + spec: kit.NewEthFilterBuilder().FromBlock(fromBlock).Topic1OneOf(paddedEthHash(kit.EventMatrixContract.Ev["EventThreeIndexed"])).Filter(), expected: []ExpectedEthLog{ { Address: contract2, Topics: []ethtypes.EthBytes{ - EventMatrixContract.Ev["EventThreeIndexed"], + kit.EventMatrixContract.Ev["EventThreeIndexed"], paddedUint64(44), paddedUint64(27), paddedUint64(19), @@ -1501,13 +1453,13 @@ func getTopicFilterTestCases(contract1, contract2 ethtypes.EthAddress, fromBlock }, { name: "find all EventOneIndexedWithData events", - spec: newEthFilterBuilder().FromBlock(fromBlock).Topic1OneOf(paddedEthHash(EventMatrixContract.Ev["EventOneIndexedWithData"])).Filter(), + spec: kit.NewEthFilterBuilder().FromBlock(fromBlock).Topic1OneOf(paddedEthHash(kit.EventMatrixContract.Ev["EventOneIndexedWithData"])).Filter(), expected: []ExpectedEthLog{ { Address: contract1, Topics: []ethtypes.EthBytes{ - EventMatrixContract.Ev["EventOneIndexedWithData"], + kit.EventMatrixContract.Ev["EventOneIndexedWithData"], paddedUint64(44), }, Data: paddedUint64(19), @@ -1515,7 +1467,7 @@ func getTopicFilterTestCases(contract1, contract2 ethtypes.EthAddress, fromBlock { Address: contract1, Topics: []ethtypes.EthBytes{ - EventMatrixContract.Ev["EventOneIndexedWithData"], + kit.EventMatrixContract.Ev["EventOneIndexedWithData"], paddedUint64(46), }, Data: paddedUint64(12), @@ -1523,7 +1475,7 @@ func getTopicFilterTestCases(contract1, contract2 ethtypes.EthAddress, fromBlock { Address: contract2, Topics: []ethtypes.EthBytes{ - EventMatrixContract.Ev["EventOneIndexedWithData"], + kit.EventMatrixContract.Ev["EventOneIndexedWithData"], paddedUint64(50), }, Data: paddedUint64(9), @@ -1532,13 +1484,13 @@ func getTopicFilterTestCases(contract1, contract2 ethtypes.EthAddress, fromBlock }, { name: "find all EventTwoIndexedWithData events", - spec: newEthFilterBuilder().FromBlock(fromBlock).Topic1OneOf(paddedEthHash(EventMatrixContract.Ev["EventTwoIndexedWithData"])).Filter(), + spec: kit.NewEthFilterBuilder().FromBlock(fromBlock).Topic1OneOf(paddedEthHash(kit.EventMatrixContract.Ev["EventTwoIndexedWithData"])).Filter(), expected: []ExpectedEthLog{ { Address: contract1, Topics: []ethtypes.EthBytes{ - EventMatrixContract.Ev["EventTwoIndexedWithData"], + kit.EventMatrixContract.Ev["EventTwoIndexedWithData"], paddedUint64(44), paddedUint64(27), }, @@ -1547,7 +1499,7 @@ func getTopicFilterTestCases(contract1, contract2 ethtypes.EthAddress, fromBlock { Address: contract1, Topics: []ethtypes.EthBytes{ - EventMatrixContract.Ev["EventTwoIndexedWithData"], + kit.EventMatrixContract.Ev["EventTwoIndexedWithData"], paddedUint64(46), paddedUint64(27), }, @@ -1556,7 +1508,7 @@ func getTopicFilterTestCases(contract1, contract2 ethtypes.EthAddress, fromBlock { Address: contract1, Topics: []ethtypes.EthBytes{ - EventMatrixContract.Ev["EventTwoIndexedWithData"], + kit.EventMatrixContract.Ev["EventTwoIndexedWithData"], paddedUint64(46), paddedUint64(14), }, @@ -1566,13 +1518,13 @@ func getTopicFilterTestCases(contract1, contract2 ethtypes.EthAddress, fromBlock }, { name: "find all EventThreeIndexedWithData events", - spec: newEthFilterBuilder().FromBlock(fromBlock).Topic1OneOf(paddedEthHash(EventMatrixContract.Ev["EventThreeIndexedWithData"])).Filter(), + spec: kit.NewEthFilterBuilder().FromBlock(fromBlock).Topic1OneOf(paddedEthHash(kit.EventMatrixContract.Ev["EventThreeIndexedWithData"])).Filter(), expected: []ExpectedEthLog{ { Address: contract1, Topics: []ethtypes.EthBytes{ - EventMatrixContract.Ev["EventThreeIndexedWithData"], + kit.EventMatrixContract.Ev["EventThreeIndexedWithData"], paddedUint64(44), paddedUint64(27), paddedUint64(19), @@ -1584,13 +1536,13 @@ func getTopicFilterTestCases(contract1, contract2 ethtypes.EthAddress, fromBlock { name: "find all events with topic2 of 44", - spec: newEthFilterBuilder().FromBlock(fromBlock).Topic2OneOf(paddedEthHash(paddedUint64(44))).Filter(), + spec: kit.NewEthFilterBuilder().FromBlock(fromBlock).Topic2OneOf(paddedEthHash(paddedUint64(44))).Filter(), expected: []ExpectedEthLog{ { Address: contract1, Topics: []ethtypes.EthBytes{ - EventMatrixContract.Ev["EventOneIndexed"], + kit.EventMatrixContract.Ev["EventOneIndexed"], paddedUint64(44), }, Data: nil, @@ -1598,7 +1550,7 @@ func getTopicFilterTestCases(contract1, contract2 ethtypes.EthAddress, fromBlock { Address: contract2, Topics: []ethtypes.EthBytes{ - EventMatrixContract.Ev["EventTwoIndexed"], + kit.EventMatrixContract.Ev["EventTwoIndexed"], paddedUint64(44), paddedUint64(19), }, @@ -1607,7 +1559,7 @@ func getTopicFilterTestCases(contract1, contract2 ethtypes.EthAddress, fromBlock { Address: contract2, Topics: []ethtypes.EthBytes{ - EventMatrixContract.Ev["EventThreeIndexed"], + kit.EventMatrixContract.Ev["EventThreeIndexed"], paddedUint64(44), paddedUint64(27), paddedUint64(19), @@ -1617,7 +1569,7 @@ func getTopicFilterTestCases(contract1, contract2 ethtypes.EthAddress, fromBlock { Address: contract1, Topics: []ethtypes.EthBytes{ - EventMatrixContract.Ev["EventOneIndexedWithData"], + kit.EventMatrixContract.Ev["EventOneIndexedWithData"], paddedUint64(44), }, Data: paddedUint64(19), @@ -1625,7 +1577,7 @@ func getTopicFilterTestCases(contract1, contract2 ethtypes.EthAddress, fromBlock { Address: contract1, Topics: []ethtypes.EthBytes{ - EventMatrixContract.Ev["EventTwoIndexedWithData"], + kit.EventMatrixContract.Ev["EventTwoIndexedWithData"], paddedUint64(44), paddedUint64(27), }, @@ -1634,7 +1586,7 @@ func getTopicFilterTestCases(contract1, contract2 ethtypes.EthAddress, fromBlock { Address: contract1, Topics: []ethtypes.EthBytes{ - EventMatrixContract.Ev["EventThreeIndexedWithData"], + kit.EventMatrixContract.Ev["EventThreeIndexedWithData"], paddedUint64(44), paddedUint64(27), paddedUint64(19), @@ -1645,13 +1597,13 @@ func getTopicFilterTestCases(contract1, contract2 ethtypes.EthAddress, fromBlock }, { name: "find all events with topic2 of 46", - spec: newEthFilterBuilder().FromBlock(fromBlock).Topic2OneOf(paddedEthHash(paddedUint64(46))).Filter(), + spec: kit.NewEthFilterBuilder().FromBlock(fromBlock).Topic2OneOf(paddedEthHash(paddedUint64(46))).Filter(), expected: []ExpectedEthLog{ { Address: contract1, Topics: []ethtypes.EthBytes{ - EventMatrixContract.Ev["EventOneIndexedWithData"], + kit.EventMatrixContract.Ev["EventOneIndexedWithData"], paddedUint64(46), }, Data: paddedUint64(12), @@ -1659,7 +1611,7 @@ func getTopicFilterTestCases(contract1, contract2 ethtypes.EthAddress, fromBlock { Address: contract1, Topics: []ethtypes.EthBytes{ - EventMatrixContract.Ev["EventTwoIndexedWithData"], + kit.EventMatrixContract.Ev["EventTwoIndexedWithData"], paddedUint64(46), paddedUint64(27), }, @@ -1668,7 +1620,7 @@ func getTopicFilterTestCases(contract1, contract2 ethtypes.EthAddress, fromBlock { Address: contract1, Topics: []ethtypes.EthBytes{ - EventMatrixContract.Ev["EventTwoIndexedWithData"], + kit.EventMatrixContract.Ev["EventTwoIndexedWithData"], paddedUint64(46), paddedUint64(14), }, @@ -1678,13 +1630,13 @@ func getTopicFilterTestCases(contract1, contract2 ethtypes.EthAddress, fromBlock }, { name: "find all events with topic2 of 50", - spec: newEthFilterBuilder().FromBlock(fromBlock).Topic2OneOf(paddedEthHash(paddedUint64(50))).Filter(), + spec: kit.NewEthFilterBuilder().FromBlock(fromBlock).Topic2OneOf(paddedEthHash(paddedUint64(50))).Filter(), expected: []ExpectedEthLog{ { Address: contract2, Topics: []ethtypes.EthBytes{ - EventMatrixContract.Ev["EventOneIndexedWithData"], + kit.EventMatrixContract.Ev["EventOneIndexedWithData"], paddedUint64(50), }, Data: paddedUint64(9), @@ -1693,13 +1645,13 @@ func getTopicFilterTestCases(contract1, contract2 ethtypes.EthAddress, fromBlock }, { name: "find all events with topic2 of 46 or 50", - spec: newEthFilterBuilder().FromBlock(fromBlock).Topic2OneOf(paddedEthHash(paddedUint64(46)), paddedEthHash(paddedUint64(50))).Filter(), + spec: kit.NewEthFilterBuilder().FromBlock(fromBlock).Topic2OneOf(paddedEthHash(paddedUint64(46)), paddedEthHash(paddedUint64(50))).Filter(), expected: []ExpectedEthLog{ { Address: contract1, Topics: []ethtypes.EthBytes{ - EventMatrixContract.Ev["EventOneIndexedWithData"], + kit.EventMatrixContract.Ev["EventOneIndexedWithData"], paddedUint64(46), }, Data: paddedUint64(12), @@ -1707,7 +1659,7 @@ func getTopicFilterTestCases(contract1, contract2 ethtypes.EthAddress, fromBlock { Address: contract1, Topics: []ethtypes.EthBytes{ - EventMatrixContract.Ev["EventTwoIndexedWithData"], + kit.EventMatrixContract.Ev["EventTwoIndexedWithData"], paddedUint64(46), paddedUint64(27), }, @@ -1716,7 +1668,7 @@ func getTopicFilterTestCases(contract1, contract2 ethtypes.EthAddress, fromBlock { Address: contract1, Topics: []ethtypes.EthBytes{ - EventMatrixContract.Ev["EventTwoIndexedWithData"], + kit.EventMatrixContract.Ev["EventTwoIndexedWithData"], paddedUint64(46), paddedUint64(14), }, @@ -1725,7 +1677,7 @@ func getTopicFilterTestCases(contract1, contract2 ethtypes.EthAddress, fromBlock { Address: contract2, Topics: []ethtypes.EthBytes{ - EventMatrixContract.Ev["EventOneIndexedWithData"], + kit.EventMatrixContract.Ev["EventOneIndexedWithData"], paddedUint64(50), }, Data: paddedUint64(9), @@ -1735,9 +1687,9 @@ func getTopicFilterTestCases(contract1, contract2 ethtypes.EthAddress, fromBlock { name: "find all events with topic1 of EventTwoIndexedWithData and topic3 of 27", - spec: newEthFilterBuilder(). + spec: kit.NewEthFilterBuilder(). FromBlockEpoch(0). - Topic1OneOf(paddedEthHash(EventMatrixContract.Ev["EventTwoIndexedWithData"])). + Topic1OneOf(paddedEthHash(kit.EventMatrixContract.Ev["EventTwoIndexedWithData"])). Topic3OneOf(paddedEthHash(paddedUint64(27))). Filter(), @@ -1745,7 +1697,7 @@ func getTopicFilterTestCases(contract1, contract2 ethtypes.EthAddress, fromBlock { Address: contract1, Topics: []ethtypes.EthBytes{ - EventMatrixContract.Ev["EventTwoIndexedWithData"], + kit.EventMatrixContract.Ev["EventTwoIndexedWithData"], paddedUint64(44), paddedUint64(27), }, @@ -1754,7 +1706,7 @@ func getTopicFilterTestCases(contract1, contract2 ethtypes.EthAddress, fromBlock { Address: contract1, Topics: []ethtypes.EthBytes{ - EventMatrixContract.Ev["EventTwoIndexedWithData"], + kit.EventMatrixContract.Ev["EventTwoIndexedWithData"], paddedUint64(46), paddedUint64(27), }, @@ -1765,9 +1717,9 @@ func getTopicFilterTestCases(contract1, contract2 ethtypes.EthAddress, fromBlock { name: "find all events with topic1 of EventTwoIndexedWithData or EventOneIndexed and topic2 of 44", - spec: newEthFilterBuilder(). + spec: kit.NewEthFilterBuilder(). FromBlockEpoch(0). - Topic1OneOf(paddedEthHash(EventMatrixContract.Ev["EventTwoIndexedWithData"]), paddedEthHash(EventMatrixContract.Ev["EventOneIndexed"])). + Topic1OneOf(paddedEthHash(kit.EventMatrixContract.Ev["EventTwoIndexedWithData"]), paddedEthHash(kit.EventMatrixContract.Ev["EventOneIndexed"])). Topic2OneOf(paddedEthHash(paddedUint64(44))). Filter(), @@ -1775,7 +1727,7 @@ func getTopicFilterTestCases(contract1, contract2 ethtypes.EthAddress, fromBlock { Address: contract1, Topics: []ethtypes.EthBytes{ - EventMatrixContract.Ev["EventTwoIndexedWithData"], + kit.EventMatrixContract.Ev["EventTwoIndexedWithData"], paddedUint64(44), paddedUint64(27), }, @@ -1784,7 +1736,7 @@ func getTopicFilterTestCases(contract1, contract2 ethtypes.EthAddress, fromBlock { Address: contract1, Topics: []ethtypes.EthBytes{ - EventMatrixContract.Ev["EventOneIndexed"], + kit.EventMatrixContract.Ev["EventOneIndexed"], paddedUint64(44), }, Data: nil, @@ -1799,20 +1751,20 @@ func getAddressFilterTestCases(contract1, contract2 ethtypes.EthAddress, fromBlo return []filterTestCase{ { name: "find all events from contract2", - spec: newEthFilterBuilder().FromBlock(fromBlock).AddressOneOf(contract2).Filter(), + spec: kit.NewEthFilterBuilder().FromBlock(fromBlock).AddressOneOf(contract2).Filter(), expected: []ExpectedEthLog{ { Address: contract2, Topics: []ethtypes.EthBytes{ - EventMatrixContract.Ev["EventZeroData"], + kit.EventMatrixContract.Ev["EventZeroData"], }, Data: nil, }, { Address: contract2, Topics: []ethtypes.EthBytes{ - EventMatrixContract.Ev["EventThreeIndexed"], + kit.EventMatrixContract.Ev["EventThreeIndexed"], paddedUint64(44), paddedUint64(27), paddedUint64(19), @@ -1822,7 +1774,7 @@ func getAddressFilterTestCases(contract1, contract2 ethtypes.EthAddress, fromBlo { Address: contract2, Topics: []ethtypes.EthBytes{ - EventMatrixContract.Ev["EventTwoIndexed"], + kit.EventMatrixContract.Ev["EventTwoIndexed"], paddedUint64(44), paddedUint64(19), }, @@ -1831,7 +1783,7 @@ func getAddressFilterTestCases(contract1, contract2 ethtypes.EthAddress, fromBlo { Address: contract2, Topics: []ethtypes.EthBytes{ - EventMatrixContract.Ev["EventOneIndexedWithData"], + kit.EventMatrixContract.Ev["EventOneIndexedWithData"], paddedUint64(50), }, Data: paddedUint64(9), @@ -1841,13 +1793,13 @@ func getAddressFilterTestCases(contract1, contract2 ethtypes.EthAddress, fromBlo { name: "find all events with topic2 of 44 from contract2", - spec: newEthFilterBuilder().FromBlock(fromBlock).AddressOneOf(contract2).Topic2OneOf(paddedEthHash(paddedUint64(44))).Filter(), + spec: kit.NewEthFilterBuilder().FromBlock(fromBlock).AddressOneOf(contract2).Topic2OneOf(paddedEthHash(paddedUint64(44))).Filter(), expected: []ExpectedEthLog{ { Address: contract2, Topics: []ethtypes.EthBytes{ - EventMatrixContract.Ev["EventThreeIndexed"], + kit.EventMatrixContract.Ev["EventThreeIndexed"], paddedUint64(44), paddedUint64(27), paddedUint64(19), @@ -1857,7 +1809,7 @@ func getAddressFilterTestCases(contract1, contract2 ethtypes.EthAddress, fromBlo { Address: contract2, Topics: []ethtypes.EthBytes{ - EventMatrixContract.Ev["EventTwoIndexed"], + kit.EventMatrixContract.Ev["EventTwoIndexed"], paddedUint64(44), paddedUint64(19), }, @@ -1868,17 +1820,17 @@ func getAddressFilterTestCases(contract1, contract2 ethtypes.EthAddress, fromBlo { name: "find all EventOneIndexedWithData events from contract1 or contract2", - spec: newEthFilterBuilder(). + spec: kit.NewEthFilterBuilder(). FromBlockEpoch(0). AddressOneOf(contract1, contract2). - Topic1OneOf(paddedEthHash(EventMatrixContract.Ev["EventOneIndexedWithData"])). + Topic1OneOf(paddedEthHash(kit.EventMatrixContract.Ev["EventOneIndexedWithData"])). Filter(), expected: []ExpectedEthLog{ { Address: contract1, Topics: []ethtypes.EthBytes{ - EventMatrixContract.Ev["EventOneIndexedWithData"], + kit.EventMatrixContract.Ev["EventOneIndexedWithData"], paddedUint64(44), }, Data: paddedUint64(19), @@ -1886,7 +1838,7 @@ func getAddressFilterTestCases(contract1, contract2 ethtypes.EthAddress, fromBlo { Address: contract1, Topics: []ethtypes.EthBytes{ - EventMatrixContract.Ev["EventOneIndexedWithData"], + kit.EventMatrixContract.Ev["EventOneIndexedWithData"], paddedUint64(46), }, Data: paddedUint64(12), @@ -1894,7 +1846,7 @@ func getAddressFilterTestCases(contract1, contract2 ethtypes.EthAddress, fromBlo { Address: contract2, Topics: []ethtypes.EthBytes{ - EventMatrixContract.Ev["EventOneIndexedWithData"], + kit.EventMatrixContract.Ev["EventOneIndexedWithData"], paddedUint64(50), }, Data: paddedUint64(9), @@ -2195,18 +2147,6 @@ func paddedEthHash(orig []byte) ethtypes.EthHash { return ret } -func ethTopicHash(sig string) []byte { - hasher := sha3.NewLegacyKeccak256() - hasher.Write([]byte(sig)) - return hasher.Sum(nil) -} - -func ethFunctionHash(sig string) []byte { - hasher := sha3.NewLegacyKeccak256() - hasher.Write([]byte(sig)) - return hasher.Sum(nil)[:4] -} - func packUint64Values(vals ...uint64) []byte { ret := []byte{} for _, v := range vals { @@ -2229,78 +2169,6 @@ func unpackUint64Values(data []byte) []uint64 { return vals } -func newEthFilterBuilder() *ethFilterBuilder { return ðFilterBuilder{} } - -type ethFilterBuilder struct { - filter ethtypes.EthFilterSpec -} - -func (e *ethFilterBuilder) Filter() *ethtypes.EthFilterSpec { return &e.filter } - -func (e *ethFilterBuilder) FromBlock(v string) *ethFilterBuilder { - e.filter.FromBlock = &v - return e -} - -func (e *ethFilterBuilder) FromBlockEpoch(v abi.ChainEpoch) *ethFilterBuilder { - s := ethtypes.EthUint64(v).Hex() - e.filter.FromBlock = &s - return e -} - -func (e *ethFilterBuilder) ToBlock(v string) *ethFilterBuilder { - e.filter.ToBlock = &v - return e -} - -func (e *ethFilterBuilder) ToBlockEpoch(v abi.ChainEpoch) *ethFilterBuilder { - s := ethtypes.EthUint64(v).Hex() - e.filter.ToBlock = &s - return e -} - -func (e *ethFilterBuilder) BlockHash(h ethtypes.EthHash) *ethFilterBuilder { - e.filter.BlockHash = &h - return e -} - -func (e *ethFilterBuilder) AddressOneOf(as ...ethtypes.EthAddress) *ethFilterBuilder { - e.filter.Address = as - return e -} - -func (e *ethFilterBuilder) Topic1OneOf(hs ...ethtypes.EthHash) *ethFilterBuilder { - if len(e.filter.Topics) == 0 { - e.filter.Topics = make(ethtypes.EthTopicSpec, 1) - } - e.filter.Topics[0] = hs - return e -} - -func (e *ethFilterBuilder) Topic2OneOf(hs ...ethtypes.EthHash) *ethFilterBuilder { - for len(e.filter.Topics) < 2 { - e.filter.Topics = append(e.filter.Topics, nil) - } - e.filter.Topics[1] = hs - return e -} - -func (e *ethFilterBuilder) Topic3OneOf(hs ...ethtypes.EthHash) *ethFilterBuilder { - for len(e.filter.Topics) < 3 { - e.filter.Topics = append(e.filter.Topics, nil) - } - e.filter.Topics[2] = hs - return e -} - -func (e *ethFilterBuilder) Topic4OneOf(hs ...ethtypes.EthHash) *ethFilterBuilder { - for len(e.filter.Topics) < 4 { - e.filter.Topics = append(e.filter.Topics, nil) - } - e.filter.Topics[3] = hs - return e -} - func decodeLogBytes(orig []byte) []byte { if len(orig) == 0 { return orig diff --git a/itests/kit/evm.go b/itests/kit/evm.go index b3693ea45..71f8583aa 100644 --- a/itests/kit/evm.go +++ b/itests/kit/evm.go @@ -252,6 +252,7 @@ func (e *EVM) InvokeContractByFuncNameExpectExit(ctx context.Context, fromAddr a require.Equal(e.t, exit, wait.Receipt.ExitCode) } + // function signatures are the first 4 bytes of the hash of the function name and types func CalcFuncSignature(funcName string) []byte { hasher := sha3.NewLegacyKeccak256() @@ -310,3 +311,77 @@ func removeLeadingZeros(data []byte) []byte { } return data[firstNonZeroIndex:] } + +func NewEthFilterBuilder() *EthFilterBuilder { + return &EthFilterBuilder{} +} + +type EthFilterBuilder struct { + filter ethtypes.EthFilterSpec +} + +func (e *EthFilterBuilder) Filter() *ethtypes.EthFilterSpec { return &e.filter } + +func (e *EthFilterBuilder) FromBlock(v string) *EthFilterBuilder { + e.filter.FromBlock = &v + return e +} + +func (e *EthFilterBuilder) FromBlockEpoch(v abi.ChainEpoch) *EthFilterBuilder { + s := ethtypes.EthUint64(v).Hex() + e.filter.FromBlock = &s + return e +} + +func (e *EthFilterBuilder) ToBlock(v string) *EthFilterBuilder { + e.filter.ToBlock = &v + return e +} + +func (e *EthFilterBuilder) ToBlockEpoch(v abi.ChainEpoch) *EthFilterBuilder { + s := ethtypes.EthUint64(v).Hex() + e.filter.ToBlock = &s + return e +} + +func (e *EthFilterBuilder) BlockHash(h ethtypes.EthHash) *EthFilterBuilder { + e.filter.BlockHash = &h + return e +} + +func (e *EthFilterBuilder) AddressOneOf(as ...ethtypes.EthAddress) *EthFilterBuilder { + e.filter.Address = as + return e +} + +func (e *EthFilterBuilder) Topic1OneOf(hs ...ethtypes.EthHash) *EthFilterBuilder { + if len(e.filter.Topics) == 0 { + e.filter.Topics = make(ethtypes.EthTopicSpec, 1) + } + e.filter.Topics[0] = hs + return e +} + +func (e *EthFilterBuilder) Topic2OneOf(hs ...ethtypes.EthHash) *EthFilterBuilder { + for len(e.filter.Topics) < 2 { + e.filter.Topics = append(e.filter.Topics, nil) + } + e.filter.Topics[1] = hs + return e +} + +func (e *EthFilterBuilder) Topic3OneOf(hs ...ethtypes.EthHash) *EthFilterBuilder { + for len(e.filter.Topics) < 3 { + e.filter.Topics = append(e.filter.Topics, nil) + } + e.filter.Topics[2] = hs + return e +} + +func (e *EthFilterBuilder) Topic4OneOf(hs ...ethtypes.EthHash) *EthFilterBuilder { + for len(e.filter.Topics) < 4 { + e.filter.Topics = append(e.filter.Topics, nil) + } + e.filter.Topics[3] = hs + return e +} diff --git a/itests/kit/solidity.go b/itests/kit/solidity.go new file mode 100644 index 000000000..ea9d452f4 --- /dev/null +++ b/itests/kit/solidity.go @@ -0,0 +1,64 @@ +package kit + +import ( + "golang.org/x/crypto/sha3" +) + +func EthTopicHash(sig string) []byte { + hasher := sha3.NewLegacyKeccak256() + hasher.Write([]byte(sig)) + return hasher.Sum(nil) +} + +func EthFunctionHash(sig string) []byte { + hasher := sha3.NewLegacyKeccak256() + hasher.Write([]byte(sig)) + return hasher.Sum(nil)[:4] +} + +// SolidityContractDef holds information about one of the test contracts +type SolidityContractDef struct { + Filename string // filename of the hex of the contract, e.g. contracts/EventMatrix.hex + Fn map[string][]byte // mapping of function names to 32-bit selector + Ev map[string][]byte // mapping of event names to 256-bit signature hashes +} + +var EventMatrixContract = SolidityContractDef{ + Filename: "contracts/EventMatrix.hex", + Fn: map[string][]byte{ + "logEventZeroData": EthFunctionHash("logEventZeroData()"), + "logEventOneData": EthFunctionHash("logEventOneData(uint256)"), + "logEventTwoData": EthFunctionHash("logEventTwoData(uint256,uint256)"), + "logEventThreeData": EthFunctionHash("logEventThreeData(uint256,uint256,uint256)"), + "logEventFourData": EthFunctionHash("logEventFourData(uint256,uint256,uint256,uint256)"), + "logEventOneIndexed": EthFunctionHash("logEventOneIndexed(uint256)"), + "logEventTwoIndexed": EthFunctionHash("logEventTwoIndexed(uint256,uint256)"), + "logEventThreeIndexed": EthFunctionHash("logEventThreeIndexed(uint256,uint256,uint256)"), + "logEventOneIndexedWithData": EthFunctionHash("logEventOneIndexedWithData(uint256,uint256)"), + "logEventTwoIndexedWithData": EthFunctionHash("logEventTwoIndexedWithData(uint256,uint256,uint256)"), + "logEventThreeIndexedWithData": EthFunctionHash("logEventThreeIndexedWithData(uint256,uint256,uint256,uint256)"), + }, + Ev: map[string][]byte{ + "EventZeroData": EthTopicHash("EventZeroData()"), + "EventOneData": EthTopicHash("EventOneData(uint256)"), + "EventTwoData": EthTopicHash("EventTwoData(uint256,uint256)"), + "EventThreeData": EthTopicHash("EventThreeData(uint256,uint256,uint256)"), + "EventFourData": EthTopicHash("EventFourData(uint256,uint256,uint256,uint256)"), + "EventOneIndexed": EthTopicHash("EventOneIndexed(uint256)"), + "EventTwoIndexed": EthTopicHash("EventTwoIndexed(uint256,uint256)"), + "EventThreeIndexed": EthTopicHash("EventThreeIndexed(uint256,uint256,uint256)"), + "EventOneIndexedWithData": EthTopicHash("EventOneIndexedWithData(uint256,uint256)"), + "EventTwoIndexedWithData": EthTopicHash("EventTwoIndexedWithData(uint256,uint256,uint256)"), + "EventThreeIndexedWithData": EthTopicHash("EventThreeIndexedWithData(uint256,uint256,uint256,uint256)"), + }, +} + +var EventsContract = SolidityContractDef{ + Filename: "contracts/events.bin", + Fn: map[string][]byte{ + "log_zero_data": {0x00, 0x00, 0x00, 0x00}, + "log_zero_nodata": {0x00, 0x00, 0x00, 0x01}, + "log_four_data": {0x00, 0x00, 0x00, 0x02}, + }, + Ev: map[string][]byte{}, +} From eacf3f18309209cd8f916f6f60f785bd6c0c0619 Mon Sep 17 00:00:00 2001 From: Ian Davis Date: Fri, 27 Jan 2023 15:20:23 +0000 Subject: [PATCH 18/30] go fmt --- itests/kit/evm.go | 1 - 1 file changed, 1 deletion(-) diff --git a/itests/kit/evm.go b/itests/kit/evm.go index 71f8583aa..0de09da90 100644 --- a/itests/kit/evm.go +++ b/itests/kit/evm.go @@ -252,7 +252,6 @@ func (e *EVM) InvokeContractByFuncNameExpectExit(ctx context.Context, fromAddr a require.Equal(e.t, exit, wait.Receipt.ExitCode) } - // function signatures are the first 4 bytes of the hash of the function name and types func CalcFuncSignature(funcName string) []byte { hasher := sha3.NewLegacyKeccak256() From 245be406d9c5559bb4a887ad5b6b50745e61a6b6 Mon Sep 17 00:00:00 2001 From: Ian Davis Date: Wed, 1 Feb 2023 16:41:01 +0000 Subject: [PATCH 19/30] Fix merge conflicts --- api/api_full.go | 2 +- go.mod | 10 ++++++---- go.sum | 18 ++++++------------ itests/eth_conformance_test.go | 3 ++- 4 files changed, 15 insertions(+), 18 deletions(-) diff --git a/api/api_full.go b/api/api_full.go index 2ac92a764..20870adce 100644 --- a/api/api_full.go +++ b/api/api_full.go @@ -853,7 +853,7 @@ type FullNode interface { // reverse interface to the client, called after EthSubscribe type EthSubscriber interface { // note: the parameter is ethtypes.EthSubscriptionResponse serialized as json object - EthSubscription(ctx context.Context, r jsonrpc.RawParams) error //rpc_method:eth_subscription notify:true + EthSubscription(ctx context.Context, r jsonrpc.RawParams) error // rpc_method:eth_subscription notify:true } type StorageAsk struct { diff --git a/go.mod b/go.mod index 4e0c58ca6..f32f2f7ee 100644 --- a/go.mod +++ b/go.mod @@ -62,7 +62,7 @@ require ( github.com/gbrlsnchs/jwt/v3 v3.0.1 github.com/gdamore/tcell/v2 v2.2.0 github.com/go-kit/kit v0.12.0 - github.com/go-openapi/spec v0.20.8 + github.com/go-openapi/spec v0.19.11 github.com/golang/mock v1.6.0 github.com/google/uuid v1.3.0 github.com/gorilla/mux v1.8.0 @@ -171,6 +171,8 @@ require ( require ( github.com/GeertJohan/go.incremental v1.0.0 // indirect + github.com/PuerkitoBio/purell v1.1.1 // indirect + github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578 // indirect github.com/StackExchange/wmi v1.2.1 // indirect github.com/Stebalien/go-bitfield v0.0.1 // indirect github.com/akavel/rsrc v0.8.0 // indirect @@ -212,9 +214,9 @@ require ( github.com/go-logr/logr v1.2.3 // indirect github.com/go-logr/stdr v1.2.2 // indirect github.com/go-ole/go-ole v1.2.5 // indirect - github.com/go-openapi/jsonpointer v0.19.5 // indirect - github.com/go-openapi/jsonreference v0.20.0 // indirect - github.com/go-openapi/swag v0.21.1 // indirect + github.com/go-openapi/jsonpointer v0.19.3 // indirect + github.com/go-openapi/jsonreference v0.19.4 // indirect + github.com/go-openapi/swag v0.19.11 // indirect github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0 // indirect github.com/godbus/dbus/v5 v5.1.0 // indirect github.com/gogo/protobuf v1.3.2 // indirect diff --git a/go.sum b/go.sum index 8a42779ef..c0fa2bbcb 100644 --- a/go.sum +++ b/go.sum @@ -69,7 +69,9 @@ github.com/Masterminds/semver v1.4.2/go.mod h1:MB6lktGJrhw8PrUyiEoblNEGEQ+RzHPF0 github.com/Masterminds/vcs v1.13.0/go.mod h1:N09YCmOQr6RLxC6UNHzuVwAdodYbbnycGHSmwVJjcKA= github.com/OneOfOne/xxhash v1.2.2 h1:KMrpdQIwFcEqXDklaen+P1axHaj9BSKzvpUUfnHldSE= github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU= +github.com/PuerkitoBio/purell v1.1.1 h1:WEQqlqaGbrPkxLJWfBwQmfEAE1Z7ONdDLqrN38tNFfI= github.com/PuerkitoBio/purell v1.1.1/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0= +github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578 h1:d+Bc7a5rLufV/sSk/8dngufqelfh6jnri85riMAaF/M= github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578/go.mod h1:uGdkoq3SwY9Y+13GIhn11/XLaGBb4BfwItxLd5jeuXE= github.com/Shopify/sarama v1.19.0/go.mod h1:FVkBWblsNy7DGZRfXLU0O9RCGt5g3g3yEuWXgklEdEo= github.com/Shopify/toxiproxy v2.1.4+incompatible/go.mod h1:OXgGpZ6Cli1/URJOF1DMxUHB2q5Ap20/P/eIdh4G0pI= @@ -443,26 +445,21 @@ github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre github.com/go-ole/go-ole v1.2.5 h1:t4MGB5xEDZvXI+0rMjjsfBsD7yAgp/s9ZDkL1JndXwY= github.com/go-ole/go-ole v1.2.5/go.mod h1:pprOEPIfldk/42T2oK7lQ4v4JSDwmV0As9GaiUsvbm0= github.com/go-openapi/jsonpointer v0.19.2/go.mod h1:3akKfEdA7DF1sugOqz1dVQHBcuDBPKZGEoHC/NkiQRg= +github.com/go-openapi/jsonpointer v0.19.3 h1:gihV7YNZK1iK6Tgwwsxo2rJbD1GTbdm72325Bq8FI3w= github.com/go-openapi/jsonpointer v0.19.3/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg= -github.com/go-openapi/jsonpointer v0.19.5 h1:gZr+CIYByUqjcgeLXnQu2gHYQC9o73G2XUeOFYEICuY= -github.com/go-openapi/jsonpointer v0.19.5/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg= github.com/go-openapi/jsonreference v0.19.2/go.mod h1:jMjeRr2HHw6nAVajTXJ4eiUwohSTlpa0o73RUL1owJc= github.com/go-openapi/jsonreference v0.19.3/go.mod h1:rjx6GuL8TTa9VaixXglHmQmIL98+wF9xc8zWvFonSJ8= +github.com/go-openapi/jsonreference v0.19.4 h1:3Vw+rh13uq2JFNxgnMTGE1rnoieU9FmyE1gvnyylsYg= github.com/go-openapi/jsonreference v0.19.4/go.mod h1:RdybgQwPxbL4UEjuAruzK1x3nE69AqPYEJeo/TWfEeg= -github.com/go-openapi/jsonreference v0.20.0 h1:MYlu0sBgChmCfJxxUKZ8g1cPWFOB37YSZqewK7OKeyA= -github.com/go-openapi/jsonreference v0.20.0/go.mod h1:Ag74Ico3lPc+zR+qjn4XBUmXymS4zJbYVCZmcgkasdo= github.com/go-openapi/spec v0.19.2/go.mod h1:sCxk3jxKgioEJikev4fgkNmwS+3kuYdJtcsZsD5zxMY= github.com/go-openapi/spec v0.19.7/go.mod h1:Hm2Jr4jv8G1ciIAo+frC/Ft+rR2kQDh8JHKHb3gWUSk= +github.com/go-openapi/spec v0.19.11 h1:ogU5q8dtp3MMPn59a9VRrPKVxvJHEs5P7yNMR5sNnis= github.com/go-openapi/spec v0.19.11/go.mod h1:vqK/dIdLGCosfvYsQV3WfC7N3TiZSnGY2RZKoFK7X28= -github.com/go-openapi/spec v0.20.8 h1:ubHmXNY3FCIOinT8RNrrPfGc9t7I1qhPtdOGoG2AxRU= -github.com/go-openapi/spec v0.20.8/go.mod h1:2OpW+JddWPrpXSCIX8eOx7lZ5iyuWj3RYR6VaaBKcWA= github.com/go-openapi/swag v0.19.2/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh66Z9tfKk= github.com/go-openapi/swag v0.19.5/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh66Z9tfKk= github.com/go-openapi/swag v0.19.8/go.mod h1:ao+8BpOPyKdpQz3AOJfbeEVpLmWAvlT1IfTe5McPyhY= +github.com/go-openapi/swag v0.19.11 h1:RFTu/dlFySpyVvJDfp/7674JY4SDglYWKztbiIGFpmc= github.com/go-openapi/swag v0.19.11/go.mod h1:Uc0gKkdR+ojzsEpjh39QChyu92vPgIr72POcgHMAgSY= -github.com/go-openapi/swag v0.19.15/go.mod h1:QYRuS/SOXUCsnplDa677K7+DxSOj6IPNl/eQntq43wQ= -github.com/go-openapi/swag v0.21.1 h1:wm0rhTb5z7qpJRHBdPOMuY4QjVUMbF6/kwoYeRAOrKU= -github.com/go-openapi/swag v0.21.1/go.mod h1:QYRuS/SOXUCsnplDa677K7+DxSOj6IPNl/eQntq43wQ= github.com/go-sql-driver/mysql v1.4.0/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG5ZlKdlhCg5w= github.com/go-sql-driver/mysql v1.5.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg= github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= @@ -1566,7 +1563,6 @@ github.com/nats-io/nuid v1.0.1/go.mod h1:19wcPz3Ph3q0Jbyiqsd0kePYG7A95tJPxeL+1OS github.com/neelance/astrewrite v0.0.0-20160511093645-99348263ae86/go.mod h1:kHJEU3ofeGjhHklVoIGuVj85JJwZ6kWPaJwCIxgnFmo= github.com/neelance/sourcemap v0.0.0-20151028013722-8c68805598ab/go.mod h1:Qr6/a/Q4r9LP1IltGz7tA7iOK1WonHEYhu1HRBA7ZiM= github.com/ngdinhtoan/glide-cleanup v0.2.0/go.mod h1:UQzsmiDOb8YV3nOsCxK/c9zPpCZVNoHScRE3EO9pVMM= -github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno= github.com/nikkolasg/hexjson v0.0.0-20181101101858-78e39397e00c h1:5bFTChQxSKNwy8ALwOebjekYExl9HTT9urdawqC95tA= github.com/nikkolasg/hexjson v0.0.0-20181101101858-78e39397e00c/go.mod h1:7qN3Y0BvzRUf4LofcoJplQL10lsFDb4PYlePTVwrP28= github.com/nkovacs/streamquote v1.0.0 h1:PmVIV08Zlx2lZK5fFZlMZ04eHcDTIFJCv/5/0twVUow= @@ -2508,7 +2504,6 @@ gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLks gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= gopkg.in/cheggaaa/pb.v1 v1.0.25/go.mod h1:V/YB90LKu/1FcN3WVnfiiE5oMCibMjukxqG/qStrOgw= @@ -2535,7 +2530,6 @@ gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= -gopkg.in/yaml.v3 v3.0.0-20200615113413-eeeca48fe776/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/itests/eth_conformance_test.go b/itests/eth_conformance_test.go index 5d04b5948..b11366465 100644 --- a/itests/eth_conformance_test.go +++ b/itests/eth_conformance_test.go @@ -498,7 +498,8 @@ func waitForMessageWithEvents(ctx context.Context, t *testing.T, client *kit.Tes } // send a message that exercises event logs - ret := client.EVM().InvokeSolidity(ctx, sender, target, kit.EventMatrixContract.Fn["logEventThreeIndexedWithData"], inputData) + ret, err := client.EVM().InvokeSolidity(ctx, sender, target, kit.EventMatrixContract.Fn["logEventThreeIndexedWithData"], inputData) + require.NoError(t, err) require.True(t, ret.Receipt.ExitCode.IsSuccess(), "contract execution failed") msgHash, err := client.EthGetTransactionHashByCid(ctx, ret.Message) From f05437d2537f12072b5166888a2507783af22637 Mon Sep 17 00:00:00 2001 From: Geoff Stuart Date: Wed, 1 Feb 2023 16:44:44 -0500 Subject: [PATCH 20/30] Check decoding params for new methods --- .circleci/config.yml | 5 ++ cli/state.go | 8 ++-- itests/decode_params_test.go | 92 ++++++++++++++++++++++++++++++++++++ 3 files changed, 101 insertions(+), 4 deletions(-) create mode 100644 itests/decode_params_test.go diff --git a/.circleci/config.yml b/.circleci/config.yml index 847c4faf4..3e4abfb5d 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -603,6 +603,11 @@ workflows: suite: itest-deals target: "./itests/deals_test.go" + - test: + name: test-itest-decode_params + suite: itest-decode_params + target: "./itests/decode_params_test.go" + - test: name: test-itest-dup_mpool_messages suite: itest-dup_mpool_messages diff --git a/cli/state.go b/cli/state.go index c69299946..3d629bb0b 100644 --- a/cli/state.go +++ b/cli/state.go @@ -1354,7 +1354,7 @@ func ComputeStateHTMLTempl(w io.Writer, ts *types.TipSet, o *api.ComputeStateOut "GetMethod": getMethod, "ToFil": toFil, "JsonParams": JsonParams, - "JsonReturn": jsonReturn, + "JsonReturn": JsonReturn, "IsSlow": isSlow, "IsVerySlow": isVerySlow, "IntExit": func(i exitcode.ExitCode) int64 { return int64(i) }, @@ -1440,7 +1440,7 @@ func JsonParams(code cid.Cid, method abi.MethodNum, params []byte) (string, erro return string(b), err } -func jsonReturn(code cid.Cid, method abi.MethodNum, ret []byte) (string, error) { +func JsonReturn(code cid.Cid, method abi.MethodNum, ret []byte) (string, error) { methodMeta, found := filcns.NewActorRegistry().Methods[code][method] // TODO: use remote if !found { return "", fmt.Errorf("method %d not found on actor %s", method, code) @@ -1549,7 +1549,7 @@ func printReceiptReturn(ctx context.Context, api v0api.FullNode, m *types.Messag return err } - jret, err := jsonReturn(act.Code, m.Method, r.Return) + jret, err := JsonReturn(act.Code, m.Method, r.Return) if err != nil { return err } @@ -1689,7 +1689,7 @@ var StateCallCmd = &cli.Command{ return xerrors.Errorf("getting actor: %w", err) } - retStr, err := jsonReturn(act.Code, abi.MethodNum(method), ret.MsgRct.Return) + retStr, err := JsonReturn(act.Code, abi.MethodNum(method), ret.MsgRct.Return) if err != nil { return xerrors.Errorf("decoding return: %w", err) } diff --git a/itests/decode_params_test.go b/itests/decode_params_test.go new file mode 100644 index 000000000..1387bff9d --- /dev/null +++ b/itests/decode_params_test.go @@ -0,0 +1,92 @@ +// stm: #integration +package itests + +import ( + "encoding/hex" + "fmt" + "testing" + + "github.com/stretchr/testify/require" + + "github.com/filecoin-project/go-state-types/abi" + actorstypes "github.com/filecoin-project/go-state-types/actors" + "github.com/filecoin-project/go-state-types/builtin" + "github.com/filecoin-project/go-state-types/manifest" + + "github.com/filecoin-project/lotus/build" + "github.com/filecoin-project/lotus/chain/actors" + "github.com/filecoin-project/lotus/cli" +) + +type testCase struct { + ActorKey string + MethodNum abi.MethodNum + Bytestr string +} + +// Used './lotus state replay --show-trace ' to get params/return to decode. +func TestDecodeParams(t *testing.T) { + testCases := []testCase{ + { + ActorKey: manifest.EvmKey, + MethodNum: builtin.MethodsEVM.InvokeContract, + Bytestr: "58a4d4a0cd0a000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000028000181e20392202034194f3b7cae3042a57b63ea4c36a962478e41bfa8ddc80dd61cae8bebdedf23000000000000000000000000000000000000000000000000", + }, + { + ActorKey: manifest.EamKey, + MethodNum: builtin.MethodsEAM.CreateExternal, + Bytestr: "590dde608060405234801561001057600080fd5b5061002d61002261003260201b60201c565b61003a60201b60201c565b6100fe565b600033905090565b60008060009054906101000a900473ffffffffffffffffffffffffffffffffffffffff169050816000806101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055508173ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff167f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e060405160405180910390a35050565b610cd18061010d6000396000f3fe6080604052600436106100555760003560e01c806305b03ef81461005a5780635a73b0bf14610097578063715018a6146100c05780638da5cb5b146100d7578063a26e118614610102578063f2fde38b1461011e575b600080fd5b34801561006657600080fd5b50610081600480360381019061007c91906106ec565b610147565b60405161008e9190610978565b60405180910390f35b3480156100a357600080fd5b506100be60048036038101906100b99190610748565b610182565b005b3480156100cc57600080fd5b506100d5610321565b005b3480156100e357600080fd5b506100ec610335565b6040516100f991906108bf565b60405180910390f35b61011c600480360381019061011791906106a3565b61035e565b005b34801561012a57600080fd5b5061014560048036038101906101409190610676565b61040e565b005b600182805160208101820180518482526020830160208501208183528095505050505050602052806000526040600020600091509150505481565b61018a610492565b8060018460405161019b91906108a8565b908152602001604051809103902060008473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020541015610228576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161021f90610938565b60405180910390fd5b8060018460405161023991906108a8565b908152602001604051809103902060008473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060008282546102939190610a66565b925050819055503373ffffffffffffffffffffffffffffffffffffffff166108fc829081150290604051600060405180830381858888f193505050501580156102e0573d6000803e3d6000fd5b507f901c03da5d88eb3d62ab4617e7b7d17d86db16356823a7971127d5181a842fef828483604051610314939291906108da565b60405180910390a1505050565b610329610492565b6103336000610510565b565b60008060009054906101000a900473ffffffffffffffffffffffffffffffffffffffff16905090565b3460018260405161036f91906108a8565b908152602001604051809103902060003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060008282546103c99190610a10565b925050819055507f2d4b597935f3cd67fb2eebf1db4debc934cee5c7baa7153f980fdbeb2e74084e338234604051610403939291906108da565b60405180910390a150565b610416610492565b600073ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff161415610486576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161047d90610918565b60405180910390fd5b61048f81610510565b50565b61049a6105d4565b73ffffffffffffffffffffffffffffffffffffffff166104b8610335565b73ffffffffffffffffffffffffffffffffffffffff161461050e576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161050590610958565b60405180910390fd5b565b60008060009054906101000a900473ffffffffffffffffffffffffffffffffffffffff169050816000806101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055508173ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff167f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e060405160405180910390a35050565b600033905090565b60006105ef6105ea846109b8565b610993565b90508281526020810184848401111561060b5761060a610bac565b5b610616848285610ad6565b509392505050565b60008135905061062d81610c6d565b92915050565b600082601f83011261064857610647610ba7565b5b81356106588482602086016105dc565b91505092915050565b60008135905061067081610c84565b92915050565b60006020828403121561068c5761068b610bb6565b5b600061069a8482850161061e565b91505092915050565b6000602082840312156106b9576106b8610bb6565b5b600082013567ffffffffffffffff8111156106d7576106d6610bb1565b5b6106e384828501610633565b91505092915050565b6000806040838503121561070357610702610bb6565b5b600083013567ffffffffffffffff81111561072157610720610bb1565b5b61072d85828601610633565b925050602061073e8582860161061e565b9150509250929050565b60008060006060848603121561076157610760610bb6565b5b600084013567ffffffffffffffff81111561077f5761077e610bb1565b5b61078b86828701610633565b935050602061079c8682870161061e565b92505060406107ad86828701610661565b9150509250925092565b6107c081610a9a565b82525050565b60006107d1826109e9565b6107db81856109f4565b93506107eb818560208601610ae5565b6107f481610bbb565b840191505092915050565b600061080a826109e9565b6108148185610a05565b9350610824818560208601610ae5565b80840191505092915050565b600061083d6026836109f4565b915061084882610bcc565b604082019050919050565b6000610860600f836109f4565b915061086b82610c1b565b602082019050919050565b60006108836020836109f4565b915061088e82610c44565b602082019050919050565b6108a281610acc565b82525050565b60006108b482846107ff565b915081905092915050565b60006020820190506108d460008301846107b7565b92915050565b60006060820190506108ef60008301866107b7565b818103602083015261090181856107c6565b90506109106040830184610899565b949350505050565b6000602082019050818103600083015261093181610830565b9050919050565b6000602082019050818103600083015261095181610853565b9050919050565b6000602082019050818103600083015261097181610876565b9050919050565b600060208201905061098d6000830184610899565b92915050565b600061099d6109ae565b90506109a98282610b18565b919050565b6000604051905090565b600067ffffffffffffffff8211156109d3576109d2610b78565b5b6109dc82610bbb565b9050602081019050919050565b600081519050919050565b600082825260208201905092915050565b600081905092915050565b6000610a1b82610acc565b9150610a2683610acc565b9250827fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff03821115610a5b57610a5a610b49565b5b828201905092915050565b6000610a7182610acc565b9150610a7c83610acc565b925082821015610a8f57610a8e610b49565b5b828203905092915050565b6000610aa582610aac565b9050919050565b600073ffffffffffffffffffffffffffffffffffffffff82169050919050565b6000819050919050565b82818337600083830152505050565b60005b83811015610b03578082015181840152602081019050610ae8565b83811115610b12576000848401525b50505050565b610b2182610bbb565b810181811067ffffffffffffffff82111715610b4057610b3f610b78565b5b80604052505050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b600080fd5b600080fd5b600080fd5b600080fd5b6000601f19601f8301169050919050565b7f4f776e61626c653a206e6577206f776e657220697320746865207a65726f206160008201527f6464726573730000000000000000000000000000000000000000000000000000602082015250565b7f62616c616e636520746f6f206c6f770000000000000000000000000000000000600082015250565b7f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e6572600082015250565b610c7681610a9a565b8114610c8157600080fd5b50565b610c8d81610acc565b8114610c9857600080fd5b5056fea2646970667358221220bdef2e63f4a80f250df77d0955fb43ff301f5a632865a02ac71cdc94ded8edd964736f6c63430008070033", + }, + } + + for _, _tc := range testCases { + tc := _tc + t.Run(tc.ActorKey+" "+tc.MethodNum.String(), func(t *testing.T) { + av, err := actorstypes.VersionForNetwork(build.TestNetworkVersion) + require.NoError(t, err) + actorCodeCid, found := actors.GetActorCodeID(av, tc.ActorKey) + require.True(t, found) + + params, err := hex.DecodeString(tc.Bytestr) + require.NoError(t, err) + + paramString, err := cli.JsonParams(actorCodeCid, tc.MethodNum, params) + require.NoError(t, err) + + fmt.Println(paramString) + }) + } +} + +func TestDecodeReturn(t *testing.T) { + testCases := []testCase{ + { + ActorKey: manifest.EvmKey, + MethodNum: builtin.MethodsEVM.InvokeContract, + Bytestr: "5820000000000000000000000000000000000000000000000000000000000000002d", + }, + { + ActorKey: manifest.EamKey, + MethodNum: builtin.MethodsEAM.CreateExternal, + Bytestr: "8319195b5502ff4af64187327799171354df10b4b3bfc2e4c30654922d6956c99e12dfeb3224dea977d0939758a1fe", + }, + } + + for _, _tc := range testCases { + tc := _tc + t.Run(tc.ActorKey+" "+tc.MethodNum.String(), func(t *testing.T) { + av, err := actorstypes.VersionForNetwork(build.TestNetworkVersion) + require.NoError(t, err) + actorCodeCid, found := actors.GetActorCodeID(av, tc.ActorKey) + require.True(t, found) + + ret, err := hex.DecodeString(tc.Bytestr) + require.NoError(t, err) + + paramString, err := cli.JsonReturn(actorCodeCid, tc.MethodNum, ret) + require.NoError(t, err) + + fmt.Println(paramString) + }) + } +} From ffc1c33be06ce7247480b775236226ae0a8105ca Mon Sep 17 00:00:00 2001 From: Geoff Stuart Date: Thu, 2 Feb 2023 16:37:58 -0500 Subject: [PATCH 21/30] Review fixes --- itests/decode_params_test.go | 62 +++++++++++++++++++++++++++--------- 1 file changed, 47 insertions(+), 15 deletions(-) diff --git a/itests/decode_params_test.go b/itests/decode_params_test.go index 1387bff9d..6a4a8c681 100644 --- a/itests/decode_params_test.go +++ b/itests/decode_params_test.go @@ -2,15 +2,18 @@ package itests import ( - "encoding/hex" - "fmt" + "bytes" + "encoding/json" "testing" "github.com/stretchr/testify/require" + "github.com/filecoin-project/go-address" "github.com/filecoin-project/go-state-types/abi" actorstypes "github.com/filecoin-project/go-state-types/actors" "github.com/filecoin-project/go-state-types/builtin" + "github.com/filecoin-project/go-state-types/builtin/v10/eam" + "github.com/filecoin-project/go-state-types/cbor" "github.com/filecoin-project/go-state-types/manifest" "github.com/filecoin-project/lotus/build" @@ -18,24 +21,31 @@ import ( "github.com/filecoin-project/lotus/cli" ) +type marshalable interface { + cbor.Marshaler + cbor.Unmarshaler +} + type testCase struct { ActorKey string MethodNum abi.MethodNum - Bytestr string + retVal marshalable } // Used './lotus state replay --show-trace ' to get params/return to decode. func TestDecodeParams(t *testing.T) { + testCborBytes := abi.CborBytes([]byte{1, 2, 3}) + testCases := []testCase{ { ActorKey: manifest.EvmKey, MethodNum: builtin.MethodsEVM.InvokeContract, - Bytestr: "58a4d4a0cd0a000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000028000181e20392202034194f3b7cae3042a57b63ea4c36a962478e41bfa8ddc80dd61cae8bebdedf23000000000000000000000000000000000000000000000000", + retVal: &testCborBytes, }, { ActorKey: manifest.EamKey, MethodNum: builtin.MethodsEAM.CreateExternal, - Bytestr: "590dde608060405234801561001057600080fd5b5061002d61002261003260201b60201c565b61003a60201b60201c565b6100fe565b600033905090565b60008060009054906101000a900473ffffffffffffffffffffffffffffffffffffffff169050816000806101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055508173ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff167f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e060405160405180910390a35050565b610cd18061010d6000396000f3fe6080604052600436106100555760003560e01c806305b03ef81461005a5780635a73b0bf14610097578063715018a6146100c05780638da5cb5b146100d7578063a26e118614610102578063f2fde38b1461011e575b600080fd5b34801561006657600080fd5b50610081600480360381019061007c91906106ec565b610147565b60405161008e9190610978565b60405180910390f35b3480156100a357600080fd5b506100be60048036038101906100b99190610748565b610182565b005b3480156100cc57600080fd5b506100d5610321565b005b3480156100e357600080fd5b506100ec610335565b6040516100f991906108bf565b60405180910390f35b61011c600480360381019061011791906106a3565b61035e565b005b34801561012a57600080fd5b5061014560048036038101906101409190610676565b61040e565b005b600182805160208101820180518482526020830160208501208183528095505050505050602052806000526040600020600091509150505481565b61018a610492565b8060018460405161019b91906108a8565b908152602001604051809103902060008473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020541015610228576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161021f90610938565b60405180910390fd5b8060018460405161023991906108a8565b908152602001604051809103902060008473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060008282546102939190610a66565b925050819055503373ffffffffffffffffffffffffffffffffffffffff166108fc829081150290604051600060405180830381858888f193505050501580156102e0573d6000803e3d6000fd5b507f901c03da5d88eb3d62ab4617e7b7d17d86db16356823a7971127d5181a842fef828483604051610314939291906108da565b60405180910390a1505050565b610329610492565b6103336000610510565b565b60008060009054906101000a900473ffffffffffffffffffffffffffffffffffffffff16905090565b3460018260405161036f91906108a8565b908152602001604051809103902060003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060008282546103c99190610a10565b925050819055507f2d4b597935f3cd67fb2eebf1db4debc934cee5c7baa7153f980fdbeb2e74084e338234604051610403939291906108da565b60405180910390a150565b610416610492565b600073ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff161415610486576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161047d90610918565b60405180910390fd5b61048f81610510565b50565b61049a6105d4565b73ffffffffffffffffffffffffffffffffffffffff166104b8610335565b73ffffffffffffffffffffffffffffffffffffffff161461050e576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161050590610958565b60405180910390fd5b565b60008060009054906101000a900473ffffffffffffffffffffffffffffffffffffffff169050816000806101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055508173ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff167f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e060405160405180910390a35050565b600033905090565b60006105ef6105ea846109b8565b610993565b90508281526020810184848401111561060b5761060a610bac565b5b610616848285610ad6565b509392505050565b60008135905061062d81610c6d565b92915050565b600082601f83011261064857610647610ba7565b5b81356106588482602086016105dc565b91505092915050565b60008135905061067081610c84565b92915050565b60006020828403121561068c5761068b610bb6565b5b600061069a8482850161061e565b91505092915050565b6000602082840312156106b9576106b8610bb6565b5b600082013567ffffffffffffffff8111156106d7576106d6610bb1565b5b6106e384828501610633565b91505092915050565b6000806040838503121561070357610702610bb6565b5b600083013567ffffffffffffffff81111561072157610720610bb1565b5b61072d85828601610633565b925050602061073e8582860161061e565b9150509250929050565b60008060006060848603121561076157610760610bb6565b5b600084013567ffffffffffffffff81111561077f5761077e610bb1565b5b61078b86828701610633565b935050602061079c8682870161061e565b92505060406107ad86828701610661565b9150509250925092565b6107c081610a9a565b82525050565b60006107d1826109e9565b6107db81856109f4565b93506107eb818560208601610ae5565b6107f481610bbb565b840191505092915050565b600061080a826109e9565b6108148185610a05565b9350610824818560208601610ae5565b80840191505092915050565b600061083d6026836109f4565b915061084882610bcc565b604082019050919050565b6000610860600f836109f4565b915061086b82610c1b565b602082019050919050565b60006108836020836109f4565b915061088e82610c44565b602082019050919050565b6108a281610acc565b82525050565b60006108b482846107ff565b915081905092915050565b60006020820190506108d460008301846107b7565b92915050565b60006060820190506108ef60008301866107b7565b818103602083015261090181856107c6565b90506109106040830184610899565b949350505050565b6000602082019050818103600083015261093181610830565b9050919050565b6000602082019050818103600083015261095181610853565b9050919050565b6000602082019050818103600083015261097181610876565b9050919050565b600060208201905061098d6000830184610899565b92915050565b600061099d6109ae565b90506109a98282610b18565b919050565b6000604051905090565b600067ffffffffffffffff8211156109d3576109d2610b78565b5b6109dc82610bbb565b9050602081019050919050565b600081519050919050565b600082825260208201905092915050565b600081905092915050565b6000610a1b82610acc565b9150610a2683610acc565b9250827fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff03821115610a5b57610a5a610b49565b5b828201905092915050565b6000610a7182610acc565b9150610a7c83610acc565b925082821015610a8f57610a8e610b49565b5b828203905092915050565b6000610aa582610aac565b9050919050565b600073ffffffffffffffffffffffffffffffffffffffff82169050919050565b6000819050919050565b82818337600083830152505050565b60005b83811015610b03578082015181840152602081019050610ae8565b83811115610b12576000848401525b50505050565b610b2182610bbb565b810181811067ffffffffffffffff82111715610b4057610b3f610b78565b5b80604052505050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b600080fd5b600080fd5b600080fd5b600080fd5b6000601f19601f8301169050919050565b7f4f776e61626c653a206e6577206f776e657220697320746865207a65726f206160008201527f6464726573730000000000000000000000000000000000000000000000000000602082015250565b7f62616c616e636520746f6f206c6f770000000000000000000000000000000000600082015250565b7f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e6572600082015250565b610c7681610a9a565b8114610c8157600080fd5b50565b610c8d81610acc565b8114610c9857600080fd5b5056fea2646970667358221220bdef2e63f4a80f250df77d0955fb43ff301f5a632865a02ac71cdc94ded8edd964736f6c63430008070033", + retVal: &testCborBytes, }, } @@ -47,28 +57,46 @@ func TestDecodeParams(t *testing.T) { actorCodeCid, found := actors.GetActorCodeID(av, tc.ActorKey) require.True(t, found) - params, err := hex.DecodeString(tc.Bytestr) + buf := bytes.NewBuffer(nil) + if err := tc.retVal.MarshalCBOR(buf); err != nil { + t.Fatal(err) + } + + paramString, err := cli.JsonParams(actorCodeCid, tc.MethodNum, buf.Bytes()) require.NoError(t, err) - paramString, err := cli.JsonParams(actorCodeCid, tc.MethodNum, params) + jsonParams, err := json.MarshalIndent(tc.retVal, "", " ") require.NoError(t, err) - - fmt.Println(paramString) + require.Equal(t, string(jsonParams), paramString) }) } } func TestDecodeReturn(t *testing.T) { + testCborBytes := abi.CborBytes([]byte{1, 2, 3}) + + robustAddr, err := address.NewIDAddress(12345) + require.NoError(t, err) + + //ethAddr, err := ethtypes.ParseEthAddress("d4c5fb16488Aa48081296299d54b0c648C9333dA") + //require.NoError(t, err) + + testReturn := eam.CreateExternalReturn{ + ActorID: 12345, + RobustAddress: &robustAddr, + EthAddress: [20]byte{}, + } + testCases := []testCase{ { ActorKey: manifest.EvmKey, MethodNum: builtin.MethodsEVM.InvokeContract, - Bytestr: "5820000000000000000000000000000000000000000000000000000000000000002d", + retVal: &testCborBytes, }, { ActorKey: manifest.EamKey, MethodNum: builtin.MethodsEAM.CreateExternal, - Bytestr: "8319195b5502ff4af64187327799171354df10b4b3bfc2e4c30654922d6956c99e12dfeb3224dea977d0939758a1fe", + retVal: &testReturn, }, } @@ -80,13 +108,17 @@ func TestDecodeReturn(t *testing.T) { actorCodeCid, found := actors.GetActorCodeID(av, tc.ActorKey) require.True(t, found) - ret, err := hex.DecodeString(tc.Bytestr) + buf := bytes.NewBuffer(nil) + if err := tc.retVal.MarshalCBOR(buf); err != nil { + t.Fatal(err) + } + + returnString, err := cli.JsonReturn(actorCodeCid, tc.MethodNum, buf.Bytes()) require.NoError(t, err) - paramString, err := cli.JsonReturn(actorCodeCid, tc.MethodNum, ret) + jsonReturn, err := json.MarshalIndent(tc.retVal, "", " ") require.NoError(t, err) - - fmt.Println(paramString) + require.Equal(t, string(jsonReturn), returnString) }) } } From 03b419e3d47f6a1f99045a39eb2fdd5e24d3ad59 Mon Sep 17 00:00:00 2001 From: Travis Person Date: Tue, 24 Jan 2023 17:25:21 +0000 Subject: [PATCH 22/30] feat: compute a better gas limit for recursive external contract calls --- itests/contracts/EventMatrix.hex | 2 +- itests/contracts/EventMatrix.sol | 3 +- .../contracts/ExternalRecursiveCallSimple.hex | 1 + .../contracts/ExternalRecursiveCallSimple.sol | 13 ++ itests/fevm_test.go | 98 +++++++++++++ node/impl/full/eth.go | 132 +++++++++++++++++- node/impl/full/gas.go | 43 ++++-- 7 files changed, 278 insertions(+), 14 deletions(-) create mode 100644 itests/contracts/ExternalRecursiveCallSimple.hex create mode 100644 itests/contracts/ExternalRecursiveCallSimple.sol diff --git a/itests/contracts/EventMatrix.hex b/itests/contracts/EventMatrix.hex index 2b3ad91ad..be831e397 100644 --- a/itests/contracts/EventMatrix.hex +++ b/itests/contracts/EventMatrix.hex @@ -1 +1 @@ -608060405234801561001057600080fd5b506105eb806100206000396000f3fe608060405234801561001057600080fd5b50600436106100a95760003560e01c8063c755553811610071578063c755553814610198578063cbfc3b58146101c6578063cc6f8faf14610212578063cd5b6c3d14610254578063e2a614731461028c578063fb62b28b146102d8576100a9565b80630919b8be146100ae5780636199074d146100e657806366eef3461461012857806375091b1f14610132578063a63ae81a1461016a575b600080fd5b6100e4600480360360408110156100c457600080fd5b81019080803590602001909291908035906020019092919050505061031a565b005b610126600480360360608110156100fc57600080fd5b8101908080359060200190929190803590602001909291908035906020019092919050505061035d565b005b610130610391565b005b6101686004803603604081101561014857600080fd5b8101908080359060200190929190803590602001909291905050506103bf565b005b6101966004803603602081101561018057600080fd5b81019080803590602001909291905050506103fb565b005b6101c4600480360360208110156101ae57600080fd5b8101908080359060200190929190505050610435565b005b610210600480360360808110156101dc57600080fd5b8101908080359060200190929190803590602001909291908035906020019092919080359060200190929190505050610465565b005b6102526004803603606081101561022857600080fd5b810190808035906020019092919080359060200190929190803590602001909291905050506104ba565b005b61028a6004803603604081101561026a57600080fd5b8101908080359060200190929190803590602001909291905050506104f8565b005b6102d6600480360360808110156102a257600080fd5b810190808035906020019092919080359060200190929190803590602001909291908035906020019092919050505061052a565b005b610318600480360360608110156102ee57600080fd5b8101908080359060200190929190803590602001909291908035906020019092919050505061056a565b005b7f5469c6b769315f5668523937f05ca07d4cc87849432bc5f5907f1d90fa73b9f98282604051808381526020018281526020019250505060405180910390a15050565b8082847fb89dabcdb7ff41f1794c0da92f65ece6c19b6b0caeac5407b2a721efe27c080460405160405180910390a4505050565b7fc3f6f1c76bd4e74ee5782052b0b4f8bd5c50b86c3c5a2f52638e03066e50a91b60405160405180910390a1565b817f6709824ebe5f6e620ca3f4b02a3428e8ce2dc97c550816eaeeb3a342b214bd85826040518082815260200191505060405180910390a25050565b7fc804e53d6048af1b3e6a352e246d5f3864fea9d635ace499e023a58c383b3a88816040518082815260200191505060405180910390a150565b807f44a227a31429ab5eb00daf6611c6422f10571619f2267e0e149e9ebe6d2a5d0560405160405180910390a250565b7f28d45631a87b2a52a9625f8520fa37ff8c4d926cdf17042e241985da5cb7b850848484846040518085815260200184815260200183815260200182815260200194505050505060405180910390a150505050565b81837fcd5fe5fbc1d27b90036997224cea7aa565e3779622867265081f636b3a5ccb08836040518082815260200191505060405180910390a3505050565b80827f232f09cef3babc26e58d1cc1346c0a8bc626ffe600c9605b5d747783eda484a760405160405180910390a35050565b8183857f812e73dbcf7e267f27ecb1383bfc902a6650b41b6e7d03ac265108c369673d95846040518082815260200191505060405180910390a450505050565b7fd4d143faaf60340ad98e1f2c96fc26f5695834c21b5200edad339ee7e9a372cc83838360405180848152602001838152602001828152602001935050505060405180910390a150505056fea265627a7a72315820954561fde80ab925299e0a9f3356b01f64fb1976dd335ac2ebd9367441e29f0564736f6c63430005110032 +608060405234801561001057600080fd5b506106af806100206000396000f3fe608060405234801561001057600080fd5b50600436106100a95760003560e01c8063c755553811610071578063c755553814610128578063cbfc3b5814610144578063cc6f8faf14610160578063cd5b6c3d1461017c578063e2a6147314610198578063fb62b28b146101b4576100a9565b80630919b8be146100ae5780636199074d146100ca57806366eef346146100e657806375091b1f146100f0578063a63ae81a1461010c575b600080fd5b6100c860048036038101906100c39190610483565b6101d0565b005b6100e460048036038101906100df91906104c3565b61020d565b005b6100ee610241565b005b61010a60048036038101906101059190610483565b61026f565b005b61012660048036038101906101219190610516565b6102ab565b005b610142600480360381019061013d9190610516565b6102e5565b005b61015e60048036038101906101599190610543565b610315565b005b61017a600480360381019061017591906104c3565b610358565b005b61019660048036038101906101919190610483565b610396565b005b6101b260048036038101906101ad9190610543565b6103c8565b005b6101ce60048036038101906101c991906104c3565b610408565b005b7f5469c6b769315f5668523937f05ca07d4cc87849432bc5f5907f1d90fa73b9f982826040516102019291906105b9565b60405180910390a15050565b8082847fb89dabcdb7ff41f1794c0da92f65ece6c19b6b0caeac5407b2a721efe27c080460405160405180910390a4505050565b7fc3f6f1c76bd4e74ee5782052b0b4f8bd5c50b86c3c5a2f52638e03066e50a91b60405160405180910390a1565b817f6709824ebe5f6e620ca3f4b02a3428e8ce2dc97c550816eaeeb3a342b214bd858260405161029f91906105e2565b60405180910390a25050565b7fc804e53d6048af1b3e6a352e246d5f3864fea9d635ace499e023a58c383b3a88816040516102da91906105e2565b60405180910390a150565b807f44a227a31429ab5eb00daf6611c6422f10571619f2267e0e149e9ebe6d2a5d0560405160405180910390a250565b7f28d45631a87b2a52a9625f8520fa37ff8c4d926cdf17042e241985da5cb7b8508484848460405161034a94939291906105fd565b60405180910390a150505050565b81837fcd5fe5fbc1d27b90036997224cea7aa565e3779622867265081f636b3a5ccb088360405161038991906105e2565b60405180910390a3505050565b80827f232f09cef3babc26e58d1cc1346c0a8bc626ffe600c9605b5d747783eda484a760405160405180910390a35050565b8183857f812e73dbcf7e267f27ecb1383bfc902a6650b41b6e7d03ac265108c369673d95846040516103fa91906105e2565b60405180910390a450505050565b7fd4d143faaf60340ad98e1f2c96fc26f5695834c21b5200edad339ee7e9a372cc83838360405161043b93929190610642565b60405180910390a1505050565b600080fd5b6000819050919050565b6104608161044d565b811461046b57600080fd5b50565b60008135905061047d81610457565b92915050565b6000806040838503121561049a57610499610448565b5b60006104a88582860161046e565b92505060206104b98582860161046e565b9150509250929050565b6000806000606084860312156104dc576104db610448565b5b60006104ea8682870161046e565b93505060206104fb8682870161046e565b925050604061050c8682870161046e565b9150509250925092565b60006020828403121561052c5761052b610448565b5b600061053a8482850161046e565b91505092915050565b6000806000806080858703121561055d5761055c610448565b5b600061056b8782880161046e565b945050602061057c8782880161046e565b935050604061058d8782880161046e565b925050606061059e8782880161046e565b91505092959194509250565b6105b38161044d565b82525050565b60006040820190506105ce60008301856105aa565b6105db60208301846105aa565b9392505050565b60006020820190506105f760008301846105aa565b92915050565b600060808201905061061260008301876105aa565b61061f60208301866105aa565b61062c60408301856105aa565b61063960608301846105aa565b95945050505050565b600060608201905061065760008301866105aa565b61066460208301856105aa565b61067160408301846105aa565b94935050505056fea26469706673582212201b2f4de851da592b926eb2cd07ccfbbd02270fde6dee2459ba942e5dcf5685d364736f6c63430008110033 \ No newline at end of file diff --git a/itests/contracts/EventMatrix.sol b/itests/contracts/EventMatrix.sol index bd008e27b..f1d63c69e 100644 --- a/itests/contracts/EventMatrix.sol +++ b/itests/contracts/EventMatrix.sol @@ -1,4 +1,5 @@ -pragma solidity ^0.5.0; +// SPDX-License-Identifier: MIT +pragma solidity >=0.5.0; contract EventMatrix { event EventZeroData(); diff --git a/itests/contracts/ExternalRecursiveCallSimple.hex b/itests/contracts/ExternalRecursiveCallSimple.hex new file mode 100644 index 000000000..03d79fe2d --- /dev/null +++ b/itests/contracts/ExternalRecursiveCallSimple.hex @@ -0,0 +1 @@ +608060405234801561001057600080fd5b506101ee806100206000396000f3fe60806040526004361061001e5760003560e01c8063c38e07dd14610023575b600080fd5b61003d600480360381019061003891906100fe565b61003f565b005b60008111156100c0573073ffffffffffffffffffffffffffffffffffffffff1663c38e07dd600183610071919061015a565b6040518263ffffffff1660e01b815260040161008d919061019d565b600060405180830381600087803b1580156100a757600080fd5b505af11580156100bb573d6000803e3d6000fd5b505050505b50565b600080fd5b6000819050919050565b6100db816100c8565b81146100e657600080fd5b50565b6000813590506100f8816100d2565b92915050565b600060208284031215610114576101136100c3565b5b6000610122848285016100e9565b91505092915050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b6000610165826100c8565b9150610170836100c8565b92508282039050818111156101885761018761012b565b5b92915050565b610197816100c8565b82525050565b60006020820190506101b2600083018461018e565b9291505056fea264697066735822122033d012e17f5d7a62bb724021b5c4e0d109aeb28d1cd5b5c0a0b1b801c0b5032164736f6c63430008110033 \ No newline at end of file diff --git a/itests/contracts/ExternalRecursiveCallSimple.sol b/itests/contracts/ExternalRecursiveCallSimple.sol new file mode 100644 index 000000000..97a27811b --- /dev/null +++ b/itests/contracts/ExternalRecursiveCallSimple.sol @@ -0,0 +1,13 @@ + +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.17; + +contract StackRecCallExp { + function exec1(uint256 r) public payable { + if(r > 0) { + StackRecCallExp(address(this)).exec1(r-1); + } + + return; + } +} diff --git a/itests/fevm_test.go b/itests/fevm_test.go index 304a805c3..14b767621 100644 --- a/itests/fevm_test.go +++ b/itests/fevm_test.go @@ -16,6 +16,7 @@ import ( "github.com/filecoin-project/go-state-types/exitcode" "github.com/filecoin-project/go-state-types/manifest" + "github.com/filecoin-project/lotus/build" "github.com/filecoin-project/lotus/chain/types" "github.com/filecoin-project/lotus/chain/types/ethtypes" "github.com/filecoin-project/lotus/itests/kit" @@ -606,3 +607,100 @@ func TestFEVMRecursiveActorCall(t *testing.T) { t.Run("n=0,r=256-fails", testN(0, 256, exitcode.ExitCode(33))) // 33 means transaction reverted t.Run("n=251,r=167-fails", testN(251, 167, exitcode.ExitCode(33))) } + +// TestFEVMRecursiveActorCallEstimate +func TestFEVMRecursiveActorCallEstimate(t *testing.T) { + ctx, cancel, client := kit.SetupFEVMTest(t) + defer cancel() + + //install contract Actor + filenameActor := "contracts/ExternalRecursiveCallSimple.hex" + _, actorAddr := client.EVM().DeployContractFromFilename(ctx, filenameActor) + + contractAddr, err := ethtypes.EthAddressFromFilecoinAddress(actorAddr) + require.NoError(t, err) + + // create a new Ethereum account + key, ethAddr, ethFilAddr := client.EVM().NewAccount() + kit.SendFunds(ctx, t, client, ethFilAddr, types.FromFil(1000)) + + makeParams := func(r int) []byte { + funcSignature := "exec1(uint256)" + entryPoint := kit.CalcFuncSignature(funcSignature) + + inputData := make([]byte, 32) + binary.BigEndian.PutUint64(inputData[24:], uint64(r)) + + params := append(entryPoint, inputData...) + + return params + } + + testN := func(r int) func(t *testing.T) { + return func(t *testing.T) { + t.Logf("running with %d recursive calls", r) + + params := makeParams(r) + gaslimit, err := client.EthEstimateGas(ctx, ethtypes.EthCall{ + From: ðAddr, + To: &contractAddr, + Data: params, + }) + require.NoError(t, err) + require.LessOrEqual(t, int64(gaslimit), build.BlockGasLimit) + + t.Logf("EthEstimateGas GasLimit=%d", gaslimit) + + maxPriorityFeePerGas, err := client.EthMaxPriorityFeePerGas(ctx) + require.NoError(t, err) + + nonce, err := client.MpoolGetNonce(ctx, ethFilAddr) + require.NoError(t, err) + + tx := ðtypes.EthTxArgs{ + ChainID: build.Eip155ChainId, + To: &contractAddr, + Value: big.Zero(), + Nonce: int(nonce), + MaxFeePerGas: types.NanoFil, + MaxPriorityFeePerGas: big.Int(maxPriorityFeePerGas), + GasLimit: int(gaslimit), + Input: params, + V: big.Zero(), + R: big.Zero(), + S: big.Zero(), + } + + client.EVM().SignTransaction(tx, key.PrivateKey) + hash := client.EVM().SubmitTransaction(ctx, tx) + + smsg, err := tx.ToSignedMessage() + require.NoError(t, err) + + _, err = client.StateWaitMsg(ctx, smsg.Cid(), 0, 0, false) + require.NoError(t, err) + + receipt, err := client.EthGetTransactionReceipt(ctx, hash) + require.NoError(t, err) + require.NotNil(t, receipt) + + t.Logf("Receipt GasUsed=%d", receipt.GasUsed) + t.Logf("Ratio %0.2f", float64(receipt.GasUsed)/float64(gaslimit)) + t.Logf("Overestimate %0.2f", ((float64(gaslimit)/float64(receipt.GasUsed))-1)*100) + + require.EqualValues(t, ethtypes.EthUint64(1), receipt.Status) + } + } + + t.Run("n=1", testN(1)) + t.Run("n=2", testN(2)) + t.Run("n=3", testN(3)) + t.Run("n=4", testN(4)) + t.Run("n=5", testN(5)) + t.Run("n=10", testN(10)) + t.Run("n=20", testN(20)) + t.Run("n=30", testN(30)) + t.Run("n=40", testN(40)) + t.Run("n=50", testN(50)) + t.Run("n=100", testN(100)) +} diff --git a/node/impl/full/eth.go b/node/impl/full/eth.go index 679f61e37..02d055d03 100644 --- a/node/impl/full/eth.go +++ b/node/impl/full/eth.go @@ -24,6 +24,7 @@ import ( "github.com/filecoin-project/go-state-types/builtin/v10/eam" "github.com/filecoin-project/go-state-types/builtin/v10/evm" "github.com/filecoin-project/go-state-types/crypto" + "github.com/filecoin-project/go-state-types/exitcode" "github.com/filecoin-project/lotus/api" "github.com/filecoin-project/lotus/build" @@ -809,12 +810,137 @@ func (a *EthModule) EthEstimateGas(ctx context.Context, tx ethtypes.EthCall) (et // gas estimation actually run. msg.GasLimit = 0 - msg, err = a.GasAPI.GasEstimateMessageGas(ctx, msg, nil, types.EmptyTSK) + ts := a.Chain.GetHeaviestTipSet() + msg, err = a.GasAPI.GasEstimateMessageGas(ctx, msg, nil, ts.Key()) if err != nil { - return ethtypes.EthUint64(0), err + return ethtypes.EthUint64(0), xerrors.Errorf("failed to estimate gas: %w", err) } - return ethtypes.EthUint64(msg.GasLimit), nil + expectedGas, err := ethGasSearch(ctx, a.Chain, a.Stmgr, a.Mpool, msg, ts) + if err != nil { + log.Errorw("expected gas", "err", err) + } + + return ethtypes.EthUint64(expectedGas), nil +} + +// gasSearch does an exponential search to find a gas value to execute the +// message with. It first finds a high gas limit that allows the message to execute +// by doubling the previous gas limit until it succeeds then does a binary +// search till it gets within a range of 1% +func gasSearch( + ctx context.Context, + smgr *stmgr.StateManager, + msgIn *types.Message, + priorMsgs []types.ChainMsg, + ts *types.TipSet, +) (int64, error) { + msg := *msgIn + + high := msg.GasLimit + low := msg.GasLimit + + canSucceed := func(limit int64) (bool, error) { + msg.GasLimit = limit + + res, err := smgr.CallWithGas(ctx, &msg, priorMsgs, ts) + if err != nil { + return false, xerrors.Errorf("CallWithGas failed: %w", err) + } + + if res.MsgRct.ExitCode.IsSuccess() { + return true, nil + } + + return false, nil + } + + for { + ok, err := canSucceed(high) + if err != nil { + return -1, xerrors.Errorf("searching for high gas limit failed: %w", err) + } + if ok { + break + } + + low = high + high = high * 2 + + if high > build.BlockGasLimit { + high = build.BlockGasLimit + break + } + } + + checkThreshold := high / 100 + for (high - low) > checkThreshold { + median := (low + high) / 2 + ok, err := canSucceed(median) + if err != nil { + return -1, xerrors.Errorf("searching for optimal gas limit failed: %w", err) + } + + if ok { + high = median + } else { + low = median + } + + checkThreshold = median / 100 + } + + return high, nil +} + +func traceContainsExitCode(et types.ExecutionTrace, ex exitcode.ExitCode) bool { + if et.MsgRct.ExitCode == ex { + return true + } + + for _, et := range et.Subcalls { + if traceContainsExitCode(et, ex) { + return true + } + } + + return false +} + +// ethGasSearch executes a message for gas estimation using the previously estimated gas. +// If the message fails due to an out of gas error then a gas search is performed. +// See gasSearch. +func ethGasSearch( + ctx context.Context, + cstore *store.ChainStore, + smgr *stmgr.StateManager, + mpool *messagepool.MessagePool, + msgIn *types.Message, + ts *types.TipSet, +) (int64, error) { + msg := *msgIn + currTs := ts + + res, priorMsgs, ts, err := gasEstimateCallWithGas(ctx, cstore, smgr, mpool, &msg, currTs) + if err != nil { + return -1, xerrors.Errorf("gas estimation failed: %w", err) + } + + if res.MsgRct.ExitCode.IsSuccess() { + return msg.GasLimit, nil + } + + if traceContainsExitCode(res.ExecutionTrace, exitcode.SysErrOutOfGas) { + ret, err := gasSearch(ctx, smgr, &msg, priorMsgs, ts) + if err != nil { + return -1, xerrors.Errorf("gas estimation search failed: %w", err) + } + + ret = int64(float64(ret) * mpool.GetConfig().GasLimitOverestimation) + return ret, nil + } + + return -1, xerrors.Errorf("message execution failed: exit %s, reason: %s", res.MsgRct.ExitCode, res.Error) } func (a *EthModule) EthCall(ctx context.Context, tx ethtypes.EthCall, blkParam string) (ethtypes.EthBytes, error) { diff --git a/node/impl/full/gas.go b/node/impl/full/gas.go index 435e2c65b..c0e328bc9 100644 --- a/node/impl/full/gas.go +++ b/node/impl/full/gas.go @@ -248,22 +248,23 @@ func (m *GasModule) GasEstimateGasLimit(ctx context.Context, msgIn *types.Messag } return gasEstimateGasLimit(ctx, m.Chain, m.Stmgr, m.Mpool, msgIn, ts) } -func gasEstimateGasLimit( + +// gasEstimateCallWithGas invokes a message "msgIn" on the earliest available tipset with pending +// messages in the message pool. The function returns the result of the message invocation, the +// pending messages, the tipset used for the invocation, and an error if occurred. +// The returned information can be used to make subsequent calls to CallWithGas with the same parameters. +func gasEstimateCallWithGas( ctx context.Context, cstore *store.ChainStore, smgr *stmgr.StateManager, mpool *messagepool.MessagePool, msgIn *types.Message, currTs *types.TipSet, -) (int64, error) { +) (*api.InvocResult, []types.ChainMsg, *types.TipSet, error) { msg := *msgIn - msg.GasLimit = build.BlockGasLimit - msg.GasFeeCap = big.Zero() - msg.GasPremium = big.Zero() - fromA, err := smgr.ResolveToDeterministicAddress(ctx, msgIn.From, currTs) if err != nil { - return -1, xerrors.Errorf("getting key address: %w", err) + return nil, []types.ChainMsg{}, nil, xerrors.Errorf("getting key address: %w", err) } pending, ts := mpool.PendingFor(ctx, fromA) @@ -284,12 +285,34 @@ func gasEstimateGasLimit( } ts, err = cstore.GetTipSetFromKey(ctx, ts.Parents()) if err != nil { - return -1, xerrors.Errorf("getting parent tipset: %w", err) + return nil, []types.ChainMsg{}, nil, xerrors.Errorf("getting parent tipset: %w", err) } } if err != nil { - return -1, xerrors.Errorf("CallWithGas failed: %w", err) + return nil, []types.ChainMsg{}, nil, xerrors.Errorf("CallWithGas failed: %w", err) } + + return res, priorMsgs, ts, nil +} + +func gasEstimateGasLimit( + ctx context.Context, + cstore *store.ChainStore, + smgr *stmgr.StateManager, + mpool *messagepool.MessagePool, + msgIn *types.Message, + currTs *types.TipSet, +) (int64, error) { + msg := *msgIn + msg.GasLimit = build.BlockGasLimit + msg.GasFeeCap = big.Zero() + msg.GasPremium = big.Zero() + + res, _, ts, err := gasEstimateCallWithGas(ctx, cstore, smgr, mpool, &msg, currTs) + if err != nil { + return -1, xerrors.Errorf("gas estimation failed: %w", err) + } + if res.MsgRct.ExitCode == exitcode.SysErrOutOfGas { return -1, &api.ErrOutOfGas{} } @@ -300,6 +323,8 @@ func gasEstimateGasLimit( ret := res.MsgRct.GasUsed + log.Infow("GasEstimateMessageGas CallWithGas Result", "GasUsed", ret, "ExitCode", res.MsgRct.ExitCode) + transitionalMulti := 1.0 // Overestimate gas around the upgrade if ts.Height() <= build.UpgradeSkyrHeight && (build.UpgradeSkyrHeight-ts.Height() <= 20) { From bca48dd1eba7eb14516aa22fe7c96363299cfb10 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Magiera?= Date: Mon, 6 Feb 2023 10:46:24 +0100 Subject: [PATCH 23/30] make gen --- .circleci/config.yml | 10 +++++----- build/openrpc/full.json.gz | Bin 33149 -> 33104 bytes build/openrpc/gateway.json.gz | Bin 8482 -> 8481 bytes documentation/en/api-v1-unstable-methods.md | 6 +++--- 4 files changed, 8 insertions(+), 8 deletions(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index 1cf8ae94c..c3db959dd 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -623,16 +623,16 @@ workflows: suite: itest-eth_block_hash target: "./itests/eth_block_hash_test.go" + - test: + name: test-itest-eth_config + suite: itest-eth_config + target: "./itests/eth_config_test.go" + - test: name: test-itest-eth_conformance suite: itest-eth_conformance target: "./itests/eth_conformance_test.go" - - test: - name: test-itest-eth_config - suite: itest-eth_config - target: "./itests/eth_config_test.go" - - test: name: test-itest-eth_deploy suite: itest-eth_deploy diff --git a/build/openrpc/full.json.gz b/build/openrpc/full.json.gz index ddeb9c9a3fc9e25feda96d3e32b9488e497b6575..1fa47bab5929323a6272378d92ad39f24f592e70 100644 GIT binary patch delta 32849 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

a0 zUK`}@Qo!z)t=xRk-p}k_5BO@7s~fm!~5hiqY|xL|M>CuYR)Lx(4ma?8R5jNdbdx8C7a+Mn#vSq zZ&(QH{3G5JeQ#o5UW~!NYfXczY!aeL>-bKAmg`9GwLT@3cg-E8V{UExCQ*_W5KmH! z(|bECgzGwRnE{v}2^q0mFe)QZ7#M)09m%Oqz~4y$KVG?pyH1N$R&OyA9xHuhoYS??wtRub#K6hJ^{l zFF08C2c0rqVyAlyWO!L$qF<*xemrQ6Y$um^!_9!*0HK&b|NFZoThyXNB+Y+vMw)x8 z&EjZ!E;(@Qz^^b2t#MuWD9Y~7UkPL(Ud;Ufb`YTS&TPe$C z0>y4v2wNUu{}nhg^jsW2)+B^&@kjq;akbC>F$Jxm>X#*S5fw?NAZbRtm5$HPMlZ4N{=#s4<%i*C93L?(mJi3d& z?;T>Lk-e9pw^8)V`f`W>T^n#*MtC`%uu}q_inZh(G>Z(UjFz%IPmUmg2`IKY*aF0^ z3Lh$E{s#dv%I*h1zb||~)#C`49r=y6(Ls13kZ5~GpP-?nDRN62rjGdeeMU~8wq{q^ z)f-+_f+EI0IkAmho9cP~mBj-uRrGx0bUrc0UntjOb62&`efHwp&t3Wh_UK1TdEpPl zX{S23&bVd^p3=hJ;Vo|KL(O>9z*eB*?oL$P`a@>L*o|7jv+5f+z^Q7*Z1{F0qdoAh zY`NIC4R?fWOnw42lQgSgh)+e2C6XGL`ydZEOH1$dbdl>`U8N+af(K04U}Nv&Niz}W zWI)>M2?yf+USl_@W45c(t~$Esx2JdfL^42s;jM^QOC+?3wa4IAg;}&~4FJ*riATB> ziG4;DF06a2qViP#T^{+thu7daIk&HkbjK#=2b-{c2(bZ%Y3;_iOd(^~&2QKke^e#Z2{Z_5zZJ7Za@s5qgInsM+CUS_cM?IW?kT zt^Q=eO`lO0Y8Xy!`aLf^Si-tcpv%PkYsyVqH^yiLHKx%a=;z5H)CB@>q-k;syks7E z%x%?@XH;Xsak5UOjrSb=5uOKMPpn8ythVdTepegHaC4DodS(zI2r^$qA`z^=XjJxo z^0vaaP(b4b)^UvCjySfA@gi{N`B%H0SZkUbr+!V_-@nPr@i7?uJ?HYQm+2Gm_E0-A zD*1u7U&*x+O7C3Xc) z2{j}#qyflsT?{|-DZpcYu6ptiZ*r%6c+C9S8c3FgbX1!mD4H-CRQkger#nKP%SBWH*46EEdxmFMMl)6ZKV zZyDDpZm1i#haYI+`}Km=kyiX17uzznQ&jm=cwB$xBJ}Oq{B*aJxOYQ|HNb~(`vLV* z7_IlKCnsw*0H|L*xRqBt;!)oIl4RBOO2{)8(yL(as&H`cyfOmF{t5NOQ9+5=M4ofq z`(-Wpah9#{wHhO}Zi^1nlIP?BDz`%^!l;aICR!Nc%Lyb%g>IHj(ixcxHL<*5Ti8I< z2$#wOFiLG5@v>3?ra0r@nbaP=Dt!AKPL5QYkrr$iT?6K&ma>FFHc_r2i5V|!3oVUROPz2 zrL#p9R)za1uIjG3Y=YY}*<}-qK{KUxC;82kXLkF~>cOt?m*3af7dQyW*O%#&<;8n3 H6v+PoCtj@l diff --git a/build/openrpc/gateway.json.gz b/build/openrpc/gateway.json.gz index 790c44fc42bfa55e6540996c6a9b24c5142a16e5..f10a433e32c53993f6846ac3259f3bd75d135e25 100644 GIT binary patch literal 8481 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 P00960$pwa$QPKebz5kxQ literal 8482 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 Q7XSeN{}u+j&=k@E0Heux82|tP diff --git a/documentation/en/api-v1-unstable-methods.md b/documentation/en/api-v1-unstable-methods.md index c8c650395..5ec108347 100644 --- a/documentation/en/api-v1-unstable-methods.md +++ b/documentation/en/api-v1-unstable-methods.md @@ -2342,7 +2342,7 @@ Inputs: Response: ```json { - "oldestBlock": 42, + "oldestBlock": "0x5", "baseFeePerGas": [ "0x0" ], @@ -2407,7 +2407,7 @@ Response: "gasLimit": "0x5", "gasUsed": "0x5", "timestamp": "0x5", - "extraData": "Ynl0ZSBhcnJheQ==", + "extraData": "0x07", "mixHash": "0x37690cfec6c1bf4c3b9288c7a5d783e98731e90b0a4c177c2a374c7a9427355e", "nonce": "0x0707070707070707", "baseFeePerGas": "0x0", @@ -2451,7 +2451,7 @@ Response: "gasLimit": "0x5", "gasUsed": "0x5", "timestamp": "0x5", - "extraData": "Ynl0ZSBhcnJheQ==", + "extraData": "0x07", "mixHash": "0x37690cfec6c1bf4c3b9288c7a5d783e98731e90b0a4c177c2a374c7a9427355e", "nonce": "0x0707070707070707", "baseFeePerGas": "0x0", From 424824019b402b29d1d06967ad484c1d247f3d7e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Magiera?= Date: Mon, 6 Feb 2023 14:38:48 +0100 Subject: [PATCH 24/30] fix: ethtypes: Correct 'no uncles' hash in NewEthBlock --- chain/types/ethtypes/eth_types.go | 4 +++- lib/must/must.go | 9 +++++++++ 2 files changed, 12 insertions(+), 1 deletion(-) create mode 100644 lib/must/must.go diff --git a/chain/types/ethtypes/eth_types.go b/chain/types/ethtypes/eth_types.go index e84517396..9d1924d9c 100644 --- a/chain/types/ethtypes/eth_types.go +++ b/chain/types/ethtypes/eth_types.go @@ -22,6 +22,7 @@ import ( builtintypes "github.com/filecoin-project/go-state-types/builtin" "github.com/filecoin-project/lotus/build" + "github.com/filecoin-project/lotus/lib/must" ) var ( @@ -157,13 +158,14 @@ type EthBlock struct { var ( EmptyEthBloom = [256]byte{} EmptyEthHash = EthHash{} + NoUncleHash = must.One(ParseEthHash("0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347")) EmptyEthInt = EthUint64(0) EmptyEthNonce = [8]byte{0, 0, 0, 0, 0, 0, 0, 0} ) func NewEthBlock() EthBlock { return EthBlock{ - Sha3Uncles: EmptyEthHash, + Sha3Uncles: NoUncleHash, StateRoot: EmptyEthHash, TransactionsRoot: EmptyEthHash, ReceiptsRoot: EmptyEthHash, diff --git a/lib/must/must.go b/lib/must/must.go new file mode 100644 index 000000000..e072b4e04 --- /dev/null +++ b/lib/must/must.go @@ -0,0 +1,9 @@ +package must + +func One[R any](r R, err error) R { + if err != nil { + panic(err) + } + + return r +} From 5dc56841eacfdcaf70733a2c47448f532d57faf7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Magiera?= Date: Mon, 6 Feb 2023 15:00:42 +0100 Subject: [PATCH 25/30] fix: ethtypes: Correct 'no transactions' hash in NewEthBlock --- chain/types/ethtypes/eth_types.go | 24 +++++++++++++++--------- node/impl/full/eth.go | 2 +- 2 files changed, 16 insertions(+), 10 deletions(-) diff --git a/chain/types/ethtypes/eth_types.go b/chain/types/ethtypes/eth_types.go index 9d1924d9c..d003e5ed2 100644 --- a/chain/types/ethtypes/eth_types.go +++ b/chain/types/ethtypes/eth_types.go @@ -156,18 +156,19 @@ type EthBlock struct { } var ( - EmptyEthBloom = [256]byte{} - EmptyEthHash = EthHash{} - NoUncleHash = must.One(ParseEthHash("0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347")) - EmptyEthInt = EthUint64(0) - EmptyEthNonce = [8]byte{0, 0, 0, 0, 0, 0, 0, 0} + EmptyEthBloom = [256]byte{} + EmptyEthHash = EthHash{} + EmptyUncleHash = must.One(ParseEthHash("0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347")) // Keccak-256 of an RLP of an empty array + EmptyRootHash = must.One(ParseEthHash("0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421")) // Keccak-256 hash of the RLP of null + EmptyEthInt = EthUint64(0) + EmptyEthNonce = [8]byte{0, 0, 0, 0, 0, 0, 0, 0} ) -func NewEthBlock() EthBlock { - return EthBlock{ - Sha3Uncles: NoUncleHash, +func NewEthBlock(hasTransactions bool) EthBlock { + b := EthBlock{ + Sha3Uncles: EmptyUncleHash, // Sha3Uncles set to a hardcoded value which is used by some clients to determine if has no uncles. StateRoot: EmptyEthHash, - TransactionsRoot: EmptyEthHash, + TransactionsRoot: EmptyRootHash, // TransactionsRoot set to a hardcoded value which is used by some clients to determine if has no transactions. ReceiptsRoot: EmptyEthHash, Difficulty: EmptyEthInt, LogsBloom: EmptyEthBloom[:], @@ -178,6 +179,11 @@ func NewEthBlock() EthBlock { Uncles: []EthHash{}, Transactions: []interface{}{}, } + if hasTransactions { + b.TransactionsRoot = EmptyEthHash + } + + return b } type EthCall struct { diff --git a/node/impl/full/eth.go b/node/impl/full/eth.go index 040611684..6d144b9ae 100644 --- a/node/impl/full/eth.go +++ b/node/impl/full/eth.go @@ -1598,7 +1598,7 @@ func newEthBlockFromFilecoinTipSet(ctx context.Context, ts *types.TipSet, fullTx return ethtypes.EthBlock{}, xerrors.Errorf("error loading messages for tipset: %v: %w", ts, err) } - block := ethtypes.NewEthBlock() + block := ethtypes.NewEthBlock(len(msgs) > 0) // this seems to be a very expensive way to get gasUsed of the block. may need to find an efficient way to do it gasUsed := int64(0) From 6bf3a21158d00ea5e51ee35116cbb177f4728b8c Mon Sep 17 00:00:00 2001 From: Steven Allen Date: Mon, 6 Feb 2023 09:20:22 -0800 Subject: [PATCH 26/30] itest: fix FEVM tests for upstream changes --- itests/fevm_test.go | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/itests/fevm_test.go b/itests/fevm_test.go index 14b767621..15b45ad4a 100644 --- a/itests/fevm_test.go +++ b/itests/fevm_test.go @@ -57,7 +57,7 @@ func buildInputFromuint64(number uint64) []byte { // recursive delegate calls that fail due to gas limits are currently getting to 229 iterations // before running out of gas func recursiveDelegatecallFail(ctx context.Context, t *testing.T, client *kit.TestFullNode, filename string, count uint64) { - expectedIterationsBeforeFailing := int(229) + expectedIterationsBeforeFailing := int(228) fromAddr, idAddr := client.EVM().DeployContractFromFilename(ctx, filename) t.Log("recursion count - ", count) inputData := buildInputFromuint64(count) @@ -123,7 +123,7 @@ func TestFEVMRecursiveFail(t *testing.T) { t.Run(fmt.Sprintf("TestFEVMRecursiveFail%d", failCallCount), func(t *testing.T) { _, wait, err := client.EVM().InvokeContractByFuncName(ctx, fromAddr, idAddr, "recursiveCall(uint256)", buildInputFromuint64(failCallCount)) require.Error(t, err) - require.Equal(t, exitcode.ExitCode(23), wait.Receipt.ExitCode) + require.Equal(t, exitcode.ExitCode(37), wait.Receipt.ExitCode) }) } } @@ -151,7 +151,7 @@ func TestFEVMRecursive2(t *testing.T) { } // TestFEVMBasic does a basic fevm contract installation and invocation -// recursive delegate call succeeds up to 238 times +// recursive delegate call succeeds up to 228 times func TestFEVMRecursiveDelegatecall(t *testing.T) { ctx, cancel, client := kit.SetupFEVMTest(t) @@ -159,11 +159,11 @@ func TestFEVMRecursiveDelegatecall(t *testing.T) { filename := "contracts/RecursiveDelegeatecall.hex" - //success with 238 or fewer calls - for i := uint64(1); i <= 238; i += 30 { + //success with 228 or fewer calls + for i := uint64(1); i <= 228; i += 30 { recursiveDelegatecallSuccess(ctx, t, client, filename, i) } - recursiveDelegatecallSuccess(ctx, t, client, filename, uint64(238)) + recursiveDelegatecallSuccess(ctx, t, client, filename, uint64(228)) for i := uint64(239); i <= 800; i += 40 { recursiveDelegatecallFail(ctx, t, client, filename, i) From d16b2902d46d789a693249bb9e096b01220ccdd7 Mon Sep 17 00:00:00 2001 From: Steven Allen Date: Mon, 6 Feb 2023 09:52:38 -0800 Subject: [PATCH 27/30] itest: fix: test comment --- itests/fevm_test.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/itests/fevm_test.go b/itests/fevm_test.go index 15b45ad4a..b6ad88b8e 100644 --- a/itests/fevm_test.go +++ b/itests/fevm_test.go @@ -150,8 +150,8 @@ func TestFEVMRecursive2(t *testing.T) { require.Equal(t, 2, len(events)) } -// TestFEVMBasic does a basic fevm contract installation and invocation -// recursive delegate call succeeds up to 228 times +// TestFEVMRecursiveDelegateCall tests the maximum delegatecall recursion depth. It currently +// succeeds succeeds up to 228 times. func TestFEVMRecursiveDelegatecall(t *testing.T) { ctx, cancel, client := kit.SetupFEVMTest(t) From e0931f8f90adaf46133a400fed87bdc97ac7c53e Mon Sep 17 00:00:00 2001 From: Steven Allen Date: Mon, 6 Feb 2023 11:01:49 -0800 Subject: [PATCH 28/30] itest: fix remaining fevm failures --- itests/fevm_test.go | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/itests/fevm_test.go b/itests/fevm_test.go index b6ad88b8e..fdf88db80 100644 --- a/itests/fevm_test.go +++ b/itests/fevm_test.go @@ -470,10 +470,9 @@ func TestFEVMSendGasLimit(t *testing.T) { } // TestFEVMDelegateCall deploys the two contracts in TestFEVMDelegateCall but instead of A calling B, A calls A which should cause A to cause A in an infinite loop and should give a reasonable error -// XXX should not be fatal errors func TestFEVMDelegateCallRecursiveFail(t *testing.T) { - //TODO change the gas limit of this invocation and confirm that the number of errors is different - //also TODO should we not have fatal error show up here? + //TODO change the gas limit of this invocation and confirm that the number of errors is + // different ctx, cancel, client := kit.SetupFEVMTest(t) defer cancel() @@ -486,17 +485,16 @@ func TestFEVMDelegateCallRecursiveFail(t *testing.T) { inputDataValue := inputDataFromArray([]byte{7}) inputData := append(inputDataContract, inputDataValue...) - //verify that the returned value of the call to setvars is 7 + //verify that we run out of gas then revert. _, wait, err := client.EVM().InvokeContractByFuncName(ctx, fromAddr, actorAddr, "setVarsSelf(address,uint256)", inputData) require.Error(t, err) - require.Equal(t, exitcode.SysErrorIllegalArgument, wait.Receipt.ExitCode) + require.Equal(t, exitcode.ExitCode(33), wait.Receipt.ExitCode) //assert no fatal errors but still there are errors:: errorAny := "fatal error" require.NotContains(t, err.Error(), errorAny) } -// XXX Currently fails as self destruct has a bug // TestFEVMTestSendValueThroughContracts creates A and B contract and exchanges value // and self destructs and accounts for value sent func TestFEVMTestSendValueThroughContractsAndDestroy(t *testing.T) { From 1ab53051f7b3e20065a6630f37cec1d241cdf369 Mon Sep 17 00:00:00 2001 From: snissn Date: Mon, 6 Feb 2023 10:17:15 -1000 Subject: [PATCH 29/30] improve evm error handling in itests (#10161) --- itests/kit/evm.go | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/itests/kit/evm.go b/itests/kit/evm.go index 35f50d4c1..eac8f0234 100644 --- a/itests/kit/evm.go +++ b/itests/kit/evm.go @@ -5,6 +5,7 @@ import ( "context" "encoding/binary" "encoding/hex" + "errors" "fmt" "os" "testing" @@ -126,7 +127,11 @@ func (e *EVM) InvokeSolidity(ctx context.Context, sender address.Address, target if err != nil { return nil, err } - + if !wait.Receipt.ExitCode.IsSuccess() { + result, err := e.StateReplay(ctx, types.EmptyTSK, wait.Message) + require.NoError(e.t, err) + e.t.Log(result.Error) + } return wait, nil } @@ -244,7 +249,9 @@ func (e *EVM) InvokeContractByFuncName(ctx context.Context, fromAddr address.Add return nil, wait, err } if !wait.Receipt.ExitCode.IsSuccess() { - return nil, wait, fmt.Errorf("contract execution failed - %v", wait.Receipt.ExitCode) + result, err := e.StateReplay(ctx, types.EmptyTSK, wait.Message) + require.NoError(e.t, err) + return nil, wait, errors.New(result.Error) } result, err := cbg.ReadByteArray(bytes.NewBuffer(wait.Receipt.Return), uint64(len(wait.Receipt.Return))) if err != nil { From 23eaee49d409757959c2b80a4edc5a1aaaf2c611 Mon Sep 17 00:00:00 2001 From: snissn Date: Mon, 6 Feb 2023 11:32:39 -1000 Subject: [PATCH 30/30] clean up test for recursive delegate call count. improved readability (#10195) --- itests/fevm_test.go | 24 +++++++++++++----------- 1 file changed, 13 insertions(+), 11 deletions(-) diff --git a/itests/fevm_test.go b/itests/fevm_test.go index fdf88db80..68a653ca0 100644 --- a/itests/fevm_test.go +++ b/itests/fevm_test.go @@ -150,24 +150,26 @@ func TestFEVMRecursive2(t *testing.T) { require.Equal(t, 2, len(events)) } -// TestFEVMRecursiveDelegateCall tests the maximum delegatecall recursion depth. It currently -// succeeds succeeds up to 228 times. -func TestFEVMRecursiveDelegatecall(t *testing.T) { +// TestFEVMRecursiveDelegatecallCount tests the maximum delegatecall recursion depth. It currently +// succeeds succeeds up to 237 times. +func TestFEVMRecursiveDelegatecallCount(t *testing.T) { ctx, cancel, client := kit.SetupFEVMTest(t) defer cancel() + highestSuccessCount := uint64(237) + filename := "contracts/RecursiveDelegeatecall.hex" + recursiveDelegatecallSuccess(ctx, t, client, filename, uint64(1)) + recursiveDelegatecallSuccess(ctx, t, client, filename, uint64(2)) + recursiveDelegatecallSuccess(ctx, t, client, filename, uint64(10)) + recursiveDelegatecallSuccess(ctx, t, client, filename, uint64(100)) + recursiveDelegatecallSuccess(ctx, t, client, filename, highestSuccessCount) - //success with 228 or fewer calls - for i := uint64(1); i <= 228; i += 30 { - recursiveDelegatecallSuccess(ctx, t, client, filename, i) - } - recursiveDelegatecallSuccess(ctx, t, client, filename, uint64(228)) + recursiveDelegatecallFail(ctx, t, client, filename, highestSuccessCount+1) + recursiveDelegatecallFail(ctx, t, client, filename, uint64(1000)) + recursiveDelegatecallFail(ctx, t, client, filename, uint64(10000000)) - for i := uint64(239); i <= 800; i += 40 { - recursiveDelegatecallFail(ctx, t, client, filename, i) - } } // TestFEVMBasic does a basic fevm contract installation and invocation