diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index c2a61b944c..0f82322672 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -6,5 +6,5 @@ # review whenever someone opens a pull request. -* @ceyonur @darioush @aaronbuchwald +* @ceyonur @darioush diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index b5fa749548..480f3f1b24 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -8,6 +8,9 @@ on: - 'v[0-9]+.[0-9]+.[0-9]+' pull_request: +env: + tmpnet_data_path: ~/.tmpnet/networks/1000 + jobs: lint_test: name: Lint @@ -88,7 +91,7 @@ jobs: uses: actions/upload-artifact@v4 with: name: subnet-evm-e2e-logs-precompile - path: /tmp/network-runner-root-data*/ + path: /tmp/e2e-test/precompile-data retention-days: 5 e2e_warp: name: e2e warp tests @@ -124,14 +127,14 @@ jobs: run: ./scripts/build.sh /tmp/e2e-test/avalanchego/plugins/srEXiWaHuhNyGwPUi444Tu47ZEDwxTWrbQiuD7FmgSAQ6X7Dy - name: Run Warp E2E Tests shell: bash - run: AVALANCHEGO_BUILD_PATH=/tmp/e2e-test/avalanchego DATA_DIR=/tmp/e2e-test/warp-data ./scripts/run_ginkgo_warp.sh - - name: Upload Artifact + run: AVALANCHEGO_BUILD_PATH=/tmp/e2e-test/avalanchego ./scripts/run_ginkgo_warp.sh + - name: Upload tmpnet network dir for warp testing if: always() uses: actions/upload-artifact@v4 with: - name: subnet-evm-e2e-logs-warp - path: /tmp/network-runner-root-data*/ - retention-days: 5 + name: warp-tmpnet-data + path: ${{ env.tmpnet_data_path }} + if-no-files-found: error e2e_load: name: e2e load tests runs-on: ubuntu-latest @@ -153,14 +156,14 @@ jobs: run: ./scripts/build.sh /tmp/e2e-test/avalanchego/plugins/srEXiWaHuhNyGwPUi444Tu47ZEDwxTWrbQiuD7FmgSAQ6X7Dy - name: Run E2E Load Tests shell: bash - run: AVALANCHEGO_BUILD_PATH=/tmp/e2e-test/avalanchego DATA_DIR=/tmp/e2e-test/load-data ./scripts/run_ginkgo_load.sh - - name: Upload Artifact + run: AVALANCHEGO_BUILD_PATH=/tmp/e2e-test/avalanchego ./scripts/run_ginkgo_load.sh + - name: Upload tmpnet network dir for load testing if: always() uses: actions/upload-artifact@v4 with: - name: subnet-evm-e2e-logs-load - path: /tmp/network-runner-root-data*/ - retention-days: 5 + name: load-tmpnet-data + path: ${{ env.tmpnet_data_path }} + if-no-files-found: error build_image: name: Build Docker Image diff --git a/README.md b/README.md index 18e870e55b..a502f877fa 100644 --- a/README.md +++ b/README.md @@ -72,6 +72,7 @@ The Subnet EVM runs in a separate process from the main AvalancheGo process and [v0.5.11] AvalancheGo@v1.10.18-v1.10.19 (Protocol Version: 31) [v0.6.0] AvalancheGo@v1.11.0-v1.11.1 (Protocol Version: 33) [v0.6.1] AvalancheGo@v1.11.0-v1.11.1 (Protocol Version: 33) +[v0.6.2] AvalancheGo@v1.11.2 (Protocol Version: 34) ``` ## API diff --git a/accounts/abi/bind/precompilebind/precompile_bind.go b/accounts/abi/bind/precompilebind/precompile_bind.go index f517e0cb68..28fde58688 100644 --- a/accounts/abi/bind/precompilebind/precompile_bind.go +++ b/accounts/abi/bind/precompilebind/precompile_bind.go @@ -155,6 +155,9 @@ func createPrecompileHook(abifilename string, template string) bind.BindHook { for key := range allowlist.AllowListABI.Methods { delete(funcs, key) } + for events := range allowlist.AllowListABI.Events { + delete(contract.Events, events) + } } precompileContract := &tmplPrecompileContract{ diff --git a/accounts/abi/bind/precompilebind/precompile_bind_test.go b/accounts/abi/bind/precompilebind/precompile_bind_test.go index 07a4285f3a..9c1114a0f7 100644 --- a/accounts/abi/bind/precompilebind/precompile_bind_test.go +++ b/accounts/abi/bind/precompilebind/precompile_bind_test.go @@ -536,7 +536,7 @@ var bindTests = []struct { require.NoError(t, err) require.Equal(t, testBytes, unpacked.BytesTest) gasCost := GetTestEventGasCost(testEventData) - require.Equal(t, contract.LogGas + 2 * contract.LogTopicGas + contract.LogDataGas, gasCost) + require.Equal(t, contract.LogGas + 3 * contract.LogTopicGas + contract.LogDataGas, gasCost) topics, data, err = PackEmptyEvent() require.NoError(t, err) @@ -553,7 +553,7 @@ var bindTests = []struct { require.Equal(t, eventID, topics[0]) require.Equal(t, testAddr.Hash(), topics[1]) require.Equal(t, 0, len(data)) - require.Equal(t, contract.LogGas + 2 * contract.LogTopicGas, GetIndexedEventGasCost()) + require.Equal(t, contract.LogGas + 3 * contract.LogTopicGas, GetIndexedEventGasCost()) testMixedData := MixedEventData{ Num: testInt, @@ -567,7 +567,7 @@ var bindTests = []struct { unpackedMixedData, err := UnpackMixedEventData(data) require.NoError(t, err) require.Equal(t, testMixedData, unpackedMixedData) - require.Equal(t, contract.LogGas + contract.LogTopicGas + contract.LogDataGas, GetMixedEventGasCost(testMixedData)) + require.Equal(t, contract.LogGas + 2 * contract.LogTopicGas + contract.LogDataGas, GetMixedEventGasCost(testMixedData)) testDynamicData := DynamicEventData{ Str: "test", @@ -581,7 +581,7 @@ var bindTests = []struct { unpackedDynamicData, err := UnpackDynamicEventData(data) require.NoError(t, err) require.Equal(t, testDynamicData, unpackedDynamicData) - require.Equal(t, contract.LogGas + 2 * contract.LogTopicGas + 2 * contract.LogDataGas, GetDynamicEventGasCost(testDynamicData)) + require.Equal(t, contract.LogGas + 3 * contract.LogTopicGas + 2 * contract.LogDataGas, GetDynamicEventGasCost(testDynamicData)) topics, data, err = PackUnnamedEvent(testUint, testUint) require.NoError(t, err) @@ -589,7 +589,7 @@ var bindTests = []struct { require.Len(t, topics, 3) require.Equal(t, eventID, topics[0]) require.Equal(t, 0, len(data)) - require.Equal(t, contract.LogGas + 2 * contract.LogTopicGas, GetUnnamedEventGasCost()) + require.Equal(t, contract.LogGas + 3 * contract.LogTopicGas, GetUnnamedEventGasCost()) `, "", false, diff --git a/accounts/abi/bind/precompilebind/precompile_event_template.go b/accounts/abi/bind/precompilebind/precompile_event_template.go index 469a44d601..3a5582b35e 100644 --- a/accounts/abi/bind/precompilebind/precompile_event_template.go +++ b/accounts/abi/bind/precompilebind/precompile_event_template.go @@ -58,7 +58,7 @@ accessibleState.GetStateDB().AddLog( {{range .Contract.Events}} {{$event := .}} {{$createdDataStruct := false}} - {{$topicCount := 0}} + {{$topicCount := 1}} {{- range .Normalized.Inputs}} {{- if .Indexed}} {{$topicCount = add $topicCount 1}} @@ -81,8 +81,9 @@ accessibleState.GetStateDB().AddLog( // The gas cost of the non-indexed data depends on the data type and the data size. func Get{{.Normalized.Name}}EventGasCost({{if $createdDataStruct}} data {{.Normalized.Name}}EventData{{end}}) uint64 { gas := contract.LogGas // base gas cost - {{if $topicCount | lt 0}} + {{if $topicCount | lt 1}} // Add topics gas cost ({{$topicCount}} topics) + // Topics always include the signature hash of the event. The rest are the indexed event arguments. gas += contract.LogTopicGas * {{$topicCount}} {{end}} diff --git a/cmd/precompilegen/template-readme.md b/cmd/precompilegen/template-readme.md index d81e622b2b..09aa152658 100644 --- a/cmd/precompilegen/template-readme.md +++ b/cmd/precompilegen/template-readme.md @@ -5,19 +5,21 @@ For testing take a look at other precompile tests in contract_test.go and config See the tutorial in for more information about precompile development. General guidelines for precompile development: + 1- Set a suitable config key in generated module.go. E.g: "yourPrecompileConfig" 2- Read the comment and set a suitable contract address in generated module.go. E.g: ContractAddress = common.HexToAddress("ASUITABLEHEXADDRESS") 3- It is recommended to only modify code in the highlighted areas marked with "CUSTOM CODE STARTS HERE". Typically, custom codes are required in only those areas. Modifying code outside of these areas should be done with caution and with a deep understanding of how these changes may impact the EVM. -4- Set gas costs in generated contract.go -5- Force import your precompile package in precompile/registry/registry.go -6- Add your config unit tests under generated package config_test.go -7- Add your contract unit tests under generated package contract_test.go -8- Additionally you can add a full-fledged VM test for your precompile under plugin/vm/vm_test.go. See existing precompile tests for examples. -9- Add your solidity interface and test contract to contracts/contracts -10- Write solidity contract tests for your precompile in contracts/contracts/test -11- Write TypeScript DS-Test counterparts for your solidity tests in contracts/test -12- Create your genesis with your precompile enabled in tests/precompile/genesis/ -13- Create e2e test for your solidity test in tests/precompile/solidity/suites.go -14- Run your e2e precompile Solidity tests with './scripts/run_ginkgo.sh` +4- If you have any event defined in your precompile, review the generated event.go file and set your event gas costs. You should also emit your event in your function in the contract.go file. +5- Set gas costs in generated contract.go +6- Force import your precompile package in precompile/registry/registry.go +7- Add your config unit tests under generated package config_test.go +8- Add your contract unit tests under generated package contract_test.go +9- Additionally you can add a full-fledged VM test for your precompile under plugin/vm/vm_test.go. See existing precompile tests for examples. +10- Add your solidity interface and test contract to contracts/contracts +11- Write solidity contract tests for your precompile in contracts/contracts/test +12- Write TypeScript DS-Test counterparts for your solidity tests in contracts/test +13- Create your genesis with your precompile enabled in tests/precompile/genesis/ +14- Create e2e test for your solidity test in tests/precompile/solidity/suites.go +15- Run your e2e precompile Solidity tests with './scripts/run_ginkgo.sh` diff --git a/compatibility.json b/compatibility.json index 8e2d1a7e01..99f42b82fd 100644 --- a/compatibility.json +++ b/compatibility.json @@ -1,5 +1,6 @@ { "rpcChainVMProtocolVersion": { + "v0.6.2": 34, "v0.6.1": 33, "v0.6.0": 33, "v0.5.11": 31, diff --git a/core/txpool/txpool.go b/core/txpool/txpool.go index a175eaf9a1..1f33febc0a 100644 --- a/core/txpool/txpool.go +++ b/core/txpool/txpool.go @@ -202,7 +202,9 @@ type Config struct { // DefaultConfig contains the default configurations for the transaction // pool. var DefaultConfig = Config{ - Journal: "transactions.rlp", + // If we re-enable txpool journaling, we should also add the saved local + // transactions to the p2p gossip on startup. + Journal: "", Rejournal: time.Hour, PriceLimit: 1, @@ -213,7 +215,7 @@ var DefaultConfig = Config{ AccountQueue: 64, GlobalQueue: 1024, - Lifetime: 3 * time.Hour, + Lifetime: 10 * time.Minute, } // sanitize checks the provided user configurations and changes anything that's diff --git a/core/txpool/txpool_test.go b/core/txpool/txpool_test.go index c8f39a61e7..11bb991ac7 100644 --- a/core/txpool/txpool_test.go +++ b/core/txpool/txpool_test.go @@ -57,7 +57,7 @@ import ( var ( // testTxPoolConfig is a transaction pool configuration without stateful disk // sideeffects used during testing. - testTxPoolConfig Config + testTxPoolConfig = DefaultConfig // eip1559Config is a chain config with EIP-1559 enabled at block 0. eip1559Config *params.ChainConfig @@ -77,9 +77,6 @@ var ( ) func init() { - testTxPoolConfig = DefaultConfig - testTxPoolConfig.Journal = "" - cpy := *params.TestChainConfig eip1559Config = &cpy eip1559Config.SubnetEVMTimestamp = utils.NewUint64(0) diff --git a/eth/api_backend.go b/eth/api_backend.go index c7adcdaa5a..bf0594d550 100644 --- a/eth/api_backend.go +++ b/eth/api_backend.go @@ -340,7 +340,14 @@ func (b *EthAPIBackend) SendTx(ctx context.Context, signedTx *types.Transaction) if err := ctx.Err(); err != nil { return err } - return b.eth.txPool.AddLocal(signedTx) + if err := b.eth.txPool.AddLocal(signedTx); err != nil { + return err + } + + // We only enqueue transactions for push gossip if they were submitted over the RPC and + // added to the mempool. + b.eth.gossiper.Add(signedTx) + return nil } func (b *EthAPIBackend) GetPoolTransactions() (types.Transactions, error) { diff --git a/eth/backend.go b/eth/backend.go index 213387073f..a8de24fad9 100644 --- a/eth/backend.go +++ b/eth/backend.go @@ -70,6 +70,12 @@ type Settings struct { MaxBlocksPerRequest int64 // Maximum number of blocks to serve per getLogs request } +// PushGossiper sends pushes pending transactions to peers until they are +// removed from the mempool. +type PushGossiper interface { + Add(*types.Transaction) +} + // Ethereum implements the Ethereum full node service. type Ethereum struct { config *Config @@ -77,6 +83,7 @@ type Ethereum struct { // Handlers txPool *txpool.TxPool blockchain *core.BlockChain + gossiper PushGossiper // DB interfaces chainDb ethdb.Database // Block chain database @@ -117,6 +124,7 @@ func roundUpCacheSize(input int, allocSize int) int { func New( stack *node.Node, config *Config, + gossiper PushGossiper, chainDb ethdb.Database, settings Settings, lastAcceptedHash common.Hash, @@ -150,6 +158,7 @@ func New( eth := &Ethereum{ config: config, + gossiper: gossiper, chainDb: chainDb, eventMux: new(event.TypeMux), accountManager: stack.AccountManager(), @@ -228,7 +237,6 @@ func New( eth.bloomIndexer.Start(eth.blockchain) - config.TxPool.Journal = "" eth.txPool = txpool.NewTxPool(config.TxPool, eth.blockchain.Config(), eth.blockchain) eth.miner = miner.New(eth, &config.Miner, eth.blockchain.Config(), eth.EventMux(), eth.engine, clock) diff --git a/go.mod b/go.mod index 6f38b319ab..ee5eaf90cd 100644 --- a/go.mod +++ b/go.mod @@ -4,8 +4,7 @@ go 1.21 require ( github.com/VictoriaMetrics/fastcache v1.10.0 - github.com/ava-labs/avalanche-network-runner v1.7.6 - github.com/ava-labs/avalanchego v1.11.1 + github.com/ava-labs/avalanchego v1.11.2 github.com/cespare/cp v0.1.0 github.com/davecgh/go-spew v1.1.1 github.com/deckarep/golang-set/v2 v2.1.0 @@ -16,7 +15,7 @@ require ( github.com/fsnotify/fsnotify v1.6.0 github.com/gballet/go-libpcsclite v0.0.0-20191108122812-4678299bea08 github.com/go-cmd/cmd v1.4.1 - github.com/google/uuid v1.3.0 + github.com/google/uuid v1.6.0 github.com/gorilla/rpc v1.2.0 github.com/gorilla/websocket v1.4.2 github.com/hashicorp/go-bexpr v0.1.10 @@ -39,27 +38,26 @@ require ( github.com/stretchr/testify v1.8.4 github.com/tyler-smith/go-bip39 v1.1.0 github.com/urfave/cli/v2 v2.17.2-0.20221006022127-8f469abc00aa - go.uber.org/goleak v1.2.1 + go.uber.org/goleak v1.3.0 go.uber.org/mock v0.4.0 - golang.org/x/crypto v0.17.0 - golang.org/x/sync v0.5.0 - golang.org/x/sys v0.15.0 + golang.org/x/crypto v0.18.0 + golang.org/x/sync v0.6.0 + golang.org/x/sys v0.16.0 golang.org/x/text v0.14.0 golang.org/x/time v0.1.0 - google.golang.org/protobuf v1.31.0 + google.golang.org/protobuf v1.32.0 gopkg.in/natefinch/lumberjack.v2 v2.0.0 ) require ( github.com/BurntSushi/toml v1.3.2 // indirect github.com/DataDog/zstd v1.5.2 // indirect - github.com/Microsoft/go-winio v0.5.2 // indirect github.com/NYTimes/gziphandler v1.1.1 // indirect - github.com/ava-labs/coreth v0.13.0-rc.0 // indirect + github.com/ava-labs/coreth v0.13.1-rc.5 // indirect github.com/beorn7/perks v1.0.1 // indirect github.com/btcsuite/btcd/btcec/v2 v2.3.2 // indirect github.com/btcsuite/btcd/btcutil v1.1.3 // indirect - github.com/cenkalti/backoff/v4 v4.1.3 // indirect + github.com/cenkalti/backoff/v4 v4.2.1 // indirect github.com/cespare/xxhash/v2 v2.2.0 // indirect github.com/cockroachdb/errors v1.9.1 // indirect github.com/cockroachdb/logtags v0.0.0-20230118201751-21c54148d20b // indirect @@ -69,15 +67,13 @@ require ( github.com/decred/dcrd/dcrec/secp256k1/v4 v4.1.0 // indirect github.com/dlclark/regexp2 v1.7.0 // indirect github.com/getsentry/sentry-go v0.18.0 // indirect - github.com/go-logr/logr v1.3.0 // indirect + github.com/go-logr/logr v1.4.1 // indirect github.com/go-logr/stdr v1.2.2 // indirect github.com/go-ole/go-ole v1.2.6 // indirect github.com/go-sourcemap/sourcemap v2.1.3+incompatible // indirect github.com/go-stack/stack v1.8.1 // indirect github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572 // indirect github.com/gogo/protobuf v1.3.2 // indirect - github.com/golang-jwt/jwt/v4 v4.3.0 // indirect - github.com/golang/mock v1.6.0 // indirect github.com/golang/protobuf v1.5.3 // indirect github.com/golang/snappy v0.0.5-0.20220116011046-fa5810519dcb // indirect github.com/google/btree v1.1.2 // indirect @@ -86,7 +82,7 @@ require ( github.com/google/renameio/v2 v2.0.0 // indirect github.com/gorilla/mux v1.8.0 // indirect github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0 // indirect - github.com/grpc-ecosystem/grpc-gateway/v2 v2.15.2 // indirect + github.com/grpc-ecosystem/grpc-gateway/v2 v2.16.0 // indirect github.com/hashicorp/hcl v1.0.0 // indirect github.com/huin/goupnp v1.0.3 // indirect github.com/jackpal/gateway v1.0.6 // indirect @@ -101,7 +97,6 @@ require ( github.com/mitchellh/pointerstructure v1.2.0 // indirect github.com/mr-tron/base58 v1.2.0 // indirect github.com/nbutton23/zxcvbn-go v0.0.0-20180912185939-ae427f1e4c1d // indirect - github.com/otiai10/copy v1.11.0 // indirect github.com/pelletier/go-toml v1.9.5 // indirect github.com/pelletier/go-toml/v2 v2.0.5 // indirect github.com/pires/go-proxyproto v0.6.2 // indirect @@ -122,26 +117,24 @@ require ( github.com/tklauser/numcpus v0.2.2 // indirect github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673 // indirect github.com/yusufpapurcu/wmi v1.2.2 // indirect - go.opentelemetry.io/otel v1.11.0 // indirect - go.opentelemetry.io/otel/exporters/otlp/internal/retry v1.11.0 // indirect - go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.11.0 // indirect - go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.11.0 // indirect - go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.11.0 // indirect - go.opentelemetry.io/otel/sdk v1.11.0 // indirect - go.opentelemetry.io/otel/trace v1.11.0 // indirect - go.opentelemetry.io/proto/otlp v0.19.0 // indirect + go.opentelemetry.io/otel v1.22.0 // indirect + go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.22.0 // indirect + go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.22.0 // indirect + go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.22.0 // indirect + go.opentelemetry.io/otel/metric v1.22.0 // indirect + go.opentelemetry.io/otel/sdk v1.22.0 // indirect + go.opentelemetry.io/otel/trace v1.22.0 // indirect + go.opentelemetry.io/proto/otlp v1.0.0 // indirect go.uber.org/multierr v1.11.0 // indirect go.uber.org/zap v1.26.0 // indirect golang.org/x/exp v0.0.0-20231127185646-65229373498e // indirect - golang.org/x/mod v0.14.0 // indirect - golang.org/x/net v0.19.0 // indirect - golang.org/x/term v0.15.0 // indirect + golang.org/x/net v0.20.0 // indirect + golang.org/x/term v0.16.0 // indirect golang.org/x/tools v0.16.0 // indirect gonum.org/v1/gonum v0.11.0 // indirect - google.golang.org/genproto v0.0.0-20230711160842-782d3b101e98 // indirect - google.golang.org/genproto/googleapis/api v0.0.0-20230711160842-782d3b101e98 // indirect - google.golang.org/genproto/googleapis/rpc v0.0.0-20230711160842-782d3b101e98 // indirect - google.golang.org/grpc v1.58.3 // indirect + google.golang.org/genproto/googleapis/api v0.0.0-20240123012728-ef4313101c80 // indirect + google.golang.org/genproto/googleapis/rpc v0.0.0-20240123012728-ef4313101c80 // indirect + google.golang.org/grpc v1.62.0 // indirect gopkg.in/ini.v1 v1.67.0 // indirect gopkg.in/yaml.v2 v2.4.0 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect diff --git a/go.sum b/go.sum index 620f90fc9e..e1d4e023c1 100644 --- a/go.sum +++ b/go.sum @@ -46,11 +46,8 @@ github.com/CloudyKit/jet/v3 v3.0.0/go.mod h1:HKQPgSJmdK8hdoAbKUUWajkHyHo4RaU5rMd github.com/DataDog/zstd v1.5.2 h1:vUG4lAyuPCXO0TLbXvPv7EB7cNK1QV/luu55UHLrrn8= github.com/DataDog/zstd v1.5.2/go.mod h1:g4AWEaM3yOg3HYfnJ3YIawPnVdXJh9QME85blwSAmyw= github.com/Joker/hpp v1.0.0/go.mod h1:8x5n+M1Hp5hC0g8okX3sR3vFQwynaX/UgSOM9MeBKzY= -github.com/Microsoft/go-winio v0.5.2 h1:a9IhgEQBCUEk6QCdml9CiJGhAws+YwffDHEMp1VMrpA= -github.com/Microsoft/go-winio v0.5.2/go.mod h1:WpS1mjBmmwHBEWmogvA2mj8546UReBk4v8QkMxJ6pZY= github.com/NYTimes/gziphandler v1.1.1 h1:ZUDjpQae29j0ryrS0u/B8HZfJBtBQHjqw2rQ2cqUQ3I= github.com/NYTimes/gziphandler v1.1.1/go.mod h1:n/CVRwUEOgIxrgPvAQhUUr9oeUtvrhMomdKFjzJNB0c= -github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU= github.com/Shopify/goreferrer v0.0.0-20181106222321-ec9c9a553398/go.mod h1:a1uqRtAwp2Xwc6WNPJEufxJ7fx3npB4UV/JOLmbu5I0= github.com/VictoriaMetrics/fastcache v1.10.0 h1:5hDJnLsKLpnUEToub7ETuRu8RCkb40woBZAUiKonXzY= github.com/VictoriaMetrics/fastcache v1.10.0/go.mod h1:tjiYeEfYXCqacuvYw/7UoDIeJaNxq6132xHICNP77w8= @@ -58,14 +55,11 @@ github.com/aead/siphash v1.0.1/go.mod h1:Nywa3cDsYNNK3gaciGTWPwHt0wlpNV15vwmswBA github.com/ajg/form v1.5.1/go.mod h1:uL1WgH+h2mgNtvBq0339dVnzXdBETtL2LeUXaIv25UY= github.com/allegro/bigcache v1.2.1-0.20190218064605-e24eb225f156 h1:eMwmnE/GDgah4HI848JfFxHt+iPb26b4zyfspmqY0/8= github.com/allegro/bigcache v1.2.1-0.20190218064605-e24eb225f156/go.mod h1:Cb/ax3seSYIx7SuZdm2G2xzfwmv3TPSk2ucNfQESPXM= -github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY= github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5doyWs3UAsr3K4I6qtAmlQcZDesFNEHPZAzj8= -github.com/ava-labs/avalanche-network-runner v1.7.6 h1:2yXa2Zq099t900ffpn0RG9D5dca98fs3l+/hF7AumIY= -github.com/ava-labs/avalanche-network-runner v1.7.6/go.mod h1:+Br4mCjreTMtnDiUDNXJba500fnchMk0Ygu5qWVj6A4= -github.com/ava-labs/avalanchego v1.11.1 h1:NSelfZ/Di8uGCsRoFK32HOR262eHlpUFmAu8pbfg0Jo= -github.com/ava-labs/avalanchego v1.11.1/go.mod h1:+UpgT8X2fNN93+iE100efkZL7ePfBRfRdmpJ/i3YnyY= -github.com/ava-labs/coreth v0.13.0-rc.0 h1:V2l3qj2ek3geKDJAnF2M94mYJK8kg2kePixujfJ0bmk= -github.com/ava-labs/coreth v0.13.0-rc.0/go.mod h1:eUMbBLDhlZASJjcbf0gIcD2GMn2rRRCUxC8MXLt5QQk= +github.com/ava-labs/avalanchego v1.11.2 h1:8iodZ+RjqpRwHdiXPPtvaNt72qravge7voGzw3yPRzg= +github.com/ava-labs/avalanchego v1.11.2/go.mod h1:oTVnF9idL57J4LM/6RByTmKhI4QvV6OCnF99ysyBljE= +github.com/ava-labs/coreth v0.13.1-rc.5 h1:YcTs9nryZLkf4gPmMyFx1TREFpDTPdg/VCNGGHSF2TY= +github.com/ava-labs/coreth v0.13.1-rc.5/go.mod h1:4y1igTe/sFOIrpAtXoY+AdmfftNHrmrhBBRVfGCAPcw= github.com/aymerick/raymond v2.0.3-0.20180322193309-b565731e1464+incompatible/go.mod h1:osfaiScAUVup+UC9Nfq76eWqDhXlp+4UYaA8uhTBO6g= github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= @@ -93,13 +87,11 @@ github.com/btcsuite/snappy-go v0.0.0-20151229074030-0bdef8d06723/go.mod h1:8woku github.com/btcsuite/snappy-go v1.0.0/go.mod h1:8woku9dyThutzjeg+3xrA5iCpBRH8XEEg3lh6TiUghc= github.com/btcsuite/websocket v0.0.0-20150119174127-31079b680792/go.mod h1:ghJtEyQwv5/p4Mg4C0fgbePVuGr935/5ddU9Z3TmDRY= github.com/btcsuite/winsvc v1.0.0/go.mod h1:jsenWakMcC0zFBFurPLEAyrnc/teJEM1O46fmI40EZs= -github.com/cenkalti/backoff/v4 v4.1.3 h1:cFAlzYUlVYDysBEH2T5hyJZMh3+5+WCBvSnK6Q8UtC4= -github.com/cenkalti/backoff/v4 v4.1.3/go.mod h1:scbssz8iZGpm3xbr14ovlUdkxfGXNInqkPWOWmG2CLw= +github.com/cenkalti/backoff/v4 v4.2.1 h1:y4OZtCnogmCPw98Zjyt5a6+QwPLGkiQsYW5oUqylYbM= +github.com/cenkalti/backoff/v4 v4.2.1/go.mod h1:Y3VNntkOUPxTVeUxJ/G5vcM//AlwfmyYozVcomhLiZE= github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= github.com/cespare/cp v0.1.0 h1:SE+dxFebS7Iik5LK0tsi1k9ZCxEaFX4AjQmoyA+1dJk= github.com/cespare/cp v0.1.0/go.mod h1:SOGHArjBr4JWaSDEVpWpo/hNg6RoKrls6Oh40hiwW+s= -github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc= -github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= github.com/cespare/xxhash/v2 v2.1.2/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= github.com/cespare/xxhash/v2 v2.2.0 h1:DC2CZ1Ep5Y4k3ZQ899DldepgrayRUGE6BBZ/cd9Cj44= github.com/cespare/xxhash/v2 v2.2.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= @@ -113,11 +105,6 @@ github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDk github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc= github.com/cncf/udpa/go v0.0.0-20200629203442-efcf912fb354/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk= github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk= -github.com/cncf/udpa/go v0.0.0-20210930031921-04548b0d99d4/go.mod h1:6pvJx4me5XPnfI9Z40ddWsdw2W/uZgQLFXToKeRcDiI= -github.com/cncf/xds/go v0.0.0-20210312221358-fbca930ec8ed/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= -github.com/cncf/xds/go v0.0.0-20210805033703-aa0b78936158/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= -github.com/cncf/xds/go v0.0.0-20210922020428-25de7278fc84/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= -github.com/cncf/xds/go v0.0.0-20211011173535-cb28da3451f1/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= github.com/cockroachdb/datadriven v1.0.2 h1:H9MtNqVoVhvd9nCBwOyDjUEdZCREqbIdCJD93PBm/jA= github.com/cockroachdb/datadriven v1.0.2/go.mod h1:a9RdTaap04u637JoCzcUoIcDmvwSUtcUFtT/C3kJlTU= github.com/cockroachdb/errors v1.9.1 h1:yFVvsI0VxmRShfawbt/laCIDy/mtTqqnvoNgiy5bEV8= @@ -169,8 +156,6 @@ github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1m github.com/envoyproxy/go-control-plane v0.9.7/go.mod h1:cwu0lG7PUMfa9snN8LXBig5ynNVH9qI8YYLbd1fK2po= github.com/envoyproxy/go-control-plane v0.9.9-0.20201210154907-fd9021fe5dad/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk= github.com/envoyproxy/go-control-plane v0.9.9-0.20210217033140-668b12f5399d/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk= -github.com/envoyproxy/go-control-plane v0.9.9-0.20210512163311-63b5d3c536b0/go.mod h1:hliV/p42l8fGbc6Y9bQ70uLwIvmJyVE5k4iMKlh8wCQ= -github.com/envoyproxy/go-control-plane v0.9.10-0.20210907150352-cf90f659a021/go.mod h1:AFq3mo9L8Lqqiid3OhADV3RfLJnjiw63cSpi+fDTRC0= github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= github.com/etcd-io/bbolt v1.3.3/go.mod h1:ZF2nL25h33cCyBtcyWeZ2/I3HQOfTP+0PIEvHjkjCrw= github.com/ethereum/go-ethereum v1.12.0 h1:bdnhLPtqETd4m3mS8BGMNvBTf36bO5bx/hxE2zljOa0= @@ -192,7 +177,6 @@ github.com/gballet/go-libpcsclite v0.0.0-20191108122812-4678299bea08/go.mod h1:x github.com/getsentry/sentry-go v0.12.0/go.mod h1:NSap0JBYWzHND8oMbyi0+XZhUalc1TBdRL1M71JZW2c= github.com/getsentry/sentry-go v0.18.0 h1:MtBW5H9QgdcJabtZcuJG80BMOwaBpkRDZkxRkNC1sN0= github.com/getsentry/sentry-go v0.18.0/go.mod h1:Kgon4Mby+FJ7ZWHFUAZgVaIa8sxHtnRJRLTXZr51aKQ= -github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= github.com/gin-contrib/sse v0.0.0-20190301062529-5545eab6dad3/go.mod h1:VJ0WA2NBN22VlZ2dKZQPAPnyWw5XTlK1KymzLKsr59s= github.com/gin-gonic/gin v1.4.0/go.mod h1:OW2EZn3DO8Ln9oIKOvM++LBO+5UPHJJDH72/q/3rZdM= github.com/go-check/check v0.0.0-20180628173108-788fd7840127/go.mod h1:9ES+weclKsC9YodN5RgxqK/VD9HM9JsCSh7rNhMZE98= @@ -205,8 +189,8 @@ github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9 github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= -github.com/go-logr/logr v1.3.0 h1:2y3SDp0ZXuc6/cjLSZ+Q3ir+QB9T/iG5yYRXqsagWSY= -github.com/go-logr/logr v1.3.0/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= +github.com/go-logr/logr v1.4.1 h1:pKouT5E8xu9zeFC39JXRDukb6JFQPXM5p5I91188VAQ= +github.com/go-logr/logr v1.4.1/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag= github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE= github.com/go-martini/martini v0.0.0-20170121215854-22fa46961aab/go.mod h1:/P9AEU963A2AYjv4d1V5eVL1CQbEJq6aCNHDDjibzu8= @@ -233,12 +217,9 @@ github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q= github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q= github.com/gogo/status v1.1.0/go.mod h1:BFv9nrluPLmrS0EmGVvLaPNmRosr9KapBYd5/hpY1WM= github.com/golang-jwt/jwt v3.2.2+incompatible/go.mod h1:8pz2t5EyA70fFQQSrl6XZXzqecmYZeUEB8OUGHkxJ+I= -github.com/golang-jwt/jwt/v4 v4.3.0 h1:kHL1vqdqWNfATmA0FNMdmZNMyZI1U6O31X4rlIPoBog= -github.com/golang-jwt/jwt/v4 v4.3.0/go.mod h1:/xlHOz8bRuivTWchD4jCa+NbatV+wEUSzwAxVc6locg= github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= -github.com/golang/glog v1.0.0/go.mod h1:EWib/APOK0SL3dFbYqvxE3UYd8E6s1ouQ7iEp/0LWV4= -github.com/golang/glog v1.1.0 h1:/d3pCKDPWNnvIWe0vVUpNP32qc8U3PDVxySP/y360qE= -github.com/golang/glog v1.1.0/go.mod h1:pfYeQZ3JWZoXTV5sFc986z3HTpwQs9At6P4ImfuP3NQ= +github.com/golang/glog v1.2.0 h1:uCdmnmatrKCgMBlM4rMuJZWOkPDqdbZPnrMXDY4gI68= +github.com/golang/glog v1.2.0/go.mod h1:6AhwSGph0fcJtXVM/PEHPqZlFeoLxhs7/t5UDAwmO+w= github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/groupcache v0.0.0-20191227052852-215e87163ea7/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= @@ -287,7 +268,6 @@ github.com/google/go-cmp v0.5.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/ github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/google/go-querystring v1.0.0/go.mod h1:odCYkC5MyYFN7vkCjXpyrEuKhc/BUO6wN/zVPAxq5ck= @@ -312,8 +292,8 @@ github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm4 github.com/google/renameio/v2 v2.0.0 h1:UifI23ZTGY8Tt29JbYFiuyIU3eX+RNFtUwefq9qAhxg= github.com/google/renameio/v2 v2.0.0/go.mod h1:BtmJXm5YlszgC+TD4HOEEUFgkJP3nLxehU6hfe7jRt4= github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= -github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I= -github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= +github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg= github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk= github.com/googleapis/google-cloud-go-testing v0.0.0-20200911160855-bcd43fbb19e8/go.mod h1:dvDLG8qkwmyD9a/MJJN3XJcT3xFxOKAvTZGvuZmac9g= @@ -327,10 +307,8 @@ github.com/gorilla/websocket v1.4.2 h1:+/TMaTYc4QFitKJxsQ7Yye35DkWvkdLcvGKqM+x0U github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0 h1:Ovs26xHkKqVztRpIrF/92BcuyuQ/YW4NSIpoGtfXNho= github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgfV/d3M/q6VIi02HzZEHgUlZvzk= -github.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw= -github.com/grpc-ecosystem/grpc-gateway/v2 v2.7.0/go.mod h1:hgWBS7lorOAVIJEQMi4ZsPv9hVvWI6+ch50m39Pf2Ks= -github.com/grpc-ecosystem/grpc-gateway/v2 v2.15.2 h1:gDLXvp5S9izjldquuoAhDzccbskOL6tDC5jMSyx3zxE= -github.com/grpc-ecosystem/grpc-gateway/v2 v2.15.2/go.mod h1:7pdNwVWBBHGiCxa9lAszqCJMbfTISJ7oMftp8+UGV08= +github.com/grpc-ecosystem/grpc-gateway/v2 v2.16.0 h1:YBftPWNWd4WwGqtY2yeZL2ef8rHAxPBD8KFhJpmcqms= +github.com/grpc-ecosystem/grpc-gateway/v2 v2.16.0/go.mod h1:YN5jB8ie0yfIUg6VvR9Kz84aCaG7AsGZnLjhHbUqwPg= 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= @@ -467,10 +445,6 @@ github.com/onsi/gomega v1.17.0/go.mod h1:HnhC7FXeEQY45zxNK3PPoIUhzk/80Xly9PcubAl github.com/onsi/gomega v1.19.0/go.mod h1:LY+I3pBVzYsTBU1AnDwOSxaYi9WoWiqgwooUqq9yPro= github.com/onsi/gomega v1.29.0 h1:KIA/t2t5UBzoirT4H9tsML45GEbo3ouUnBHsCfD2tVg= github.com/onsi/gomega v1.29.0/go.mod h1:9sxs+SwGrKI0+PWe4Fxa9tFQQBG5xSsSbMXOI8PPpoQ= -github.com/otiai10/copy v1.11.0 h1:OKBD80J/mLBrwnzXqGtFCzprFSGioo30JcmR4APsNwc= -github.com/otiai10/copy v1.11.0/go.mod h1:rSaLseMUsZFFbsFGc7wCJnnkTAvdc5L6VWxPE4308Ww= -github.com/otiai10/mint v1.5.1 h1:XaPLeE+9vGbuyEHem1JNk3bYc7KKqyI/na0/mLd/Kks= -github.com/otiai10/mint v1.5.1/go.mod h1:MJm72SBthJjz8qhefc4z1PYEieWmy8Bku7CjcAqyUSM= github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic= github.com/pelletier/go-toml v1.9.5 h1:4yBQzkHv+7BHq2PQUZF3Mx0IYxG7LsP222s7Agd3ve8= github.com/pelletier/go-toml v1.9.5/go.mod h1:u1nR/EPcESfeI/szUZKdtJ0xRNbUoANCkoOuaOx1Y+c= @@ -496,7 +470,6 @@ github.com/prometheus/common v0.42.0 h1:EKsfXEYo4JpWMHH5cg+KOUWeuJSov1Id8zGR8eeI github.com/prometheus/common v0.42.0/go.mod h1:xBwqVerjNdUDjgODMpudtOMwlOwf2SaTr1yjz4b7Zbc= github.com/prometheus/procfs v0.10.1 h1:kYK1Va/YMlutzCGazswoHKo//tZVlFpKYh+PymziUAg= github.com/prometheus/procfs v0.10.1/go.mod h1:nwNm2aOCAYw8uTR/9bWRREkZFxAUcWzPHWJq+XBB/FM= -github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ= github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= github.com/rogpeppe/go-internal v1.6.1/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc= github.com/rogpeppe/go-internal v1.8.1/go.mod h1:JeRgkft04UBgHMgCIwADu4Pn6Mtm5d4nPKWu0nJ5d+o= @@ -516,10 +489,8 @@ github.com/sergi/go-diff v1.0.0/go.mod h1:0CfEIISq7TuYL3j771MWULgwwjU+GofnZX9QAm github.com/shirou/gopsutil v3.21.11+incompatible h1:+1+c1VGhc88SSonWP6foOcLhvnKlUeu/erjjvaPEYiI= github.com/shirou/gopsutil v3.21.11+incompatible/go.mod h1:5b4v6he4MtMOwMlS0TUMTu2PcXUg8+E1lC7eC3UO/RA= github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc= -github.com/sirupsen/logrus v1.7.0/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0= github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc= github.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA= -github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA= github.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ= github.com/spf13/afero v1.8.2 h1:xehSyVa0YnHWsJ49JFljMpg1HX19V6NDZ1fkm1Xznbo= github.com/spf13/afero v1.8.2/go.mod h1:CtAatgMJh6bJEIs48Ay/FOnkljP3WeGUG0MC1RfAqwo= @@ -540,8 +511,6 @@ github.com/status-im/keycard-go v0.2.0 h1:QDLFswOQu1r5jsycloeQh3bVU8n/NatHHaZobt 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/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= -github.com/stretchr/objx v0.5.0 h1:1zr/of2m5FGMsad5YfcqgdqdWrIhu+EBEJRhR1U7z/c= -github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= @@ -604,25 +573,24 @@ go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= go.opencensus.io v0.22.3/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= go.opencensus.io v0.22.4/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= go.opencensus.io v0.22.5/go.mod h1:5pWMHQbX5EPX2/62yrJeAkowc+lfs/XD7Uxpq3pI6kk= -go.opentelemetry.io/otel v1.11.0 h1:kfToEGMDq6TrVrJ9Vht84Y8y9enykSZzDDZglV0kIEk= -go.opentelemetry.io/otel v1.11.0/go.mod h1:H2KtuEphyMvlhZ+F7tg9GRhAOe60moNx61Ex+WmiKkk= -go.opentelemetry.io/otel/exporters/otlp/internal/retry v1.11.0 h1:0dly5et1i/6Th3WHn0M6kYiJfFNzhhxanrJ0bOfnjEo= -go.opentelemetry.io/otel/exporters/otlp/internal/retry v1.11.0/go.mod h1:+Lq4/WkdCkjbGcBMVHHg2apTbv8oMBf29QCnyCCJjNQ= -go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.11.0 h1:eyJ6njZmH16h9dOKCi7lMswAnGsSOwgTqWzfxqcuNr8= -go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.11.0/go.mod h1:FnDp7XemjN3oZ3xGunnfOUTVwd2XcvLbtRAuOSU3oc8= -go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.11.0 h1:j2RFV0Qdt38XQ2Jvi4WIsQ56w8T7eSirYbMw19VXRDg= -go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.11.0/go.mod h1:pILgiTEtrqvZpoiuGdblDgS5dbIaTgDrkIuKfEFkt+A= -go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.11.0 h1:v29I/NbVp7LXQYMFZhU6q17D0jSEbYOAVONlrO1oH5s= -go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.11.0/go.mod h1:/RpLsmbQLDO1XCbWAM4S6TSwj8FKwwgyKKyqtvVfAnw= -go.opentelemetry.io/otel/sdk v1.11.0 h1:ZnKIL9V9Ztaq+ME43IUi/eo22mNsb6a7tGfzaOWB5fo= -go.opentelemetry.io/otel/sdk v1.11.0/go.mod h1:REusa8RsyKaq0OlyangWXaw97t2VogoO4SSEeKkSTAk= -go.opentelemetry.io/otel/trace v1.11.0 h1:20U/Vj42SX+mASlXLmSGBg6jpI1jQtv682lZtTAOVFI= -go.opentelemetry.io/otel/trace v1.11.0/go.mod h1:nyYjis9jy0gytE9LXGU+/m1sHTKbRY0fX0hulNNDP1U= -go.opentelemetry.io/proto/otlp v0.7.0/go.mod h1:PqfVotwruBrMGOCsRd/89rSnXhoiJIqeYNgFYFoEGnI= -go.opentelemetry.io/proto/otlp v0.19.0 h1:IVN6GR+mhC4s5yfcTbmzHYODqvWAp3ZedA2SJPI1Nnw= -go.opentelemetry.io/proto/otlp v0.19.0/go.mod h1:H7XAot3MsfNsj7EXtrA2q5xSNQ10UqI405h3+duxN4U= -go.uber.org/goleak v1.2.1 h1:NBol2c7O1ZokfZ0LEU9K6Whx/KnwvepVetCUhtKja4A= -go.uber.org/goleak v1.2.1/go.mod h1:qlT2yGI9QafXHhZZLxlSuNsMw3FFLxBr+tBRlmO1xH4= +go.opentelemetry.io/otel v1.22.0 h1:xS7Ku+7yTFvDfDraDIJVpw7XPyuHlB9MCiqqX5mcJ6Y= +go.opentelemetry.io/otel v1.22.0/go.mod h1:eoV4iAi3Ea8LkAEI9+GFT44O6T/D0GWAVFyZVCC6pMI= +go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.22.0 h1:9M3+rhx7kZCIQQhQRYaZCdNu1V73tm4TvXs2ntl98C4= +go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.22.0/go.mod h1:noq80iT8rrHP1SfybmPiRGc9dc5M8RPmGvtwo7Oo7tc= +go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.22.0 h1:H2JFgRcGiyHg7H7bwcwaQJYrNFqCqrbTQ8K4p1OvDu8= +go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.22.0/go.mod h1:WfCWp1bGoYK8MeULtI15MmQVczfR+bFkk0DF3h06QmQ= +go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.22.0 h1:FyjCyI9jVEfqhUh2MoSkmolPjfh5fp2hnV0b0irxH4Q= +go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.22.0/go.mod h1:hYwym2nDEeZfG/motx0p7L7J1N1vyzIThemQsb4g2qY= +go.opentelemetry.io/otel/metric v1.22.0 h1:lypMQnGyJYeuYPhOM/bgjbFM6WE44W1/T45er4d8Hhg= +go.opentelemetry.io/otel/metric v1.22.0/go.mod h1:evJGjVpZv0mQ5QBRJoBF64yMuOf4xCWdXjK8pzFvliY= +go.opentelemetry.io/otel/sdk v1.22.0 h1:6coWHw9xw7EfClIC/+O31R8IY3/+EiRFHevmHafB2Gw= +go.opentelemetry.io/otel/sdk v1.22.0/go.mod h1:iu7luyVGYovrRpe2fmj3CVKouQNdTOkxtLzPvPz1DOc= +go.opentelemetry.io/otel/trace v1.22.0 h1:Hg6pPujv0XG9QaVbGOBVHunyuLcCC3jN7WEhPx83XD0= +go.opentelemetry.io/otel/trace v1.22.0/go.mod h1:RbbHXVqKES9QhzZq/fE5UnOSILqRt40a21sPw2He1xo= +go.opentelemetry.io/proto/otlp v1.0.0 h1:T0TX0tmXU8a3CbNXzEKGeU5mIVOdf0oykP+u2lIVU/I= +go.opentelemetry.io/proto/otlp v1.0.0/go.mod h1:Sy6pihPLfYHkr3NkUbEhGHFhINUSI/v80hjKIs5JXpM= +go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto= +go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE= go.uber.org/mock v0.4.0 h1:VcM4ZOtdbR4f6VXfiOpwpVJDL6lCReaZ6mw31wqh7KU= go.uber.org/mock v0.4.0/go.mod h1:a6FSlNadKUHUa9IP5Vyt1zh4fC7uAwxMutEAscFbkZc= go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0= @@ -642,8 +610,8 @@ golang.org/x/crypto v0.0.0-20210322153248-0c34fe9e7dc2/go.mod h1:T9bdIzuCu7OtxOm golang.org/x/crypto v0.0.0-20210421170649-83a5a9bb288b/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4= golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= golang.org/x/crypto v0.0.0-20211108221036-ceb1ce70b4fa/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= -golang.org/x/crypto v0.17.0 h1:r8bRNjWL3GshPW3gkd+RpvzWrZAwPS49OmTGZ/uhM4k= -golang.org/x/crypto v0.17.0/go.mod h1:gCAAfMLgwOJRpTjQ2zCCt2OcSfYMTeZVSRtQlPC7Nq4= +golang.org/x/crypto v0.18.0 h1:PGVlW0xEltQnzFZ55hkuX5+KLyrMYhHld1YHO4AKcdc= +golang.org/x/crypto v0.18.0/go.mod h1:R0j02AL6hcrfOiy9T4ZYp/rcWeMxM3L6QYxlOuEG1mg= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8= @@ -728,8 +696,8 @@ golang.org/x/net v0.0.0-20211008194852-3b03d305991f/go.mod h1:9nx3DQGgdP8bBQD5qx golang.org/x/net v0.0.0-20220225172249-27dd8689420f/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= golang.org/x/net v0.0.0-20220607020251-c690dde0001d/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= -golang.org/x/net v0.19.0 h1:zTwKpTd2XuCqf8huc7Fo2iSy+4RHPd10s4KzeTnVr1c= -golang.org/x/net v0.19.0/go.mod h1:CfAk/cbD4CthTvqiEl8NpboMuiuOYsAr/7NOjZJtv1U= +golang.org/x/net v0.20.0 h1:aCL9BSgETF1k+blQaYUBx9hJ9LOGP3gAVemcZlf1Kpo= +golang.org/x/net v0.20.0/go.mod h1:z8BVo6PvndSri0LbOE3hAn0apkU+1YvI6E70E9jsnvY= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= @@ -739,7 +707,6 @@ golang.org/x/oauth2 v0.0.0-20200902213428-5d25da1a8d43/go.mod h1:KelEdhl1UZF7XfJ golang.org/x/oauth2 v0.0.0-20201109201403-9fd604954f58/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= golang.org/x/oauth2 v0.0.0-20201208152858-08078c50e5b5/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= golang.org/x/oauth2 v0.0.0-20210218202405-ba52d332ba99/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= -golang.org/x/oauth2 v0.0.0-20211104180415-d3ed0bb246c8/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= @@ -752,8 +719,8 @@ golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJ golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.5.0 h1:60k92dhOjHxJkrqnwsfl8KuaHbn/5dl0lUPUklKo3qE= -golang.org/x/sync v0.5.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= +golang.org/x/sync v0.6.0 h1:5BMeUDZ7vkXGfEr1x9B4bRcTH4lpkTkpdh0T/J+qjbQ= +golang.org/x/sync v0.6.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20181205085412-a5c9d58dba9a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= @@ -772,7 +739,6 @@ golang.org/x/sys v0.0.0-20190904154756-749cb33beabd/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191001151750-bb3f8db39f24/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191120155948-bd437916bb0e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191228213918-04cbcbbfeed8/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -799,7 +765,6 @@ golang.org/x/sys v0.0.0-20201201145000-ef89a241ccb3/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20210104204734-6f8348627aad/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210112080510-489259a85091/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210119212857-b64e53b001e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210225134936-a50acf3fe073/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210316164454-77fc1eacc6aa/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -820,12 +785,12 @@ golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220908164124-27713097b956/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.15.0 h1:h48lPFYpsTvQJZF4EKyI4aLHaev3CxivZmv7yZig9pc= -golang.org/x/sys v0.15.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/sys v0.16.0 h1:xWw16ngr6ZMtmxDyKyIgsE93KNKz5HKmMa3b8ALHidU= +golang.org/x/sys v0.16.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= -golang.org/x/term v0.15.0 h1:y/Oo/a/q3IXu26lQgl04j/gjuBDOBlx7X6Om1j2CPW4= -golang.org/x/term v0.15.0/go.mod h1:BDl952bC7+uMoWR75FIrCDx79TPU9oHkTZ9yRbYOrX0= +golang.org/x/term v0.16.0 h1:m+B6fahuftsE9qjo0VWp2FW0mB3MTJvR0BaMQrq0pmE= +golang.org/x/term v0.16.0/go.mod h1:yn7UURbUtPyrVJPGPq404EukNFxcm/foM+bV/bfcDsY= golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= @@ -959,7 +924,6 @@ google.golang.org/genproto v0.0.0-20200312145019-da6875a35672/go.mod h1:55QSHmfG google.golang.org/genproto v0.0.0-20200331122359-1ee6d9798940/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= google.golang.org/genproto v0.0.0-20200430143042-b979b6f78d84/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= google.golang.org/genproto v0.0.0-20200511104702-f5ebc3bea380/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= -google.golang.org/genproto v0.0.0-20200513103714-09dca8ec2884/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= google.golang.org/genproto v0.0.0-20200515170657-fc4c6c6a6587/go.mod h1:YsZOwe1myG/8QRHRsmBRE1LrgQY60beZKjly0O1fX9U= google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo= google.golang.org/genproto v0.0.0-20200618031413-b414f8b61790/go.mod h1:jDfRM7FcilCzHH/e9qn6dsT145K34l5v+OpcnNgKAAA= @@ -974,13 +938,12 @@ google.golang.org/genproto v0.0.0-20201214200347-8c77b98c765d/go.mod h1:FWY/as6D google.golang.org/genproto v0.0.0-20210108203827-ffc7fda8c3d7/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= google.golang.org/genproto v0.0.0-20210226172003-ab064af71705/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= google.golang.org/genproto v0.0.0-20210624195500-8bfb893ecb84/go.mod h1:SzzZ/N+nwJDaO1kznhnlzqS8ocJICar6hYhVyhi++24= -google.golang.org/genproto v0.0.0-20211118181313-81c1377c94b1/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= -google.golang.org/genproto v0.0.0-20230711160842-782d3b101e98 h1:Z0hjGZePRE0ZBWotvtrwxFNrNE9CUAGtplaDK5NNI/g= -google.golang.org/genproto v0.0.0-20230711160842-782d3b101e98/go.mod h1:S7mY02OqCJTD0E1OiQy1F72PWFB4bZJ87cAtLPYgDR0= -google.golang.org/genproto/googleapis/api v0.0.0-20230711160842-782d3b101e98 h1:FmF5cCW94Ij59cfpoLiwTgodWmm60eEV0CjlsVg2fuw= -google.golang.org/genproto/googleapis/api v0.0.0-20230711160842-782d3b101e98/go.mod h1:rsr7RhLuwsDKL7RmgDDCUc6yaGr1iqceVb5Wv6f6YvQ= -google.golang.org/genproto/googleapis/rpc v0.0.0-20230711160842-782d3b101e98 h1:bVf09lpb+OJbByTj913DRJioFFAjf/ZGxEz7MajTp2U= -google.golang.org/genproto/googleapis/rpc v0.0.0-20230711160842-782d3b101e98/go.mod h1:TUfxEVdsvPg18p6AslUXFoLdpED4oBnGwyqk3dV1XzM= +google.golang.org/genproto v0.0.0-20240123012728-ef4313101c80 h1:KAeGQVN3M9nD0/bQXnr/ClcEMJ968gUXJQ9pwfSynuQ= +google.golang.org/genproto v0.0.0-20240123012728-ef4313101c80/go.mod h1:cc8bqMqtv9gMOr0zHg2Vzff5ULhhL2IXP4sbcn32Dro= +google.golang.org/genproto/googleapis/api v0.0.0-20240123012728-ef4313101c80 h1:Lj5rbfG876hIAYFjqiJnPHfhXbv+nzTWfm04Fg/XSVU= +google.golang.org/genproto/googleapis/api v0.0.0-20240123012728-ef4313101c80/go.mod h1:4jWUdICTdgc3Ibxmr8nAJiiLHwQBY0UI0XZcEMaFKaA= +google.golang.org/genproto/googleapis/rpc v0.0.0-20240123012728-ef4313101c80 h1:AjyfHzEPEFp/NpvfN5g+KDla3EMojjhRVZc1i7cj+oM= +google.golang.org/genproto/googleapis/rpc v0.0.0-20240123012728-ef4313101c80/go.mod h1:PAREbraiVEVGVdTZsVWjSbbTtSyGbAgIIvni8a8CD5s= google.golang.org/grpc v1.12.0/go.mod h1:yo6s7OP7yaDglbqo1J04qKzAhqBH6lvTonzMVmEdcZw= google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38= @@ -995,16 +958,12 @@ google.golang.org/grpc v1.29.1/go.mod h1:itym6AZVZYACWQqET3MqgPpjcuV5QH3BxFS3Iji google.golang.org/grpc v1.30.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= google.golang.org/grpc v1.31.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= google.golang.org/grpc v1.31.1/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= -google.golang.org/grpc v1.33.1/go.mod h1:fr5YgcSWrqhRRxogOsw7RzIpsmvOZ6IcH4kBYTpR3n0= google.golang.org/grpc v1.33.2/go.mod h1:JMHMWHQWaTccqQQlmk3MJZS+GWXOdAesneDmEnv2fbc= google.golang.org/grpc v1.34.0/go.mod h1:WotjhfgOW/POjDeRt8vscBtXq+2VjORFy659qA51WJ8= google.golang.org/grpc v1.35.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU= -google.golang.org/grpc v1.36.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU= google.golang.org/grpc v1.38.0/go.mod h1:NREThFqKR1f3iQ6oBuvc5LadQuXVGo9rkm5ZGrQdJfM= -google.golang.org/grpc v1.40.0/go.mod h1:ogyxbiOoUXAkP+4+xa6PZSE9DZgIHtSpzjDTB9KAK34= -google.golang.org/grpc v1.42.0/go.mod h1:k+4IHHFw41K8+bbowsex27ge2rCb65oeWqe4jJ590SU= -google.golang.org/grpc v1.58.3 h1:BjnpXut1btbtgN/6sp+brB2Kbm2LjNXnidYujAVbSoQ= -google.golang.org/grpc v1.58.3/go.mod h1:tgX3ZQDlNJGU96V6yHh1T/JeoBQ2TXdr43YbYSsCJk0= +google.golang.org/grpc v1.62.0 h1:HQKZ/fa1bXkX1oFOvSjmZEUL8wLSaZTjCcLAlmZRtdk= +google.golang.org/grpc v1.62.0/go.mod h1:IWTG0VlJLCh1SkC58F7np9ka9mx/WNkjl4PGJaiq+QE= google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= @@ -1017,9 +976,8 @@ google.golang.org/protobuf v1.24.0/go.mod h1:r/3tXBNzIEhYS9I1OUVjXDlt8tc493IdKGj google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c= google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= -google.golang.org/protobuf v1.27.1/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= -google.golang.org/protobuf v1.31.0 h1:g0LDEJHgrBl9N9r17Ru3sqWhkIx2NB67okBHPwC7hs8= -google.golang.org/protobuf v1.31.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= +google.golang.org/protobuf v1.32.0 h1:pPC6BG5ex8PDFnkbrGU3EixyhKcQ2aDuBS36lqK/C7I= +google.golang.org/protobuf v1.32.0/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= @@ -1039,7 +997,6 @@ gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkep gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw= gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v2 v2.2.3/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= diff --git a/internal/ethapi/api_test.go b/internal/ethapi/api_test.go index 6d7fb681bc..45884d2995 100644 --- a/internal/ethapi/api_test.go +++ b/internal/ethapi/api_test.go @@ -54,7 +54,6 @@ import ( "github.com/ava-labs/subnet-evm/core/vm" "github.com/ava-labs/subnet-evm/params" "github.com/ava-labs/subnet-evm/rpc" - "github.com/ethereum/go-ethereum" "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common/hexutil" "github.com/ethereum/go-ethereum/crypto" @@ -225,7 +224,6 @@ func newTestBackend(t *testing.T, n int, gspec *core.Genesis, generator func(i i return backend } -func (b testBackend) SyncProgress() ethereum.SyncProgress { return ethereum.SyncProgress{} } func (b testBackend) SuggestGasTipCap(ctx context.Context) (*big.Int, error) { return big.NewInt(0), nil } diff --git a/peer/client.go b/peer/client.go index 41ebc7d6d9..d40e1a268a 100644 --- a/peer/client.go +++ b/peer/client.go @@ -34,9 +34,6 @@ type NetworkClient interface { // Returns response bytes, and ErrRequestFailed if the request failed. SendCrossChainRequest(ctx context.Context, chainID ids.ID, request []byte) ([]byte, error) - // Gossip sends given gossip message to peers - Gossip(gossip []byte) error - // TrackBandwidth should be called for each valid request with the bandwidth // (length of response divided by request time), and with 0 if the response is invalid. TrackBandwidth(nodeID ids.NodeID, bandwidth float64) @@ -90,10 +87,6 @@ func (c *client) SendCrossChainRequest(ctx context.Context, chainID ids.ID, requ return waitingHandler.WaitForResult(ctx) } -func (c *client) Gossip(gossip []byte) error { - return c.network.Gossip(gossip) -} - func (c *client) TrackBandwidth(nodeID ids.NodeID, bandwidth float64) { c.network.TrackBandwidth(nodeID, bandwidth) } diff --git a/peer/network.go b/peer/network.go index 3011356093..3e64df7872 100644 --- a/peer/network.go +++ b/peer/network.go @@ -51,9 +51,6 @@ type Network interface { // SendAppRequest sends message to given nodeID, notifying handler when there's a response or timeout SendAppRequest(ctx context.Context, nodeID ids.NodeID, message []byte, handler message.ResponseHandler) error - // Gossip sends given gossip message to peers - Gossip(gossip []byte) error - // SendCrossChainRequest sends a message to given chainID notifying handler when there's a response or timeout SendCrossChainRequest(ctx context.Context, chainID ids.ID, message []byte, handler message.ResponseHandler) error @@ -437,15 +434,6 @@ func (n *network) markRequestFulfilled(requestID uint32) (message.ResponseHandle return handler, true } -// Gossip sends given gossip message to peers -func (n *network) Gossip(gossip []byte) error { - if n.closed.Get() { - return nil - } - - return n.appSender.SendAppGossip(context.TODO(), gossip) -} - // AppGossip is called by avalanchego -> VM when there is an incoming AppGossip // from a peer. An error returned by this function is treated as fatal by the // engine. diff --git a/peer/network_test.go b/peer/network_test.go index 6ae1a6d2b4..2bd20fd477 100644 --- a/peer/network_test.go +++ b/peer/network_test.go @@ -503,49 +503,6 @@ func TestOnRequestHonoursDeadline(t *testing.T) { assert.EqualValues(t, requestHandler.calls, 1) } -func TestGossip(t *testing.T) { - codecManager := buildCodec(t, HelloGossip{}) - crossChainCodecManager := buildCodec(t, ExampleCrossChainRequest{}, ExampleCrossChainResponse{}) - - nodeID := ids.GenerateTestNodeID() - var clientNetwork Network - wg := &sync.WaitGroup{} - sentGossip := false - wg.Add(1) - sender := testAppSender{ - sendAppGossipFn: func(msg []byte) error { - go func() { - defer wg.Done() - err := clientNetwork.AppGossip(context.Background(), nodeID, msg) - assert.NoError(t, err) - }() - sentGossip = true - return nil - }, - } - - gossipHandler := &testGossipHandler{} - p2pNetwork, err := p2p.NewNetwork(logging.NoLog{}, nil, prometheus.NewRegistry(), "") - require.NoError(t, err) - clientNetwork = NewNetwork(p2pNetwork, sender, codecManager, crossChainCodecManager, ids.EmptyNodeID, 1, 1) - clientNetwork.SetGossipHandler(gossipHandler) - - assert.NoError(t, clientNetwork.Connected(context.Background(), nodeID, defaultPeerVersion)) - - client := NewNetworkClient(clientNetwork) - defer clientNetwork.Shutdown() - - b, err := buildGossip(codecManager, HelloGossip{Msg: "hello there!"}) - assert.NoError(t, err) - - err = client.Gossip(b) - assert.NoError(t, err) - - wg.Wait() - assert.True(t, sentGossip) - assert.True(t, gossipHandler.received) -} - func TestHandleInvalidMessages(t *testing.T) { codecManager := buildCodec(t, HelloGossip{}, TestMessage{}) crossChainCodecManager := buildCodec(t, ExampleCrossChainRequest{}, ExampleCrossChainResponse{}) @@ -914,7 +871,7 @@ type testAppSender struct { sendCrossChainAppResponseFn func(ids.ID, uint32, []byte) error sendAppRequestFn func(context.Context, set.Set[ids.NodeID], uint32, []byte) error sendAppResponseFn func(ids.NodeID, uint32, []byte) error - sendAppGossipFn func([]byte) error + sendAppGossipFn func([]byte, int, int, int) error } func (t testAppSender) SendCrossChainAppRequest(_ context.Context, chainID ids.ID, requestID uint32, appRequestBytes []byte) error { @@ -937,8 +894,8 @@ func (t testAppSender) SendAppResponse(_ context.Context, nodeID ids.NodeID, req return t.sendAppResponseFn(nodeID, requestID, message) } -func (t testAppSender) SendAppGossip(_ context.Context, message []byte) error { - return t.sendAppGossipFn(message) +func (t testAppSender) SendAppGossip(_ context.Context, message []byte, numValidators int, numNonValidators int, numPeers int) error { + return t.sendAppGossipFn(message, numValidators, numNonValidators, numPeers) } func (t testAppSender) SendAppError(ctx context.Context, nodeID ids.NodeID, requestID uint32, errorCode int32, errorMessage string) error { diff --git a/plugin/evm/block_builder.go b/plugin/evm/block_builder.go index 640ea79888..00d1596318 100644 --- a/plugin/evm/block_builder.go +++ b/plugin/evm/block_builder.go @@ -27,8 +27,7 @@ type blockBuilder struct { ctx *snow.Context chainConfig *params.ChainConfig - txPool *txpool.TxPool - gossiper Gossiper + txPool *txpool.TxPool shutdownChan <-chan struct{} shutdownWg *sync.WaitGroup @@ -56,7 +55,6 @@ func (vm *VM) NewBlockBuilder(notifyBuildBlockChan chan<- commonEng.Message) *bl ctx: vm.ctx, chainConfig: vm.chainConfig, txPool: vm.txPool, - gossiper: vm.gossiper, shutdownChan: vm.shutdownChan, shutdownWg: &vm.shutdownWg, notifyBuildBlockChan: notifyBuildBlockChan, @@ -152,20 +150,9 @@ func (b *blockBuilder) awaitSubmittedTxs() { for { select { - case ethTxsEvent := <-txSubmitChan: + case <-txSubmitChan: log.Trace("New tx detected, trying to generate a block") b.signalTxsReady() - - if b.gossiper != nil && len(ethTxsEvent.Txs) > 0 { - // [GossipEthTxs] will block unless [gossiper.ethTxsToGossipChan] (an - // unbuffered channel) is listened on - if err := b.gossiper.GossipEthTxs(ethTxsEvent.Txs); err != nil { - log.Warn( - "failed to gossip new eth transactions", - "err", err, - ) - } - } case <-b.shutdownChan: b.buildBlockTimer.Stop() return diff --git a/plugin/evm/config.go b/plugin/evm/config.go index 5cf2027eab..4443680d08 100644 --- a/plugin/evm/config.go +++ b/plugin/evm/config.go @@ -35,12 +35,13 @@ const ( defaultMaxBlocksPerRequest = 0 // Default to no maximum on the number of blocks per getLogs request defaultContinuousProfilerFrequency = 15 * time.Minute defaultContinuousProfilerMaxFiles = 5 - defaultRegossipFrequency = 1 * time.Minute - defaultRegossipMaxTxs = 16 - defaultRegossipTxsPerAddress = 1 - defaultPriorityRegossipFrequency = 1 * time.Second - defaultPriorityRegossipMaxTxs = 32 - defaultPriorityRegossipTxsPerAddress = 16 + defaultPushGossipNumValidators = 100 + defaultPushGossipNumPeers = 0 + defaultPushRegossipNumValidators = 10 + defaultPushRegossipNumPeers = 0 + defaultPushGossipFrequency = 100 * time.Millisecond + defaultPullGossipFrequency = 1 * time.Second + defaultRegossipFrequency = 30 * time.Second defaultOfflinePruningBloomFilterSize uint64 = 512 // Default size (MB) for the offline pruner to use defaultLogLevel = "info" defaultLogJSONFormat = false @@ -141,14 +142,13 @@ type Config struct { // API Settings LocalTxsEnabled bool `json:"local-txs-enabled"` - TxPoolJournal string `json:"tx-pool-journal"` - TxPoolRejournal Duration `json:"tx-pool-rejournal"` TxPoolPriceLimit uint64 `json:"tx-pool-price-limit"` TxPoolPriceBump uint64 `json:"tx-pool-price-bump"` TxPoolAccountSlots uint64 `json:"tx-pool-account-slots"` TxPoolGlobalSlots uint64 `json:"tx-pool-global-slots"` TxPoolAccountQueue uint64 `json:"tx-pool-account-queue"` TxPoolGlobalQueue uint64 `json:"tx-pool-global-queue"` + TxPoolLifetime Duration `json:"tx-pool-lifetime"` APIMaxDuration Duration `json:"api-max-duration"` WSCPURefillRate Duration `json:"ws-cpu-refill-rate"` @@ -164,14 +164,14 @@ type Config struct { KeystoreInsecureUnlockAllowed bool `json:"keystore-insecure-unlock-allowed"` // Gossip Settings - RemoteGossipOnlyEnabled bool `json:"remote-gossip-only-enabled"` - RegossipFrequency Duration `json:"regossip-frequency"` - RegossipMaxTxs int `json:"regossip-max-txs"` - RegossipTxsPerAddress int `json:"regossip-txs-per-address"` - PriorityRegossipFrequency Duration `json:"priority-regossip-frequency"` - PriorityRegossipMaxTxs int `json:"priority-regossip-max-txs"` - PriorityRegossipTxsPerAddress int `json:"priority-regossip-txs-per-address"` - PriorityRegossipAddresses []common.Address `json:"priority-regossip-addresses"` + PushGossipNumValidators int `json:"push-gossip-num-validators"` + PushGossipNumPeers int `json:"push-gossip-num-peers"` + PushRegossipNumValidators int `json:"push-regossip-num-validators"` + PushRegossipNumPeers int `json:"push-regossip-num-peers"` + PushGossipFrequency Duration `json:"push-gossip-frequency"` + PullGossipFrequency Duration `json:"pull-gossip-frequency"` + RegossipFrequency Duration `json:"regossip-frequency"` + PriorityRegossipAddresses []common.Address `json:"priority-regossip-addresses"` // Log LogLevel string `json:"log-level"` @@ -267,14 +267,13 @@ func (c *Config) SetDefaults() { c.RPCTxFeeCap = defaultRpcTxFeeCap c.MetricsExpensiveEnabled = defaultMetricsExpensiveEnabled - c.TxPoolJournal = txpool.DefaultConfig.Journal - c.TxPoolRejournal = Duration{txpool.DefaultConfig.Rejournal} c.TxPoolPriceLimit = txpool.DefaultConfig.PriceLimit c.TxPoolPriceBump = txpool.DefaultConfig.PriceBump c.TxPoolAccountSlots = txpool.DefaultConfig.AccountSlots c.TxPoolGlobalSlots = txpool.DefaultConfig.GlobalSlots c.TxPoolAccountQueue = txpool.DefaultConfig.AccountQueue c.TxPoolGlobalQueue = txpool.DefaultConfig.GlobalQueue + c.TxPoolLifetime.Duration = txpool.DefaultConfig.Lifetime c.APIMaxDuration.Duration = defaultApiMaxDuration c.WSCPURefillRate.Duration = defaultWsCpuRefillRate @@ -291,12 +290,13 @@ func (c *Config) SetDefaults() { c.AcceptorQueueLimit = defaultAcceptorQueueLimit c.CommitInterval = defaultCommitInterval c.SnapshotWait = defaultSnapshotWait + c.PushGossipNumValidators = defaultPushGossipNumValidators + c.PushGossipNumPeers = defaultPushGossipNumPeers + c.PushRegossipNumValidators = defaultPushRegossipNumValidators + c.PushRegossipNumPeers = defaultPushRegossipNumPeers + c.PushGossipFrequency.Duration = defaultPushGossipFrequency + c.PullGossipFrequency.Duration = defaultPullGossipFrequency c.RegossipFrequency.Duration = defaultRegossipFrequency - c.RegossipMaxTxs = defaultRegossipMaxTxs - c.RegossipTxsPerAddress = defaultRegossipTxsPerAddress - c.PriorityRegossipFrequency.Duration = defaultPriorityRegossipFrequency - c.PriorityRegossipMaxTxs = defaultPriorityRegossipMaxTxs - c.PriorityRegossipTxsPerAddress = defaultPriorityRegossipTxsPerAddress c.OfflinePruningBloomFilterSize = defaultOfflinePruningBloomFilterSize c.LogLevel = defaultLogLevel c.LogJSONFormat = defaultLogJSONFormat diff --git a/plugin/evm/config_test.go b/plugin/evm/config_test.go index 53a7777423..2a69ea68d0 100644 --- a/plugin/evm/config_test.go +++ b/plugin/evm/config_test.go @@ -22,8 +22,8 @@ func TestUnmarshalConfig(t *testing.T) { }{ { "string durations parsed", - []byte(`{"api-max-duration": "1m", "continuous-profiler-frequency": "2m", "tx-pool-rejournal": "3m30s"}`), - Config{APIMaxDuration: Duration{1 * time.Minute}, ContinuousProfilerFrequency: Duration{2 * time.Minute}, TxPoolRejournal: Duration{3*time.Minute + 30*time.Second}}, + []byte(`{"api-max-duration": "1m", "continuous-profiler-frequency": "2m"}`), + Config{APIMaxDuration: Duration{1 * time.Minute}, ContinuousProfilerFrequency: Duration{2 * time.Minute}}, false, }, { @@ -34,8 +34,8 @@ func TestUnmarshalConfig(t *testing.T) { }, { "nanosecond durations parsed", - []byte(`{"api-max-duration": 5000000000, "continuous-profiler-frequency": 5000000000, "tx-pool-rejournal": 9000000000}`), - Config{APIMaxDuration: Duration{5 * time.Second}, ContinuousProfilerFrequency: Duration{5 * time.Second}, TxPoolRejournal: Duration{9 * time.Second}}, + []byte(`{"api-max-duration": 5000000000, "continuous-profiler-frequency": 5000000000}`), + Config{APIMaxDuration: Duration{5 * time.Second}, ContinuousProfilerFrequency: Duration{5 * time.Second}}, false, }, { @@ -47,9 +47,8 @@ func TestUnmarshalConfig(t *testing.T) { { "tx pool configurations", - []byte(`{"tx-pool-journal": "hello", "tx-pool-price-limit": 1, "tx-pool-price-bump": 2, "tx-pool-account-slots": 3, "tx-pool-global-slots": 4, "tx-pool-account-queue": 5, "tx-pool-global-queue": 6}`), + []byte(`{"tx-pool-price-limit": 1, "tx-pool-price-bump": 2, "tx-pool-account-slots": 3, "tx-pool-global-slots": 4, "tx-pool-account-queue": 5, "tx-pool-global-queue": 6}`), Config{ - TxPoolJournal: "hello", TxPoolPriceLimit: 1, TxPoolPriceBump: 2, TxPoolAccountSlots: 3, diff --git a/plugin/evm/gossip.go b/plugin/evm/gossip.go index 0db574f5fe..e87bb354d3 100644 --- a/plugin/evm/gossip.go +++ b/plugin/evm/gossip.go @@ -11,6 +11,7 @@ import ( "github.com/ava-labs/avalanchego/ids" "github.com/ava-labs/avalanchego/utils/logging" + "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/log" "github.com/prometheus/client_golang/prometheus" @@ -20,14 +21,19 @@ import ( "github.com/ava-labs/subnet-evm/core" "github.com/ava-labs/subnet-evm/core/txpool" "github.com/ava-labs/subnet-evm/core/types" + "github.com/ava-labs/subnet-evm/eth" ) +const pendingTxsBuffer = 10 + var ( _ p2p.Handler = (*txGossipHandler)(nil) _ gossip.Gossipable = (*GossipEthTx)(nil) _ gossip.Marshaller[*GossipEthTx] = (*GossipEthTxMarshaller)(nil) _ gossip.Set[*GossipEthTx] = (*GossipEthTxPool)(nil) + + _ eth.PushGossiper = (*EthPushGossiper)(nil) ) func newTxGossipHandler[T gossip.Gossipable]( @@ -41,11 +47,9 @@ func newTxGossipHandler[T gossip.Gossipable]( validators *p2p.Validators, ) txGossipHandler { // push gossip messages can be handled from any peer - handler := gossip.NewHandler[T]( + handler := gossip.NewHandler( log, marshaller, - // Don't forward gossip to avoid double-forwarding - gossip.NoOpAccumulator[T]{}, mempool, metrics, maxMessageSize, @@ -93,7 +97,7 @@ func NewGossipEthTxPool(mempool *txpool.TxPool, registerer prometheus.Registerer return &GossipEthTxPool{ mempool: mempool, - pendingTxs: make(chan core.NewTxsEvent), + pendingTxs: make(chan core.NewTxsEvent, pendingTxsBuffer), bloom: bloom, }, nil } @@ -146,6 +150,12 @@ func (g *GossipEthTxPool) Add(tx *GossipEthTx) error { return g.mempool.AddRemotes([]*types.Transaction{tx.Tx})[0] } +// Has should just return whether or not the [txID] is still in the mempool, +// not whether it is in the mempool AND pending. +func (g *GossipEthTxPool) Has(txID ids.ID) bool { + return g.mempool.Has(common.Hash(txID)) +} + func (g *GossipEthTxPool) Iterate(f func(tx *GossipEthTx) bool) { g.mempool.IteratePending(func(tx *types.Transaction) bool { return f(&GossipEthTx{Tx: tx}) @@ -180,3 +190,19 @@ type GossipEthTx struct { func (tx *GossipEthTx) GossipID() ids.ID { return ids.ID(tx.Tx.Hash()) } + +// EthPushGossiper is used by the ETH backend to push transactions issued over +// the RPC and added to the mempool to peers. +type EthPushGossiper struct { + vm *VM +} + +func (e *EthPushGossiper) Add(tx *types.Transaction) { + // eth.Backend is initialized before the [ethTxPushGossiper] is created, so + // we just ignore any gossip requests until it is set. + ethTxPushGossiper := e.vm.ethTxPushGossiper.Get() + if ethTxPushGossiper == nil { + return + } + ethTxPushGossiper.Add(&GossipEthTx{tx}) +} diff --git a/plugin/evm/gossip_stats.go b/plugin/evm/gossip_stats.go index 45cb43445a..4b093f89ac 100644 --- a/plugin/evm/gossip_stats.go +++ b/plugin/evm/gossip_stats.go @@ -9,12 +9,6 @@ var _ GossipStats = &gossipStats{} // GossipStats contains methods for updating incoming and outgoing gossip stats. type GossipStats interface { - GossipReceivedStats - GossipSentStats -} - -// GossipReceivedStats groups functions for incoming gossip stats. -type GossipReceivedStats interface { IncEthTxsGossipReceived() // new vs. known txs received @@ -33,13 +27,6 @@ type GossipReceivedStats interface { // GossipSentStats groups functions for outgoing gossip stats. type GossipSentStats interface { - IncEthTxsGossipSent() - - // regossip - IncEthTxsRegossipQueued() - IncEthTxsRegossipQueuedLocal(count int) - IncEthTxsRegossipQueuedRemote(count int) - IncSignedOrdersGossipSent(count int64) IncSignedOrdersGossipBatchSent() IncSignedOrdersGossipSendError() @@ -49,14 +36,8 @@ type GossipSentStats interface { // gossipStats implements stats for incoming and outgoing gossip stats. type gossipStats struct { // messages - ethTxsGossipSent metrics.Counter ethTxsGossipReceived metrics.Counter - // regossip - ethTxsRegossipQueued metrics.Counter - ethTxsRegossipQueuedLocal metrics.Counter - ethTxsRegossipQueuedRemote metrics.Counter - // new vs. known txs received ethTxsGossipReceivedError metrics.Counter ethTxsGossipReceivedKnown metrics.Counter @@ -79,13 +60,7 @@ type gossipStats struct { func NewGossipStats() GossipStats { return &gossipStats{ - ethTxsGossipSent: metrics.GetOrRegisterCounter("gossip_eth_txs_sent", nil), - ethTxsGossipReceived: metrics.GetOrRegisterCounter("gossip_eth_txs_received", nil), - - ethTxsRegossipQueued: metrics.GetOrRegisterCounter("regossip_eth_txs_queued_attempts", nil), - ethTxsRegossipQueuedLocal: metrics.GetOrRegisterCounter("regossip_eth_txs_queued_local_tx_count", nil), - ethTxsRegossipQueuedRemote: metrics.GetOrRegisterCounter("regossip_eth_txs_queued_remote_tx_count", nil), - + ethTxsGossipReceived: metrics.GetOrRegisterCounter("gossip_eth_txs_received", nil), ethTxsGossipReceivedError: metrics.GetOrRegisterCounter("gossip_eth_txs_received_error", nil), ethTxsGossipReceivedKnown: metrics.GetOrRegisterCounter("gossip_eth_txs_received_known", nil), ethTxsGossipReceivedNew: metrics.GetOrRegisterCounter("gossip_eth_txs_received_new", nil), @@ -111,18 +86,6 @@ func (g *gossipStats) IncEthTxsGossipReceivedError() { g.ethTxsGossipReceivedErr func (g *gossipStats) IncEthTxsGossipReceivedKnown() { g.ethTxsGossipReceivedKnown.Inc(1) } func (g *gossipStats) IncEthTxsGossipReceivedNew() { g.ethTxsGossipReceivedNew.Inc(1) } -// outgoing messages -func (g *gossipStats) IncEthTxsGossipSent() { g.ethTxsGossipSent.Inc(1) } - -// regossip -func (g *gossipStats) IncEthTxsRegossipQueued() { g.ethTxsRegossipQueued.Inc(1) } -func (g *gossipStats) IncEthTxsRegossipQueuedLocal(count int) { - g.ethTxsRegossipQueuedLocal.Inc(int64(count)) -} -func (g *gossipStats) IncEthTxsRegossipQueuedRemote(count int) { - g.ethTxsRegossipQueuedRemote.Inc(int64(count)) -} - // incoming messages func (g *gossipStats) IncSignedOrdersGossipReceived(count int64) { g.signedOrdersGossipReceived.Inc(count) diff --git a/plugin/evm/gossip_test.go b/plugin/evm/gossip_test.go index a6b8dc0498..e1b47ec972 100644 --- a/plugin/evm/gossip_test.go +++ b/plugin/evm/gossip_test.go @@ -98,7 +98,6 @@ func setupPoolWithConfig(t *testing.T, config *params.ChainConfig, fundedAddress chain, err := core.NewBlockChain(diskdb, core.DefaultCacheConfig, gspec, engine, vm.Config{}, common.Hash{}, false) require.NoError(t, err) testTxPoolConfig := txpool.DefaultConfig - testTxPoolConfig.Journal = "" pool := txpool.NewTxPool(testTxPoolConfig, config, chain) return pool diff --git a/plugin/evm/gossiper_eth_gossiping_test.go b/plugin/evm/gossiper_eth_gossiping_test.go index 22982f2d69..a2bb95c93b 100644 --- a/plugin/evm/gossiper_eth_gossiping_test.go +++ b/plugin/evm/gossiper_eth_gossiping_test.go @@ -7,7 +7,6 @@ import ( "context" "crypto/ecdsa" "encoding/json" - "fmt" "math/big" "os" "strings" @@ -72,158 +71,6 @@ func getValidEthTxs(key *ecdsa.PrivateKey, count int, gasPrice *big.Int) []*type return res } -// show that locally issued eth txs are gossiped -// Note: channel through which subnet-evm mempool push txs to vm is injected here -// to ease up UT, which target only VM behaviors in response to subnet-evm mempool -// signals -func TestMempoolEthTxsAddedTxsGossipedAfterActivation(t *testing.T) { - if os.Getenv("RUN_FLAKY_TESTS") != "true" { - t.Skip("FLAKY") - } - assert := assert.New(t) - - key, err := crypto.GenerateKey() - assert.NoError(err) - - addr := crypto.PubkeyToAddress(key.PublicKey) - - genesisJSON, err := fundAddressByGenesis([]common.Address{addr}) - assert.NoError(err) - - _, vm, _, sender := GenesisVM(t, true, genesisJSON, "", "") - defer func() { - err := vm.Shutdown(context.Background()) - assert.NoError(err) - }() - vm.txPool.SetGasPrice(common.Big1) - vm.txPool.SetMinFee(common.Big0) - - // create eth txes - ethTxs := getValidEthTxs(key, 3, common.Big1) - - var wg sync.WaitGroup - wg.Add(2) - sender.CantSendAppGossip = false - signal1 := make(chan struct{}) - seen := 0 - sender.SendAppGossipF = func(_ context.Context, gossipedBytes []byte) error { - if seen == 0 { - notifyMsgIntf, err := message.ParseGossipMessage(vm.networkCodec, gossipedBytes) - assert.NoError(err) - - requestMsg, ok := notifyMsgIntf.(message.EthTxsGossip) - assert.True(ok) - assert.NotEmpty(requestMsg.Txs) - - txs := make([]*types.Transaction, 0) - assert.NoError(rlp.DecodeBytes(requestMsg.Txs, &txs)) - assert.Len(txs, 2) - assert.ElementsMatch( - []common.Hash{ethTxs[0].Hash(), ethTxs[1].Hash()}, - []common.Hash{txs[0].Hash(), txs[1].Hash()}, - ) - seen++ - close(signal1) - } else if seen == 1 { - notifyMsgIntf, err := message.ParseGossipMessage(vm.networkCodec, gossipedBytes) - assert.NoError(err) - - requestMsg, ok := notifyMsgIntf.(message.EthTxsGossip) - assert.True(ok) - assert.NotEmpty(requestMsg.Txs) - - txs := make([]*types.Transaction, 0) - assert.NoError(rlp.DecodeBytes(requestMsg.Txs, &txs)) - assert.Len(txs, 1) - assert.Equal(ethTxs[2].Hash(), txs[0].Hash()) - - seen++ - } else { - t.Fatal("should not be seen 3 times") - } - wg.Done() - return nil - } - - // Notify VM about eth txs - errs := vm.txPool.AddRemotesSync(ethTxs[:2]) - for _, err := range errs { - assert.NoError(err, "failed adding subnet-evm tx to mempool") - } - - // Gossip txs again (shouldn't gossip hashes) - <-signal1 // wait until reorg processed - assert.NoError(vm.gossiper.GossipEthTxs(ethTxs[:2])) - - errs = vm.txPool.AddRemotesSync(ethTxs) - assert.Contains(errs[0].Error(), "already known") - assert.Contains(errs[1].Error(), "already known") - assert.NoError(errs[2], "failed adding subnet-evm tx to mempool") - - attemptAwait(t, &wg, 5*time.Second) -} - -// show that locally issued eth txs are chunked correctly -func TestMempoolEthTxsAddedTxsGossipedAfterActivationChunking(t *testing.T) { - if os.Getenv("RUN_FLAKY_TESTS") != "true" { - t.Skip("FLAKY") - } - assert := assert.New(t) - - key, err := crypto.GenerateKey() - assert.NoError(err) - - addr := crypto.PubkeyToAddress(key.PublicKey) - - genesisJSON, err := fundAddressByGenesis([]common.Address{addr}) - assert.NoError(err) - - _, vm, _, sender := GenesisVM(t, true, genesisJSON, "", "") - defer func() { - err := vm.Shutdown(context.Background()) - assert.NoError(err) - }() - vm.txPool.SetGasPrice(common.Big1) - vm.txPool.SetMinFee(common.Big0) - - // create eth txes - ethTxs := getValidEthTxs(key, 100, common.Big1) - - var wg sync.WaitGroup - wg.Add(2) - sender.CantSendAppGossip = false - seen := map[common.Hash]struct{}{} - sender.SendAppGossipF = func(_ context.Context, gossipedBytes []byte) error { - notifyMsgIntf, err := message.ParseGossipMessage(vm.networkCodec, gossipedBytes) - assert.NoError(err) - - requestMsg, ok := notifyMsgIntf.(message.EthTxsGossip) - assert.True(ok) - assert.NotEmpty(requestMsg.Txs) - - txs := make([]*types.Transaction, 0) - assert.NoError(rlp.DecodeBytes(requestMsg.Txs, &txs)) - for _, tx := range txs { - seen[tx.Hash()] = struct{}{} - } - wg.Done() - return nil - } - - // Notify VM about eth txs - errs := vm.txPool.AddRemotesSync(ethTxs) - for _, err := range errs { - assert.NoError(err, "failed adding subnet-evm tx to mempool") - } - - attemptAwait(t, &wg, 5*time.Second) - - for _, tx := range ethTxs { - _, ok := seen[tx.Hash()] - assert.True(ok, "missing hash: %v", tx.Hash()) - } -} - // show that a geth tx discovered from gossip is requested to the same node that // gossiped it func TestMempoolEthTxsAppGossipHandling(t *testing.T) { @@ -258,7 +105,7 @@ func TestMempoolEthTxsAppGossipHandling(t *testing.T) { return nil } wg.Add(1) - sender.SendAppGossipF = func(context.Context, []byte) error { + sender.SendAppGossipF = func(context.Context, []byte, int, int, int) error { wg.Done() return nil } @@ -284,147 +131,6 @@ func TestMempoolEthTxsAppGossipHandling(t *testing.T) { attemptAwait(t, &wg, 5*time.Second) } -func TestMempoolEthTxsRegossipSingleAccount(t *testing.T) { - assert := assert.New(t) - - key, err := crypto.GenerateKey() - assert.NoError(err) - - addr := crypto.PubkeyToAddress(key.PublicKey) - - genesisJSON, err := fundAddressByGenesis([]common.Address{addr}) - assert.NoError(err) - - _, vm, _, _ := GenesisVM(t, true, genesisJSON, `{"local-txs-enabled":true}`, "") - defer func() { - err := vm.Shutdown(context.Background()) - assert.NoError(err) - }() - vm.txPool.SetGasPrice(common.Big1) - vm.txPool.SetMinFee(common.Big0) - - // create eth txes - ethTxs := getValidEthTxs(key, 10, big.NewInt(226*params.GWei)) - - // Notify VM about eth txs - errs := vm.txPool.AddRemotesSync(ethTxs) - for _, err := range errs { - assert.NoError(err, "failed adding subnet-evm tx to remote mempool") - } - - // Only 1 transaction will be regossiped for an address (should be lowest - // nonce) - pushNetwork := vm.gossiper.(*pushGossiper) - queued := pushNetwork.queueRegossipTxs() - assert.Len(queued, 1, "unexpected length of queued txs") - assert.Equal(ethTxs[0].Hash(), queued[0].Hash()) -} - -func TestMempoolEthTxsRegossip(t *testing.T) { - assert := assert.New(t) - - keys := make([]*ecdsa.PrivateKey, 20) - addrs := make([]common.Address, 20) - for i := 0; i < 20; i++ { - key, err := crypto.GenerateKey() - assert.NoError(err) - keys[i] = key - addrs[i] = crypto.PubkeyToAddress(key.PublicKey) - } - - genesisJSON, err := fundAddressByGenesis(addrs) - assert.NoError(err) - - _, vm, _, _ := GenesisVM(t, true, genesisJSON, `{"local-txs-enabled":true}`, "") - defer func() { - err := vm.Shutdown(context.Background()) - assert.NoError(err) - }() - vm.txPool.SetGasPrice(common.Big1) - vm.txPool.SetMinFee(common.Big0) - - // create eth txes - ethTxs := make([]*types.Transaction, 20) - ethTxHashes := make([]common.Hash, 20) - for i := 0; i < 20; i++ { - txs := getValidEthTxs(keys[i], 1, big.NewInt(226*params.GWei)) - tx := txs[0] - ethTxs[i] = tx - ethTxHashes[i] = tx.Hash() - } - - // Notify VM about eth txs - errs := vm.txPool.AddRemotesSync(ethTxs[:10]) - for _, err := range errs { - assert.NoError(err, "failed adding subnet-evm tx to remote mempool") - } - errs = vm.txPool.AddLocals(ethTxs[10:]) - for _, err := range errs { - assert.NoError(err, "failed adding subnet-evm tx to local mempool") - } - - // We expect 16 transactions (the default max number of transactions to - // regossip) comprised of 10 local txs and 5 remote txs (we prioritize local - // txs over remote). - pushNetwork := vm.gossiper.(*pushGossiper) - queued := pushNetwork.queueRegossipTxs() - assert.Len(queued, 16, "unexpected length of queued txs") - - // Confirm queued transactions (should be ordered based on - // timestamp submitted, with local priorized over remote) - queuedTxHashes := make([]common.Hash, 16) - for i, tx := range queued { - queuedTxHashes[i] = tx.Hash() - } - assert.ElementsMatch(queuedTxHashes[:10], ethTxHashes[10:], "missing local transactions") - - // NOTE: We don't care which remote transactions are included in this test - // (due to the non-deterministic way pending transactions are surfaced, this can be difficult - // to assert as well). -} - -func TestMempoolTxsPriorityRegossip(t *testing.T) { - assert := assert.New(t) - - key, err := crypto.GenerateKey() - assert.NoError(err) - addr := crypto.PubkeyToAddress(key.PublicKey) - - key2, err := crypto.GenerateKey() - assert.NoError(err) - addr2 := crypto.PubkeyToAddress(key2.PublicKey) - - cfgJson, err := fundAddressByGenesis([]common.Address{addr, addr2}) - assert.NoError(err) - - cfg := fmt.Sprintf(`{"local-txs-enabled":true,"priority-regossip-addresses":["%s"]}`, addr) - _, vm, _, _ := GenesisVM(t, true, cfgJson, cfg, "") - defer func() { - err := vm.Shutdown(context.Background()) - assert.NoError(err) - }() - vm.txPool.SetGasPrice(common.Big1) - vm.txPool.SetMinFee(common.Big0) - - // create eth txes - txs := getValidEthTxs(key, 10, big.NewInt(226*params.GWei)) - txs2 := getValidEthTxs(key2, 10, big.NewInt(226*params.GWei)) - - // Notify VM about eth txs - for _, err := range vm.txPool.AddRemotesSync(txs) { - assert.NoError(err, "failed adding subnet-evm tx to remote mempool") - } - for _, err := range vm.txPool.AddRemotesSync(txs2) { - assert.NoError(err, "failed adding subnet-evm tx 2 to remote mempool") - } - - // 10 transactions will be regossiped for a priority address (others ignored) - pushNetwork := vm.gossiper.(*pushGossiper) - queued := pushNetwork.queuePriorityRegossipTxs() - assert.Len(queued, 10, "unexpected length of queued txs") - assert.ElementsMatch(txs, queued) -} - func attemptAwait(t *testing.T, wg *sync.WaitGroup, delay time.Duration) { ticker := make(chan struct{}) diff --git a/plugin/evm/handler.go b/plugin/evm/handler.go new file mode 100644 index 0000000000..2915d422a2 --- /dev/null +++ b/plugin/evm/handler.go @@ -0,0 +1,76 @@ +// (c) 2019-2021, Ava Labs, Inc. All rights reserved. +// See the file LICENSE for licensing terms. + +package evm + +import ( + "github.com/ava-labs/avalanchego/ids" + + "github.com/ethereum/go-ethereum/log" + "github.com/ethereum/go-ethereum/rlp" + + "github.com/ava-labs/subnet-evm/core/txpool" + "github.com/ava-labs/subnet-evm/core/types" + "github.com/ava-labs/subnet-evm/plugin/evm/message" +) + +// GossipHandler handles incoming gossip messages +type GossipHandler struct { + vm *VM + txPool *txpool.TxPool + stats GossipStats +} + +func NewGossipHandler(vm *VM, stats GossipStats) *GossipHandler { + return &GossipHandler{ + vm: vm, + txPool: vm.txPool, + stats: stats, + } +} + +func (h *GossipHandler) HandleEthTxs(nodeID ids.NodeID, msg message.EthTxsGossip) error { + log.Trace( + "AppGossip called with EthTxsGossip", + "peerID", nodeID, + "size(txs)", len(msg.Txs), + ) + + if len(msg.Txs) == 0 { + log.Trace( + "AppGossip received empty EthTxsGossip Message", + "peerID", nodeID, + ) + return nil + } + + // The maximum size of this encoded object is enforced by the codec. + txs := make([]*types.Transaction, 0) + if err := rlp.DecodeBytes(msg.Txs, &txs); err != nil { + log.Trace( + "AppGossip provided invalid txs", + "peerID", nodeID, + "err", err, + ) + return nil + } + h.stats.IncEthTxsGossipReceived() + errs := h.txPool.AddRemotes(txs) + for i, err := range errs { + if err != nil { + log.Trace( + "AppGossip failed to add to mempool", + "err", err, + "tx", txs[i].Hash(), + ) + if err == txpool.ErrAlreadyKnown { + h.stats.IncEthTxsGossipReceivedKnown() + } else { + h.stats.IncEthTxsGossipReceivedError() + } + continue + } + h.stats.IncEthTxsGossipReceivedNew() + } + return nil +} diff --git a/plugin/evm/syncervm_test.go b/plugin/evm/syncervm_test.go index 69a15ca49d..8b3e0b6fa9 100644 --- a/plugin/evm/syncervm_test.go +++ b/plugin/evm/syncervm_test.go @@ -113,7 +113,7 @@ func TestStateSyncToggleEnabledToDisabled(t *testing.T) { syncDisabledVM := &VM{} appSender := &commonEng.SenderTest{T: t} - appSender.SendAppGossipF = func(context.Context, []byte) error { return nil } + appSender.SendAppGossipF = func(context.Context, []byte, int, int, int) error { return nil } appSender.SendAppRequestF = func(ctx context.Context, nodeSet set.Set[ids.NodeID], requestID uint32, request []byte) error { nodeID, hasItem := nodeSet.Pop() if !hasItem { diff --git a/plugin/evm/tx_gossip_test.go b/plugin/evm/tx_gossip_test.go index 54257b4bb2..fc9355ed84 100644 --- a/plugin/evm/tx_gossip_test.go +++ b/plugin/evm/tx_gossip_test.go @@ -170,6 +170,10 @@ func TestEthTxPushGossipOutbound(t *testing.T) { )) require.NoError(vm.SetState(ctx, snow.NormalOp)) + defer func() { + require.NoError(vm.Shutdown(ctx)) + }() + address := testEthAddrs[0] key := testKeys[0] tx := types.NewTransaction(0, address, big.NewInt(10), 21000, big.NewInt(testMinGasPrice), nil) @@ -178,6 +182,7 @@ func TestEthTxPushGossipOutbound(t *testing.T) { // issue a tx require.NoError(vm.txPool.AddLocal(signedTx)) + vm.ethTxPushGossiper.Get().Add(&GossipEthTx{signedTx}) sent := <-sender.SentAppGossip got := &sdk.PushGossip{} @@ -200,9 +205,7 @@ func TestEthTxPushGossipInbound(t *testing.T) { ctx := context.Background() snowCtx := utils.TestSnowContext() - sender := &common.FakeSender{ - SentAppGossip: make(chan []byte, 1), - } + sender := &common.SenderTest{} vm := &VM{ p2pSender: sender, ethTxPullGossiper: gossip.NoOpGossiper{}, @@ -221,6 +224,10 @@ func TestEthTxPushGossipInbound(t *testing.T) { )) require.NoError(vm.SetState(ctx, snow.NormalOp)) + defer func() { + require.NoError(vm.Shutdown(ctx)) + }() + address := testEthAddrs[0] key := testKeys[0] tx := types.NewTransaction(0, address, big.NewInt(10), 21000, big.NewInt(testMinGasPrice), nil) @@ -244,15 +251,5 @@ func TestEthTxPushGossipInbound(t *testing.T) { inboundGossipMsg := append(binary.AppendUvarint(nil, ethTxGossipProtocol), inboundGossipBytes...) require.NoError(vm.AppGossip(ctx, ids.EmptyNodeID, inboundGossipMsg)) - forwardedMsg := &sdk.PushGossip{} - outboundGossipBytes := <-sender.SentAppGossip - - require.Equal(byte(ethTxGossipProtocol), outboundGossipBytes[0]) - require.NoError(proto.Unmarshal(outboundGossipBytes[1:], forwardedMsg)) - require.Len(forwardedMsg.Gossip, 1) - - forwardedTx, err := marshaller.UnmarshalGossip(forwardedMsg.Gossip[0]) - require.NoError(err) - require.Equal(gossipedTx.GossipID(), forwardedTx.GossipID()) require.True(vm.txPool.Has(signedTx.Hash())) } diff --git a/plugin/evm/version.go b/plugin/evm/version.go index 99a014faf1..83794966d1 100644 --- a/plugin/evm/version.go +++ b/plugin/evm/version.go @@ -11,7 +11,7 @@ var ( // GitCommit is set by the build script GitCommit string // Version is the version of Subnet EVM - Version string = "v0.6.1" + Version string = "v0.6.2" ) func init() { diff --git a/plugin/evm/vm.go b/plugin/evm/vm.go index e9b1948624..1303e350d5 100644 --- a/plugin/evm/vm.go +++ b/plugin/evm/vm.go @@ -81,6 +81,7 @@ import ( commonEng "github.com/ava-labs/avalanchego/snow/engine/common" + avalancheUtils "github.com/ava-labs/avalanchego/utils" avalancheJSON "github.com/ava-labs/avalanchego/utils/json" ) @@ -109,6 +110,7 @@ const ( ethTxGossipProtocol = 0x0 // gossip constants + pushGossipDiscardedElements = 16_384 txGossipBloomMinTargetElements = 8 * 1024 txGossipBloomTargetFalsePositiveRate = 0.01 txGossipBloomResetFalsePositiveRate = 0.05 @@ -117,8 +119,7 @@ const ( maxValidatorSetStaleness = time.Minute txGossipThrottlingPeriod = 10 * time.Second txGossipThrottlingLimit = 2 - gossipFrequency = 10 * time.Second - txGossipPollSize = 10 + txGossipPollSize = 1 ) // Define the API endpoints for the VM @@ -228,8 +229,6 @@ type VM struct { limitOrderProcesser LimitOrderProcesser - gossiper Gossiper - clock mockable.Clock shutdownChan chan struct{} @@ -262,8 +261,8 @@ type VM struct { // Initialize only sets these if nil so they can be overridden in tests p2pSender commonEng.AppSender ethTxGossipHandler p2p.Handler + ethTxPushGossiper avalancheUtils.Atomic[*gossip.PushGossiper[*GossipEthTx]] ethTxPullGossiper gossip.Gossiper - ethTxPushGossiper gossip.Accumulator[*GossipEthTx] } // Initialize implements the snowman.ChainVM interface @@ -406,14 +405,13 @@ func (vm *VM) Initialize( vm.ethConfig.TxPool.Locals = vm.config.PriorityRegossipAddresses vm.ethConfig.TxPool.NoLocals = !vm.config.LocalTxsEnabled - vm.ethConfig.TxPool.Journal = vm.config.TxPoolJournal - vm.ethConfig.TxPool.Rejournal = vm.config.TxPoolRejournal.Duration vm.ethConfig.TxPool.PriceLimit = vm.config.TxPoolPriceLimit vm.ethConfig.TxPool.PriceBump = vm.config.TxPoolPriceBump vm.ethConfig.TxPool.AccountSlots = vm.config.TxPoolAccountSlots vm.ethConfig.TxPool.GlobalSlots = vm.config.TxPoolGlobalSlots vm.ethConfig.TxPool.AccountQueue = vm.config.TxPoolAccountQueue vm.ethConfig.TxPool.GlobalQueue = vm.config.TxPoolGlobalQueue + vm.ethConfig.TxPool.Lifetime = vm.config.TxPoolLifetime.Duration vm.ethConfig.AllowUnfinalizedQueries = vm.config.AllowUnfinalizedQueries vm.ethConfig.AllowUnprotectedTxs = vm.config.AllowUnprotectedTxs @@ -552,6 +550,7 @@ func (vm *VM) initializeChain(lastAcceptedHash common.Hash, ethConfig ethconfig. vm.eth, err = eth.New( node, &vm.ethConfig, + &EthPushGossiper{vm: vm}, vm.chaindb, vm.config.EthBackendSettings(), lastAcceptedHash, @@ -691,40 +690,55 @@ func (vm *VM) initBlockBuilding() error { vm.cancel = cancel ethTxGossipMarshaller := GossipEthTxMarshaller{} - ethTxGossipClient := vm.Network.NewClient(ethTxGossipProtocol, p2p.WithValidatorSampling(vm.validators)) - ethTxGossipMetrics, err := gossip.NewMetrics(vm.sdkMetrics, ethTxGossipNamespace) if err != nil { return fmt.Errorf("failed to initialize eth tx gossip metrics: %w", err) } + ethTxPool, err := NewGossipEthTxPool(vm.txPool, vm.sdkMetrics) + if err != nil { + return err + } + vm.shutdownWg.Add(1) + go func() { + ethTxPool.Subscribe(ctx) + vm.shutdownWg.Done() + }() + + pushGossipParams := gossip.BranchingFactor{ + Validators: vm.config.PushGossipNumValidators, + Peers: vm.config.PushGossipNumPeers, + } + pushRegossipParams := gossip.BranchingFactor{ + Validators: vm.config.PushRegossipNumValidators, + Peers: vm.config.PushRegossipNumPeers, + } - if vm.ethTxPushGossiper == nil { - vm.ethTxPushGossiper = gossip.NewPushGossiper[*GossipEthTx]( + ethTxPushGossiper := vm.ethTxPushGossiper.Get() + if ethTxPushGossiper == nil { + ethTxPushGossiper, err = gossip.NewPushGossiper[*GossipEthTx]( ethTxGossipMarshaller, + ethTxPool, ethTxGossipClient, ethTxGossipMetrics, + pushGossipParams, + pushRegossipParams, + pushGossipDiscardedElements, txGossipTargetMessageSize, + vm.config.RegossipFrequency.Duration, ) + if err != nil { + return fmt.Errorf("failed to initialize eth tx push gossiper: %w", err) + } + vm.ethTxPushGossiper.Set(ethTxPushGossiper) } // NOTE: gossip network must be initialized first otherwise ETH tx gossip will not work. gossipStats := NewGossipStats() - vm.gossiper = vm.createGossiper(gossipStats, vm.ethTxPushGossiper) vm.builder = vm.NewBlockBuilder(vm.toEngine) vm.builder.awaitSubmittedTxs() vm.Network.SetGossipHandler(NewGossipHandler(vm, gossipStats)) - ethTxPool, err := NewGossipEthTxPool(vm.txPool, vm.sdkMetrics) - if err != nil { - return err - } - vm.shutdownWg.Add(1) - go func() { - ethTxPool.Subscribe(ctx) - vm.shutdownWg.Done() - }() - if vm.ethTxGossipHandler == nil { vm.ethTxGossipHandler = newTxGossipHandler[*GossipEthTx]( vm.ctx.Log, @@ -759,9 +773,13 @@ func (vm *VM) initBlockBuilding() error { } } - vm.shutdownWg.Add(1) + vm.shutdownWg.Add(2) + go func() { + gossip.Every(ctx, vm.ctx.Log, ethTxPushGossiper, vm.config.PushGossipFrequency.Duration) + vm.shutdownWg.Done() + }() go func() { - gossip.Every(ctx, vm.ctx.Log, vm.ethTxPullGossiper, gossipFrequency) + gossip.Every(ctx, vm.ctx.Log, vm.ethTxPullGossiper, vm.config.PullGossipFrequency.Duration) vm.shutdownWg.Done() }() vm.limitOrderProcesser.ListenAndProcessTransactions(vm.builder) diff --git a/plugin/evm/vm_test.go b/plugin/evm/vm_test.go index 2f41efb6a3..64c5034887 100644 --- a/plugin/evm/vm_test.go +++ b/plugin/evm/vm_test.go @@ -208,8 +208,8 @@ func GenesisVM(t *testing.T, ctx, dbManager, genesisBytes, issuer, _ := setupGenesis(t, genesisJSON) appSender := &commonEng.SenderTest{T: t} appSender.CantSendAppGossip = true - appSender.SendAppGossipF = func(context.Context, []byte) error { return nil } createValidatorPrivateKeyIfNotExists() + appSender.SendAppGossipF = func(context.Context, []byte, int, int, int) error { return nil } err := vm.Initialize( context.Background(), ctx, @@ -1996,8 +1996,8 @@ func TestConfigureLogLevel(t *testing.T) { ctx, dbManager, genesisBytes, issuer, _ := setupGenesis(t, test.genesisJSON) appSender := &commonEng.SenderTest{T: t} appSender.CantSendAppGossip = true - appSender.SendAppGossipF = func(context.Context, []byte) error { return nil } createValidatorPrivateKeyIfNotExists() + appSender.SendAppGossipF = func(context.Context, []byte, int, int, int) error { return nil } err := vm.Initialize( context.Background(), ctx, diff --git a/plugin/evm/vm_upgrade_bytes_test.go b/plugin/evm/vm_upgrade_bytes_test.go index c4cb94223e..6f19398525 100644 --- a/plugin/evm/vm_upgrade_bytes_test.go +++ b/plugin/evm/vm_upgrade_bytes_test.go @@ -276,7 +276,7 @@ func TestMandatoryUpgradesEnforced(t *testing.T) { ctx.NetworkID = test.networkID appSender := &commonEng.SenderTest{T: t} appSender.CantSendAppGossip = true - appSender.SendAppGossipF = func(context.Context, []byte) error { return nil } + appSender.SendAppGossipF = func(context.Context, []byte, int, int, int) error { return nil } err := vm.Initialize( context.Background(), ctx, diff --git a/precompile/contracts/warp/README.md b/precompile/contracts/warp/README.md index dd4170690f..10e1daaa38 100644 --- a/precompile/contracts/warp/README.md +++ b/precompile/contracts/warp/README.md @@ -1,18 +1,16 @@ -# Avalanche Warp Messaging +# Integrating Avalanche Warp Messaging into the EVM Avalanche Warp Messaging offers a basic primitive to enable Cross-Subnet communication on the Avalanche Network. It is intended to allow communication between arbitrary Custom Virtual Machines (including, but not limited to Subnet-EVM and Coreth). -## How does Avalanche Warp Messaging Work +## How does Avalanche Warp Messaging Work? Avalanche Warp Messaging uses BLS Multi-Signatures with Public-Key Aggregation where every Avalanche validator registers a public key alongside its NodeID on the Avalanche P-Chain. Every node tracking a Subnet has read access to the Avalanche P-Chain. This provides weighted sets of BLS Public Keys that correspond to the validator sets of each Subnet on the Avalanche Network. Avalanche Warp Messaging provides a basic primitive for signing and verifying messages between Subnets: the receiving network can verify whether an aggregation of signatures from a set of source Subnet validators represents a threshold of stake large enough for the receiving network to process the message. -For more details on Avalanche Warp Messaging, see the AvalancheGo [Warp README](https://github.com/ava-labs/avalanchego/blob/warp-readme/vms/platformvm/warp/README.md). - -## Integrating Avalanche Warp Messaging into the EVM +For more details on Avalanche Warp Messaging, see the AvalancheGo [Warp README](https://docs.avax.network/build/cross-chain/awm/deep-dive). ### Flow of Sending / Receiving a Warp Message within the EVM @@ -46,7 +44,7 @@ Additionally, the `SourceChainID` is excluded because anyone parsing the chain c - `sender` - The `messageID` of the unsigned message (sha256 of the unsigned message) -The actual `message` is the entire [Avalanche Warp Unsigned Message](https://github.com/ava-labs/avalanchego/blob/master/vms/platformvm/warp/unsigned_message.go#L14) including an [AddressedCall](https://github.com/ava-labs/avalanchego/tree/v1.10.15/vms/platformvm/warp/payload). The unsigned message is emitted as the unindexed data in the log. +The actual `message` is the entire [Avalanche Warp Unsigned Message](https://github.com/ava-labs/avalanchego/blob/master/vms/platformvm/warp/unsigned_message.go#L14) including an [AddressedCall](https://github.com/ava-labs/avalanchego/tree/master/vms/platformvm/warp/payload#readme). The unsigned message is emitted as the unindexed data in the log. #### getVerifiedMessage @@ -73,11 +71,11 @@ The `blockchainID` in Avalanche refers to the txID that created the blockchain o ### Predicate Encoding -Avalanche Warp Messages are encoded as a signed Avalanche [Warp Message](https://github.com/ava-labs/avalanchego/blob/v1.10.4/vms/platformvm/warp/message.go#L7) where the [UnsignedMessage](https://github.com/ava-labs/avalanchego/blob/v1.10.4/vms/platformvm/warp/unsigned_message.go#L14)'s payload includes an [AddressedPayload](../../../warp/payload/payload.go). +Avalanche Warp Messages are encoded as a signed Avalanche [Warp Message](https://github.com/ava-labs/avalanchego/blob/master/vms/platformvm/warp/message.go) where the [UnsignedMessage](https://github.com/ava-labs/avalanchego/blob/master/vms/platformvm/warp/unsigned_message.go)'s payload includes an [AddressedPayload](https://github.com/ava-labs/avalanchego/blob/master/vms/platformvm/warp/payload/payload.go). Since the predicate is encoded into the [Transaction Access List](https://eips.ethereum.org/EIPS/eip-2930), it is packed into 32 byte hashes intended to declare storage slots that should be pre-warmed into the cache prior to transaction execution. -Therefore, we use the [Predicate Utils](../../../utils/predicate/README.md) package to encode the actual byte slice of size N into the access list. +Therefore, we use the [Predicate Utils](https://github.com/ava-labs/coreth/blob/master/predicate/Predicate.md) package to encode the actual byte slice of size N into the access list. ### Performance Optimization: C-Chain to Subnet diff --git a/precompile/contracts/warp/config.go b/precompile/contracts/warp/config.go index 5275671e6a..dde04a8695 100644 --- a/precompile/contracts/warp/config.go +++ b/precompile/contracts/warp/config.go @@ -116,7 +116,7 @@ func (c *Config) Accept(acceptCtx *precompileconfig.AcceptContext, blockHash com if err != nil { return fmt.Errorf("failed to parse warp log data into unsigned message (TxHash: %s, LogIndex: %d): %w", txHash, logIndex, err) } - log.Info( + log.Debug( "Accepted warp unsigned message", "blockHash", blockHash, "blockNumber", blockNumber, diff --git a/scripts/run_ginkgo_load.sh b/scripts/run_ginkgo_load.sh index e4f31c31ac..56f3a09c6b 100755 --- a/scripts/run_ginkgo_load.sh +++ b/scripts/run_ginkgo_load.sh @@ -19,8 +19,11 @@ source "$SUBNET_EVM_PATH"/scripts/versions.sh # to install the ginkgo binary (required for test build and run) go install -v github.com/onsi/ginkgo/v2/ginkgo@"${GINKGO_VERSION}" -ACK_GINKGO_RC=true ginkgo build ./tests/load +EXTRA_ARGS=() +AVALANCHEGO_BUILD_PATH="${AVALANCHEGO_BUILD_PATH:-}" +if [[ -n "${AVALANCHEGO_BUILD_PATH}" ]]; then + EXTRA_ARGS=("--avalanchego-path=${AVALANCHEGO_BUILD_PATH}/avalanchego" "--plugin-dir=${AVALANCHEGO_BUILD_PATH}/plugins") + echo "Running with extra args:" "${EXTRA_ARGS[@]}" +fi -./tests/load/load.test \ - --ginkgo.vv \ - --ginkgo.label-filter="${GINKGO_LABEL_FILTER:-""}" +ginkgo -vv --label-filter="${GINKGO_LABEL_FILTER:-}" ./tests/load -- "${EXTRA_ARGS[@]}" diff --git a/scripts/run_ginkgo_warp.sh b/scripts/run_ginkgo_warp.sh index 3f12a4800c..a30295efb8 100755 --- a/scripts/run_ginkgo_warp.sh +++ b/scripts/run_ginkgo_warp.sh @@ -1,5 +1,6 @@ #!/usr/bin/env bash -set -e + +set -euo pipefail # This script assumes that an AvalancheGo and Subnet-EVM binaries are available in the standard location # within the $GOPATH @@ -17,10 +18,13 @@ source "$SUBNET_EVM_PATH"/scripts/versions.sh # Build ginkgo # to install the ginkgo binary (required for test build and run) -go install -v "github.com/onsi/ginkgo/v2/ginkgo@${GINKGO_VERSION}" +go install -v github.com/onsi/ginkgo/v2/ginkgo@"${GINKGO_VERSION}" -ACK_GINKGO_RC=true ginkgo build ./tests/warp +EXTRA_ARGS=() +AVALANCHEGO_BUILD_PATH="${AVALANCHEGO_BUILD_PATH:-}" +if [[ -n "${AVALANCHEGO_BUILD_PATH}" ]]; then + EXTRA_ARGS=("--avalanchego-path=${AVALANCHEGO_BUILD_PATH}/avalanchego" "--plugin-dir=${AVALANCHEGO_BUILD_PATH}/plugins") + echo "Running with extra args:" "${EXTRA_ARGS[@]}" +fi -./tests/warp/warp.test \ - --ginkgo.vv \ - --ginkgo.label-filter="${GINKGO_LABEL_FILTER:-""}" +ginkgo -vv --label-filter="${GINKGO_LABEL_FILTER:-}" ./tests/warp -- "${EXTRA_ARGS[@]}" diff --git a/scripts/versions.sh b/scripts/versions.sh index 573a83dd3a..0754f0451b 100644 --- a/scripts/versions.sh +++ b/scripts/versions.sh @@ -4,8 +4,8 @@ # shellcheck disable=SC2034 # Don't export them as they're used in the context of other calls -AVALANCHE_VERSION=${AVALANCHE_VERSION:-'v1.11.1'} +AVALANCHE_VERSION=${AVALANCHE_VERSION:-'v1.11.2'} GINKGO_VERSION=${GINKGO_VERSION:-'v2.2.0'} # This won't be used, but it's here to make code syncs easier -LATEST_CORETH_VERSION='0.12.11-rc.3' +LATEST_CORETH_VERSION='0.13.1-rc.5' diff --git a/tests/README.md b/tests/README.md new file mode 100644 index 0000000000..a99cac0b6f --- /dev/null +++ b/tests/README.md @@ -0,0 +1,31 @@ +# Developing with tmpnet + +The `load/` and `warp/` paths contain end-to-end (e2e) tests that use +the [tmpnet +fixture](https://github.com/ava-labs/avalanchego/blob/master/tests/fixture/tmpnet/README.md). By +default both test suites use the tmpnet fixture to create a temporary +network that exists for only the duration of their execution. + +It is possible to create a temporary network that can be reused across +test runs to minimize the setup cost involved: + +```bash +# From the root of a clone of avalanchego, build the tmpnetctl cli +$ ./scripts/build_tmpnetctl.sh + +# Start a new temporary network configured with subnet-evm's default plugin path +$ ./build/tmpnetctl start-network \ + --avalanche-path=./build/avalanchego + --plugin-dir=$GOPATH/src/github.com/ava-labs/avalanchego/build/plugins + +# From the root of a clone of subnet-evm, execute the warp test suite against the existing network +$ ginkgo -vv ./tests/warp -- --use-existing-network --network-dir=$HOME/.tmpnet/networks/latest + +# To stop the temporary network when no longer needed, execute the following from the root of the clone of avalanchego +$ ./build/tmpnetctl stop-network --network-dir=$HOME/.tmpnet/networks/latest +``` + +The network started by `tmpnetctl` won't come with subnets configured, +so the test suite will add them to the network the first time it +runs. Subsequent test runs will be able to reuse those subnets without +having to set them up. diff --git a/tests/init.go b/tests/init.go index 69ed834d6e..6d2d5bae9e 100644 --- a/tests/init.go +++ b/tests/init.go @@ -29,7 +29,9 @@ package tests import ( "fmt" "math/big" + "os" "sort" + "strings" "github.com/ava-labs/subnet-evm/params" "github.com/ava-labs/subnet-evm/utils" @@ -215,3 +217,23 @@ type UnsupportedForkError struct { func (e UnsupportedForkError) Error() string { return fmt.Sprintf("unsupported fork %q", e.Name) } + +func GetRepoRootPath(suffix string) string { + // - When executed via a test binary, the working directory will be wherever + // the binary is executed from, but scripts should require execution from + // the repo root. + // + // - When executed via ginkgo (nicer for development + supports + // parallel execution) the working directory will always be the + // target path (e.g. [repo root]./tests/warp) and getting the repo + // root will require stripping the target path suffix. + // + // TODO(marun) Avoid relying on the current working directory to find test + // dependencies by embedding data where possible (e.g. for genesis) and + // explicitly configuring paths for execution. + cwd, err := os.Getwd() + if err != nil { + panic(err) + } + return strings.TrimSuffix(cwd, suffix) +} diff --git a/tests/load/load_test.go b/tests/load/load_test.go index eafe8b69a3..5a2b5c21e5 100644 --- a/tests/load/load_test.go +++ b/tests/load/load_test.go @@ -7,19 +7,43 @@ import ( "fmt" "os" "os/exec" + "path/filepath" "strings" "testing" - "github.com/ava-labs/subnet-evm/tests/utils/runner" - "github.com/ethereum/go-ethereum/log" ginkgo "github.com/onsi/ginkgo/v2" + "github.com/onsi/gomega" + + "github.com/stretchr/testify/require" + + "github.com/ethereum/go-ethereum/log" + + "github.com/ava-labs/avalanchego/config" + "github.com/ava-labs/avalanchego/ids" + "github.com/ava-labs/avalanchego/tests/fixture/e2e" + "github.com/ava-labs/avalanchego/tests/fixture/tmpnet" + "github.com/ava-labs/avalanchego/utils/set" + + "github.com/ava-labs/subnet-evm/tests" + "github.com/ava-labs/subnet-evm/tests/utils" ) -var getSubnet func() *runner.Subnet +const ( + // The load test requires 5 nodes + nodeCount = 5 + + subnetAName = "load-subnet-a" +) + +var ( + flagVars *e2e.FlagVars + repoRootPath = tests.GetRepoRootPath("tests/load") +) func init() { - getSubnet = runner.RegisterFiveNodeSubnetRun() + // Configures flags used to configure tmpnet + flagVars = e2e.RegisterFlags() } func TestE2E(t *testing.T) { @@ -28,25 +52,60 @@ func TestE2E(t *testing.T) { } var _ = ginkgo.Describe("[Load Simulator]", ginkgo.Ordered, func() { + require := require.New(ginkgo.GinkgoT()) + + var env *e2e.TestEnvironment + + ginkgo.BeforeAll(func() { + genesisPath := filepath.Join(repoRootPath, "tests/load/genesis/genesis.json") + + // The load tests are flaky at high levels of evm logging, so leave it at + // the default level instead of raising it to debug (as the warp testing does). + chainConfig := tmpnet.FlagsMap{} + + nodes := utils.NewTmpnetNodes(nodeCount) + + env = e2e.NewTestEnvironment( + flagVars, + utils.NewTmpnetNetwork( + nodes, + tmpnet.FlagsMap{ + // The default tmpnet log level (debug) induces too much overhead for load testing. + config.LogLevelKey: "info", + }, + utils.NewTmpnetSubnet(subnetAName, genesisPath, chainConfig, nodes...), + ), + ) + }) + ginkgo.It("basic subnet load test", ginkgo.Label("load"), func() { - subnetDetails := getSubnet() - blockchainID := subnetDetails.BlockchainID + network := env.GetNetwork() + + subnet := network.GetSubnet(subnetAName) + require.NotNil(subnet) + blockchainID := subnet.Chains[0].ChainID - nodeURIs := subnetDetails.ValidatorURIs + nodeURIs := tmpnet.GetNodeURIs(network.Nodes) + validatorIDs := set.NewSet[ids.NodeID](len(subnet.ValidatorIDs)) + validatorIDs.Add(subnet.ValidatorIDs...) rpcEndpoints := make([]string, 0, len(nodeURIs)) - for _, uri := range nodeURIs { - rpcEndpoints = append(rpcEndpoints, fmt.Sprintf("%s/ext/bc/%s/rpc", uri, blockchainID)) + for _, nodeURI := range nodeURIs { + if !validatorIDs.Contains(nodeURI.NodeID) { + continue + } + rpcEndpoints = append(rpcEndpoints, fmt.Sprintf("%s/ext/bc/%s/rpc", nodeURI.URI, blockchainID)) } commaSeparatedRPCEndpoints := strings.Join(rpcEndpoints, ",") err := os.Setenv("RPC_ENDPOINTS", commaSeparatedRPCEndpoints) - gomega.Expect(err).Should(gomega.BeNil()) + require.NoError(err) log.Info("Running load simulator...", "rpcEndpoints", commaSeparatedRPCEndpoints) cmd := exec.Command("./scripts/run_simulator.sh") + cmd.Dir = repoRootPath log.Info("Running load simulator script", "cmd", cmd.String()) out, err := cmd.CombinedOutput() fmt.Printf("\nCombined output:\n\n%s\n", string(out)) - gomega.Expect(err).Should(gomega.BeNil()) + require.NoError(err) }) }) diff --git a/tests/utils/constants.go b/tests/utils/constants.go index 4b07626d08..cd507eca1b 100644 --- a/tests/utils/constants.go +++ b/tests/utils/constants.go @@ -14,7 +14,3 @@ const ( DefaultLocalNodeURI = "http://127.0.0.1:9650" ) - -var ( - NodeURIs = []string{DefaultLocalNodeURI, "http://127.0.0.1:9652", "http://127.0.0.1:9654", "http://127.0.0.1:9656", "http://127.0.0.1:9658"} -) diff --git a/tests/utils/runner/network_manager.go b/tests/utils/runner/network_manager.go deleted file mode 100644 index 138ce73034..0000000000 --- a/tests/utils/runner/network_manager.go +++ /dev/null @@ -1,399 +0,0 @@ -// Copyright (C) 2023, Ava Labs, Inc. All rights reserved. -// See the file LICENSE for licensing terms. - -package runner - -import ( - "context" - "fmt" - "os" - "time" - - runner_sdk "github.com/ava-labs/avalanche-network-runner/client" - "github.com/ava-labs/avalanche-network-runner/rpcpb" - runner_server "github.com/ava-labs/avalanche-network-runner/server" - "github.com/ava-labs/avalanchego/ids" - "github.com/ava-labs/avalanchego/utils/logging" - "github.com/ava-labs/avalanchego/utils/wrappers" - "github.com/ava-labs/subnet-evm/plugin/evm" - "github.com/ethereum/go-ethereum/log" - "github.com/onsi/ginkgo/v2" - "github.com/onsi/gomega" -) - -// Subnet provides the basic details of a created subnet -// Note: currently assumes one blockchain per subnet -type Subnet struct { - // SubnetID is the txID of the transaction that created the subnet - SubnetID ids.ID `json:"subnetID"` - // Current ANR assumes one blockchain per subnet, so we have a single blockchainID here - BlockchainID ids.ID `json:"blockchainID"` - // ValidatorURIs is the base URIs for each participant of the Subnet - ValidatorURIs []string `json:"validatorURIs"` -} - -type ANRConfig struct { - LogLevel string - AvalancheGoExecPath string - PluginDir string - GlobalNodeConfig string - GlobalCChainConfig string -} - -// NetworkManager is a wrapper around the ANR to simplify the setup and teardown code -// of tests that rely on the ANR. -type NetworkManager struct { - ANRConfig ANRConfig - - subnets []*Subnet - - logFactory logging.Factory - anrClient runner_sdk.Client - anrServer runner_server.Server - done chan struct{} - serverCtxCancel context.CancelFunc -} - -// NewDefaultANRConfig returns a default config for launching the avalanche-network-runner manager -// with both a server and client. -// By default, it expands $GOPATH/src/github.com/ava-labs/avalanchego/build/ directory to extract -// the AvalancheGoExecPath and PluginDir arguments. -// If the AVALANCHEGO_BUILD_PATH environment variable is set, it overrides the default location for -// the AvalancheGoExecPath and PluginDir arguments. -func NewDefaultANRConfig() ANRConfig { - defaultConfig := ANRConfig{ - LogLevel: "info", - AvalancheGoExecPath: os.ExpandEnv("$GOPATH/src/github.com/ava-labs/avalanchego/build/avalanchego"), - PluginDir: os.ExpandEnv("$GOPATH/src/github.com/ava-labs/avalanchego/build/plugins"), - GlobalNodeConfig: `{ - "log-level":"info", - "proposervm-use-current-height":true - }`, - GlobalCChainConfig: `{ - "warp-api-enabled": true, - "log-level": "debug" - }`, - } - // If AVALANCHEGO_BUILD_PATH is populated, override location set by GOPATH - if envBuildPath, exists := os.LookupEnv("AVALANCHEGO_BUILD_PATH"); exists { - defaultConfig.AvalancheGoExecPath = fmt.Sprintf("%s/avalanchego", envBuildPath) - defaultConfig.PluginDir = fmt.Sprintf("%s/plugins", envBuildPath) - } - return defaultConfig -} - -// NewNetworkManager constructs a new instance of a network manager -func NewNetworkManager(config ANRConfig) *NetworkManager { - manager := &NetworkManager{ - ANRConfig: config, - } - - logLevel, err := logging.ToLevel(config.LogLevel) - if err != nil { - panic(fmt.Errorf("invalid ANR log level: %w", err)) - } - manager.logFactory = logging.NewFactory(logging.Config{ - DisplayLevel: logLevel, - LogLevel: logLevel, - }) - - return manager -} - -// startServer starts a new ANR server and sets/overwrites the anrServer, done channel, and serverCtxCancel function. -func (n *NetworkManager) startServer(ctx context.Context) (<-chan struct{}, error) { - done := make(chan struct{}) - zapServerLog, err := n.logFactory.Make("server") - if err != nil { - return nil, fmt.Errorf("failed to make server log: %w", err) - } - - logLevel, err := logging.ToLevel(n.ANRConfig.LogLevel) - if err != nil { - return nil, fmt.Errorf("failed to parse ANR log level: %w", err) - } - - n.anrServer, err = runner_server.New( - runner_server.Config{ - Port: ":12352", - GwPort: ":12353", - GwDisabled: false, - DialTimeout: 10 * time.Second, - RedirectNodesOutput: true, - SnapshotsDir: "", - LogLevel: logLevel, - }, - zapServerLog, - ) - if err != nil { - return nil, fmt.Errorf("failed to start ANR server: %w", err) - } - n.done = done - - // Use a separate background context here, since the server should only be canceled by explicit shutdown - serverCtx, serverCtxCancel := context.WithCancel(context.Background()) - n.serverCtxCancel = serverCtxCancel - go func() { - if err := n.anrServer.Run(serverCtx); err != nil { - log.Error("Error shutting down ANR server", "err", err) - } else { - log.Info("Terminating ANR Server") - } - close(done) - }() - - return done, nil -} - -// startClient starts an ANR Client dialing the ANR server at the expected endpoint. -// Note: will overwrite client if it already exists. -func (n *NetworkManager) startClient() error { - logLevel, err := logging.ToLevel(n.ANRConfig.LogLevel) - if err != nil { - return fmt.Errorf("failed to parse ANR log level: %w", err) - } - logFactory := logging.NewFactory(logging.Config{ - DisplayLevel: logLevel, - LogLevel: logLevel, - }) - zapLog, err := logFactory.Make("main") - if err != nil { - return fmt.Errorf("failed to make client log: %w", err) - } - - n.anrClient, err = runner_sdk.New(runner_sdk.Config{ - Endpoint: "0.0.0.0:12352", - DialTimeout: 10 * time.Second, - }, zapLog) - if err != nil { - return fmt.Errorf("failed to start ANR client: %w", err) - } - - return nil -} - -// initServer starts the ANR server if it is not populated -func (n *NetworkManager) initServer() error { - if n.anrServer != nil { - return nil - } - - _, err := n.startServer(context.Background()) - return err -} - -// initClient starts an ANR client if it not populated -func (n *NetworkManager) initClient() error { - if n.anrClient != nil { - return nil - } - - return n.startClient() -} - -// init starts the ANR server and client if they are not yet populated -func (n *NetworkManager) init() error { - if err := n.initServer(); err != nil { - return err - } - return n.initClient() -} - -// StartDefaultNetwork constructs a default 5 node network. -func (n *NetworkManager) StartDefaultNetwork(ctx context.Context) (<-chan struct{}, error) { - if err := n.init(); err != nil { - return nil, err - } - - log.Info("Sending 'start'", "AvalancheGoExecPath", n.ANRConfig.AvalancheGoExecPath) - - // Start cluster - opts := []runner_sdk.OpOption{ - runner_sdk.WithPluginDir(n.ANRConfig.PluginDir), - runner_sdk.WithGlobalNodeConfig(n.ANRConfig.GlobalNodeConfig), - } - if len(n.ANRConfig.GlobalCChainConfig) != 0 { - opts = append(opts, runner_sdk.WithChainConfigs(map[string]string{ - "C": n.ANRConfig.GlobalCChainConfig, - })) - } - resp, err := n.anrClient.Start( - ctx, - n.ANRConfig.AvalancheGoExecPath, - opts..., - ) - if err != nil { - return nil, fmt.Errorf("failed to start ANR network: %w", err) - } - log.Info("successfully started cluster", "RootDataDir", resp.ClusterInfo.RootDataDir, "Subnets", resp.GetClusterInfo().GetSubnets()) - return n.done, nil -} - -// SetupNetwork constructs blockchains with the given [blockchainSpecs] and adds them to the network manager. -// Uses [execPath] as the AvalancheGo binary execution path for any started nodes. -// Note: this assumes that the default network has already been constructed. -func (n *NetworkManager) SetupNetwork(ctx context.Context, execPath string, blockchainSpecs []*rpcpb.BlockchainSpec) error { - // timeout according to how many blockchains we're creating - timeout := 2 * time.Minute * time.Duration(len(blockchainSpecs)) - cctx, cancel := context.WithTimeout(ctx, timeout) - defer cancel() - if err := n.init(); err != nil { - return err - } - sresp, err := n.anrClient.CreateBlockchains( - ctx, - blockchainSpecs, - ) - if err != nil { - return fmt.Errorf("failed to create blockchains: %w", err) - } - - // TODO: network runner health should imply custom VM healthiness - // or provide a separate API for custom VM healthiness - // "start" is async, so wait some time for cluster health - log.Info("waiting for all VMs to report healthy", "VMID", evm.ID) - for { - v, err := n.anrClient.Health(ctx) - log.Info("Pinged CLI Health", "result", v, "err", err) - if err != nil { - time.Sleep(1 * time.Second) - continue - } else if ctx.Err() != nil { - return fmt.Errorf("failed to await healthy network: %w", ctx.Err()) - } - break - } - - status, err := n.anrClient.Status(cctx) - if err != nil { - return fmt.Errorf("failed to get ANR status: %w", err) - } - nodeInfos := status.GetClusterInfo().GetNodeInfos() - - for i, chainSpec := range blockchainSpecs { - blockchainIDStr := sresp.ChainIds[i] - blockchainID, err := ids.FromString(blockchainIDStr) - if err != nil { - panic(err) - } - subnetIDStr := sresp.ClusterInfo.CustomChains[blockchainIDStr].SubnetId - subnetID, err := ids.FromString(subnetIDStr) - if err != nil { - panic(err) - } - subnet := &Subnet{ - SubnetID: subnetID, - BlockchainID: blockchainID, - } - for _, nodeName := range chainSpec.SubnetSpec.Participants { - subnet.ValidatorURIs = append(subnet.ValidatorURIs, nodeInfos[nodeName].Uri) - } - n.subnets = append(n.subnets, subnet) - } - - return nil -} - -// TeardownNetwork tears down the network constructed by the network manager and cleans up -// everything associated with it. -func (n *NetworkManager) TeardownNetwork() error { - if err := n.initClient(); err != nil { - return err - } - errs := wrappers.Errs{} - log.Info("Shutting down cluster") - ctx, cancel := context.WithTimeout(context.Background(), 2*time.Minute) - _, err := n.anrClient.Stop(ctx) - cancel() - errs.Add(err) - errs.Add(n.anrClient.Close()) - if n.serverCtxCancel != nil { - n.serverCtxCancel() - } - return errs.Err -} - -// CloseClient closes the connection between the ANR client and server without terminating the -// running network. -func (n *NetworkManager) CloseClient() error { - if n.anrClient == nil { - return nil - } - err := n.anrClient.Close() - n.anrClient = nil - return err -} - -// GetSubnets returns the IDs of the currently running subnets -func (n *NetworkManager) GetSubnets() []ids.ID { - subnetIDs := make([]ids.ID, 0, len(n.subnets)) - for _, subnet := range n.subnets { - subnetIDs = append(subnetIDs, subnet.SubnetID) - } - return subnetIDs -} - -// GetSubnet retrieves the subnet details for the requested subnetID -func (n *NetworkManager) GetSubnet(subnetID ids.ID) (*Subnet, bool) { - for _, subnet := range n.subnets { - if subnet.SubnetID == subnetID { - return subnet, true - } - } - return nil, false -} - -func (n *NetworkManager) GetAllURIs(ctx context.Context) ([]string, error) { - return n.anrClient.URIs(ctx) -} - -func RegisterFiveNodeSubnetRun() func() *Subnet { - var ( - config = NewDefaultANRConfig() - manager = NewNetworkManager(config) - numNodes = 5 - ) - - _ = ginkgo.BeforeSuite(func() { - // Name 10 new validators (which should have BLS key registered) - subnetA := make([]string, 0) - for i := 1; i <= numNodes; i++ { - subnetA = append(subnetA, fmt.Sprintf("node%d-bls", i)) - } - - ctx := context.Background() - var err error - _, err = manager.StartDefaultNetwork(ctx) - gomega.Expect(err).Should(gomega.BeNil()) - err = manager.SetupNetwork( - ctx, - config.AvalancheGoExecPath, - []*rpcpb.BlockchainSpec{ - { - VmName: evm.IDStr, - Genesis: "./tests/load/genesis/genesis.json", - ChainConfig: "", - SubnetSpec: &rpcpb.SubnetSpec{ - Participants: subnetA, - }, - }, - }, - ) - gomega.Expect(err).Should(gomega.BeNil()) - }) - - _ = ginkgo.AfterSuite(func() { - gomega.Expect(manager).ShouldNot(gomega.BeNil()) - gomega.Expect(manager.TeardownNetwork()).Should(gomega.BeNil()) - // TODO: bootstrap an additional node to ensure that we can bootstrap the test data correctly - }) - - return func() *Subnet { - subnetIDs := manager.GetSubnets() - gomega.Expect(len(subnetIDs)).Should(gomega.Equal(1)) - subnetID := subnetIDs[0] - subnetDetails, ok := manager.GetSubnet(subnetID) - gomega.Expect(ok).Should(gomega.BeTrue()) - return subnetDetails - } -} diff --git a/tests/utils/tmpnet.go b/tests/utils/tmpnet.go new file mode 100644 index 0000000000..af1e24908a --- /dev/null +++ b/tests/utils/tmpnet.go @@ -0,0 +1,76 @@ +// Copyright (C) 2023, Ava Labs, Inc. All rights reserved. +// See the file LICENSE for licensing terms. + +package utils + +import ( + "encoding/json" + "os" + + "github.com/ava-labs/avalanchego/config" + "github.com/ava-labs/avalanchego/ids" + "github.com/ava-labs/avalanchego/tests/fixture/tmpnet" + + "github.com/ava-labs/subnet-evm/plugin/evm" +) + +func NewTmpnetNodes(count int) []*tmpnet.Node { + nodes := make([]*tmpnet.Node, count) + for i := range nodes { + node := tmpnet.NewNode("") + node.EnsureKeys() + nodes[i] = node + } + return nodes +} + +func NewTmpnetNetwork(nodes []*tmpnet.Node, flags tmpnet.FlagsMap, subnets ...*tmpnet.Subnet) *tmpnet.Network { + defaultFlags := tmpnet.FlagsMap{} + defaultFlags.SetDefaults(flags) + defaultFlags.SetDefaults(tmpnet.FlagsMap{ + // Remove when vendored tmpnet default is `off`. tmpnet nodes are run headless so stdout logging is unnecessary. + config.LogDisplayLevelKey: "off", + config.ProposerVMUseCurrentHeightKey: true, + }) + return &tmpnet.Network{ + DefaultFlags: defaultFlags, + Nodes: nodes, + Subnets: subnets, + } +} + +// Create the configuration that will enable creation and access to a +// subnet created on a temporary network. +func NewTmpnetSubnet(name string, genesisPath string, chainConfig tmpnet.FlagsMap, nodes ...*tmpnet.Node) *tmpnet.Subnet { + if len(nodes) == 0 { + panic("a subnet must be validated by at least one node") + } + + validatorIDs := make([]ids.NodeID, len(nodes)) + for i, node := range nodes { + validatorIDs[i] = node.NodeID + } + + genesisBytes, err := os.ReadFile(genesisPath) + if err != nil { + panic(err) + } + + chainConfigBytes, err := json.Marshal(chainConfig) + if err != nil { + panic(err) + } + + return &tmpnet.Subnet{ + Name: name, + Chains: []*tmpnet.Chain{ + { + VMID: evm.ID, + Genesis: genesisBytes, + Config: string(chainConfigBytes), + PreFundedKey: tmpnet.HardhatKey, + }, + }, + ValidatorIDs: validatorIDs, + } +} diff --git a/tests/warp/warp_test.go b/tests/warp/warp_test.go index a783dab59d..3475aecbc8 100644 --- a/tests/warp/warp_test.go +++ b/tests/warp/warp_test.go @@ -11,18 +11,31 @@ import ( "fmt" "math/big" "os" + "path/filepath" "strings" "testing" "time" - "github.com/ava-labs/avalanche-network-runner/rpcpb" + ginkgo "github.com/onsi/ginkgo/v2" + + "github.com/onsi/gomega" + + "github.com/stretchr/testify/require" + + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/crypto" + "github.com/ethereum/go-ethereum/log" + "github.com/ava-labs/avalanchego/api/info" "github.com/ava-labs/avalanchego/ids" "github.com/ava-labs/avalanchego/snow/validators" + "github.com/ava-labs/avalanchego/tests/fixture/e2e" + "github.com/ava-labs/avalanchego/tests/fixture/tmpnet" "github.com/ava-labs/avalanchego/utils/constants" "github.com/ava-labs/avalanchego/vms/platformvm" avalancheWarp "github.com/ava-labs/avalanchego/vms/platformvm/warp" "github.com/ava-labs/avalanchego/vms/platformvm/warp/payload" + "github.com/ava-labs/subnet-evm/cmd/simulator/key" "github.com/ava-labs/subnet-evm/cmd/simulator/load" "github.com/ava-labs/subnet-evm/cmd/simulator/metrics" @@ -31,168 +44,152 @@ import ( "github.com/ava-labs/subnet-evm/ethclient" "github.com/ava-labs/subnet-evm/interfaces" "github.com/ava-labs/subnet-evm/params" - "github.com/ava-labs/subnet-evm/plugin/evm" "github.com/ava-labs/subnet-evm/precompile/contracts/warp" "github.com/ava-labs/subnet-evm/predicate" + "github.com/ava-labs/subnet-evm/tests" "github.com/ava-labs/subnet-evm/tests/utils" - "github.com/ava-labs/subnet-evm/tests/utils/runner" warpBackend "github.com/ava-labs/subnet-evm/warp" "github.com/ava-labs/subnet-evm/warp/aggregator" - "github.com/ethereum/go-ethereum/common" - "github.com/ethereum/go-ethereum/crypto" - "github.com/ethereum/go-ethereum/log" - ginkgo "github.com/onsi/ginkgo/v2" - "github.com/onsi/gomega" - "github.com/stretchr/testify/require" ) -const fundedKeyStr = "56289e99c94b6912bfc12adc093c9b51124f0dc54ac7a766b2bc5ccf558d8027" // addr: 0x8db97C7cEcE249c2b98bDC0226Cc4C2A57BF52FC - -var ( - config = runner.NewDefaultANRConfig() - manager = runner.NewNetworkManager(config) - warpChainConfigPath string - testPayload = []byte{1, 2, 3} - nodesPerSubnet = 5 - fundedKey *ecdsa.PrivateKey - subnetA, subnetB, cChainSubnetDetails *runner.Subnet - warpTableEntries = []ginkgo.TableEntry{ - ginkgo.Entry("SubnetA -> SubnetB", func() *warpTest { - return newWarpTest(context.Background(), subnetA, fundedKey, subnetB, fundedKey) - }), - ginkgo.Entry("SubnetA -> SubnetA", func() *warpTest { - return newWarpTest(context.Background(), subnetA, fundedKey, subnetA, fundedKey) - }), - ginkgo.Entry("SubnetA -> C-Chain", func() *warpTest { - return newWarpTest(context.Background(), subnetA, fundedKey, cChainSubnetDetails, fundedKey) - }), - ginkgo.Entry("C-Chain -> SubnetA", func() *warpTest { - return newWarpTest(context.Background(), cChainSubnetDetails, fundedKey, subnetA, fundedKey) - }), - ginkgo.Entry("C-Chain -> C-Chain", func() *warpTest { - return newWarpTest(context.Background(), cChainSubnetDetails, fundedKey, cChainSubnetDetails, fundedKey) - }), - } +const ( + subnetAName = "warp-subnet-a" + subnetBName = "warp-subnet-b" ) -var _ = ginkgo.DescribeTable("[Warp]", func(gen func() *warpTest) { - w := gen() - - log.Info("Sending message from A to B") - w.sendMessageFromSendingSubnet() +var ( + flagVars *e2e.FlagVars - log.Info("Aggregating signatures via API") - w.aggregateSignaturesViaAPI() + repoRootPath = tests.GetRepoRootPath("tests/warp") - log.Info("Aggregating signatures via p2p aggregator") - w.aggregateSignatures() + genesisPath = filepath.Join(repoRootPath, "tests/precompile/genesis/warp.json") - log.Info("Delivering addressed call payload to receiving subnet") - w.deliverAddressedCallToReceivingSubnet() + subnetA, subnetB, cChainSubnetDetails *Subnet - log.Info("Delivering block hash payload to receiving subnet") - w.deliverBlockHashPayload() + testPayload = []byte{1, 2, 3} +) - log.Info("Executing HardHat test") - w.executeHardHatTest() +func init() { + // Configures flags used to configure tmpnet (via SynchronizedBeforeSuite) + flagVars = e2e.RegisterFlags() +} - log.Info("Executing warp load test") - w.warpLoad() -}, warpTableEntries) +// Subnet provides the basic details of a created subnet +type Subnet struct { + // SubnetID is the txID of the transaction that created the subnet + SubnetID ids.ID + // For simplicity assume a single blockchain per subnet + BlockchainID ids.ID + // Key funded in the genesis of the blockchain + PreFundedKey *ecdsa.PrivateKey + // ValidatorURIs are the base URIs for each participant of the Subnet + ValidatorURIs []string +} func TestE2E(t *testing.T) { gomega.RegisterFailHandler(ginkgo.Fail) ginkgo.RunSpecs(t, "subnet-evm warp e2e test") } -func toWebsocketURI(uri string, blockchainID string) string { - return fmt.Sprintf("ws://%s/ext/bc/%s/ws", strings.TrimPrefix(uri, "http://"), blockchainID) -} +var _ = ginkgo.SynchronizedBeforeSuite(func() []byte { + // Run only once in the first ginkgo process + + chainConfig := tmpnet.FlagsMap{ + "log-level": "debug", + "warp-api-enabled": true, + } + + nodes := utils.NewTmpnetNodes(tmpnet.DefaultNodeCount) + + env := e2e.NewTestEnvironment( + flagVars, + utils.NewTmpnetNetwork( + nodes, + tmpnet.FlagsMap{}, + utils.NewTmpnetSubnet(subnetAName, genesisPath, chainConfig, nodes...), + utils.NewTmpnetSubnet(subnetBName, genesisPath, chainConfig, nodes...), + ), + ) + + return env.Marshal() +}, func(envBytes []byte) { + // Run in every ginkgo process -// BeforeSuite starts the default network and adds 10 new nodes as validators with BLS keys -// registered on the P-Chain. -// Adds two disjoint sets of 5 of the new validator nodes to validate two new subnets with a -// a single Subnet-EVM blockchain. -var _ = ginkgo.BeforeSuite(func() { - ctx := context.Background() require := require.New(ginkgo.GinkgoT()) - // Name 10 new validators (which should have BLS key registered) - subnetANodeNames := make([]string, 0) - subnetBNodeNames := make([]string, 0) - for i := 0; i < nodesPerSubnet; i++ { - subnetANodeNames = append(subnetANodeNames, fmt.Sprintf("node%d-subnetA-bls", i)) - subnetBNodeNames = append(subnetBNodeNames, fmt.Sprintf("node%d-subnetB-bls", i)) + // Initialize the local test environment from the global state + if len(envBytes) > 0 { + e2e.InitSharedTestEnvironment(envBytes) } - f, err := os.CreateTemp(os.TempDir(), "config.json") - require.NoError(err) - _, err = f.Write([]byte(`{ - "warp-api-enabled": true, - "log-level": "debug" - }`)) - require.NoError(err) - warpChainConfigPath = f.Name() - - // Construct the network using the avalanche-network-runner - _, err = manager.StartDefaultNetwork(ctx) - require.NoError(err) - err = manager.SetupNetwork( - ctx, - config.AvalancheGoExecPath, - []*rpcpb.BlockchainSpec{ - { - VmName: evm.IDStr, - Genesis: "./tests/precompile/genesis/warp.json", - ChainConfig: warpChainConfigPath, - SubnetSpec: &rpcpb.SubnetSpec{ - SubnetConfig: "", - Participants: subnetANodeNames, - }, - }, - { - VmName: evm.IDStr, - Genesis: "./tests/precompile/genesis/warp.json", - ChainConfig: warpChainConfigPath, - SubnetSpec: &rpcpb.SubnetSpec{ - SubnetConfig: "", - Participants: subnetBNodeNames, - }, - }, - }, - ) - require.NoError(err) + network := e2e.Env.GetNetwork() - fundedKey, err = crypto.HexToECDSA(fundedKeyStr) - require.NoError(err) - subnetIDs := manager.GetSubnets() + // By default all nodes are validating all subnets + validatorURIs := make([]string, len(network.Nodes)) + for i, node := range network.Nodes { + validatorURIs[i] = node.URI + } - var ok bool - subnetA, ok = manager.GetSubnet(subnetIDs[0]) - require.True(ok) - subnetB, ok = manager.GetSubnet(subnetIDs[1]) - require.True(ok) + tmpnetSubnetA := network.GetSubnet(subnetAName) + require.NotNil(tmpnetSubnetA) + subnetA = &Subnet{ + SubnetID: tmpnetSubnetA.SubnetID, + BlockchainID: tmpnetSubnetA.Chains[0].ChainID, + PreFundedKey: tmpnetSubnetA.Chains[0].PreFundedKey.ToECDSA(), + ValidatorURIs: validatorURIs, + } - infoClient := info.NewClient(subnetA.ValidatorURIs[0]) - cChainBlockchainID, err := infoClient.GetBlockchainID(ctx, "C") - require.NoError(err) + tmpnetSubnetB := network.GetSubnet(subnetBName) + require.NotNil(tmpnetSubnetB) + subnetB = &Subnet{ + SubnetID: tmpnetSubnetB.SubnetID, + BlockchainID: tmpnetSubnetB.Chains[0].ChainID, + PreFundedKey: tmpnetSubnetB.Chains[0].PreFundedKey.ToECDSA(), + ValidatorURIs: validatorURIs, + } - allURIs, err := manager.GetAllURIs(ctx) + infoClient := info.NewClient(network.Nodes[0].URI) + cChainBlockchainID, err := infoClient.GetBlockchainID(e2e.DefaultContext(), "C") require.NoError(err) - cChainSubnetDetails = &runner.Subnet{ + cChainSubnetDetails = &Subnet{ SubnetID: constants.PrimaryNetworkID, BlockchainID: cChainBlockchainID, - ValidatorURIs: allURIs, + PreFundedKey: tmpnet.HardhatKey.ToECDSA(), + ValidatorURIs: validatorURIs, } }) -var _ = ginkgo.AfterSuite(func() { - require := require.New(ginkgo.GinkgoT()) - require.NotNil(manager) - require.NoError(manager.TeardownNetwork()) - require.NoError(os.Remove(warpChainConfigPath)) - // TODO: bootstrap an additional node (covering all of the subnets) after the test) +var _ = ginkgo.Describe("[Warp]", func() { + testFunc := func(sendingSubnet *Subnet, receivingSubnet *Subnet) { + w := newWarpTest(e2e.DefaultContext(), sendingSubnet, receivingSubnet) + + log.Info("Sending message from A to B") + w.sendMessageFromSendingSubnet() + + log.Info("Aggregating signatures via API") + w.aggregateSignaturesViaAPI() + + log.Info("Aggregating signatures via p2p aggregator") + w.aggregateSignatures() + + log.Info("Delivering addressed call payload to receiving subnet") + w.deliverAddressedCallToReceivingSubnet() + + log.Info("Delivering block hash payload to receiving subnet") + w.deliverBlockHashPayload() + + log.Info("Executing HardHat test") + w.executeHardHatTest() + + log.Info("Executing warp load test") + w.warpLoad() + } + ginkgo.It("SubnetA -> SubnetB", func() { testFunc(subnetA, subnetB) }) + ginkgo.It("SubnetA -> SubnetA", func() { testFunc(subnetA, subnetA) }) + ginkgo.It("SubnetA -> C-Chain", func() { testFunc(subnetA, cChainSubnetDetails) }) + ginkgo.It("C-Chain -> SubnetA", func() { testFunc(cChainSubnetDetails, subnetA) }) + ginkgo.It("C-Chain -> C-Chain", func() { testFunc(cChainSubnetDetails, cChainSubnetDetails) }) }) type warpTest struct { @@ -200,7 +197,7 @@ type warpTest struct { networkID uint32 // sendingSubnet fields set in the constructor - sendingSubnet *runner.Subnet + sendingSubnet *Subnet sendingSubnetURIs []string sendingSubnetClients []ethclient.Client sendingSubnetFundedKey *ecdsa.PrivateKey @@ -209,7 +206,7 @@ type warpTest struct { sendingSubnetSigner types.Signer // receivingSubnet fields set in the constructor - receivingSubnet *runner.Subnet + receivingSubnet *Subnet receivingSubnetURIs []string receivingSubnetClients []ethclient.Client receivingSubnetFundedKey *ecdsa.PrivateKey @@ -227,9 +224,12 @@ type warpTest struct { addressedCallSignedMessage *avalancheWarp.Message } -func newWarpTest(ctx context.Context, sendingSubnet *runner.Subnet, sendingSubnetFundedKey *ecdsa.PrivateKey, receivingSubnet *runner.Subnet, receivingSubnetFundedKey *ecdsa.PrivateKey) *warpTest { +func newWarpTest(ctx context.Context, sendingSubnet *Subnet, receivingSubnet *Subnet) *warpTest { require := require.New(ginkgo.GinkgoT()) + sendingSubnetFundedKey := sendingSubnet.PreFundedKey + receivingSubnetFundedKey := receivingSubnet.PreFundedKey + warpTest := &warpTest{ sendingSubnet: sendingSubnet, sendingSubnetURIs: sendingSubnet.ValidatorURIs, @@ -301,7 +301,7 @@ func (w *warpTest) getBlockHashAndNumberFromTxReceipt(ctx context.Context, clien } func (w *warpTest) sendMessageFromSendingSubnet() { - ctx := context.Background() + ctx := e2e.DefaultContext() require := require.New(ginkgo.GinkgoT()) client := w.sendingSubnetClients[0] @@ -382,7 +382,7 @@ func (w *warpTest) sendMessageFromSendingSubnet() { func (w *warpTest) aggregateSignaturesViaAPI() { require := require.New(ginkgo.GinkgoT()) - ctx := context.Background() + ctx := e2e.DefaultContext() warpAPIs := make(map[ids.NodeID]warpBackend.Client, len(w.sendingSubnetURIs)) for _, uri := range w.sendingSubnetURIs { @@ -441,7 +441,7 @@ func (w *warpTest) aggregateSignaturesViaAPI() { func (w *warpTest) aggregateSignatures() { require := require.New(ginkgo.GinkgoT()) - ctx := context.Background() + ctx := e2e.DefaultContext() // Verify that the signature aggregation matches the results of manually constructing the warp message client, err := warpBackend.NewClient(w.sendingSubnetURIs[0], w.sendingSubnet.BlockchainID.String()) @@ -464,7 +464,7 @@ func (w *warpTest) aggregateSignatures() { func (w *warpTest) deliverAddressedCallToReceivingSubnet() { require := require.New(ginkgo.GinkgoT()) - ctx := context.Background() + ctx := e2e.DefaultContext() client := w.receivingSubnetClients[0] log.Info("Subscribing to new heads") @@ -518,7 +518,7 @@ func (w *warpTest) deliverAddressedCallToReceivingSubnet() { func (w *warpTest) deliverBlockHashPayload() { require := require.New(ginkgo.GinkgoT()) - ctx := context.Background() + ctx := e2e.DefaultContext() client := w.receivingSubnetClients[0] log.Info("Subscribing to new heads") @@ -572,8 +572,7 @@ func (w *warpTest) deliverBlockHashPayload() { func (w *warpTest) executeHardHatTest() { require := require.New(ginkgo.GinkgoT()) - ctx, cancel := context.WithTimeout(context.Background(), time.Minute) - defer cancel() + ctx := e2e.DefaultContext() client := w.sendingSubnetClients[0] log.Info("Subscribing to new heads") @@ -587,13 +586,13 @@ func (w *warpTest) executeHardHatTest() { rpcURI := toRPCURI(w.sendingSubnetURIs[0], w.sendingSubnet.BlockchainID.String()) - os.Setenv("SENDER_ADDRESS", crypto.PubkeyToAddress(fundedKey.PublicKey).Hex()) + os.Setenv("SENDER_ADDRESS", crypto.PubkeyToAddress(w.sendingSubnetFundedKey.PublicKey).Hex()) os.Setenv("SOURCE_CHAIN_ID", "0x"+w.sendingSubnet.BlockchainID.Hex()) os.Setenv("PAYLOAD", "0x"+common.Bytes2Hex(testPayload)) os.Setenv("EXPECTED_UNSIGNED_MESSAGE", "0x"+hex.EncodeToString(w.addressedCallUnsignedMessage.Bytes())) os.Setenv("CHAIN_ID", fmt.Sprintf("%d", chainID.Uint64())) - cmdPath := "./contracts" + cmdPath := filepath.Join(repoRootPath, "contracts") // test path is relative to the cmd path testPath := "./test/warp.ts" utils.RunHardhatTestsCustomURI(ctx, rpcURI, cmdPath, testPath) @@ -601,44 +600,35 @@ func (w *warpTest) executeHardHatTest() { func (w *warpTest) warpLoad() { require := require.New(ginkgo.GinkgoT()) - ctx, cancel := context.WithTimeout(context.Background(), 30*time.Minute) - defer cancel() + ctx := e2e.DefaultContext() var ( - numWorkers = 5 + numWorkers = len(w.sendingSubnetClients) txsPerWorker uint64 = 10 batchSize uint64 = 10 sendingClient = w.sendingSubnetClients[0] ) - keys := make([]*key.Key, 0, numWorkers) - privateKeys := make([]*ecdsa.PrivateKey, 0, numWorkers) - prefundedKey := key.CreateKey(fundedKey) - keys = append(keys, prefundedKey) - for i := 1; i < numWorkers; i++ { - newKey, err := key.Generate() - require.NoError(err) - keys = append(keys, newKey) - privateKeys = append(privateKeys, newKey.PrivKey) - } + chainAKeys, chainAPrivateKeys := generateKeys(w.sendingSubnetFundedKey, numWorkers) + chainBKeys, chainBPrivateKeys := generateKeys(w.receivingSubnetFundedKey, numWorkers) loadMetrics := metrics.NewDefaultMetrics() - log.Info("Distributing funds on sending subnet", "numKeys", len(keys)) - keys, err := load.DistributeFunds(ctx, sendingClient, keys, len(keys), new(big.Int).Mul(big.NewInt(100), big.NewInt(params.Ether)), loadMetrics) + log.Info("Distributing funds on sending subnet", "numKeys", len(chainAKeys)) + chainAKeys, err := load.DistributeFunds(ctx, sendingClient, chainAKeys, len(chainAKeys), new(big.Int).Mul(big.NewInt(100), big.NewInt(params.Ether)), loadMetrics) require.NoError(err) - log.Info("Distributing funds on receiving subnet", "numKeys", len(keys)) - _, err = load.DistributeFunds(ctx, w.receivingSubnetClients[0], keys, len(keys), new(big.Int).Mul(big.NewInt(100), big.NewInt(params.Ether)), loadMetrics) + log.Info("Distributing funds on receiving subnet", "numKeys", len(chainBKeys)) + _, err = load.DistributeFunds(ctx, w.receivingSubnetClients[0], chainBKeys, len(chainBKeys), new(big.Int).Mul(big.NewInt(100), big.NewInt(params.Ether)), loadMetrics) require.NoError(err) log.Info("Creating workers for each subnet...") - chainAWorkers := make([]txs.Worker[*types.Transaction], 0, len(keys)) - for i := range keys { + chainAWorkers := make([]txs.Worker[*types.Transaction], 0, len(chainAKeys)) + for i := range chainAKeys { chainAWorkers = append(chainAWorkers, load.NewTxReceiptWorker(ctx, w.sendingSubnetClients[i])) } - chainBWorkers := make([]txs.Worker[*types.Transaction], 0, len(keys)) - for i := range keys { + chainBWorkers := make([]txs.Worker[*types.Transaction], 0, len(chainBKeys)) + for i := range chainBKeys { chainBWorkers = append(chainBWorkers, load.NewTxReceiptWorker(ctx, w.receivingSubnetClients[i])) } @@ -671,7 +661,7 @@ func (w *warpTest) warpLoad() { Data: data, }) return types.SignTx(tx, w.sendingSubnetSigner, key) - }, w.sendingSubnetClients[0], privateKeys, txsPerWorker, false) + }, w.sendingSubnetClients[0], chainAPrivateKeys, txsPerWorker, false) require.NoError(err) log.Info("Executing warp send loader...") warpSendLoader := load.New(chainAWorkers, warpSendSequences, batchSize, loadMetrics) @@ -720,8 +710,9 @@ func (w *warpTest) warpLoad() { signedWarpMessageBytes, ) return types.SignTx(tx, w.receivingSubnetSigner, key) - }, w.receivingSubnetClients[0], privateKeys, txsPerWorker, true) + }, w.receivingSubnetClients[0], chainBPrivateKeys, txsPerWorker, true) require.NoError(err) + log.Info("Executing warp delivery...") warpDeliverLoader := load.New(chainBWorkers, warpDeliverSequences, batchSize, loadMetrics) require.NoError(warpDeliverLoader.Execute(ctx)) @@ -729,6 +720,26 @@ func (w *warpTest) warpLoad() { log.Info("Completed warp delivery successfully.") } +func generateKeys(preFundedKey *ecdsa.PrivateKey, numWorkers int) ([]*key.Key, []*ecdsa.PrivateKey) { + keys := []*key.Key{ + key.CreateKey(preFundedKey), + } + privateKeys := []*ecdsa.PrivateKey{ + preFundedKey, + } + for i := 1; i < numWorkers; i++ { + newKey, err := key.Generate() + require.NoError(ginkgo.GinkgoT(), err) + keys = append(keys, newKey) + privateKeys = append(privateKeys, newKey.PrivKey) + } + return keys, privateKeys +} + +func toWebsocketURI(uri string, blockchainID string) string { + return fmt.Sprintf("ws://%s/ext/bc/%s/ws", strings.TrimPrefix(uri, "http://"), blockchainID) +} + func toRPCURI(uri string, blockchainID string) string { return fmt.Sprintf("%s/ext/bc/%s/rpc", uri, blockchainID) }