diff --git a/go.sum b/go.sum index 181c530d4056..49fe151bf251 100644 --- a/go.sum +++ b/go.sum @@ -43,8 +43,8 @@ connectrpc.com/grpcreflect v1.3.0/go.mod h1:nfloOtCS8VUQOQ1+GTdFzVg2CJo4ZGaat8JI dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU= github.com/AndreasBriese/bbloom v0.0.0-20190306092124-e2d15f34fcf9/go.mod h1:bOvUY6CB00SOBii9/FifXqc0awNKxLFCL/+pkDPuyl8= github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= -github.com/BurntSushi/toml v1.5.0 h1:W5quZX/G/csjUnuI8SUYlsHs9M38FC7znL0lIO+DvMg= -github.com/BurntSushi/toml v1.5.0/go.mod h1:ukJfTF/6rtPPRCnwkur4qwRxa8vTRFBF0uk2lLoLwho= +github.com/BurntSushi/toml v1.3.2 h1:o7IhLm0Msx3BaB+n3Ag7L8EVlByGnpq14C4YWiu/gL8= +github.com/BurntSushi/toml v1.3.2/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ= github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo= github.com/CloudyKit/fastprinter v0.0.0-20200109182630-33d98a066a53/go.mod h1:+3IMCy2vIlbG1XG/0ggNQv0SvxCAIpPM5b1nCz56Xno= github.com/CloudyKit/jet/v3 v3.0.0/go.mod h1:HKQPgSJmdK8hdoAbKUUWajkHyHo4RaU5rMdUywE7VMo= diff --git a/graft/coreth/accounts/abi/bind/base.go b/graft/coreth/accounts/abi/bind/base.go index 2e230d8ce6c2..dedf592a6890 100644 --- a/graft/coreth/accounts/abi/bind/base.go +++ b/graft/coreth/accounts/abi/bind/base.go @@ -37,7 +37,7 @@ import ( "github.com/ava-labs/avalanchego/graft/coreth/accounts/abi" "github.com/ava-labs/avalanchego/graft/coreth/nativeasset" - "github.com/ava-labs/avalanchego/graft/coreth/rpc" + "github.com/ava-labs/avalanchego/graft/evm/rpc" ethereum "github.com/ava-labs/libevm" "github.com/ava-labs/libevm/common" "github.com/ava-labs/libevm/core/types" diff --git a/graft/coreth/cmd/simulator/metrics/metrics.go b/graft/coreth/cmd/simulator/metrics/metrics.go index 01c7c16bed6e..0080ffbaef4e 100644 --- a/graft/coreth/cmd/simulator/metrics/metrics.go +++ b/graft/coreth/cmd/simulator/metrics/metrics.go @@ -15,7 +15,7 @@ import ( "github.com/prometheus/client_golang/prometheus" "github.com/prometheus/client_golang/prometheus/promhttp" - "github.com/ava-labs/avalanchego/graft/coreth/rpc" + "github.com/ava-labs/avalanchego/graft/evm/rpc" ) type Metrics struct { diff --git a/graft/coreth/eth/api_backend.go b/graft/coreth/eth/api_backend.go index b85cea10509d..737dc919b011 100644 --- a/graft/coreth/eth/api_backend.go +++ b/graft/coreth/eth/api_backend.go @@ -40,7 +40,7 @@ import ( "github.com/ava-labs/avalanchego/graft/coreth/eth/tracers" "github.com/ava-labs/avalanchego/graft/coreth/internal/ethapi" "github.com/ava-labs/avalanchego/graft/coreth/params" - "github.com/ava-labs/avalanchego/graft/coreth/rpc" + "github.com/ava-labs/avalanchego/graft/evm/rpc" "github.com/ava-labs/libevm/accounts" "github.com/ava-labs/libevm/common" "github.com/ava-labs/libevm/core/bloombits" diff --git a/graft/coreth/eth/api_debug.go b/graft/coreth/eth/api_debug.go index 3aa538882ffa..55d82b6286c4 100644 --- a/graft/coreth/eth/api_debug.go +++ b/graft/coreth/eth/api_debug.go @@ -34,7 +34,7 @@ import ( "time" "github.com/ava-labs/avalanchego/graft/coreth/internal/ethapi" - "github.com/ava-labs/avalanchego/graft/coreth/rpc" + "github.com/ava-labs/avalanchego/graft/evm/rpc" "github.com/ava-labs/avalanchego/vms/evm/sync/customrawdb" "github.com/ava-labs/libevm/common" "github.com/ava-labs/libevm/common/hexutil" diff --git a/graft/coreth/eth/backend.go b/graft/coreth/eth/backend.go index ac10213f5805..c68ab3366f7e 100644 --- a/graft/coreth/eth/backend.go +++ b/graft/coreth/eth/backend.go @@ -50,8 +50,8 @@ import ( "github.com/ava-labs/avalanchego/graft/coreth/miner" "github.com/ava-labs/avalanchego/graft/coreth/node" "github.com/ava-labs/avalanchego/graft/coreth/params" - "github.com/ava-labs/avalanchego/graft/coreth/rpc" "github.com/ava-labs/avalanchego/graft/evm/core/state/pruner" + "github.com/ava-labs/avalanchego/graft/evm/rpc" "github.com/ava-labs/avalanchego/utils/timer/mockable" "github.com/ava-labs/avalanchego/vms/evm/sync/customrawdb" "github.com/ava-labs/libevm/accounts" diff --git a/graft/coreth/eth/filters/api.go b/graft/coreth/eth/filters/api.go index 706bce14a784..3dcce6ff142f 100644 --- a/graft/coreth/eth/filters/api.go +++ b/graft/coreth/eth/filters/api.go @@ -37,7 +37,7 @@ import ( "time" "github.com/ava-labs/avalanchego/graft/coreth/internal/ethapi" - "github.com/ava-labs/avalanchego/graft/coreth/rpc" + "github.com/ava-labs/avalanchego/graft/evm/rpc" ethereum "github.com/ava-labs/libevm" "github.com/ava-labs/libevm/common" "github.com/ava-labs/libevm/common/hexutil" diff --git a/graft/coreth/eth/filters/api_test.go b/graft/coreth/eth/filters/api_test.go index cda514afd707..b54988f234c1 100644 --- a/graft/coreth/eth/filters/api_test.go +++ b/graft/coreth/eth/filters/api_test.go @@ -33,7 +33,7 @@ import ( "strings" "testing" - "github.com/ava-labs/avalanchego/graft/coreth/rpc" + "github.com/ava-labs/avalanchego/graft/evm/rpc" "github.com/ava-labs/libevm/common" ) diff --git a/graft/coreth/eth/filters/filter.go b/graft/coreth/eth/filters/filter.go index 077fda18c64e..142a5b38fc4a 100644 --- a/graft/coreth/eth/filters/filter.go +++ b/graft/coreth/eth/filters/filter.go @@ -33,7 +33,7 @@ import ( "fmt" "math/big" - "github.com/ava-labs/avalanchego/graft/coreth/rpc" + "github.com/ava-labs/avalanchego/graft/evm/rpc" "github.com/ava-labs/libevm/common" "github.com/ava-labs/libevm/core/bloombits" "github.com/ava-labs/libevm/core/types" diff --git a/graft/coreth/eth/filters/filter_system.go b/graft/coreth/eth/filters/filter_system.go index 2562112577c0..9f01eb7a84a7 100644 --- a/graft/coreth/eth/filters/filter_system.go +++ b/graft/coreth/eth/filters/filter_system.go @@ -37,7 +37,7 @@ import ( "github.com/ava-labs/avalanchego/graft/coreth/core" "github.com/ava-labs/avalanchego/graft/coreth/params" - "github.com/ava-labs/avalanchego/graft/coreth/rpc" + "github.com/ava-labs/avalanchego/graft/evm/rpc" ethereum "github.com/ava-labs/libevm" "github.com/ava-labs/libevm/common" "github.com/ava-labs/libevm/core/bloombits" diff --git a/graft/coreth/eth/filters/filter_system_test.go b/graft/coreth/eth/filters/filter_system_test.go index 093c710d2f77..08768105107d 100644 --- a/graft/coreth/eth/filters/filter_system_test.go +++ b/graft/coreth/eth/filters/filter_system_test.go @@ -46,7 +46,7 @@ import ( "github.com/ava-labs/avalanchego/graft/coreth/internal/ethapi" "github.com/ava-labs/avalanchego/graft/coreth/params" "github.com/ava-labs/avalanchego/graft/coreth/plugin/evm/customtypes" - "github.com/ava-labs/avalanchego/graft/coreth/rpc" + "github.com/ava-labs/avalanchego/graft/evm/rpc" "github.com/ava-labs/avalanchego/vms/evm/sync/customrawdb" ethereum "github.com/ava-labs/libevm" "github.com/ava-labs/libevm/common" diff --git a/graft/coreth/eth/filters/filter_test.go b/graft/coreth/eth/filters/filter_test.go index 4ec971a5d2d4..fe06605e2bdf 100644 --- a/graft/coreth/eth/filters/filter_test.go +++ b/graft/coreth/eth/filters/filter_test.go @@ -39,7 +39,7 @@ import ( "github.com/ava-labs/avalanchego/graft/coreth/consensus/dummy" "github.com/ava-labs/avalanchego/graft/coreth/core" "github.com/ava-labs/avalanchego/graft/coreth/params" - "github.com/ava-labs/avalanchego/graft/coreth/rpc" + "github.com/ava-labs/avalanchego/graft/evm/rpc" "github.com/ava-labs/avalanchego/vms/evm/sync/customrawdb" "github.com/ava-labs/libevm/common" "github.com/ava-labs/libevm/core/rawdb" diff --git a/graft/coreth/eth/gasprice/fee_info_provider.go b/graft/coreth/eth/gasprice/fee_info_provider.go index 8c6fc6664d48..7ef1e659b990 100644 --- a/graft/coreth/eth/gasprice/fee_info_provider.go +++ b/graft/coreth/eth/gasprice/fee_info_provider.go @@ -32,7 +32,7 @@ import ( "math/big" "github.com/ava-labs/avalanchego/graft/coreth/core" - "github.com/ava-labs/avalanchego/graft/coreth/rpc" + "github.com/ava-labs/avalanchego/graft/evm/rpc" "github.com/ava-labs/libevm/core/types" lru "github.com/hashicorp/golang-lru" ) diff --git a/graft/coreth/eth/gasprice/feehistory.go b/graft/coreth/eth/gasprice/feehistory.go index 49e340c90381..574c7217d72f 100644 --- a/graft/coreth/eth/gasprice/feehistory.go +++ b/graft/coreth/eth/gasprice/feehistory.go @@ -34,7 +34,7 @@ import ( "math/big" "slices" - "github.com/ava-labs/avalanchego/graft/coreth/rpc" + "github.com/ava-labs/avalanchego/graft/evm/rpc" "github.com/ava-labs/libevm/common" "github.com/ava-labs/libevm/core/types" "github.com/ava-labs/libevm/log" diff --git a/graft/coreth/eth/gasprice/feehistory_test.go b/graft/coreth/eth/gasprice/feehistory_test.go index c2fb3cdc6f58..47e4a34139d4 100644 --- a/graft/coreth/eth/gasprice/feehistory_test.go +++ b/graft/coreth/eth/gasprice/feehistory_test.go @@ -35,7 +35,7 @@ import ( "github.com/ava-labs/avalanchego/graft/coreth/core" "github.com/ava-labs/avalanchego/graft/coreth/params" - "github.com/ava-labs/avalanchego/graft/coreth/rpc" + "github.com/ava-labs/avalanchego/graft/evm/rpc" "github.com/ava-labs/libevm/common" "github.com/ava-labs/libevm/core/types" ethparams "github.com/ava-labs/libevm/params" diff --git a/graft/coreth/eth/gasprice/gasprice.go b/graft/coreth/eth/gasprice/gasprice.go index f8273a5c4ede..c78e739cf193 100644 --- a/graft/coreth/eth/gasprice/gasprice.go +++ b/graft/coreth/eth/gasprice/gasprice.go @@ -35,7 +35,7 @@ import ( "github.com/ava-labs/avalanchego/graft/coreth/core" "github.com/ava-labs/avalanchego/graft/coreth/params" "github.com/ava-labs/avalanchego/graft/coreth/plugin/evm/customheader" - "github.com/ava-labs/avalanchego/graft/coreth/rpc" + "github.com/ava-labs/avalanchego/graft/evm/rpc" "github.com/ava-labs/avalanchego/utils/timer/mockable" "github.com/ava-labs/avalanchego/vms/evm/acp176" "github.com/ava-labs/libevm/common" diff --git a/graft/coreth/eth/gasprice/gasprice_test.go b/graft/coreth/eth/gasprice/gasprice_test.go index 6af590c4c56a..867d5fdeb447 100644 --- a/graft/coreth/eth/gasprice/gasprice_test.go +++ b/graft/coreth/eth/gasprice/gasprice_test.go @@ -39,7 +39,7 @@ import ( "github.com/ava-labs/avalanchego/graft/coreth/params" "github.com/ava-labs/avalanchego/graft/coreth/plugin/evm/customtypes" "github.com/ava-labs/avalanchego/graft/coreth/plugin/evm/upgrade/ap4" - "github.com/ava-labs/avalanchego/graft/coreth/rpc" + "github.com/ava-labs/avalanchego/graft/evm/rpc" "github.com/ava-labs/libevm/common" "github.com/ava-labs/libevm/core/rawdb" "github.com/ava-labs/libevm/core/types" diff --git a/graft/coreth/eth/tracers/api.go b/graft/coreth/eth/tracers/api.go index dc08d15e35ec..b2404f4aa99d 100644 --- a/graft/coreth/eth/tracers/api.go +++ b/graft/coreth/eth/tracers/api.go @@ -43,7 +43,7 @@ import ( "github.com/ava-labs/avalanchego/graft/coreth/core" "github.com/ava-labs/avalanchego/graft/coreth/internal/ethapi" "github.com/ava-labs/avalanchego/graft/coreth/params" - "github.com/ava-labs/avalanchego/graft/coreth/rpc" + "github.com/ava-labs/avalanchego/graft/evm/rpc" "github.com/ava-labs/libevm/common" "github.com/ava-labs/libevm/common/hexutil" "github.com/ava-labs/libevm/core/state" diff --git a/graft/coreth/eth/tracers/api_test.go b/graft/coreth/eth/tracers/api_test.go index e3e3b65cd7af..1c2ec1ecc74d 100644 --- a/graft/coreth/eth/tracers/api_test.go +++ b/graft/coreth/eth/tracers/api_test.go @@ -44,7 +44,7 @@ import ( "github.com/ava-labs/avalanchego/graft/coreth/core" "github.com/ava-labs/avalanchego/graft/coreth/internal/ethapi" "github.com/ava-labs/avalanchego/graft/coreth/params" - "github.com/ava-labs/avalanchego/graft/coreth/rpc" + "github.com/ava-labs/avalanchego/graft/evm/rpc" "github.com/ava-labs/avalanchego/vms/evm/sync/customrawdb" "github.com/ava-labs/libevm/common" "github.com/ava-labs/libevm/common/hexutil" diff --git a/graft/coreth/ethclient/corethclient/corethclient.go b/graft/coreth/ethclient/corethclient/corethclient.go index adc9ccf5a0e2..fabe2960a161 100644 --- a/graft/coreth/ethclient/corethclient/corethclient.go +++ b/graft/coreth/ethclient/corethclient/corethclient.go @@ -35,7 +35,7 @@ import ( "runtime/debug" "github.com/ava-labs/avalanchego/graft/coreth/ethclient" - "github.com/ava-labs/avalanchego/graft/coreth/rpc" + "github.com/ava-labs/avalanchego/graft/evm/rpc" ethereum "github.com/ava-labs/libevm" "github.com/ava-labs/libevm/common" "github.com/ava-labs/libevm/common/hexutil" diff --git a/graft/coreth/ethclient/ethclient.go b/graft/coreth/ethclient/ethclient.go index 8664cffcf4f1..478a5bcb0000 100644 --- a/graft/coreth/ethclient/ethclient.go +++ b/graft/coreth/ethclient/ethclient.go @@ -36,7 +36,7 @@ import ( "math/big" "github.com/ava-labs/avalanchego/graft/coreth/plugin/evm/customtypes" - "github.com/ava-labs/avalanchego/graft/coreth/rpc" + "github.com/ava-labs/avalanchego/graft/evm/rpc" ethereum "github.com/ava-labs/libevm" "github.com/ava-labs/libevm/common" "github.com/ava-labs/libevm/common/hexutil" diff --git a/graft/coreth/ethclient/simulated/backend.go b/graft/coreth/ethclient/simulated/backend.go index 2c0466ebc186..23df9e35f1c7 100644 --- a/graft/coreth/ethclient/simulated/backend.go +++ b/graft/coreth/ethclient/simulated/backend.go @@ -40,8 +40,8 @@ import ( "github.com/ava-labs/avalanchego/graft/coreth/interfaces" "github.com/ava-labs/avalanchego/graft/coreth/node" "github.com/ava-labs/avalanchego/graft/coreth/params" - "github.com/ava-labs/avalanchego/graft/coreth/rpc" "github.com/ava-labs/avalanchego/graft/evm/constants" + "github.com/ava-labs/avalanchego/graft/evm/rpc" "github.com/ava-labs/avalanchego/utils/timer/mockable" ethereum "github.com/ava-labs/libevm" "github.com/ava-labs/libevm/common" diff --git a/graft/coreth/ethclient/simulated/backend_test.go b/graft/coreth/ethclient/simulated/backend_test.go index 54f5e07234a3..2eba41ed88e3 100644 --- a/graft/coreth/ethclient/simulated/backend_test.go +++ b/graft/coreth/ethclient/simulated/backend_test.go @@ -39,7 +39,7 @@ import ( "github.com/ava-labs/avalanchego/graft/coreth/accounts/abi/bind" "github.com/ava-labs/avalanchego/graft/coreth/params" "github.com/ava-labs/avalanchego/graft/coreth/plugin/evm/customtypes" - "github.com/ava-labs/avalanchego/graft/coreth/rpc" + "github.com/ava-labs/avalanchego/graft/evm/rpc" "github.com/ava-labs/libevm/common" "github.com/ava-labs/libevm/core/types" "github.com/ava-labs/libevm/crypto" diff --git a/graft/coreth/go.mod b/graft/coreth/go.mod index 0c277d37d477..2e381bca94a3 100644 --- a/graft/coreth/go.mod +++ b/graft/coreth/go.mod @@ -8,15 +8,13 @@ module github.com/ava-labs/avalanchego/graft/coreth go 1.24.12 require ( - github.com/ava-labs/avalanchego v1.14.1-0.20251120155522-df4a8e531761 + github.com/ava-labs/avalanchego v1.14.1-antithesis-docker-image-fix github.com/ava-labs/avalanchego/graft/evm v0.0.0-00010101000000-000000000000 github.com/ava-labs/firewood-go-ethhash/ffi v0.0.18 github.com/ava-labs/libevm v1.13.15-0.20251210210615-b8e76562a300 github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc - github.com/deckarep/golang-set/v2 v2.1.0 github.com/go-cmd/cmd v1.4.3 github.com/google/go-cmp v0.7.0 - github.com/gorilla/websocket v1.5.0 github.com/hashicorp/go-bexpr v0.1.10 github.com/hashicorp/golang-lru v0.5.5-0.20210104140557-80c98217689d github.com/holiman/billy v0.0.0-20240216141850-2abb0c79d3c4 @@ -37,7 +35,6 @@ require ( golang.org/x/crypto v0.45.0 golang.org/x/exp v0.0.0-20241215155358-4a5509556b9e golang.org/x/sync v0.18.0 - golang.org/x/time v0.12.0 google.golang.org/protobuf v1.36.8 gopkg.in/natefinch/lumberjack.v2 v2.0.0 ) @@ -66,6 +63,7 @@ require ( github.com/cpuguy83/go-md2man/v2 v2.0.6 // indirect github.com/crate-crypto/go-ipa v0.0.0-20231025140028-3c0104f4b233 // indirect github.com/crate-crypto/go-kzg-4844 v1.1.0 // indirect + github.com/deckarep/golang-set/v2 v2.1.0 // indirect github.com/decred/dcrd/dcrec/secp256k1/v4 v4.4.0 // indirect github.com/dlclark/regexp2 v1.7.0 // indirect github.com/dop251/goja v0.0.0-20230806174421-c933cf95e127 // indirect @@ -96,6 +94,7 @@ require ( github.com/google/uuid v1.6.0 // indirect github.com/gorilla/mux v1.8.0 // indirect github.com/gorilla/rpc v1.2.0 // indirect + github.com/gorilla/websocket v1.5.0 // indirect github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0 // indirect github.com/grpc-ecosystem/grpc-gateway/v2 v2.27.1 // indirect github.com/holiman/bloomfilter/v2 v2.0.3 // indirect @@ -157,6 +156,7 @@ require ( golang.org/x/sys v0.38.0 // indirect golang.org/x/term v0.37.0 // indirect golang.org/x/text v0.31.0 // indirect + golang.org/x/time v0.12.0 // indirect golang.org/x/tools v0.38.0 // indirect gonum.org/v1/gonum v0.16.0 // indirect google.golang.org/genproto/googleapis/api v0.0.0-20250707201910-8d1bb00bc6a7 // indirect @@ -183,6 +183,8 @@ tool ( github.com/onsi/ginkgo/v2/ginkgo ) -replace github.com/ava-labs/avalanchego => ../../ - -replace github.com/ava-labs/avalanchego/graft/evm => ../evm +replace ( + github.com/ava-labs/avalanchego => ../../ + github.com/ava-labs/avalanchego/graft/evm => ../evm + github.com/ava-labs/avalanchego/graft/subnet-evm => ../subnet-evm +) diff --git a/graft/coreth/go.sum b/graft/coreth/go.sum index da5cc7bf1904..d76f26618d79 100644 --- a/graft/coreth/go.sum +++ b/graft/coreth/go.sum @@ -5,8 +5,8 @@ connectrpc.com/grpcreflect v1.3.0 h1:Y4V+ACf8/vOb1XOc251Qun7jMB75gCUNw6llvB9csXc connectrpc.com/grpcreflect v1.3.0/go.mod h1:nfloOtCS8VUQOQ1+GTdFzVg2CJo4ZGaat8JIovCtDYs= github.com/AndreasBriese/bbloom v0.0.0-20190306092124-e2d15f34fcf9/go.mod h1:bOvUY6CB00SOBii9/FifXqc0awNKxLFCL/+pkDPuyl8= github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= -github.com/BurntSushi/toml v1.5.0 h1:W5quZX/G/csjUnuI8SUYlsHs9M38FC7znL0lIO+DvMg= -github.com/BurntSushi/toml v1.5.0/go.mod h1:ukJfTF/6rtPPRCnwkur4qwRxa8vTRFBF0uk2lLoLwho= +github.com/BurntSushi/toml v1.3.2 h1:o7IhLm0Msx3BaB+n3Ag7L8EVlByGnpq14C4YWiu/gL8= +github.com/BurntSushi/toml v1.3.2/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ= github.com/CloudyKit/fastprinter v0.0.0-20200109182630-33d98a066a53/go.mod h1:+3IMCy2vIlbG1XG/0ggNQv0SvxCAIpPM5b1nCz56Xno= github.com/CloudyKit/jet/v3 v3.0.0/go.mod h1:HKQPgSJmdK8hdoAbKUUWajkHyHo4RaU5rMdUywE7VMo= github.com/DataDog/zstd v1.5.2 h1:vUG4lAyuPCXO0TLbXvPv7EB7cNK1QV/luu55UHLrrn8= diff --git a/graft/coreth/internal/ethapi/api.go b/graft/coreth/internal/ethapi/api.go index 36f3e2fb5f5c..88e7a6e03a4c 100644 --- a/graft/coreth/internal/ethapi/api.go +++ b/graft/coreth/internal/ethapi/api.go @@ -41,8 +41,8 @@ import ( "github.com/ava-labs/avalanchego/graft/coreth/eth/gasestimator" "github.com/ava-labs/avalanchego/graft/coreth/params" "github.com/ava-labs/avalanchego/graft/coreth/plugin/evm/customtypes" - "github.com/ava-labs/avalanchego/graft/coreth/rpc" "github.com/ava-labs/avalanchego/graft/evm/firewood" + "github.com/ava-labs/avalanchego/graft/evm/rpc" "github.com/ava-labs/libevm/accounts" "github.com/ava-labs/libevm/accounts/keystore" "github.com/ava-labs/libevm/accounts/scwallet" diff --git a/graft/coreth/internal/ethapi/api_extra.go b/graft/coreth/internal/ethapi/api_extra.go index 206516fa1e62..9c91565ad994 100644 --- a/graft/coreth/internal/ethapi/api_extra.go +++ b/graft/coreth/internal/ethapi/api_extra.go @@ -14,7 +14,7 @@ import ( "github.com/ava-labs/avalanchego/graft/coreth/core" "github.com/ava-labs/avalanchego/graft/coreth/params" - "github.com/ava-labs/avalanchego/graft/coreth/rpc" + "github.com/ava-labs/avalanchego/graft/evm/rpc" ) type DetailedExecutionResult struct { diff --git a/graft/coreth/internal/ethapi/api_extra_test.go b/graft/coreth/internal/ethapi/api_extra_test.go index 8ca664808abb..aa3e4c5f3666 100644 --- a/graft/coreth/internal/ethapi/api_extra_test.go +++ b/graft/coreth/internal/ethapi/api_extra_test.go @@ -19,7 +19,7 @@ import ( "github.com/ava-labs/avalanchego/graft/coreth/core" "github.com/ava-labs/avalanchego/graft/coreth/params" "github.com/ava-labs/avalanchego/graft/coreth/plugin/evm/customtypes" - "github.com/ava-labs/avalanchego/graft/coreth/rpc" + "github.com/ava-labs/avalanchego/graft/evm/rpc" ethparams "github.com/ava-labs/libevm/params" ) diff --git a/graft/coreth/internal/ethapi/api_test.go b/graft/coreth/internal/ethapi/api_test.go index 1902ff286380..614cc0cc177e 100644 --- a/graft/coreth/internal/ethapi/api_test.go +++ b/graft/coreth/internal/ethapi/api_test.go @@ -48,7 +48,7 @@ import ( "github.com/ava-labs/avalanchego/graft/coreth/internal/blocktest" "github.com/ava-labs/avalanchego/graft/coreth/params" "github.com/ava-labs/avalanchego/graft/coreth/plugin/evm/upgrade/ap3" - "github.com/ava-labs/avalanchego/graft/coreth/rpc" + "github.com/ava-labs/avalanchego/graft/evm/rpc" "github.com/ava-labs/avalanchego/utils" "github.com/ava-labs/libevm/accounts" "github.com/ava-labs/libevm/accounts/keystore" diff --git a/graft/coreth/internal/ethapi/backend.go b/graft/coreth/internal/ethapi/backend.go index d9d0ca09d5eb..e905be5f6153 100644 --- a/graft/coreth/internal/ethapi/backend.go +++ b/graft/coreth/internal/ethapi/backend.go @@ -36,7 +36,7 @@ import ( "github.com/ava-labs/avalanchego/graft/coreth/consensus" "github.com/ava-labs/avalanchego/graft/coreth/core" "github.com/ava-labs/avalanchego/graft/coreth/params" - "github.com/ava-labs/avalanchego/graft/coreth/rpc" + "github.com/ava-labs/avalanchego/graft/evm/rpc" "github.com/ava-labs/libevm/accounts" "github.com/ava-labs/libevm/common" "github.com/ava-labs/libevm/core/bloombits" diff --git a/graft/coreth/internal/ethapi/mocks_test.go b/graft/coreth/internal/ethapi/mocks_test.go index e0f819398dff..da54f093e959 100644 --- a/graft/coreth/internal/ethapi/mocks_test.go +++ b/graft/coreth/internal/ethapi/mocks_test.go @@ -18,7 +18,7 @@ import ( consensus "github.com/ava-labs/avalanchego/graft/coreth/consensus" core "github.com/ava-labs/avalanchego/graft/coreth/core" params "github.com/ava-labs/avalanchego/graft/coreth/params" - rpc "github.com/ava-labs/avalanchego/graft/coreth/rpc" + rpc "github.com/ava-labs/avalanchego/graft/evm/rpc" accounts "github.com/ava-labs/libevm/accounts" common "github.com/ava-labs/libevm/common" bloombits "github.com/ava-labs/libevm/core/bloombits" diff --git a/graft/coreth/internal/ethapi/transaction_args.go b/graft/coreth/internal/ethapi/transaction_args.go index 3e8269e44c76..106630482cc8 100644 --- a/graft/coreth/internal/ethapi/transaction_args.go +++ b/graft/coreth/internal/ethapi/transaction_args.go @@ -37,7 +37,7 @@ import ( "github.com/ava-labs/avalanchego/graft/coreth/core" "github.com/ava-labs/avalanchego/graft/coreth/params" - "github.com/ava-labs/avalanchego/graft/coreth/rpc" + "github.com/ava-labs/avalanchego/graft/evm/rpc" "github.com/ava-labs/libevm/common" "github.com/ava-labs/libevm/common/hexutil" "github.com/ava-labs/libevm/common/math" diff --git a/graft/coreth/node/api.go b/graft/coreth/node/api.go index f6ac9e7f473a..7e1d0511a366 100644 --- a/graft/coreth/node/api.go +++ b/graft/coreth/node/api.go @@ -29,7 +29,7 @@ package node import ( "github.com/ava-labs/avalanchego/graft/coreth/internal/debug" - "github.com/ava-labs/avalanchego/graft/coreth/rpc" + "github.com/ava-labs/avalanchego/graft/evm/rpc" "github.com/ava-labs/libevm/common/hexutil" "github.com/ava-labs/libevm/crypto" ) diff --git a/graft/coreth/node/node.go b/graft/coreth/node/node.go index d66fcb10ec07..d5f8f65f022b 100644 --- a/graft/coreth/node/node.go +++ b/graft/coreth/node/node.go @@ -28,7 +28,7 @@ package node import ( - "github.com/ava-labs/avalanchego/graft/coreth/rpc" + "github.com/ava-labs/avalanchego/graft/evm/rpc" "github.com/ava-labs/libevm/accounts" ) diff --git a/graft/coreth/plugin/evm/extras/extras.go b/graft/coreth/plugin/evm/extras/extras.go new file mode 100644 index 000000000000..716ce9ef88f8 --- /dev/null +++ b/graft/coreth/plugin/evm/extras/extras.go @@ -0,0 +1,50 @@ +// Copyright (C) 2019, Ava Labs, Inc. All rights reserved. +// See the file LICENSE for licensing terms. + +// Package extras provides libevm type registration for C-Chain behaviour. +// This package is intentionally kept separate from the main evm package to +// avoid import cycles - the evm package imports rpc, but many packages need +// to register extras without importing the full evm package. +package extras + +import ( + "github.com/ava-labs/libevm/libevm" + + "github.com/ava-labs/avalanchego/graft/coreth/core" + "github.com/ava-labs/avalanchego/graft/coreth/core/extstate" + "github.com/ava-labs/avalanchego/graft/coreth/params" + "github.com/ava-labs/avalanchego/graft/coreth/plugin/evm/customtypes" +) + +// RegisterAllLibEVMExtras is a convenience wrapper for calling +// [core.RegisterExtras], [customtypes.Register], [extstate.RegisterExtras], and +// [params.RegisterExtras]. Together these are necessary and sufficient for +// configuring libevm for C-Chain behaviour. +// +// It MUST NOT be called more than once and therefore is only allowed to be used +// in tests and `package main`, to avoid polluting other packages that +// transitively depend on this one but don't need registration. +func RegisterAllLibEVMExtras() { + core.RegisterExtras() + customtypes.Register() + extstate.RegisterExtras() + params.RegisterExtras() +} + +// WithTempRegisteredLibEVMExtras runs `fn` with temporary registration +// otherwise equivalent to a call to [RegisterAllLibEVMExtras], but limited to +// the life of `fn`. +func WithTempRegisteredLibEVMExtras(fn func() error) error { + return libevm.WithTemporaryExtrasLock(func(lock libevm.ExtrasLock) error { + for _, wrap := range []func(libevm.ExtrasLock, func() error) error{ + core.WithTempRegisteredExtras, + customtypes.WithTempRegisteredExtras, + extstate.WithTempRegisteredExtras, + params.WithTempRegisteredExtras, + } { + inner := fn + fn = func() error { return wrap(lock, inner) } + } + return fn() + }) +} diff --git a/graft/coreth/plugin/evm/libevm.go b/graft/coreth/plugin/evm/libevm.go index e98d12423d3f..bdb45d832f77 100644 --- a/graft/coreth/plugin/evm/libevm.go +++ b/graft/coreth/plugin/evm/libevm.go @@ -3,14 +3,7 @@ package evm -import ( - "github.com/ava-labs/libevm/libevm" - - "github.com/ava-labs/avalanchego/graft/coreth/core" - "github.com/ava-labs/avalanchego/graft/coreth/core/extstate" - "github.com/ava-labs/avalanchego/graft/coreth/params" - "github.com/ava-labs/avalanchego/graft/coreth/plugin/evm/customtypes" -) +import "github.com/ava-labs/avalanchego/graft/coreth/plugin/evm/extras" // RegisterAllLibEVMExtras is a convenience wrapper for calling // [core.RegisterExtras], [customtypes.Register], [extstate.RegisterExtras], and @@ -21,26 +14,12 @@ import ( // in tests and `package main`, to avoid polluting other packages that // transitively depend on this one but don't need registration. func RegisterAllLibEVMExtras() { - core.RegisterExtras() - customtypes.Register() - extstate.RegisterExtras() - params.RegisterExtras() + extras.RegisterAllLibEVMExtras() } // WithTempRegisteredLibEVMExtras runs `fn` with temporary registration // otherwise equivalent to a call to [RegisterAllLibEVMExtras], but limited to // the life of `fn`. func WithTempRegisteredLibEVMExtras(fn func() error) error { - return libevm.WithTemporaryExtrasLock(func(lock libevm.ExtrasLock) error { - for _, wrap := range []func(libevm.ExtrasLock, func() error) error{ - core.WithTempRegisteredExtras, - customtypes.WithTempRegisteredExtras, - extstate.WithTempRegisteredExtras, - params.WithTempRegisteredExtras, - } { - inner := fn - fn = func() error { return wrap(lock, inner) } - } - return fn() - }) + return extras.WithTempRegisteredLibEVMExtras(fn) } diff --git a/graft/coreth/plugin/evm/vm.go b/graft/coreth/plugin/evm/vm.go index 9e15bbe78685..6985dbf07192 100644 --- a/graft/coreth/plugin/evm/vm.go +++ b/graft/coreth/plugin/evm/vm.go @@ -58,11 +58,11 @@ import ( "github.com/ava-labs/avalanchego/graft/coreth/plugin/evm/vmerrors" "github.com/ava-labs/avalanchego/graft/coreth/plugin/evm/vmsync" "github.com/ava-labs/avalanchego/graft/coreth/precompile/precompileconfig" - "github.com/ava-labs/avalanchego/graft/coreth/rpc" "github.com/ava-labs/avalanchego/graft/coreth/sync/client/stats" "github.com/ava-labs/avalanchego/graft/coreth/sync/handlers" "github.com/ava-labs/avalanchego/graft/coreth/warp" "github.com/ava-labs/avalanchego/graft/evm/constants" + "github.com/ava-labs/avalanchego/graft/evm/rpc" "github.com/ava-labs/avalanchego/graft/evm/triedb/hashdb" "github.com/ava-labs/avalanchego/ids" "github.com/ava-labs/avalanchego/network/p2p" diff --git a/graft/coreth/plugin/evm/vm_test.go b/graft/coreth/plugin/evm/vm_test.go index 1fd73f8067c7..ea8907e04ecd 100644 --- a/graft/coreth/plugin/evm/vm_test.go +++ b/graft/coreth/plugin/evm/vm_test.go @@ -43,8 +43,8 @@ import ( "github.com/ava-labs/avalanchego/graft/coreth/plugin/evm/upgrade/ap0" "github.com/ava-labs/avalanchego/graft/coreth/plugin/evm/upgrade/ap1" "github.com/ava-labs/avalanchego/graft/coreth/plugin/evm/vmtest" - "github.com/ava-labs/avalanchego/graft/coreth/rpc" "github.com/ava-labs/avalanchego/graft/evm/constants" + "github.com/ava-labs/avalanchego/graft/evm/rpc" "github.com/ava-labs/avalanchego/ids" "github.com/ava-labs/avalanchego/snow/snowtest" "github.com/ava-labs/avalanchego/upgrade" diff --git a/graft/coreth/rpc/main_test.go b/graft/coreth/rpc/main_test.go deleted file mode 100644 index 48b4ab797271..000000000000 --- a/graft/coreth/rpc/main_test.go +++ /dev/null @@ -1,51 +0,0 @@ -// Copyright (C) 2019, Ava Labs, Inc. All rights reserved. -// See the file LICENSE for licensing terms. -// -// This file is a derived work, based on the go-ethereum library whose original -// notices appear below. -// -// It is distributed under a license compatible with the licensing terms of the -// original code from which it is derived. -// -// Much love to the original authors for their work. -// ********** -// Copyright 2016 The go-ethereum Authors -// This file is part of the go-ethereum library. -// -// The go-ethereum library is free software: you can redistribute it and/or modify -// it under the terms of the GNU Lesser General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// The go-ethereum library is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU Lesser General Public License for more details. -// -// You should have received a copy of the GNU Lesser General Public License -// along with the go-ethereum library. If not, see . - -package rpc - -import ( - "os" - "testing" - - "github.com/ava-labs/avalanchego/graft/coreth/plugin/evm/customtypes" -) - -func TestMain(m *testing.M) { - customtypes.Register() - - // Since there are so many flaky tests in the RPC package, we run the tests - // multiple times to try to get a passing run. - var code int - for range 5 { - code = m.Run() - if code == 0 { - break - } - } - - os.Exit(code) -} diff --git a/graft/coreth/scripts/upstream_files.txt b/graft/coreth/scripts/upstream_files.txt index a373ade8f8b3..98eddbab7eb8 100644 --- a/graft/coreth/scripts/upstream_files.txt +++ b/graft/coreth/scripts/upstream_files.txt @@ -17,7 +17,6 @@ plugin/evm/customtypes/block_test.go plugin/evm/customtypes/hashing_test.go plugin/evm/customtypes/rlp_fuzzer_test.go plugin/evm/customtypes/types_test.go -rpc/* signer/* tests/* diff --git a/graft/coreth/warp/client.go b/graft/coreth/warp/client.go index 5164cf3de2e3..6aec6245a872 100644 --- a/graft/coreth/warp/client.go +++ b/graft/coreth/warp/client.go @@ -9,7 +9,7 @@ import ( "github.com/ava-labs/libevm/common/hexutil" - "github.com/ava-labs/avalanchego/graft/coreth/rpc" + "github.com/ava-labs/avalanchego/graft/evm/rpc" "github.com/ava-labs/avalanchego/ids" ) diff --git a/graft/evm/go.mod b/graft/evm/go.mod index f74efdc7f327..bb5aa3e0d13b 100644 --- a/graft/evm/go.mod +++ b/graft/evm/go.mod @@ -4,20 +4,28 @@ go 1.24.12 require ( github.com/VictoriaMetrics/fastcache v1.12.1 - github.com/ava-labs/avalanchego v1.14.1-0.20251120155522-df4a8e531761 + github.com/ava-labs/avalanchego v1.14.1-antithesis-docker-image-fix github.com/ava-labs/firewood-go-ethhash/ffi v0.0.18 github.com/ava-labs/libevm v1.13.15-0.20251210210615-b8e76562a300 + github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc + github.com/deckarep/golang-set/v2 v2.1.0 github.com/gorilla/rpc v1.2.0 + github.com/gorilla/websocket v1.5.0 github.com/holiman/bloomfilter/v2 v2.0.3 github.com/holiman/uint256 v1.2.4 github.com/stretchr/testify v1.11.1 golang.org/x/crypto v0.45.0 golang.org/x/exp v0.0.0-20241215155358-4a5509556b9e + golang.org/x/time v0.12.0 ) require ( - github.com/BurntSushi/toml v1.5.0 // indirect + github.com/BurntSushi/toml v1.3.2 // indirect github.com/DataDog/zstd v1.5.2 // indirect + github.com/Microsoft/go-winio v0.6.1 // indirect + github.com/StephenButtolph/canoto v0.17.3 // indirect + github.com/ava-labs/avalanchego/graft/coreth v0.0.0-20251203215505-70148edc6eca // indirect + github.com/ava-labs/avalanchego/graft/subnet-evm v0.8.1-0.20251201175023-067762d6ce7d // indirect github.com/beorn7/perks v1.0.1 // indirect github.com/bits-and-blooms/bitset v1.20.0 // indirect github.com/btcsuite/btcd/btcec/v2 v2.3.5 // indirect @@ -32,7 +40,6 @@ require ( github.com/consensys/gnark-crypto v0.18.1 // indirect github.com/crate-crypto/go-ipa v0.0.0-20231025140028-3c0104f4b233 // indirect github.com/crate-crypto/go-kzg-4844 v1.1.0 // indirect - github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect github.com/decred/dcrd/dcrec/secp256k1/v4 v4.4.0 // indirect github.com/ethereum/c-kzg-4844 v1.0.0 // indirect github.com/gballet/go-verkle v0.1.1-0.20231031103413-a67434b50f46 // indirect @@ -76,13 +83,16 @@ require ( go.opentelemetry.io/otel/sdk v1.37.0 // indirect go.opentelemetry.io/otel/trace v1.37.0 // indirect go.opentelemetry.io/proto/otlp v1.7.0 // indirect + go.uber.org/mock v0.5.0 // indirect go.uber.org/multierr v1.11.0 // indirect go.uber.org/zap v1.27.0 // indirect + golang.org/x/mod v0.29.0 // indirect golang.org/x/net v0.47.0 // indirect golang.org/x/sync v0.18.0 // indirect golang.org/x/sys v0.38.0 // indirect golang.org/x/term v0.37.0 // indirect golang.org/x/text v0.31.0 // indirect + golang.org/x/tools v0.38.0 // indirect gonum.org/v1/gonum v0.16.0 // indirect google.golang.org/genproto/googleapis/api v0.0.0-20250707201910-8d1bb00bc6a7 // indirect google.golang.org/genproto/googleapis/rpc v0.0.0-20250818200422-3122310a409c // indirect @@ -92,4 +102,8 @@ require ( gopkg.in/yaml.v3 v3.0.1 // indirect ) -replace github.com/ava-labs/avalanchego => ../../ +replace ( + github.com/ava-labs/avalanchego => ../../ + github.com/ava-labs/avalanchego/graft/coreth => ../coreth + github.com/ava-labs/avalanchego/graft/subnet-evm => ../subnet-evm +) diff --git a/graft/evm/go.sum b/graft/evm/go.sum index c16622cb5916..8aac5f0dd6ab 100644 --- a/graft/evm/go.sum +++ b/graft/evm/go.sum @@ -1,8 +1,8 @@ cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= github.com/AndreasBriese/bbloom v0.0.0-20190306092124-e2d15f34fcf9/go.mod h1:bOvUY6CB00SOBii9/FifXqc0awNKxLFCL/+pkDPuyl8= github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= -github.com/BurntSushi/toml v1.5.0 h1:W5quZX/G/csjUnuI8SUYlsHs9M38FC7znL0lIO+DvMg= -github.com/BurntSushi/toml v1.5.0/go.mod h1:ukJfTF/6rtPPRCnwkur4qwRxa8vTRFBF0uk2lLoLwho= +github.com/BurntSushi/toml v1.3.2 h1:o7IhLm0Msx3BaB+n3Ag7L8EVlByGnpq14C4YWiu/gL8= +github.com/BurntSushi/toml v1.3.2/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ= github.com/CloudyKit/fastprinter v0.0.0-20200109182630-33d98a066a53/go.mod h1:+3IMCy2vIlbG1XG/0ggNQv0SvxCAIpPM5b1nCz56Xno= github.com/CloudyKit/jet/v3 v3.0.0/go.mod h1:HKQPgSJmdK8hdoAbKUUWajkHyHo4RaU5rMdUywE7VMo= github.com/DataDog/zstd v1.5.2 h1:vUG4lAyuPCXO0TLbXvPv7EB7cNK1QV/luu55UHLrrn8= @@ -11,6 +11,8 @@ github.com/Joker/hpp v1.0.0/go.mod h1:8x5n+M1Hp5hC0g8okX3sR3vFQwynaX/UgSOM9MeBKz github.com/Microsoft/go-winio v0.6.1 h1:9/kr64B9VUZrLm5YYwbGtUJnMgqWVOdUAXu6Migciow= github.com/Microsoft/go-winio v0.6.1/go.mod h1:LRdKpFKfdobln8UmuiYcKPot9D2v6svN5+sAH+4kjUM= github.com/Shopify/goreferrer v0.0.0-20181106222321-ec9c9a553398/go.mod h1:a1uqRtAwp2Xwc6WNPJEufxJ7fx3npB4UV/JOLmbu5I0= +github.com/StephenButtolph/canoto v0.17.3 h1:lvsnYD4b96vD1knnmp1xCmZqfYpY/jSeRozGdOfdvGI= +github.com/StephenButtolph/canoto v0.17.3/go.mod h1:IcnAHC6nJUfQFVR9y60ko2ecUqqHHSB6UwI9NnBFZnE= github.com/VictoriaMetrics/fastcache v1.12.1 h1:i0mICQuojGDL3KblA7wUNlY5lOK6a4bwt3uRKnkZU40= github.com/VictoriaMetrics/fastcache v1.12.1/go.mod h1:tX04vaqcNoQeGLD+ra5pU5sWkuxnzWhEzLwhP9w653o= github.com/ajg/form v1.5.1/go.mod h1:uL1WgH+h2mgNtvBq0339dVnzXdBETtL2LeUXaIv25UY= @@ -28,8 +30,8 @@ github.com/bits-and-blooms/bitset v1.20.0 h1:2F+rfL86jE2d/bmw7OhqUg2Sj/1rURkBn3M github.com/bits-and-blooms/bitset v1.20.0/go.mod h1:7hO7Gc7Pp1vODcmWvKMRA9BNmbv6a/7QIWpPxHddWR8= github.com/btcsuite/btcd/btcec/v2 v2.3.5 h1:dpAlnAwmT1yIBm3exhT1/8iUSD98RDJM5vqJVQDQLiU= github.com/btcsuite/btcd/btcec/v2 v2.3.5/go.mod h1:m22FrOAiuxl/tht9wIqAoGHcbnCCaPWyauO8y2LGGtQ= -github.com/btcsuite/btcd/chaincfg/chainhash v1.0.1 h1:q0rUy8C/TYNBQS1+CGKw68tLOFYSNEs0TFnxxnS9+4U= -github.com/btcsuite/btcd/chaincfg/chainhash v1.0.1/go.mod h1:7SFka0XMvUgj3hfZtydOrQY2mwhPclbT2snogU7SQQc= +github.com/btcsuite/btcd/chaincfg/chainhash v1.1.0 h1:59Kx4K6lzOW5w6nFlA0v5+lk/6sjybR934QNHSJZPTQ= +github.com/btcsuite/btcd/chaincfg/chainhash v1.1.0/go.mod h1:7SFka0XMvUgj3hfZtydOrQY2mwhPclbT2snogU7SQQc= github.com/cenkalti/backoff/v4 v4.3.0 h1:MyRJ/UdXutAwSAT+s3wNd7MfTIcy71VQueUuFK343L8= github.com/cenkalti/backoff/v4 v4.3.0/go.mod h1:Y3VNntkOUPxTVeUxJ/G5vcM//AlwfmyYozVcomhLiZE= github.com/cenkalti/backoff/v5 v5.0.2 h1:rIfFVxEf1QsI7E1ZHfp/B4DF/6QBAUhmgkxc0H7Zss8= @@ -63,7 +65,10 @@ github.com/consensys/gnark-crypto v0.18.1/go.mod h1:L3mXGFTe1ZN+RSJ+CLjUt9x7PNdx github.com/coreos/etcd v3.3.10+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE= github.com/coreos/go-etcd v2.0.0+incompatible/go.mod h1:Jez6KQU2B/sWsbdaef3ED8NzMklzPG4d5KIOhIy30Tk= github.com/coreos/go-semver v0.2.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk= +github.com/cpuguy83/go-md2man v1.0.10 h1:BSKMNlYxDvnunlTymqtgONjNnaRV1sTpcovwwjF22jk= github.com/cpuguy83/go-md2man v1.0.10/go.mod h1:SmD6nW6nTyfqj6ABTjUi3V3JVMnlJmwcJI5acqYI6dE= +github.com/cpuguy83/go-md2man/v2 v2.0.6 h1:XJtiaUW6dEEqVuZiMTn1ldk455QWwEIsMIJlo5vtkx0= +github.com/cpuguy83/go-md2man/v2 v2.0.6/go.mod h1:oOW0eioCTA6cOiMLiUPZOpcVxMig6NIQQ7OS05n1F4g= github.com/crate-crypto/go-ipa v0.0.0-20231025140028-3c0104f4b233 h1:d28BXYi+wUpz1KBmiF9bWrjEMacUEREV6MBi2ODnrfQ= github.com/crate-crypto/go-ipa v0.0.0-20231025140028-3c0104f4b233/go.mod h1:geZJZH3SzKCqnz5VT0q/DyIG/tvu/dZk+VIfXicupJs= github.com/crate-crypto/go-kzg-4844 v1.1.0 h1:EN/u9k2TF6OWSHrCCDBBU6GLNMq88OspHHlMnHfoyU4= @@ -98,6 +103,8 @@ github.com/fsnotify/fsnotify v1.5.4/go.mod h1:OVB6XrOHzAwXMpEM7uPOzcehqUV2UqJxmV github.com/fsnotify/fsnotify v1.9.0 h1:2Ml+OJNzbYCTzsxtv8vKSFD9PbJjmhYF14k/jKC7S9k= github.com/fsnotify/fsnotify v1.9.0/go.mod h1:8jBTzvmWwFyi3Pb8djgCCO5IBqzKJ/Jwo8TRcHyHii0= github.com/gavv/httpexpect v2.0.0+incompatible/go.mod h1:x+9tiU1YnrOvnB725RkpoLv1M62hOWzwo5OXotisrKc= +github.com/gballet/go-libpcsclite v0.0.0-20191108122812-4678299bea08 h1:f6D9Hr8xV8uYKlyuj8XIruxlh9WjVjdh1gIicAS7ays= +github.com/gballet/go-libpcsclite v0.0.0-20191108122812-4678299bea08/go.mod h1:x7DCsMOv1taUwEWCzT4cmDeAkigA5/QCwUodaVOe8Ww= github.com/gballet/go-verkle v0.1.1-0.20231031103413-a67434b50f46 h1:BAIP2GihuqhwdILrV+7GJel5lyPV3u1+PgzrWLc0TkE= github.com/gballet/go-verkle v0.1.1-0.20231031103413-a67434b50f46/go.mod h1:QNpY22eby74jVhqH4WhDLDwxc/vqsern6pW+u2kbkpc= github.com/getsentry/sentry-go v0.12.0/go.mod h1:NSap0JBYWzHND8oMbyi0+XZhUalc1TBdRL1M71JZW2c= @@ -175,13 +182,21 @@ github.com/gorilla/websocket v1.5.0 h1:PPwGk2jz7EePpoHN/+ClbZu8SPxiqlu12wZP/3sWm github.com/gorilla/websocket v1.5.0/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= github.com/grpc-ecosystem/grpc-gateway/v2 v2.27.1 h1:X5VWvz21y3gzm9Nw/kaUeku/1+uBhcekkmy4IkffJww= github.com/grpc-ecosystem/grpc-gateway/v2 v2.27.1/go.mod h1:Zanoh4+gvIgluNqcfMVTJueD4wSS5hT7zTt4Mrutd90= +github.com/hashicorp/go-bexpr v0.1.10 h1:9kuI5PFotCboP3dkDYFr/wi0gg0QVbSNz5oFRpxn4uE= +github.com/hashicorp/go-bexpr v0.1.10/go.mod h1:oxlubA2vC/gFVfX1A6JGp7ls7uCDlfJn732ehYYg+g0= github.com/hashicorp/go-version v1.2.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA= +github.com/hashicorp/golang-lru v0.5.5-0.20210104140557-80c98217689d h1:dg1dEPuWpEqDnvIw251EVy4zlP8gWbsGj4BsUKCRpYs= +github.com/hashicorp/golang-lru v0.5.5-0.20210104140557-80c98217689d/go.mod h1:iADmTwqILo4mZ8BN3D2Q6+9jd8WM5uGBxy+E8yxSoD4= github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ= +github.com/holiman/billy v0.0.0-20240216141850-2abb0c79d3c4 h1:X4egAf/gcS1zATw6wn4Ej8vjuVGxeHdan+bRb2ebyv4= +github.com/holiman/billy v0.0.0-20240216141850-2abb0c79d3c4/go.mod h1:5GuXa7vkL8u9FkFuWdVvfR5ix8hRB7DbOAaYULamFpc= github.com/holiman/bloomfilter/v2 v2.0.3 h1:73e0e/V0tCydx14a0SCYS/EWCxgwLZ18CZcZKVu0fao= github.com/holiman/bloomfilter/v2 v2.0.3/go.mod h1:zpoh+gs7qcpqrHr3dB55AMiJwo0iURXE7ZOP9L9hSkA= github.com/holiman/uint256 v1.2.4 h1:jUc4Nk8fm9jZabQuqr2JzednajVmBpC+oiTiXZJEApU= github.com/holiman/uint256 v1.2.4/go.mod h1:EOMSn4q6Nyt9P6efbI3bueV4e1b3dGlUCXeiRV4ng7E= github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= +github.com/huin/goupnp v1.3.0 h1:UvLUlWDNpoUdYzb2TCn+MuTWtcjXKSza2n6CBdQ0xXc= +github.com/huin/goupnp v1.3.0/go.mod h1:gnGPsThkYa7bFi/KWmEysQRf48l2dvR5bxr2OFckNX8= github.com/hydrogen18/memlistener v0.0.0-20200120041712-dcc25e7acd91/go.mod h1:qEIFzExnS6016fRpRfxrExeVn2gbClQA99gQhnIcdhE= github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= github.com/imkira/go-interpol v1.1.0/go.mod h1:z0h2/2T3XF8kyEPpRgJ3kmNv+C43p+I/CoI+jC3w2iA= @@ -191,6 +206,8 @@ github.com/iris-contrib/go.uuid v2.0.0+incompatible/go.mod h1:iz2lgM/1UnEf1kP0L/ github.com/iris-contrib/jade v1.1.3/go.mod h1:H/geBymxJhShH5kecoiOCSssPX7QWYH7UaeZTSWddIk= github.com/iris-contrib/pongo2 v0.0.1/go.mod h1:Ssh+00+3GAZqSQb30AvBRNxBx7rf0GqwkjqxNd0u65g= github.com/iris-contrib/schema v0.0.1/go.mod h1:urYA3uvUNG1TIIjOSCzHr9/LmbQo8LrOcOqfqxa4hXw= +github.com/jackpal/go-nat-pmp v1.0.2 h1:KzKSgb7qkJvOUTqYl9/Hg/me3pWgBmERKrTGD7BdWus= +github.com/jackpal/go-nat-pmp v1.0.2/go.mod h1:QPH045xvCAeXUZOxsnwmrtiCoxIr9eob+4orBN1SBKc= github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU= github.com/json-iterator/go v1.1.9/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU= @@ -225,11 +242,15 @@ github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czP github.com/mattn/go-colorable v0.1.2/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE= github.com/mattn/go-colorable v0.1.8/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc= github.com/mattn/go-colorable v0.1.11/go.mod h1:u5H1YNBxpqRaxsYJYSkiCWKzEfiAb1Gb520KVy5xxl4= +github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA= +github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg= github.com/mattn/go-isatty v0.0.7/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s= github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s= github.com/mattn/go-isatty v0.0.9/go.mod h1:YNRxwqDuOph6SZLI9vUUz6OYw3QyUt7WiY2yME+cCiQ= github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU= github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94= +github.com/mattn/go-isatty v0.0.17 h1:BTarxUcIeDqL27Mc+vyvdWYSL28zpIhv3RoTdsLMPng= +github.com/mattn/go-isatty v0.0.17/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= github.com/mattn/go-runewidth v0.0.9/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI= github.com/mattn/go-runewidth v0.0.13 h1:lTGmDsbAYt5DmK6OnoV7EuIF1wEIFAcxld6ypU4OSgU= github.com/mattn/go-runewidth v0.0.13/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w= @@ -238,6 +259,10 @@ github.com/mediocregopher/radix/v3 v3.4.2/go.mod h1:8FL3F6UQRXHXIBSPUs5h0RybMF8i github.com/microcosm-cc/bluemonday v1.0.2/go.mod h1:iVP4YcDBq+n/5fb23BhYFvIMq/leAFZyRl6bYmGDlGc= github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= +github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY= +github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= +github.com/mitchellh/pointerstructure v1.2.0 h1:O+i9nHnXS3l/9Wu7r4NrEdwA2VFTicjUEN1uBnDo34A= +github.com/mitchellh/pointerstructure v1.2.0/go.mod h1:BRAsLI5zgXmw97Lf6s25bs8ohIXc3tViBH44KcwB2g4= 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= @@ -294,7 +319,10 @@ github.com/rogpeppe/go-internal v1.8.1/go.mod h1:JeRgkft04UBgHMgCIwADu4Pn6Mtm5d4 github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs= github.com/rogpeppe/go-internal v1.14.1 h1:UQB4HGPB6osV0SQTLymcB4TgvyWu6ZyliaW0tI/otEQ= github.com/rogpeppe/go-internal v1.14.1/go.mod h1:MaRKkUm5W0goXpeCfT7UZI6fk/L7L7so1lCWt35ZSgc= +github.com/russross/blackfriday v1.5.2 h1:HyvC0ARfnZBqnXwABFeSZHpKvJHJJfPz81GNueLj0oo= github.com/russross/blackfriday v1.5.2/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g= +github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk= +github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= github.com/ryanuber/columnize v2.1.0+incompatible/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts= github.com/sanity-io/litter v1.5.1 h1:dwnrSypP6q56o3lFxTU+t2fwQ9A+U5qrXVO4Qg9KwVU= github.com/sanity-io/litter v1.5.1/go.mod h1:5Z71SvaYy5kcGtyglXOC9rrUi3c1E8CamFWjQsazTh0= @@ -311,6 +339,8 @@ github.com/spf13/cobra v0.0.5/go.mod h1:3K3wKZymM7VvHMDS9+Akkh4K60UwM26emMESw8tL github.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo= github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= github.com/spf13/viper v1.3.2/go.mod h1:ZiWeW+zYFKm7srdB9IoDzzZXaJaI5eL9QjNiN/DMA2s= +github.com/status-im/keycard-go v0.2.0 h1:QDLFswOQu1r5jsycloeQh3bVU8n/NatHHaZobtDnDzA= +github.com/status-im/keycard-go v0.2.0/go.mod h1:wlp8ZLbsmrF6g6WjugPAx+IzoLrkdf9+mHxBEeo3Hbg= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= @@ -330,10 +360,14 @@ github.com/tklauser/go-sysconf v0.3.15 h1:VE89k0criAymJ/Os65CSn1IXaol+1wrsFHEB8O github.com/tklauser/go-sysconf v0.3.15/go.mod h1:Dmjwr6tYFIseJw7a3dRLJfsHAMXZ3nEnL/aZY+0IuI4= github.com/tklauser/numcpus v0.10.0 h1:18njr6LDBk1zuna922MgdjQuJFjrdppsZG60sHGfjso= github.com/tklauser/numcpus v0.10.0/go.mod h1:BiTKazU708GQTYF4mB+cmlpT2Is1gLk7XVuEeem8LsQ= +github.com/tyler-smith/go-bip39 v1.1.0 h1:5eUemwrMargf3BSLRRCalXT93Ns6pQJIjYQN2nyfOP8= +github.com/tyler-smith/go-bip39 v1.1.0/go.mod h1:gUYDtqQw1JS3ZJ8UWVcGTGqqr6YIN3CWg+kkNaLt55U= github.com/ugorji/go v1.1.4/go.mod h1:uQMGLiO92mf5W77hV/PUCpI3pbzQx3CRekS0kk+RGrc= github.com/ugorji/go v1.1.7/go.mod h1:kZn38zHttfInRq0xu/PH0az30d+z6vm202qpg1oXVMw= github.com/ugorji/go/codec v0.0.0-20181204163529-d75b2dcb6bc8/go.mod h1:VFNgLljTbGfSG7qAOspJ7OScBnGdDN/yBr0sguwnwf0= github.com/ugorji/go/codec v1.1.7/go.mod h1:Ax+UKWsSmolVDwsd+7N3ZtXu+yMGCf907BLYF3GoBXY= +github.com/urfave/cli/v2 v2.25.7 h1:VAzn5oq403l5pHjc4OhD54+XGO9cdKVL/7lDjF+iKUs= +github.com/urfave/cli/v2 v2.25.7/go.mod h1:8qnjx1vcq5s2/wpsqoZFndg2CE5tNFyrTvS6SinrnYQ= github.com/urfave/negroni v1.0.0/go.mod h1:Meg73S6kFm/4PpbYdq35yYWoCZ9mS/YSx+lKnmiohz4= github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc= github.com/valyala/fasthttp v1.6.0/go.mod h1:FstJa9V+Pj9vQ7OJie2qMHdwemEDaDiSdBnvPM1Su9w= @@ -344,6 +378,8 @@ github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f/go.mod h1:N2 github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415/go.mod h1:GwrjFmJcFw6At/Gs6z4yjiIwzuJ1/+UwLxMQDVQXShQ= github.com/xeipuuv/gojsonschema v1.2.0/go.mod h1:anYRn/JVcOK2ZgGU+IjEV4nwlhoK5sQluxsYJ78Id3Y= 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/yalp/jsonpath v0.0.0-20180802001716-5cc68e5049a0/go.mod h1:/LWChgwKmvncFJFHJ7Gvn9wZArjbV5/FppcK2fKk/tI= github.com/yudai/gojsondiff v1.0.0/go.mod h1:AY32+k2cwILAkW1fbgxQ5mUmMiZFgLIV+FBNExI05xg= github.com/yudai/golcs v0.0.0-20170316035057-ecda9a501e82/go.mod h1:lgjkn3NuSvDfVJdfcVVdX+jpBxNmX4rDAzaS45IcYoM= @@ -485,6 +521,8 @@ golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= golang.org/x/text v0.31.0 h1:aC8ghyu4JhP8VojJ2lEHBnochRno1sgL6nEi9WGFGMM= golang.org/x/text v0.31.0/go.mod h1:tKRAlv61yKIjGGHX/4tP1LTbc13YSec1pxVEWXzfoeM= golang.org/x/time v0.0.0-20201208040808-7e3f01d25324/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +golang.org/x/time v0.12.0 h1:ScB/8o8olJvc+CQPWrK3fPZNfh7qgwCrY0zJmoEQLSE= +golang.org/x/time v0.12.0/go.mod h1:CDIdPxbZBQxdj6cxyCIdrNogrJKMJ7pr37NYpMcMDSg= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20181221001348-537d06c36207/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= diff --git a/graft/evm/libevmtest/testmain.go b/graft/evm/libevmtest/testmain.go new file mode 100644 index 000000000000..81e16fa4fc2c --- /dev/null +++ b/graft/evm/libevmtest/testmain.go @@ -0,0 +1,75 @@ +// Copyright (C) 2019, Ava Labs, Inc. All rights reserved. +// See the file LICENSE for licensing terms. + +package libevmtest + +import ( + "fmt" + "os" + "testing" + + "github.com/ava-labs/avalanchego/vms/evm/emulate" +) + +// Variant represents which EVM variant is currently registered. +type Variant int32 + +const ( + // UnknownVariant indicates no variant is registered. + UnknownVariant Variant = iota + // CChainVariant indicates C-Chain types are registered. + CChainVariant + // SubnetEVMVariant indicates Subnet-EVM types are registered. + SubnetEVMVariant +) + +// RunWithAll runs all tests with both C-Chain and Subnet-EVM type registration. +// Tests are run twice - once with C-Chain types and once with Subnet-EVM types. +// Both test runs must pass for the overall result to be successful. +// +// The current variant is set before each test run, allowing tests to check +// which variant they're running under via CurrentVariant(). +func RunWithAll(m *testing.M) int { + fmt.Println("=== Running tests with both C-Chain and Subnet-EVM ===") + + fmt.Println("--- Running C-Chain variant ---") + cchainCode := runWith(m, emulate.CChain, CChainVariant) + + fmt.Println("--- Running Subnet-EVM variant ---") + subnetCode := runWith(m, emulate.SubnetEVM, SubnetEVMVariant) + + if cchainCode != 0 { + fmt.Fprintln(os.Stderr, "C-Chain tests failed") + } else { + fmt.Println("C-Chain tests passed") + } + + if subnetCode != 0 { + fmt.Fprintln(os.Stderr, "Subnet-EVM tests failed") + } else { + fmt.Println("Subnet-EVM tests passed") + } + + return cchainCode | subnetCode +} + +var current Variant + +// CurrentVariant returns the currently emulated EVM variant. Note that +// if emulation was initiated outside of this package then [UnknownVariant] +// will be returned. +func CurrentVariant() Variant { + return current +} + +// runWith executes tests with the specified emulation function and variant. +func runWith(m *testing.M, emulateFn func(func() error) error, variant Variant) int { + var code int + _ = emulateFn(func() error { + current = variant + defer func() { current = UnknownVariant }() + code = m.Run() + return nil + }) + return code +} diff --git a/graft/coreth/rpc/client.go b/graft/evm/rpc/client.go similarity index 100% rename from graft/coreth/rpc/client.go rename to graft/evm/rpc/client.go diff --git a/graft/coreth/rpc/client_opt.go b/graft/evm/rpc/client_opt.go similarity index 100% rename from graft/coreth/rpc/client_opt.go rename to graft/evm/rpc/client_opt.go diff --git a/graft/coreth/rpc/client_opt_test.go b/graft/evm/rpc/client_opt_test.go similarity index 75% rename from graft/coreth/rpc/client_opt_test.go rename to graft/evm/rpc/client_opt_test.go index 1721210c0ee4..8bf1204ec1ab 100644 --- a/graft/coreth/rpc/client_opt_test.go +++ b/graft/evm/rpc/client_opt_test.go @@ -10,26 +10,24 @@ // Much love to the original authors for their work. // ********** -package rpc_test +package rpc import ( "context" "net/http" "time" - - "github.com/ava-labs/avalanchego/graft/coreth/rpc" ) // This example configures a HTTP-based RPC client with two options - one setting the // overall request timeout, the other adding a custom HTTP header to all requests. func ExampleDialOptions() { - tokenHeader := rpc.WithHeader("x-token", "foo") - httpClient := rpc.WithHTTPClient(&http.Client{ + tokenHeader := WithHeader("x-token", "foo") + httpClient := WithHTTPClient(&http.Client{ Timeout: 10 * time.Second, }) ctx := context.Background() - c, err := rpc.DialOptions(ctx, "http://rpc.example.com", httpClient, tokenHeader) + c, err := DialOptions(ctx, "http://rpc.example.com", httpClient, tokenHeader) if err != nil { panic(err) } diff --git a/graft/coreth/rpc/client_test.go b/graft/evm/rpc/client_test.go similarity index 100% rename from graft/coreth/rpc/client_test.go rename to graft/evm/rpc/client_test.go diff --git a/graft/coreth/rpc/context_headers.go b/graft/evm/rpc/context_headers.go similarity index 100% rename from graft/coreth/rpc/context_headers.go rename to graft/evm/rpc/context_headers.go diff --git a/graft/coreth/rpc/doc.go b/graft/evm/rpc/doc.go similarity index 100% rename from graft/coreth/rpc/doc.go rename to graft/evm/rpc/doc.go diff --git a/graft/coreth/rpc/errors.go b/graft/evm/rpc/errors.go similarity index 100% rename from graft/coreth/rpc/errors.go rename to graft/evm/rpc/errors.go diff --git a/graft/coreth/rpc/handler.go b/graft/evm/rpc/handler.go similarity index 100% rename from graft/coreth/rpc/handler.go rename to graft/evm/rpc/handler.go diff --git a/graft/coreth/rpc/http.go b/graft/evm/rpc/http.go similarity index 100% rename from graft/coreth/rpc/http.go rename to graft/evm/rpc/http.go diff --git a/graft/coreth/rpc/http_test.go b/graft/evm/rpc/http_test.go similarity index 100% rename from graft/coreth/rpc/http_test.go rename to graft/evm/rpc/http_test.go diff --git a/graft/coreth/rpc/inproc.go b/graft/evm/rpc/inproc.go similarity index 100% rename from graft/coreth/rpc/inproc.go rename to graft/evm/rpc/inproc.go diff --git a/graft/coreth/rpc/json.go b/graft/evm/rpc/json.go similarity index 100% rename from graft/coreth/rpc/json.go rename to graft/evm/rpc/json.go diff --git a/graft/evm/rpc/main_test.go b/graft/evm/rpc/main_test.go new file mode 100644 index 000000000000..b3c37a254f60 --- /dev/null +++ b/graft/evm/rpc/main_test.go @@ -0,0 +1,18 @@ +// Copyright (C) 2019, Ava Labs, Inc. All rights reserved. +// See the file LICENSE for licensing terms. + +package rpc + +import ( + "os" + "testing" + + "github.com/ava-labs/avalanchego/graft/evm/libevmtest" +) + +// TestMain runs all tests in the rpc package with proper EVM type registration. +// Tests are run twice - once with C-Chain types and once with Subnet-EVM types. +// Both test runs must pass for the overall result to be successful. +func TestMain(m *testing.M) { + os.Exit(libevmtest.RunWithAll(m)) +} diff --git a/graft/coreth/rpc/metrics.go b/graft/evm/rpc/metrics.go similarity index 100% rename from graft/coreth/rpc/metrics.go rename to graft/evm/rpc/metrics.go diff --git a/graft/coreth/rpc/server.go b/graft/evm/rpc/server.go similarity index 100% rename from graft/coreth/rpc/server.go rename to graft/evm/rpc/server.go diff --git a/graft/coreth/rpc/server_test.go b/graft/evm/rpc/server_test.go similarity index 100% rename from graft/coreth/rpc/server_test.go rename to graft/evm/rpc/server_test.go diff --git a/graft/coreth/rpc/service.go b/graft/evm/rpc/service.go similarity index 100% rename from graft/coreth/rpc/service.go rename to graft/evm/rpc/service.go diff --git a/graft/coreth/rpc/subscription.go b/graft/evm/rpc/subscription.go similarity index 100% rename from graft/coreth/rpc/subscription.go rename to graft/evm/rpc/subscription.go diff --git a/graft/coreth/rpc/subscription_test.go b/graft/evm/rpc/subscription_test.go similarity index 79% rename from graft/coreth/rpc/subscription_test.go rename to graft/evm/rpc/subscription_test.go index 86cc5d26c548..2e9526ebdd16 100644 --- a/graft/coreth/rpc/subscription_test.go +++ b/graft/evm/rpc/subscription_test.go @@ -28,7 +28,6 @@ package rpc import ( - "bytes" "context" "encoding/json" "fmt" @@ -273,23 +272,3 @@ func BenchmarkNotify(b *testing.B) { notifier.Notify(id, msg) } } - -func TestNotify(t *testing.T) { - out := new(bytes.Buffer) - id := ID("test") - notifier := &Notifier{ - h: &handler{conn: &mockConn{json.NewEncoder(out)}}, - sub: &Subscription{ID: id}, - activated: true, - } - msg := &types.Header{ - ParentHash: common.HexToHash("0x01"), - Number: big.NewInt(100), - } - notifier.Notify(id, msg) - have := strings.TrimSpace(out.String()) - want := `{"jsonrpc":"2.0","method":"_subscription","params":{"subscription":"test","result":{"parentHash":"0x0000000000000000000000000000000000000000000000000000000000000001","sha3Uncles":"0x0000000000000000000000000000000000000000000000000000000000000000","miner":"0x0000000000000000000000000000000000000000","stateRoot":"0x0000000000000000000000000000000000000000000000000000000000000000","transactionsRoot":"0x0000000000000000000000000000000000000000000000000000000000000000","receiptsRoot":"0x0000000000000000000000000000000000000000000000000000000000000000","logsBloom":"0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000","difficulty":null,"number":"0x64","gasLimit":"0x0","gasUsed":"0x0","timestamp":"0x0","extraData":"0x","mixHash":"0x0000000000000000000000000000000000000000000000000000000000000000","nonce":"0x0000000000000000","extDataHash":"0x0000000000000000000000000000000000000000000000000000000000000000","baseFeePerGas":null,"extDataGasUsed":null,"blockGasCost":null,"blobGasUsed":null,"excessBlobGas":null,"parentBeaconBlockRoot":null,"timestampMilliseconds":null,"minDelayExcess":null,"hash":"0x9a9ca1b5790a674785245afedcee9fc1a90d3514c6faa1cbd26f696136d6fd12"}}}` - if have != want { - t.Errorf("have:\n%v\nwant:\n%v\n", have, want) - } -} diff --git a/graft/coreth/rpc/testdata/internal-error.js b/graft/evm/rpc/testdata/internal-error.js similarity index 100% rename from graft/coreth/rpc/testdata/internal-error.js rename to graft/evm/rpc/testdata/internal-error.js diff --git a/graft/coreth/rpc/testdata/invalid-badid.js b/graft/evm/rpc/testdata/invalid-badid.js similarity index 100% rename from graft/coreth/rpc/testdata/invalid-badid.js rename to graft/evm/rpc/testdata/invalid-badid.js diff --git a/graft/coreth/rpc/testdata/invalid-badversion.js b/graft/evm/rpc/testdata/invalid-badversion.js similarity index 100% rename from graft/coreth/rpc/testdata/invalid-badversion.js rename to graft/evm/rpc/testdata/invalid-badversion.js diff --git a/graft/coreth/rpc/testdata/invalid-batch-toolarge.js b/graft/evm/rpc/testdata/invalid-batch-toolarge.js similarity index 100% rename from graft/coreth/rpc/testdata/invalid-batch-toolarge.js rename to graft/evm/rpc/testdata/invalid-batch-toolarge.js diff --git a/graft/coreth/rpc/testdata/invalid-batch.js b/graft/evm/rpc/testdata/invalid-batch.js similarity index 100% rename from graft/coreth/rpc/testdata/invalid-batch.js rename to graft/evm/rpc/testdata/invalid-batch.js diff --git a/graft/coreth/rpc/testdata/invalid-idonly.js b/graft/evm/rpc/testdata/invalid-idonly.js similarity index 100% rename from graft/coreth/rpc/testdata/invalid-idonly.js rename to graft/evm/rpc/testdata/invalid-idonly.js diff --git a/graft/coreth/rpc/testdata/invalid-nonobj.js b/graft/evm/rpc/testdata/invalid-nonobj.js similarity index 100% rename from graft/coreth/rpc/testdata/invalid-nonobj.js rename to graft/evm/rpc/testdata/invalid-nonobj.js diff --git a/graft/coreth/rpc/testdata/invalid-syntax.json b/graft/evm/rpc/testdata/invalid-syntax.json similarity index 100% rename from graft/coreth/rpc/testdata/invalid-syntax.json rename to graft/evm/rpc/testdata/invalid-syntax.json diff --git a/graft/coreth/rpc/testdata/reqresp-batch.js b/graft/evm/rpc/testdata/reqresp-batch.js similarity index 100% rename from graft/coreth/rpc/testdata/reqresp-batch.js rename to graft/evm/rpc/testdata/reqresp-batch.js diff --git a/graft/coreth/rpc/testdata/reqresp-echo.js b/graft/evm/rpc/testdata/reqresp-echo.js similarity index 100% rename from graft/coreth/rpc/testdata/reqresp-echo.js rename to graft/evm/rpc/testdata/reqresp-echo.js diff --git a/graft/coreth/rpc/testdata/reqresp-namedparam.js b/graft/evm/rpc/testdata/reqresp-namedparam.js similarity index 100% rename from graft/coreth/rpc/testdata/reqresp-namedparam.js rename to graft/evm/rpc/testdata/reqresp-namedparam.js diff --git a/graft/coreth/rpc/testdata/reqresp-noargsrets.js b/graft/evm/rpc/testdata/reqresp-noargsrets.js similarity index 100% rename from graft/coreth/rpc/testdata/reqresp-noargsrets.js rename to graft/evm/rpc/testdata/reqresp-noargsrets.js diff --git a/graft/coreth/rpc/testdata/reqresp-nomethod.js b/graft/evm/rpc/testdata/reqresp-nomethod.js similarity index 100% rename from graft/coreth/rpc/testdata/reqresp-nomethod.js rename to graft/evm/rpc/testdata/reqresp-nomethod.js diff --git a/graft/coreth/rpc/testdata/reqresp-noparam.js b/graft/evm/rpc/testdata/reqresp-noparam.js similarity index 100% rename from graft/coreth/rpc/testdata/reqresp-noparam.js rename to graft/evm/rpc/testdata/reqresp-noparam.js diff --git a/graft/coreth/rpc/testdata/reqresp-paramsnull.js b/graft/evm/rpc/testdata/reqresp-paramsnull.js similarity index 100% rename from graft/coreth/rpc/testdata/reqresp-paramsnull.js rename to graft/evm/rpc/testdata/reqresp-paramsnull.js diff --git a/graft/coreth/rpc/testdata/revcall.js b/graft/evm/rpc/testdata/revcall.js similarity index 100% rename from graft/coreth/rpc/testdata/revcall.js rename to graft/evm/rpc/testdata/revcall.js diff --git a/graft/coreth/rpc/testdata/revcall2.js b/graft/evm/rpc/testdata/revcall2.js similarity index 100% rename from graft/coreth/rpc/testdata/revcall2.js rename to graft/evm/rpc/testdata/revcall2.js diff --git a/graft/coreth/rpc/testdata/subscription.js b/graft/evm/rpc/testdata/subscription.js similarity index 100% rename from graft/coreth/rpc/testdata/subscription.js rename to graft/evm/rpc/testdata/subscription.js diff --git a/graft/subnet-evm/rpc/inproc.go b/graft/evm/rpc/testing.go similarity index 56% rename from graft/subnet-evm/rpc/inproc.go rename to graft/evm/rpc/testing.go index 194bd7dd9b5e..223740ddb0d9 100644 --- a/graft/subnet-evm/rpc/inproc.go +++ b/graft/evm/rpc/testing.go @@ -9,7 +9,7 @@ // // Much love to the original authors for their work. // ********** -// Copyright 2016 The go-ethereum Authors +// Copyright 2015 The go-ethereum Authors // This file is part of the go-ethereum library. // // The go-ethereum library is free software: you can redistribute it and/or modify @@ -29,17 +29,36 @@ package rpc import ( "context" - "net" + "encoding/json" + "io" ) -// DialInProc attaches an in-process connection to the given RPC server. -func DialInProc(handler *Server) *Client { - initctx := context.Background() - cfg := new(clientConfig) - c, _ := newClient(initctx, cfg, func(context.Context) (ServerCodec, error) { - p1, p2 := net.Pipe() - go handler.ServeCodec(NewCodec(p1), 0, 0, 0, 0) - return NewCodec(p2), nil - }) - return c +// testConn is a test implementation of the serverConn interface. +type testConn struct { + enc *json.Encoder +} + +func (c *testConn) writeJSON(_ context.Context, msg interface{}, _ bool) error { + return c.enc.Encode(msg) +} + +func (c *testConn) writeJSONSkipDeadline(_ context.Context, msg interface{}, _, _ bool) error { + return c.enc.Encode(msg) +} + +func (*testConn) closed() <-chan interface{} { return nil } + +func (*testConn) remoteAddr() string { return "" } + +// NewTestNotifier creates a Notifier for testing that writes to the given writer. +// This is exported so that tests in package rpc_test can create Notifiers without +// accessing internal types. +func NewTestNotifier(w io.Writer, subID ID) *Notifier { + return &Notifier{ + h: &handler{ + conn: &testConn{enc: json.NewEncoder(w)}, + }, + sub: &Subscription{ID: subID}, + activated: true, + } } diff --git a/graft/coreth/rpc/testservice_test.go b/graft/evm/rpc/testservice_test.go similarity index 100% rename from graft/coreth/rpc/testservice_test.go rename to graft/evm/rpc/testservice_test.go diff --git a/graft/coreth/rpc/types.go b/graft/evm/rpc/types.go similarity index 100% rename from graft/coreth/rpc/types.go rename to graft/evm/rpc/types.go diff --git a/graft/coreth/rpc/types_test.go b/graft/evm/rpc/types_test.go similarity index 100% rename from graft/coreth/rpc/types_test.go rename to graft/evm/rpc/types_test.go diff --git a/graft/coreth/rpc/websocket.go b/graft/evm/rpc/websocket.go similarity index 100% rename from graft/coreth/rpc/websocket.go rename to graft/evm/rpc/websocket.go diff --git a/graft/coreth/rpc/websocket_test.go b/graft/evm/rpc/websocket_test.go similarity index 100% rename from graft/coreth/rpc/websocket_test.go rename to graft/evm/rpc/websocket_test.go diff --git a/graft/evm/scripts/upstream_files.txt b/graft/evm/scripts/upstream_files.txt index b07e81b4ce64..c7a5d3ebd6ae 100644 --- a/graft/evm/scripts/upstream_files.txt +++ b/graft/evm/scripts/upstream_files.txt @@ -1,4 +1,6 @@ +rpc/* core/* triedb/* -!core/state/snapshot/snapshot_ext.go \ No newline at end of file +!core/state/snapshot/snapshot_ext.go +!rpc/main_test.go diff --git a/graft/subnet-evm/accounts/abi/bind/base.go b/graft/subnet-evm/accounts/abi/bind/base.go index fcbf4571f8fc..d44504ff5618 100644 --- a/graft/subnet-evm/accounts/abi/bind/base.go +++ b/graft/subnet-evm/accounts/abi/bind/base.go @@ -35,8 +35,8 @@ import ( "strings" "sync" + "github.com/ava-labs/avalanchego/graft/evm/rpc" "github.com/ava-labs/avalanchego/graft/subnet-evm/accounts/abi" - "github.com/ava-labs/avalanchego/graft/subnet-evm/rpc" ethereum "github.com/ava-labs/libevm" "github.com/ava-labs/libevm/common" "github.com/ava-labs/libevm/core/types" diff --git a/graft/subnet-evm/accounts/abi/bind/precompilebind/precompile_bind_test.go b/graft/subnet-evm/accounts/abi/bind/precompilebind/precompile_bind_test.go index c33953a61a4b..016f124e7a00 100644 --- a/graft/subnet-evm/accounts/abi/bind/precompilebind/precompile_bind_test.go +++ b/graft/subnet-evm/accounts/abi/bind/precompilebind/precompile_bind_test.go @@ -694,6 +694,11 @@ func TestPrecompileBind(t *testing.T) { out, err = replacer.CombinedOutput() require.NoError(t, err, "failed to replace binding test dependency to current source tree: %v\n%s", err, out) + replacer = exec.Command(gocmd, "mod", "edit", "-x", "-require", "github.com/ava-labs/avalanchego/graft/coreth@v0.0.0", "-replace", "github.com/ava-labs/avalanchego/graft/coreth="+filepath.Join(pwd, "..", "..", "..", "..", "..", "coreth")) + replacer.Dir = pkg + out, err = replacer.CombinedOutput() + require.NoError(t, err, "failed to replace binding test dependency to current source tree: %v\n%s", err, out) + replacer = exec.Command(gocmd, "mod", "edit", "-x", "-require", "github.com/ava-labs/avalanchego@v0.0.0", "-replace", "github.com/ava-labs/avalanchego="+filepath.Join(pwd, "..", "..", "..", "..", "..", "..")) replacer.Dir = pkg out, err = replacer.CombinedOutput() diff --git a/graft/subnet-evm/cmd/simulator/metrics/metrics.go b/graft/subnet-evm/cmd/simulator/metrics/metrics.go index d1e47c1d7ef4..0080ffbaef4e 100644 --- a/graft/subnet-evm/cmd/simulator/metrics/metrics.go +++ b/graft/subnet-evm/cmd/simulator/metrics/metrics.go @@ -15,7 +15,7 @@ import ( "github.com/prometheus/client_golang/prometheus" "github.com/prometheus/client_golang/prometheus/promhttp" - "github.com/ava-labs/avalanchego/graft/subnet-evm/rpc" + "github.com/ava-labs/avalanchego/graft/evm/rpc" ) type Metrics struct { diff --git a/graft/subnet-evm/eth/api_backend.go b/graft/subnet-evm/eth/api_backend.go index 77b1790795e8..58d6ace7d122 100644 --- a/graft/subnet-evm/eth/api_backend.go +++ b/graft/subnet-evm/eth/api_backend.go @@ -33,13 +33,13 @@ import ( "math/big" "time" + "github.com/ava-labs/avalanchego/graft/evm/rpc" "github.com/ava-labs/avalanchego/graft/subnet-evm/commontype" "github.com/ava-labs/avalanchego/graft/subnet-evm/consensus" "github.com/ava-labs/avalanchego/graft/subnet-evm/core" "github.com/ava-labs/avalanchego/graft/subnet-evm/core/txpool" "github.com/ava-labs/avalanchego/graft/subnet-evm/eth/gasprice" "github.com/ava-labs/avalanchego/graft/subnet-evm/eth/tracers" - "github.com/ava-labs/avalanchego/graft/subnet-evm/rpc" "github.com/ava-labs/libevm/accounts" "github.com/ava-labs/libevm/common" "github.com/ava-labs/libevm/core/bloombits" diff --git a/graft/subnet-evm/eth/api_debug.go b/graft/subnet-evm/eth/api_debug.go index d9a060644427..144a4373b6ef 100644 --- a/graft/subnet-evm/eth/api_debug.go +++ b/graft/subnet-evm/eth/api_debug.go @@ -43,8 +43,8 @@ import ( "github.com/ava-labs/libevm/rlp" "github.com/ava-labs/libevm/trie" + "github.com/ava-labs/avalanchego/graft/evm/rpc" "github.com/ava-labs/avalanchego/graft/subnet-evm/internal/ethapi" - "github.com/ava-labs/avalanchego/graft/subnet-evm/rpc" "github.com/ava-labs/avalanchego/vms/evm/sync/customrawdb" ) diff --git a/graft/subnet-evm/eth/backend.go b/graft/subnet-evm/eth/backend.go index 26a8401dece4..71e437034459 100644 --- a/graft/subnet-evm/eth/backend.go +++ b/graft/subnet-evm/eth/backend.go @@ -35,6 +35,7 @@ import ( "sync" "time" + "github.com/ava-labs/avalanchego/graft/evm/rpc" ethparams "github.com/ava-labs/libevm/params" "github.com/ava-labs/avalanchego/graft/evm/core/state/pruner" @@ -51,7 +52,6 @@ import ( "github.com/ava-labs/avalanchego/graft/subnet-evm/miner" "github.com/ava-labs/avalanchego/graft/subnet-evm/node" "github.com/ava-labs/avalanchego/graft/subnet-evm/params" - "github.com/ava-labs/avalanchego/graft/subnet-evm/rpc" "github.com/ava-labs/avalanchego/utils/timer/mockable" "github.com/ava-labs/avalanchego/vms/evm/sync/customrawdb" "github.com/ava-labs/libevm/accounts" diff --git a/graft/subnet-evm/eth/filters/api.go b/graft/subnet-evm/eth/filters/api.go index be45277231ce..6b57b3cac535 100644 --- a/graft/subnet-evm/eth/filters/api.go +++ b/graft/subnet-evm/eth/filters/api.go @@ -36,8 +36,8 @@ import ( "sync" "time" + "github.com/ava-labs/avalanchego/graft/evm/rpc" "github.com/ava-labs/avalanchego/graft/subnet-evm/internal/ethapi" - "github.com/ava-labs/avalanchego/graft/subnet-evm/rpc" ethereum "github.com/ava-labs/libevm" "github.com/ava-labs/libevm/common" "github.com/ava-labs/libevm/common/hexutil" diff --git a/graft/subnet-evm/eth/filters/api_test.go b/graft/subnet-evm/eth/filters/api_test.go index 511e73996da6..b54988f234c1 100644 --- a/graft/subnet-evm/eth/filters/api_test.go +++ b/graft/subnet-evm/eth/filters/api_test.go @@ -33,7 +33,7 @@ import ( "strings" "testing" - "github.com/ava-labs/avalanchego/graft/subnet-evm/rpc" + "github.com/ava-labs/avalanchego/graft/evm/rpc" "github.com/ava-labs/libevm/common" ) diff --git a/graft/subnet-evm/eth/filters/filter.go b/graft/subnet-evm/eth/filters/filter.go index 3e5d282fb24b..4e384c7a166a 100644 --- a/graft/subnet-evm/eth/filters/filter.go +++ b/graft/subnet-evm/eth/filters/filter.go @@ -33,7 +33,7 @@ import ( "fmt" "math/big" - "github.com/ava-labs/avalanchego/graft/subnet-evm/rpc" + "github.com/ava-labs/avalanchego/graft/evm/rpc" "github.com/ava-labs/libevm/common" "github.com/ava-labs/libevm/core/bloombits" "github.com/ava-labs/libevm/core/types" diff --git a/graft/subnet-evm/eth/filters/filter_system.go b/graft/subnet-evm/eth/filters/filter_system.go index ce7df7f86f4d..890157b3281c 100644 --- a/graft/subnet-evm/eth/filters/filter_system.go +++ b/graft/subnet-evm/eth/filters/filter_system.go @@ -35,9 +35,9 @@ import ( "sync" "time" + "github.com/ava-labs/avalanchego/graft/evm/rpc" "github.com/ava-labs/avalanchego/graft/subnet-evm/core" "github.com/ava-labs/avalanchego/graft/subnet-evm/params" - "github.com/ava-labs/avalanchego/graft/subnet-evm/rpc" ethereum "github.com/ava-labs/libevm" "github.com/ava-labs/libevm/common" "github.com/ava-labs/libevm/core/bloombits" diff --git a/graft/subnet-evm/eth/filters/filter_system_test.go b/graft/subnet-evm/eth/filters/filter_system_test.go index 3c4a37bc884d..fbcec791dc6a 100644 --- a/graft/subnet-evm/eth/filters/filter_system_test.go +++ b/graft/subnet-evm/eth/filters/filter_system_test.go @@ -38,13 +38,13 @@ import ( "testing" "time" + "github.com/ava-labs/avalanchego/graft/evm/rpc" ethparams "github.com/ava-labs/libevm/params" "github.com/ava-labs/avalanchego/graft/subnet-evm/consensus/dummy" "github.com/ava-labs/avalanchego/graft/subnet-evm/core" "github.com/ava-labs/avalanchego/graft/subnet-evm/internal/ethapi" "github.com/ava-labs/avalanchego/graft/subnet-evm/params" - "github.com/ava-labs/avalanchego/graft/subnet-evm/rpc" "github.com/ava-labs/avalanchego/vms/evm/sync/customrawdb" ethereum "github.com/ava-labs/libevm" "github.com/ava-labs/libevm/common" diff --git a/graft/subnet-evm/eth/filters/filter_test.go b/graft/subnet-evm/eth/filters/filter_test.go index 840a64718246..6676b30c8af4 100644 --- a/graft/subnet-evm/eth/filters/filter_test.go +++ b/graft/subnet-evm/eth/filters/filter_test.go @@ -36,12 +36,12 @@ import ( "testing" "time" + "github.com/ava-labs/avalanchego/graft/evm/rpc" "github.com/ava-labs/avalanchego/graft/subnet-evm/accounts/abi" "github.com/ava-labs/avalanchego/graft/subnet-evm/consensus/dummy" "github.com/ava-labs/avalanchego/graft/subnet-evm/core" "github.com/ava-labs/avalanchego/graft/subnet-evm/params" "github.com/ava-labs/avalanchego/graft/subnet-evm/plugin/evm/customtypes" - "github.com/ava-labs/avalanchego/graft/subnet-evm/rpc" "github.com/ava-labs/avalanchego/vms/evm/sync/customrawdb" "github.com/ava-labs/libevm/common" "github.com/ava-labs/libevm/core/rawdb" diff --git a/graft/subnet-evm/eth/gasprice/fee_info_provider.go b/graft/subnet-evm/eth/gasprice/fee_info_provider.go index c44cb91935b3..497751eaa4ec 100644 --- a/graft/subnet-evm/eth/gasprice/fee_info_provider.go +++ b/graft/subnet-evm/eth/gasprice/fee_info_provider.go @@ -31,8 +31,8 @@ import ( "context" "math/big" + "github.com/ava-labs/avalanchego/graft/evm/rpc" "github.com/ava-labs/avalanchego/graft/subnet-evm/core" - "github.com/ava-labs/avalanchego/graft/subnet-evm/rpc" "github.com/ava-labs/libevm/core/types" lru "github.com/hashicorp/golang-lru" ) diff --git a/graft/subnet-evm/eth/gasprice/feehistory.go b/graft/subnet-evm/eth/gasprice/feehistory.go index 0a60a6fb2af3..574c7217d72f 100644 --- a/graft/subnet-evm/eth/gasprice/feehistory.go +++ b/graft/subnet-evm/eth/gasprice/feehistory.go @@ -34,7 +34,7 @@ import ( "math/big" "slices" - "github.com/ava-labs/avalanchego/graft/subnet-evm/rpc" + "github.com/ava-labs/avalanchego/graft/evm/rpc" "github.com/ava-labs/libevm/common" "github.com/ava-labs/libevm/core/types" "github.com/ava-labs/libevm/log" diff --git a/graft/subnet-evm/eth/gasprice/feehistory_test.go b/graft/subnet-evm/eth/gasprice/feehistory_test.go index 65af41db8e87..cea6b5600ba2 100644 --- a/graft/subnet-evm/eth/gasprice/feehistory_test.go +++ b/graft/subnet-evm/eth/gasprice/feehistory_test.go @@ -38,8 +38,8 @@ import ( ethparams "github.com/ava-labs/libevm/params" "github.com/stretchr/testify/require" + "github.com/ava-labs/avalanchego/graft/evm/rpc" "github.com/ava-labs/avalanchego/graft/subnet-evm/params" - "github.com/ava-labs/avalanchego/graft/subnet-evm/rpc" "github.com/ava-labs/libevm/common" ) diff --git a/graft/subnet-evm/eth/gasprice/gasprice.go b/graft/subnet-evm/eth/gasprice/gasprice.go index bfe16258bb27..c5d3e05b4d41 100644 --- a/graft/subnet-evm/eth/gasprice/gasprice.go +++ b/graft/subnet-evm/eth/gasprice/gasprice.go @@ -32,12 +32,12 @@ import ( "math/big" "sync" + "github.com/ava-labs/avalanchego/graft/evm/rpc" "github.com/ava-labs/avalanchego/graft/subnet-evm/commontype" "github.com/ava-labs/avalanchego/graft/subnet-evm/core" "github.com/ava-labs/avalanchego/graft/subnet-evm/params" "github.com/ava-labs/avalanchego/graft/subnet-evm/plugin/evm/customheader" "github.com/ava-labs/avalanchego/graft/subnet-evm/plugin/evm/upgrade/legacy" - "github.com/ava-labs/avalanchego/graft/subnet-evm/rpc" "github.com/ava-labs/avalanchego/utils/timer/mockable" "github.com/ava-labs/avalanchego/vms/evm/acp176" "github.com/ava-labs/libevm/common" diff --git a/graft/subnet-evm/eth/gasprice/gasprice_test.go b/graft/subnet-evm/eth/gasprice/gasprice_test.go index 0980ff8aa01c..da04be9bcc42 100644 --- a/graft/subnet-evm/eth/gasprice/gasprice_test.go +++ b/graft/subnet-evm/eth/gasprice/gasprice_test.go @@ -34,12 +34,12 @@ import ( "testing" "time" + "github.com/ava-labs/avalanchego/graft/evm/rpc" "github.com/ava-labs/avalanchego/graft/subnet-evm/commontype" "github.com/ava-labs/avalanchego/graft/subnet-evm/consensus/dummy" "github.com/ava-labs/avalanchego/graft/subnet-evm/core" "github.com/ava-labs/avalanchego/graft/subnet-evm/params" "github.com/ava-labs/avalanchego/graft/subnet-evm/plugin/evm/customtypes" - "github.com/ava-labs/avalanchego/graft/subnet-evm/rpc" "github.com/ava-labs/libevm/common" "github.com/ava-labs/libevm/core/rawdb" "github.com/ava-labs/libevm/core/types" diff --git a/graft/subnet-evm/eth/tracers/api.go b/graft/subnet-evm/eth/tracers/api.go index 1d6800a2ab08..1d200eef99aa 100644 --- a/graft/subnet-evm/eth/tracers/api.go +++ b/graft/subnet-evm/eth/tracers/api.go @@ -39,11 +39,11 @@ import ( "sync" "time" + "github.com/ava-labs/avalanchego/graft/evm/rpc" "github.com/ava-labs/avalanchego/graft/subnet-evm/consensus" "github.com/ava-labs/avalanchego/graft/subnet-evm/core" "github.com/ava-labs/avalanchego/graft/subnet-evm/internal/ethapi" "github.com/ava-labs/avalanchego/graft/subnet-evm/params" - "github.com/ava-labs/avalanchego/graft/subnet-evm/rpc" "github.com/ava-labs/libevm/common" "github.com/ava-labs/libevm/common/hexutil" "github.com/ava-labs/libevm/core/state" diff --git a/graft/subnet-evm/eth/tracers/api_extra_test.go b/graft/subnet-evm/eth/tracers/api_extra_test.go index 05da4d863243..9c2a71becc59 100644 --- a/graft/subnet-evm/eth/tracers/api_extra_test.go +++ b/graft/subnet-evm/eth/tracers/api_extra_test.go @@ -20,13 +20,13 @@ import ( "github.com/ava-labs/libevm/eth/tracers/logger" "github.com/stretchr/testify/require" + "github.com/ava-labs/avalanchego/graft/evm/rpc" "github.com/ava-labs/avalanchego/graft/subnet-evm/core" "github.com/ava-labs/avalanchego/graft/subnet-evm/internal/ethapi" "github.com/ava-labs/avalanchego/graft/subnet-evm/params" "github.com/ava-labs/avalanchego/graft/subnet-evm/params/extras" "github.com/ava-labs/avalanchego/graft/subnet-evm/plugin/evm/customtypes" "github.com/ava-labs/avalanchego/graft/subnet-evm/precompile/contracts/txallowlist" - "github.com/ava-labs/avalanchego/graft/subnet-evm/rpc" "github.com/ava-labs/avalanchego/vms/evm/sync/customrawdb" ethparams "github.com/ava-labs/libevm/params" diff --git a/graft/subnet-evm/eth/tracers/api_test.go b/graft/subnet-evm/eth/tracers/api_test.go index 109044d457ab..94e97f50a390 100644 --- a/graft/subnet-evm/eth/tracers/api_test.go +++ b/graft/subnet-evm/eth/tracers/api_test.go @@ -39,12 +39,12 @@ import ( "sync/atomic" "testing" + "github.com/ava-labs/avalanchego/graft/evm/rpc" "github.com/ava-labs/avalanchego/graft/subnet-evm/consensus" "github.com/ava-labs/avalanchego/graft/subnet-evm/consensus/dummy" "github.com/ava-labs/avalanchego/graft/subnet-evm/core" "github.com/ava-labs/avalanchego/graft/subnet-evm/internal/ethapi" "github.com/ava-labs/avalanchego/graft/subnet-evm/params" - "github.com/ava-labs/avalanchego/graft/subnet-evm/rpc" "github.com/ava-labs/avalanchego/vms/evm/sync/customrawdb" "github.com/ava-labs/libevm/common" "github.com/ava-labs/libevm/common/hexutil" diff --git a/graft/subnet-evm/ethclient/ethclient.go b/graft/subnet-evm/ethclient/ethclient.go index a858cfc4f5e0..0036899c5b7e 100644 --- a/graft/subnet-evm/ethclient/ethclient.go +++ b/graft/subnet-evm/ethclient/ethclient.go @@ -35,10 +35,10 @@ import ( "fmt" "math/big" + "github.com/ava-labs/avalanchego/graft/evm/rpc" "github.com/ava-labs/avalanchego/graft/subnet-evm/accounts/abi/bind" "github.com/ava-labs/avalanchego/graft/subnet-evm/interfaces" "github.com/ava-labs/avalanchego/graft/subnet-evm/params" - "github.com/ava-labs/avalanchego/graft/subnet-evm/rpc" ethereum "github.com/ava-labs/libevm" "github.com/ava-labs/libevm/common" "github.com/ava-labs/libevm/common/hexutil" diff --git a/graft/subnet-evm/ethclient/simulated/backend.go b/graft/subnet-evm/ethclient/simulated/backend.go index 9884d395e940..89317f659df1 100644 --- a/graft/subnet-evm/ethclient/simulated/backend.go +++ b/graft/subnet-evm/ethclient/simulated/backend.go @@ -33,6 +33,7 @@ import ( "time" "github.com/ava-labs/avalanchego/graft/evm/constants" + "github.com/ava-labs/avalanchego/graft/evm/rpc" "github.com/ava-labs/avalanchego/graft/subnet-evm/consensus/dummy" "github.com/ava-labs/avalanchego/graft/subnet-evm/core" "github.com/ava-labs/avalanchego/graft/subnet-evm/eth" @@ -41,7 +42,6 @@ import ( "github.com/ava-labs/avalanchego/graft/subnet-evm/interfaces" "github.com/ava-labs/avalanchego/graft/subnet-evm/node" "github.com/ava-labs/avalanchego/graft/subnet-evm/params" - "github.com/ava-labs/avalanchego/graft/subnet-evm/rpc" "github.com/ava-labs/avalanchego/utils/timer/mockable" ethereum "github.com/ava-labs/libevm" "github.com/ava-labs/libevm/common" diff --git a/graft/subnet-evm/ethclient/simulated/backend_test.go b/graft/subnet-evm/ethclient/simulated/backend_test.go index b1f10fcfa23a..662df2a6a6bf 100644 --- a/graft/subnet-evm/ethclient/simulated/backend_test.go +++ b/graft/subnet-evm/ethclient/simulated/backend_test.go @@ -36,10 +36,10 @@ import ( "testing" "time" + "github.com/ava-labs/avalanchego/graft/evm/rpc" "github.com/ava-labs/avalanchego/graft/subnet-evm/accounts/abi/bind" "github.com/ava-labs/avalanchego/graft/subnet-evm/params" "github.com/ava-labs/avalanchego/graft/subnet-evm/plugin/evm/customtypes" - "github.com/ava-labs/avalanchego/graft/subnet-evm/rpc" "github.com/ava-labs/libevm/common" "github.com/ava-labs/libevm/core/types" "github.com/ava-labs/libevm/crypto" diff --git a/graft/subnet-evm/ethclient/subnetevmclient/subnet_evm_client.go b/graft/subnet-evm/ethclient/subnetevmclient/subnet_evm_client.go index 794b82e40f01..fd0b08ca9c1e 100644 --- a/graft/subnet-evm/ethclient/subnetevmclient/subnet_evm_client.go +++ b/graft/subnet-evm/ethclient/subnetevmclient/subnet_evm_client.go @@ -34,8 +34,8 @@ import ( "runtime" "runtime/debug" + "github.com/ava-labs/avalanchego/graft/evm/rpc" "github.com/ava-labs/avalanchego/graft/subnet-evm/ethclient" - "github.com/ava-labs/avalanchego/graft/subnet-evm/rpc" ethereum "github.com/ava-labs/libevm" "github.com/ava-labs/libevm/common" "github.com/ava-labs/libevm/common/hexutil" diff --git a/graft/subnet-evm/go.mod b/graft/subnet-evm/go.mod index d7c3bf9946db..3dbf70b99807 100644 --- a/graft/subnet-evm/go.mod +++ b/graft/subnet-evm/go.mod @@ -20,10 +20,8 @@ require ( github.com/ava-labs/firewood-go-ethhash/ffi v0.0.18 github.com/ava-labs/libevm v1.13.15-0.20251210210615-b8e76562a300 github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc - github.com/deckarep/golang-set/v2 v2.1.0 github.com/go-cmd/cmd v1.4.3 github.com/gorilla/rpc v1.2.0 - github.com/gorilla/websocket v1.5.0 github.com/hashicorp/go-bexpr v0.1.10 github.com/hashicorp/golang-lru v0.5.5-0.20210104140557-80c98217689d github.com/holiman/billy v0.0.0-20240216141850-2abb0c79d3c4 @@ -44,7 +42,6 @@ require ( golang.org/x/crypto v0.45.0 golang.org/x/exp v0.0.0-20241215155358-4a5509556b9e golang.org/x/sync v0.18.0 - golang.org/x/time v0.12.0 google.golang.org/protobuf v1.36.8 gopkg.in/natefinch/lumberjack.v2 v2.0.0 ) @@ -74,6 +71,7 @@ require ( github.com/cpuguy83/go-md2man/v2 v2.0.6 // indirect github.com/crate-crypto/go-ipa v0.0.0-20231025140028-3c0104f4b233 // indirect github.com/crate-crypto/go-kzg-4844 v1.1.0 // indirect + github.com/deckarep/golang-set/v2 v2.1.0 // indirect github.com/decred/dcrd/dcrec/secp256k1/v4 v4.4.0 // indirect github.com/deepmap/oapi-codegen v1.6.0 // indirect github.com/distribution/reference v0.5.0 // indirect @@ -109,6 +107,7 @@ require ( github.com/google/renameio/v2 v2.0.0 // indirect github.com/google/uuid v1.6.0 // indirect github.com/gorilla/mux v1.8.0 // indirect + github.com/gorilla/websocket v1.5.0 // indirect github.com/graph-gophers/graphql-go v1.3.0 // indirect github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0 // indirect github.com/grpc-ecosystem/grpc-gateway/v2 v2.27.1 // indirect @@ -181,6 +180,7 @@ require ( golang.org/x/sys v0.38.0 // indirect golang.org/x/term v0.37.0 // indirect golang.org/x/text v0.31.0 // indirect + golang.org/x/time v0.12.0 // indirect golang.org/x/tools v0.38.0 // indirect gonum.org/v1/gonum v0.16.0 // indirect google.golang.org/genproto/googleapis/api v0.0.0-20250707201910-8d1bb00bc6a7 // indirect @@ -210,3 +210,5 @@ tool ( replace github.com/ava-labs/avalanchego => ../../ replace github.com/ava-labs/avalanchego/graft/evm => ../evm + +replace github.com/ava-labs/avalanchego/graft/coreth => ../coreth diff --git a/graft/subnet-evm/go.sum b/graft/subnet-evm/go.sum index 5f623548adb5..0c555e63e3fc 100644 --- a/graft/subnet-evm/go.sum +++ b/graft/subnet-evm/go.sum @@ -5,8 +5,8 @@ connectrpc.com/grpcreflect v1.3.0 h1:Y4V+ACf8/vOb1XOc251Qun7jMB75gCUNw6llvB9csXc connectrpc.com/grpcreflect v1.3.0/go.mod h1:nfloOtCS8VUQOQ1+GTdFzVg2CJo4ZGaat8JIovCtDYs= github.com/AndreasBriese/bbloom v0.0.0-20190306092124-e2d15f34fcf9/go.mod h1:bOvUY6CB00SOBii9/FifXqc0awNKxLFCL/+pkDPuyl8= github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= -github.com/BurntSushi/toml v1.5.0 h1:W5quZX/G/csjUnuI8SUYlsHs9M38FC7znL0lIO+DvMg= -github.com/BurntSushi/toml v1.5.0/go.mod h1:ukJfTF/6rtPPRCnwkur4qwRxa8vTRFBF0uk2lLoLwho= +github.com/BurntSushi/toml v1.3.2 h1:o7IhLm0Msx3BaB+n3Ag7L8EVlByGnpq14C4YWiu/gL8= +github.com/BurntSushi/toml v1.3.2/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ= github.com/CloudyKit/fastprinter v0.0.0-20200109182630-33d98a066a53/go.mod h1:+3IMCy2vIlbG1XG/0ggNQv0SvxCAIpPM5b1nCz56Xno= github.com/CloudyKit/jet/v3 v3.0.0/go.mod h1:HKQPgSJmdK8hdoAbKUUWajkHyHo4RaU5rMdUywE7VMo= github.com/DataDog/zstd v1.5.2 h1:vUG4lAyuPCXO0TLbXvPv7EB7cNK1QV/luu55UHLrrn8= @@ -28,8 +28,6 @@ github.com/antithesishq/antithesis-sdk-go v0.3.8/go.mod h1:IUpT2DPAKh6i/YhSbt6Gl github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5doyWs3UAsr3K4I6qtAmlQcZDesFNEHPZAzj8= github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5 h1:0CwZNZbxp69SHPdPJAN/hZIm0C4OItdklCFmMRWYpio= github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5/go.mod h1:wHh0iHkYZB8zMSxRWpUBQtwG5a7fFgvEO+odwuTv2gs= -github.com/ava-labs/avalanchego/graft/coreth v0.0.0-20251203215505-70148edc6eca h1:zZIQZhOqKe82SUvEx7IeRVoahjyKI0gfouHPQkvEHeI= -github.com/ava-labs/avalanchego/graft/coreth v0.0.0-20251203215505-70148edc6eca/go.mod h1:y+/5DAxCTLAXdWRxAYN1V8DV0DIF7uHhOOeNa9oASuU= github.com/ava-labs/firewood-go-ethhash/ffi v0.0.18 h1:Lk4yxNL3iZMRxKZlTKVCHp0Rg7i5QclRei0ZKCgtPac= github.com/ava-labs/firewood-go-ethhash/ffi v0.0.18/go.mod h1:hR/JSGXxST9B9olwu/NpLXHAykfAyNGfyKnYQqiiOeE= github.com/ava-labs/libevm v1.13.15-0.20251210210615-b8e76562a300 h1:9VRvqASGSAnQ9tKVRKGH8Q0Yq8efCwYTBWp0p2creho= diff --git a/graft/subnet-evm/internal/ethapi/api.go b/graft/subnet-evm/internal/ethapi/api.go index 8679726e6aa3..19e2450ad787 100644 --- a/graft/subnet-evm/internal/ethapi/api.go +++ b/graft/subnet-evm/internal/ethapi/api.go @@ -37,12 +37,12 @@ import ( "time" "github.com/ava-labs/avalanchego/graft/evm/firewood" + "github.com/ava-labs/avalanchego/graft/evm/rpc" "github.com/ava-labs/avalanchego/graft/subnet-evm/consensus" "github.com/ava-labs/avalanchego/graft/subnet-evm/core" "github.com/ava-labs/avalanchego/graft/subnet-evm/eth/gasestimator" "github.com/ava-labs/avalanchego/graft/subnet-evm/params" "github.com/ava-labs/avalanchego/graft/subnet-evm/plugin/evm/customtypes" - "github.com/ava-labs/avalanchego/graft/subnet-evm/rpc" "github.com/ava-labs/libevm/accounts" "github.com/ava-labs/libevm/accounts/keystore" "github.com/ava-labs/libevm/accounts/scwallet" diff --git a/graft/subnet-evm/internal/ethapi/api_extra.go b/graft/subnet-evm/internal/ethapi/api_extra.go index 2679a6709dd3..af2dfd31dafe 100644 --- a/graft/subnet-evm/internal/ethapi/api_extra.go +++ b/graft/subnet-evm/internal/ethapi/api_extra.go @@ -14,11 +14,11 @@ import ( "github.com/ava-labs/libevm/core/types" "github.com/ava-labs/libevm/rlp" + "github.com/ava-labs/avalanchego/graft/evm/rpc" "github.com/ava-labs/avalanchego/graft/subnet-evm/commontype" "github.com/ava-labs/avalanchego/graft/subnet-evm/core" "github.com/ava-labs/avalanchego/graft/subnet-evm/params" "github.com/ava-labs/avalanchego/graft/subnet-evm/params/extras" - "github.com/ava-labs/avalanchego/graft/subnet-evm/rpc" ) func (s *BlockChainAPI) GetChainConfig(context.Context) *params.ChainConfigWithUpgradesJSON { diff --git a/graft/subnet-evm/internal/ethapi/api_extra_test.go b/graft/subnet-evm/internal/ethapi/api_extra_test.go index a76760e99196..f359f4ca5afd 100644 --- a/graft/subnet-evm/internal/ethapi/api_extra_test.go +++ b/graft/subnet-evm/internal/ethapi/api_extra_test.go @@ -15,11 +15,11 @@ import ( "github.com/stretchr/testify/require" "go.uber.org/mock/gomock" + "github.com/ava-labs/avalanchego/graft/evm/rpc" "github.com/ava-labs/avalanchego/graft/subnet-evm/consensus/dummy" "github.com/ava-labs/avalanchego/graft/subnet-evm/core" "github.com/ava-labs/avalanchego/graft/subnet-evm/params" "github.com/ava-labs/avalanchego/graft/subnet-evm/plugin/evm/customtypes" - "github.com/ava-labs/avalanchego/graft/subnet-evm/rpc" ethparams "github.com/ava-labs/libevm/params" ) diff --git a/graft/subnet-evm/internal/ethapi/api_test.go b/graft/subnet-evm/internal/ethapi/api_test.go index 27385bf86d54..c17b12a55e76 100644 --- a/graft/subnet-evm/internal/ethapi/api_test.go +++ b/graft/subnet-evm/internal/ethapi/api_test.go @@ -42,6 +42,7 @@ import ( "testing" "time" + "github.com/ava-labs/avalanchego/graft/evm/rpc" "github.com/ava-labs/avalanchego/graft/subnet-evm/commontype" "github.com/ava-labs/avalanchego/graft/subnet-evm/consensus" "github.com/ava-labs/avalanchego/graft/subnet-evm/consensus/dummy" @@ -49,7 +50,6 @@ import ( "github.com/ava-labs/avalanchego/graft/subnet-evm/internal/blocktest" "github.com/ava-labs/avalanchego/graft/subnet-evm/params" "github.com/ava-labs/avalanchego/graft/subnet-evm/plugin/evm/upgrade/legacy" - "github.com/ava-labs/avalanchego/graft/subnet-evm/rpc" "github.com/ava-labs/avalanchego/upgrade" "github.com/ava-labs/avalanchego/utils" "github.com/ava-labs/libevm/accounts" diff --git a/graft/subnet-evm/internal/ethapi/backend.go b/graft/subnet-evm/internal/ethapi/backend.go index ba55365e1076..a3f0694bbe16 100644 --- a/graft/subnet-evm/internal/ethapi/backend.go +++ b/graft/subnet-evm/internal/ethapi/backend.go @@ -33,11 +33,11 @@ import ( "math/big" "time" + "github.com/ava-labs/avalanchego/graft/evm/rpc" "github.com/ava-labs/avalanchego/graft/subnet-evm/commontype" "github.com/ava-labs/avalanchego/graft/subnet-evm/consensus" "github.com/ava-labs/avalanchego/graft/subnet-evm/core" "github.com/ava-labs/avalanchego/graft/subnet-evm/params" - "github.com/ava-labs/avalanchego/graft/subnet-evm/rpc" "github.com/ava-labs/libevm/accounts" "github.com/ava-labs/libevm/common" "github.com/ava-labs/libevm/core/bloombits" diff --git a/graft/subnet-evm/internal/ethapi/mocks_test.go b/graft/subnet-evm/internal/ethapi/mocks_test.go index b541034bfa5d..b33696800875 100644 --- a/graft/subnet-evm/internal/ethapi/mocks_test.go +++ b/graft/subnet-evm/internal/ethapi/mocks_test.go @@ -15,11 +15,11 @@ import ( reflect "reflect" time "time" + rpc "github.com/ava-labs/avalanchego/graft/evm/rpc" commontype "github.com/ava-labs/avalanchego/graft/subnet-evm/commontype" consensus "github.com/ava-labs/avalanchego/graft/subnet-evm/consensus" core "github.com/ava-labs/avalanchego/graft/subnet-evm/core" params "github.com/ava-labs/avalanchego/graft/subnet-evm/params" - rpc "github.com/ava-labs/avalanchego/graft/subnet-evm/rpc" accounts "github.com/ava-labs/libevm/accounts" common "github.com/ava-labs/libevm/common" bloombits "github.com/ava-labs/libevm/core/bloombits" diff --git a/graft/subnet-evm/internal/ethapi/transaction_args.go b/graft/subnet-evm/internal/ethapi/transaction_args.go index a226e86bc11f..39b87bec655e 100644 --- a/graft/subnet-evm/internal/ethapi/transaction_args.go +++ b/graft/subnet-evm/internal/ethapi/transaction_args.go @@ -35,9 +35,9 @@ import ( "fmt" "math/big" + "github.com/ava-labs/avalanchego/graft/evm/rpc" "github.com/ava-labs/avalanchego/graft/subnet-evm/core" "github.com/ava-labs/avalanchego/graft/subnet-evm/params" - "github.com/ava-labs/avalanchego/graft/subnet-evm/rpc" "github.com/ava-labs/libevm/common" "github.com/ava-labs/libevm/common/hexutil" "github.com/ava-labs/libevm/common/math" diff --git a/graft/subnet-evm/node/api.go b/graft/subnet-evm/node/api.go index b95bd37d2a39..72c484892f37 100644 --- a/graft/subnet-evm/node/api.go +++ b/graft/subnet-evm/node/api.go @@ -28,8 +28,8 @@ package node import ( + "github.com/ava-labs/avalanchego/graft/evm/rpc" "github.com/ava-labs/avalanchego/graft/subnet-evm/internal/debug" - "github.com/ava-labs/avalanchego/graft/subnet-evm/rpc" "github.com/ava-labs/libevm/common/hexutil" "github.com/ava-labs/libevm/crypto" ) diff --git a/graft/subnet-evm/node/node.go b/graft/subnet-evm/node/node.go index bf6a8c9f0968..d5f8f65f022b 100644 --- a/graft/subnet-evm/node/node.go +++ b/graft/subnet-evm/node/node.go @@ -28,7 +28,7 @@ package node import ( - "github.com/ava-labs/avalanchego/graft/subnet-evm/rpc" + "github.com/ava-labs/avalanchego/graft/evm/rpc" "github.com/ava-labs/libevm/accounts" ) diff --git a/graft/subnet-evm/plugin/evm/extras/extras.go b/graft/subnet-evm/plugin/evm/extras/extras.go new file mode 100644 index 000000000000..7bbbddb53fce --- /dev/null +++ b/graft/subnet-evm/plugin/evm/extras/extras.go @@ -0,0 +1,47 @@ +// Copyright (C) 2019, Ava Labs, Inc. All rights reserved. +// See the file LICENSE for licensing terms. + +// Package extras provides libevm type registration for Subnet-EVM behaviour. +// This package is intentionally kept separate from the main evm package to +// avoid import cycles - the evm package imports rpc, but many packages need +// to register extras without importing the full evm package. +package extras + +import ( + "github.com/ava-labs/libevm/libevm" + + "github.com/ava-labs/avalanchego/graft/subnet-evm/core" + "github.com/ava-labs/avalanchego/graft/subnet-evm/params" + "github.com/ava-labs/avalanchego/graft/subnet-evm/plugin/evm/customtypes" +) + +// RegisterAllLibEVMExtras is a convenience wrapper for calling +// [core.RegisterExtras], [customtypes.Register], and [params.RegisterExtras]. +// Together these are necessary and sufficient for configuring libevm for +// Subnet-EVM behaviour. +// +// It MUST NOT be called more than once and therefore is only allowed to be used +// in tests and `package main`, to avoid polluting other packages that +// transitively depend on this one but don't need registration. +func RegisterAllLibEVMExtras() { + core.RegisterExtras() + customtypes.Register() + params.RegisterExtras() +} + +// WithTempRegisteredLibEVMExtras runs `fn` with temporary registration +// otherwise equivalent to a call to [RegisterAllLibEVMExtras], but limited to +// the life of `fn`. +func WithTempRegisteredLibEVMExtras(fn func() error) error { + return libevm.WithTemporaryExtrasLock(func(lock libevm.ExtrasLock) error { + for _, wrap := range []func(libevm.ExtrasLock, func() error) error{ + core.WithTempRegisteredExtras, + customtypes.WithTempRegisteredExtras, + params.WithTempRegisteredExtras, + } { + inner := fn + fn = func() error { return wrap(lock, inner) } + } + return fn() + }) +} diff --git a/graft/subnet-evm/plugin/evm/libevm.go b/graft/subnet-evm/plugin/evm/libevm.go index 1267e80eeb79..01e482c4facd 100644 --- a/graft/subnet-evm/plugin/evm/libevm.go +++ b/graft/subnet-evm/plugin/evm/libevm.go @@ -3,41 +3,23 @@ package evm -import ( - "github.com/ava-labs/libevm/libevm" - - "github.com/ava-labs/avalanchego/graft/subnet-evm/core" - "github.com/ava-labs/avalanchego/graft/subnet-evm/params" - "github.com/ava-labs/avalanchego/graft/subnet-evm/plugin/evm/customtypes" -) +import "github.com/ava-labs/avalanchego/graft/subnet-evm/plugin/evm/extras" // RegisterAllLibEVMExtras is a convenience wrapper for calling // [core.RegisterExtras], [customtypes.Register], and [params.RegisterExtras]. // Together these are necessary and sufficient for configuring libevm for -// C-Chain behaviour. +// Subnet-EVM behaviour. // // It MUST NOT be called more than once and therefore is only allowed to be used // in tests and `package main`, to avoid polluting other packages that // transitively depend on this one but don't need registration. func RegisterAllLibEVMExtras() { - core.RegisterExtras() - customtypes.Register() - params.RegisterExtras() + extras.RegisterAllLibEVMExtras() } // WithTempRegisteredLibEVMExtras runs `fn` with temporary registration // otherwise equivalent to a call to [RegisterAllLibEVMExtras], but limited to // the life of `fn`. func WithTempRegisteredLibEVMExtras(fn func() error) error { - return libevm.WithTemporaryExtrasLock(func(lock libevm.ExtrasLock) error { - for _, wrap := range []func(libevm.ExtrasLock, func() error) error{ - core.WithTempRegisteredExtras, - customtypes.WithTempRegisteredExtras, - params.WithTempRegisteredExtras, - } { - inner := fn - fn = func() error { return wrap(lock, inner) } - } - return fn() - }) + return extras.WithTempRegisteredLibEVMExtras(fn) } diff --git a/graft/subnet-evm/plugin/evm/vm.go b/graft/subnet-evm/plugin/evm/vm.go index a1e506c979f4..72a0422985cc 100644 --- a/graft/subnet-evm/plugin/evm/vm.go +++ b/graft/subnet-evm/plugin/evm/vm.go @@ -42,6 +42,7 @@ import ( "github.com/ava-labs/avalanchego/database" "github.com/ava-labs/avalanchego/database/versiondb" "github.com/ava-labs/avalanchego/graft/evm/constants" + "github.com/ava-labs/avalanchego/graft/evm/rpc" "github.com/ava-labs/avalanchego/graft/evm/triedb/hashdb" "github.com/ava-labs/avalanchego/graft/subnet-evm/commontype" "github.com/ava-labs/avalanchego/graft/subnet-evm/consensus/dummy" @@ -58,7 +59,6 @@ import ( "github.com/ava-labs/avalanchego/graft/subnet-evm/plugin/evm/extension" "github.com/ava-labs/avalanchego/graft/subnet-evm/plugin/evm/message" "github.com/ava-labs/avalanchego/graft/subnet-evm/precompile/precompileconfig" - "github.com/ava-labs/avalanchego/graft/subnet-evm/rpc" "github.com/ava-labs/avalanchego/graft/subnet-evm/sync/client/stats" "github.com/ava-labs/avalanchego/graft/subnet-evm/sync/handlers" "github.com/ava-labs/avalanchego/graft/subnet-evm/warp" diff --git a/graft/subnet-evm/plugin/evm/vm_test.go b/graft/subnet-evm/plugin/evm/vm_test.go index 20a94952a2cd..48e70df6adb6 100644 --- a/graft/subnet-evm/plugin/evm/vm_test.go +++ b/graft/subnet-evm/plugin/evm/vm_test.go @@ -31,6 +31,7 @@ import ( "github.com/ava-labs/avalanchego/database/memdb" "github.com/ava-labs/avalanchego/database/prefixdb" "github.com/ava-labs/avalanchego/graft/evm/constants" + "github.com/ava-labs/avalanchego/graft/evm/rpc" "github.com/ava-labs/avalanchego/graft/evm/utils" "github.com/ava-labs/avalanchego/graft/evm/utils/utilstest" "github.com/ava-labs/avalanchego/graft/subnet-evm/commontype" @@ -52,7 +53,6 @@ import ( "github.com/ava-labs/avalanchego/graft/subnet-evm/precompile/contracts/feemanager" "github.com/ava-labs/avalanchego/graft/subnet-evm/precompile/contracts/rewardmanager" "github.com/ava-labs/avalanchego/graft/subnet-evm/precompile/contracts/txallowlist" - "github.com/ava-labs/avalanchego/graft/subnet-evm/rpc" "github.com/ava-labs/avalanchego/ids" "github.com/ava-labs/avalanchego/snow" "github.com/ava-labs/avalanchego/snow/consensus/snowman" diff --git a/graft/subnet-evm/rpc/client.go b/graft/subnet-evm/rpc/client.go deleted file mode 100644 index a32ca9012b1e..000000000000 --- a/graft/subnet-evm/rpc/client.go +++ /dev/null @@ -1,741 +0,0 @@ -// Copyright (C) 2019, Ava Labs, Inc. All rights reserved. -// See the file LICENSE for licensing terms. -// -// This file is a derived work, based on the go-ethereum library whose original -// notices appear below. -// -// It is distributed under a license compatible with the licensing terms of the -// original code from which it is derived. -// -// Much love to the original authors for their work. -// ********** -// Copyright 2016 The go-ethereum Authors -// This file is part of the go-ethereum library. -// -// The go-ethereum library is free software: you can redistribute it and/or modify -// it under the terms of the GNU Lesser General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// The go-ethereum library is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU Lesser General Public License for more details. -// -// You should have received a copy of the GNU Lesser General Public License -// along with the go-ethereum library. If not, see . - -package rpc - -import ( - "context" - "encoding/json" - "errors" - "fmt" - "net/url" - "reflect" - "strconv" - "sync/atomic" - "time" - - "github.com/ava-labs/libevm/log" -) - -var ( - ErrBadResult = errors.New("bad result in JSON-RPC response") - ErrClientQuit = errors.New("client is closed") - ErrNoResult = errors.New("JSON-RPC response has no result") - ErrMissingBatchResponse = errors.New("response batch did not contain a response to this call") - ErrSubscriptionQueueOverflow = errors.New("subscription queue overflow") - errClientReconnected = errors.New("client reconnected") - errDead = errors.New("connection lost") -) - -// Timeouts -const ( - defaultDialTimeout = 10 * time.Second // used if context has no deadline - subscribeTimeout = 10 * time.Second // overall timeout eth_subscribe, rpc_modules calls -) - -const ( - // Subscriptions are removed when the subscriber cannot keep up. - // - // This can be worked around by supplying a channel with sufficiently sized buffer, - // but this can be inconvenient and hard to explain in the docs. Another issue with - // buffered channels is that the buffer is static even though it might not be needed - // most of the time. - // - // The approach taken here is to maintain a per-subscription linked list buffer - // shrinks on demand. If the buffer reaches the size below, the subscription is - // dropped. - maxClientSubscriptionBuffer = 20000 -) - -// BatchElem is an element in a batch request. -type BatchElem struct { - Method string - Args []interface{} - // The result is unmarshaled into this field. Result must be set to a - // non-nil pointer value of the desired type, otherwise the response will be - // discarded. - Result interface{} - // Error is set if the server returns an error for this request, or if - // unmarshaling into Result fails. It is not set for I/O errors. - Error error -} - -// Client represents a connection to an RPC server. -type Client struct { - idgen func() ID // for subscriptions - isHTTP bool // isHTTP specifies if the client uses an HTTP connection - services *serviceRegistry - - idCounter atomic.Uint32 - - // This function, if non-nil, is called when the connection is lost. - reconnectFunc reconnectFunc - - // config fields - batchItemLimit int - batchResponseMaxSize int - - // writeConn is used for writing to the connection on the caller's goroutine. It should - // only be accessed outside of dispatch, with the write lock held. The write lock is - // taken by sending on reqInit and released by sending on reqSent. - writeConn jsonWriter - - // for dispatch - close chan struct{} - closing chan struct{} // closed when client is quitting - didClose chan struct{} // closed when client quits - reconnected chan ServerCodec // where write/reconnect sends the new connection - readOp chan readOp // read messages - readErr chan error // errors from read - reqInit chan *requestOp // register response IDs, takes write lock - reqSent chan error // signals write completion, releases write lock - reqTimeout chan *requestOp // removes response IDs when call timeout expires -} - -type reconnectFunc func(context.Context) (ServerCodec, error) - -type clientContextKey struct{} - -type clientConn struct { - codec ServerCodec - handler *handler -} - -func (c *Client) newClientConn(conn ServerCodec, apiMaxDuration, refillRate, maxStored time.Duration) *clientConn { - ctx := context.Background() - ctx = context.WithValue(ctx, clientContextKey{}, c) - ctx = context.WithValue(ctx, peerInfoContextKey{}, conn.peerInfo()) - handler := newHandler(ctx, conn, c.idgen, c.services, c.batchItemLimit, c.batchResponseMaxSize) - - // When [apiMaxDuration] or [refillRate]/[maxStored] is 0 (as is the case for - // all client invocations of this function), it is ignored. - handler.deadlineContext = apiMaxDuration - handler.addLimiter(refillRate, maxStored) - return &clientConn{conn, handler} -} - -func (cc *clientConn) close(err error, inflightReq *requestOp) { - cc.handler.close(err, inflightReq) - cc.codec.close() -} - -type readOp struct { - msgs []*jsonrpcMessage - batch bool -} - -// requestOp represents a pending request. This is used for both batch and non-batch -// requests. -type requestOp struct { - ids []json.RawMessage - err error - resp chan []*jsonrpcMessage // the response goes here - sub *ClientSubscription // set for Subscribe requests. - hadResponse bool // true when the request was responded to -} - -func (op *requestOp) wait(ctx context.Context, c *Client) ([]*jsonrpcMessage, error) { - select { - case <-ctx.Done(): - // Send the timeout to dispatch so it can remove the request IDs. - if !c.isHTTP { - select { - case c.reqTimeout <- op: - case <-c.closing: - } - } - return nil, ctx.Err() - case resp := <-op.resp: - return resp, op.err - } -} - -// Dial creates a new client for the given URL. -// -// The currently supported URL schemes are "http", "https", "ws" and "wss". If rawurl is a -// file name with no URL scheme, a local socket connection is established using UNIX -// domain sockets on supported platforms and named pipes on Windows. -// -// If you want to further configure the transport, use DialOptions instead of this -// function. -// -// For websocket connections, the origin is set to the local host name. -// -// The client reconnects automatically when the connection is lost. -func Dial(rawurl string) (*Client, error) { - return DialOptions(context.Background(), rawurl) -} - -// DialContext creates a new RPC client, just like Dial. -// -// The context is used to cancel or time out the initial connection establishment. It does -// not affect subsequent interactions with the client. -func DialContext(ctx context.Context, rawurl string) (*Client, error) { - return DialOptions(ctx, rawurl) -} - -// DialOptions creates a new RPC client for the given URL. You can supply any of the -// pre-defined client options to configure the underlying transport. -// -// The context is used to cancel or time out the initial connection establishment. It does -// not affect subsequent interactions with the client. -// -// The client reconnects automatically when the connection is lost. -func DialOptions(ctx context.Context, rawurl string, options ...ClientOption) (*Client, error) { - u, err := url.Parse(rawurl) - if err != nil { - return nil, err - } - - cfg := new(clientConfig) - for _, opt := range options { - opt.applyOption(cfg) - } - - var reconnect reconnectFunc - switch u.Scheme { - case "http", "https": - reconnect = newClientTransportHTTP(rawurl, cfg) - case "ws", "wss": - rc, err := newClientTransportWS(rawurl, cfg) - if err != nil { - return nil, err - } - reconnect = rc - //case "stdio": - //reconnect = newClientTransportIO(os.Stdin, os.Stdout) - //case "": - //reconnect = newClientTransportIPC(rawurl) - default: - return nil, fmt.Errorf("no known transport for URL scheme %q", u.Scheme) - } - - return newClient(ctx, cfg, reconnect) -} - -// ClientFromContext retrieves the client from the context, if any. This can be used to perform -// 'reverse calls' in a handler method. -func ClientFromContext(ctx context.Context) (*Client, bool) { - client, ok := ctx.Value(clientContextKey{}).(*Client) - return client, ok -} - -func newClient(initctx context.Context, cfg *clientConfig, connect reconnectFunc) (*Client, error) { - conn, err := connect(initctx) - if err != nil { - return nil, err - } - c := initClient(conn, new(serviceRegistry), cfg, 0, 0, 0) - c.reconnectFunc = connect - return c, nil -} - -func initClient(conn ServerCodec, services *serviceRegistry, cfg *clientConfig, apiMaxDuration, refillRate, maxStored time.Duration) *Client { - _, isHTTP := conn.(*httpConn) - c := &Client{ - isHTTP: isHTTP, - services: services, - idgen: cfg.idgen, - batchItemLimit: cfg.batchItemLimit, - batchResponseMaxSize: cfg.batchResponseLimit, - writeConn: conn, - close: make(chan struct{}), - closing: make(chan struct{}), - didClose: make(chan struct{}), - reconnected: make(chan ServerCodec), - readOp: make(chan readOp), - readErr: make(chan error), - reqInit: make(chan *requestOp), - reqSent: make(chan error, 1), - reqTimeout: make(chan *requestOp), - } - - // Set defaults. - if c.idgen == nil { - c.idgen = randomIDGenerator() - } - - // Launch the main loop. - if !isHTTP { - go c.dispatch(conn, apiMaxDuration, refillRate, maxStored) - } - return c -} - -// RegisterName creates a service for the given receiver type under the given name. When no -// methods on the given receiver match the criteria to be either a RPC method or a -// subscription an error is returned. Otherwise a new service is created and added to the -// service collection this client provides to the server. -func (c *Client) RegisterName(name string, receiver interface{}) error { - return c.services.registerName(name, receiver) -} - -func (c *Client) nextID() json.RawMessage { - id := c.idCounter.Add(1) - return strconv.AppendUint(nil, uint64(id), 10) -} - -// SupportedModules calls the rpc_modules method, retrieving the list of -// APIs that are available on the server. -func (c *Client) SupportedModules() (map[string]string, error) { - var result map[string]string - ctx, cancel := context.WithTimeout(context.Background(), subscribeTimeout) - defer cancel() - err := c.CallContext(ctx, &result, "rpc_modules") - return result, err -} - -// Close closes the client, aborting any in-flight requests. -func (c *Client) Close() { - if c.isHTTP { - return - } - select { - case c.close <- struct{}{}: - <-c.didClose - case <-c.didClose: - } -} - -// SetHeader adds a custom HTTP header to the client's requests. -// This method only works for clients using HTTP, it doesn't have -// any effect for clients using another transport. -func (c *Client) SetHeader(key, value string) { - if !c.isHTTP { - return - } - conn := c.writeConn.(*httpConn) - conn.mu.Lock() - conn.headers.Set(key, value) - conn.mu.Unlock() -} - -// Call performs a JSON-RPC call with the given arguments and unmarshals into -// result if no error occurred. -// -// The result must be a pointer so that package json can unmarshal into it. You -// can also pass nil, in which case the result is ignored. -func (c *Client) Call(result interface{}, method string, args ...interface{}) error { - ctx := context.Background() - return c.CallContext(ctx, result, method, args...) -} - -// CallContext performs a JSON-RPC call with the given arguments. If the context is -// canceled before the call has successfully returned, CallContext returns immediately. -// -// The result must be a pointer so that package json can unmarshal into it. You -// can also pass nil, in which case the result is ignored. -func (c *Client) CallContext(ctx context.Context, result interface{}, method string, args ...interface{}) error { - if result != nil && reflect.TypeOf(result).Kind() != reflect.Ptr { - return fmt.Errorf("call result parameter must be pointer or nil interface: %v", result) - } - msg, err := c.newMessage(method, args...) - if err != nil { - return err - } - op := &requestOp{ - ids: []json.RawMessage{msg.ID}, - resp: make(chan []*jsonrpcMessage, 1), - } - - if c.isHTTP { - err = c.sendHTTP(ctx, op, msg) - } else { - err = c.send(ctx, op, msg) - } - if err != nil { - return err - } - - // dispatch has accepted the request and will close the channel when it quits. - batchresp, err := op.wait(ctx, c) - if err != nil { - return err - } - resp := batchresp[0] - switch { - case resp.Error != nil: - return resp.Error - case len(resp.Result) == 0: - return ErrNoResult - default: - if result == nil { - return nil - } - return json.Unmarshal(resp.Result, result) - } -} - -// BatchCall sends all given requests as a single batch and waits for the server -// to return a response for all of them. -// -// In contrast to Call, BatchCall only returns I/O errors. Any error specific to -// a request is reported through the Error field of the corresponding BatchElem. -// -// Note that batch calls may not be executed atomically on the server side. -func (c *Client) BatchCall(b []BatchElem) error { - ctx := context.Background() - return c.BatchCallContext(ctx, b) -} - -// BatchCallContext sends all given requests as a single batch and waits for the server -// to return a response for all of them. The wait duration is bounded by the -// context's deadline. -// -// In contrast to CallContext, BatchCallContext only returns errors that have occurred -// while sending the request. Any error specific to a request is reported through the -// Error field of the corresponding BatchElem. -// -// Note that batch calls may not be executed atomically on the server side. -func (c *Client) BatchCallContext(ctx context.Context, b []BatchElem) error { - var ( - msgs = make([]*jsonrpcMessage, len(b)) - byID = make(map[string]int, len(b)) - ) - op := &requestOp{ - ids: make([]json.RawMessage, len(b)), - resp: make(chan []*jsonrpcMessage, 1), - } - for i, elem := range b { - msg, err := c.newMessage(elem.Method, elem.Args...) - if err != nil { - return err - } - msgs[i] = msg - op.ids[i] = msg.ID - byID[string(msg.ID)] = i - } - - var err error - if c.isHTTP { - err = c.sendBatchHTTP(ctx, op, msgs) - } else { - err = c.send(ctx, op, msgs) - } - if err != nil { - return err - } - - batchresp, err := op.wait(ctx, c) - if err != nil { - return err - } - - // Wait for all responses to come back. - for n := 0; n < len(batchresp) && err == nil; n++ { - resp := batchresp[n] - if resp == nil { - // Ignore null responses. These can happen for batches sent via HTTP. - continue - } - - // Find the element corresponding to this response. - index, ok := byID[string(resp.ID)] - if !ok { - continue - } - delete(byID, string(resp.ID)) - - // Assign result and error. - elem := &b[index] - switch { - case resp.Error != nil: - elem.Error = resp.Error - case resp.Result == nil: - elem.Error = ErrNoResult - default: - elem.Error = json.Unmarshal(resp.Result, elem.Result) - } - } - - // Check that all expected responses have been received. - for _, index := range byID { - elem := &b[index] - elem.Error = ErrMissingBatchResponse - } - - return err -} - -// Notify sends a notification, i.e. a method call that doesn't expect a response. -func (c *Client) Notify(ctx context.Context, method string, args ...interface{}) error { - op := new(requestOp) - msg, err := c.newMessage(method, args...) - if err != nil { - return err - } - msg.ID = nil - - if c.isHTTP { - return c.sendHTTP(ctx, op, msg) - } - return c.send(ctx, op, msg) -} - -// EthSubscribe registers a subscription under the "eth" namespace. -func (c *Client) EthSubscribe(ctx context.Context, channel interface{}, args ...interface{}) (*ClientSubscription, error) { - return c.Subscribe(ctx, "eth", channel, args...) -} - -// ShhSubscribe registers a subscription under the "shh" namespace. -// Deprecated: use Subscribe(ctx, "shh", ...). -func (c *Client) ShhSubscribe(ctx context.Context, channel interface{}, args ...interface{}) (*ClientSubscription, error) { - return c.Subscribe(ctx, "shh", channel, args...) -} - -// Subscribe calls the "_subscribe" method with the given arguments, -// registering a subscription. Server notifications for the subscription are -// sent to the given channel. The element type of the channel must match the -// expected type of content returned by the subscription. -// -// The context argument cancels the RPC request that sets up the subscription but has no -// effect on the subscription after Subscribe has returned. -// -// Slow subscribers will be dropped eventually. Client buffers up to 20000 notifications -// before considering the subscriber dead. The subscription Err channel will receive -// ErrSubscriptionQueueOverflow. Use a sufficiently large buffer on the channel or ensure -// that the channel usually has at least one reader to prevent this issue. -func (c *Client) Subscribe(ctx context.Context, namespace string, channel interface{}, args ...interface{}) (*ClientSubscription, error) { - // Check type of channel first. - chanVal := reflect.ValueOf(channel) - if chanVal.Kind() != reflect.Chan || chanVal.Type().ChanDir()&reflect.SendDir == 0 { - panic(fmt.Sprintf("channel argument of Subscribe has type %T, need writable channel", channel)) - } - if chanVal.IsNil() { - panic("channel given to Subscribe must not be nil") - } - if c.isHTTP { - return nil, ErrNotificationsUnsupported - } - - msg, err := c.newMessage(namespace+subscribeMethodSuffix, args...) - if err != nil { - return nil, err - } - op := &requestOp{ - ids: []json.RawMessage{msg.ID}, - resp: make(chan []*jsonrpcMessage, 1), - sub: newClientSubscription(c, namespace, chanVal), - } - - // Send the subscription request. - // The arrival and validity of the response is signaled on sub.quit. - if err := c.send(ctx, op, msg); err != nil { - return nil, err - } - if _, err := op.wait(ctx, c); err != nil { - return nil, err - } - return op.sub, nil -} - -// SupportsSubscriptions reports whether subscriptions are supported by the client -// transport. When this returns false, Subscribe and related methods will return -// ErrNotificationsUnsupported. -func (c *Client) SupportsSubscriptions() bool { - return !c.isHTTP -} - -func (c *Client) newMessage(method string, paramsIn ...interface{}) (*jsonrpcMessage, error) { - msg := &jsonrpcMessage{Version: vsn, ID: c.nextID(), Method: method} - if paramsIn != nil { // prevent sending "params":null - var err error - if msg.Params, err = json.Marshal(paramsIn); err != nil { - return nil, err - } - } - return msg, nil -} - -// send registers op with the dispatch loop, then sends msg on the connection. -// if sending fails, op is deregistered. -func (c *Client) send(ctx context.Context, op *requestOp, msg interface{}) error { - select { - case c.reqInit <- op: - err := c.write(ctx, msg, false) - c.reqSent <- err - return err - case <-ctx.Done(): - // This can happen if the client is overloaded or unable to keep up with - // subscription notifications. - return ctx.Err() - case <-c.closing: - return ErrClientQuit - } -} - -func (c *Client) write(ctx context.Context, msg interface{}, retry bool) error { - if c.writeConn == nil { - // The previous write failed. Try to establish a new connection. - if err := c.reconnect(ctx); err != nil { - return err - } - } - err := c.writeConn.writeJSON(ctx, msg, false) - if err != nil { - c.writeConn = nil - if !retry { - return c.write(ctx, msg, true) - } - } - return err -} - -func (c *Client) reconnect(ctx context.Context) error { - if c.reconnectFunc == nil { - return errDead - } - - if _, ok := ctx.Deadline(); !ok { - var cancel func() - ctx, cancel = context.WithTimeout(ctx, defaultDialTimeout) - defer cancel() - } - newconn, err := c.reconnectFunc(ctx) - if err != nil { - log.Trace("RPC client reconnect failed", "err", err) - return err - } - select { - case c.reconnected <- newconn: - c.writeConn = newconn - return nil - case <-c.didClose: - newconn.close() - return ErrClientQuit - } -} - -// dispatch is the main loop of the client. -// It sends read messages to waiting calls to Call and BatchCall -// and subscription notifications to registered subscriptions. -func (c *Client) dispatch(codec ServerCodec, apiMaxDuration, refillRate, maxStored time.Duration) { - var ( - lastOp *requestOp // tracks last send operation - reqInitLock = c.reqInit // nil while the send lock is held - conn = c.newClientConn(codec, apiMaxDuration, refillRate, maxStored) - reading = true - ) - defer func() { - close(c.closing) - if reading { - conn.close(ErrClientQuit, nil) - c.drainRead() - } - close(c.didClose) - }() - - // Spawn the initial read loop. - go c.read(codec) - - for { - select { - case <-c.close: - return - - // Read path: - case op := <-c.readOp: - if op.batch { - conn.handler.handleBatch(op.msgs) - } else { - conn.handler.handleMsg(op.msgs[0]) - } - - case err := <-c.readErr: - conn.handler.log.Debug("RPC connection read error", "err", err) - conn.handler.cancelRoot() - conn.close(err, lastOp) - reading = false - - // Reconnect: - case newcodec := <-c.reconnected: - log.Debug("RPC client reconnected", "reading", reading, "conn", newcodec.remoteAddr()) - if reading { - // Wait for the previous read loop to exit. This is a rare case which - // happens if this loop isn't notified in time after the connection breaks. - // In those cases the caller will notice first and reconnect. Closing the - // handler terminates all waiting requests (closing op.resp) except for - // lastOp, which will be transferred to the new handler. - conn.close(errClientReconnected, lastOp) - c.drainRead() - } - go c.read(newcodec) - reading = true - conn = c.newClientConn(newcodec, apiMaxDuration, refillRate, maxStored) - // Re-register the in-flight request on the new handler - // because that's where it will be sent. - conn.handler.addRequestOp(lastOp) - - // Send path: - case op := <-reqInitLock: - // Stop listening for further requests until the current one has been sent. - reqInitLock = nil - lastOp = op - conn.handler.addRequestOp(op) - - case err := <-c.reqSent: - if err != nil { - // Remove response handlers for the last send. When the read loop - // goes down, it will signal all other current operations. - conn.handler.removeRequestOp(lastOp) - } - // Let the next request in. - reqInitLock = c.reqInit - lastOp = nil - - case op := <-c.reqTimeout: - conn.handler.removeRequestOp(op) - } - } -} - -// drainRead drops read messages until an error occurs. -func (c *Client) drainRead() { - for { - select { - case <-c.readOp: - case <-c.readErr: - return - } - } -} - -// read decodes RPC messages from a codec, feeding them into dispatch. -func (c *Client) read(codec ServerCodec) { - for { - msgs, batch, err := codec.readBatch() - if _, ok := err.(*json.SyntaxError); ok { - msg := errorMessage(&parseError{err.Error()}) - codec.writeJSON(context.Background(), msg, true) - } - if err != nil { - c.readErr <- err - return - } - c.readOp <- readOp{msgs, batch} - } -} diff --git a/graft/subnet-evm/rpc/client_opt.go b/graft/subnet-evm/rpc/client_opt.go deleted file mode 100644 index 748ca9d7acf0..000000000000 --- a/graft/subnet-evm/rpc/client_opt.go +++ /dev/null @@ -1,155 +0,0 @@ -// Copyright (C) 2019, Ava Labs, Inc. All rights reserved. -// See the file LICENSE for licensing terms. -// -// This file is a derived work, based on the go-ethereum library whose original -// notices appear below. -// -// It is distributed under a license compatible with the licensing terms of the -// original code from which it is derived. -// -// Much love to the original authors for their work. -// ********** -// Copyright 2022 The go-ethereum Authors -// This file is part of the go-ethereum library. -// -// The go-ethereum library is free software: you can redistribute it and/or modify -// it under the terms of the GNU Lesser General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// The go-ethereum library is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU Lesser General Public License for more details. -// -// You should have received a copy of the GNU Lesser General Public License -// along with the go-ethereum library. If not, see . - -package rpc - -import ( - "net/http" - - "github.com/gorilla/websocket" -) - -// ClientOption is a configuration option for the RPC client. -type ClientOption interface { - applyOption(*clientConfig) -} - -type clientConfig struct { - // HTTP settings - httpClient *http.Client - httpHeaders http.Header - httpAuth HTTPAuth - - // WebSocket options - wsDialer *websocket.Dialer - wsMessageSizeLimit *int64 // wsMessageSizeLimit nil = default, 0 = no limit - - // RPC handler options - idgen func() ID - batchItemLimit int - batchResponseLimit int -} - -func (cfg *clientConfig) initHeaders() { - if cfg.httpHeaders == nil { - cfg.httpHeaders = make(http.Header) - } -} - -func (cfg *clientConfig) setHeader(key, value string) { - cfg.initHeaders() - cfg.httpHeaders.Set(key, value) -} - -type optionFunc func(*clientConfig) - -func (fn optionFunc) applyOption(opt *clientConfig) { - fn(opt) -} - -// WithWebsocketDialer configures the websocket.Dialer used by the RPC client. -func WithWebsocketDialer(dialer websocket.Dialer) ClientOption { - return optionFunc(func(cfg *clientConfig) { - cfg.wsDialer = &dialer - }) -} - -// WithWebsocketMessageSizeLimit configures the websocket message size limit used by the RPC -// client. Passing a limit of 0 means no limit. -func WithWebsocketMessageSizeLimit(messageSizeLimit int64) ClientOption { - return optionFunc(func(cfg *clientConfig) { - cfg.wsMessageSizeLimit = &messageSizeLimit - }) -} - -// WithHeader configures HTTP headers set by the RPC client. Headers set using this option -// will be used for both HTTP and WebSocket connections. -func WithHeader(key, value string) ClientOption { - return optionFunc(func(cfg *clientConfig) { - cfg.initHeaders() - cfg.httpHeaders.Set(key, value) - }) -} - -// WithHeaders configures HTTP headers set by the RPC client. Headers set using this -// option will be used for both HTTP and WebSocket connections. -func WithHeaders(headers http.Header) ClientOption { - return optionFunc(func(cfg *clientConfig) { - cfg.initHeaders() - for k, vs := range headers { - cfg.httpHeaders[k] = vs - } - }) -} - -// WithHTTPClient configures the http.Client used by the RPC client. -func WithHTTPClient(c *http.Client) ClientOption { - return optionFunc(func(cfg *clientConfig) { - cfg.httpClient = c - }) -} - -// WithHTTPAuth configures HTTP request authentication. The given provider will be called -// whenever a request is made. Note that only one authentication provider can be active at -// any time. -func WithHTTPAuth(a HTTPAuth) ClientOption { - if a == nil { - panic("nil auth") - } - return optionFunc(func(cfg *clientConfig) { - cfg.httpAuth = a - }) -} - -// A HTTPAuth function is called by the client whenever a HTTP request is sent. -// The function must be safe for concurrent use. -// -// Usually, HTTPAuth functions will call h.Set("authorization", "...") to add -// auth information to the request. -type HTTPAuth func(h http.Header) error - -// WithBatchItemLimit changes the maximum number of items allowed in batch requests. -// -// Note: this option applies when processing incoming batch requests. It does not affect -// batch requests sent by the client. -func WithBatchItemLimit(limit int) ClientOption { - return optionFunc(func(cfg *clientConfig) { - cfg.batchItemLimit = limit - }) -} - -// WithBatchResponseSizeLimit changes the maximum number of response bytes that can be -// generated for batch requests. When this limit is reached, further calls in the batch -// will not be processed. -// -// Note: this option applies when processing incoming batch requests. It does not affect -// batch requests sent by the client. -func WithBatchResponseSizeLimit(sizeLimit int) ClientOption { - return optionFunc(func(cfg *clientConfig) { - cfg.batchResponseLimit = sizeLimit - }) -} diff --git a/graft/subnet-evm/rpc/client_opt_test.go b/graft/subnet-evm/rpc/client_opt_test.go deleted file mode 100644 index dd521808ea2d..000000000000 --- a/graft/subnet-evm/rpc/client_opt_test.go +++ /dev/null @@ -1,37 +0,0 @@ -// Copyright (C) 2019, Ava Labs, Inc. All rights reserved. -// See the file LICENSE for licensing terms. -// -// This file is a derived work, based on the go-ethereum library whose original -// notices appear below. -// -// It is distributed under a license compatible with the licensing terms of the -// original code from which it is derived. -// -// Much love to the original authors for their work. -// ********** - -package rpc_test - -import ( - "context" - "net/http" - "time" - - "github.com/ava-labs/avalanchego/graft/subnet-evm/rpc" -) - -// This example configures a HTTP-based RPC client with two options - one setting the -// overall request timeout, the other adding a custom HTTP header to all requests. -func ExampleDialOptions() { - tokenHeader := rpc.WithHeader("x-token", "foo") - httpClient := rpc.WithHTTPClient(&http.Client{ - Timeout: 10 * time.Second, - }) - - ctx := context.Background() - c, err := rpc.DialOptions(ctx, "http://rpc.example.com", httpClient, tokenHeader) - if err != nil { - panic(err) - } - c.Close() -} diff --git a/graft/subnet-evm/rpc/client_test.go b/graft/subnet-evm/rpc/client_test.go deleted file mode 100644 index c6e62fdedd87..000000000000 --- a/graft/subnet-evm/rpc/client_test.go +++ /dev/null @@ -1,893 +0,0 @@ -// Copyright (C) 2019, Ava Labs, Inc. All rights reserved. -// See the file LICENSE for licensing terms. -// -// This file is a derived work, based on the go-ethereum library whose original -// notices appear below. -// -// It is distributed under a license compatible with the licensing terms of the -// original code from which it is derived. -// -// Much love to the original authors for their work. -// ********** -// Copyright 2016 The go-ethereum Authors -// This file is part of the go-ethereum library. -// -// The go-ethereum library is free software: you can redistribute it and/or modify -// it under the terms of the GNU Lesser General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// The go-ethereum library is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU Lesser General Public License for more details. -// -// You should have received a copy of the GNU Lesser General Public License -// along with the go-ethereum library. If not, see . - -package rpc - -import ( - "context" - "encoding/json" - "errors" - "fmt" - "math/rand" - "net" - "net/http" - "net/http/httptest" - "reflect" - "runtime" - "strings" - "sync" - "testing" - "time" - - "github.com/ava-labs/libevm/log" - "github.com/davecgh/go-spew/spew" -) - -func TestClientRequest(t *testing.T) { - server := newTestServer() - defer server.Stop() - client := DialInProc(server) - defer client.Close() - - var resp echoResult - if err := client.Call(&resp, "test_echo", "hello", 10, &echoArgs{"world"}); err != nil { - t.Fatal(err) - } - if !reflect.DeepEqual(resp, echoResult{"hello", 10, &echoArgs{"world"}}) { - t.Errorf("incorrect result %#v", resp) - } -} - -func TestClientResponseType(t *testing.T) { - server := newTestServer() - defer server.Stop() - client := DialInProc(server) - defer client.Close() - - if err := client.Call(nil, "test_echo", "hello", 10, &echoArgs{"world"}); err != nil { - t.Errorf("Passing nil as result should be fine, but got an error: %v", err) - } - var resultVar echoResult - // Note: passing the var, not a ref - err := client.Call(resultVar, "test_echo", "hello", 10, &echoArgs{"world"}) - if err == nil { - t.Error("Passing a var as result should be an error") - } -} - -// This test checks calling a method that returns 'null'. -func TestClientNullResponse(t *testing.T) { - server := newTestServer() - defer server.Stop() - - client := DialInProc(server) - defer client.Close() - - var result json.RawMessage - if err := client.Call(&result, "test_null"); err != nil { - t.Fatal(err) - } - if result == nil { - t.Fatal("Expected non-nil result") - } - if !reflect.DeepEqual(result, json.RawMessage("null")) { - t.Errorf("Expected null, got %s", result) - } -} - -// This test checks that server-returned errors with code and data come out of Client.Call. -func TestClientErrorData(t *testing.T) { - server := newTestServer() - defer server.Stop() - client := DialInProc(server) - defer client.Close() - - var resp interface{} - err := client.Call(&resp, "test_returnError") - if err == nil { - t.Fatal("expected error") - } - - // Check code. - // The method handler returns an error value which implements the rpc.Error - // interface, i.e. it has a custom error code. The server returns this error code. - expectedCode := testError{}.ErrorCode() - if e, ok := err.(Error); !ok { - t.Fatalf("client did not return rpc.Error, got %#v", e) - } else if e.ErrorCode() != expectedCode { - t.Fatalf("wrong error code %d, want %d", e.ErrorCode(), expectedCode) - } - - // Check data. - if e, ok := err.(DataError); !ok { - t.Fatalf("client did not return rpc.DataError, got %#v", e) - } else if e.ErrorData() != (testError{}.ErrorData()) { - t.Fatalf("wrong error data %#v, want %#v", e.ErrorData(), testError{}.ErrorData()) - } -} - -func TestClientBatchRequest(t *testing.T) { - server := newTestServer() - defer server.Stop() - client := DialInProc(server) - defer client.Close() - - batch := []BatchElem{ - { - Method: "test_echo", - Args: []interface{}{"hello", 10, &echoArgs{"world"}}, - Result: new(echoResult), - }, - { - Method: "test_echo", - Args: []interface{}{"hello2", 11, &echoArgs{"world"}}, - Result: new(echoResult), - }, - { - Method: "no_such_method", - Args: []interface{}{1, 2, 3}, - Result: new(int), - }, - } - if err := client.BatchCall(batch); err != nil { - t.Fatal(err) - } - wantResult := []BatchElem{ - { - Method: "test_echo", - Args: []interface{}{"hello", 10, &echoArgs{"world"}}, - Result: &echoResult{"hello", 10, &echoArgs{"world"}}, - }, - { - Method: "test_echo", - Args: []interface{}{"hello2", 11, &echoArgs{"world"}}, - Result: &echoResult{"hello2", 11, &echoArgs{"world"}}, - }, - { - Method: "no_such_method", - Args: []interface{}{1, 2, 3}, - Result: new(int), - Error: &jsonError{Code: -32601, Message: "the method no_such_method does not exist/is not available"}, - }, - } - if !reflect.DeepEqual(batch, wantResult) { - t.Errorf("batch results mismatch:\ngot %swant %s", spew.Sdump(batch), spew.Sdump(wantResult)) - } -} - -// This checks that, for HTTP connections, the length of batch responses is validated to -// match the request exactly. -func TestClientBatchRequest_len(t *testing.T) { - b, err := json.Marshal([]jsonrpcMessage{ - {Version: "2.0", ID: json.RawMessage("1"), Result: json.RawMessage(`"0x1"`)}, - {Version: "2.0", ID: json.RawMessage("2"), Result: json.RawMessage(`"0x2"`)}, - }) - if err != nil { - t.Fatal("failed to encode jsonrpc message:", err) - } - s := httptest.NewServer(http.HandlerFunc(func(rw http.ResponseWriter, r *http.Request) { - _, err := rw.Write(b) - if err != nil { - t.Error("failed to write response:", err) - } - })) - t.Cleanup(s.Close) - - t.Run("too-few", func(t *testing.T) { - client, err := Dial(s.URL) - if err != nil { - t.Fatal("failed to dial test server:", err) - } - defer client.Close() - - batch := []BatchElem{ - {Method: "foo", Result: new(string)}, - {Method: "bar", Result: new(string)}, - {Method: "baz", Result: new(string)}, - } - ctx, cancelFn := context.WithTimeout(context.Background(), time.Second) - defer cancelFn() - - if err := client.BatchCallContext(ctx, batch); err != nil { - t.Fatal("error:", err) - } - for i, elem := range batch[:2] { - if elem.Error != nil { - t.Errorf("expected no error for batch element %d, got %q", i, elem.Error) - } - } - for i, elem := range batch[2:] { - if elem.Error != ErrMissingBatchResponse { - t.Errorf("wrong error %q for batch element %d", elem.Error, i+2) - } - } - }) - - t.Run("too-many", func(t *testing.T) { - client, err := Dial(s.URL) - if err != nil { - t.Fatal("failed to dial test server:", err) - } - defer client.Close() - - batch := []BatchElem{ - {Method: "foo", Result: new(string)}, - } - ctx, cancelFn := context.WithTimeout(context.Background(), time.Second) - defer cancelFn() - - if err := client.BatchCallContext(ctx, batch); err != nil { - t.Fatal("error:", err) - } - for i, elem := range batch[:1] { - if elem.Error != nil { - t.Errorf("expected no error for batch element %d, got %q", i, elem.Error) - } - } - for i, elem := range batch[1:] { - if elem.Error != ErrMissingBatchResponse { - t.Errorf("wrong error %q for batch element %d", elem.Error, i+2) - } - } - }) -} - -// This checks that the client can handle the case where the server doesn't -// respond to all requests in a batch. -func TestClientBatchRequestLimit(t *testing.T) { - server := newTestServer() - defer server.Stop() - server.SetBatchLimits(2, 100000) - client := DialInProc(server) - defer client.Close() - - batch := []BatchElem{ - {Method: "foo"}, - {Method: "bar"}, - {Method: "baz"}, - } - err := client.BatchCall(batch) - if err != nil { - t.Fatal("unexpected error:", err) - } - - // Check that the first response indicates an error with batch size. - var err0 Error - if !errors.As(batch[0].Error, &err0) { - t.Log("error zero:", batch[0].Error) - t.Fatalf("batch elem 0 has wrong error type: %T", batch[0].Error) - } else { - if err0.ErrorCode() != -32600 || err0.Error() != errMsgBatchTooLarge { - t.Fatalf("wrong error on batch elem zero: %v", err0) - } - } - - // Check that remaining response batch elements are reported as absent. - for i, elem := range batch[1:] { - if elem.Error != ErrMissingBatchResponse { - t.Fatalf("batch elem %d has unexpected error: %v", i+1, elem.Error) - } - } -} - -func TestClientNotify(t *testing.T) { - server := newTestServer() - defer server.Stop() - client := DialInProc(server) - defer client.Close() - - if err := client.Notify(context.Background(), "test_echo", "hello", 10, &echoArgs{"world"}); err != nil { - t.Fatal(err) - } -} - -// func TestClientCancelInproc(t *testing.T) { testClientCancel("inproc", t) } -func TestClientCancelWebsocket(t *testing.T) { testClientCancel("ws", t) } -func TestClientCancelHTTP(t *testing.T) { testClientCancel("http", t) } - -// func TestClientCancelIPC(t *testing.T) { testClientCancel("ipc", t) } - -// This test checks that requests made through CallContext can be canceled by canceling -// the context. -func testClientCancel(transport string, t *testing.T) { - // These tests take a lot of time, run them all at once. - // You probably want to run with -parallel 1 or comment out - // the call to t.Parallel if you enable the logging. - t.Parallel() - - server := newTestServer() - defer server.Stop() - - // What we want to achieve is that the context gets canceled - // at various stages of request processing. The interesting cases - // are: - // - cancel during dial - // - cancel while performing a HTTP request - // - cancel while waiting for a response - // - // To trigger those, the times are chosen such that connections - // are killed within the deadline for every other call (maxKillTimeout - // is 2x maxCancelTimeout). - // - // Once a connection is dead, there is a fair chance it won't connect - // successfully because the accept is delayed by 1s. - maxContextCancelTimeout := 300 * time.Millisecond - fl := &flakeyListener{ - maxAcceptDelay: 1 * time.Second, - maxKillTimeout: 600 * time.Millisecond, - } - - var client *Client - switch transport { - case "ws", "http": - c, hs := httpTestClient(server, transport, fl) - defer hs.Close() - client = c - // case "ipc": - // c, l := ipcTestClient(server, fl) - // defer l.Close() - // client = c - default: - panic("unknown transport: " + transport) - } - defer client.Close() - - // The actual test starts here. - var ( - wg sync.WaitGroup - nreqs = 10 - ncallers = 10 - ) - caller := func(index int) { - defer wg.Done() - for i := 0; i < nreqs; i++ { - var ( - ctx context.Context - cancel func() - timeout = time.Duration(rand.Int63n(int64(maxContextCancelTimeout))) - ) - if index < ncallers/2 { - // For half of the callers, create a context without deadline - // and cancel it later. - ctx, cancel = context.WithCancel(context.Background()) - time.AfterFunc(timeout, cancel) - } else { - // For the other half, create a context with a deadline instead. This is - // different because the context deadline is used to set the socket write - // deadline. - ctx, cancel = context.WithTimeout(context.Background(), timeout) - } - - // Now perform a call with the context. - // The key thing here is that no call will ever complete successfully. - err := client.CallContext(ctx, nil, "test_block") - switch { - case err == nil: - _, hasDeadline := ctx.Deadline() - t.Errorf("no error for call with %v wait time (deadline: %v)", timeout, hasDeadline) - // default: - // t.Logf("got expected error with %v wait time: %v", timeout, err) - } - cancel() - } - } - wg.Add(ncallers) - for i := 0; i < ncallers; i++ { - go caller(i) - } - wg.Wait() -} - -func TestClientSubscribeInvalidArg(t *testing.T) { - server := newTestServer() - defer server.Stop() - client := DialInProc(server) - defer client.Close() - - check := func(shouldPanic bool, arg interface{}) { - defer func() { - err := recover() - if shouldPanic && err == nil { - t.Errorf("EthSubscribe should've panicked for %#v", arg) - } - if !shouldPanic && err != nil { - t.Errorf("EthSubscribe shouldn't have panicked for %#v", arg) - buf := make([]byte, 1024*1024) - buf = buf[:runtime.Stack(buf, false)] - t.Error(err) - t.Error(string(buf)) - } - }() - client.EthSubscribe(context.Background(), arg, "foo_bar") - } - check(true, nil) - check(true, 1) - check(true, (chan int)(nil)) - check(true, make(<-chan int)) - check(false, make(chan int)) - check(false, make(chan<- int)) -} - -func TestClientSubscribe(t *testing.T) { - server := newTestServer() - defer server.Stop() - client := DialInProc(server) - defer client.Close() - - nc := make(chan int) - count := 10 - sub, err := client.Subscribe(context.Background(), "nftest", nc, "someSubscription", count, 0) - if err != nil { - t.Fatal("can't subscribe:", err) - } - for i := 0; i < count; i++ { - if val := <-nc; val != i { - t.Fatalf("value mismatch: got %d, want %d", val, i) - } - } - - sub.Unsubscribe() - select { - case v := <-nc: - t.Fatal("received value after unsubscribe:", v) - case err := <-sub.Err(): - if err != nil { - t.Fatalf("Err returned a non-nil error after explicit unsubscribe: %q", err) - } - case <-time.After(1 * time.Second): - t.Fatalf("subscription not closed within 1s after unsubscribe") - } -} - -// In this test, the connection drops while Subscribe is waiting for a response. -func TestClientSubscribeClose(t *testing.T) { - server := newTestServer() - service := ¬ificationTestService{ - gotHangSubscriptionReq: make(chan struct{}), - unblockHangSubscription: make(chan struct{}), - } - if err := server.RegisterName("nftest2", service); err != nil { - t.Fatal(err) - } - - defer server.Stop() - client := DialInProc(server) - defer client.Close() - - var ( - nc = make(chan int) - errc = make(chan error, 1) - sub *ClientSubscription - err error - ) - go func() { - sub, err = client.Subscribe(context.Background(), "nftest2", nc, "hangSubscription", 999) - errc <- err - }() - - <-service.gotHangSubscriptionReq - client.Close() - service.unblockHangSubscription <- struct{}{} - - select { - case err := <-errc: - if err == nil { - t.Errorf("Subscribe returned nil error after Close") - } - if sub != nil { - t.Error("Subscribe returned non-nil subscription after Close") - } - case <-time.After(1 * time.Second): - t.Fatalf("Subscribe did not return within 1s after Close") - } -} - -// This test reproduces https://github.com/ethereum/go-ethereum/issues/17837 where the -// client hangs during shutdown when Unsubscribe races with Client.Close. -func TestClientCloseUnsubscribeRace(t *testing.T) { - server := newTestServer() - defer server.Stop() - - for i := 0; i < 20; i++ { - client := DialInProc(server) - nc := make(chan int) - sub, err := client.Subscribe(context.Background(), "nftest", nc, "someSubscription", 3, 1) - if err != nil { - t.Fatal(err) - } - go client.Close() - go sub.Unsubscribe() - select { - case <-sub.Err(): - case <-time.After(5 * time.Second): - t.Fatal("subscription not closed within timeout") - } - } -} - -// unsubscribeRecorder collects the subscription IDs of *_unsubscribe calls. -type unsubscribeRecorder struct { - ServerCodec - unsubscribes map[string]bool -} - -func (r *unsubscribeRecorder) readBatch() ([]*jsonrpcMessage, bool, error) { - if r.unsubscribes == nil { - r.unsubscribes = make(map[string]bool) - } - - msgs, batch, err := r.ServerCodec.readBatch() - for _, msg := range msgs { - if msg.isUnsubscribe() { - var params []string - if err := json.Unmarshal(msg.Params, ¶ms); err != nil { - panic("unsubscribe decode error: " + err.Error()) - } - r.unsubscribes[params[0]] = true - } - } - return msgs, batch, err -} - -// This checks that Client calls the _unsubscribe method on the server when Unsubscribe is -// called on a subscription. -func TestClientSubscriptionUnsubscribeServer(t *testing.T) { - t.Parallel() - - // Create the server. - srv := NewServer(0) - srv.RegisterName("nftest", new(notificationTestService)) - p1, p2 := net.Pipe() - recorder := &unsubscribeRecorder{ServerCodec: NewCodec(p1)} - go srv.ServeCodec(recorder, OptionMethodInvocation|OptionSubscriptions, 0, 0, 0) - defer srv.Stop() - - // Create the client on the other end of the pipe. - cfg := new(clientConfig) - client, _ := newClient(context.Background(), cfg, func(context.Context) (ServerCodec, error) { - return NewCodec(p2), nil - }) - defer client.Close() - - // Create the subscription. - ch := make(chan int) - sub, err := client.Subscribe(context.Background(), "nftest", ch, "someSubscription", 1, 1) - if err != nil { - t.Fatal(err) - } - - // Unsubscribe and check that unsubscribe was called. - sub.Unsubscribe() - if !recorder.unsubscribes[sub.subid] { - t.Fatal("client did not call unsubscribe method") - } - if _, open := <-sub.Err(); open { - t.Fatal("subscription error channel not closed after unsubscribe") - } -} - -// This checks that the subscribed channel can be closed after Unsubscribe. -// It is the reproducer for https://github.com/ethereum/go-ethereum/issues/22322 -func TestClientSubscriptionChannelClose(t *testing.T) { - t.Parallel() - - var ( - srv = NewServer(0) - httpsrv = httptest.NewServer(srv.WebsocketHandler(nil)) - wsURL = "ws:" + strings.TrimPrefix(httpsrv.URL, "http:") - ) - defer srv.Stop() - defer httpsrv.Close() - - srv.RegisterName("nftest", new(notificationTestService)) - client, _ := Dial(wsURL) - defer client.Close() - - for i := 0; i < 5; i++ { - ch := make(chan int, 100) - sub, err := client.Subscribe(context.Background(), "nftest", ch, "someSubscription", 100, 1) - if err != nil { - t.Fatal(err) - } - sub.Unsubscribe() - close(ch) - } -} - -// This test checks that Client doesn't lock up when a single subscriber -// doesn't read subscription events. -func TestClientNotificationStorm(t *testing.T) { - server := newTestServer() - defer server.Stop() - - doTest := func(count int, wantError bool) { - client := DialInProc(server) - defer client.Close() - ctx, cancel := context.WithTimeout(context.Background(), 1*time.Minute) - defer cancel() - - // Subscribe on the server. It will start sending many notifications - // very quickly. - nc := make(chan int) - sub, err := client.Subscribe(ctx, "nftest", nc, "someSubscription", count, 0) - if err != nil { - t.Fatal("can't subscribe:", err) - } - defer sub.Unsubscribe() - - // Process each notification, try to run a call in between each of them. - for i := 0; i < count; i++ { - select { - case val := <-nc: - if val != i { - t.Fatalf("(%d/%d) unexpected value %d", i, count, val) - } - case err := <-sub.Err(): - if wantError && err != ErrSubscriptionQueueOverflow { - t.Fatalf("(%d/%d) got error %q, want %q", i, count, err, ErrSubscriptionQueueOverflow) - } else if !wantError { - t.Fatalf("(%d/%d) got unexpected error %q", i, count, err) - } - return - } - var r int - err := client.CallContext(ctx, &r, "nftest_echo", i) - if err != nil { - if !wantError { - t.Fatalf("(%d/%d) call error: %v", i, count, err) - } - return - } - } - if wantError { - t.Fatalf("didn't get expected error") - } - } - - doTest(8000, false) - doTest(100000, true) -} - -func TestClientSetHeader(t *testing.T) { - var gotHeader bool - srv := newTestServer() - httpsrv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - if r.Header.Get("test") == "ok" { - gotHeader = true - } - srv.ServeHTTP(w, r) - })) - defer httpsrv.Close() - defer srv.Stop() - - client, err := Dial(httpsrv.URL) - if err != nil { - t.Fatal(err) - } - defer client.Close() - - client.SetHeader("test", "ok") - if _, err := client.SupportedModules(); err != nil { - t.Fatal(err) - } - if !gotHeader { - t.Fatal("client did not set custom header") - } - - // Check that Content-Type can be replaced. - client.SetHeader("content-type", "application/x-garbage") - _, err = client.SupportedModules() - if err == nil { - t.Fatal("no error for invalid content-type header") - } else if !strings.Contains(err.Error(), "Unsupported Media Type") { - t.Fatalf("error is not related to content-type: %q", err) - } -} - -func TestClientHTTP(t *testing.T) { - server := newTestServer() - defer server.Stop() - - client, hs := httpTestClient(server, "http", nil) - defer hs.Close() - defer client.Close() - - // Launch concurrent requests. - var ( - results = make([]echoResult, 100) - errc = make(chan error, len(results)) - wantResult = echoResult{"a", 1, new(echoArgs)} - ) - defer client.Close() - for i := range results { - go func() { - errc <- client.Call(&results[i], "test_echo", wantResult.String, wantResult.Int, wantResult.Args) - }() - } - - // Wait for all of them to complete. - timeout := time.NewTimer(5 * time.Second) - defer timeout.Stop() - for i := range results { - select { - case err := <-errc: - if err != nil { - t.Fatal(err) - } - case <-timeout.C: - t.Fatalf("timeout (got %d/%d) results)", i+1, len(results)) - } - } - - // Check results. - for i := range results { - if !reflect.DeepEqual(results[i], wantResult) { - t.Errorf("result %d mismatch: got %#v, want %#v", i, results[i], wantResult) - } - } -} - -func TestClientReconnect(t *testing.T) { - startServer := func(addr string) (*Server, net.Listener) { - srv := newTestServer() - l, err := net.Listen("tcp", addr) - if err != nil { - t.Fatal("can't listen:", err) - } - go http.Serve(l, srv.WebsocketHandler([]string{"*"})) - return srv, l - } - - ctx, cancel := context.WithTimeout(context.Background(), 12*time.Second) - defer cancel() - - // Start a server and corresponding client. - s1, l1 := startServer("127.0.0.1:0") - client, err := DialContext(ctx, "ws://"+l1.Addr().String()) - if err != nil { - t.Fatal("can't dial", err) - } - defer client.Close() - - // Perform a call. This should work because the server is up. - var resp echoResult - if err := client.CallContext(ctx, &resp, "test_echo", "", 1, nil); err != nil { - t.Fatal(err) - } - - // Shut down the server and allow for some cool down time so we can listen on the same - // address again. - l1.Close() - s1.Stop() - time.Sleep(2 * time.Second) - - // Try calling again. It shouldn't work. - if err := client.CallContext(ctx, &resp, "test_echo", "", 2, nil); err == nil { - t.Error("successful call while the server is down") - t.Logf("resp: %#v", resp) - } - - // Start it up again and call again. The connection should be reestablished. - // We spawn multiple calls here to check whether this hangs somehow. - s2, l2 := startServer(l1.Addr().String()) - defer l2.Close() - defer s2.Stop() - - start := make(chan struct{}) - errors := make(chan error, 20) - for i := 0; i < cap(errors); i++ { - go func() { - <-start - var resp echoResult - errors <- client.CallContext(ctx, &resp, "test_echo", "", 3, nil) - }() - } - close(start) - errcount := 0 - for i := 0; i < cap(errors); i++ { - if err = <-errors; err != nil { - errcount++ - } - } - t.Logf("%d errors, last error: %v", errcount, err) - if errcount > 1 { - t.Errorf("expected one error after disconnect, got %d", errcount) - } -} - -func httpTestClient(srv *Server, transport string, fl *flakeyListener) (*Client, *httptest.Server) { - // Create the HTTP server. - var hs *httptest.Server - switch transport { - case "ws": - hs = httptest.NewUnstartedServer(srv.WebsocketHandler([]string{"*"})) - case "http": - hs = httptest.NewUnstartedServer(srv) - default: - panic("unknown HTTP transport: " + transport) - } - // Wrap the listener if required. - if fl != nil { - fl.Listener = hs.Listener - hs.Listener = fl - } - // Connect the client. - hs.Start() - client, err := Dial(transport + "://" + hs.Listener.Addr().String()) - if err != nil { - panic(err) - } - return client, hs -} - -// func ipcTestClient(srv *Server, fl *flakeyListener) (*Client, net.Listener) { -// // Listen on a random endpoint. -// endpoint := fmt.Sprintf("go-ethereum-test-ipc-%d-%d", os.Getpid(), rand.Int63()) -// if runtime.GOOS == "windows" { -// endpoint = `\\.\pipe\` + endpoint -// } else { -// endpoint = os.TempDir() + "/" + endpoint -// } -// l, err := ipcListen(endpoint) -// if err != nil { -// panic(err) -// } -// // Connect the listener to the server. -// if fl != nil { -// fl.Listener = l -// l = fl -// } -// go srv.ServeListener(l) -// // Connect the client. -// client, err := Dial(endpoint) -// if err != nil { -// panic(err) -// } -// return client, l -// } - -// flakeyListener kills accepted connections after a random timeout. -type flakeyListener struct { - net.Listener - maxKillTimeout time.Duration - maxAcceptDelay time.Duration -} - -func (l *flakeyListener) Accept() (net.Conn, error) { - delay := time.Duration(rand.Int63n(int64(l.maxAcceptDelay))) - time.Sleep(delay) - - c, err := l.Listener.Accept() - if err == nil { - timeout := time.Duration(rand.Int63n(int64(l.maxKillTimeout))) - time.AfterFunc(timeout, func() { - log.Debug(fmt.Sprintf("killing conn %v after %v", c.LocalAddr(), timeout)) - c.Close() - }) - } - return c, err -} diff --git a/graft/subnet-evm/rpc/context_headers.go b/graft/subnet-evm/rpc/context_headers.go deleted file mode 100644 index 2c2f3ca35c32..000000000000 --- a/graft/subnet-evm/rpc/context_headers.go +++ /dev/null @@ -1,67 +0,0 @@ -// Copyright (C) 2019, Ava Labs, Inc. All rights reserved. -// See the file LICENSE for licensing terms. -// -// This file is a derived work, based on the go-ethereum library whose original -// notices appear below. -// -// It is distributed under a license compatible with the licensing terms of the -// original code from which it is derived. -// -// Much love to the original authors for their work. -// ********** -// Copyright 2022 The go-ethereum Authors -// This file is part of the go-ethereum library. -// -// The go-ethereum library is free software: you can redistribute it and/or modify -// it under the terms of the GNU Lesser General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// The go-ethereum library is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU Lesser General Public License for more details. -// -// You should have received a copy of the GNU Lesser General Public License -// along with the go-ethereum library. If not, see . - -package rpc - -import ( - "context" - "net/http" -) - -type mdHeaderKey struct{} - -// NewContextWithHeaders wraps the given context, adding HTTP headers. These headers will -// be applied by Client when making a request using the returned context. -func NewContextWithHeaders(ctx context.Context, h http.Header) context.Context { - if len(h) == 0 { - // This check ensures the header map set in context will never be nil. - return ctx - } - - var ctxh http.Header - prev, ok := ctx.Value(mdHeaderKey{}).(http.Header) - if ok { - ctxh = setHeaders(prev.Clone(), h) - } else { - ctxh = h.Clone() - } - return context.WithValue(ctx, mdHeaderKey{}, ctxh) -} - -// headersFromContext is used to extract http.Header from context. -func headersFromContext(ctx context.Context) http.Header { - source, _ := ctx.Value(mdHeaderKey{}).(http.Header) - return source -} - -// setHeaders sets all headers from src in dst. -func setHeaders(dst http.Header, src http.Header) http.Header { - for key, values := range src { - dst[http.CanonicalHeaderKey(key)] = values - } - return dst -} diff --git a/graft/subnet-evm/rpc/doc.go b/graft/subnet-evm/rpc/doc.go deleted file mode 100644 index 9bed4c8e4216..000000000000 --- a/graft/subnet-evm/rpc/doc.go +++ /dev/null @@ -1,120 +0,0 @@ -// Copyright (C) 2019, Ava Labs, Inc. All rights reserved. -// See the file LICENSE for licensing terms. -// -// This file is a derived work, based on the go-ethereum library whose original -// notices appear below. -// -// It is distributed under a license compatible with the licensing terms of the -// original code from which it is derived. -// -// Much love to the original authors for their work. -// ********** -// Copyright 2015 The go-ethereum Authors -// This file is part of the go-ethereum library. -// -// The go-ethereum library is free software: you can redistribute it and/or modify -// it under the terms of the GNU Lesser General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// The go-ethereum library is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU Lesser General Public License for more details. -// -// You should have received a copy of the GNU Lesser General Public License -// along with the go-ethereum library. If not, see . - -/* -Package rpc implements bi-directional JSON-RPC 2.0 on multiple transports. - -It provides access to the exported methods of an object across a network or other I/O -connection. After creating a server or client instance, objects can be registered to make -them visible as 'services'. Exported methods that follow specific conventions can be -called remotely. It also has support for the publish/subscribe pattern. - -# RPC Methods - -Methods that satisfy the following criteria are made available for remote access: - - - method must be exported - - method returns 0, 1 (response or error) or 2 (response and error) values - -An example method: - - func (s *CalcService) Add(a, b int) (int, error) - -When the returned error isn't nil the returned integer is ignored and the error is sent -back to the client. Otherwise the returned integer is sent back to the client. - -Optional arguments are supported by accepting pointer values as arguments. E.g. if we want -to do the addition in an optional finite field we can accept a mod argument as pointer -value. - - func (s *CalcService) Add(a, b int, mod *int) (int, error) - -This RPC method can be called with 2 integers and a null value as third argument. In that -case the mod argument will be nil. Or it can be called with 3 integers, in that case mod -will be pointing to the given third argument. Since the optional argument is the last -argument the RPC package will also accept 2 integers as arguments. It will pass the mod -argument as nil to the RPC method. - -The server offers the ServeCodec method which accepts a ServerCodec instance. It will read -requests from the codec, process the request and sends the response back to the client -using the codec. The server can execute requests concurrently. Responses can be sent back -to the client out of order. - -An example server which uses the JSON codec: - - type CalculatorService struct {} - - func (s *CalculatorService) Add(a, b int) int { - return a + b - } - - func (s *CalculatorService) Div(a, b int) (int, error) { - if b == 0 { - return 0, errors.New("divide by zero") - } - return a/b, nil - } - - calculator := new(CalculatorService) - server := NewServer() - server.RegisterName("calculator", calculator) - l, _ := net.ListenUnix("unix", &net.UnixAddr{Net: "unix", Name: "/tmp/calculator.sock"}) - server.ServeListener(l) - -# Subscriptions - -The package also supports the publish subscribe pattern through the use of subscriptions. -A method that is considered eligible for notifications must satisfy the following -criteria: - - - method must be exported - - first method argument type must be context.Context - - method must have return types (rpc.Subscription, error) - -An example method: - - func (s *BlockChainService) NewBlocks(ctx context.Context) (rpc.Subscription, error) { - ... - } - -When the service containing the subscription method is registered to the server, for -example under the "blockchain" namespace, a subscription is created by calling the -"blockchain_subscribe" method. - -Subscriptions are deleted when the user sends an unsubscribe request or when the -connection which was used to create the subscription is closed. This can be initiated by -the client and server. The server will close the connection for any write error. - -For more information about subscriptions, see https://github.com/ethereum/go-ethereum/wiki/RPC-PUB-SUB. - -# Reverse Calls - -In any method handler, an instance of rpc.Client can be accessed through the -ClientFromContext method. Using this client instance, server-to-client method calls can be -performed on the RPC connection. -*/ -package rpc diff --git a/graft/subnet-evm/rpc/errors.go b/graft/subnet-evm/rpc/errors.go deleted file mode 100644 index add007edde4c..000000000000 --- a/graft/subnet-evm/rpc/errors.go +++ /dev/null @@ -1,167 +0,0 @@ -// Copyright (C) 2019, Ava Labs, Inc. All rights reserved. -// See the file LICENSE for licensing terms. -// -// This file is a derived work, based on the go-ethereum library whose original -// notices appear below. -// -// It is distributed under a license compatible with the licensing terms of the -// original code from which it is derived. -// -// Much love to the original authors for their work. -// ********** -// Copyright 2015 The go-ethereum Authors -// This file is part of the go-ethereum library. -// -// The go-ethereum library is free software: you can redistribute it and/or modify -// it under the terms of the GNU Lesser General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// The go-ethereum library is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU Lesser General Public License for more details. -// -// You should have received a copy of the GNU Lesser General Public License -// along with the go-ethereum library. If not, see . - -package rpc - -import "fmt" - -// HTTPError is returned by client operations when the HTTP status code of the -// response is not a 2xx status. -type HTTPError struct { - StatusCode int - Status string - Body []byte -} - -func (err HTTPError) Error() string { - if len(err.Body) == 0 { - return err.Status - } - return fmt.Sprintf("%v: %s", err.Status, err.Body) -} - -// Error wraps RPC errors, which contain an error code in addition to the message. -type Error interface { - Error() string // returns the message - ErrorCode() int // returns the code -} - -// A DataError contains some data in addition to the error message. -type DataError interface { - Error() string // returns the message - ErrorData() interface{} // returns the error data -} - -// Error types defined below are the built-in JSON-RPC errors. - -var ( - _ Error = new(methodNotFoundError) - _ Error = new(subscriptionNotFoundError) - _ Error = new(parseError) - _ Error = new(invalidRequestError) - _ Error = new(invalidMessageError) - _ Error = new(invalidParamsError) - _ Error = new(internalServerError) -) - -const ( - errcodeDefault = -32000 - errcodeTimeout = -32002 - errcodeResponseTooLarge = -32003 - errcodePanic = -32603 - errcodeMarshalError = -32603 - - legacyErrcodeNotificationsUnsupported = -32001 -) - -const ( - errMsgTimeout = "request timed out" - errMsgResponseTooLarge = "response too large" - errMsgBatchTooLarge = "batch too large" -) - -type methodNotFoundError struct{ method string } - -func (e *methodNotFoundError) ErrorCode() int { return -32601 } - -func (e *methodNotFoundError) Error() string { - return fmt.Sprintf("the method %s does not exist/is not available", e.method) -} - -type notificationsUnsupportedError struct{} - -func (e notificationsUnsupportedError) Error() string { - return "notifications not supported" -} - -func (e notificationsUnsupportedError) ErrorCode() int { return -32601 } - -// Is checks for equivalence to another error. Here we define that all errors with code -// -32601 (method not found) are equivalent to notificationsUnsupportedError. This is -// done to enable the following pattern: -// -// sub, err := client.Subscribe(...) -// if errors.Is(err, rpc.ErrNotificationsUnsupported) { -// // server doesn't support subscriptions -// } -func (e notificationsUnsupportedError) Is(other error) bool { - if other == (notificationsUnsupportedError{}) { - return true - } - rpcErr, ok := other.(Error) - if ok { - code := rpcErr.ErrorCode() - return code == -32601 || code == legacyErrcodeNotificationsUnsupported - } - return false -} - -type subscriptionNotFoundError struct{ namespace, subscription string } - -func (e *subscriptionNotFoundError) ErrorCode() int { return -32601 } - -func (e *subscriptionNotFoundError) Error() string { - return fmt.Sprintf("no %q subscription in %s namespace", e.subscription, e.namespace) -} - -// Invalid JSON was received by the server. -type parseError struct{ message string } - -func (e *parseError) ErrorCode() int { return -32700 } - -func (e *parseError) Error() string { return e.message } - -// received message isn't a valid request -type invalidRequestError struct{ message string } - -func (e *invalidRequestError) ErrorCode() int { return -32600 } - -func (e *invalidRequestError) Error() string { return e.message } - -// received message is invalid -type invalidMessageError struct{ message string } - -func (e *invalidMessageError) ErrorCode() int { return -32700 } - -func (e *invalidMessageError) Error() string { return e.message } - -// unable to decode supplied params, or an invalid number of parameters -type invalidParamsError struct{ message string } - -func (e *invalidParamsError) ErrorCode() int { return -32602 } - -func (e *invalidParamsError) Error() string { return e.message } - -// internalServerError is used for server errors during request processing. -type internalServerError struct { - code int - message string -} - -func (e *internalServerError) ErrorCode() int { return e.code } - -func (e *internalServerError) Error() string { return e.message } diff --git a/graft/subnet-evm/rpc/handler.go b/graft/subnet-evm/rpc/handler.go deleted file mode 100644 index 01b2fd1adaf4..000000000000 --- a/graft/subnet-evm/rpc/handler.go +++ /dev/null @@ -1,727 +0,0 @@ -// Copyright (C) 2019, Ava Labs, Inc. All rights reserved. -// See the file LICENSE for licensing terms. -// -// This file is a derived work, based on the go-ethereum library whose original -// notices appear below. -// -// It is distributed under a license compatible with the licensing terms of the -// original code from which it is derived. -// -// Much love to the original authors for their work. -// ********** -// Copyright 2019 The go-ethereum Authors -// This file is part of the go-ethereum library. -// -// The go-ethereum library is free software: you can redistribute it and/or modify -// it under the terms of the GNU Lesser General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// The go-ethereum library is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU Lesser General Public License for more details. -// -// You should have received a copy of the GNU Lesser General Public License -// along with the go-ethereum library. If not, see . - -package rpc - -import ( - "bytes" - "context" - "encoding/json" - "errors" - "fmt" - "reflect" - "strconv" - "strings" - "sync" - "time" - - "github.com/ava-labs/libevm/log" - "github.com/ava-labs/libevm/metrics" - "golang.org/x/time/rate" -) - -// handler handles JSON-RPC messages. There is one handler per connection. Note that -// handler is not safe for concurrent use. Message handling never blocks indefinitely -// because RPCs are processed on background goroutines launched by handler. -// -// The entry points for incoming messages are: -// -// h.handleMsg(message) -// h.handleBatch(message) -// -// Outgoing calls use the requestOp struct. Register the request before sending it -// on the connection: -// -// op := &requestOp{ids: ...} -// h.addRequestOp(op) -// -// Now send the request, then wait for the reply to be delivered through handleMsg: -// -// if err := op.wait(...); err != nil { -// h.removeRequestOp(op) // timeout, etc. -// } -type handler struct { - reg *serviceRegistry - unsubscribeCb *callback - idgen func() ID // subscription ID generator - respWait map[string]*requestOp // active client requests - clientSubs map[string]*ClientSubscription // active client subscriptions - callWG sync.WaitGroup // pending call goroutines - rootCtx context.Context // canceled by close() - cancelRoot func() // cancel function for rootCtx - conn jsonWriter // where responses will be sent - log log.Logger - allowSubscribe bool - batchRequestLimit int - batchResponseMaxSize int - - subLock sync.Mutex - serverSubs map[ID]*Subscription - - deadlineContext time.Duration // limits execution after some time.Duration - limiter *rate.Limiter -} - -type callProc struct { - ctx context.Context - notifiers []*Notifier - callStart time.Time - procStart time.Time -} - -func newHandler(connCtx context.Context, conn jsonWriter, idgen func() ID, reg *serviceRegistry, batchRequestLimit, batchResponseMaxSize int) *handler { - rootCtx, cancelRoot := context.WithCancel(connCtx) - h := &handler{ - reg: reg, - idgen: idgen, - conn: conn, - respWait: make(map[string]*requestOp), - clientSubs: make(map[string]*ClientSubscription), - rootCtx: rootCtx, - cancelRoot: cancelRoot, - allowSubscribe: true, - serverSubs: make(map[ID]*Subscription), - log: log.Root(), - batchRequestLimit: batchRequestLimit, - batchResponseMaxSize: batchResponseMaxSize, - } - if conn.remoteAddr() != "" { - h.log = h.log.New("conn", conn.remoteAddr()) - } - h.unsubscribeCb = newCallback(reflect.Value{}, reflect.ValueOf(h.unsubscribe)) - return h -} - -// batchCallBuffer manages in progress call messages and their responses during a batch -// call. Calls need to be synchronized between the processing and timeout-triggering -// goroutines. -type batchCallBuffer struct { - mutex sync.Mutex - calls []*jsonrpcMessage - resp []*jsonrpcMessage - wrote bool -} - -// nextCall returns the next unprocessed message. -func (b *batchCallBuffer) nextCall() *jsonrpcMessage { - b.mutex.Lock() - defer b.mutex.Unlock() - - if len(b.calls) == 0 { - return nil - } - // The popping happens in `pushAnswer`. The in progress call is kept - // so we can return an error for it in case of timeout. - msg := b.calls[0] - return msg -} - -// pushResponse adds the response to last call returned by nextCall. -func (b *batchCallBuffer) pushResponse(answer *jsonrpcMessage) { - b.mutex.Lock() - defer b.mutex.Unlock() - - if answer != nil { - b.resp = append(b.resp, answer) - } - b.calls = b.calls[1:] -} - -// write sends the responses. -func (b *batchCallBuffer) write(ctx context.Context, conn jsonWriter) { - b.mutex.Lock() - defer b.mutex.Unlock() - - b.doWrite(ctx, conn, false) -} - -// respondWithError sends the responses added so far. For the remaining unanswered call -// messages, it responds with the given error. -func (b *batchCallBuffer) respondWithError(ctx context.Context, conn jsonWriter, err error) { - b.mutex.Lock() - defer b.mutex.Unlock() - - for _, msg := range b.calls { - if !msg.isNotification() { - b.resp = append(b.resp, msg.errorResponse(err)) - } - } - b.doWrite(ctx, conn, true) -} - -// doWrite actually writes the response. -// This assumes b.mutex is held. -func (b *batchCallBuffer) doWrite(ctx context.Context, conn jsonWriter, isErrorResponse bool) { - if b.wrote { - return - } - b.wrote = true // can only write once - if len(b.resp) > 0 { - conn.writeJSONSkipDeadline(ctx, b.resp, isErrorResponse, true) - } -} - -// addLimiter adds a rate limiter to the handler that will allow at most -// [refillRate] cpu to be used per second. At most [maxStored] cpu time will be -// stored for this limiter. -// If any values are provided that would make the rate limiting trivial, then no -// limiter is added. -func (h *handler) addLimiter(refillRate, maxStored time.Duration) { - if refillRate <= 0 || maxStored < h.deadlineContext || h.deadlineContext <= 0 { - return - } - h.limiter = rate.NewLimiter(rate.Limit(refillRate), int(maxStored)) -} - -// handleBatch executes all messages in a batch and returns the responses. -func (h *handler) handleBatch(msgs []*jsonrpcMessage) { - // Emit error response for empty batches: - if len(msgs) == 0 { - h.startCallProc(func(cp *callProc) { - resp := errorMessage(&invalidRequestError{"empty batch"}) - h.conn.writeJSONSkipDeadline(cp.ctx, resp, true, h.deadlineContext > 0) - }) - return - } - // Apply limit on total number of requests. - if h.batchRequestLimit != 0 && len(msgs) > h.batchRequestLimit { - h.startCallProc(func(cp *callProc) { - h.respondWithBatchTooLarge(cp, msgs) - }) - return - } - - // Handle non-call messages first. - // Here we need to find the requestOp that sent the request batch. - calls := make([]*jsonrpcMessage, 0, len(msgs)) - h.handleResponses(msgs, func(msg *jsonrpcMessage) { - calls = append(calls, msg) - }) - if len(calls) == 0 { - return - } - - // Process calls on a goroutine because they may block indefinitely: - h.startCallProc(func(cp *callProc) { - var ( - timer *time.Timer - cancel context.CancelFunc - callBuffer = &batchCallBuffer{calls: calls, resp: make([]*jsonrpcMessage, 0, len(calls))} - ) - - cp.ctx, cancel = context.WithCancel(cp.ctx) - defer cancel() - - // Cancel the request context after timeout and send an error response. Since the - // currently-running method might not return immediately on timeout, we must wait - // for the timeout concurrently with processing the request. - if timeout, ok := ContextRequestTimeout(cp.ctx); ok { - timer = time.AfterFunc(timeout, func() { - cancel() - err := &internalServerError{errcodeTimeout, errMsgTimeout} - callBuffer.respondWithError(cp.ctx, h.conn, err) - }) - } - - responseBytes := 0 - for { - // No need to handle rest of calls if timed out. - if cp.ctx.Err() != nil { - break - } - msg := callBuffer.nextCall() - if msg == nil { - break - } - resp := h.handleCallMsg(cp, msg) - callBuffer.pushResponse(resp) - if resp != nil && h.batchResponseMaxSize != 0 { - responseBytes += len(resp.Result) - if responseBytes > h.batchResponseMaxSize { - err := &internalServerError{errcodeResponseTooLarge, errMsgResponseTooLarge} - callBuffer.respondWithError(cp.ctx, h.conn, err) - break - } - } - } - if timer != nil { - timer.Stop() - } - - h.addSubscriptions(cp.notifiers) - callBuffer.write(cp.ctx, h.conn) - for _, n := range cp.notifiers { - n.activate() - } - }) -} - -func (h *handler) respondWithBatchTooLarge(cp *callProc, batch []*jsonrpcMessage) { - resp := errorMessage(&invalidRequestError{errMsgBatchTooLarge}) - // Find the first call and add its "id" field to the error. - // This is the best we can do, given that the protocol doesn't have a way - // of reporting an error for the entire batch. - for _, msg := range batch { - if msg.isCall() { - resp.ID = msg.ID - break - } - } - h.conn.writeJSONSkipDeadline(cp.ctx, []*jsonrpcMessage{resp}, true, h.deadlineContext > 0) -} - -// handleMsg handles a single non-batch message. -func (h *handler) handleMsg(msg *jsonrpcMessage) { - msgs := []*jsonrpcMessage{msg} - h.handleResponses(msgs, func(msg *jsonrpcMessage) { - h.startCallProc(func(cp *callProc) { - h.handleNonBatchCall(cp, msg) - }) - }) -} - -func (h *handler) handleNonBatchCall(cp *callProc, msg *jsonrpcMessage) { - var ( - responded sync.Once - timer *time.Timer - cancel context.CancelFunc - ) - cp.ctx, cancel = context.WithCancel(cp.ctx) - defer cancel() - - // Cancel the request context after timeout and send an error response. Since the - // running method might not return immediately on timeout, we must wait for the - // timeout concurrently with processing the request. - if timeout, ok := ContextRequestTimeout(cp.ctx); ok { - timer = time.AfterFunc(timeout, func() { - cancel() - responded.Do(func() { - resp := msg.errorResponse(&internalServerError{errcodeTimeout, errMsgTimeout}) - h.conn.writeJSONSkipDeadline(cp.ctx, resp, true, h.deadlineContext > 0) - }) - }) - } - - answer := h.handleCallMsg(cp, msg) - if timer != nil { - timer.Stop() - } - h.addSubscriptions(cp.notifiers) - if answer != nil { - responded.Do(func() { - h.conn.writeJSONSkipDeadline(cp.ctx, answer, false, h.deadlineContext > 0) - }) - } - for _, n := range cp.notifiers { - n.activate() - } -} - -// close cancels all requests except for inflightReq and waits for -// call goroutines to shut down. -func (h *handler) close(err error, inflightReq *requestOp) { - h.cancelAllRequests(err, inflightReq) - h.callWG.Wait() - h.cancelRoot() - h.cancelServerSubscriptions(err) -} - -// addRequestOp registers a request operation. -func (h *handler) addRequestOp(op *requestOp) { - for _, id := range op.ids { - h.respWait[string(id)] = op - } -} - -// removeRequestOp stops waiting for the given request IDs. -func (h *handler) removeRequestOp(op *requestOp) { - for _, id := range op.ids { - delete(h.respWait, string(id)) - } -} - -// cancelAllRequests unblocks and removes pending requests and active subscriptions. -func (h *handler) cancelAllRequests(err error, inflightReq *requestOp) { - didClose := make(map[*requestOp]bool) - if inflightReq != nil { - didClose[inflightReq] = true - } - - for id, op := range h.respWait { - // Remove the op so that later calls will not close op.resp again. - delete(h.respWait, id) - - if !didClose[op] { - op.err = err - close(op.resp) - didClose[op] = true - } - } - for id, sub := range h.clientSubs { - delete(h.clientSubs, id) - sub.close(err) - } -} - -func (h *handler) addSubscriptions(nn []*Notifier) { - h.subLock.Lock() - defer h.subLock.Unlock() - - for _, n := range nn { - if sub := n.takeSubscription(); sub != nil { - h.serverSubs[sub.ID] = sub - } - } -} - -// cancelServerSubscriptions removes all subscriptions and closes their error channels. -func (h *handler) cancelServerSubscriptions(err error) { - h.subLock.Lock() - defer h.subLock.Unlock() - - for id, s := range h.serverSubs { - s.err <- err - close(s.err) - delete(h.serverSubs, id) - } -} - -// awaitLimit blocks until the context is marked as done or the rate limiter is -// full. -func (h *handler) awaitLimit(ctx context.Context) { - if h.limiter == nil { - return - } - - now := time.Now() - reservation := h.limiter.ReserveN(now, int(h.deadlineContext)) - delay := reservation.Delay() - reservation.CancelAt(now) - - timer := time.NewTimer(delay) - select { - case <-ctx.Done(): - case <-timer.C: - } - timer.Stop() -} - -// consumeLimit removes the time since [procStart] from the rate limiter. It is -// assumed that the rate limiter is full. -func (h *handler) consumeLimit(procStart time.Time) { - if h.limiter == nil { - return - } - - stopTime := time.Now() - processingTime := stopTime.Sub(procStart) - if processingTime > h.deadlineContext { - processingTime = h.deadlineContext - } - - h.limiter.ReserveN(stopTime, int(processingTime)) -} - -// startCallProc runs fn in a new goroutine and starts tracking it in the h.calls wait group. -func (h *handler) startCallProc(fn func(*callProc)) { - h.callWG.Add(1) - callFn := func() { - var ( - ctx context.Context - cancel context.CancelFunc - ) - if h.deadlineContext > 0 { - ctx, cancel = context.WithTimeout(h.rootCtx, h.deadlineContext) - } else { - ctx, cancel = context.WithCancel(h.rootCtx) - } - defer h.callWG.Done() - - // Capture the time before we await for processing - callStart := time.Now() - h.awaitLimit(ctx) - - // If we are not limiting CPU, [procStart] will be identical to - // [callStart] - procStart := time.Now() - defer cancel() - - fn(&callProc{ctx: ctx, callStart: callStart, procStart: procStart}) - h.consumeLimit(procStart) - } - if h.limiter == nil { - go callFn() - } else { - callFn() - } -} - -// handleResponses processes method call responses. -func (h *handler) handleResponses(batch []*jsonrpcMessage, handleCall func(*jsonrpcMessage)) { - var resolvedops []*requestOp - handleResp := func(msg *jsonrpcMessage) { - op := h.respWait[string(msg.ID)] - if op == nil { - h.log.Debug("Unsolicited RPC response", "reqid", idForLog{msg.ID}) - return - } - resolvedops = append(resolvedops, op) - delete(h.respWait, string(msg.ID)) - - // For subscription responses, start the subscription if the server - // indicates success. EthSubscribe gets unblocked in either case through - // the op.resp channel. - if op.sub != nil { - if msg.Error != nil { - op.err = msg.Error - } else { - op.err = json.Unmarshal(msg.Result, &op.sub.subid) - if op.err == nil { - go op.sub.run() - h.clientSubs[op.sub.subid] = op.sub - } - } - } - - if !op.hadResponse { - op.hadResponse = true - op.resp <- batch - } - } - - for _, msg := range batch { - start := time.Now() - switch { - case msg.isResponse(): - handleResp(msg) - h.log.Trace("Handled RPC response", "reqid", idForLog{msg.ID}, "duration", time.Since(start)) - - case msg.isNotification(): - if strings.HasSuffix(msg.Method, notificationMethodSuffix) { - h.handleSubscriptionResult(msg) - continue - } - handleCall(msg) - - default: - handleCall(msg) - } - } - - for _, op := range resolvedops { - h.removeRequestOp(op) - } -} - -// handleSubscriptionResult processes subscription notifications. -func (h *handler) handleSubscriptionResult(msg *jsonrpcMessage) { - var result subscriptionResult - if err := json.Unmarshal(msg.Params, &result); err != nil { - h.log.Debug("Dropping invalid subscription message") - return - } - if h.clientSubs[result.ID] != nil { - h.clientSubs[result.ID].deliver(result.Result) - } -} - -// handleCallMsg executes a call message and returns the answer. -func (h *handler) handleCallMsg(ctx *callProc, msg *jsonrpcMessage) *jsonrpcMessage { - // [callStart] is the time the message was enqueued for handler processing - callStart := ctx.callStart - // [procStart] is the time the message cleared the [limiter] and began to be - // processed by the handler - procStart := ctx.procStart - // [execStart] is the time the message began to be executed by the handler - // - // Note: This can be different than the executionStart in [startCallProc] as - // the goroutine that handles execution may not be executed right away. - execStart := time.Now() - - switch { - case msg.isNotification(): - h.handleCall(ctx, msg) - h.log.Debug("Served "+msg.Method, "execTime", time.Since(execStart), "procTime", time.Since(procStart), "totalTime", time.Since(callStart)) - return nil - - case msg.isCall(): - resp := h.handleCall(ctx, msg) - var logctx []any - logctx = append(logctx, "reqid", idForLog{msg.ID}, "execTime", time.Since(execStart), "procTime", time.Since(procStart), "totalTime", time.Since(callStart)) - if resp.Error != nil { - logctx = append(logctx, "err", resp.Error.Message) - if resp.Error.Data != nil { - logctx = append(logctx, "errdata", formatErrorData(resp.Error.Data)) - } - h.log.Info("Served "+msg.Method, logctx...) - } else { - h.log.Debug("Served "+msg.Method, logctx...) - } - return resp - - case msg.hasValidID(): - return msg.errorResponse(&invalidRequestError{"invalid request"}) - - default: - return errorMessage(&invalidRequestError{"invalid request"}) - } -} - -// handleCall processes method calls. -func (h *handler) handleCall(cp *callProc, msg *jsonrpcMessage) *jsonrpcMessage { - if msg.isSubscribe() { - return h.handleSubscribe(cp, msg) - } - var callb *callback - if msg.isUnsubscribe() { - callb = h.unsubscribeCb - } else { - callb = h.reg.callback(msg.Method) - } - if callb == nil { - return msg.errorResponse(&methodNotFoundError{method: msg.Method}) - } - - args, err := parsePositionalArguments(msg.Params, callb.argTypes) - if err != nil { - return msg.errorResponse(&invalidParamsError{err.Error()}) - } - start := time.Now() - answer := h.runMethod(cp.ctx, msg, callb, args) - - // Collect the statistics for RPC calls if metrics is enabled. - // We only care about pure rpc call. Filter out subscription. - if callb != h.unsubscribeCb { - rpcRequestGauge.Inc(1) - if answer.Error != nil { - failedRequestGauge.Inc(1) - } else { - successfulRequestGauge.Inc(1) - } - rpcServingTimer.UpdateSince(start) - if metrics.EnabledExpensive { - updateServeTimeHistogram(msg.Method, answer.Error == nil, time.Since(start)) - } - } - - return answer -} - -// handleSubscribe processes *_subscribe method calls. -func (h *handler) handleSubscribe(cp *callProc, msg *jsonrpcMessage) *jsonrpcMessage { - if !h.allowSubscribe { - return msg.errorResponse(ErrNotificationsUnsupported) - } - - // Subscription method name is first argument. - name, err := parseSubscriptionName(msg.Params) - if err != nil { - return msg.errorResponse(&invalidParamsError{err.Error()}) - } - namespace := msg.namespace() - callb := h.reg.subscription(namespace, name) - if callb == nil { - return msg.errorResponse(&subscriptionNotFoundError{namespace, name}) - } - - // Parse subscription name arg too, but remove it before calling the callback. - argTypes := append([]reflect.Type{stringType}, callb.argTypes...) - args, err := parsePositionalArguments(msg.Params, argTypes) - if err != nil { - return msg.errorResponse(&invalidParamsError{err.Error()}) - } - args = args[1:] - - // Install notifier in context so the subscription handler can find it. - n := &Notifier{h: h, namespace: namespace} - cp.notifiers = append(cp.notifiers, n) - ctx := context.WithValue(cp.ctx, notifierKey{}, n) - - return h.runMethod(ctx, msg, callb, args) -} - -// runMethod runs the Go callback for an RPC method. -func (h *handler) runMethod(ctx context.Context, msg *jsonrpcMessage, callb *callback, args []reflect.Value) *jsonrpcMessage { - result, err := callb.call(ctx, msg.Method, args) - if err != nil { - return msg.errorResponse(err) - } - return msg.response(result) -} - -// unsubscribe is the callback function for all *_unsubscribe calls. -func (h *handler) unsubscribe(ctx context.Context, id ID) (bool, error) { - h.subLock.Lock() - defer h.subLock.Unlock() - - s := h.serverSubs[id] - if s == nil { - return false, ErrSubscriptionNotFound - } - close(s.err) - delete(h.serverSubs, id) - return true, nil -} - -type idForLog struct{ json.RawMessage } - -func (id idForLog) String() string { - if s, err := strconv.Unquote(string(id.RawMessage)); err == nil { - return s - } - return string(id.RawMessage) -} - -var errTruncatedOutput = errors.New("truncated output") - -type limitedBuffer struct { - output []byte - limit int -} - -func (buf *limitedBuffer) Write(data []byte) (int, error) { - avail := max(buf.limit, len(buf.output)) - if len(data) < avail { - buf.output = append(buf.output, data...) - return len(data), nil - } - buf.output = append(buf.output, data[:avail]...) - return avail, errTruncatedOutput -} - -func formatErrorData(v any) string { - buf := limitedBuffer{limit: 1024} - err := json.NewEncoder(&buf).Encode(v) - switch { - case err == nil: - return string(bytes.TrimRight(buf.output, "\n")) - case errors.Is(err, errTruncatedOutput): - return fmt.Sprintf("%s... (truncated)", buf.output) - default: - return fmt.Sprintf("bad error data (err=%v)", err) - } -} diff --git a/graft/subnet-evm/rpc/http.go b/graft/subnet-evm/rpc/http.go deleted file mode 100644 index 9ec06ec36e66..000000000000 --- a/graft/subnet-evm/rpc/http.go +++ /dev/null @@ -1,418 +0,0 @@ -// Copyright (C) 2019, Ava Labs, Inc. All rights reserved. -// See the file LICENSE for licensing terms. -// -// This file is a derived work, based on the go-ethereum library whose original -// notices appear below. -// -// It is distributed under a license compatible with the licensing terms of the -// original code from which it is derived. -// -// Much love to the original authors for their work. -// ********** -// Copyright 2015 The go-ethereum Authors -// This file is part of the go-ethereum library. -// -// The go-ethereum library is free software: you can redistribute it and/or modify -// it under the terms of the GNU Lesser General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// The go-ethereum library is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU Lesser General Public License for more details. -// -// You should have received a copy of the GNU Lesser General Public License -// along with the go-ethereum library. If not, see . - -package rpc - -import ( - "bytes" - "context" - "encoding/json" - "errors" - "fmt" - "io" - "math" - "mime" - "net/http" - "net/url" - "strconv" - "sync" - "time" -) - -const ( - defaultBodyLimit = 5 * 1024 * 1024 - contentType = "application/json" -) - -// https://www.jsonrpc.org/historical/json-rpc-over-http.html#id13 -var acceptedContentTypes = []string{contentType, "application/json-rpc", "application/jsonrequest"} - -type httpConn struct { - client *http.Client - url string - closeOnce sync.Once - closeCh chan interface{} - mu sync.Mutex // protects headers - headers http.Header - auth HTTPAuth -} - -// httpConn implements ServerCodec, but it is treated specially by Client -// and some methods don't work. The panic() stubs here exist to ensure -// this special treatment is correct. - -func (hc *httpConn) writeJSON(ctx context.Context, val interface{}, isError bool) error { - return hc.writeJSONSkipDeadline(ctx, val, isError, false) -} - -func (hc *httpConn) writeJSONSkipDeadline(context.Context, interface{}, bool, bool) error { - panic("writeJSON called on httpConn") -} - -func (hc *httpConn) peerInfo() PeerInfo { - panic("peerInfo called on httpConn") -} - -func (hc *httpConn) remoteAddr() string { - return hc.url -} - -func (hc *httpConn) readBatch() ([]*jsonrpcMessage, bool, error) { - <-hc.closeCh - return nil, false, io.EOF -} - -func (hc *httpConn) close() { - hc.closeOnce.Do(func() { close(hc.closeCh) }) -} - -func (hc *httpConn) closed() <-chan interface{} { - return hc.closeCh -} - -// HTTPTimeouts represents the configuration params for the HTTP RPC server. -type HTTPTimeouts struct { - // ReadTimeout is the maximum duration for reading the entire - // request, including the body. - // - // Because ReadTimeout does not let Handlers make per-request - // decisions on each request body's acceptable deadline or - // upload rate, most users will prefer to use - // ReadHeaderTimeout. It is valid to use them both. - ReadTimeout time.Duration - - // ReadHeaderTimeout is the amount of time allowed to read - // request headers. The connection's read deadline is reset - // after reading the headers and the Handler can decide what - // is considered too slow for the body. If ReadHeaderTimeout - // is zero, the value of ReadTimeout is used. If both are - // zero, there is no timeout. - ReadHeaderTimeout time.Duration - - // WriteTimeout is the maximum duration before timing out - // writes of the response. It is reset whenever a new - // request's header is read. Like ReadTimeout, it does not - // let Handlers make decisions on a per-request basis. - WriteTimeout time.Duration - - // IdleTimeout is the maximum amount of time to wait for the - // next request when keep-alives are enabled. If IdleTimeout - // is zero, the value of ReadTimeout is used. If both are - // zero, ReadHeaderTimeout is used. - IdleTimeout time.Duration -} - -// DefaultHTTPTimeouts represents the default timeout values used if further -// configuration is not provided. -var DefaultHTTPTimeouts = HTTPTimeouts{ - ReadTimeout: 30 * time.Second, - ReadHeaderTimeout: 30 * time.Second, - WriteTimeout: 30 * time.Second, - IdleTimeout: 120 * time.Second, -} - -// DialHTTP creates a new RPC client that connects to an RPC server over HTTP. -func DialHTTP(endpoint string) (*Client, error) { - return DialHTTPWithClient(endpoint, new(http.Client)) -} - -// DialHTTPWithClient creates a new RPC client that connects to an RPC server over HTTP -// using the provided HTTP Client. -// -// Deprecated: use DialOptions and the WithHTTPClient option. -func DialHTTPWithClient(endpoint string, client *http.Client) (*Client, error) { - // Sanity check URL so we don't end up with a client that will fail every request. - _, err := url.Parse(endpoint) - if err != nil { - return nil, err - } - - var cfg clientConfig - cfg.httpClient = client - fn := newClientTransportHTTP(endpoint, &cfg) - return newClient(context.Background(), &cfg, fn) -} - -func newClientTransportHTTP(endpoint string, cfg *clientConfig) reconnectFunc { - headers := make(http.Header, 2+len(cfg.httpHeaders)) - headers.Set("accept", contentType) - headers.Set("content-type", contentType) - for key, values := range cfg.httpHeaders { - headers[key] = values - } - - client := cfg.httpClient - if client == nil { - client = new(http.Client) - } - - hc := &httpConn{ - client: client, - headers: headers, - url: endpoint, - auth: cfg.httpAuth, - closeCh: make(chan interface{}), - } - - return func(ctx context.Context) (ServerCodec, error) { - return hc, nil - } -} - -// cleanlyCloseBody avoids sending unnecessary RST_STREAM and PING frames by -// ensuring the whole body is read before being closed. -// See https://blog.cloudflare.com/go-and-enhance-your-calm/#reading-bodies-in-go-can-be-unintuitive -func cleanlyCloseBody(body io.ReadCloser) error { - io.Copy(io.Discard, body) - return body.Close() -} - -func (c *Client) sendHTTP(ctx context.Context, op *requestOp, msg interface{}) error { - hc := c.writeConn.(*httpConn) - respBody, err := hc.doRequest(ctx, msg) - if err != nil { - return err - } - defer cleanlyCloseBody(respBody) - - var resp jsonrpcMessage - batch := [1]*jsonrpcMessage{&resp} - if err := json.NewDecoder(respBody).Decode(&resp); err != nil { - return err - } - op.resp <- batch[:] - return nil -} - -func (c *Client) sendBatchHTTP(ctx context.Context, op *requestOp, msgs []*jsonrpcMessage) error { - hc := c.writeConn.(*httpConn) - respBody, err := hc.doRequest(ctx, msgs) - if err != nil { - return err - } - defer cleanlyCloseBody(respBody) - - var respmsgs []*jsonrpcMessage - if err := json.NewDecoder(respBody).Decode(&respmsgs); err != nil { - return err - } - op.resp <- respmsgs - return nil -} - -func (hc *httpConn) doRequest(ctx context.Context, msg interface{}) (io.ReadCloser, error) { - body, err := json.Marshal(msg) - if err != nil { - return nil, err - } - req, err := http.NewRequestWithContext(ctx, http.MethodPost, hc.url, io.NopCloser(bytes.NewReader(body))) - if err != nil { - return nil, err - } - req.ContentLength = int64(len(body)) - req.GetBody = func() (io.ReadCloser, error) { return io.NopCloser(bytes.NewReader(body)), nil } - - // set headers - hc.mu.Lock() - req.Header = hc.headers.Clone() - hc.mu.Unlock() - setHeaders(req.Header, headersFromContext(ctx)) - - if hc.auth != nil { - if err := hc.auth(req.Header); err != nil { - return nil, err - } - } - - // do request - resp, err := hc.client.Do(req) - if err != nil { - return nil, err - } - if resp.StatusCode < 200 || resp.StatusCode >= 300 { - var buf bytes.Buffer - var body []byte - if _, err := buf.ReadFrom(resp.Body); err == nil { - body = buf.Bytes() - } - cleanlyCloseBody(resp.Body) - return nil, HTTPError{ - Status: resp.Status, - StatusCode: resp.StatusCode, - Body: body, - } - } - return resp.Body, nil -} - -// httpServerConn turns a HTTP connection into a Conn. -type httpServerConn struct { - io.Reader - io.Writer - r *http.Request -} - -func (s *Server) newHTTPServerConn(r *http.Request, w http.ResponseWriter) ServerCodec { - body := io.LimitReader(r.Body, int64(s.httpBodyLimit)) - conn := &httpServerConn{Reader: body, Writer: w, r: r} - - encoder := func(v any, isErrorResponse bool) error { - if !isErrorResponse { - return json.NewEncoder(conn).Encode(v) - } - - // It's an error response and requires special treatment. - // - // In case of a timeout error, the response must be written before the HTTP - // server's write timeout occurs. So we need to flush the response. The - // Content-Length header also needs to be set to ensure the client knows - // when it has the full response. - encdata, err := json.Marshal(v) - if err != nil { - return err - } - w.Header().Set("content-length", strconv.Itoa(len(encdata))) - - // If this request is wrapped in a handler that might remove Content-Length (such - // as the automatic gzip we do in package node), we need to ensure the HTTP server - // doesn't perform chunked encoding. In case WriteTimeout is reached, the chunked - // encoding might not be finished correctly, and some clients do not like it when - // the final chunk is missing. - w.Header().Set("transfer-encoding", "identity") - - _, err = w.Write(encdata) - if f, ok := w.(http.Flusher); ok { - f.Flush() - } - return err - } - - dec := json.NewDecoder(conn) - dec.UseNumber() - - return NewFuncCodec(conn, encoder, dec.Decode) -} - -// Close does nothing and always returns nil. -func (t *httpServerConn) Close() error { return nil } - -// RemoteAddr returns the peer address of the underlying connection. -func (t *httpServerConn) RemoteAddr() string { - return t.r.RemoteAddr -} - -// SetWriteDeadline does nothing and always returns nil. -func (t *httpServerConn) SetWriteDeadline(time.Time) error { return nil } - -// ServeHTTP serves JSON-RPC requests over HTTP. -func (s *Server) ServeHTTP(w http.ResponseWriter, r *http.Request) { - // Permit dumb empty requests for remote health-checks (AWS) - if r.Method == http.MethodGet && r.ContentLength == 0 && r.URL.RawQuery == "" { - w.WriteHeader(http.StatusOK) - return - } - if code, err := s.validateRequest(r); err != nil { - http.Error(w, err.Error(), code) - return - } - - // Create request-scoped context. - connInfo := PeerInfo{Transport: "http", RemoteAddr: r.RemoteAddr} - connInfo.HTTP.Version = r.Proto - connInfo.HTTP.Host = r.Host - connInfo.HTTP.Origin = r.Header.Get("Origin") - connInfo.HTTP.UserAgent = r.Header.Get("User-Agent") - ctx := r.Context() - ctx = context.WithValue(ctx, peerInfoContextKey{}, connInfo) - - // All checks passed, create a codec that reads directly from the request body - // until EOF, writes the response to w, and orders the server to process a - // single request. - w.Header().Set("content-type", contentType) - codec := s.newHTTPServerConn(r, w) - defer codec.close() - s.serveSingleRequest(ctx, codec) -} - -// validateRequest returns a non-zero response code and error message if the -// request is invalid. -func (s *Server) validateRequest(r *http.Request) (int, error) { - if r.Method == http.MethodPut || r.Method == http.MethodDelete { - return http.StatusMethodNotAllowed, errors.New("method not allowed") - } - if r.ContentLength > int64(s.httpBodyLimit) { - err := fmt.Errorf("content length too large (%d>%d)", r.ContentLength, s.httpBodyLimit) - return http.StatusRequestEntityTooLarge, err - } - // Allow OPTIONS (regardless of content-type) - if r.Method == http.MethodOptions { - return 0, nil - } - // Check content-type - if mt, _, err := mime.ParseMediaType(r.Header.Get("content-type")); err == nil { - for _, accepted := range acceptedContentTypes { - if accepted == mt { - return 0, nil - } - } - } - // Invalid content-type - err := fmt.Errorf("invalid content type, only %s is supported", contentType) - return http.StatusUnsupportedMediaType, err -} - -// ContextRequestTimeout returns the request timeout derived from the given context. -func ContextRequestTimeout(ctx context.Context) (time.Duration, bool) { - timeout := time.Duration(math.MaxInt64) - hasTimeout := false - setTimeout := func(d time.Duration) { - if d < timeout { - timeout = d - hasTimeout = true - } - } - - if deadline, ok := ctx.Deadline(); ok { - setTimeout(time.Until(deadline)) - } - - // If the context is an HTTP request context, use the server's WriteTimeout. - httpSrv, ok := ctx.Value(http.ServerContextKey).(*http.Server) - if ok && httpSrv.WriteTimeout > 0 { - wt := httpSrv.WriteTimeout - // When a write timeout is configured, we need to send the response message before - // the HTTP server cuts connection. So our internal timeout must be earlier than - // the server's true timeout. - // - // Note: Timeouts are sanitized to be a minimum of 1 second. - // Also see issue: https://github.com/golang/go/issues/47229 - wt -= 100 * time.Millisecond - setTimeout(wt) - } - - return timeout, hasTimeout -} diff --git a/graft/subnet-evm/rpc/http_test.go b/graft/subnet-evm/rpc/http_test.go deleted file mode 100644 index 9f6f3bfbbe0d..000000000000 --- a/graft/subnet-evm/rpc/http_test.go +++ /dev/null @@ -1,256 +0,0 @@ -// Copyright (C) 2019, Ava Labs, Inc. All rights reserved. -// See the file LICENSE for licensing terms. -// -// This file is a derived work, based on the go-ethereum library whose original -// notices appear below. -// -// It is distributed under a license compatible with the licensing terms of the -// original code from which it is derived. -// -// Much love to the original authors for their work. -// ********** -// Copyright 2017 The go-ethereum Authors -// This file is part of the go-ethereum library. -// -// The go-ethereum library is free software: you can redistribute it and/or modify -// it under the terms of the GNU Lesser General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// The go-ethereum library is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU Lesser General Public License for more details. -// -// You should have received a copy of the GNU Lesser General Public License -// along with the go-ethereum library. If not, see . - -package rpc - -import ( - "context" - "fmt" - "net/http" - "net/http/httptest" - "strings" - "testing" -) - -func confirmStatusCode(t *testing.T, got, want int) { - t.Helper() - if got == want { - return - } - if gotName := http.StatusText(got); len(gotName) > 0 { - if wantName := http.StatusText(want); len(wantName) > 0 { - t.Fatalf("response status code: got %d (%s), want %d (%s)", got, gotName, want, wantName) - } - } - t.Fatalf("response status code: got %d, want %d", got, want) -} - -func confirmRequestValidationCode(t *testing.T, method, contentType, body string, expectedStatusCode int) { - t.Helper() - - s := NewServer(0) - request := httptest.NewRequest(method, "http://url.com", strings.NewReader(body)) - if len(contentType) > 0 { - request.Header.Set("Content-Type", contentType) - } - code, err := s.validateRequest(request) - if code == 0 { - if err != nil { - t.Errorf("validation: got error %v, expected nil", err) - } - } else if err == nil { - t.Errorf("validation: code %d: got nil, expected error", code) - } - confirmStatusCode(t, code, expectedStatusCode) -} - -func TestHTTPErrorResponseWithDelete(t *testing.T) { - confirmRequestValidationCode(t, http.MethodDelete, contentType, "", http.StatusMethodNotAllowed) -} - -func TestHTTPErrorResponseWithPut(t *testing.T) { - confirmRequestValidationCode(t, http.MethodPut, contentType, "", http.StatusMethodNotAllowed) -} - -func TestHTTPErrorResponseWithMaxContentLength(t *testing.T) { - body := make([]rune, defaultBodyLimit+1) - confirmRequestValidationCode(t, - http.MethodPost, contentType, string(body), http.StatusRequestEntityTooLarge) -} - -func TestHTTPErrorResponseWithEmptyContentType(t *testing.T) { - confirmRequestValidationCode(t, http.MethodPost, "", "", http.StatusUnsupportedMediaType) -} - -func TestHTTPErrorResponseWithValidRequest(t *testing.T) { - confirmRequestValidationCode(t, http.MethodPost, contentType, "", 0) -} - -func confirmHTTPRequestYieldsStatusCode(t *testing.T, method, contentType, body string, expectedStatusCode int) { - t.Helper() - s := Server{} - ts := httptest.NewServer(&s) - defer ts.Close() - - request, err := http.NewRequest(method, ts.URL, strings.NewReader(body)) - if err != nil { - t.Fatalf("failed to create a valid HTTP request: %v", err) - } - if len(contentType) > 0 { - request.Header.Set("Content-Type", contentType) - } - resp, err := http.DefaultClient.Do(request) - if err != nil { - t.Fatalf("request failed: %v", err) - } - cleanlyCloseBody(resp.Body) - confirmStatusCode(t, resp.StatusCode, expectedStatusCode) -} - -func TestHTTPResponseWithEmptyGet(t *testing.T) { - confirmHTTPRequestYieldsStatusCode(t, http.MethodGet, "", "", http.StatusOK) -} - -// This checks that maxRequestContentLength is not applied to the response of a request. -func TestHTTPRespBodyUnlimited(t *testing.T) { - const respLength = defaultBodyLimit * 3 - - s := NewServer(0) - defer s.Stop() - s.RegisterName("test", largeRespService{respLength}) - ts := httptest.NewServer(s) - defer ts.Close() - - c, err := DialHTTP(ts.URL) - if err != nil { - t.Fatal(err) - } - defer c.Close() - - var r string - if err := c.Call(&r, "test_largeResp"); err != nil { - t.Fatal(err) - } - if len(r) != respLength { - t.Fatalf("response has wrong length %d, want %d", len(r), respLength) - } -} - -// Tests that an HTTP error results in an HTTPError instance -// being returned with the expected attributes. -func TestHTTPErrorResponse(t *testing.T) { - ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - http.Error(w, "error has occurred!", http.StatusTeapot) - })) - defer ts.Close() - - c, err := DialHTTP(ts.URL) - if err != nil { - t.Fatal(err) - } - - var r string - err = c.Call(&r, "test_method") - if err == nil { - t.Fatal("error was expected") - } - - httpErr, ok := err.(HTTPError) - if !ok { - t.Fatalf("unexpected error type %T", err) - } - - if httpErr.StatusCode != http.StatusTeapot { - t.Error("unexpected status code", httpErr.StatusCode) - } - if httpErr.Status != "418 I'm a teapot" { - t.Error("unexpected status text", httpErr.Status) - } - if body := string(httpErr.Body); body != "error has occurred!\n" { - t.Error("unexpected body", body) - } - - if errMsg := httpErr.Error(); errMsg != "418 I'm a teapot: error has occurred!\n" { - t.Error("unexpected error message", errMsg) - } -} - -func TestHTTPPeerInfo(t *testing.T) { - s := newTestServer() - defer s.Stop() - ts := httptest.NewServer(s) - defer ts.Close() - - c, err := Dial(ts.URL) - if err != nil { - t.Fatal(err) - } - c.SetHeader("user-agent", "ua-testing") - c.SetHeader("origin", "origin.example.com") - - // Request peer information. - var info PeerInfo - if err := c.Call(&info, "test_peerInfo"); err != nil { - t.Fatal(err) - } - - if info.RemoteAddr == "" { - t.Error("RemoteAddr not set") - } - if info.Transport != "http" { - t.Errorf("wrong Transport %q", info.Transport) - } - if info.HTTP.Version != "HTTP/1.1" { - t.Errorf("wrong HTTP.Version %q", info.HTTP.Version) - } - if info.HTTP.UserAgent != "ua-testing" { - t.Errorf("wrong HTTP.UserAgent %q", info.HTTP.UserAgent) - } - if info.HTTP.Origin != "origin.example.com" { - t.Errorf("wrong HTTP.Origin %q", info.HTTP.UserAgent) - } -} - -func TestNewContextWithHeaders(t *testing.T) { - expectedHeaders := 0 - server := httptest.NewServer(http.HandlerFunc(func(writer http.ResponseWriter, request *http.Request) { - for i := 0; i < expectedHeaders; i++ { - key, want := fmt.Sprintf("key-%d", i), fmt.Sprintf("val-%d", i) - if have := request.Header.Get(key); have != want { - t.Errorf("wrong request headers for %s, want: %s, have: %s", key, want, have) - } - } - writer.WriteHeader(http.StatusOK) - _, _ = writer.Write([]byte(`{}`)) - })) - defer server.Close() - - client, err := Dial(server.URL) - if err != nil { - t.Fatalf("failed to dial: %s", err) - } - defer client.Close() - - newHdr := func(k, v string) http.Header { - header := http.Header{} - header.Set(k, v) - return header - } - ctx1 := NewContextWithHeaders(context.Background(), newHdr("key-0", "val-0")) - ctx2 := NewContextWithHeaders(ctx1, newHdr("key-1", "val-1")) - ctx3 := NewContextWithHeaders(ctx2, newHdr("key-2", "val-2")) - - expectedHeaders = 3 - if err := client.CallContext(ctx3, nil, "test"); err != ErrNoResult { - t.Error("call failed", err) - } - - expectedHeaders = 2 - if err := client.CallContext(ctx2, nil, "test"); err != ErrNoResult { - t.Error("call failed:", err) - } -} diff --git a/graft/subnet-evm/rpc/json.go b/graft/subnet-evm/rpc/json.go deleted file mode 100644 index 4025866768fb..000000000000 --- a/graft/subnet-evm/rpc/json.go +++ /dev/null @@ -1,387 +0,0 @@ -// Copyright (C) 2019, Ava Labs, Inc. All rights reserved. -// See the file LICENSE for licensing terms. -// -// This file is a derived work, based on the go-ethereum library whose original -// notices appear below. -// -// It is distributed under a license compatible with the licensing terms of the -// original code from which it is derived. -// -// Much love to the original authors for their work. -// ********** -// Copyright 2015 The go-ethereum Authors -// This file is part of the go-ethereum library. -// -// The go-ethereum library is free software: you can redistribute it and/or modify -// it under the terms of the GNU Lesser General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// The go-ethereum library is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU Lesser General Public License for more details. -// -// You should have received a copy of the GNU Lesser General Public License -// along with the go-ethereum library. If not, see . - -package rpc - -import ( - "bytes" - "context" - "encoding/json" - "errors" - "fmt" - "io" - "reflect" - "strings" - "sync" - "time" -) - -const ( - vsn = "2.0" - serviceMethodSeparator = "_" - subscribeMethodSuffix = "_subscribe" - unsubscribeMethodSuffix = "_unsubscribe" - notificationMethodSuffix = "_subscription" - - defaultWriteTimeout = 10 * time.Second // used if context has no deadline -) - -var null = json.RawMessage("null") - -type subscriptionResult struct { - ID string `json:"subscription"` - Result json.RawMessage `json:"result,omitempty"` -} - -type subscriptionResultEnc struct { - ID string `json:"subscription"` - Result any `json:"result"` -} - -type jsonrpcSubscriptionNotification struct { - Version string `json:"jsonrpc"` - Method string `json:"method"` - Params subscriptionResultEnc `json:"params"` -} - -// A value of this type can a JSON-RPC request, notification, successful response or -// error response. Which one it is depends on the fields. -type jsonrpcMessage struct { - Version string `json:"jsonrpc,omitempty"` - ID json.RawMessage `json:"id,omitempty"` - Method string `json:"method,omitempty"` - Params json.RawMessage `json:"params,omitempty"` - Error *jsonError `json:"error,omitempty"` - Result json.RawMessage `json:"result,omitempty"` -} - -func (msg *jsonrpcMessage) isNotification() bool { - return msg.hasValidVersion() && msg.ID == nil && msg.Method != "" -} - -func (msg *jsonrpcMessage) isCall() bool { - return msg.hasValidVersion() && msg.hasValidID() && msg.Method != "" -} - -func (msg *jsonrpcMessage) isResponse() bool { - return msg.hasValidVersion() && msg.hasValidID() && msg.Method == "" && msg.Params == nil && (msg.Result != nil || msg.Error != nil) -} - -func (msg *jsonrpcMessage) hasValidID() bool { - return len(msg.ID) > 0 && msg.ID[0] != '{' && msg.ID[0] != '[' -} - -func (msg *jsonrpcMessage) hasValidVersion() bool { - return msg.Version == vsn -} - -func (msg *jsonrpcMessage) isSubscribe() bool { - return strings.HasSuffix(msg.Method, subscribeMethodSuffix) -} - -func (msg *jsonrpcMessage) isUnsubscribe() bool { - return strings.HasSuffix(msg.Method, unsubscribeMethodSuffix) -} - -func (msg *jsonrpcMessage) namespace() string { - before, _, _ := strings.Cut(msg.Method, serviceMethodSeparator) - return before -} - -func (msg *jsonrpcMessage) String() string { - b, _ := json.Marshal(msg) - return string(b) -} - -func (msg *jsonrpcMessage) errorResponse(err error) *jsonrpcMessage { - resp := errorMessage(err) - resp.ID = msg.ID - return resp -} - -func (msg *jsonrpcMessage) response(result interface{}) *jsonrpcMessage { - enc, err := json.Marshal(result) - if err != nil { - return msg.errorResponse(&internalServerError{errcodeMarshalError, err.Error()}) - } - return &jsonrpcMessage{Version: vsn, ID: msg.ID, Result: enc} -} - -func errorMessage(err error) *jsonrpcMessage { - msg := &jsonrpcMessage{Version: vsn, ID: null, Error: &jsonError{ - Code: errcodeDefault, - Message: err.Error(), - }} - ec, ok := err.(Error) - if ok { - msg.Error.Code = ec.ErrorCode() - } - de, ok := err.(DataError) - if ok { - msg.Error.Data = de.ErrorData() - } - return msg -} - -type jsonError struct { - Code int `json:"code"` - Message string `json:"message"` - Data interface{} `json:"data,omitempty"` -} - -func (err *jsonError) Error() string { - if err.Message == "" { - return fmt.Sprintf("json-rpc error %d", err.Code) - } - return err.Message -} - -func (err *jsonError) ErrorCode() int { - return err.Code -} - -func (err *jsonError) ErrorData() interface{} { - return err.Data -} - -// Conn is a subset of the methods of net.Conn which are sufficient for ServerCodec. -type Conn interface { - io.ReadWriteCloser - SetWriteDeadline(time.Time) error -} - -type deadlineCloser interface { - io.Closer - SetWriteDeadline(time.Time) error -} - -// ConnRemoteAddr wraps the RemoteAddr operation, which returns a description -// of the peer address of a connection. If a Conn also implements ConnRemoteAddr, this -// description is used in log messages. -type ConnRemoteAddr interface { - RemoteAddr() string -} - -// jsonCodec reads and writes JSON-RPC messages to the underlying connection. It also has -// support for parsing arguments and serializing (result) objects. -type jsonCodec struct { - remote string - closer sync.Once // close closed channel once - closeCh chan interface{} // closed on Close - decode decodeFunc // decoder to allow multiple transports - encMu sync.Mutex // guards the encoder - encode encodeFunc // encoder to allow multiple transports - conn deadlineCloser -} - -type encodeFunc = func(v interface{}, isErrorResponse bool) error - -type decodeFunc = func(v interface{}) error - -// NewFuncCodec creates a codec which uses the given functions to read and write. If conn -// implements ConnRemoteAddr, log messages will use it to include the remote address of -// the connection. -func NewFuncCodec(conn deadlineCloser, encode encodeFunc, decode decodeFunc) ServerCodec { - codec := &jsonCodec{ - closeCh: make(chan interface{}), - encode: encode, - decode: decode, - conn: conn, - } - if ra, ok := conn.(ConnRemoteAddr); ok { - codec.remote = ra.RemoteAddr() - } - return codec -} - -// NewCodec creates a codec on the given connection. If conn implements ConnRemoteAddr, log -// messages will use it to include the remote address of the connection. -func NewCodec(conn Conn) ServerCodec { - enc := json.NewEncoder(conn) - dec := json.NewDecoder(conn) - dec.UseNumber() - - encode := func(v interface{}, isErrorResponse bool) error { - return enc.Encode(v) - } - return NewFuncCodec(conn, encode, dec.Decode) -} - -func (c *jsonCodec) peerInfo() PeerInfo { - // This returns "ipc" because all other built-in transports have a separate codec type. - return PeerInfo{Transport: "ipc", RemoteAddr: c.remote} -} - -func (c *jsonCodec) remoteAddr() string { - return c.remote -} - -func (c *jsonCodec) readBatch() (messages []*jsonrpcMessage, batch bool, err error) { - // Decode the next JSON object in the input stream. - // This verifies basic syntax, etc. - var rawmsg json.RawMessage - if err := c.decode(&rawmsg); err != nil { - return nil, false, err - } - messages, batch = parseMessage(rawmsg) - for i, msg := range messages { - if msg == nil { - // Message is JSON 'null'. Replace with zero value so it - // will be treated like any other invalid message. - messages[i] = new(jsonrpcMessage) - } - } - return messages, batch, nil -} - -func (c *jsonCodec) writeJSON(ctx context.Context, val interface{}, isErrorResponse bool) error { - return c.writeJSONSkipDeadline(ctx, val, isErrorResponse, false) -} - -func (c *jsonCodec) writeJSONSkipDeadline(ctx context.Context, v interface{}, isErrorResponse bool, skip bool) error { - c.encMu.Lock() - defer c.encMu.Unlock() - - deadline := time.Now().Add(defaultWriteTimeout) - if !skip { - deadlineCtx, ok := ctx.Deadline() - if ok { - deadline = deadlineCtx - } - } - c.conn.SetWriteDeadline(deadline) - return c.encode(v, isErrorResponse) -} - -func (c *jsonCodec) close() { - c.closer.Do(func() { - close(c.closeCh) - c.conn.Close() - }) -} - -// Closed returns a channel which will be closed when Close is called -func (c *jsonCodec) closed() <-chan interface{} { - return c.closeCh -} - -// parseMessage parses raw bytes as a (batch of) JSON-RPC message(s). There are no error -// checks in this function because the raw message has already been syntax-checked when it -// is called. Any non-JSON-RPC messages in the input return the zero value of -// jsonrpcMessage. -func parseMessage(raw json.RawMessage) ([]*jsonrpcMessage, bool) { - if !isBatch(raw) { - msgs := []*jsonrpcMessage{{}} - json.Unmarshal(raw, &msgs[0]) - return msgs, false - } - dec := json.NewDecoder(bytes.NewReader(raw)) - dec.Token() // skip '[' - var msgs []*jsonrpcMessage - for dec.More() { - msgs = append(msgs, new(jsonrpcMessage)) - dec.Decode(&msgs[len(msgs)-1]) - } - return msgs, true -} - -// isBatch returns true when the first non-whitespace characters is '[' -func isBatch(raw json.RawMessage) bool { - for _, c := range raw { - // skip insignificant whitespace (http://www.ietf.org/rfc/rfc4627.txt) - if c == 0x20 || c == 0x09 || c == 0x0a || c == 0x0d { - continue - } - return c == '[' - } - return false -} - -// parsePositionalArguments tries to parse the given args to an array of values with the -// given types. It returns the parsed values or an error when the args could not be -// parsed. Missing optional arguments are returned as reflect.Zero values. -func parsePositionalArguments(rawArgs json.RawMessage, types []reflect.Type) ([]reflect.Value, error) { - dec := json.NewDecoder(bytes.NewReader(rawArgs)) - var args []reflect.Value - tok, err := dec.Token() - switch { - case err == io.EOF || tok == nil && err == nil: - // "params" is optional and may be empty. Also allow "params":null even though it's - // not in the spec because our own client used to send it. - case err != nil: - return nil, err - case tok == json.Delim('['): - // Read argument array. - if args, err = parseArgumentArray(dec, types); err != nil { - return nil, err - } - default: - return nil, errors.New("non-array args") - } - // Set any missing args to nil. - for i := len(args); i < len(types); i++ { - if types[i].Kind() != reflect.Ptr { - return nil, fmt.Errorf("missing value for required argument %d", i) - } - args = append(args, reflect.Zero(types[i])) - } - return args, nil -} - -func parseArgumentArray(dec *json.Decoder, types []reflect.Type) ([]reflect.Value, error) { - args := make([]reflect.Value, 0, len(types)) - for i := 0; dec.More(); i++ { - if i >= len(types) { - return args, fmt.Errorf("too many arguments, want at most %d", len(types)) - } - argval := reflect.New(types[i]) - if err := dec.Decode(argval.Interface()); err != nil { - return args, fmt.Errorf("invalid argument %d: %v", i, err) - } - if argval.IsNil() && types[i].Kind() != reflect.Ptr { - return args, fmt.Errorf("missing value for required argument %d", i) - } - args = append(args, argval.Elem()) - } - // Read end of args array. - _, err := dec.Token() - return args, err -} - -// parseSubscriptionName extracts the subscription name from an encoded argument array. -func parseSubscriptionName(rawArgs json.RawMessage) (string, error) { - dec := json.NewDecoder(bytes.NewReader(rawArgs)) - if tok, _ := dec.Token(); tok != json.Delim('[') { - return "", errors.New("non-array args") - } - v, _ := dec.Token() - method, ok := v.(string) - if !ok { - return "", errors.New("expected subscription name as first argument") - } - return method, nil -} diff --git a/graft/subnet-evm/rpc/metrics.go b/graft/subnet-evm/rpc/metrics.go deleted file mode 100644 index 9f799c461f52..000000000000 --- a/graft/subnet-evm/rpc/metrics.go +++ /dev/null @@ -1,70 +0,0 @@ -// Copyright (C) 2019, Ava Labs, Inc. All rights reserved. -// See the file LICENSE for licensing terms. -// -// This file is a derived work, based on the go-ethereum library whose original -// notices appear below. -// -// It is distributed under a license compatible with the licensing terms of the -// original code from which it is derived. -// -// Much love to the original authors for their work. -// ********** -// Copyright 2020 The go-ethereum Authors -// This file is part of the go-ethereum library. -// -// The go-ethereum library is free software: you can redistribute it and/or modify -// it under the terms of the GNU Lesser General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// The go-ethereum library is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU Lesser General Public License for more details. -// -// You should have received a copy of the GNU Lesser General Public License -// along with the go-ethereum library. If not, see . - -package rpc - -import ( - "fmt" - "time" - - "github.com/ava-labs/libevm/metrics" - - // Force libevm metrics of the same name to be registered first. - _ "github.com/ava-labs/libevm/rpc" -) - -// ====== If resolving merge conflicts ====== -// -// All calls to metrics.NewRegistered*() for metrics also defined in libevm/rpc have -// been replaced with metrics.GetOrRegister*() to get metrics already registered in -// libevm/rpc or register them here otherwise. These replacements ensure the same -// metrics are shared between the two packages. -var ( - rpcRequestGauge = metrics.GetOrRegisterGauge("rpc/requests", nil) - successfulRequestGauge = metrics.GetOrRegisterGauge("rpc/success", nil) - failedRequestGauge = metrics.GetOrRegisterGauge("rpc/failure", nil) - - // serveTimeHistName is the prefix of the per-request serving time histograms. - serveTimeHistName = "rpc/duration" - - rpcServingTimer = metrics.GetOrRegisterTimer("rpc/duration/all", nil) -) - -// updateServeTimeHistogram tracks the serving time of a remote RPC call. -func updateServeTimeHistogram(method string, success bool, elapsed time.Duration) { - note := "success" - if !success { - note = "failure" - } - h := fmt.Sprintf("%s/%s/%s", serveTimeHistName, method, note) - sampler := func() metrics.Sample { - return metrics.ResettingSample( - metrics.NewExpDecaySample(1028, 0.015), - ) - } - metrics.GetOrRegisterHistogramLazy(h, nil, sampler).Update(elapsed.Nanoseconds()) -} diff --git a/graft/subnet-evm/rpc/server.go b/graft/subnet-evm/rpc/server.go deleted file mode 100644 index 888db63916c1..000000000000 --- a/graft/subnet-evm/rpc/server.go +++ /dev/null @@ -1,251 +0,0 @@ -// Copyright (C) 2019, Ava Labs, Inc. All rights reserved. -// See the file LICENSE for licensing terms. -// -// This file is a derived work, based on the go-ethereum library whose original -// notices appear below. -// -// It is distributed under a license compatible with the licensing terms of the -// original code from which it is derived. -// -// Much love to the original authors for their work. -// ********** -// Copyright 2015 The go-ethereum Authors -// This file is part of the go-ethereum library. -// -// The go-ethereum library is free software: you can redistribute it and/or modify -// it under the terms of the GNU Lesser General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// The go-ethereum library is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU Lesser General Public License for more details. -// -// You should have received a copy of the GNU Lesser General Public License -// along with the go-ethereum library. If not, see . - -package rpc - -import ( - "context" - "io" - "sync" - "sync/atomic" - "time" - - "github.com/ava-labs/libevm/log" -) - -const MetadataApi = "rpc" - -// CodecOption specifies which type of messages a codec supports. -// -// Deprecated: this option is no longer honored by Server. -type CodecOption int - -const ( - // OptionMethodInvocation is an indication that the codec supports RPC method calls - OptionMethodInvocation CodecOption = 1 << iota - - // OptionSubscriptions is an indication that the codec supports RPC notifications - OptionSubscriptions = 1 << iota // support pub sub -) - -// Server is an RPC server. -type Server struct { - services serviceRegistry - idgen func() ID - maximumDuration time.Duration - - mutex sync.Mutex - codecs map[ServerCodec]struct{} - run atomic.Bool - batchItemLimit int - batchResponseLimit int - httpBodyLimit int -} - -// NewServer creates a new server instance with no registered handlers. -// -// If [maximumDuration] > 0, the deadline of incoming requests is -// [maximumDuration] in the future. Otherwise, no deadline is assigned to -// incoming requests. -func NewServer(maximumDuration time.Duration) *Server { - server := &Server{ - idgen: randomIDGenerator(), - codecs: make(map[ServerCodec]struct{}), - maximumDuration: maximumDuration, - httpBodyLimit: defaultBodyLimit, - } - server.run.Store(true) - // Register the default service providing meta information about the RPC service such - // as the services and methods it offers. - rpcService := &RPCService{server} - server.RegisterName(MetadataApi, rpcService) - return server -} - -// SetBatchLimits sets limits applied to batch requests. There are two limits: 'itemLimit' -// is the maximum number of items in a batch. 'maxResponseSize' is the maximum number of -// response bytes across all requests in a batch. -// -// This method should be called before processing any requests via ServeCodec, ServeHTTP, -// ServeListener etc. -func (s *Server) SetBatchLimits(itemLimit, maxResponseSize int) { - s.batchItemLimit = itemLimit - s.batchResponseLimit = maxResponseSize -} - -// SetHTTPBodyLimit sets the size limit for HTTP requests. -// -// This method should be called before processing any requests via ServeHTTP. -func (s *Server) SetHTTPBodyLimit(limit int) { - s.httpBodyLimit = limit -} - -// RegisterName creates a service for the given receiver type under the given name. When no -// methods on the given receiver match the criteria to be either a RPC method or a -// subscription an error is returned. Otherwise a new service is created and added to the -// service collection this server provides to clients. -func (s *Server) RegisterName(name string, receiver interface{}) error { - return s.services.registerName(name, receiver) -} - -// ServeCodec reads incoming requests from codec, calls the appropriate callback and writes -// the response back using the given codec. It will block until the codec is closed or the -// server is stopped. In either case the codec is closed. -// -// Note that codec options are no longer supported. -func (s *Server) ServeCodec(codec ServerCodec, options CodecOption, apiMaxDuration, refillRate, maxStored time.Duration) { - defer codec.close() - - if !s.trackCodec(codec) { - return - } - defer s.untrackCodec(codec) - - cfg := &clientConfig{ - idgen: s.idgen, - batchItemLimit: s.batchItemLimit, - batchResponseLimit: s.batchResponseLimit, - } - c := initClient(codec, &s.services, cfg, apiMaxDuration, refillRate, maxStored) - <-codec.closed() - c.Close() -} - -func (s *Server) trackCodec(codec ServerCodec) bool { - s.mutex.Lock() - defer s.mutex.Unlock() - - if !s.run.Load() { - return false // Don't serve if server is stopped. - } - s.codecs[codec] = struct{}{} - return true -} - -func (s *Server) untrackCodec(codec ServerCodec) { - s.mutex.Lock() - defer s.mutex.Unlock() - - delete(s.codecs, codec) -} - -// serveSingleRequest reads and processes a single RPC request from the given codec. This -// is used to serve HTTP connections. Subscriptions and reverse calls are not allowed in -// this mode. -func (s *Server) serveSingleRequest(ctx context.Context, codec ServerCodec) { - // Don't serve if server is stopped. - if !s.run.Load() { - return - } - - h := newHandler(ctx, codec, s.idgen, &s.services, s.batchItemLimit, s.batchResponseLimit) - h.deadlineContext = s.maximumDuration - h.allowSubscribe = false - defer h.close(io.EOF, nil) - - reqs, batch, err := codec.readBatch() - if err != nil { - if err != io.EOF { - resp := errorMessage(&invalidMessageError{"parse error"}) - codec.writeJSON(ctx, resp, true) - } - return - } - if batch { - h.handleBatch(reqs) - } else { - h.handleMsg(reqs[0]) - } -} - -// Stop stops reading new requests, waits for stopPendingRequestTimeout to allow pending -// requests to finish, then closes all codecs which will cancel pending requests and -// subscriptions. -func (s *Server) Stop() { - s.mutex.Lock() - defer s.mutex.Unlock() - - if s.run.CompareAndSwap(true, false) { - log.Debug("RPC server shutting down") - for codec := range s.codecs { - codec.close() - } - } -} - -// RPCService gives meta information about the server. -// e.g. gives information about the loaded modules. -type RPCService struct { - server *Server -} - -// Modules returns the list of RPC services with their version number -func (s *RPCService) Modules() map[string]string { - s.server.services.mu.Lock() - defer s.server.services.mu.Unlock() - - modules := make(map[string]string) - for name := range s.server.services.services { - modules[name] = "1.0" - } - return modules -} - -// PeerInfo contains information about the remote end of the network connection. -// -// This is available within RPC method handlers through the context. Call -// PeerInfoFromContext to get information about the client connection related to -// the current method call. -type PeerInfo struct { - // Transport is name of the protocol used by the client. - // This can be "http", "ws" or "ipc". - Transport string - - // Address of client. This will usually contain the IP address and port. - RemoteAddr string - - // Additional information for HTTP and WebSocket connections. - HTTP struct { - // Protocol version, i.e. "HTTP/1.1". This is not set for WebSocket. - Version string - // Header values sent by the client. - UserAgent string - Origin string - Host string - } -} - -type peerInfoContextKey struct{} - -// PeerInfoFromContext returns information about the client's network connection. -// Use this with the context passed to RPC method handler functions. -// -// The zero value is returned if no connection info is present in ctx. -func PeerInfoFromContext(ctx context.Context) PeerInfo { - info, _ := ctx.Value(peerInfoContextKey{}).(PeerInfo) - return info -} diff --git a/graft/subnet-evm/rpc/server_test.go b/graft/subnet-evm/rpc/server_test.go deleted file mode 100644 index 82ebe9bcb159..000000000000 --- a/graft/subnet-evm/rpc/server_test.go +++ /dev/null @@ -1,204 +0,0 @@ -// Copyright (C) 2019, Ava Labs, Inc. All rights reserved. -// See the file LICENSE for licensing terms. -// -// This file is a derived work, based on the go-ethereum library whose original -// notices appear below. -// -// It is distributed under a license compatible with the licensing terms of the -// original code from which it is derived. -// -// Much love to the original authors for their work. -// ********** -// Copyright 2015 The go-ethereum Authors -// This file is part of the go-ethereum library. -// -// The go-ethereum library is free software: you can redistribute it and/or modify -// it under the terms of the GNU Lesser General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// The go-ethereum library is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU Lesser General Public License for more details. -// -// You should have received a copy of the GNU Lesser General Public License -// along with the go-ethereum library. If not, see . - -package rpc - -import ( - "bufio" - "io" - "net" - "os" - "path/filepath" - "strings" - "testing" - "time" -) - -func TestServerRegisterName(t *testing.T) { - server := NewServer(0) - service := new(testService) - - svcName := "test" - if err := server.RegisterName(svcName, service); err != nil { - t.Fatalf("%v", err) - } - - if len(server.services.services) != 2 { - t.Fatalf("Expected 2 service entries, got %d", len(server.services.services)) - } - - svc, ok := server.services.services[svcName] - if !ok { - t.Fatalf("Expected service %s to be registered", svcName) - } - - wantCallbacks := 14 - if len(svc.callbacks) != wantCallbacks { - t.Errorf("Expected %d callbacks for service 'service', got %d", wantCallbacks, len(svc.callbacks)) - } -} - -func TestServer(t *testing.T) { - files, err := os.ReadDir("testdata") - if err != nil { - t.Fatal("where'd my testdata go?") - } - for _, f := range files { - if f.IsDir() || strings.HasPrefix(f.Name(), ".") { - continue - } - path := filepath.Join("testdata", f.Name()) - name := strings.TrimSuffix(f.Name(), filepath.Ext(f.Name())) - t.Run(name, func(t *testing.T) { - runTestScript(t, path) - }) - } -} - -func runTestScript(t *testing.T, file string) { - server := newTestServer() - server.SetBatchLimits(4, 100000) - content, err := os.ReadFile(file) - if err != nil { - t.Fatal(err) - } - - clientConn, serverConn := net.Pipe() - defer clientConn.Close() - go server.ServeCodec(NewCodec(serverConn), 0, 0, 0, 0) - readbuf := bufio.NewReader(clientConn) - for _, line := range strings.Split(string(content), "\n") { - line = strings.TrimSpace(line) - switch { - case len(line) == 0 || strings.HasPrefix(line, "//"): - // skip comments, blank lines - continue - case strings.HasPrefix(line, "--> "): - t.Log(line) - // write to connection - clientConn.SetWriteDeadline(time.Now().Add(5 * time.Second)) - if _, err := io.WriteString(clientConn, line[4:]+"\n"); err != nil { - t.Fatalf("write error: %v", err) - } - case strings.HasPrefix(line, "<-- "): - t.Log(line) - want := line[4:] - // read line from connection and compare text - clientConn.SetReadDeadline(time.Now().Add(5 * time.Second)) - sent, err := readbuf.ReadString('\n') - if err != nil { - t.Fatalf("read error: %v", err) - } - sent = strings.TrimRight(sent, "\r\n") - if sent != want { - t.Errorf("wrong line from server\ngot: %s\nwant: %s", sent, want) - } - default: - panic("invalid line in test script: " + line) - } - } -} - -// // This test checks that responses are delivered for very short-lived connections that -// // only carry a single request. -// func TestServerShortLivedConn(t *testing.T) { -// server := newTestServer() -// defer server.Stop() - -// listener, err := net.Listen("tcp", "127.0.0.1:0") -// if err != nil { -// t.Fatal("can't listen:", err) -// } -// defer listener.Close() -// go server.ServeListener(listener) - -// var ( -// request = `{"jsonrpc":"2.0","id":1,"method":"rpc_modules"}` + "\n" -// wantResp = `{"jsonrpc":"2.0","id":1,"result":{"nftest":"1.0","rpc":"1.0","test":"1.0"}}` + "\n" -// deadline = time.Now().Add(10 * time.Second) -// ) -// for i := 0; i < 20; i++ { -// conn, err := net.Dial("tcp", listener.Addr().String()) -// if err != nil { -// t.Fatal("can't dial:", err) -// } -// conn.SetDeadline(deadline) -// // Write the request, then half-close the connection so the server stops reading. -// conn.Write([]byte(request)) -// conn.(*net.TCPConn).CloseWrite() -// // Now try to get the response. -// buf := make([]byte, 2000) -// n, err := conn.Read(buf) -// conn.Close() -// -// if err != nil { -// t.Fatal("read error:", err) -// } -// if !bytes.Equal(buf[:n], []byte(wantResp)) { -// t.Fatalf("wrong response: %s", buf[:n]) -// } -// } -// } - -func TestServerBatchResponseSizeLimit(t *testing.T) { - server := newTestServer() - defer server.Stop() - server.SetBatchLimits(100, 60) - var ( - batch []BatchElem - client = DialInProc(server) - ) - defer client.Close() - for i := 0; i < 5; i++ { - batch = append(batch, BatchElem{ - Method: "test_echo", - Args: []any{"x", 1}, - Result: new(echoResult), - }) - } - if err := client.BatchCall(batch); err != nil { - t.Fatal("error sending batch:", err) - } - for i := range batch { - // We expect the first two queries to be ok, but after that the size limit takes effect. - if i < 2 { - if batch[i].Error != nil { - t.Fatalf("batch elem %d has unexpected error: %v", i, batch[i].Error) - } - continue - } - // After two, we expect an error. - re, ok := batch[i].Error.(Error) - if !ok { - t.Fatalf("batch elem %d has wrong error: %v", i, batch[i].Error) - } - wantedCode := errcodeResponseTooLarge - if re.ErrorCode() != wantedCode { - t.Errorf("batch elem %d wrong error code, have %d want %d", i, re.ErrorCode(), wantedCode) - } - } -} diff --git a/graft/subnet-evm/rpc/service.go b/graft/subnet-evm/rpc/service.go deleted file mode 100644 index a1c51a40dd25..000000000000 --- a/graft/subnet-evm/rpc/service.go +++ /dev/null @@ -1,260 +0,0 @@ -// Copyright (C) 2019, Ava Labs, Inc. All rights reserved. -// See the file LICENSE for licensing terms. -// -// This file is a derived work, based on the go-ethereum library whose original -// notices appear below. -// -// It is distributed under a license compatible with the licensing terms of the -// original code from which it is derived. -// -// Much love to the original authors for their work. -// ********** -// Copyright 2019 The go-ethereum Authors -// This file is part of the go-ethereum library. -// -// The go-ethereum library is free software: you can redistribute it and/or modify -// it under the terms of the GNU Lesser General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// The go-ethereum library is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU Lesser General Public License for more details. -// -// You should have received a copy of the GNU Lesser General Public License -// along with the go-ethereum library. If not, see . - -package rpc - -import ( - "context" - "fmt" - "reflect" - "runtime" - "strings" - "sync" - "unicode" - - "github.com/ava-labs/libevm/log" -) - -var ( - contextType = reflect.TypeOf((*context.Context)(nil)).Elem() - errorType = reflect.TypeOf((*error)(nil)).Elem() - subscriptionType = reflect.TypeOf(Subscription{}) - stringType = reflect.TypeOf("") -) - -type serviceRegistry struct { - mu sync.Mutex - services map[string]service -} - -// service represents a registered object. -type service struct { - name string // name for service - callbacks map[string]*callback // registered handlers - subscriptions map[string]*callback // available subscriptions/notifications -} - -// callback is a method callback which was registered in the server -type callback struct { - fn reflect.Value // the function - rcvr reflect.Value // receiver object of method, set if fn is method - argTypes []reflect.Type // input argument types - hasCtx bool // method's first argument is a context (not included in argTypes) - errPos int // err return idx, of -1 when method cannot return error - isSubscribe bool // true if this is a subscription callback -} - -func (r *serviceRegistry) registerName(name string, rcvr interface{}) error { - rcvrVal := reflect.ValueOf(rcvr) - if name == "" { - return fmt.Errorf("no service name for type %s", rcvrVal.Type().String()) - } - callbacks := suitableCallbacks(rcvrVal) - if len(callbacks) == 0 { - return fmt.Errorf("service %T doesn't have any suitable methods/subscriptions to expose", rcvr) - } - - r.mu.Lock() - defer r.mu.Unlock() - if r.services == nil { - r.services = make(map[string]service) - } - svc, ok := r.services[name] - if !ok { - svc = service{ - name: name, - callbacks: make(map[string]*callback), - subscriptions: make(map[string]*callback), - } - r.services[name] = svc - } - for name, cb := range callbacks { - if cb.isSubscribe { - svc.subscriptions[name] = cb - } else { - svc.callbacks[name] = cb - } - } - return nil -} - -// callback returns the callback corresponding to the given RPC method name. -func (r *serviceRegistry) callback(method string) *callback { - before, after, found := strings.Cut(method, serviceMethodSeparator) - if !found { - return nil - } - r.mu.Lock() - defer r.mu.Unlock() - return r.services[before].callbacks[after] -} - -// subscription returns a subscription callback in the given service. -func (r *serviceRegistry) subscription(service, name string) *callback { - r.mu.Lock() - defer r.mu.Unlock() - return r.services[service].subscriptions[name] -} - -// suitableCallbacks iterates over the methods of the given type. It determines if a method -// satisfies the criteria for a RPC callback or a subscription callback and adds it to the -// collection of callbacks. See server documentation for a summary of these criteria. -func suitableCallbacks(receiver reflect.Value) map[string]*callback { - typ := receiver.Type() - callbacks := make(map[string]*callback) - for m := 0; m < typ.NumMethod(); m++ { - method := typ.Method(m) - if method.PkgPath != "" { - continue // method not exported - } - cb := newCallback(receiver, method.Func) - if cb == nil { - continue // function invalid - } - name := formatName(method.Name) - callbacks[name] = cb - } - return callbacks -} - -// newCallback turns fn (a function) into a callback object. It returns nil if the function -// is unsuitable as an RPC callback. -func newCallback(receiver, fn reflect.Value) *callback { - fntype := fn.Type() - c := &callback{fn: fn, rcvr: receiver, errPos: -1, isSubscribe: isPubSub(fntype)} - // Determine parameter types. They must all be exported or builtin types. - c.makeArgTypes() - - // Verify return types. The function must return at most one error - // and/or one other non-error value. - outs := make([]reflect.Type, fntype.NumOut()) - for i := 0; i < fntype.NumOut(); i++ { - outs[i] = fntype.Out(i) - } - if len(outs) > 2 { - return nil - } - // If an error is returned, it must be the last returned value. - switch { - case len(outs) == 1 && isErrorType(outs[0]): - c.errPos = 0 - case len(outs) == 2: - if isErrorType(outs[0]) || !isErrorType(outs[1]) { - return nil - } - c.errPos = 1 - } - return c -} - -// makeArgTypes composes the argTypes list. -func (c *callback) makeArgTypes() { - fntype := c.fn.Type() - // Skip receiver and context.Context parameter (if present). - firstArg := 0 - if c.rcvr.IsValid() { - firstArg++ - } - if fntype.NumIn() > firstArg && fntype.In(firstArg) == contextType { - c.hasCtx = true - firstArg++ - } - // Add all remaining parameters. - c.argTypes = make([]reflect.Type, fntype.NumIn()-firstArg) - for i := firstArg; i < fntype.NumIn(); i++ { - c.argTypes[i-firstArg] = fntype.In(i) - } -} - -// call invokes the callback. -func (c *callback) call(ctx context.Context, method string, args []reflect.Value) (res interface{}, errRes error) { - // Create the argument slice. - fullargs := make([]reflect.Value, 0, 2+len(args)) - if c.rcvr.IsValid() { - fullargs = append(fullargs, c.rcvr) - } - if c.hasCtx { - fullargs = append(fullargs, reflect.ValueOf(ctx)) - } - fullargs = append(fullargs, args...) - - // Catch panic while running the callback. - defer func() { - if err := recover(); err != nil { - const size = 64 << 10 - buf := make([]byte, size) - buf = buf[:runtime.Stack(buf, false)] - log.Error("RPC method " + method + " crashed: " + fmt.Sprintf("%v\n%s", err, buf)) - errRes = &internalServerError{errcodePanic, "method handler crashed"} - } - }() - // Run the callback. - results := c.fn.Call(fullargs) - if len(results) == 0 { - return nil, nil - } - if c.errPos >= 0 && !results[c.errPos].IsNil() { - // Method has returned non-nil error value. - err := results[c.errPos].Interface().(error) - return reflect.Value{}, err - } - return results[0].Interface(), nil -} - -// Does t satisfy the error interface? -func isErrorType(t reflect.Type) bool { - return t.Implements(errorType) -} - -// Is t Subscription or *Subscription? -func isSubscriptionType(t reflect.Type) bool { - for t.Kind() == reflect.Ptr { - t = t.Elem() - } - return t == subscriptionType -} - -// isPubSub tests whether the given method has as as first argument a context.Context and -// returns the pair (Subscription, error). -func isPubSub(methodType reflect.Type) bool { - // numIn(0) is the receiver type - if methodType.NumIn() < 2 || methodType.NumOut() != 2 { - return false - } - return methodType.In(1) == contextType && - isSubscriptionType(methodType.Out(0)) && - isErrorType(methodType.Out(1)) -} - -// formatName converts to first character of name to lowercase. -func formatName(name string) string { - ret := []rune(name) - if len(ret) > 0 { - ret[0] = unicode.ToLower(ret[0]) - } - return string(ret) -} diff --git a/graft/subnet-evm/rpc/subscription.go b/graft/subnet-evm/rpc/subscription.go deleted file mode 100644 index 4384478afccd..000000000000 --- a/graft/subnet-evm/rpc/subscription.go +++ /dev/null @@ -1,392 +0,0 @@ -// Copyright (C) 2019, Ava Labs, Inc. All rights reserved. -// See the file LICENSE for licensing terms. -// -// This file is a derived work, based on the go-ethereum library whose original -// notices appear below. -// -// It is distributed under a license compatible with the licensing terms of the -// original code from which it is derived. -// -// Much love to the original authors for their work. -// ********** -// Copyright 2016 The go-ethereum Authors -// This file is part of the go-ethereum library. -// -// The go-ethereum library is free software: you can redistribute it and/or modify -// it under the terms of the GNU Lesser General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// The go-ethereum library is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU Lesser General Public License for more details. -// -// You should have received a copy of the GNU Lesser General Public License -// along with the go-ethereum library. If not, see . - -package rpc - -import ( - "container/list" - "context" - crand "crypto/rand" - "encoding/binary" - "encoding/hex" - "encoding/json" - "errors" - "math/rand" - "reflect" - "strings" - "sync" - "time" -) - -var ( - // ErrNotificationsUnsupported is returned by the client when the connection doesn't - // support notifications. You can use this error value to check for subscription - // support like this: - // - // sub, err := client.EthSubscribe(ctx, channel, "newHeads", true) - // if errors.Is(err, rpc.ErrNotificationsUnsupported) { - // // Server does not support subscriptions, fall back to polling. - // } - // - ErrNotificationsUnsupported = notificationsUnsupportedError{} - - // ErrSubscriptionNotFound is returned when the notification for the given id is not found - ErrSubscriptionNotFound = errors.New("subscription not found") -) - -var globalGen = randomIDGenerator() - -// ID defines a pseudo random number that is used to identify RPC subscriptions. -type ID string - -// NewID returns a new, random ID. -func NewID() ID { - return globalGen() -} - -// randomIDGenerator returns a function generates a random IDs. -func randomIDGenerator() func() ID { - var buf = make([]byte, 8) - var seed int64 - if _, err := crand.Read(buf); err == nil { - seed = int64(binary.BigEndian.Uint64(buf)) - } else { - seed = int64(time.Now().Nanosecond()) - } - - var ( - mu sync.Mutex - rng = rand.New(rand.NewSource(seed)) - ) - return func() ID { - mu.Lock() - defer mu.Unlock() - id := make([]byte, 16) - rng.Read(id) - return encodeID(id) - } -} - -func encodeID(b []byte) ID { - id := hex.EncodeToString(b) - id = strings.TrimLeft(id, "0") - if id == "" { - id = "0" // ID's are RPC quantities, no leading zero's and 0 is 0x0. - } - return ID("0x" + id) -} - -type notifierKey struct{} - -// NotifierFromContext returns the Notifier value stored in ctx, if any. -func NotifierFromContext(ctx context.Context) (*Notifier, bool) { - n, ok := ctx.Value(notifierKey{}).(*Notifier) - return n, ok -} - -// Notifier is tied to a RPC connection that supports subscriptions. -// Server callbacks use the notifier to send notifications. -type Notifier struct { - h *handler - namespace string - - mu sync.Mutex - sub *Subscription - buffer []any - callReturned bool - activated bool -} - -// CreateSubscription returns a new subscription that is coupled to the -// RPC connection. By default subscriptions are inactive and notifications -// are dropped until the subscription is marked as active. This is done -// by the RPC server after the subscription ID is send to the client. -func (n *Notifier) CreateSubscription() *Subscription { - n.mu.Lock() - defer n.mu.Unlock() - - if n.sub != nil { - panic("can't create multiple subscriptions with Notifier") - } else if n.callReturned { - panic("can't create subscription after subscribe call has returned") - } - n.sub = &Subscription{ID: n.h.idgen(), namespace: n.namespace, err: make(chan error, 1)} - return n.sub -} - -// Notify sends a notification to the client with the given data as payload. -// If an error occurs the RPC connection is closed and the error is returned. -func (n *Notifier) Notify(id ID, data any) error { - n.mu.Lock() - defer n.mu.Unlock() - - if n.sub == nil { - panic("can't Notify before subscription is created") - } else if n.sub.ID != id { - panic("Notify with wrong ID") - } - if n.activated { - return n.send(n.sub, data) - } - n.buffer = append(n.buffer, data) - return nil -} - -// Closed returns a channel that is closed when the RPC connection is closed. -// Deprecated: use subscription error channel -func (n *Notifier) Closed() <-chan interface{} { - return n.h.conn.closed() -} - -// takeSubscription returns the subscription (if one has been created). No subscription can -// be created after this call. -func (n *Notifier) takeSubscription() *Subscription { - n.mu.Lock() - defer n.mu.Unlock() - n.callReturned = true - return n.sub -} - -// activate is called after the subscription ID was sent to client. Notifications are -// buffered before activation. This prevents notifications being sent to the client before -// the subscription ID is sent to the client. -func (n *Notifier) activate() error { - n.mu.Lock() - defer n.mu.Unlock() - - for _, data := range n.buffer { - if err := n.send(n.sub, data); err != nil { - return err - } - } - n.activated = true - return nil -} - -func (n *Notifier) send(sub *Subscription, data any) error { - msg := jsonrpcSubscriptionNotification{ - Version: vsn, - Method: n.namespace + notificationMethodSuffix, - Params: subscriptionResultEnc{ - ID: string(sub.ID), - Result: data, - }, - } - return n.h.conn.writeJSON(context.Background(), &msg, false) -} - -// A Subscription is created by a notifier and tied to that notifier. The client can use -// this subscription to wait for an unsubscribe request for the client, see Err(). -type Subscription struct { - ID ID - namespace string - err chan error // closed on unsubscribe -} - -// Err returns a channel that is closed when the client send an unsubscribe request. -func (s *Subscription) Err() <-chan error { - return s.err -} - -// MarshalJSON marshals a subscription as its ID. -func (s *Subscription) MarshalJSON() ([]byte, error) { - return json.Marshal(s.ID) -} - -// ClientSubscription is a subscription established through the Client's Subscribe or -// EthSubscribe methods. -type ClientSubscription struct { - client *Client - etype reflect.Type - channel reflect.Value - namespace string - subid string - - // The in channel receives notification values from client dispatcher. - in chan json.RawMessage - - // The error channel receives the error from the forwarding loop. - // It is closed by Unsubscribe. - err chan error - errOnce sync.Once - - // Closing of the subscription is requested by sending on 'quit'. This is handled by - // the forwarding loop, which closes 'forwardDone' when it has stopped sending to - // sub.channel. Finally, 'unsubDone' is closed after unsubscribing on the server side. - quit chan error - forwardDone chan struct{} - unsubDone chan struct{} -} - -// This is the sentinel value sent on sub.quit when Unsubscribe is called. -var errUnsubscribed = errors.New("unsubscribed") - -func newClientSubscription(c *Client, namespace string, channel reflect.Value) *ClientSubscription { - sub := &ClientSubscription{ - client: c, - namespace: namespace, - etype: channel.Type().Elem(), - channel: channel, - in: make(chan json.RawMessage), - quit: make(chan error), - forwardDone: make(chan struct{}), - unsubDone: make(chan struct{}), - err: make(chan error, 1), - } - return sub -} - -// Err returns the subscription error channel. The intended use of Err is to schedule -// resubscription when the client connection is closed unexpectedly. -// -// The error channel receives a value when the subscription has ended due to an error. The -// received error is nil if Close has been called on the underlying client and no other -// error has occurred. -// -// The error channel is closed when Unsubscribe is called on the subscription. -func (sub *ClientSubscription) Err() <-chan error { - return sub.err -} - -// Unsubscribe unsubscribes the notification and closes the error channel. -// It can safely be called more than once. -func (sub *ClientSubscription) Unsubscribe() { - sub.errOnce.Do(func() { - select { - case sub.quit <- errUnsubscribed: - <-sub.unsubDone - case <-sub.unsubDone: - } - close(sub.err) - }) -} - -// deliver is called by the client's message dispatcher to send a notification value. -func (sub *ClientSubscription) deliver(result json.RawMessage) (ok bool) { - select { - case sub.in <- result: - return true - case <-sub.forwardDone: - return false - } -} - -// close is called by the client's message dispatcher when the connection is closed. -func (sub *ClientSubscription) close(err error) { - select { - case sub.quit <- err: - case <-sub.forwardDone: - } -} - -// run is the forwarding loop of the subscription. It runs in its own goroutine and -// is launched by the client's handler after the subscription has been created. -func (sub *ClientSubscription) run() { - defer close(sub.unsubDone) - - unsubscribe, err := sub.forward() - - // The client's dispatch loop won't be able to execute the unsubscribe call if it is - // blocked in sub.deliver() or sub.close(). Closing forwardDone unblocks them. - close(sub.forwardDone) - - // Call the unsubscribe method on the server. - if unsubscribe { - sub.requestUnsubscribe() - } - - // Send the error. - if err != nil { - if err == ErrClientQuit { - // ErrClientQuit gets here when Client.Close is called. This is reported as a - // nil error because it's not an error, but we can't close sub.err here. - err = nil - } - sub.err <- err - } -} - -// forward is the forwarding loop. It takes in RPC notifications and sends them -// on the subscription channel. -func (sub *ClientSubscription) forward() (unsubscribeServer bool, err error) { - cases := []reflect.SelectCase{ - {Dir: reflect.SelectRecv, Chan: reflect.ValueOf(sub.quit)}, - {Dir: reflect.SelectRecv, Chan: reflect.ValueOf(sub.in)}, - {Dir: reflect.SelectSend, Chan: sub.channel}, - } - buffer := list.New() - - for { - var chosen int - var recv reflect.Value - if buffer.Len() == 0 { - // Idle, omit send case. - chosen, recv, _ = reflect.Select(cases[:2]) - } else { - // Non-empty buffer, send the first queued item. - cases[2].Send = reflect.ValueOf(buffer.Front().Value) - chosen, recv, _ = reflect.Select(cases) - } - - switch chosen { - case 0: // <-sub.quit - if !recv.IsNil() { - err = recv.Interface().(error) - } - if err == errUnsubscribed { - // Exiting because Unsubscribe was called, unsubscribe on server. - return true, nil - } - return false, err - - case 1: // <-sub.in - val, err := sub.unmarshal(recv.Interface().(json.RawMessage)) - if err != nil { - return true, err - } - if buffer.Len() == maxClientSubscriptionBuffer { - return true, ErrSubscriptionQueueOverflow - } - buffer.PushBack(val) - - case 2: // sub.channel<- - cases[2].Send = reflect.Value{} // Don't hold onto the value. - buffer.Remove(buffer.Front()) - } - } -} - -func (sub *ClientSubscription) unmarshal(result json.RawMessage) (interface{}, error) { - val := reflect.New(sub.etype) - err := json.Unmarshal(result, val.Interface()) - return val.Elem().Interface(), err -} - -func (sub *ClientSubscription) requestUnsubscribe() error { - var result interface{} - return sub.client.Call(&result, sub.namespace+unsubscribeMethodSuffix, sub.subid) -} diff --git a/graft/subnet-evm/rpc/subscription_test.go b/graft/subnet-evm/rpc/subscription_test.go deleted file mode 100644 index 2f5483187b4c..000000000000 --- a/graft/subnet-evm/rpc/subscription_test.go +++ /dev/null @@ -1,303 +0,0 @@ -// Copyright (C) 2019, Ava Labs, Inc. All rights reserved. -// See the file LICENSE for licensing terms. -// -// This file is a derived work, based on the go-ethereum library whose original -// notices appear below. -// -// It is distributed under a license compatible with the licensing terms of the -// original code from which it is derived. -// -// Much love to the original authors for their work. -// ********** -// Copyright 2016 The go-ethereum Authors -// This file is part of the go-ethereum library. -// -// The go-ethereum library is free software: you can redistribute it and/or modify -// it under the terms of the GNU Lesser General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// The go-ethereum library is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU Lesser General Public License for more details. -// -// You should have received a copy of the GNU Lesser General Public License -// along with the go-ethereum library. If not, see . - -package rpc - -import ( - "bytes" - "context" - "encoding/json" - "fmt" - "io" - "math/big" - "net" - "os" - "strings" - "testing" - "time" - - "github.com/ava-labs/libevm/common" - "github.com/ava-labs/libevm/core/types" - - "github.com/ava-labs/avalanchego/graft/subnet-evm/plugin/evm/customtypes" -) - -func TestMain(m *testing.M) { - customtypes.Register() - os.Exit(m.Run()) -} - -func TestNewID(t *testing.T) { - hexchars := "0123456789ABCDEFabcdef" - for i := 0; i < 100; i++ { - id := string(NewID()) - if !strings.HasPrefix(id, "0x") { - t.Fatalf("invalid ID prefix, want '0x...', got %s", id) - } - - id = id[2:] - if len(id) == 0 || len(id) > 32 { - t.Fatalf("invalid ID length, want len(id) > 0 && len(id) <= 32), got %d", len(id)) - } - - for i := 0; i < len(id); i++ { - if strings.IndexByte(hexchars, id[i]) == -1 { - t.Fatalf("unexpected byte, want any valid hex char, got %c", id[i]) - } - } - } -} - -func TestSubscriptions(t *testing.T) { - var ( - namespaces = []string{"eth", "bzz"} - service = ¬ificationTestService{} - subCount = len(namespaces) - notificationCount = 3 - - server = NewServer(0) - clientConn, serverConn = net.Pipe() - out = json.NewEncoder(clientConn) - in = json.NewDecoder(clientConn) - successes = make(chan subConfirmation) - notifications = make(chan subscriptionResult) - errors = make(chan error, subCount*notificationCount+1) - ) - - // setup and start server - for _, namespace := range namespaces { - if err := server.RegisterName(namespace, service); err != nil { - t.Fatalf("unable to register test service %v", err) - } - } - go server.ServeCodec(NewCodec(serverConn), 0, 0, 0, 0) - defer server.Stop() - - // wait for message and write them to the given channels - go waitForMessages(in, successes, notifications, errors) - - // create subscriptions one by one - for i, namespace := range namespaces { - request := map[string]interface{}{ - "id": i, - "method": fmt.Sprintf("%s_subscribe", namespace), - "jsonrpc": "2.0", - "params": []interface{}{"someSubscription", notificationCount, i}, - } - if err := out.Encode(&request); err != nil { - t.Fatalf("Could not create subscription: %v", err) - } - } - - timeout := time.After(30 * time.Second) - subids := make(map[string]string, subCount) - count := make(map[string]int, subCount) - allReceived := func() bool { - done := len(count) == subCount - for _, c := range count { - if c < notificationCount { - done = false - } - } - return done - } - for !allReceived() { - select { - case confirmation := <-successes: // subscription created - subids[namespaces[confirmation.reqid]] = string(confirmation.subid) - case notification := <-notifications: - count[notification.ID]++ - case err := <-errors: - t.Fatal(err) - case <-timeout: - for _, namespace := range namespaces { - subid, found := subids[namespace] - if !found { - t.Errorf("subscription for %q not created", namespace) - continue - } - if count, found := count[subid]; !found || count < notificationCount { - t.Errorf("didn't receive all notifications (%d<%d) in time for namespace %q", count, notificationCount, namespace) - } - } - t.Fatal("timed out") - } - } -} - -// This test checks that unsubscribing works. -func TestServerUnsubscribe(t *testing.T) { - p1, p2 := net.Pipe() - defer p2.Close() - - // Start the server. - server := newTestServer() - service := ¬ificationTestService{unsubscribed: make(chan string, 1)} - server.RegisterName("nftest2", service) - go server.ServeCodec(NewCodec(p1), 0, 0, 0, 0) - - // Subscribe. - p2.SetDeadline(time.Now().Add(10 * time.Second)) - p2.Write([]byte(`{"jsonrpc":"2.0","id":1,"method":"nftest2_subscribe","params":["someSubscription",0,10]}`)) - - // Handle received messages. - var ( - resps = make(chan subConfirmation) - notifications = make(chan subscriptionResult) - errors = make(chan error, 1) - ) - go waitForMessages(json.NewDecoder(p2), resps, notifications, errors) - - // Receive the subscription ID. - var sub subConfirmation - select { - case sub = <-resps: - case err := <-errors: - t.Fatal(err) - } - - // Unsubscribe and check that it is handled on the server side. - p2.Write([]byte(`{"jsonrpc":"2.0","method":"nftest2_unsubscribe","params":["` + sub.subid + `"]}`)) - for { - select { - case id := <-service.unsubscribed: - if id != string(sub.subid) { - t.Errorf("wrong subscription ID unsubscribed") - } - return - case err := <-errors: - t.Fatal(err) - case <-notifications: - // drop notifications - } - } -} - -type subConfirmation struct { - reqid int - subid ID -} - -// waitForMessages reads RPC messages from 'in' and dispatches them into the given channels. -// It stops if there is an error. -func waitForMessages(in *json.Decoder, successes chan subConfirmation, notifications chan subscriptionResult, errors chan error) { - for { - resp, notification, err := readAndValidateMessage(in) - if err != nil { - errors <- err - return - } else if resp != nil { - successes <- *resp - } else { - notifications <- *notification - } - } -} - -func readAndValidateMessage(in *json.Decoder) (*subConfirmation, *subscriptionResult, error) { - var msg jsonrpcMessage - if err := in.Decode(&msg); err != nil { - return nil, nil, fmt.Errorf("decode error: %v", err) - } - switch { - case msg.isNotification(): - var res subscriptionResult - if err := json.Unmarshal(msg.Params, &res); err != nil { - return nil, nil, fmt.Errorf("invalid subscription result: %v", err) - } - return nil, &res, nil - case msg.isResponse(): - var c subConfirmation - if msg.Error != nil { - return nil, nil, msg.Error - } else if err := json.Unmarshal(msg.Result, &c.subid); err != nil { - return nil, nil, fmt.Errorf("invalid response: %v", err) - } else { - json.Unmarshal(msg.ID, &c.reqid) - return &c, nil, nil - } - default: - return nil, nil, fmt.Errorf("unrecognized message: %v", msg) - } -} - -type mockConn struct { - enc *json.Encoder -} - -// writeJSON writes a message to the connection. -func (c *mockConn) writeJSON(ctx context.Context, msg interface{}, isError bool) error { - return c.enc.Encode(msg) -} - -func (c *mockConn) writeJSONSkipDeadline(ctx context.Context, msg interface{}, isError bool, skip bool) error { - return c.enc.Encode(msg) -} - -// Closed returns a channel which is closed when the connection is closed. -func (c *mockConn) closed() <-chan interface{} { return nil } - -// RemoteAddr returns the peer address of the connection. -func (c *mockConn) remoteAddr() string { return "" } - -// BenchmarkNotify benchmarks the performance of notifying a subscription. -func BenchmarkNotify(b *testing.B) { - id := ID("test") - notifier := &Notifier{ - h: &handler{conn: &mockConn{json.NewEncoder(io.Discard)}}, - sub: &Subscription{ID: id}, - activated: true, - } - msg := &types.Header{ - ParentHash: common.HexToHash("0x01"), - Number: big.NewInt(100), - } - b.ResetTimer() - for i := 0; i < b.N; i++ { - notifier.Notify(id, msg) - } -} - -func TestNotify(t *testing.T) { - out := new(bytes.Buffer) - id := ID("test") - notifier := &Notifier{ - h: &handler{conn: &mockConn{json.NewEncoder(out)}}, - sub: &Subscription{ID: id}, - activated: true, - } - msg := &types.Header{ - ParentHash: common.HexToHash("0x01"), - Number: big.NewInt(100), - } - notifier.Notify(id, msg) - have := strings.TrimSpace(out.String()) - want := `{"jsonrpc":"2.0","method":"_subscription","params":{"subscription":"test","result":{"parentHash":"0x0000000000000000000000000000000000000000000000000000000000000001","sha3Uncles":"0x0000000000000000000000000000000000000000000000000000000000000000","miner":"0x0000000000000000000000000000000000000000","stateRoot":"0x0000000000000000000000000000000000000000000000000000000000000000","transactionsRoot":"0x0000000000000000000000000000000000000000000000000000000000000000","receiptsRoot":"0x0000000000000000000000000000000000000000000000000000000000000000","logsBloom":"0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000","difficulty":null,"number":"0x64","gasLimit":"0x0","gasUsed":"0x0","timestamp":"0x0","extraData":"0x","mixHash":"0x0000000000000000000000000000000000000000000000000000000000000000","nonce":"0x0000000000000000","baseFeePerGas":null,"blockGasCost":null,"blobGasUsed":null,"excessBlobGas":null,"parentBeaconBlockRoot":null,"timestampMilliseconds":null,"minDelayExcess":null,"hash":"0xe5fb877dde471b45b9742bb4bb4b3d74a761e2fb7cb849a3d2b687eed90fb604"}}}` - if have != want { - t.Errorf("have:\n%v\nwant:\n%v\n", have, want) - } -} diff --git a/graft/subnet-evm/rpc/testdata/internal-error.js b/graft/subnet-evm/rpc/testdata/internal-error.js deleted file mode 100644 index 2ba387401f24..000000000000 --- a/graft/subnet-evm/rpc/testdata/internal-error.js +++ /dev/null @@ -1,7 +0,0 @@ -// These tests trigger various 'internal error' conditions. - ---> {"jsonrpc":"2.0","id":1,"method":"test_marshalError","params": []} -<-- {"jsonrpc":"2.0","id":1,"error":{"code":-32603,"message":"json: error calling MarshalText for type *rpc.MarshalErrObj: marshal error"}} - ---> {"jsonrpc":"2.0","id":2,"method":"test_panic","params": []} -<-- {"jsonrpc":"2.0","id":2,"error":{"code":-32603,"message":"method handler crashed"}} diff --git a/graft/subnet-evm/rpc/testdata/invalid-badid.js b/graft/subnet-evm/rpc/testdata/invalid-badid.js deleted file mode 100644 index 2202b8ccd26e..000000000000 --- a/graft/subnet-evm/rpc/testdata/invalid-badid.js +++ /dev/null @@ -1,7 +0,0 @@ -// This test checks processing of messages with invalid ID. - ---> {"id":[],"method":"test_foo"} -<-- {"jsonrpc":"2.0","id":null,"error":{"code":-32600,"message":"invalid request"}} - ---> {"id":{},"method":"test_foo"} -<-- {"jsonrpc":"2.0","id":null,"error":{"code":-32600,"message":"invalid request"}} diff --git a/graft/subnet-evm/rpc/testdata/invalid-badversion.js b/graft/subnet-evm/rpc/testdata/invalid-badversion.js deleted file mode 100644 index 75b5291dc3f0..000000000000 --- a/graft/subnet-evm/rpc/testdata/invalid-badversion.js +++ /dev/null @@ -1,19 +0,0 @@ -// This test checks processing of messages with invalid Version. - ---> {"jsonrpc":"2.0","id":1,"method":"test_echo","params":["x", 3]} -<-- {"jsonrpc":"2.0","id":1,"result":{"String":"x","Int":3,"Args":null}} - ---> {"jsonrpc":"2.1","id":1,"method":"test_echo","params":["x", 3]} -<-- {"jsonrpc":"2.0","id":1,"error":{"code":-32600,"message":"invalid request"}} - ---> {"jsonrpc":"go-ethereum","id":1,"method":"test_echo","params":["x", 3]} -<-- {"jsonrpc":"2.0","id":1,"error":{"code":-32600,"message":"invalid request"}} - ---> {"jsonrpc":1,"id":1,"method":"test_echo","params":["x", 3]} -<-- {"jsonrpc":"2.0","id":1,"error":{"code":-32600,"message":"invalid request"}} - ---> {"jsonrpc":2.0,"id":1,"method":"test_echo","params":["x", 3]} -<-- {"jsonrpc":"2.0","id":1,"error":{"code":-32600,"message":"invalid request"}} - ---> {"id":1,"method":"test_echo","params":["x", 3]} -<-- {"jsonrpc":"2.0","id":1,"error":{"code":-32600,"message":"invalid request"}} diff --git a/graft/subnet-evm/rpc/testdata/invalid-batch-toolarge.js b/graft/subnet-evm/rpc/testdata/invalid-batch-toolarge.js deleted file mode 100644 index 218fea58aaac..000000000000 --- a/graft/subnet-evm/rpc/testdata/invalid-batch-toolarge.js +++ /dev/null @@ -1,13 +0,0 @@ -// This file checks the behavior of the batch item limit code. -// In tests, the batch item limit is set to 4. So to trigger the error, -// all batches in this file have 5 elements. - -// For batches that do not contain any calls, a response message with "id" == null -// is returned. - ---> [{"jsonrpc":"2.0","method":"test_echo","params":["x",99]},{"jsonrpc":"2.0","method":"test_echo","params":["x",99]},{"jsonrpc":"2.0","method":"test_echo","params":["x",99]},{"jsonrpc":"2.0","method":"test_echo","params":["x",99]},{"jsonrpc":"2.0","method":"test_echo","params":["x",99]}] -<-- [{"jsonrpc":"2.0","id":null,"error":{"code":-32600,"message":"batch too large"}}] - -// For batches with at least one call, the call's "id" is used. ---> [{"jsonrpc":"2.0","method":"test_echo","params":["x",99]},{"jsonrpc":"2.0","id":3,"method":"test_echo","params":["x",99]},{"jsonrpc":"2.0","method":"test_echo","params":["x",99]},{"jsonrpc":"2.0","method":"test_echo","params":["x",99]},{"jsonrpc":"2.0","method":"test_echo","params":["x",99]}] -<-- [{"jsonrpc":"2.0","id":3,"error":{"code":-32600,"message":"batch too large"}}] diff --git a/graft/subnet-evm/rpc/testdata/invalid-batch.js b/graft/subnet-evm/rpc/testdata/invalid-batch.js deleted file mode 100644 index 768dbc837e95..000000000000 --- a/graft/subnet-evm/rpc/testdata/invalid-batch.js +++ /dev/null @@ -1,17 +0,0 @@ -// This test checks the behavior of batches with invalid elements. -// Empty batches are not allowed. Batches may contain junk. - ---> [] -<-- {"jsonrpc":"2.0","id":null,"error":{"code":-32600,"message":"empty batch"}} - ---> [1] -<-- [{"jsonrpc":"2.0","id":null,"error":{"code":-32600,"message":"invalid request"}}] - ---> [1,2,3] -<-- [{"jsonrpc":"2.0","id":null,"error":{"code":-32600,"message":"invalid request"}},{"jsonrpc":"2.0","id":null,"error":{"code":-32600,"message":"invalid request"}},{"jsonrpc":"2.0","id":null,"error":{"code":-32600,"message":"invalid request"}}] - ---> [null] -<-- [{"jsonrpc":"2.0","id":null,"error":{"code":-32600,"message":"invalid request"}}] - ---> [{"jsonrpc":"2.0","id":1,"method":"test_echo","params":["foo",1]},55,{"jsonrpc":"2.0","id":2,"method":"unknown_method"},{"foo":"bar"}] -<-- [{"jsonrpc":"2.0","id":1,"result":{"String":"foo","Int":1,"Args":null}},{"jsonrpc":"2.0","id":null,"error":{"code":-32600,"message":"invalid request"}},{"jsonrpc":"2.0","id":2,"error":{"code":-32601,"message":"the method unknown_method does not exist/is not available"}},{"jsonrpc":"2.0","id":null,"error":{"code":-32600,"message":"invalid request"}}] diff --git a/graft/subnet-evm/rpc/testdata/invalid-idonly.js b/graft/subnet-evm/rpc/testdata/invalid-idonly.js deleted file mode 100644 index 79997bee3060..000000000000 --- a/graft/subnet-evm/rpc/testdata/invalid-idonly.js +++ /dev/null @@ -1,7 +0,0 @@ -// This test checks processing of messages that contain just the ID and nothing else. - ---> {"id":1} -<-- {"jsonrpc":"2.0","id":1,"error":{"code":-32600,"message":"invalid request"}} - ---> {"jsonrpc":"2.0","id":1} -<-- {"jsonrpc":"2.0","id":1,"error":{"code":-32600,"message":"invalid request"}} diff --git a/graft/subnet-evm/rpc/testdata/invalid-nonobj.js b/graft/subnet-evm/rpc/testdata/invalid-nonobj.js deleted file mode 100644 index ffdd4a5b8779..000000000000 --- a/graft/subnet-evm/rpc/testdata/invalid-nonobj.js +++ /dev/null @@ -1,7 +0,0 @@ -// This test checks behavior for invalid requests. - ---> 1 -<-- {"jsonrpc":"2.0","id":null,"error":{"code":-32600,"message":"invalid request"}} - ---> null -<-- {"jsonrpc":"2.0","id":null,"error":{"code":-32600,"message":"invalid request"}} diff --git a/graft/subnet-evm/rpc/testdata/invalid-syntax.json b/graft/subnet-evm/rpc/testdata/invalid-syntax.json deleted file mode 100644 index b19429960309..000000000000 --- a/graft/subnet-evm/rpc/testdata/invalid-syntax.json +++ /dev/null @@ -1,5 +0,0 @@ -// This test checks that an error is written for invalid JSON requests. - ---> 'f -<-- {"jsonrpc":"2.0","id":null,"error":{"code":-32700,"message":"invalid character '\\'' looking for beginning of value"}} - diff --git a/graft/subnet-evm/rpc/testdata/reqresp-batch.js b/graft/subnet-evm/rpc/testdata/reqresp-batch.js deleted file mode 100644 index 977af7663099..000000000000 --- a/graft/subnet-evm/rpc/testdata/reqresp-batch.js +++ /dev/null @@ -1,8 +0,0 @@ -// There is no response for all-notification batches. - ---> [{"jsonrpc":"2.0","method":"test_echo","params":["x",99]}] - -// This test checks regular batch calls. - ---> [{"jsonrpc":"2.0","id":2,"method":"test_echo","params":[]}, {"jsonrpc":"2.0","id": 3,"method":"test_echo","params":["x",3]}] -<-- [{"jsonrpc":"2.0","id":2,"error":{"code":-32602,"message":"missing value for required argument 0"}},{"jsonrpc":"2.0","id":3,"result":{"String":"x","Int":3,"Args":null}}] diff --git a/graft/subnet-evm/rpc/testdata/reqresp-echo.js b/graft/subnet-evm/rpc/testdata/reqresp-echo.js deleted file mode 100644 index 7a9e90321c47..000000000000 --- a/graft/subnet-evm/rpc/testdata/reqresp-echo.js +++ /dev/null @@ -1,16 +0,0 @@ -// This test calls the test_echo method. - ---> {"jsonrpc": "2.0", "id": 2, "method": "test_echo", "params": []} -<-- {"jsonrpc":"2.0","id":2,"error":{"code":-32602,"message":"missing value for required argument 0"}} - ---> {"jsonrpc": "2.0", "id": 2, "method": "test_echo", "params": ["x"]} -<-- {"jsonrpc":"2.0","id":2,"error":{"code":-32602,"message":"missing value for required argument 1"}} - ---> {"jsonrpc": "2.0", "id": 2, "method": "test_echo", "params": ["x", 3]} -<-- {"jsonrpc":"2.0","id":2,"result":{"String":"x","Int":3,"Args":null}} - ---> {"jsonrpc": "2.0", "id": 2, "method": "test_echo", "params": ["x", 3, {"S": "foo"}]} -<-- {"jsonrpc":"2.0","id":2,"result":{"String":"x","Int":3,"Args":{"S":"foo"}}} - ---> {"jsonrpc": "2.0", "id": 2, "method": "test_echoWithCtx", "params": ["x", 3, {"S": "foo"}]} -<-- {"jsonrpc":"2.0","id":2,"result":{"String":"x","Int":3,"Args":{"S":"foo"}}} diff --git a/graft/subnet-evm/rpc/testdata/reqresp-namedparam.js b/graft/subnet-evm/rpc/testdata/reqresp-namedparam.js deleted file mode 100644 index 9a9372b0a711..000000000000 --- a/graft/subnet-evm/rpc/testdata/reqresp-namedparam.js +++ /dev/null @@ -1,5 +0,0 @@ -// This test checks that an error response is sent for calls -// with named parameters. - ---> {"jsonrpc":"2.0","method":"test_echo","params":{"int":23},"id":3} -<-- {"jsonrpc":"2.0","id":3,"error":{"code":-32602,"message":"non-array args"}} diff --git a/graft/subnet-evm/rpc/testdata/reqresp-noargsrets.js b/graft/subnet-evm/rpc/testdata/reqresp-noargsrets.js deleted file mode 100644 index e61cc708ba33..000000000000 --- a/graft/subnet-evm/rpc/testdata/reqresp-noargsrets.js +++ /dev/null @@ -1,4 +0,0 @@ -// This test calls the test_noArgsRets method. - ---> {"jsonrpc": "2.0", "id": "foo", "method": "test_noArgsRets", "params": []} -<-- {"jsonrpc":"2.0","id":"foo","result":null} diff --git a/graft/subnet-evm/rpc/testdata/reqresp-nomethod.js b/graft/subnet-evm/rpc/testdata/reqresp-nomethod.js deleted file mode 100644 index 58ea6f3079b6..000000000000 --- a/graft/subnet-evm/rpc/testdata/reqresp-nomethod.js +++ /dev/null @@ -1,4 +0,0 @@ -// This test calls a method that doesn't exist. - ---> {"jsonrpc": "2.0", "id": 2, "method": "invalid_method", "params": [2, 3]} -<-- {"jsonrpc":"2.0","id":2,"error":{"code":-32601,"message":"the method invalid_method does not exist/is not available"}} diff --git a/graft/subnet-evm/rpc/testdata/reqresp-noparam.js b/graft/subnet-evm/rpc/testdata/reqresp-noparam.js deleted file mode 100644 index 2edf486d9f85..000000000000 --- a/graft/subnet-evm/rpc/testdata/reqresp-noparam.js +++ /dev/null @@ -1,4 +0,0 @@ -// This test checks that calls with no parameters work. - ---> {"jsonrpc":"2.0","method":"test_noArgsRets","id":3} -<-- {"jsonrpc":"2.0","id":3,"result":null} diff --git a/graft/subnet-evm/rpc/testdata/reqresp-paramsnull.js b/graft/subnet-evm/rpc/testdata/reqresp-paramsnull.js deleted file mode 100644 index 8a01bae1bbe7..000000000000 --- a/graft/subnet-evm/rpc/testdata/reqresp-paramsnull.js +++ /dev/null @@ -1,4 +0,0 @@ -// This test checks that calls with "params":null work. - ---> {"jsonrpc":"2.0","method":"test_noArgsRets","params":null,"id":3} -<-- {"jsonrpc":"2.0","id":3,"result":null} diff --git a/graft/subnet-evm/rpc/testdata/revcall.js b/graft/subnet-evm/rpc/testdata/revcall.js deleted file mode 100644 index 695d9858f87e..000000000000 --- a/graft/subnet-evm/rpc/testdata/revcall.js +++ /dev/null @@ -1,6 +0,0 @@ -// This test checks reverse calls. - ---> {"jsonrpc":"2.0","id":2,"method":"test_callMeBack","params":["foo",[1]]} -<-- {"jsonrpc":"2.0","id":1,"method":"foo","params":[1]} ---> {"jsonrpc":"2.0","id":1,"result":"my result"} -<-- {"jsonrpc":"2.0","id":2,"result":"my result"} diff --git a/graft/subnet-evm/rpc/testdata/revcall2.js b/graft/subnet-evm/rpc/testdata/revcall2.js deleted file mode 100644 index acab46551ec6..000000000000 --- a/graft/subnet-evm/rpc/testdata/revcall2.js +++ /dev/null @@ -1,7 +0,0 @@ -// This test checks reverse calls. - ---> {"jsonrpc":"2.0","id":2,"method":"test_callMeBackLater","params":["foo",[1]]} -<-- {"jsonrpc":"2.0","id":2,"result":null} -<-- {"jsonrpc":"2.0","id":1,"method":"foo","params":[1]} ---> {"jsonrpc":"2.0","id":1,"result":"my result"} - diff --git a/graft/subnet-evm/rpc/testdata/subscription.js b/graft/subnet-evm/rpc/testdata/subscription.js deleted file mode 100644 index 9f1007301080..000000000000 --- a/graft/subnet-evm/rpc/testdata/subscription.js +++ /dev/null @@ -1,12 +0,0 @@ -// This test checks basic subscription support. - ---> {"jsonrpc":"2.0","id":1,"method":"nftest_subscribe","params":["someSubscription",5,1]} -<-- {"jsonrpc":"2.0","id":1,"result":"0x1"} -<-- {"jsonrpc":"2.0","method":"nftest_subscription","params":{"subscription":"0x1","result":1}} -<-- {"jsonrpc":"2.0","method":"nftest_subscription","params":{"subscription":"0x1","result":2}} -<-- {"jsonrpc":"2.0","method":"nftest_subscription","params":{"subscription":"0x1","result":3}} -<-- {"jsonrpc":"2.0","method":"nftest_subscription","params":{"subscription":"0x1","result":4}} -<-- {"jsonrpc":"2.0","method":"nftest_subscription","params":{"subscription":"0x1","result":5}} - ---> {"jsonrpc":"2.0","id":2,"method":"nftest_echo","params":[11]} -<-- {"jsonrpc":"2.0","id":2,"result":11} diff --git a/graft/subnet-evm/rpc/testservice_test.go b/graft/subnet-evm/rpc/testservice_test.go deleted file mode 100644 index ced25556abe0..000000000000 --- a/graft/subnet-evm/rpc/testservice_test.go +++ /dev/null @@ -1,243 +0,0 @@ -// Copyright (C) 2019, Ava Labs, Inc. All rights reserved. -// See the file LICENSE for licensing terms. -// -// This file is a derived work, based on the go-ethereum library whose original -// notices appear below. -// -// It is distributed under a license compatible with the licensing terms of the -// original code from which it is derived. -// -// Much love to the original authors for their work. -// ********** -// Copyright 2019 The go-ethereum Authors -// This file is part of the go-ethereum library. -// -// The go-ethereum library is free software: you can redistribute it and/or modify -// it under the terms of the GNU Lesser General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// The go-ethereum library is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU Lesser General Public License for more details. -// -// You should have received a copy of the GNU Lesser General Public License -// along with the go-ethereum library. If not, see . - -package rpc - -import ( - "context" - "encoding/binary" - "errors" - "strings" - "sync" - "time" -) - -func newTestServer() *Server { - server := NewServer(0) - server.idgen = sequentialIDGenerator() - if err := server.RegisterName("test", new(testService)); err != nil { - panic(err) - } - if err := server.RegisterName("nftest", new(notificationTestService)); err != nil { - panic(err) - } - return server -} - -func sequentialIDGenerator() func() ID { - var ( - mu sync.Mutex - counter uint64 - ) - return func() ID { - mu.Lock() - defer mu.Unlock() - counter++ - id := make([]byte, 8) - binary.BigEndian.PutUint64(id, counter) - return encodeID(id) - } -} - -type testService struct{} - -type echoArgs struct { - S string -} - -type echoResult struct { - String string - Int int - Args *echoArgs -} - -type testError struct{} - -func (testError) Error() string { return "testError" } -func (testError) ErrorCode() int { return 444 } -func (testError) ErrorData() interface{} { return "testError data" } - -type MarshalErrObj struct{} - -func (o *MarshalErrObj) MarshalText() ([]byte, error) { - return nil, errors.New("marshal error") -} - -func (s *testService) NoArgsRets() {} - -func (s *testService) Null() any { - return nil -} - -func (s *testService) Echo(str string, i int, args *echoArgs) echoResult { - return echoResult{str, i, args} -} - -func (s *testService) EchoWithCtx(ctx context.Context, str string, i int, args *echoArgs) echoResult { - return echoResult{str, i, args} -} - -func (s *testService) Repeat(msg string, i int) string { - return strings.Repeat(msg, i) -} - -func (s *testService) PeerInfo(ctx context.Context) PeerInfo { - return PeerInfoFromContext(ctx) -} - -func (s *testService) Sleep(ctx context.Context, duration time.Duration) { - time.Sleep(duration) -} - -func (s *testService) Block(ctx context.Context) error { - <-ctx.Done() - return errors.New("context canceled in testservice_block") -} - -func (s *testService) Rets() (string, error) { - return "", nil -} - -//lint:ignore ST1008 returns error first on purpose. -func (s *testService) InvalidRets1() (error, string) { - return nil, "" -} - -func (s *testService) InvalidRets2() (string, string) { - return "", "" -} - -func (s *testService) InvalidRets3() (string, string, error) { - return "", "", nil -} - -func (s *testService) ReturnError() error { - return testError{} -} - -func (s *testService) MarshalError() *MarshalErrObj { - return &MarshalErrObj{} -} - -func (s *testService) Panic() string { - panic("service panic") -} - -func (s *testService) CallMeBack(ctx context.Context, method string, args []interface{}) (interface{}, error) { - c, ok := ClientFromContext(ctx) - if !ok { - return nil, errors.New("no client") - } - var result interface{} - err := c.Call(&result, method, args...) - return result, err -} - -func (s *testService) CallMeBackLater(ctx context.Context, method string, args []interface{}) error { - c, ok := ClientFromContext(ctx) - if !ok { - return errors.New("no client") - } - go func() { - <-ctx.Done() - var result interface{} - c.Call(&result, method, args...) - }() - return nil -} - -func (s *testService) Subscription(ctx context.Context) (*Subscription, error) { - return nil, nil -} - -type notificationTestService struct { - unsubscribed chan string - gotHangSubscriptionReq chan struct{} - unblockHangSubscription chan struct{} -} - -func (s *notificationTestService) Echo(i int) int { - return i -} - -func (s *notificationTestService) Unsubscribe(subid string) { - if s.unsubscribed != nil { - s.unsubscribed <- subid - } -} - -func (s *notificationTestService) SomeSubscription(ctx context.Context, n, val int) (*Subscription, error) { - notifier, supported := NotifierFromContext(ctx) - if !supported { - return nil, ErrNotificationsUnsupported - } - - // By explicitly creating an subscription we make sure that the subscription id is send - // back to the client before the first subscription.Notify is called. Otherwise the - // events might be send before the response for the *_subscribe method. - subscription := notifier.CreateSubscription() - go func() { - for i := 0; i < n; i++ { - if err := notifier.Notify(subscription.ID, val+i); err != nil { - return - } - } - select { - case <-notifier.Closed(): - case <-subscription.Err(): - } - if s.unsubscribed != nil { - s.unsubscribed <- string(subscription.ID) - } - }() - return subscription, nil -} - -// HangSubscription blocks on s.unblockHangSubscription before sending anything. -func (s *notificationTestService) HangSubscription(ctx context.Context, val int) (*Subscription, error) { - notifier, supported := NotifierFromContext(ctx) - if !supported { - return nil, ErrNotificationsUnsupported - } - s.gotHangSubscriptionReq <- struct{}{} - <-s.unblockHangSubscription - subscription := notifier.CreateSubscription() - - go func() { - notifier.Notify(subscription.ID, val) - }() - return subscription, nil -} - -// largeRespService generates arbitrary-size JSON responses. -type largeRespService struct { - length int -} - -func (x largeRespService) LargeResp() string { - return strings.Repeat("x", x.length) -} diff --git a/graft/subnet-evm/rpc/types.go b/graft/subnet-evm/rpc/types.go deleted file mode 100644 index 67d806b499b4..000000000000 --- a/graft/subnet-evm/rpc/types.go +++ /dev/null @@ -1,275 +0,0 @@ -// Copyright (C) 2019, Ava Labs, Inc. All rights reserved. -// See the file LICENSE for licensing terms. -// -// This file is a derived work, based on the go-ethereum library whose original -// notices appear below. -// -// It is distributed under a license compatible with the licensing terms of the -// original code from which it is derived. -// -// Much love to the original authors for their work. -// ********** -// Copyright 2015 The go-ethereum Authors -// This file is part of the go-ethereum library. -// -// The go-ethereum library is free software: you can redistribute it and/or modify -// it under the terms of the GNU Lesser General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// The go-ethereum library is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU Lesser General Public License for more details. -// -// You should have received a copy of the GNU Lesser General Public License -// along with the go-ethereum library. If not, see . - -package rpc - -import ( - "context" - "encoding/json" - "errors" - "fmt" - "math" - "strings" - - "github.com/ava-labs/libevm/common" - "github.com/ava-labs/libevm/common/hexutil" -) - -// API describes the set of methods offered over the RPC interface -type API struct { - Namespace string // namespace under which the rpc methods of Service are exposed - Version string // deprecated - this field is no longer used, but retained for compatibility - Service interface{} // receiver instance which holds the methods - Name string // Name of the API -} - -// ServerCodec implements reading, parsing and writing RPC messages for the server side of -// a RPC session. Implementations must be go-routine safe since the codec can be called in -// multiple go-routines concurrently. -type ServerCodec interface { - peerInfo() PeerInfo - readBatch() (msgs []*jsonrpcMessage, isBatch bool, err error) - close() - - jsonWriter -} - -// jsonWriter can write JSON messages to its underlying connection. -// Implementations must be safe for concurrent use. -type jsonWriter interface { - // writeJSON writes a message to the connection. - writeJSON(ctx context.Context, msg interface{}, isError bool) error - // writeJSONSkipDeadline writes a message to the connection with the option of skipping the deadline. - writeJSONSkipDeadline(ctx context.Context, msg interface{}, isError bool, skip bool) error - // Closed returns a channel which is closed when the connection is closed. - closed() <-chan interface{} - // RemoteAddr returns the peer address of the connection. - remoteAddr() string -} - -type BlockNumber int64 - -const ( - SafeBlockNumber = BlockNumber(-4) - FinalizedBlockNumber = BlockNumber(-3) - LatestBlockNumber = BlockNumber(-2) - PendingBlockNumber = BlockNumber(-1) - EarliestBlockNumber = BlockNumber(0) -) - -// UnmarshalJSON parses the given JSON fragment into a BlockNumber. It supports: -// - "accepted", "safe", "finalized", "latest", "earliest" or "pending" as string arguments -// - the block number -// Returned errors: -// - an invalid block number error when the given argument isn't a known strings -// - an out of range error when the given block number is either too little or too large -func (bn *BlockNumber) UnmarshalJSON(data []byte) error { - input := strings.TrimSpace(string(data)) - if len(input) >= 2 && input[0] == '"' && input[len(input)-1] == '"' { - input = input[1 : len(input)-1] - } - - switch input { - case "earliest": - *bn = EarliestBlockNumber - return nil - case "latest": - *bn = LatestBlockNumber - return nil - case "pending": - *bn = PendingBlockNumber - return nil - // Include "finalized" as an option for compatibility with FinalizedBlockNumber from geth. - case "accepted", "finalized": - *bn = FinalizedBlockNumber - return nil - case "safe": - *bn = SafeBlockNumber - return nil - } - - blckNum, err := hexutil.DecodeUint64(input) - if err != nil { - return err - } - if blckNum > math.MaxInt64 { - return errors.New("block number larger than int64") - } - *bn = BlockNumber(blckNum) - return nil -} - -// Int64 returns the block number as int64. -func (bn BlockNumber) Int64() int64 { - return (int64)(bn) -} - -// MarshalText implements encoding.TextMarshaler. It marshals: -// - "accepted", "latest", "earliest" or "pending" as strings -// - other numbers as hex -func (bn BlockNumber) MarshalText() ([]byte, error) { - return []byte(bn.String()), nil -} - -func (bn BlockNumber) String() string { - switch bn { - case EarliestBlockNumber: - return "earliest" - case LatestBlockNumber: - return "latest" - case PendingBlockNumber: - return "pending" - case FinalizedBlockNumber: - return "accepted" - case SafeBlockNumber: - return "safe" - default: - if bn < 0 { - return fmt.Sprintf("", bn) - } - return hexutil.Uint64(bn).String() - } -} - -// IsAccepted returns true if this blockNumber should be treated as a request for the last accepted block -func (bn BlockNumber) IsAccepted() bool { - return bn < EarliestBlockNumber && bn >= SafeBlockNumber -} - -func (bn BlockNumber) IsLatest() bool { - return bn == LatestBlockNumber || bn == PendingBlockNumber -} - -type BlockNumberOrHash struct { - BlockNumber *BlockNumber `json:"blockNumber,omitempty"` - BlockHash *common.Hash `json:"blockHash,omitempty"` - RequireCanonical bool `json:"requireCanonical,omitempty"` -} - -func (bnh *BlockNumberOrHash) UnmarshalJSON(data []byte) error { - type erased BlockNumberOrHash - e := erased{} - err := json.Unmarshal(data, &e) - if err == nil { - if e.BlockNumber != nil && e.BlockHash != nil { - return errors.New("cannot specify both BlockHash and BlockNumber, choose one or the other") - } - bnh.BlockNumber = e.BlockNumber - bnh.BlockHash = e.BlockHash - bnh.RequireCanonical = e.RequireCanonical - return nil - } - var input string - err = json.Unmarshal(data, &input) - if err != nil { - return err - } - switch input { - case "earliest": - bn := EarliestBlockNumber - bnh.BlockNumber = &bn - return nil - case "latest": - bn := LatestBlockNumber - bnh.BlockNumber = &bn - return nil - case "pending": - bn := PendingBlockNumber - bnh.BlockNumber = &bn - return nil - // Include "finalized" as an option for compatibility with FinalizedBlockNumber from geth. - case "accepted", "finalized": - bn := FinalizedBlockNumber - bnh.BlockNumber = &bn - return nil - case "safe": - bn := SafeBlockNumber - bnh.BlockNumber = &bn - return nil - default: - if len(input) == 66 { - hash := common.Hash{} - err := hash.UnmarshalText([]byte(input)) - if err != nil { - return err - } - bnh.BlockHash = &hash - return nil - } else { - blckNum, err := hexutil.DecodeUint64(input) - if err != nil { - return err - } - if blckNum > math.MaxInt64 { - return errors.New("blocknumber too high") - } - bn := BlockNumber(blckNum) - bnh.BlockNumber = &bn - return nil - } - } -} - -func (bnh *BlockNumberOrHash) Number() (BlockNumber, bool) { - if bnh.BlockNumber != nil { - return *bnh.BlockNumber, true - } - return BlockNumber(0), false -} - -func (bnh *BlockNumberOrHash) String() string { - if bnh.BlockNumber != nil { - return bnh.BlockNumber.String() - } - if bnh.BlockHash != nil { - return bnh.BlockHash.String() - } - return "nil" -} - -func (bnh *BlockNumberOrHash) Hash() (common.Hash, bool) { - if bnh.BlockHash != nil { - return *bnh.BlockHash, true - } - return common.Hash{}, false -} - -func BlockNumberOrHashWithNumber(blockNr BlockNumber) BlockNumberOrHash { - return BlockNumberOrHash{ - BlockNumber: &blockNr, - BlockHash: nil, - RequireCanonical: false, - } -} - -func BlockNumberOrHashWithHash(hash common.Hash, canonical bool) BlockNumberOrHash { - return BlockNumberOrHash{ - BlockNumber: nil, - BlockHash: &hash, - RequireCanonical: canonical, - } -} diff --git a/graft/subnet-evm/rpc/types_test.go b/graft/subnet-evm/rpc/types_test.go deleted file mode 100644 index 8f1a2cb9628f..000000000000 --- a/graft/subnet-evm/rpc/types_test.go +++ /dev/null @@ -1,186 +0,0 @@ -// Copyright (C) 2019, Ava Labs, Inc. All rights reserved. -// See the file LICENSE for licensing terms. -// -// This file is a derived work, based on the go-ethereum library whose original -// notices appear below. -// -// It is distributed under a license compatible with the licensing terms of the -// original code from which it is derived. -// -// Much love to the original authors for their work. -// ********** -// Copyright 2015 The go-ethereum Authors -// This file is part of the go-ethereum library. -// -// The go-ethereum library is free software: you can redistribute it and/or modify -// it under the terms of the GNU Lesser General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// The go-ethereum library is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU Lesser General Public License for more details. -// -// You should have received a copy of the GNU Lesser General Public License -// along with the go-ethereum library. If not, see . - -package rpc - -import ( - "encoding/json" - "reflect" - "testing" - - "github.com/ava-labs/libevm/common" - "github.com/ava-labs/libevm/common/math" -) - -func TestBlockNumberJSONUnmarshal(t *testing.T) { - tests := []struct { - input string - mustFail bool - expected BlockNumber - }{ - 0: {`"0x"`, true, BlockNumber(0)}, - 1: {`"0x0"`, false, BlockNumber(0)}, - 2: {`"0X1"`, false, BlockNumber(1)}, - 3: {`"0x00"`, true, BlockNumber(0)}, - 4: {`"0x01"`, true, BlockNumber(0)}, - 5: {`"0x1"`, false, BlockNumber(1)}, - 6: {`"0x12"`, false, BlockNumber(18)}, - 7: {`"0x7fffffffffffffff"`, false, BlockNumber(math.MaxInt64)}, - 8: {`"0x8000000000000000"`, true, BlockNumber(0)}, - 9: {"0", true, BlockNumber(0)}, - 10: {`"ff"`, true, BlockNumber(0)}, - 11: {`"pending"`, false, PendingBlockNumber}, - 12: {`"latest"`, false, LatestBlockNumber}, - 13: {`"earliest"`, false, EarliestBlockNumber}, - 14: {`someString`, true, BlockNumber(0)}, - 15: {`""`, true, BlockNumber(0)}, - 16: {``, true, BlockNumber(0)}, - } - - for i, test := range tests { - var num BlockNumber - err := json.Unmarshal([]byte(test.input), &num) - if test.mustFail && err == nil { - t.Errorf("Test %d should fail", i) - continue - } - if !test.mustFail && err != nil { - t.Errorf("Test %d should pass but got err: %v", i, err) - continue - } - if num != test.expected { - t.Errorf("Test %d got unexpected value, want %d, got %d", i, test.expected, num) - } - } -} - -func TestBlockNumberOrHash_UnmarshalJSON(t *testing.T) { - tests := []struct { - input string - mustFail bool - expected BlockNumberOrHash - }{ - 0: {`"0x"`, true, BlockNumberOrHash{}}, - 1: {`"0x0"`, false, BlockNumberOrHashWithNumber(0)}, - 2: {`"0X1"`, false, BlockNumberOrHashWithNumber(1)}, - 3: {`"0x00"`, true, BlockNumberOrHash{}}, - 4: {`"0x01"`, true, BlockNumberOrHash{}}, - 5: {`"0x1"`, false, BlockNumberOrHashWithNumber(1)}, - 6: {`"0x12"`, false, BlockNumberOrHashWithNumber(18)}, - 7: {`"0x7fffffffffffffff"`, false, BlockNumberOrHashWithNumber(math.MaxInt64)}, - 8: {`"0x8000000000000000"`, true, BlockNumberOrHash{}}, - 9: {"0", true, BlockNumberOrHash{}}, - 10: {`"ff"`, true, BlockNumberOrHash{}}, - 11: {`"pending"`, false, BlockNumberOrHashWithNumber(PendingBlockNumber)}, - 12: {`"latest"`, false, BlockNumberOrHashWithNumber(LatestBlockNumber)}, - 13: {`"earliest"`, false, BlockNumberOrHashWithNumber(EarliestBlockNumber)}, - 14: {`someString`, true, BlockNumberOrHash{}}, - 15: {`""`, true, BlockNumberOrHash{}}, - 16: {``, true, BlockNumberOrHash{}}, - 17: {`"0x0000000000000000000000000000000000000000000000000000000000000000"`, false, BlockNumberOrHashWithHash(common.HexToHash("0x0000000000000000000000000000000000000000000000000000000000000000"), false)}, - 18: {`{"blockHash":"0x0000000000000000000000000000000000000000000000000000000000000000"}`, false, BlockNumberOrHashWithHash(common.HexToHash("0x0000000000000000000000000000000000000000000000000000000000000000"), false)}, - 19: {`{"blockHash":"0x0000000000000000000000000000000000000000000000000000000000000000","requireCanonical":false}`, false, BlockNumberOrHashWithHash(common.HexToHash("0x0000000000000000000000000000000000000000000000000000000000000000"), false)}, - 20: {`{"blockHash":"0x0000000000000000000000000000000000000000000000000000000000000000","requireCanonical":true}`, false, BlockNumberOrHashWithHash(common.HexToHash("0x0000000000000000000000000000000000000000000000000000000000000000"), true)}, - 21: {`{"blockNumber":"0x1"}`, false, BlockNumberOrHashWithNumber(1)}, - 22: {`{"blockNumber":"pending"}`, false, BlockNumberOrHashWithNumber(PendingBlockNumber)}, - 23: {`{"blockNumber":"latest"}`, false, BlockNumberOrHashWithNumber(LatestBlockNumber)}, - 24: {`{"blockNumber":"earliest"}`, false, BlockNumberOrHashWithNumber(EarliestBlockNumber)}, - 25: {`{"blockNumber":"0x1", "blockHash":"0x0000000000000000000000000000000000000000000000000000000000000000"}`, true, BlockNumberOrHash{}}, - } - - for i, test := range tests { - var bnh BlockNumberOrHash - err := json.Unmarshal([]byte(test.input), &bnh) - if test.mustFail && err == nil { - t.Errorf("Test %d should fail", i) - continue - } - if !test.mustFail && err != nil { - t.Errorf("Test %d should pass but got err: %v", i, err) - continue - } - hash, hashOk := bnh.Hash() - expectedHash, expectedHashOk := test.expected.Hash() - num, numOk := bnh.Number() - expectedNum, expectedNumOk := test.expected.Number() - if bnh.RequireCanonical != test.expected.RequireCanonical || - hash != expectedHash || hashOk != expectedHashOk || - num != expectedNum || numOk != expectedNumOk { - t.Errorf("Test %d got unexpected value, want %v, got %v", i, test.expected, bnh) - } - } -} - -func TestBlockNumberOrHash_WithNumber_MarshalAndUnmarshal(t *testing.T) { - tests := []struct { - name string - number int64 - }{ - {"max", math.MaxInt64}, - {"pending", int64(PendingBlockNumber)}, - {"latest", int64(LatestBlockNumber)}, - {"earliest", int64(EarliestBlockNumber)}, - } - for _, test := range tests { - t.Run(test.name, func(t *testing.T) { - bnh := BlockNumberOrHashWithNumber(BlockNumber(test.number)) - marshalled, err := json.Marshal(bnh) - if err != nil { - t.Fatal("cannot marshal:", err) - } - var unmarshalled BlockNumberOrHash - err = json.Unmarshal(marshalled, &unmarshalled) - if err != nil { - t.Fatal("cannot unmarshal:", err) - } - if !reflect.DeepEqual(bnh, unmarshalled) { - t.Fatalf("wrong result: expected %v, got %v", bnh, unmarshalled) - } - }) - } -} - -func TestBlockNumberOrHash_StringAndUnmarshal(t *testing.T) { - tests := []BlockNumberOrHash{ - BlockNumberOrHashWithNumber(math.MaxInt64), - BlockNumberOrHashWithNumber(PendingBlockNumber), - BlockNumberOrHashWithNumber(LatestBlockNumber), - BlockNumberOrHashWithNumber(EarliestBlockNumber), - BlockNumberOrHashWithNumber(32), - BlockNumberOrHashWithHash(common.Hash{0xaa}, false), - } - for _, want := range tests { - marshalled, _ := json.Marshal(want.String()) - var have BlockNumberOrHash - if err := json.Unmarshal(marshalled, &have); err != nil { - t.Fatalf("cannot unmarshal (%v): %v", string(marshalled), err) - } - if !reflect.DeepEqual(want, have) { - t.Fatalf("wrong result: have %v, want %v", have, want) - } - } -} diff --git a/graft/subnet-evm/rpc/websocket.go b/graft/subnet-evm/rpc/websocket.go deleted file mode 100644 index 03b281b4ac3c..000000000000 --- a/graft/subnet-evm/rpc/websocket.go +++ /dev/null @@ -1,391 +0,0 @@ -// Copyright (C) 2019, Ava Labs, Inc. All rights reserved. -// See the file LICENSE for licensing terms. -// -// This file is a derived work, based on the go-ethereum library whose original -// notices appear below. -// -// It is distributed under a license compatible with the licensing terms of the -// original code from which it is derived. -// -// Much love to the original authors for their work. -// ********** -// Copyright 2015 The go-ethereum Authors -// This file is part of the go-ethereum library. -// -// The go-ethereum library is free software: you can redistribute it and/or modify -// it under the terms of the GNU Lesser General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// The go-ethereum library is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU Lesser General Public License for more details. -// -// You should have received a copy of the GNU Lesser General Public License -// along with the go-ethereum library. If not, see . - -package rpc - -import ( - "context" - "encoding/base64" - "fmt" - "net/http" - "net/url" - "os" - "strings" - "sync" - "time" - - "github.com/ava-labs/libevm/log" - mapset "github.com/deckarep/golang-set/v2" - "github.com/gorilla/websocket" -) - -const ( - wsReadBuffer = 1024 - wsWriteBuffer = 1024 - wsPingInterval = 30 * time.Second - wsPingWriteTimeout = 5 * time.Second - wsPongTimeout = 30 * time.Second - wsDefaultReadLimit = 32 * 1024 * 1024 -) - -var wsBufferPool = new(sync.Pool) - -// WebsocketHandler returns a handler that serves JSON-RPC to WebSocket connections. -// -// allowedOrigins should be a comma-separated list of allowed origin URLs. -// To allow connections with any origin, pass "*". -func (s *Server) WebsocketHandler(allowedOrigins []string) http.Handler { - return s.WebsocketHandlerWithDuration(allowedOrigins, 0, 0, 0) -} - -func (s *Server) WebsocketHandlerWithDuration(allowedOrigins []string, apiMaxDuration, refillRate, maxStored time.Duration) http.Handler { - var upgrader = websocket.Upgrader{ - ReadBufferSize: wsReadBuffer, - WriteBufferSize: wsWriteBuffer, - WriteBufferPool: wsBufferPool, - CheckOrigin: wsHandshakeValidator(allowedOrigins), - } - return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - conn, err := upgrader.Upgrade(w, r, nil) - if err != nil { - log.Debug("WebSocket upgrade failed", "err", err) - return - } - codec := newWebsocketCodec(conn, r.Host, r.Header, wsDefaultReadLimit) - s.ServeCodec(codec, 0, apiMaxDuration, refillRate, maxStored) - }) -} - -// wsHandshakeValidator returns a handler that verifies the origin during the -// websocket upgrade process. When a '*' is specified as an allowed origins all -// connections are accepted. -func wsHandshakeValidator(allowedOrigins []string) func(*http.Request) bool { - origins := mapset.NewSet[string]() - allowAllOrigins := false - - for _, origin := range allowedOrigins { - if origin == "*" { - allowAllOrigins = true - } - if origin != "" { - origins.Add(origin) - } - } - // allow localhost if no allowedOrigins are specified. - if len(origins.ToSlice()) == 0 { - origins.Add("http://localhost") - if hostname, err := os.Hostname(); err == nil { - origins.Add("http://" + hostname) - } - } - log.Debug(fmt.Sprintf("Allowed origin(s) for WS RPC interface %v", origins.ToSlice())) - - f := func(req *http.Request) bool { - // Skip origin verification if no Origin header is present. The origin check - // is supposed to protect against browser based attacks. Browsers always set - // Origin. Non-browser software can put anything in origin and checking it doesn't - // provide additional security. - if _, ok := req.Header["Origin"]; !ok { - return true - } - // Verify origin against allow list. - origin := strings.ToLower(req.Header.Get("Origin")) - if allowAllOrigins || originIsAllowed(origins, origin) { - return true - } - log.Warn("Rejected WebSocket connection", "origin", origin) - return false - } - - return f -} - -type wsHandshakeError struct { - err error - status string -} - -func (e wsHandshakeError) Error() string { - s := e.err.Error() - if e.status != "" { - s += " (HTTP status " + e.status + ")" - } - return s -} - -func originIsAllowed(allowedOrigins mapset.Set[string], browserOrigin string) bool { - it := allowedOrigins.Iterator() - for origin := range it.C { - if ruleAllowsOrigin(origin, browserOrigin) { - return true - } - } - return false -} - -func ruleAllowsOrigin(allowedOrigin string, browserOrigin string) bool { - var ( - allowedScheme, allowedHostname, allowedPort string - browserScheme, browserHostname, browserPort string - err error - ) - allowedScheme, allowedHostname, allowedPort, err = parseOriginURL(allowedOrigin) - if err != nil { - log.Warn("Error parsing allowed origin specification", "spec", allowedOrigin, "error", err) - return false - } - browserScheme, browserHostname, browserPort, err = parseOriginURL(browserOrigin) - if err != nil { - log.Warn("Error parsing browser 'Origin' field", "Origin", browserOrigin, "error", err) - return false - } - if allowedScheme != "" && allowedScheme != browserScheme { - return false - } - if allowedHostname != "" && allowedHostname != browserHostname { - return false - } - if allowedPort != "" && allowedPort != browserPort { - return false - } - return true -} - -func parseOriginURL(origin string) (string, string, string, error) { - parsedURL, err := url.Parse(strings.ToLower(origin)) - if err != nil { - return "", "", "", err - } - var scheme, hostname, port string - if strings.Contains(origin, "://") { - scheme = parsedURL.Scheme - hostname = parsedURL.Hostname() - port = parsedURL.Port() - } else { - scheme = "" - hostname = parsedURL.Scheme - port = parsedURL.Opaque - if hostname == "" { - hostname = origin - } - } - return scheme, hostname, port, nil -} - -// DialWebsocketWithDialer creates a new RPC client using WebSocket. -// -// The context is used for the initial connection establishment. It does not -// affect subsequent interactions with the client. -// -// Deprecated: use DialOptions and the WithWebsocketDialer option. -func DialWebsocketWithDialer(ctx context.Context, endpoint, origin string, dialer websocket.Dialer) (*Client, error) { - cfg := new(clientConfig) - cfg.wsDialer = &dialer - if origin != "" { - cfg.setHeader("origin", origin) - } - connect, err := newClientTransportWS(endpoint, cfg) - if err != nil { - return nil, err - } - return newClient(ctx, cfg, connect) -} - -// DialWebsocket creates a new RPC client that communicates with a JSON-RPC server -// that is listening on the given endpoint. -// -// The context is used for the initial connection establishment. It does not -// affect subsequent interactions with the client. -func DialWebsocket(ctx context.Context, endpoint, origin string) (*Client, error) { - cfg := new(clientConfig) - if origin != "" { - cfg.setHeader("origin", origin) - } - connect, err := newClientTransportWS(endpoint, cfg) - if err != nil { - return nil, err - } - return newClient(ctx, cfg, connect) -} - -func newClientTransportWS(endpoint string, cfg *clientConfig) (reconnectFunc, error) { - dialer := cfg.wsDialer - if dialer == nil { - dialer = &websocket.Dialer{ - ReadBufferSize: wsReadBuffer, - WriteBufferSize: wsWriteBuffer, - WriteBufferPool: wsBufferPool, - Proxy: http.ProxyFromEnvironment, - } - } - - dialURL, header, err := wsClientHeaders(endpoint, "") - if err != nil { - return nil, err - } - for key, values := range cfg.httpHeaders { - header[key] = values - } - - connect := func(ctx context.Context) (ServerCodec, error) { - header := header.Clone() - if cfg.httpAuth != nil { - if err := cfg.httpAuth(header); err != nil { - return nil, err - } - } - conn, resp, err := dialer.DialContext(ctx, dialURL, header) - if err != nil { - hErr := wsHandshakeError{err: err} - if resp != nil { - hErr.status = resp.Status - } - return nil, hErr - } - messageSizeLimit := int64(wsDefaultReadLimit) - if cfg.wsMessageSizeLimit != nil && *cfg.wsMessageSizeLimit >= 0 { - messageSizeLimit = *cfg.wsMessageSizeLimit - } - return newWebsocketCodec(conn, dialURL, header, messageSizeLimit), nil - } - return connect, nil -} - -func wsClientHeaders(endpoint, origin string) (string, http.Header, error) { - endpointURL, err := url.Parse(endpoint) - if err != nil { - return endpoint, nil, err - } - header := make(http.Header) - if origin != "" { - header.Add("origin", origin) - } - if endpointURL.User != nil { - b64auth := base64.StdEncoding.EncodeToString([]byte(endpointURL.User.String())) - header.Add("authorization", "Basic "+b64auth) - endpointURL.User = nil - } - return endpointURL.String(), header, nil -} - -type websocketCodec struct { - *jsonCodec - conn *websocket.Conn - info PeerInfo - - wg sync.WaitGroup - pingReset chan struct{} - pongReceived chan struct{} -} - -func newWebsocketCodec(conn *websocket.Conn, host string, req http.Header, readLimit int64) ServerCodec { - conn.SetReadLimit(readLimit) - encode := func(v interface{}, isErrorResponse bool) error { - return conn.WriteJSON(v) - } - wc := &websocketCodec{ - jsonCodec: NewFuncCodec(conn, encode, conn.ReadJSON).(*jsonCodec), - conn: conn, - pingReset: make(chan struct{}, 1), - pongReceived: make(chan struct{}), - info: PeerInfo{ - Transport: "ws", - RemoteAddr: conn.RemoteAddr().String(), - }, - } - // Fill in connection details. - wc.info.HTTP.Host = host - wc.info.HTTP.Origin = req.Get("Origin") - wc.info.HTTP.UserAgent = req.Get("User-Agent") - // Start pinger. - conn.SetPongHandler(func(appData string) error { - select { - case wc.pongReceived <- struct{}{}: - case <-wc.closed(): - } - return nil - }) - wc.wg.Add(1) - go wc.pingLoop() - return wc -} - -func (wc *websocketCodec) close() { - wc.jsonCodec.close() - wc.wg.Wait() -} - -func (wc *websocketCodec) peerInfo() PeerInfo { - return wc.info -} - -func (wc *websocketCodec) writeJSON(ctx context.Context, v interface{}, isError bool) error { - return wc.writeJSONSkipDeadline(ctx, v, isError, false) -} - -func (wc *websocketCodec) writeJSONSkipDeadline(ctx context.Context, v interface{}, isError bool, skip bool) error { - err := wc.jsonCodec.writeJSONSkipDeadline(ctx, v, isError, skip) - if err == nil { - // Notify pingLoop to delay the next idle ping. - select { - case wc.pingReset <- struct{}{}: - default: - } - } - return err -} - -// pingLoop sends periodic ping frames when the connection is idle. -func (wc *websocketCodec) pingLoop() { - var pingTimer = time.NewTimer(wsPingInterval) - defer wc.wg.Done() - defer pingTimer.Stop() - - for { - select { - case <-wc.closed(): - return - - case <-wc.pingReset: - if !pingTimer.Stop() { - <-pingTimer.C - } - pingTimer.Reset(wsPingInterval) - - case <-pingTimer.C: - wc.jsonCodec.encMu.Lock() - wc.conn.SetWriteDeadline(time.Now().Add(wsPingWriteTimeout)) - wc.conn.WriteMessage(websocket.PingMessage, nil) - wc.conn.SetReadDeadline(time.Now().Add(wsPongTimeout)) - wc.jsonCodec.encMu.Unlock() - pingTimer.Reset(wsPingInterval) - - case <-wc.pongReceived: - wc.conn.SetReadDeadline(time.Time{}) - } - } -} diff --git a/graft/subnet-evm/rpc/websocket_test.go b/graft/subnet-evm/rpc/websocket_test.go deleted file mode 100644 index 56d43862aa95..000000000000 --- a/graft/subnet-evm/rpc/websocket_test.go +++ /dev/null @@ -1,412 +0,0 @@ -// Copyright (C) 2019, Ava Labs, Inc. All rights reserved. -// See the file LICENSE for licensing terms. -// -// This file is a derived work, based on the go-ethereum library whose original -// notices appear below. -// -// It is distributed under a license compatible with the licensing terms of the -// original code from which it is derived. -// -// Much love to the original authors for their work. -// ********** -// Copyright 2018 The go-ethereum Authors -// This file is part of the go-ethereum library. -// -// The go-ethereum library is free software: you can redistribute it and/or modify -// it under the terms of the GNU Lesser General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// The go-ethereum library is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU Lesser General Public License for more details. -// -// You should have received a copy of the GNU Lesser General Public License -// along with the go-ethereum library. If not, see . - -package rpc - -import ( - "context" - "errors" - "net" - "net/http" - "net/http/httptest" - "strings" - "testing" - "time" - - "github.com/gorilla/websocket" -) - -func TestWebsocketClientHeaders(t *testing.T) { - t.Parallel() - - endpoint, header, err := wsClientHeaders("wss://testuser:test-PASS_01@example.com:1234", "https://example.com") - if err != nil { - t.Fatalf("wsGetConfig failed: %s", err) - } - if endpoint != "wss://example.com:1234" { - t.Fatal("User should have been stripped from the URL") - } - if header.Get("authorization") != "Basic dGVzdHVzZXI6dGVzdC1QQVNTXzAx" { - t.Fatal("Basic auth header is incorrect") - } - if header.Get("origin") != "https://example.com" { - t.Fatal("Origin not set") - } -} - -// This test checks that the server rejects connections from disallowed origins. -func TestWebsocketOriginCheck(t *testing.T) { - t.Parallel() - - var ( - srv = newTestServer() - httpsrv = httptest.NewServer(srv.WebsocketHandler([]string{"http://example.com"})) - wsURL = "ws:" + strings.TrimPrefix(httpsrv.URL, "http:") - ) - defer srv.Stop() - defer httpsrv.Close() - - client, err := DialWebsocket(context.Background(), wsURL, "http://ekzample.com") - if err == nil { - client.Close() - t.Fatal("no error for wrong origin") - } - wantErr := wsHandshakeError{websocket.ErrBadHandshake, "403 Forbidden"} - if !errors.Is(err, wantErr) { - t.Fatalf("wrong error for wrong origin: %q", err) - } - - // Connections without origin header should work. - client, err = DialWebsocket(context.Background(), wsURL, "") - if err != nil { - t.Fatalf("error for empty origin: %v", err) - } - client.Close() -} - -// This test checks whether calls exceeding the request size limit are rejected. -// -// This test times out occasionally due to context timeout differences with go-ethereum. -// These differences are not critical, so this test can simply be skipped. -func TestWebsocketLargeCall(t *testing.T) { - t.Skip("Flaky") - t.Parallel() - - var ( - srv = newTestServer() - httpsrv = httptest.NewServer(srv.WebsocketHandler([]string{"*"})) - wsURL = "ws:" + strings.TrimPrefix(httpsrv.URL, "http:") - ) - defer srv.Stop() - defer httpsrv.Close() - - client, err := DialWebsocket(context.Background(), wsURL, "") - if err != nil { - t.Fatalf("can't dial: %v", err) - } - defer client.Close() - - // This call sends slightly less than the limit and should work. - var result echoResult - arg := strings.Repeat("x", defaultBodyLimit-200) - if err := client.Call(&result, "test_echo", arg, 1); err != nil { - t.Fatalf("valid call didn't work: %v", err) - } - if result.String != arg { - t.Fatal("wrong string echoed") - } - - // This call sends twice the allowed size and shouldn't work. - arg = strings.Repeat("x", defaultBodyLimit*2) - err = client.Call(&result, "test_echo", arg) - if err == nil { - t.Fatal("no error for too large call") - } -} - -// This test checks whether the wsMessageSizeLimit option is obeyed. -// -// This test times out occasionally due to context timeout differences with go-ethereum. -// These differences are not critical, so this test can simply be skipped. -func TestWebsocketLargeRead(t *testing.T) { - t.Skip("Flaky") - t.Parallel() - - var ( - srv = newTestServer() - httpsrv = httptest.NewServer(srv.WebsocketHandler([]string{"*"})) - wsURL = "ws:" + strings.TrimPrefix(httpsrv.URL, "http:") - ) - defer srv.Stop() - defer httpsrv.Close() - - testLimit := func(limit *int64) { - opts := []ClientOption{} - expLimit := int64(wsDefaultReadLimit) - if limit != nil && *limit >= 0 { - opts = append(opts, WithWebsocketMessageSizeLimit(*limit)) - if *limit > 0 { - expLimit = *limit // 0 means infinite - } - } - client, err := DialOptions(context.Background(), wsURL, opts...) - if err != nil { - t.Fatalf("can't dial: %v", err) - } - defer client.Close() - // Remove some bytes for json encoding overhead. - underLimit := int(expLimit - 128) - overLimit := expLimit + 1 - if expLimit == wsDefaultReadLimit { - // No point trying the full 32MB in tests. Just sanity-check that - // it's not obviously limited. - underLimit = 1024 - overLimit = -1 - } - var res string - // Check under limit - if err = client.Call(&res, "test_repeat", "A", underLimit); err != nil { - t.Fatalf("unexpected error with limit %d: %v", expLimit, err) - } - if len(res) != underLimit || strings.Count(res, "A") != underLimit { - t.Fatal("incorrect data") - } - // Check over limit - if overLimit > 0 { - err = client.Call(&res, "test_repeat", "A", expLimit+1) - if err == nil || err != websocket.ErrReadLimit { - t.Fatalf("wrong error with limit %d: %v expecting %v", expLimit, err, websocket.ErrReadLimit) - } - } - } - ptr := func(v int64) *int64 { return &v } - - testLimit(ptr(-1)) // Should be ignored (use default) - testLimit(ptr(0)) // Should be ignored (use default) - testLimit(nil) // Should be ignored (use default) - testLimit(ptr(200)) - testLimit(ptr(wsDefaultReadLimit + 1024)) -} - -func TestWebsocketPeerInfo(t *testing.T) { - var ( - s = newTestServer() - ts = httptest.NewServer(s.WebsocketHandler([]string{"origin.example.com"})) - tsurl = "ws:" + strings.TrimPrefix(ts.URL, "http:") - ) - defer s.Stop() - defer ts.Close() - - ctx := context.Background() - c, err := DialWebsocket(ctx, tsurl, "origin.example.com") - if err != nil { - t.Fatal(err) - } - defer c.Close() - - // Request peer information. - var connInfo PeerInfo - if err := c.Call(&connInfo, "test_peerInfo"); err != nil { - t.Fatal(err) - } - - if connInfo.RemoteAddr == "" { - t.Error("RemoteAddr not set") - } - if connInfo.Transport != "ws" { - t.Errorf("wrong Transport %q", connInfo.Transport) - } - if connInfo.HTTP.UserAgent != "Go-http-client/1.1" { - t.Errorf("wrong HTTP.UserAgent %q", connInfo.HTTP.UserAgent) - } - if connInfo.HTTP.Origin != "origin.example.com" { - t.Errorf("wrong HTTP.Origin %q", connInfo.HTTP.UserAgent) - } -} - -// This test checks that client handles WebSocket ping frames correctly. -func TestClientWebsocketPing(t *testing.T) { - t.Parallel() - - var ( - sendPing = make(chan struct{}) - server = wsPingTestServer(t, sendPing) - ctx, cancel = context.WithTimeout(context.Background(), 2*time.Second) - ) - defer cancel() - defer server.Shutdown(ctx) - - client, err := DialContext(ctx, "ws://"+server.Addr) - if err != nil { - t.Fatalf("client dial error: %v", err) - } - defer client.Close() - - resultChan := make(chan int) - sub, err := client.EthSubscribe(ctx, resultChan, "foo") - if err != nil { - t.Fatalf("client subscribe error: %v", err) - } - // Note: Unsubscribe is not called on this subscription because the mockup - // server can't handle the request. - - // Wait for the context's deadline to be reached before proceeding. - // This is important for reproducing https://github.com/ethereum/go-ethereum/issues/19798 - <-ctx.Done() - close(sendPing) - - // Wait for the subscription result. - timeout := time.NewTimer(5 * time.Second) - defer timeout.Stop() - for { - select { - case err := <-sub.Err(): - t.Error("client subscription error:", err) - case result := <-resultChan: - t.Log("client got result:", result) - return - case <-timeout.C: - t.Error("didn't get any result within the test timeout") - return - } - } -} - -// This checks that the websocket transport can deal with large messages. -// -// This test is upstream from go-ethereum but is skipped because it is flaky. -// Error message: call failed: websocket: close 1006 (abnormal closure): unexpected EOF -func TestClientWebsocketLargeMessage(t *testing.T) { - t.Skip("Flaky") - var ( - srv = NewServer(0) - httpsrv = httptest.NewServer(srv.WebsocketHandler(nil)) - wsURL = "ws:" + strings.TrimPrefix(httpsrv.URL, "http:") - ) - defer srv.Stop() - defer httpsrv.Close() - - respLength := wsDefaultReadLimit - 50 - srv.RegisterName("test", largeRespService{respLength}) - - c, err := DialWebsocket(context.Background(), wsURL, "") - if err != nil { - t.Fatal(err) - } - defer c.Close() - - var r string - if err := c.Call(&r, "test_largeResp"); err != nil { - t.Fatal("call failed:", err) - } - if len(r) != respLength { - t.Fatalf("response has wrong length %d, want %d", len(r), respLength) - } -} - -// wsPingTestServer runs a WebSocket server which accepts a single subscription request. -// When a value arrives on sendPing, the server sends a ping frame, waits for a matching -// pong and finally delivers a single subscription result. -func wsPingTestServer(t *testing.T, sendPing <-chan struct{}) *http.Server { - var srv http.Server - shutdown := make(chan struct{}) - srv.RegisterOnShutdown(func() { - close(shutdown) - }) - srv.Handler = http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - // Upgrade to WebSocket. - upgrader := websocket.Upgrader{ - CheckOrigin: func(r *http.Request) bool { return true }, - } - conn, err := upgrader.Upgrade(w, r, nil) - if err != nil { - t.Errorf("server WS upgrade error: %v", err) - return - } - defer conn.Close() - - // Handle the connection. - wsPingTestHandler(t, conn, shutdown, sendPing) - }) - - // Start the server. - listener, err := net.Listen("tcp", "127.0.0.1:0") - if err != nil { - t.Fatal("can't listen:", err) - } - srv.Addr = listener.Addr().String() - go srv.Serve(listener) - return &srv -} - -func wsPingTestHandler(t *testing.T, conn *websocket.Conn, shutdown, sendPing <-chan struct{}) { - // Canned responses for the eth_subscribe call in TestClientWebsocketPing. - const ( - subResp = `{"jsonrpc":"2.0","id":1,"result":"0x00"}` - subNotify = `{"jsonrpc":"2.0","method":"eth_subscription","params":{"subscription":"0x00","result":1}}` - ) - - // Handle subscribe request. - if _, _, err := conn.ReadMessage(); err != nil { - t.Errorf("server read error: %v", err) - return - } - if err := conn.WriteMessage(websocket.TextMessage, []byte(subResp)); err != nil { - t.Errorf("server write error: %v", err) - return - } - - // Read from the connection to process control messages. - var pongCh = make(chan string) - conn.SetPongHandler(func(d string) error { - t.Logf("server got pong: %q", d) - pongCh <- d - return nil - }) - go func() { - for { - typ, msg, err := conn.ReadMessage() - if err != nil { - return - } - t.Logf("server got message (%d): %q", typ, msg) - } - }() - - // Write messages. - var ( - wantPong string - timer = time.NewTimer(0) - ) - defer timer.Stop() - <-timer.C - for { - select { - case _, open := <-sendPing: - if !open { - sendPing = nil - } - t.Logf("server sending ping") - conn.WriteMessage(websocket.PingMessage, []byte("ping")) - wantPong = "ping" - case data := <-pongCh: - if wantPong == "" { - t.Errorf("unexpected pong") - } else if data != wantPong { - t.Errorf("got pong with wrong data %q", data) - } - wantPong = "" - timer.Reset(200 * time.Millisecond) - case <-timer.C: - t.Logf("server sending response") - conn.WriteMessage(websocket.TextMessage, []byte(subNotify)) - case <-shutdown: - conn.Close() - return - } - } -} diff --git a/graft/subnet-evm/scripts/upstream_files.txt b/graft/subnet-evm/scripts/upstream_files.txt index f61354d464ab..8098030581a5 100644 --- a/graft/subnet-evm/scripts/upstream_files.txt +++ b/graft/subnet-evm/scripts/upstream_files.txt @@ -16,7 +16,6 @@ plugin/evm/customtypes/block_test.go plugin/evm/customtypes/hashing_test.go plugin/evm/customtypes/rlp_fuzzer_test.go plugin/evm/customtypes/types_test.go -rpc/* signer/* tests/init.go tests/rlp_test_util.go diff --git a/graft/subnet-evm/warp/client.go b/graft/subnet-evm/warp/client.go index a1aaf16cd923..6aec6245a872 100644 --- a/graft/subnet-evm/warp/client.go +++ b/graft/subnet-evm/warp/client.go @@ -9,7 +9,7 @@ import ( "github.com/ava-labs/libevm/common/hexutil" - "github.com/ava-labs/avalanchego/graft/subnet-evm/rpc" + "github.com/ava-labs/avalanchego/graft/evm/rpc" "github.com/ava-labs/avalanchego/ids" ) diff --git a/vms/evm/emulate/emulate.go b/vms/evm/emulate/emulate.go index ddff7444690b..5ad7834f57c9 100644 --- a/vms/evm/emulate/emulate.go +++ b/vms/evm/emulate/emulate.go @@ -8,8 +8,8 @@ package emulate import ( - cchain "github.com/ava-labs/avalanchego/graft/coreth/plugin/evm" - subnet "github.com/ava-labs/avalanchego/graft/subnet-evm/plugin/evm" + cchain "github.com/ava-labs/avalanchego/graft/coreth/plugin/evm/extras" + subnet "github.com/ava-labs/avalanchego/graft/subnet-evm/plugin/evm/extras" ) // CChain executes `fn` as if running in a `coreth` node.