diff --git a/README.md b/README.md index 5f2fc45017..5ff6f58df3 100644 --- a/README.md +++ b/README.md @@ -55,6 +55,7 @@ The Subnet EVM runs in a separate process from the main AvalancheGo process and [v0.5.8] AvalancheGo@v1.10.13-v1.10.14 (Protocol Version: 29) [v0.5.9] AvalancheGo@v1.10.15-v1.10.17 (Protocol Version: 30) [v0.5.10] AvalancheGo@v1.10.15-v1.10.17 (Protocol Version: 30) +[v0.5.11] AvalancheGo@v1.10.18-v1.10.18 (Protocol Version: 31) ``` ## API diff --git a/compatibility.json b/compatibility.json index 23895c4bfe..614720cee6 100644 --- a/compatibility.json +++ b/compatibility.json @@ -1,5 +1,6 @@ { "rpcChainVMProtocolVersion": { + "v0.5.11": 31, "v0.5.10": 30, "v0.5.9": 30, "v0.5.8": 29, diff --git a/go.mod b/go.mod index d04b55474e..dde3fad98d 100644 --- a/go.mod +++ b/go.mod @@ -4,8 +4,8 @@ go 1.20 require ( github.com/VictoriaMetrics/fastcache v1.10.0 - github.com/ava-labs/avalanche-network-runner v1.7.4-0.20231127162258-2f3ceed8ae4b - github.com/ava-labs/avalanchego v1.10.17 + github.com/ava-labs/avalanche-network-runner v1.7.4-rc.0 + github.com/ava-labs/avalanchego v1.10.18 github.com/cespare/cp v0.1.0 github.com/cockroachdb/pebble v0.0.0-20230209160836-829675f94811 github.com/davecgh/go-spew v1.1.1 @@ -29,8 +29,8 @@ require ( github.com/mattn/go-colorable v0.1.13 github.com/mattn/go-isatty v0.0.16 github.com/olekukonko/tablewriter v0.0.5 - github.com/onsi/ginkgo/v2 v2.8.1 - github.com/onsi/gomega v1.26.0 + github.com/onsi/ginkgo/v2 v2.13.1 + github.com/onsi/gomega v1.29.0 github.com/prometheus/client_golang v1.16.0 github.com/prometheus/client_model v0.3.0 github.com/shirou/gopsutil v3.21.11+incompatible @@ -43,11 +43,11 @@ require ( 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/mock v0.2.0 - golang.org/x/crypto v0.14.0 - golang.org/x/sync v0.3.0 - golang.org/x/sys v0.13.0 - golang.org/x/text v0.13.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/text v0.14.0 golang.org/x/time v0.1.0 google.golang.org/protobuf v1.31.0 gopkg.in/natefinch/lumberjack.v2 v2.0.0 @@ -57,7 +57,7 @@ require ( 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.12.9-rc.9 // indirect + github.com/ava-labs/coreth v0.12.10-rc.5 // indirect github.com/beorn7/perks v1.0.1 // indirect github.com/bits-and-blooms/bitset v1.7.0 // indirect github.com/btcsuite/btcd/btcec/v2 v2.3.2 // indirect @@ -68,7 +68,7 @@ require ( github.com/cockroachdb/logtags v0.0.0-20230118201751-21c54148d20b // indirect github.com/cockroachdb/redact v1.1.3 // indirect github.com/consensys/bavard v0.1.13 // indirect - github.com/consensys/gnark-crypto v0.10.0 // indirect + github.com/consensys/gnark-crypto v0.12.1 // indirect github.com/cpuguy83/go-md2man/v2 v2.0.2 // indirect github.com/crate-crypto/go-kzg-4844 v0.2.0 // indirect github.com/decred/dcrd/dcrec/secp256k1/v4 v4.1.0 // indirect @@ -76,18 +76,19 @@ require ( github.com/dlclark/regexp2 v1.7.0 // indirect github.com/ethereum/c-kzg-4844 v0.2.0 // indirect github.com/getsentry/sentry-go v0.18.0 // indirect - github.com/go-logr/logr v1.2.3 // indirect + github.com/go-logr/logr v1.3.0 // 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/gofrs/flock v0.8.1 // indirect github.com/gogo/protobuf v1.3.2 // indirect github.com/golang-jwt/jwt/v4 v4.3.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 - github.com/google/go-cmp v0.5.9 // indirect + github.com/google/go-cmp v0.6.0 // indirect github.com/google/pprof v0.0.0-20230207041349-798e818bf904 // indirect github.com/google/renameio/v2 v2.0.0 // indirect github.com/gorilla/mux v1.8.0 // indirect @@ -125,7 +126,6 @@ require ( github.com/rogpeppe/go-internal v1.10.0 // indirect github.com/rs/cors v1.7.0 // indirect github.com/russross/blackfriday/v2 v2.1.0 // indirect - github.com/spaolacci/murmur3 v1.1.0 // indirect github.com/spf13/afero v1.8.2 // indirect github.com/spf13/jwalterweatherman v1.1.0 // indirect github.com/subosito/gotenv v1.3.0 // indirect @@ -144,10 +144,11 @@ require ( go.opentelemetry.io/proto/otlp v0.19.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-20230626212559-97b1e661b5df // indirect - golang.org/x/mod v0.11.0 // indirect - golang.org/x/net v0.17.0 // indirect - golang.org/x/term v0.13.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/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 diff --git a/go.sum b/go.sum index 5ac902dae5..5bd15d9519 100644 --- a/go.sum +++ b/go.sum @@ -59,12 +59,12 @@ github.com/allegro/bigcache v1.2.1-0.20190218064605-e24eb225f156 h1:eMwmnE/GDgah 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.4-0.20231127162258-2f3ceed8ae4b h1:iH6q+S7dmBOYCXrZx+nNlS1HBp72L2msiVCLs39Ls5A= -github.com/ava-labs/avalanche-network-runner v1.7.4-0.20231127162258-2f3ceed8ae4b/go.mod h1:aeAm8dgJ1xucQKlYoRDMgYjA0UWGwmaICG9wL0WvseU= -github.com/ava-labs/avalanchego v1.10.17 h1:Ri01nU5ukKC38ZCkCh3namaMZtJkSuv1X/vC13uJguc= -github.com/ava-labs/avalanchego v1.10.17/go.mod h1:A6f3877qlq7bePjCU4T0D60bZGecRMCk15pMpJGOb4Q= -github.com/ava-labs/coreth v0.12.9-rc.9 h1:mvYxABdyPByXwwwIxnTBCiNO23dsE1Kfnd5H106lric= -github.com/ava-labs/coreth v0.12.9-rc.9/go.mod h1:yrf2vEah4Fgj6sJ4UpHewo4DLolwdpf2bJuLRT80PGw= +github.com/ava-labs/avalanche-network-runner v1.7.4-rc.0 h1:xNbCMNqenaDr0bb35j27sqwa+C8t8BgRz51vXd6q0QM= +github.com/ava-labs/avalanche-network-runner v1.7.4-rc.0/go.mod h1:B7Ynk/avkCk49CCIWbM4j1UrPlqIi0IHCPAB2MZNvLw= +github.com/ava-labs/avalanchego v1.10.18 h1:ErJ+SJBtN9tVqk3OPRXffpf+MWeQnNZJlBNWQIgAg8A= +github.com/ava-labs/avalanchego v1.10.18/go.mod h1:wqmokVcLXE+9Nfa8wHdGJtjEW35acVf8rkraSZPVBA4= +github.com/ava-labs/coreth v0.12.10-rc.5 h1:FMVvXHssvMQ3Eade7i85Wsx9tuD3kUOFMG8ktHeDTp8= +github.com/ava-labs/coreth v0.12.10-rc.5/go.mod h1:a58HbIBc9jscGc3aL8e7JuG8RfhBBOm63waq1q0YM+U= 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= @@ -133,8 +133,8 @@ github.com/cockroachdb/redact v1.1.3/go.mod h1:BVNblN9mBWFyMyqK1k3AAiSxhvhfK2oOZ github.com/codegangsta/inject v0.0.0-20150114235600-33e0aa1cb7c0/go.mod h1:4Zcjuz89kmFXt9morQgcfYZAYZ5n8WHjt81YYWIwtTM= github.com/consensys/bavard v0.1.13 h1:oLhMLOFGTLdlda/kma4VOJazblc7IM5y5QPd2A/YjhQ= github.com/consensys/bavard v0.1.13/go.mod h1:9ItSMtA/dXMAiL7BG6bqW2m3NdSEObYWoH223nGHukI= -github.com/consensys/gnark-crypto v0.10.0 h1:zRh22SR7o4K35SoNqouS9J/TKHTyU2QWaj5ldehyXtA= -github.com/consensys/gnark-crypto v0.10.0/go.mod h1:Iq/P3HHl0ElSjsg2E1gsMwhAyxnxoKK5nVyZKd+/KhU= +github.com/consensys/gnark-crypto v0.12.1 h1:lHH39WuuFgVHONRl3J0LRBtuYdQTumFSDtJF7HpyG8M= +github.com/consensys/gnark-crypto v0.12.1/go.mod h1:v2Gy7L/4ZRosZ7Ivs+9SfUDr0f5UlG+EM5t7MPHiLuY= github.com/coreos/etcd v3.3.10+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE= github.com/coreos/go-etcd v2.0.0+incompatible/go.mod h1:Jez6KQU2B/sWsbdaef3ED8NzMklzPG4d5KIOhIy30Tk= github.com/coreos/go-semver v0.2.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk= @@ -220,8 +220,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.2.3 h1:2DntVwHkVopvECVRSlL5PSo9eG+cAkDCuckLubN+rq0= -github.com/go-logr/logr v1.2.3/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/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= @@ -234,6 +234,8 @@ github.com/go-sourcemap/sourcemap v2.1.3+incompatible/go.mod h1:F8jJfvm2KbVjc5Nq github.com/go-stack/stack v1.8.1 h1:ntEHSVwIt7PNXNpgPmVfMrNhLtgjlmnZha2kOpuRiDw= github.com/go-stack/stack v1.8.1/go.mod h1:dcoOX6HbPZSZptuspn9bctJ+N/CnF5gGygcUP3XYfe4= github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0/go.mod h1:fyg7847qk6SyHyPtNmDHnmrv/HOrqktSC+C9fM+CJOE= +github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572 h1:tfuBGBXKqDEevZMzYi5KSi8KkcZtzBcTgAUUtapy0OI= +github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572/go.mod h1:9Pwr4B2jHnOSGXyyzV8ROjYa2ojvAY6HCGYYfMoC3Ls= github.com/go-test/deep v1.0.7 h1:/VSMRlnY/JSyqxQUzQLKVMAskpY/NZKFA5j2P+0pP2M= github.com/gobwas/httphead v0.0.0-20180130184737-2c6c146eadee/go.mod h1:L0fX3K22YWvt/FAX9NnzrNzcI4wNYi9Yku4O0LKYflo= github.com/gobwas/pool v0.2.0/go.mod h1:q8bcK0KcYlCgd9e7WYLm9LpyS+YeLd8JVDW6WezmKEw= @@ -300,8 +302,8 @@ github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/ github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38= -github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= +github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= +github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/google/go-querystring v1.0.0/go.mod h1:odCYkC5MyYFN7vkCjXpyrEuKhc/BUO6wN/zVPAxq5ck= github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs= @@ -490,16 +492,16 @@ github.com/onsi/ginkgo v1.16.4/go.mod h1:dX+/inL/fNMqNlz0e9LfyB9TswhZpCVdJM/Z6Vv github.com/onsi/ginkgo v1.16.5 h1:8xi0RTUf59SOSfEtZMvwTvXYMzG4gV23XVHOZiXNtnE= github.com/onsi/ginkgo v1.16.5/go.mod h1:+E8gABHa3K6zRBolWtd+ROzc/U5bkGt0FwiG042wbpU= github.com/onsi/ginkgo/v2 v2.1.3/go.mod h1:vw5CSIxN1JObi/U8gcbwft7ZxR2dgaR70JSE3/PpL4c= -github.com/onsi/ginkgo/v2 v2.8.1 h1:xFTEVwOFa1D/Ty24Ws1npBWkDYEV9BqZrsDxVrVkrrU= -github.com/onsi/ginkgo/v2 v2.8.1/go.mod h1:N1/NbDngAFcSLdyZ+/aYTYGSlq9qMCS/cNKGJjy+csc= +github.com/onsi/ginkgo/v2 v2.13.1 h1:LNGfMbR2OVGBfXjvRZIZ2YCTQdGKtPLvuI1rMCCj3OU= +github.com/onsi/ginkgo/v2 v2.13.1/go.mod h1:XStQ8QcGwLyF4HdfcZB8SFOS/MWCgDuXMSBe6zrvLgM= github.com/onsi/gomega v1.4.1/go.mod h1:C1qb7wdrVGGVU+Z6iS04AVkA3Q65CEZX59MT0QO5uiA= github.com/onsi/gomega v1.4.3/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY= github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY= github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo= github.com/onsi/gomega v1.17.0/go.mod h1:HnhC7FXeEQY45zxNK3PPoIUhzk/80Xly9PcubAlGdZY= github.com/onsi/gomega v1.19.0/go.mod h1:LY+I3pBVzYsTBU1AnDwOSxaYi9WoWiqgwooUqq9yPro= -github.com/onsi/gomega v1.26.0 h1:03cDLK28U6hWvCAns6NeydX3zIm4SF3ci69ulidS32Q= -github.com/onsi/gomega v1.26.0/go.mod h1:r+zV744Re+DiYCIPRlYOTxn0YkOLcAnW8k1xXdMPGhM= +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/opentracing/opentracing-go v1.1.0 h1:pWlfV3Bxv7k65HYwkikxat0+s3pV4bsqf19k25Ur8rU= github.com/opentracing/opentracing-go v1.1.0/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFStlNbqXla1AfSYxPUl2o= github.com/otiai10/copy v1.11.0 h1:OKBD80J/mLBrwnzXqGtFCzprFSGioo30JcmR4APsNwc= @@ -555,8 +557,6 @@ github.com/sirupsen/logrus v1.7.0/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic 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/spaolacci/murmur3 v1.1.0 h1:7c1g84S4BPRrfL5Xrdp6fOJ206sU9y293DDHaoy0bLI= -github.com/spaolacci/murmur3 v1.1.0/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= @@ -582,6 +582,7 @@ github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXf github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= +github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.2/go.mod h1:R6va5+xMeoiuVRoj+gSkQ7d3FALtqAAGI1FQKckRals= @@ -657,8 +658,8 @@ go.opentelemetry.io/proto/otlp v0.19.0 h1:IVN6GR+mhC4s5yfcTbmzHYODqvWAp3ZedA2SJP 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.uber.org/mock v0.2.0 h1:TaP3xedm7JaAgScZO7tlvlKrqT0p7I6OsdGB5YNSMDU= -go.uber.org/mock v0.2.0/go.mod h1:J0y0rp9L3xiff1+ZBfKxlC1fz2+aO16tw0tsDOixfuM= +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= go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y= go.uber.org/zap v1.26.0 h1:sI7k6L95XOKS281NhVKOFCUNIvv9e0w4BF8N3u+tCRo= @@ -678,8 +679,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.14.0 h1:wBqGXzWJW6m1XrIKlAH0Hs1JJ7+9KBwnIO8v66Q9cHc= -golang.org/x/crypto v0.14.0/go.mod h1:MVFd36DqK4CsrnJYDkBA3VC4m2GkXAM0PvzMCn4JQf4= +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/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= @@ -690,8 +691,8 @@ golang.org/x/exp v0.0.0-20191227195350-da58074b4299/go.mod h1:2RIsYlXP63K8oxa1u0 golang.org/x/exp v0.0.0-20200119233911-0405dc783f0a/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= golang.org/x/exp v0.0.0-20200207192155-f17229e696bd/go.mod h1:J/WKrq2StrnmMY6+EHIKF9dgMWnmCNThgcyBT1FY9mM= golang.org/x/exp v0.0.0-20200224162631-6cc2880d07d6/go.mod h1:3jZMyOhIsHpP37uCMkUooju7aAi5cS1Q23tOzKc+0MU= -golang.org/x/exp v0.0.0-20230626212559-97b1e661b5df h1:UA2aFVmmsIlefxMk29Dp2juaUSth8Pyn3Tq5Y5mJGME= -golang.org/x/exp v0.0.0-20230626212559-97b1e661b5df/go.mod h1:FXUEEKJgO7OQYeo8N01OfiKP8RXMtf6e8aTskBGqWdc= +golang.org/x/exp v0.0.0-20231127185646-65229373498e h1:Gvh4YaCaXNs6dKTlfgismwWZKyjVZXwOPfIyUaqU3No= +golang.org/x/exp v0.0.0-20231127185646-65229373498e/go.mod h1:iRJReGqOEeBhDZGkGbynYwcHlctCvnjTYIamk7uXpHI= golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js= golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= @@ -718,8 +719,8 @@ golang.org/x/mod v0.4.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.4.1/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= -golang.org/x/mod v0.11.0 h1:bUO06HqtnRcc/7l71XBe4WcqTZ+3AH1J59zWDDwLKgU= -golang.org/x/mod v0.11.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= +golang.org/x/mod v0.14.0 h1:dGoOF9QVLYng8IHTm7BAyWqCqSheQ5pYWGhzW00YJr0= +golang.org/x/mod v0.14.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= golang.org/x/net v0.0.0-20180719180050-a680a1efc54d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= @@ -765,8 +766,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.17.0 h1:pVaXccu2ozPjCXewfr1S7xza/zcXTity9cCdXQYSjIM= -golang.org/x/net v0.17.0/go.mod h1:NxSsAGuq816PNPmqtQdLE42eU2Fs7NoRIZrHJAlaCOE= +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/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= @@ -789,8 +790,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.3.0 h1:ftCYgMx6zT/asHUrPw8BLLscYtGznsLAnjq5RH9P66E= -golang.org/x/sync v0.3.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y= +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/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= @@ -858,13 +859,13 @@ 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.13.0 h1:Af8nKPmuFypiUBjVoU9V20FiaFXOcuZI21p0ycVYYGE= -golang.org/x/sys v0.13.0/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/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= -golang.org/x/term v0.13.0 h1:bb+I9cTfFazGW51MZqBVmZy7+JEJMouUHTUSKVQLBek= -golang.org/x/term v0.13.0/go.mod h1:LTmsnFJwVN6bCy1rVCoS+qHT1HhALEFxKncY3WNNh4U= +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/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= @@ -875,8 +876,8 @@ golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= golang.org/x/text v0.3.8/go.mod h1:E6s5w1FMmriuDzIBO73fBruAKo1PCIq6d2Q6DHfQ8WQ= -golang.org/x/text v0.13.0 h1:ablQoSUd0tRdKxZewP80B+BaqeKJuVhuRxj/dkrun3k= -golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= +golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ= +golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= @@ -939,6 +940,8 @@ golang.org/x/tools v0.0.0-20210108195828-e2f9c7f1fc8e/go.mod h1:emZCQorbCU4vsT4f golang.org/x/tools v0.1.0/go.mod h1:xkSsbof2nBLbhDlRMhhhyNLN/zl3eTqcnHD5viDpcZ0= golang.org/x/tools v0.1.3/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= +golang.org/x/tools v0.16.0 h1:GO788SKMRunPIBCXiQyo2AaexLstOrVhuAL5YwsckQM= +golang.org/x/tools v0.16.0/go.mod h1:kYVVN6I1mBNoB1OX+noeBjbRk4IUEPa7JJ+TJMEooJ0= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= diff --git a/peer/network.go b/peer/network.go index e10ab73654..3011356093 100644 --- a/peer/network.go +++ b/peer/network.go @@ -78,9 +78,10 @@ type Network interface { // (length of response divided by request time), and with 0 if the response is invalid. TrackBandwidth(nodeID ids.NodeID, bandwidth float64) - // NewAppProtocol reserves a protocol identifier and returns a corresponding - // client to send messages with - NewAppProtocol(protocol uint64, handler p2p.Handler, options ...p2p.ClientOption) (*p2p.Client, error) + // NewClient returns a client to send messages with for the given protocol + NewClient(protocol uint64, options ...p2p.ClientOption) *p2p.Client + // AddHandler registers a server handler for an application protocol + AddHandler(protocol uint64, handler p2p.Handler) error } // network is an implementation of Network that processes message requests for @@ -92,7 +93,7 @@ type network struct { outstandingRequestHandlers map[uint32]message.ResponseHandler // maps avalanchego requestID => message.ResponseHandler activeAppRequests *semaphore.Weighted // controls maximum number of active outbound requests activeCrossChainRequests *semaphore.Weighted // controls maximum number of active outbound cross chain requests - network *p2p.Network + p2pNetwork *p2p.Network appSender common.AppSender // avalanchego AppSender for sending messages codec codec.Manager // Codec used for parsing messages crossChainCodec codec.Manager // Codec used for parsing cross chain messages @@ -123,7 +124,7 @@ func NewNetwork(p2pNetwork *p2p.Network, appSender common.AppSender, codec codec outstandingRequestHandlers: make(map[uint32]message.ResponseHandler), activeAppRequests: semaphore.NewWeighted(maxActiveAppRequests), activeCrossChainRequests: semaphore.NewWeighted(maxActiveCrossChainRequests), - network: p2pNetwork, + p2pNetwork: p2pNetwork, gossipHandler: message.NoopMempoolGossipHandler{}, appRequestHandler: message.NoopRequestHandler{}, crossChainRequestHandler: message.NoopCrossChainRequestHandler{}, @@ -183,7 +184,7 @@ func (n *network) sendAppRequest(ctx context.Context, nodeID ids.NodeID, request return nil } - log.Trace("sending request to peer", "nodeID", nodeID, "requestLen", len(request)) + log.Debug("sending request to peer", "nodeID", nodeID, "requestLen", len(request)) n.peers.TrackPeer(nodeID) requestID := n.nextRequestID() @@ -200,7 +201,7 @@ func (n *network) sendAppRequest(ctx context.Context, nodeID ids.NodeID, request return err } - log.Trace("sent request message to peer", "nodeID", nodeID, "requestID", requestID) + log.Debug("sent request message to peer", "nodeID", nodeID, "requestID", requestID) return nil } @@ -232,7 +233,7 @@ func (n *network) SendCrossChainRequest(ctx context.Context, chainID ids.ID, req return err } - log.Trace("sent request message to chain", "chainID", chainID, "crossChainRequestID", requestID) + log.Debug("sent request message to chain", "chainID", chainID, "crossChainRequestID", requestID) return nil } @@ -244,21 +245,21 @@ func (n *network) CrossChainAppRequest(ctx context.Context, requestingChainID id return nil } - log.Trace("received CrossChainAppRequest from chain", "requestingChainID", requestingChainID, "requestID", requestID, "requestLen", len(request)) + log.Debug("received CrossChainAppRequest from chain", "requestingChainID", requestingChainID, "requestID", requestID, "requestLen", len(request)) var req message.CrossChainRequest if _, err := n.crossChainCodec.Unmarshal(request, &req); err != nil { - log.Trace("failed to unmarshal CrossChainAppRequest", "requestingChainID", requestingChainID, "requestID", requestID, "requestLen", len(request), "err", err) + log.Debug("failed to unmarshal CrossChainAppRequest", "requestingChainID", requestingChainID, "requestID", requestID, "requestLen", len(request), "err", err) return nil } bufferedDeadline, err := calculateTimeUntilDeadline(deadline, n.crossChainStats) if err != nil { - log.Trace("deadline to process CrossChainAppRequest has expired, skipping", "requestingChainID", requestingChainID, "requestID", requestID, "err", err) + log.Debug("deadline to process CrossChainAppRequest has expired, skipping", "requestingChainID", requestingChainID, "requestID", requestID, "err", err) return nil } - log.Trace("processing incoming CrossChainAppRequest", "requestingChainID", requestingChainID, "requestID", requestID, "req", req) + log.Debug("processing incoming CrossChainAppRequest", "requestingChainID", requestingChainID, "requestID", requestID, "req", req) handleCtx, cancel := context.WithDeadline(context.Background(), bufferedDeadline) defer cancel() @@ -280,13 +281,13 @@ func (n *network) CrossChainAppRequest(ctx context.Context, requestingChainID id // - request times out before a response is provided // If [requestID] is not known, this function will emit a log and return a nil error. // If the response handler returns an error it is propagated as a fatal error. -func (n *network) CrossChainAppRequestFailed(ctx context.Context, respondingChainID ids.ID, requestID uint32) error { - log.Trace("received CrossChainAppRequestFailed from chain", "respondingChainID", respondingChainID, "requestID", requestID) +func (n *network) CrossChainAppRequestFailed(ctx context.Context, respondingChainID ids.ID, requestID uint32, _ *common.AppError) error { + log.Debug("received CrossChainAppRequestFailed from chain", "respondingChainID", respondingChainID, "requestID", requestID) handler, exists := n.markRequestFulfilled(requestID) if !exists { // Can happen after the network has been closed. - log.Trace("received CrossChainAppRequestFailed to unknown request", "respondingChainID", respondingChainID, "requestID", requestID) + log.Debug("received CrossChainAppRequestFailed to unknown request", "respondingChainID", respondingChainID, "requestID", requestID) return nil } @@ -301,12 +302,12 @@ func (n *network) CrossChainAppRequestFailed(ctx context.Context, respondingChai // If [requestID] is not known, this function will emit a log and return a nil error. // If the response handler returns an error it is propagated as a fatal error. func (n *network) CrossChainAppResponse(ctx context.Context, respondingChainID ids.ID, requestID uint32, response []byte) error { - log.Trace("received CrossChainAppResponse from responding chain", "respondingChainID", respondingChainID, "requestID", requestID) + log.Debug("received CrossChainAppResponse from responding chain", "respondingChainID", respondingChainID, "requestID", requestID) handler, exists := n.markRequestFulfilled(requestID) if !exists { // Can happen after the network has been closed. - log.Trace("received CrossChainAppResponse to unknown request", "respondingChainID", respondingChainID, "requestID", requestID, "responseLen", len(response)) + log.Debug("received CrossChainAppResponse to unknown request", "respondingChainID", respondingChainID, "requestID", requestID, "responseLen", len(response)) return nil } @@ -326,21 +327,21 @@ func (n *network) AppRequest(ctx context.Context, nodeID ids.NodeID, requestID u return nil } - log.Trace("received AppRequest from node", "nodeID", nodeID, "requestID", requestID, "requestLen", len(request)) + log.Debug("received AppRequest from node", "nodeID", nodeID, "requestID", requestID, "requestLen", len(request)) var req message.Request if _, err := n.codec.Unmarshal(request, &req); err != nil { - log.Trace("forwarding AppRequest to SDK network", "nodeID", nodeID, "requestID", requestID, "requestLen", len(request), "err", err) - return n.network.AppRequest(ctx, nodeID, requestID, deadline, request) + log.Debug("forwarding AppRequest to SDK network", "nodeID", nodeID, "requestID", requestID, "requestLen", len(request), "err", err) + return n.p2pNetwork.AppRequest(ctx, nodeID, requestID, deadline, request) } bufferedDeadline, err := calculateTimeUntilDeadline(deadline, n.appStats) if err != nil { - log.Trace("deadline to process AppRequest has expired, skipping", "nodeID", nodeID, "requestID", requestID, "err", err) + log.Debug("deadline to process AppRequest has expired, skipping", "nodeID", nodeID, "requestID", requestID, "err", err) return nil } - log.Trace("processing incoming request", "nodeID", nodeID, "requestID", requestID, "req", req) + log.Debug("processing incoming request", "nodeID", nodeID, "requestID", requestID, "req", req) // We make a new context here because we don't want to cancel the context // passed into n.AppSender.SendAppResponse below handleCtx, cancel := context.WithDeadline(context.Background(), bufferedDeadline) @@ -362,12 +363,12 @@ func (n *network) AppRequest(ctx context.Context, nodeID ids.NodeID, requestID u // If [requestID] is not known, this function will emit a log and return a nil error. // If the response handler returns an error it is propagated as a fatal error. func (n *network) AppResponse(ctx context.Context, nodeID ids.NodeID, requestID uint32, response []byte) error { - log.Trace("received AppResponse from peer", "nodeID", nodeID, "requestID", requestID) + log.Debug("received AppResponse from peer", "nodeID", nodeID, "requestID", requestID) handler, exists := n.markRequestFulfilled(requestID) if !exists { - log.Trace("forwarding AppResponse to SDK network", "nodeID", nodeID, "requestID", requestID, "responseLen", len(response)) - return n.network.AppResponse(ctx, nodeID, requestID, response) + log.Debug("forwarding AppResponse to SDK network", "nodeID", nodeID, "requestID", requestID, "responseLen", len(response)) + return n.p2pNetwork.AppResponse(ctx, nodeID, requestID, response) } // We must release the slot @@ -382,13 +383,13 @@ func (n *network) AppResponse(ctx context.Context, nodeID ids.NodeID, requestID // - request times out before a response is provided // error returned by this function is expected to be treated as fatal by the engine // returns error only when the response handler returns an error -func (n *network) AppRequestFailed(ctx context.Context, nodeID ids.NodeID, requestID uint32) error { - log.Trace("received AppRequestFailed from peer", "nodeID", nodeID, "requestID", requestID) +func (n *network) AppRequestFailed(ctx context.Context, nodeID ids.NodeID, requestID uint32, appErr *common.AppError) error { + log.Debug("received AppRequestFailed from peer", "nodeID", nodeID, "requestID", requestID) handler, exists := n.markRequestFulfilled(requestID) if !exists { - log.Trace("forwarding AppRequestFailed to SDK network", "nodeID", nodeID, "requestID", requestID) - return n.network.AppRequestFailed(ctx, nodeID, requestID) + log.Debug("forwarding AppRequestFailed to SDK network", "nodeID", nodeID, "requestID", requestID) + return n.p2pNetwork.AppRequestFailed(ctx, nodeID, requestID, appErr) } // We must release the slot @@ -445,22 +446,24 @@ func (n *network) Gossip(gossip []byte) error { return n.appSender.SendAppGossip(context.TODO(), gossip) } -// AppGossip is called by avalanchego -> VM when there is an incoming AppGossip from a peer -// error returned by this function is expected to be treated as fatal by the engine -// returns error if request could not be parsed as message.Request or when the requestHandler returns an error -func (n *network) AppGossip(_ context.Context, nodeID ids.NodeID, gossipBytes []byte) error { +// 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. +func (n *network) AppGossip(ctx context.Context, nodeID ids.NodeID, gossipBytes []byte) error { var gossipMsg message.GossipMessage if _, err := n.codec.Unmarshal(gossipBytes, &gossipMsg); err != nil { - log.Trace("could not parse app gossip", "nodeID", nodeID, "gossipLen", len(gossipBytes), "err", err) - return nil + log.Debug("forwarding AppGossip to SDK network", "nodeID", nodeID, "gossipLen", len(gossipBytes), "err", err) + return n.p2pNetwork.AppGossip(ctx, nodeID, gossipBytes) } - log.Trace("processing AppGossip from node", "nodeID", nodeID, "msg", gossipMsg) + log.Debug("processing AppGossip from node", "nodeID", nodeID, "msg", gossipMsg) return gossipMsg.Handle(n.gossipHandler, nodeID) } // Connected adds the given nodeID to the peer list so that it can receive messages func (n *network) Connected(ctx context.Context, nodeID ids.NodeID, nodeVersion *version.Application) error { + log.Debug("adding new peer", "nodeID", nodeID) + n.lock.Lock() defer n.lock.Unlock() @@ -469,15 +472,17 @@ func (n *network) Connected(ctx context.Context, nodeID ids.NodeID, nodeVersion } if nodeID == n.self { + log.Debug("skipping registering self as peer") return nil } n.peers.Connected(nodeID, nodeVersion) - return n.network.Connected(ctx, nodeID, nodeVersion) + return n.p2pNetwork.Connected(ctx, nodeID, nodeVersion) } // Disconnected removes given [nodeID] from the peer list func (n *network) Disconnected(ctx context.Context, nodeID ids.NodeID) error { + log.Debug("disconnecting peer", "nodeID", nodeID) n.lock.Lock() defer n.lock.Unlock() @@ -486,7 +491,7 @@ func (n *network) Disconnected(ctx context.Context, nodeID ids.NodeID) error { } n.peers.Disconnected(nodeID) - return n.network.Disconnected(ctx, nodeID) + return n.p2pNetwork.Disconnected(ctx, nodeID) } // Shutdown disconnects all peers @@ -539,8 +544,12 @@ func (n *network) TrackBandwidth(nodeID ids.NodeID, bandwidth float64) { n.peers.TrackBandwidth(nodeID, bandwidth) } -func (n *network) NewAppProtocol(protocol uint64, handler p2p.Handler, options ...p2p.ClientOption) (*p2p.Client, error) { - return n.network.NewAppProtocol(protocol, handler, options...) +func (n *network) NewClient(protocol uint64, options ...p2p.ClientOption) *p2p.Client { + return n.p2pNetwork.NewClient(protocol, options...) +} + +func (n *network) AddHandler(protocol uint64, handler p2p.Handler) error { + return n.p2pNetwork.AddHandler(protocol, handler) } // invariant: peer/network must use explicitly even request ids. diff --git a/peer/network_test.go b/peer/network_test.go index 260d54b492..c011c339f9 100644 --- a/peer/network_test.go +++ b/peer/network_test.go @@ -57,7 +57,9 @@ var ( func TestNetworkDoesNotConnectToItself(t *testing.T) { selfNodeID := ids.GenerateTestNodeID() - n := NewNetwork(p2p.NewNetwork(logging.NoLog{}, nil, prometheus.NewRegistry(), ""), nil, nil, nil, selfNodeID, 1, 1) + p2pNetwork, err := p2p.NewNetwork(logging.NoLog{}, nil, prometheus.NewRegistry(), "") + require.NoError(t, err) + n := NewNetwork(p2pNetwork, nil, nil, nil, selfNodeID, 1, 1) assert.NoError(t, n.Connected(context.Background(), selfNodeID, defaultPeerVersion)) assert.EqualValues(t, 0, n.Size()) } @@ -93,7 +95,9 @@ func TestRequestAnyRequestsRoutingAndResponse(t *testing.T) { codecManager := buildCodec(t, HelloRequest{}, HelloResponse{}) crossChainCodecManager := buildCodec(t, ExampleCrossChainRequest{}, ExampleCrossChainResponse{}) - net = NewNetwork(p2p.NewNetwork(logging.NoLog{}, nil, prometheus.NewRegistry(), ""), sender, codecManager, crossChainCodecManager, ids.EmptyNodeID, 16, 16) + p2pNetwork, err := p2p.NewNetwork(logging.NoLog{}, nil, prometheus.NewRegistry(), "") + require.NoError(t, err) + net = NewNetwork(p2pNetwork, sender, codecManager, crossChainCodecManager, ids.EmptyNodeID, 16, 16) net.SetRequestHandler(&HelloGreetingRequestHandler{codec: codecManager}) client := NewNetworkClient(net) nodeID := ids.GenerateTestNodeID() @@ -145,7 +149,9 @@ func TestAppRequestOnCtxCancellation(t *testing.T) { }, } - net := NewNetwork(p2p.NewNetwork(logging.NoLog{}, nil, prometheus.NewRegistry(), ""), sender, codecManager, crossChainCodecManager, ids.EmptyNodeID, 1, 1) + p2pNetwork, err := p2p.NewNetwork(logging.NoLog{}, nil, prometheus.NewRegistry(), "") + require.NoError(t, err) + net := NewNetwork(p2pNetwork, sender, codecManager, crossChainCodecManager, ids.EmptyNodeID, 1, 1) net.SetRequestHandler(&HelloGreetingRequestHandler{codec: codecManager}) requestMessage := HelloRequest{Message: "this is a request"} @@ -197,7 +203,9 @@ func TestRequestRequestsRoutingAndResponse(t *testing.T) { codecManager := buildCodec(t, HelloRequest{}, HelloResponse{}) crossChainCodecManager := buildCodec(t, ExampleCrossChainRequest{}, ExampleCrossChainResponse{}) - net = NewNetwork(p2p.NewNetwork(logging.NoLog{}, nil, prometheus.NewRegistry(), ""), sender, codecManager, crossChainCodecManager, ids.EmptyNodeID, 16, 16) + p2pNetwork, err := p2p.NewNetwork(logging.NoLog{}, nil, prometheus.NewRegistry(), "") + require.NoError(t, err) + net = NewNetwork(p2pNetwork, sender, codecManager, crossChainCodecManager, ids.EmptyNodeID, 16, 16) net.SetRequestHandler(&HelloGreetingRequestHandler{codec: codecManager}) client := NewNetworkClient(net) @@ -251,7 +259,7 @@ func TestRequestRequestsRoutingAndResponse(t *testing.T) { } // ensure empty nodeID is not allowed - _, err := client.SendAppRequest(context.Background(), ids.EmptyNodeID, []byte("hello there")) + _, err = client.SendAppRequest(context.Background(), ids.EmptyNodeID, []byte("hello there")) assert.Error(t, err) assert.Contains(t, err.Error(), "cannot send request to empty nodeID") } @@ -277,7 +285,9 @@ func TestAppRequestOnShutdown(t *testing.T) { codecManager := buildCodec(t, HelloRequest{}, HelloResponse{}) crossChainCodecManager := buildCodec(t, ExampleCrossChainRequest{}, ExampleCrossChainResponse{}) - net = NewNetwork(p2p.NewNetwork(logging.NoLog{}, nil, prometheus.NewRegistry(), ""), sender, codecManager, crossChainCodecManager, ids.EmptyNodeID, 1, 1) + p2pNetwork, err := p2p.NewNetwork(logging.NoLog{}, nil, prometheus.NewRegistry(), "") + require.NoError(t, err) + net = NewNetwork(p2pNetwork, sender, codecManager, crossChainCodecManager, ids.EmptyNodeID, 1, 1) client := NewNetworkClient(net) nodeID := ids.GenerateTestNodeID() require.NoError(t, net.Connected(context.Background(), nodeID, defaultPeerVersion)) @@ -326,7 +336,9 @@ func TestAppRequestAnyOnCtxCancellation(t *testing.T) { }, } - net := NewNetwork(p2p.NewNetwork(logging.NoLog{}, nil, prometheus.NewRegistry(), ""), sender, codecManager, crossChainCodecManager, ids.EmptyNodeID, 1, 1) + p2pNetwork, err := p2p.NewNetwork(logging.NoLog{}, nil, prometheus.NewRegistry(), "") + require.NoError(t, err) + net := NewNetwork(p2pNetwork, sender, codecManager, crossChainCodecManager, ids.EmptyNodeID, 1, 1) net.SetRequestHandler(&HelloGreetingRequestHandler{codec: codecManager}) assert.NoError(t, net.Connected( @@ -379,6 +391,7 @@ func TestRequestMinVersion(t *testing.T) { callNum := uint32(0) nodeID := ids.GenerateTestNodeID() codecManager := buildCodec(t, TestMessage{}) + crossChainCodecManager := buildCodec(t, ExampleCrossChainRequest{}, ExampleCrossChainResponse{}) var net Network sender := testAppSender{ @@ -402,8 +415,9 @@ func TestRequestMinVersion(t *testing.T) { } // passing nil as codec works because the net.AppRequest is never called - crossChainCodecManager := buildCodec(t, ExampleCrossChainRequest{}, ExampleCrossChainResponse{}) - net = NewNetwork(p2p.NewNetwork(logging.NoLog{}, nil, prometheus.NewRegistry(), ""), sender, codecManager, crossChainCodecManager, ids.EmptyNodeID, 1, 16) + p2pNetwork, err := p2p.NewNetwork(logging.NoLog{}, nil, prometheus.NewRegistry(), "") + require.NoError(t, err) + net = NewNetwork(p2pNetwork, sender, codecManager, crossChainCodecManager, ids.EmptyNodeID, 1, 16) client := NewNetworkClient(net) requestMessage := TestMessage{Message: "this is a request"} requestBytes, err := message.RequestToBytes(codecManager, requestMessage) @@ -413,6 +427,7 @@ func TestRequestMinVersion(t *testing.T) { context.Background(), nodeID, &version.Application{ + Name: version.Client, Major: 1, Minor: 7, Patch: 1, @@ -424,13 +439,14 @@ func TestRequestMinVersion(t *testing.T) { responseBytes, _, err := client.SendAppRequestAny( context.Background(), &version.Application{ + Name: version.Client, Major: 2, Minor: 0, Patch: 0, }, requestBytes, ) - assert.Equal(t, err.Error(), "no peers found matching version avalanche/2.0.0 out of 1 peers") + assert.Equal(t, err.Error(), "no peers found matching version avalanchego/2.0.0 out of 1 peers") assert.Nil(t, responseBytes) // ensure version matches and the request goes through @@ -467,7 +483,9 @@ func TestOnRequestHonoursDeadline(t *testing.T) { processingDuration: 500 * time.Millisecond, } - net = NewNetwork(p2p.NewNetwork(logging.NoLog{}, nil, prometheus.NewRegistry(), ""), sender, codecManager, crossChainCodecManager, ids.EmptyNodeID, 1, 1) + p2pNetwork, err := p2p.NewNetwork(logging.NoLog{}, nil, prometheus.NewRegistry(), "") + require.NoError(t, err) + net = NewNetwork(p2pNetwork, sender, codecManager, crossChainCodecManager, ids.EmptyNodeID, 1, 1) net.SetRequestHandler(requestHandler) nodeID := ids.GenerateTestNodeID() @@ -507,7 +525,9 @@ func TestGossip(t *testing.T) { } gossipHandler := &testGossipHandler{} - clientNetwork = NewNetwork(p2p.NewNetwork(logging.NoLog{}, nil, prometheus.NewRegistry(), ""), sender, codecManager, crossChainCodecManager, ids.EmptyNodeID, 1, 1) + 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)) @@ -534,7 +554,9 @@ func TestHandleInvalidMessages(t *testing.T) { requestID := uint32(1) sender := testAppSender{} - clientNetwork := NewNetwork(p2p.NewNetwork(logging.NoLog{}, nil, prometheus.NewRegistry(), ""), sender, codecManager, crossChainCodecManager, ids.EmptyNodeID, 1, 1) + 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(message.NoopMempoolGossipHandler{}) clientNetwork.SetRequestHandler(&testRequestHandler{}) @@ -583,7 +605,9 @@ func TestNetworkPropagatesRequestHandlerError(t *testing.T) { requestID := uint32(1) sender := testAppSender{} - clientNetwork := NewNetwork(p2p.NewNetwork(logging.NoLog{}, nil, prometheus.NewRegistry(), ""), sender, codecManager, crossChainCodecManager, ids.EmptyNodeID, 1, 1) + 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(message.NoopMempoolGossipHandler{}) clientNetwork.SetRequestHandler(&testRequestHandler{err: errors.New("fail")}) // Return an error from the request handler @@ -623,7 +647,9 @@ func TestCrossChainAppRequest(t *testing.T) { }, } - net = NewNetwork(p2p.NewNetwork(logging.NoLog{}, nil, prometheus.NewRegistry(), ""), sender, codecManager, crossChainCodecManager, ids.EmptyNodeID, 1, 1) + p2pNetwork, err := p2p.NewNetwork(logging.NoLog{}, nil, prometheus.NewRegistry(), "") + require.NoError(t, err) + net = NewNetwork(p2pNetwork, sender, codecManager, crossChainCodecManager, ids.EmptyNodeID, 1, 1) net.SetCrossChainRequestHandler(&testCrossChainHandler{codec: crossChainCodecManager}) client := NewNetworkClient(net) @@ -658,7 +684,9 @@ func TestCrossChainAppRequestOnCtxCancellation(t *testing.T) { }, } - net := NewNetwork(p2p.NewNetwork(logging.NoLog{}, nil, prometheus.NewRegistry(), ""), sender, codecManager, crossChainCodecManager, ids.EmptyNodeID, 1, 1) + p2pNetwork, err := p2p.NewNetwork(logging.NoLog{}, nil, prometheus.NewRegistry(), "") + require.NoError(t, err) + net := NewNetwork(p2pNetwork, sender, codecManager, crossChainCodecManager, ids.EmptyNodeID, 1, 1) net.SetCrossChainRequestHandler(&testCrossChainHandler{codec: crossChainCodecManager}) exampleCrossChainRequest := ExampleCrossChainRequest{ @@ -710,7 +738,9 @@ func TestCrossChainRequestRequestsRoutingAndResponse(t *testing.T) { codecManager := buildCodec(t, TestMessage{}) crossChainCodecManager := buildCodec(t, ExampleCrossChainRequest{}, ExampleCrossChainResponse{}) - net = NewNetwork(p2p.NewNetwork(logging.NoLog{}, nil, prometheus.NewRegistry(), ""), sender, codecManager, crossChainCodecManager, ids.EmptyNodeID, 1, 1) + p2pNetwork, err := p2p.NewNetwork(logging.NoLog{}, nil, prometheus.NewRegistry(), "") + require.NoError(t, err) + net = NewNetwork(p2pNetwork, sender, codecManager, crossChainCodecManager, ids.EmptyNodeID, 1, 1) net.SetCrossChainRequestHandler(&testCrossChainHandler{codec: crossChainCodecManager}) client := NewNetworkClient(net) @@ -770,7 +800,9 @@ func TestCrossChainRequestOnShutdown(t *testing.T) { } codecManager := buildCodec(t, TestMessage{}) crossChainCodecManager := buildCodec(t, ExampleCrossChainRequest{}, ExampleCrossChainResponse{}) - net = NewNetwork(p2p.NewNetwork(logging.NoLog{}, nil, prometheus.NewRegistry(), ""), sender, codecManager, crossChainCodecManager, ids.EmptyNodeID, 1, 1) + p2pNetwork, err := p2p.NewNetwork(logging.NoLog{}, nil, prometheus.NewRegistry(), "") + require.NoError(t, err) + net = NewNetwork(p2pNetwork, sender, codecManager, crossChainCodecManager, ids.EmptyNodeID, 1, 1) client := NewNetworkClient(net) exampleCrossChainRequest := ExampleCrossChainRequest{ @@ -823,9 +855,9 @@ func TestNetworkRouting(t *testing.T) { } protocol := 0 handler := &testSDKHandler{} - p2pNetwork := p2p.NewNetwork(logging.NoLog{}, sender, prometheus.NewRegistry(), "") - _, err := p2pNetwork.NewAppProtocol(uint64(protocol), handler) + p2pNetwork, err := p2p.NewNetwork(logging.NoLog{}, sender, prometheus.NewRegistry(), "") require.NoError(err) + require.NoError(p2pNetwork.AddHandler(uint64(protocol), handler)) networkCodec := codec.NewManager(0) crossChainCodec := codec.NewManager(0) @@ -849,13 +881,13 @@ func TestNetworkRouting(t *testing.T) { err = network.AppResponse(context.Background(), ids.GenerateTestNodeID(), 0, foobar) require.ErrorIs(err, p2p.ErrUnrequestedResponse) - err = network.AppRequestFailed(context.Background(), nodeID, 0) + err = network.AppRequestFailed(context.Background(), nodeID, 0, common.ErrTimeout) require.ErrorIs(err, p2p.ErrUnrequestedResponse) } func buildCodec(t *testing.T, types ...interface{}) codec.Manager { codecManager := codec.NewDefaultManager() - c := linearcodec.NewDefault() + c := linearcodec.NewDefault(time.Time{}) for _, typ := range types { assert.NoError(t, c.RegisterType(typ)) } @@ -978,7 +1010,7 @@ type HelloGossip struct { } func (h HelloGossip) Handle(handler message.GossipHandler, nodeID ids.NodeID) error { - return handler.HandleTxs(nodeID, message.TxsGossip{}) + return handler.HandleEthTxs(nodeID, message.EthTxsGossip{}) } func (h HelloGossip) String() string { @@ -995,7 +1027,7 @@ type testGossipHandler struct { nodeID ids.NodeID } -func (t *testGossipHandler) HandleTxs(nodeID ids.NodeID, msg message.TxsGossip) error { +func (t *testGossipHandler) HandleEthTxs(nodeID ids.NodeID, msg message.EthTxsGossip) error { t.received = true t.nodeID = nodeID return nil diff --git a/plugin/evm/block_builder.go b/plugin/evm/block_builder.go index ac78237670..68cdbf8083 100644 --- a/plugin/evm/block_builder.go +++ b/plugin/evm/block_builder.go @@ -157,9 +157,9 @@ func (b *blockBuilder) awaitSubmittedTxs() { b.signalTxsReady() if b.gossiper != nil && len(ethTxsEvent.Txs) > 0 { - // [GossipTxs] will block unless [gossiper.txsToGossipChan] (an + // [GossipEthTxs] will block unless [gossiper.ethTxsToGossipChan] (an // unbuffered channel) is listened on - if err := b.gossiper.GossipTxs(ethTxsEvent.Txs); err != nil { + if err := b.gossiper.GossipEthTxs(ethTxsEvent.Txs); err != nil { log.Warn( "failed to gossip new eth transactions", "err", err, diff --git a/plugin/evm/gossip.go b/plugin/evm/gossip.go new file mode 100644 index 0000000000..c1d1585e2b --- /dev/null +++ b/plugin/evm/gossip.go @@ -0,0 +1,181 @@ +// Copyright (C) 2019-2023, Ava Labs, Inc. All rights reserved. +// See the file LICENSE for licensing terms. + +package evm + +import ( + "context" + "fmt" + "sync" + "time" + + "github.com/ava-labs/avalanchego/ids" + "github.com/ava-labs/avalanchego/utils/logging" + "github.com/ethereum/go-ethereum/log" + + "github.com/ava-labs/avalanchego/network/p2p" + "github.com/ava-labs/avalanchego/network/p2p/gossip" + + "github.com/ava-labs/subnet-evm/core" + "github.com/ava-labs/subnet-evm/core/txpool" + "github.com/ava-labs/subnet-evm/core/types" +) + +var ( + _ p2p.Handler = (*txGossipHandler)(nil) + + _ gossip.Gossipable = (*GossipEthTx)(nil) + _ gossip.Marshaller[*GossipEthTx] = (*GossipEthTxMarshaller)(nil) + _ gossip.Set[*GossipEthTx] = (*GossipEthTxPool)(nil) +) + +func newTxGossipHandler[T gossip.Gossipable]( + log logging.Logger, + marshaller gossip.Marshaller[T], + mempool gossip.Set[T], + metrics gossip.Metrics, + maxMessageSize int, + throttlingPeriod time.Duration, + throttlingLimit int, + validators *p2p.Validators, +) txGossipHandler { + // push gossip messages can be handled from any peer + handler := gossip.NewHandler[T]( + log, + marshaller, + // Don't forward gossip to avoid double-forwarding + gossip.NoOpAccumulator[T]{}, + mempool, + metrics, + maxMessageSize, + ) + + // pull gossip requests are filtered by validators and are throttled + // to prevent spamming + validatorHandler := p2p.NewValidatorHandler( + p2p.NewThrottlerHandler( + handler, + p2p.NewSlidingWindowThrottler(throttlingPeriod, throttlingLimit), + log, + ), + validators, + log, + ) + + return txGossipHandler{ + appGossipHandler: handler, + appRequestHandler: validatorHandler, + } +} + +type txGossipHandler struct { + appGossipHandler p2p.Handler + appRequestHandler p2p.Handler +} + +func (t txGossipHandler) AppGossip(ctx context.Context, nodeID ids.NodeID, gossipBytes []byte) { + t.appGossipHandler.AppGossip(ctx, nodeID, gossipBytes) +} + +func (t txGossipHandler) AppRequest(ctx context.Context, nodeID ids.NodeID, deadline time.Time, requestBytes []byte) ([]byte, error) { + return t.appRequestHandler.AppRequest(ctx, nodeID, deadline, requestBytes) +} +func (t txGossipHandler) CrossChainAppRequest(context.Context, ids.ID, time.Time, []byte) ([]byte, error) { + return nil, nil +} + +func NewGossipEthTxPool(mempool *txpool.TxPool) (*GossipEthTxPool, error) { + bloom, err := gossip.NewBloomFilter(txGossipBloomMinTargetElements, txGossipBloomTargetFalsePositiveRate, txGossipBloomResetFalsePositiveRate) + if err != nil { + return nil, fmt.Errorf("failed to initialize bloom filter: %w", err) + } + + return &GossipEthTxPool{ + mempool: mempool, + pendingTxs: make(chan core.NewTxsEvent), + bloom: bloom, + }, nil +} + +type GossipEthTxPool struct { + mempool *txpool.TxPool + pendingTxs chan core.NewTxsEvent + + bloom *gossip.BloomFilter + lock sync.RWMutex +} + +func (g *GossipEthTxPool) Subscribe(ctx context.Context) { + g.mempool.SubscribeNewTxsEvent(g.pendingTxs) + + for { + select { + case <-ctx.Done(): + log.Debug("shutting down subscription") + return + case pendingTxs := <-g.pendingTxs: + g.lock.Lock() + optimalElements := (g.mempool.PendingSize() + len(pendingTxs.Txs)) * txGossipBloomChurnMultiplier + for _, pendingTx := range pendingTxs.Txs { + tx := &GossipEthTx{Tx: pendingTx} + g.bloom.Add(tx) + reset, err := gossip.ResetBloomFilterIfNeeded(g.bloom, optimalElements) + if err != nil { + log.Error("failed to reset bloom filter", "err", err) + continue + } + + if reset { + log.Debug("resetting bloom filter", "reason", "reached max filled ratio") + + g.mempool.IteratePending(func(tx *types.Transaction) bool { + g.bloom.Add(&GossipEthTx{Tx: tx}) + return true + }) + } + } + g.lock.Unlock() + } + } +} + +// Add enqueues the transaction to the mempool. Subscribe should be called +// to receive an event if tx is actually added to the mempool or not. +func (g *GossipEthTxPool) Add(tx *GossipEthTx) error { + return g.mempool.AddRemotes([]*types.Transaction{tx.Tx})[0] +} + +func (g *GossipEthTxPool) Iterate(f func(tx *GossipEthTx) bool) { + g.mempool.IteratePending(func(tx *types.Transaction) bool { + return f(&GossipEthTx{Tx: tx}) + }) +} + +func (g *GossipEthTxPool) GetFilter() ([]byte, []byte) { + g.lock.RLock() + defer g.lock.RUnlock() + + return g.bloom.Marshal() +} + +type GossipEthTxMarshaller struct{} + +func (g GossipEthTxMarshaller) MarshalGossip(tx *GossipEthTx) ([]byte, error) { + return tx.Tx.MarshalBinary() +} + +func (g GossipEthTxMarshaller) UnmarshalGossip(bytes []byte) (*GossipEthTx, error) { + tx := &GossipEthTx{ + Tx: &types.Transaction{}, + } + + return tx, tx.Tx.UnmarshalBinary(bytes) +} + +type GossipEthTx struct { + Tx *types.Transaction +} + +func (tx *GossipEthTx) GossipID() ids.ID { + return ids.ID(tx.Tx.Hash()) +} diff --git a/plugin/evm/gossip_mempool.go b/plugin/evm/gossip_mempool.go deleted file mode 100644 index 49c649e2bd..0000000000 --- a/plugin/evm/gossip_mempool.go +++ /dev/null @@ -1,117 +0,0 @@ -// Copyright (C) 2019-2023, Ava Labs, Inc. All rights reserved. -// See the file LICENSE for licensing terms. - -package evm - -import ( - "context" - "fmt" - "sync" - - "github.com/ava-labs/avalanchego/ids" - "github.com/ethereum/go-ethereum/log" - - "github.com/ava-labs/avalanchego/network/p2p/gossip" - - "github.com/ava-labs/subnet-evm/core" - "github.com/ava-labs/subnet-evm/core/txpool" - "github.com/ava-labs/subnet-evm/core/types" -) - -var ( - _ gossip.Gossipable = (*GossipTx)(nil) - _ gossip.Set[*GossipTx] = (*GossipTxPool)(nil) -) - -func NewGossipTxPool(mempool *txpool.TxPool) (*GossipTxPool, error) { - bloom, err := gossip.NewBloomFilter(txGossipBloomMaxItems, txGossipBloomFalsePositiveRate) - if err != nil { - return nil, fmt.Errorf("failed to initialize bloom filter: %w", err) - } - - return &GossipTxPool{ - mempool: mempool, - pendingTxs: make(chan core.NewTxsEvent), - bloom: bloom, - }, nil -} - -type GossipTxPool struct { - mempool *txpool.TxPool - pendingTxs chan core.NewTxsEvent - - bloom *gossip.BloomFilter - lock sync.RWMutex -} - -func (g *GossipTxPool) Subscribe(ctx context.Context) { - g.mempool.SubscribeNewTxsEvent(g.pendingTxs) - - for { - select { - case <-ctx.Done(): - log.Debug("shutting down subscription") - return - case pendingTxs := <-g.pendingTxs: - g.lock.Lock() - for _, pendingTx := range pendingTxs.Txs { - tx := &GossipTx{Tx: pendingTx} - g.bloom.Add(tx) - reset, err := gossip.ResetBloomFilterIfNeeded(g.bloom, txGossipMaxFalsePositiveRate) - if err != nil { - log.Error("failed to reset bloom filter", "err", err) - continue - } - - if reset { - log.Debug("resetting bloom filter", "reason", "reached max filled ratio") - - g.mempool.IteratePending(func(tx *types.Transaction) bool { - g.bloom.Add(&GossipTx{Tx: pendingTx}) - return true - }) - } - } - g.lock.Unlock() - } - } -} - -// Add enqueues the transaction to the mempool. Subscribe should be called -// to receive an event if tx is actually added to the mempool or not. -func (g *GossipTxPool) Add(tx *GossipTx) error { - return g.mempool.AddRemotes([]*types.Transaction{tx.Tx})[0] -} - -func (g *GossipTxPool) Iterate(f func(tx *GossipTx) bool) { - g.mempool.IteratePending(func(tx *types.Transaction) bool { - return f(&GossipTx{Tx: tx}) - }) -} - -func (g *GossipTxPool) GetFilter() ([]byte, []byte, error) { - g.lock.RLock() - defer g.lock.RUnlock() - - bloom, err := g.bloom.Bloom.MarshalBinary() - salt := g.bloom.Salt - - return bloom, salt[:], err -} - -type GossipTx struct { - Tx *types.Transaction -} - -func (tx *GossipTx) GetID() ids.ID { - return ids.ID(tx.Tx.Hash()) -} - -func (tx *GossipTx) Marshal() ([]byte, error) { - return tx.Tx.MarshalBinary() -} - -func (tx *GossipTx) Unmarshal(bytes []byte) error { - tx.Tx = &types.Transaction{} - return tx.Tx.UnmarshalBinary(bytes) -} diff --git a/plugin/evm/gossip_test.go b/plugin/evm/gossip_test.go new file mode 100644 index 0000000000..a9f80355e8 --- /dev/null +++ b/plugin/evm/gossip_test.go @@ -0,0 +1,104 @@ +// (c) 2019-2021, Ava Labs, Inc. All rights reserved. +// See the file LICENSE for licensing terms. + +package evm + +import ( + "context" + "math/big" + "testing" + "time" + + "github.com/ava-labs/avalanchego/network/p2p/gossip" + "github.com/ava-labs/subnet-evm/consensus/dummy" + "github.com/ava-labs/subnet-evm/core" + "github.com/ava-labs/subnet-evm/core/rawdb" + "github.com/ava-labs/subnet-evm/core/txpool" + "github.com/ava-labs/subnet-evm/core/types" + "github.com/ava-labs/subnet-evm/core/vm" + "github.com/ava-labs/subnet-evm/params" + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/crypto" + "github.com/stretchr/testify/require" +) + +func TestGossipEthTxMarshaller(t *testing.T) { + require := require.New(t) + + blobTx := &types.BlobTx{} + want := &GossipEthTx{Tx: types.NewTx(blobTx)} + marshaller := GossipEthTxMarshaller{} + + bytes, err := marshaller.MarshalGossip(want) + require.NoError(err) + + got, err := marshaller.UnmarshalGossip(bytes) + require.NoError(err) + require.Equal(want.GossipID(), got.GossipID()) +} + +func TestGossipSubscribe(t *testing.T) { + require := require.New(t) + key, err := crypto.GenerateKey() + require.NoError(err) + addr := crypto.PubkeyToAddress(key.PublicKey) + + require.NoError(err) + txPool := setupPoolWithConfig(t, params.TestChainConfig, addr) + defer txPool.Stop() + txPool.SetGasPrice(common.Big1) + txPool.SetMinFee(common.Big0) + + gossipTxPool, err := NewGossipEthTxPool(txPool) + require.NoError(err) + + // use a custom bloom filter to test the bloom filter reset + gossipTxPool.bloom, err = gossip.NewBloomFilter(1, 0.01, 0.0000000000000001) // maxCount =1 + require.NoError(err) + ctx, cancel := context.WithCancel(context.TODO()) + defer cancel() + go gossipTxPool.Subscribe(ctx) + + // create eth txs + ethTxs := getValidEthTxs(key, 10, big.NewInt(226*params.GWei)) + + // Notify mempool about txs + errs := txPool.AddRemotesSync(ethTxs) + for _, err := range errs { + require.NoError(err, "failed adding subnet-evm tx to remote mempool") + } + + require.Eventually( + func() bool { + gossipTxPool.lock.RLock() + defer gossipTxPool.lock.RUnlock() + + for _, tx := range ethTxs { + if !gossipTxPool.bloom.Has(&GossipEthTx{Tx: tx}) { + return false + } + } + return true + }, + 10*time.Second, + 10*time.Millisecond, + "expected all transactions to eventually be in the bloom filter", + ) +} + +func setupPoolWithConfig(t *testing.T, config *params.ChainConfig, fundedAddress common.Address) *txpool.TxPool { + diskdb := rawdb.NewMemoryDatabase() + engine := dummy.NewETHFaker() + + var gspec = &core.Genesis{ + Config: config, + Alloc: core.GenesisAlloc{fundedAddress: core.GenesisAccount{Balance: big.NewInt(1000000000000000000)}}, + } + 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.go b/plugin/evm/gossiper.go index 8f179d0ce5..578ed1d3ad 100644 --- a/plugin/evm/gossiper.go +++ b/plugin/evm/gossiper.go @@ -4,11 +4,13 @@ package evm import ( + "context" "math/big" "sync" "time" "github.com/ava-labs/avalanchego/codec" + "github.com/ava-labs/avalanchego/network/p2p/gossip" "github.com/ava-labs/subnet-evm/peer" @@ -32,9 +34,9 @@ const ( // in the cache, not entire transactions. recentCacheSize = 512 - // [txsGossipInterval] is how often we attempt to gossip newly seen + // [ethTxsGossipInterval] is how often we attempt to gossip newly seen // transactions to other nodes. - txsGossipInterval = 500 * time.Millisecond + ethTxsGossipInterval = 500 * time.Millisecond // [minGossipBatchInterval] is the minimum amount of time that must pass // before our last gossip to peers. @@ -43,8 +45,8 @@ const ( // Gossiper handles outgoing gossip of transactions type Gossiper interface { - // GossipTxs sends AppGossip message containing the given [txs] - GossipTxs(txs []*types.Transaction) error + // GossipEthTxs sends AppGossip message containing the given [txs] + GossipEthTxs(txs []*types.Transaction) error } // pushGossiper is used to gossip transactions to the network @@ -52,21 +54,22 @@ type pushGossiper struct { ctx *snow.Context config Config - client peer.NetworkClient - blockchain *core.BlockChain - txPool *txpool.TxPool + client peer.NetworkClient + blockchain *core.BlockChain + txPool *txpool.TxPool + ethTxGossiper gossip.Accumulator[*GossipEthTx] // We attempt to batch transactions we need to gossip to avoid runaway // amplification of mempol chatter. - txsToGossipChan chan []*types.Transaction - txsToGossip map[common.Hash]*types.Transaction - lastGossiped time.Time - shutdownChan chan struct{} - shutdownWg *sync.WaitGroup + ethTxsToGossipChan chan []*types.Transaction + ethTxsToGossip map[common.Hash]*types.Transaction + lastGossiped time.Time + shutdownChan chan struct{} + shutdownWg *sync.WaitGroup // [recentTxs] prevent us from over-gossiping the // same transaction in a short period of time. - recentTxs *cache.LRU[common.Hash, interface{}] + recentEthTxs *cache.LRU[common.Hash, interface{}] codec codec.Manager signer types.Signer @@ -75,22 +78,25 @@ type pushGossiper struct { // createGossiper constructs and returns a pushGossiper or noopGossiper // based on whether vm.chainConfig.SubnetEVMTimestamp is set -func (vm *VM) createGossiper(stats GossipStats) Gossiper { +func (vm *VM) createGossiper(stats GossipStats, ethTxGossiper gossip.Accumulator[*GossipEthTx], +) Gossiper { net := &pushGossiper{ - ctx: vm.ctx, - config: vm.config, - client: vm.client, - blockchain: vm.blockChain, - txPool: vm.txPool, - txsToGossipChan: make(chan []*types.Transaction), - txsToGossip: make(map[common.Hash]*types.Transaction), - shutdownChan: vm.shutdownChan, - shutdownWg: &vm.shutdownWg, - recentTxs: &cache.LRU[common.Hash, interface{}]{Size: recentCacheSize}, - codec: vm.networkCodec, - signer: types.LatestSigner(vm.blockChain.Config()), - stats: stats, + ctx: vm.ctx, + config: vm.config, + client: vm.client, + blockchain: vm.blockChain, + txPool: vm.txPool, + ethTxsToGossipChan: make(chan []*types.Transaction), + ethTxsToGossip: make(map[common.Hash]*types.Transaction), + shutdownChan: vm.shutdownChan, + shutdownWg: &vm.shutdownWg, + recentEthTxs: &cache.LRU[common.Hash, interface{}]{Size: recentCacheSize}, + codec: vm.networkCodec, + signer: types.LatestSigner(vm.blockChain.Config()), + stats: stats, + ethTxGossiper: ethTxGossiper, } + net.awaitEthTxGossip() return net } @@ -231,12 +237,12 @@ func (n *pushGossiper) queuePriorityRegossipTxs() types.Transactions { } // awaitEthTxGossip periodically gossips transactions that have been queued for -// gossip at least once every [txsGossipInterval]. +// gossip at least once every [ethTxsGossipInterval]. func (n *pushGossiper) awaitEthTxGossip() { n.shutdownWg.Add(1) go n.ctx.Log.RecoverAndPanic(func() { var ( - gossipTicker = time.NewTicker(txsGossipInterval) + gossipTicker = time.NewTicker(ethTxsGossipInterval) regossipTicker = time.NewTicker(n.config.RegossipFrequency.Duration) priorityRegossipTicker = time.NewTicker(n.config.PriorityRegossipFrequency.Duration) ) @@ -250,18 +256,24 @@ func (n *pushGossiper) awaitEthTxGossip() { for { select { case <-gossipTicker.C: - if attempted, err := n.gossipTxs(false); err != nil { + if attempted, err := n.gossipEthTxs(false); err != nil { log.Warn( "failed to send eth transactions", "len(txs)", attempted, "err", err, ) } + if err := n.ethTxGossiper.Gossip(context.TODO()); err != nil { + log.Warn( + "failed to send eth transactions", + "err", err, + ) + } case <-regossipTicker.C: for _, tx := range n.queueRegossipTxs() { - n.txsToGossip[tx.Hash()] = tx + n.ethTxsToGossip[tx.Hash()] = tx } - if attempted, err := n.gossipTxs(true); err != nil { + if attempted, err := n.gossipEthTxs(true); err != nil { log.Warn( "failed to regossip eth transactions", "len(txs)", attempted, @@ -270,26 +282,41 @@ func (n *pushGossiper) awaitEthTxGossip() { } case <-priorityRegossipTicker.C: for _, tx := range n.queuePriorityRegossipTxs() { - n.txsToGossip[tx.Hash()] = tx + n.ethTxsToGossip[tx.Hash()] = tx } - if attempted, err := n.gossipTxs(true); err != nil { + if attempted, err := n.gossipEthTxs(true); err != nil { log.Warn( "failed to regossip priority eth transactions", "len(txs)", attempted, "err", err, ) } - case txs := <-n.txsToGossipChan: + case txs := <-n.ethTxsToGossipChan: for _, tx := range txs { - n.txsToGossip[tx.Hash()] = tx + n.ethTxsToGossip[tx.Hash()] = tx } - if attempted, err := n.gossipTxs(false); err != nil { + if attempted, err := n.gossipEthTxs(false); err != nil { log.Warn( "failed to send eth transactions", "len(txs)", attempted, "err", err, ) } + + gossipTxs := make([]*GossipEthTx, 0, len(txs)) + for _, tx := range txs { + gossipTxs = append(gossipTxs, &GossipEthTx{Tx: tx}) + } + + n.ethTxGossiper.Add(gossipTxs...) + if err := n.ethTxGossiper.Gossip(context.TODO()); err != nil { + log.Warn( + "failed to send eth transactions", + "len(txs)", len(txs), + "err", err, + ) + } + case <-n.shutdownChan: return } @@ -297,7 +324,7 @@ func (n *pushGossiper) awaitEthTxGossip() { }) } -func (n *pushGossiper) sendTxs(txs []*types.Transaction) error { +func (n *pushGossiper) sendEthTxs(txs []*types.Transaction) error { if len(txs) == 0 { return nil } @@ -306,7 +333,7 @@ func (n *pushGossiper) sendTxs(txs []*types.Transaction) error { if err != nil { return err } - msg := message.TxsGossip{ + msg := message.EthTxsGossip{ Txs: txBytes, } msgBytes, err := message.BuildGossipMessage(n.codec, msg) @@ -323,15 +350,15 @@ func (n *pushGossiper) sendTxs(txs []*types.Transaction) error { return n.client.Gossip(msgBytes) } -func (n *pushGossiper) gossipTxs(force bool) (int, error) { - if (!force && time.Since(n.lastGossiped) < minGossipBatchInterval) || len(n.txsToGossip) == 0 { +func (n *pushGossiper) gossipEthTxs(force bool) (int, error) { + if (!force && time.Since(n.lastGossiped) < minGossipBatchInterval) || len(n.ethTxsToGossip) == 0 { return 0, nil } n.lastGossiped = time.Now() - txs := make([]*types.Transaction, 0, len(n.txsToGossip)) - for txHash, tx := range n.txsToGossip { + txs := make([]*types.Transaction, 0, len(n.ethTxsToGossip)) + for _, tx := range n.ethTxsToGossip { txs = append(txs, tx) - delete(n.txsToGossip, txHash) + delete(n.ethTxsToGossip, tx.Hash()) } selectedTxs := make([]*types.Transaction, 0) @@ -349,11 +376,11 @@ func (n *pushGossiper) gossipTxs(force bool) (int, error) { // We check [force] outside of the if statement to avoid an unnecessary // cache lookup. if !force { - if _, has := n.recentTxs.Get(txHash); has { + if _, has := n.recentEthTxs.Get(txHash); has { continue } } - n.recentTxs.Put(txHash, nil) + n.recentEthTxs.Put(txHash, nil) selectedTxs = append(selectedTxs, tx) } @@ -367,8 +394,8 @@ func (n *pushGossiper) gossipTxs(force bool) (int, error) { msgTxsSize := uint64(0) for _, tx := range selectedTxs { size := tx.Size() - if msgTxsSize+size > message.TxMsgSoftCapSize { - if err := n.sendTxs(msgTxs); err != nil { + if msgTxsSize+size > message.EthMsgSoftCapSize { + if err := n.sendEthTxs(msgTxs); err != nil { return len(selectedTxs), err } msgTxs = msgTxs[:0] @@ -379,18 +406,18 @@ func (n *pushGossiper) gossipTxs(force bool) (int, error) { } // Send any remaining [msgTxs] - return len(selectedTxs), n.sendTxs(msgTxs) + return len(selectedTxs), n.sendEthTxs(msgTxs) } -// GossipTxs enqueues the provided [txs] for gossiping. At some point, the +// GossipEthTxs enqueues the provided [txs] for gossiping. At some point, the // [pushGossiper] will attempt to gossip the provided txs to other nodes // (usually right away if not under load). // // NOTE: We never return a non-nil error from this function but retain the // option to do so in case it becomes useful. -func (n *pushGossiper) GossipTxs(txs []*types.Transaction) error { +func (n *pushGossiper) GossipEthTxs(txs []*types.Transaction) error { select { - case n.txsToGossipChan <- txs: + case n.ethTxsToGossipChan <- txs: case <-n.shutdownChan: } return nil @@ -411,16 +438,16 @@ func NewGossipHandler(vm *VM, stats GossipReceivedStats) *GossipHandler { } } -func (h *GossipHandler) HandleTxs(nodeID ids.NodeID, msg message.TxsGossip) error { +func (h *GossipHandler) HandleEthTxs(nodeID ids.NodeID, msg message.EthTxsGossip) error { log.Trace( - "AppGossip called with TxsGossip", + "AppGossip called with EthTxsGossip", "peerID", nodeID, "size(txs)", len(msg.Txs), ) if len(msg.Txs) == 0 { log.Trace( - "AppGossip received empty TxsGossip Message", + "AppGossip received empty EthTxsGossip Message", "peerID", nodeID, ) return nil diff --git a/plugin/evm/gossipper_test.go b/plugin/evm/gossipper_test.go index c2978db73f..68bcbd8c1a 100644 --- a/plugin/evm/gossipper_test.go +++ b/plugin/evm/gossipper_test.go @@ -49,12 +49,12 @@ func fundAddressByGenesis(addrs []common.Address) (string, error) { return string(bytes), err } -func getValidTxs(key *ecdsa.PrivateKey, count int, gasPrice *big.Int) []*types.Transaction { +func getValidEthTxs(key *ecdsa.PrivateKey, count int, gasPrice *big.Int) []*types.Transaction { res := make([]*types.Transaction, count) to := common.Address{} - amount := big.NewInt(10000) - gasLimit := uint64(100000) + amount := big.NewInt(0) + gasLimit := uint64(37000) for i := 0; i < count; i++ { tx, _ := types.SignTx( @@ -76,7 +76,7 @@ func getValidTxs(key *ecdsa.PrivateKey, count int, gasPrice *big.Int) []*types.T // 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 TestMempoolTxsAddedTxsGossipedAfterActivation(t *testing.T) { +func TestMempoolEthTxsAddedTxsGossipedAfterActivation(t *testing.T) { if os.Getenv("RUN_FLAKY_TESTS") != "true" { t.Skip("FLAKY") } @@ -86,10 +86,11 @@ func TestMempoolTxsAddedTxsGossipedAfterActivation(t *testing.T) { assert.NoError(err) addr := crypto.PubkeyToAddress(key.PublicKey) - cfgJson, err := fundAddressByGenesis([]common.Address{addr}) + + genesisJSON, err := fundAddressByGenesis([]common.Address{addr}) assert.NoError(err) - _, vm, _, sender := GenesisVM(t, true, cfgJson, "", "") + _, vm, _, sender := GenesisVM(t, true, genesisJSON, "", "") defer func() { err := vm.Shutdown(context.Background()) assert.NoError(err) @@ -98,20 +99,19 @@ func TestMempoolTxsAddedTxsGossipedAfterActivation(t *testing.T) { vm.txPool.SetMinFee(common.Big0) // create eth txes - ethTxs := getValidTxs(key, 3, common.Big1) + ethTxs := getValidEthTxs(key, 3, common.Big1) var wg sync.WaitGroup - var wg2 sync.WaitGroup wg.Add(2) - wg2.Add(1) 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.TxsGossip) + requestMsg, ok := notifyMsgIntf.(message.EthTxsGossip) assert.True(ok) assert.NotEmpty(requestMsg.Txs) @@ -123,12 +123,12 @@ func TestMempoolTxsAddedTxsGossipedAfterActivation(t *testing.T) { []common.Hash{txs[0].Hash(), txs[1].Hash()}, ) seen++ - wg2.Done() + close(signal1) } else if seen == 1 { notifyMsgIntf, err := message.ParseGossipMessage(vm.networkCodec, gossipedBytes) assert.NoError(err) - requestMsg, ok := notifyMsgIntf.(message.TxsGossip) + requestMsg, ok := notifyMsgIntf.(message.EthTxsGossip) assert.True(ok) assert.NotEmpty(requestMsg.Txs) @@ -152,8 +152,8 @@ func TestMempoolTxsAddedTxsGossipedAfterActivation(t *testing.T) { } // Gossip txs again (shouldn't gossip hashes) - attemptAwait(t, &wg2, 5*time.Second) // wait until reorg processed - assert.NoError(vm.gossiper.GossipTxs(ethTxs[:2])) + <-signal1 // wait until reorg processed + assert.NoError(vm.gossiper.GossipEthTxs(ethTxs[:2])) errs = vm.txPool.AddRemotesSync(ethTxs) assert.Contains(errs[0].Error(), "already known") @@ -164,7 +164,7 @@ func TestMempoolTxsAddedTxsGossipedAfterActivation(t *testing.T) { } // show that locally issued eth txs are chunked correctly -func TestMempoolTxsAddedTxsGossipedAfterActivationChunking(t *testing.T) { +func TestMempoolEthTxsAddedTxsGossipedAfterActivationChunking(t *testing.T) { if os.Getenv("RUN_FLAKY_TESTS") != "true" { t.Skip("FLAKY") } @@ -175,10 +175,10 @@ func TestMempoolTxsAddedTxsGossipedAfterActivationChunking(t *testing.T) { addr := crypto.PubkeyToAddress(key.PublicKey) - cfgJson, err := fundAddressByGenesis([]common.Address{addr}) + genesisJSON, err := fundAddressByGenesis([]common.Address{addr}) assert.NoError(err) - _, vm, _, sender := GenesisVM(t, true, cfgJson, "", "") + _, vm, _, sender := GenesisVM(t, true, genesisJSON, "", "") defer func() { err := vm.Shutdown(context.Background()) assert.NoError(err) @@ -187,7 +187,7 @@ func TestMempoolTxsAddedTxsGossipedAfterActivationChunking(t *testing.T) { vm.txPool.SetMinFee(common.Big0) // create eth txes - txs := getValidTxs(key, 100, common.Big1) + ethTxs := getValidEthTxs(key, 100, common.Big1) var wg sync.WaitGroup wg.Add(2) @@ -197,7 +197,7 @@ func TestMempoolTxsAddedTxsGossipedAfterActivationChunking(t *testing.T) { notifyMsgIntf, err := message.ParseGossipMessage(vm.networkCodec, gossipedBytes) assert.NoError(err) - requestMsg, ok := notifyMsgIntf.(message.TxsGossip) + requestMsg, ok := notifyMsgIntf.(message.EthTxsGossip) assert.True(ok) assert.NotEmpty(requestMsg.Txs) @@ -211,14 +211,14 @@ func TestMempoolTxsAddedTxsGossipedAfterActivationChunking(t *testing.T) { } // Notify VM about eth txs - errs := vm.txPool.AddRemotesSync(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 txs { + for _, tx := range ethTxs { _, ok := seen[tx.Hash()] assert.True(ok, "missing hash: %v", tx.Hash()) } @@ -226,7 +226,7 @@ func TestMempoolTxsAddedTxsGossipedAfterActivationChunking(t *testing.T) { // show that a geth tx discovered from gossip is requested to the same node that // gossiped it -func TestMempoolTxsAppGossipHandling(t *testing.T) { +func TestMempoolEthTxsAppGossipHandling(t *testing.T) { if os.Getenv("RUN_FLAKY_TESTS") != "true" { t.Skip("FLAKY") } @@ -237,10 +237,10 @@ func TestMempoolTxsAppGossipHandling(t *testing.T) { addr := crypto.PubkeyToAddress(key.PublicKey) - cfgJson, err := fundAddressByGenesis([]common.Address{addr}) + genesisJSON, err := fundAddressByGenesis([]common.Address{addr}) assert.NoError(err) - _, vm, _, sender := GenesisVM(t, true, cfgJson, "", "") + _, vm, _, sender := GenesisVM(t, true, genesisJSON, "", "") defer func() { err := vm.Shutdown(context.Background()) assert.NoError(err) @@ -264,12 +264,12 @@ func TestMempoolTxsAppGossipHandling(t *testing.T) { } // prepare a tx - tx := getValidTxs(key, 1, common.Big1)[0] + tx := getValidEthTxs(key, 1, common.Big1)[0] // show that unknown subnet-evm hashes is requested txBytes, err := rlp.EncodeToBytes([]*types.Transaction{tx}) assert.NoError(err) - msg := message.TxsGossip{ + msg := message.EthTxsGossip{ Txs: txBytes, } msgBytes, err := message.BuildGossipMessage(vm.networkCodec, msg) @@ -284,7 +284,7 @@ func TestMempoolTxsAppGossipHandling(t *testing.T) { attemptAwait(t, &wg, 5*time.Second) } -func TestMempoolTxsRegossipSingleAccount(t *testing.T) { +func TestMempoolEthTxsRegossipSingleAccount(t *testing.T) { assert := assert.New(t) key, err := crypto.GenerateKey() @@ -304,10 +304,10 @@ func TestMempoolTxsRegossipSingleAccount(t *testing.T) { vm.txPool.SetMinFee(common.Big0) // create eth txes - txs := getValidTxs(key, 10, big.NewInt(226*params.GWei)) + ethTxs := getValidEthTxs(key, 10, big.NewInt(226*params.GWei)) // Notify VM about eth txs - errs := vm.txPool.AddRemotesSync(txs) + errs := vm.txPool.AddRemotesSync(ethTxs) for _, err := range errs { assert.NoError(err, "failed adding subnet-evm tx to remote mempool") } @@ -317,10 +317,10 @@ func TestMempoolTxsRegossipSingleAccount(t *testing.T) { pushNetwork := vm.gossiper.(*pushGossiper) queued := pushNetwork.queueRegossipTxs() assert.Len(queued, 1, "unexpected length of queued txs") - assert.Equal(txs[0].Hash(), queued[0].Hash()) + assert.Equal(ethTxs[0].Hash(), queued[0].Hash()) } -func TestMempoolTxsRegossip(t *testing.T) { +func TestMempoolEthTxsRegossip(t *testing.T) { assert := assert.New(t) keys := make([]*ecdsa.PrivateKey, 20) @@ -332,10 +332,10 @@ func TestMempoolTxsRegossip(t *testing.T) { addrs[i] = crypto.PubkeyToAddress(key.PublicKey) } - cfgJson, err := fundAddressByGenesis(addrs) + genesisJSON, err := fundAddressByGenesis(addrs) assert.NoError(err) - _, vm, _, _ := GenesisVM(t, true, cfgJson, `{"local-txs-enabled":true}`, "") + _, vm, _, _ := GenesisVM(t, true, genesisJSON, `{"local-txs-enabled":true}`, "") defer func() { err := vm.Shutdown(context.Background()) assert.NoError(err) @@ -347,7 +347,7 @@ func TestMempoolTxsRegossip(t *testing.T) { ethTxs := make([]*types.Transaction, 20) ethTxHashes := make([]common.Hash, 20) for i := 0; i < 20; i++ { - txs := getValidTxs(keys[i], 1, big.NewInt(226*params.GWei)) + txs := getValidEthTxs(keys[i], 1, big.NewInt(226*params.GWei)) tx := txs[0] ethTxs[i] = tx ethTxHashes[i] = tx.Hash() @@ -407,8 +407,8 @@ func TestMempoolTxsPriorityRegossip(t *testing.T) { vm.txPool.SetMinFee(common.Big0) // create eth txes - txs := getValidTxs(key, 10, big.NewInt(226*params.GWei)) - txs2 := getValidTxs(key2, 10, big.NewInt(226*params.GWei)) + 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) { diff --git a/plugin/evm/message/codec.go b/plugin/evm/message/codec.go index 91db9633ab..6939ca4978 100644 --- a/plugin/evm/message/codec.go +++ b/plugin/evm/message/codec.go @@ -4,6 +4,8 @@ package message import ( + "time" + "github.com/ava-labs/avalanchego/codec" "github.com/ava-labs/avalanchego/codec/linearcodec" "github.com/ava-labs/avalanchego/utils/units" @@ -22,12 +24,12 @@ var ( func init() { Codec = codec.NewManager(maxMessageSize) - c := linearcodec.NewDefault() + c := linearcodec.NewDefault(time.Time{}) errs := wrappers.Errs{} errs.Add( // Gossip types - c.RegisterType(TxsGossip{}), + c.RegisterType(EthTxsGossip{}), // Types for state sync frontier consensus c.RegisterType(SyncSummary{}), @@ -53,7 +55,7 @@ func init() { } CrossChainCodec = codec.NewManager(maxMessageSize) - ccc := linearcodec.NewDefault() + ccc := linearcodec.NewDefault(time.Time{}) errs = wrappers.Errs{} errs.Add( diff --git a/plugin/evm/message/handler.go b/plugin/evm/message/handler.go index b5933f28f3..c0617b0312 100644 --- a/plugin/evm/message/handler.go +++ b/plugin/evm/message/handler.go @@ -19,13 +19,13 @@ var ( // GossipHandler handles incoming gossip messages type GossipHandler interface { - HandleTxs(nodeID ids.NodeID, msg TxsGossip) error + HandleEthTxs(nodeID ids.NodeID, msg EthTxsGossip) error } type NoopMempoolGossipHandler struct{} -func (NoopMempoolGossipHandler) HandleTxs(nodeID ids.NodeID, _ TxsGossip) error { - log.Debug("dropping unexpected Txs message", "peerID", nodeID) +func (NoopMempoolGossipHandler) HandleEthTxs(nodeID ids.NodeID, msg EthTxsGossip) error { + log.Debug("dropping unexpected EthTxsGossip message", "peerID", nodeID) return nil } diff --git a/plugin/evm/message/handler_test.go b/plugin/evm/message/handler_test.go index 37ca82ea4a..dad0ec2b90 100644 --- a/plugin/evm/message/handler_test.go +++ b/plugin/evm/message/handler_test.go @@ -12,11 +12,11 @@ import ( ) type CounterHandler struct { - Txs int + EthTxs int } -func (h *CounterHandler) HandleTxs(ids.NodeID, TxsGossip) error { - h.Txs++ +func (h *CounterHandler) HandleEthTxs(ids.NodeID, EthTxsGossip) error { + h.EthTxs++ return nil } @@ -24,11 +24,11 @@ func TestHandleTxs(t *testing.T) { assert := assert.New(t) handler := CounterHandler{} - msg := TxsGossip{} + msg := EthTxsGossip{} err := msg.Handle(&handler, ids.EmptyNodeID) assert.NoError(err) - assert.Equal(1, handler.Txs) + assert.Equal(1, handler.EthTxs) } func TestNoopHandler(t *testing.T) { @@ -36,6 +36,6 @@ func TestNoopHandler(t *testing.T) { handler := NoopMempoolGossipHandler{} - err := handler.HandleTxs(ids.EmptyNodeID, TxsGossip{}) + err := handler.HandleEthTxs(ids.EmptyNodeID, EthTxsGossip{}) assert.NoError(err) } diff --git a/plugin/evm/message/message.go b/plugin/evm/message/message.go index 89e584179f..35887911c9 100644 --- a/plugin/evm/message/message.go +++ b/plugin/evm/message/message.go @@ -14,15 +14,15 @@ import ( ) const ( - // TxMsgSoftCapSize is the ideal size of encoded transaction bytes we send in - // any [Txs] message. We do not limit inbound messages to + // EthMsgSoftCapSize is the ideal size of encoded transaction bytes we send in + // any [EthTxsGossip] or [AtomicTxGossip] message. We do not limit inbound messages to // this size, however. Max inbound message size is enforced by the codec // (512KB). - TxMsgSoftCapSize = 64 * units.KiB + EthMsgSoftCapSize = 64 * units.KiB ) var ( - _ GossipMessage = TxsGossip{} + _ GossipMessage = EthTxsGossip{} errUnexpectedCodecVersion = errors.New("unexpected codec version") ) @@ -35,16 +35,16 @@ type GossipMessage interface { Handle(handler GossipHandler, nodeID ids.NodeID) error } -type TxsGossip struct { +type EthTxsGossip struct { Txs []byte `serialize:"true"` } -func (msg TxsGossip) Handle(handler GossipHandler, nodeID ids.NodeID) error { - return handler.HandleTxs(nodeID, msg) +func (msg EthTxsGossip) Handle(handler GossipHandler, nodeID ids.NodeID) error { + return handler.HandleEthTxs(nodeID, msg) } -func (msg TxsGossip) String() string { - return fmt.Sprintf("TxsGossip(Len=%d)", len(msg.Txs)) +func (msg EthTxsGossip) String() string { + return fmt.Sprintf("EthTxsGossip(Len=%d)", len(msg.Txs)) } func ParseGossipMessage(codec codec.Manager, bytes []byte) (GossipMessage, error) { diff --git a/plugin/evm/message/message_test.go b/plugin/evm/message/message_test.go index 29f47f226d..0a18fde784 100644 --- a/plugin/evm/message/message_test.go +++ b/plugin/evm/message/message_test.go @@ -20,7 +20,7 @@ func TestMarshalTxs(t *testing.T) { base64EthTxGossip := "AAAAAAAAAAAABGJsYWg=" msg := []byte("blah") - builtMsg := TxsGossip{ + builtMsg := EthTxsGossip{ Txs: msg, } builtMsgBytes, err := BuildGossipMessage(Codec, builtMsg) @@ -30,7 +30,7 @@ func TestMarshalTxs(t *testing.T) { parsedMsgIntf, err := ParseGossipMessage(Codec, builtMsgBytes) assert.NoError(err) - parsedMsg, ok := parsedMsgIntf.(TxsGossip) + parsedMsg, ok := parsedMsgIntf.(EthTxsGossip) assert.True(ok) assert.Equal(msg, parsedMsg.Txs) @@ -39,7 +39,7 @@ func TestMarshalTxs(t *testing.T) { func TestTxsTooLarge(t *testing.T) { assert := assert.New(t) - builtMsg := TxsGossip{ + builtMsg := EthTxsGossip{ Txs: utils.RandomBytes(1024 * units.KiB), } _, err := BuildGossipMessage(Codec, builtMsg) diff --git a/plugin/evm/syncervm_test.go b/plugin/evm/syncervm_test.go index a51b3e22e1..09c8155c6b 100644 --- a/plugin/evm/syncervm_test.go +++ b/plugin/evm/syncervm_test.go @@ -87,7 +87,7 @@ func TestStateSyncToggleEnabledToDisabled(t *testing.T) { reqCount++ // Fail all requests after number 50 to interrupt the sync if reqCount > 50 { - if err := syncerVM.AppRequestFailed(context.Background(), nodeID, requestID); err != nil { + if err := syncerVM.AppRequestFailed(context.Background(), nodeID, requestID, commonEng.ErrTimeout); err != nil { panic(err) } cancel := syncerVM.StateSyncClient.(*stateSyncerClient).cancel diff --git a/plugin/evm/tx_gossip_test.go b/plugin/evm/tx_gossip_test.go index b4815359a3..b0efc23717 100644 --- a/plugin/evm/tx_gossip_test.go +++ b/plugin/evm/tx_gossip_test.go @@ -5,18 +5,21 @@ package evm import ( "context" + "encoding/binary" "math/big" "sync" "testing" "time" + "github.com/ava-labs/avalanchego/database/memdb" "github.com/ava-labs/avalanchego/ids" "github.com/ava-labs/avalanchego/network/p2p" "github.com/ava-labs/avalanchego/network/p2p/gossip" "github.com/ava-labs/avalanchego/proto/pb/sdk" + "github.com/ava-labs/avalanchego/snow" "github.com/ava-labs/avalanchego/snow/engine/common" "github.com/ava-labs/avalanchego/snow/validators" - "github.com/ava-labs/avalanchego/utils" + agoUtils "github.com/ava-labs/avalanchego/utils" "github.com/ava-labs/avalanchego/utils/logging" "github.com/ava-labs/avalanchego/utils/set" "github.com/prometheus/client_golang/prometheus" @@ -25,66 +28,72 @@ import ( "google.golang.org/protobuf/proto" "github.com/ava-labs/subnet-evm/core/types" + "github.com/ava-labs/subnet-evm/utils" ) -func TestTxGossip(t *testing.T) { +func TestEthTxGossip(t *testing.T) { require := require.New(t) + ctx := context.Background() + snowCtx := utils.TestSnowContext() + validatorState := &validators.TestState{} + snowCtx.ValidatorState = validatorState + + responseSender := &common.FakeSender{ + SentAppResponse: make(chan []byte, 1), + } + vm := &VM{ + p2pSender: responseSender, + } + + require.NoError(vm.Initialize( + ctx, + snowCtx, + memdb.New(), + []byte(genesisJSONLatest), + nil, + nil, + make(chan common.Message), + nil, + &common.SenderTest{}, + )) + require.NoError(vm.SetState(ctx, snow.NormalOp)) - // set up prefunded address - _, vm, _, sender := GenesisVM(t, true, genesisJSONLatest, "", "") defer func() { - require.NoError(vm.Shutdown(context.Background())) + require.NoError(vm.Shutdown(ctx)) }() // sender for the peer requesting gossip from [vm] - peerSender := &common.SenderTest{} - router := p2p.NewNetwork(logging.NoLog{}, peerSender, prometheus.NewRegistry(), "") + peerSender := &common.FakeSender{ + SentAppRequest: make(chan []byte, 1), + } - // we're only making client requests, so we don't need a server handler - client, err := router.NewAppProtocol(txGossipProtocol, nil) + network, err := p2p.NewNetwork(logging.NoLog{}, peerSender, prometheus.NewRegistry(), "") require.NoError(err) + client := network.NewClient(ethTxGossipProtocol) - emptyBloomFilter, err := gossip.NewBloomFilter(txGossipBloomMaxItems, txGossipBloomFalsePositiveRate) - require.NoError(err) - emptyBloomFilterBytes, err := emptyBloomFilter.Bloom.MarshalBinary() + // we only accept gossip requests from validators + requestingNodeID := ids.GenerateTestNodeID() + require.NoError(vm.Network.Connected(ctx, requestingNodeID, nil)) + validatorState.GetCurrentHeightF = func(context.Context) (uint64, error) { + return 0, nil + } + validatorState.GetValidatorSetF = func(context.Context, uint64, ids.ID) (map[ids.NodeID]*validators.GetValidatorOutput, error) { + return map[ids.NodeID]*validators.GetValidatorOutput{requestingNodeID: nil}, nil + } + + // Ask the VM for any new transactions. We should get nothing at first. + emptyBloomFilter, err := gossip.NewBloomFilter(txGossipBloomMinTargetElements, txGossipBloomTargetFalsePositiveRate, txGossipBloomResetFalsePositiveRate) require.NoError(err) + emptyBloomFilterBytes, _ := emptyBloomFilter.Marshal() request := &sdk.PullGossipRequest{ Filter: emptyBloomFilterBytes, - Salt: utils.RandomBytes(32), + Salt: agoUtils.RandomBytes(32), } requestBytes, err := proto.Marshal(request) require.NoError(err) wg := &sync.WaitGroup{} - - requestingNodeID := ids.GenerateTestNodeID() - peerSender.SendAppRequestF = func(ctx context.Context, nodeIDs set.Set[ids.NodeID], requestID uint32, appRequestBytes []byte) error { - go func() { - require.NoError(vm.AppRequest(ctx, requestingNodeID, requestID, time.Time{}, appRequestBytes)) - }() - return nil - } - - sender.SendAppResponseF = func(ctx context.Context, nodeID ids.NodeID, requestID uint32, appResponseBytes []byte) error { - go func() { - require.NoError(router.AppResponse(ctx, nodeID, requestID, appResponseBytes)) - }() - return nil - } - - // we only accept gossip requests from validators - require.NoError(vm.Network.Connected(context.Background(), requestingNodeID, nil)) - mockValidatorSet, ok := vm.ctx.ValidatorState.(*validators.TestState) - require.True(ok) - mockValidatorSet.GetCurrentHeightF = func(context.Context) (uint64, error) { - return 0, nil - } - mockValidatorSet.GetValidatorSetF = func(context.Context, uint64, ids.ID) (map[ids.NodeID]*validators.GetValidatorOutput, error) { - return map[ids.NodeID]*validators.GetValidatorOutput{requestingNodeID: nil}, nil - } - - // Ask the VM for any new transactions. We should get nothing at first. wg.Add(1) onResponse := func(_ context.Context, nodeID ids.NodeID, responseBytes []byte, err error) { require.NoError(err) @@ -94,7 +103,9 @@ func TestTxGossip(t *testing.T) { require.Empty(response.Gossip) wg.Done() } - require.NoError(client.AppRequest(context.Background(), set.Set[ids.NodeID]{vm.ctx.NodeID: struct{}{}}, requestBytes, onResponse)) + require.NoError(client.AppRequest(ctx, set.Of(vm.ctx.NodeID), requestBytes, onResponse)) + require.NoError(vm.AppRequest(ctx, requestingNodeID, 1, time.Time{}, <-peerSender.SentAppRequest)) + require.NoError(network.AppResponse(ctx, snowCtx.NodeID, 1, <-responseSender.SentAppResponse)) wg.Wait() // Issue a tx to the VM @@ -111,6 +122,7 @@ func TestTxGossip(t *testing.T) { // wait so we aren't throttled by the vm time.Sleep(5 * time.Second) + marshaller := GossipEthTxMarshaller{} // Ask the VM for new transactions. We should get the newly issued tx. wg.Add(1) onResponse = func(_ context.Context, nodeID ids.NodeID, responseBytes []byte, err error) { @@ -120,12 +132,127 @@ func TestTxGossip(t *testing.T) { require.NoError(proto.Unmarshal(responseBytes, response)) require.Len(response.Gossip, 1) - gotTx := &GossipTx{} - require.NoError(gotTx.Unmarshal(response.Gossip[0])) + gotTx, err := marshaller.UnmarshalGossip(response.Gossip[0]) + require.NoError(err) require.Equal(signedTx.Hash(), gotTx.Tx.Hash()) wg.Done() } - require.NoError(client.AppRequest(context.Background(), set.Set[ids.NodeID]{vm.ctx.NodeID: struct{}{}}, requestBytes, onResponse)) + require.NoError(client.AppRequest(ctx, set.Of(vm.ctx.NodeID), requestBytes, onResponse)) + require.NoError(vm.AppRequest(ctx, requestingNodeID, 3, time.Time{}, <-peerSender.SentAppRequest)) + require.NoError(network.AppResponse(ctx, snowCtx.NodeID, 3, <-responseSender.SentAppResponse)) wg.Wait() } + +func TestEthTxPushGossipOutbound(t *testing.T) { + require := require.New(t) + ctx := context.Background() + snowCtx := utils.TestSnowContext() + sender := &common.FakeSender{ + SentAppGossip: make(chan []byte, 1), + } + + vm := &VM{ + p2pSender: sender, + ethTxPullGossiper: gossip.NoOpGossiper{}, + } + + require.NoError(vm.Initialize( + ctx, + snowCtx, + memdb.New(), + []byte(genesisJSONLatest), + nil, + nil, + make(chan common.Message), + nil, + &common.FakeSender{}, + )) + require.NoError(vm.SetState(ctx, snow.NormalOp)) + + address := testEthAddrs[0] + key := testKeys[0] + tx := types.NewTransaction(0, address, big.NewInt(10), 21000, big.NewInt(testMinGasPrice), nil) + signedTx, err := types.SignTx(tx, types.NewEIP155Signer(vm.chainConfig.ChainID), key) + require.NoError(err) + + // issue a tx + require.NoError(vm.txPool.AddLocal(signedTx)) + + sent := <-sender.SentAppGossip + got := &sdk.PushGossip{} + + // we should get a message that has the protocol prefix and the gossip + // message + require.Equal(byte(ethTxGossipProtocol), sent[0]) + require.NoError(proto.Unmarshal(sent[1:], got)) + + marshaller := GossipEthTxMarshaller{} + require.Len(got.Gossip, 1) + gossipedTx, err := marshaller.UnmarshalGossip(got.Gossip[0]) + require.NoError(err) + require.Equal(ids.ID(signedTx.Hash()), gossipedTx.GossipID()) +} + +// Tests that a gossiped tx is added to the mempool and forwarded +func TestEthTxPushGossipInbound(t *testing.T) { + require := require.New(t) + ctx := context.Background() + snowCtx := utils.TestSnowContext() + + sender := &common.FakeSender{ + SentAppGossip: make(chan []byte, 1), + } + vm := &VM{ + p2pSender: sender, + ethTxPullGossiper: gossip.NoOpGossiper{}, + } + + require.NoError(vm.Initialize( + ctx, + snowCtx, + memdb.New(), + []byte(genesisJSONLatest), + nil, + nil, + make(chan common.Message), + nil, + &common.FakeSender{}, + )) + require.NoError(vm.SetState(ctx, snow.NormalOp)) + + address := testEthAddrs[0] + key := testKeys[0] + tx := types.NewTransaction(0, address, big.NewInt(10), 21000, big.NewInt(testMinGasPrice), nil) + signedTx, err := types.SignTx(tx, types.NewEIP155Signer(vm.chainConfig.ChainID), key) + require.NoError(err) + + marshaller := GossipEthTxMarshaller{} + gossipedTx := &GossipEthTx{ + Tx: signedTx, + } + gossipedTxBytes, err := marshaller.MarshalGossip(gossipedTx) + require.NoError(err) + + inboundGossip := &sdk.PushGossip{ + Gossip: [][]byte{gossipedTxBytes}, + } + + inboundGossipBytes, err := proto.Marshal(inboundGossip) + require.NoError(err) + + 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 202e74d813..5ecba7d5d7 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.5.10" + Version string = "v0.5.11" ) func init() { diff --git a/plugin/evm/vm.go b/plugin/evm/vm.go index c3666dbb70..8c60da40a9 100644 --- a/plugin/evm/vm.go +++ b/plugin/evm/vm.go @@ -103,35 +103,27 @@ const ( chainStateMetricsPrefix = "chain_state" // p2p app protocols - txGossipProtocol = 0x0 + ethTxGossipProtocol = 0x0 // gossip constants - txGossipBloomMaxItems = 8 * 1024 - txGossipBloomFalsePositiveRate = 0.01 - txGossipMaxFalsePositiveRate = 0.05 - txGossipTargetResponseSize = 20 * units.KiB - maxValidatorSetStaleness = time.Minute - throttlingPeriod = 10 * time.Second - throttlingLimit = 2 - gossipFrequency = 10 * time.Second -) - -var ( - txGossipConfig = gossip.Config{ - Namespace: "eth_tx_gossip", - PollSize: 10, - } - txGossipHandlerConfig = gossip.HandlerConfig{ - Namespace: "eth_tx_gossip", - TargetResponseSize: txGossipTargetResponseSize, - } + txGossipBloomMinTargetElements = 8 * 1024 + txGossipBloomTargetFalsePositiveRate = 0.01 + txGossipBloomResetFalsePositiveRate = 0.05 + txGossipBloomChurnMultiplier = 3 + txGossipTargetMessageSize = 20 * units.KiB + maxValidatorSetStaleness = time.Minute + txGossipThrottlingPeriod = 10 * time.Second + txGossipThrottlingLimit = 2 + gossipFrequency = 10 * time.Second + txGossipPollSize = 10 ) // Define the API endpoints for the VM const ( - adminEndpoint = "/admin" - ethRPCEndpoint = "/rpc" - ethWSEndpoint = "/ws" + adminEndpoint = "/admin" + ethRPCEndpoint = "/rpc" + ethWSEndpoint = "/ws" + ethTxGossipNamespace = "eth_tx_gossip" ) var ( @@ -248,6 +240,11 @@ type VM struct { // Avalanche Warp Messaging backend // Used to serve BLS signatures of warp messages over RPC warpBackend warp.Backend + // Initialize only sets these if nil so they can be overridden in tests + p2pSender commonEng.AppSender + ethTxGossipHandler p2p.Handler + ethTxPullGossiper gossip.Gossiper + ethTxPushGossiper gossip.Accumulator[*GossipEthTx] } // Initialize implements the snowman.ChainVM interface @@ -459,7 +456,14 @@ func (vm *VM) Initialize( } // initialize peer network - p2pNetwork := p2p.NewNetwork(vm.ctx.Log, appSender, vm.sdkMetrics, "p2p") + if vm.p2pSender == nil { + vm.p2pSender = appSender + } + + p2pNetwork, err := p2p.NewNetwork(vm.ctx.Log, vm.p2pSender, vm.sdkMetrics, "p2p") + if err != nil { + return fmt.Errorf("failed to initialize p2p network: %w", err) + } vm.validators = p2p.NewValidators(p2pNetwork.Peers, vm.ctx.Log, vm.ctx.SubnetID, vm.ctx.ValidatorState, maxValidatorSetStaleness) vm.networkCodec = message.Codec vm.Network = peer.NewNetwork(p2pNetwork, appSender, vm.networkCodec, message.CrossChainCodec, chainCtx.NodeID, vm.config.MaxOutboundActiveRequests, vm.config.MaxOutboundActiveCrossChainRequests) @@ -663,60 +667,78 @@ func (vm *VM) initBlockBuilding() error { ctx, cancel := context.WithCancel(context.TODO()) 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) + } + + if vm.ethTxPushGossiper == nil { + vm.ethTxPushGossiper = gossip.NewPushGossiper[*GossipEthTx]( + ethTxGossipMarshaller, + ethTxGossipClient, + ethTxGossipMetrics, + txGossipTargetMessageSize, + ) + } + // NOTE: gossip network must be initialized first otherwise ETH tx gossip will not work. gossipStats := NewGossipStats() - vm.gossiper = vm.createGossiper(gossipStats) + vm.gossiper = vm.createGossiper(gossipStats, vm.ethTxPushGossiper) vm.builder = vm.NewBlockBuilder(vm.toEngine) vm.builder.awaitSubmittedTxs() vm.Network.SetGossipHandler(NewGossipHandler(vm, gossipStats)) - txPool, err := NewGossipTxPool(vm.txPool) + ethTxPool, err := NewGossipEthTxPool(vm.txPool) if err != nil { return err } vm.shutdownWg.Add(1) go func() { - txPool.Subscribe(ctx) + ethTxPool.Subscribe(ctx) vm.shutdownWg.Done() }() - var txGossipHandler p2p.Handler - - txGossipHandler, err = gossip.NewHandler[*GossipTx](txPool, txGossipHandlerConfig, vm.sdkMetrics) - if err != nil { - return err - } - txGossipHandler = &p2p.ValidatorHandler{ - ValidatorSet: vm.validators, - Handler: &p2p.ThrottlerHandler{ - Throttler: p2p.NewSlidingWindowThrottler(throttlingPeriod, throttlingLimit), - Handler: txGossipHandler, - }, - } - txGossipClient, err := vm.Network.NewAppProtocol(txGossipProtocol, txGossipHandler, p2p.WithValidatorSampling(vm.validators)) - if err != nil { - return err + if vm.ethTxGossipHandler == nil { + vm.ethTxGossipHandler = newTxGossipHandler[*GossipEthTx]( + vm.ctx.Log, + ethTxGossipMarshaller, + ethTxPool, + ethTxGossipMetrics, + txGossipTargetMessageSize, + txGossipThrottlingPeriod, + txGossipThrottlingLimit, + vm.validators, + ) } - var ethTxGossiper gossip.Gossiper - ethTxGossiper, err = gossip.NewPullGossiper[GossipTx, *GossipTx]( - txGossipConfig, - vm.ctx.Log, - txPool, - txGossipClient, - vm.sdkMetrics, - ) - if err != nil { + + if err := vm.Network.AddHandler(ethTxGossipProtocol, vm.ethTxGossipHandler); err != nil { return err } - txGossiper := gossip.ValidatorGossiper{ - Gossiper: ethTxGossiper, - NodeID: vm.ctx.NodeID, - Validators: vm.validators, + + if vm.ethTxPullGossiper == nil { + ethTxPullGossiper := gossip.NewPullGossiper[*GossipEthTx]( + vm.ctx.Log, + ethTxGossipMarshaller, + ethTxPool, + ethTxGossipClient, + ethTxGossipMetrics, + txGossipPollSize, + ) + + vm.ethTxPullGossiper = gossip.ValidatorGossiper{ + Gossiper: ethTxPullGossiper, + NodeID: vm.ctx.NodeID, + Validators: vm.validators, + } } vm.shutdownWg.Add(1) go func() { - gossip.Every(ctx, vm.ctx.Log, txGossiper, gossipFrequency) + gossip.Every(ctx, vm.ctx.Log, vm.ethTxPullGossiper, gossipFrequency) vm.shutdownWg.Done() }() diff --git a/precompile/contract/mocks.go b/precompile/contract/mocks.go index 8a208a7e17..5ab8dbac26 100644 --- a/precompile/contract/mocks.go +++ b/precompile/contract/mocks.go @@ -1,5 +1,10 @@ // Code generated by MockGen. DO NOT EDIT. // Source: github.com/ava-labs/subnet-evm/precompile/contract (interfaces: BlockContext,AccessibleState) +// +// Generated by this command: +// +// mockgen -package=contract -destination=precompile/contract/mocks.go github.com/ava-labs/subnet-evm/precompile/contract BlockContext,AccessibleState +// // Package contract is a generated GoMock package. package contract @@ -46,7 +51,7 @@ func (m *MockBlockContext) GetPredicateResults(arg0 common.Hash, arg1 common.Add } // GetPredicateResults indicates an expected call of GetPredicateResults. -func (mr *MockBlockContextMockRecorder) GetPredicateResults(arg0, arg1 interface{}) *gomock.Call { +func (mr *MockBlockContextMockRecorder) GetPredicateResults(arg0, arg1 any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetPredicateResults", reflect.TypeOf((*MockBlockContext)(nil).GetPredicateResults), arg0, arg1) } diff --git a/precompile/contracts/warp/predicate_test.go b/precompile/contracts/warp/predicate_test.go index d2542976ff..2b293b97a9 100644 --- a/precompile/contracts/warp/predicate_test.go +++ b/precompile/contracts/warp/predicate_test.go @@ -107,8 +107,8 @@ type testValidator struct { vdr *avalancheWarp.Validator } -func (v *testValidator) Less(o *testValidator) bool { - return v.vdr.Less(o.vdr) +func (v *testValidator) Compare(o *testValidator) int { + return v.vdr.Compare(o.vdr) } func newTestValidator() *testValidator { diff --git a/precompile/precompileconfig/mocks.go b/precompile/precompileconfig/mocks.go index 8d425daefa..614ec5a522 100644 --- a/precompile/precompileconfig/mocks.go +++ b/precompile/precompileconfig/mocks.go @@ -1,5 +1,10 @@ // Code generated by MockGen. DO NOT EDIT. // Source: github.com/ava-labs/subnet-evm/precompile/precompileconfig (interfaces: Predicater,Config,ChainConfig,Accepter) +// +// Generated by this command: +// +// mockgen -package=precompileconfig -destination=precompile/precompileconfig/mocks.go github.com/ava-labs/subnet-evm/precompile/precompileconfig Predicater,Config,ChainConfig,Accepter +// // Package precompileconfig is a generated GoMock package. package precompileconfig @@ -45,7 +50,7 @@ func (m *MockPredicater) PredicateGas(arg0 []byte) (uint64, error) { } // PredicateGas indicates an expected call of PredicateGas. -func (mr *MockPredicaterMockRecorder) PredicateGas(arg0 interface{}) *gomock.Call { +func (mr *MockPredicaterMockRecorder) PredicateGas(arg0 any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "PredicateGas", reflect.TypeOf((*MockPredicater)(nil).PredicateGas), arg0) } @@ -59,7 +64,7 @@ func (m *MockPredicater) VerifyPredicate(arg0 *PredicateContext, arg1 []byte) er } // VerifyPredicate indicates an expected call of VerifyPredicate. -func (mr *MockPredicaterMockRecorder) VerifyPredicate(arg0, arg1 interface{}) *gomock.Call { +func (mr *MockPredicaterMockRecorder) VerifyPredicate(arg0, arg1 any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "VerifyPredicate", reflect.TypeOf((*MockPredicater)(nil).VerifyPredicate), arg0, arg1) } @@ -96,7 +101,7 @@ func (m *MockConfig) Equal(arg0 Config) bool { } // Equal indicates an expected call of Equal. -func (mr *MockConfigMockRecorder) Equal(arg0 interface{}) *gomock.Call { +func (mr *MockConfigMockRecorder) Equal(arg0 any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Equal", reflect.TypeOf((*MockConfig)(nil).Equal), arg0) } @@ -152,7 +157,7 @@ func (m *MockConfig) Verify(arg0 ChainConfig) error { } // Verify indicates an expected call of Verify. -func (mr *MockConfigMockRecorder) Verify(arg0 interface{}) *gomock.Call { +func (mr *MockConfigMockRecorder) Verify(arg0 any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Verify", reflect.TypeOf((*MockConfig)(nil).Verify), arg0) } @@ -217,7 +222,7 @@ func (m *MockChainConfig) IsDurango(arg0 uint64) bool { } // IsDurango indicates an expected call of IsDurango. -func (mr *MockChainConfigMockRecorder) IsDurango(arg0 interface{}) *gomock.Call { +func (mr *MockChainConfigMockRecorder) IsDurango(arg0 any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "IsDurango", reflect.TypeOf((*MockChainConfig)(nil).IsDurango), arg0) } @@ -254,7 +259,7 @@ func (m *MockAccepter) Accept(arg0 *AcceptContext, arg1 common.Hash, arg2 uint64 } // Accept indicates an expected call of Accept. -func (mr *MockAccepterMockRecorder) Accept(arg0, arg1, arg2, arg3, arg4, arg5, arg6 interface{}) *gomock.Call { +func (mr *MockAccepterMockRecorder) Accept(arg0, arg1, arg2, arg3, arg4, arg5, arg6 any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Accept", reflect.TypeOf((*MockAccepter)(nil).Accept), arg0, arg1, arg2, arg3, arg4, arg5, arg6) } diff --git a/predicate/predicate_results.go b/predicate/predicate_results.go index f87b181f44..c28d811fe4 100644 --- a/predicate/predicate_results.go +++ b/predicate/predicate_results.go @@ -6,6 +6,7 @@ package predicate import ( "fmt" "strings" + "time" "github.com/ava-labs/avalanchego/codec" "github.com/ava-labs/avalanchego/codec/linearcodec" @@ -24,7 +25,7 @@ var Codec codec.Manager func init() { Codec = codec.NewManager(MaxResultsSize) - c := linearcodec.NewDefault() + c := linearcodec.NewDefault(time.Time{}) errs := wrappers.Errs{} errs.Add( c.RegisterType(Results{}), diff --git a/scripts/mock.gen.sh b/scripts/mock.gen.sh index aa548f4cba..ae25b3f587 100755 --- a/scripts/mock.gen.sh +++ b/scripts/mock.gen.sh @@ -13,11 +13,8 @@ if ! [[ "$0" =~ scripts/mock.gen.sh ]]; then exit 255 fi -if ! command -v mockgen &>/dev/null; then - echo "mockgen not found, installing..." - # https://github.com/uber-go/mock - go install -v go.uber.org/mock/mockgen@v0.2.0 -fi +# https://github.com/uber-go/mock +go install -v go.uber.org/mock/mockgen@v0.4.0 if ! command -v go-license &>/dev/null; then echo "go-license not found, installing..." diff --git a/scripts/run_ginkgo_simulator.sh b/scripts/run_ginkgo_simulator.sh new file mode 100755 index 0000000000..16f36cf398 --- /dev/null +++ b/scripts/run_ginkgo_simulator.sh @@ -0,0 +1,28 @@ +#!/usr/bin/env bash +set -e + +# This script assumes that an AvalancheGo and Subnet-EVM binaries are available in the standard location +# within the $GOPATH +# The AvalancheGo and PluginDir paths can be specified via the environment variables used in ./scripts/run.sh. + +# Load the versions +SUBNET_EVM_PATH=$( + cd "$(dirname "${BASH_SOURCE[0]}")" + cd .. && pwd +) + +source "$SUBNET_EVM_PATH"/scripts/constants.sh + +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} + +TEST_SOURCE_ROOT=$(pwd) + +ACK_GINKGO_RC=true ginkgo build ./tests/load + +./tests/load/load.test \ + --ginkgo.vv \ + --ginkgo.label-filter=${GINKGO_LABEL_FILTER:-""} diff --git a/scripts/versions.sh b/scripts/versions.sh index f9125a2baf..d773bf1459 100644 --- a/scripts/versions.sh +++ b/scripts/versions.sh @@ -1,7 +1,7 @@ #!/usr/bin/env bash # Don't export them as they're used in the context of other calls -AVALANCHE_VERSION=${AVALANCHE_VERSION:-'v1.10.17'} +AVALANCHE_VERSION=${AVALANCHE_VERSION:-'v1.10.18'} GINKGO_VERSION=${GINKGO_VERSION:-'v2.2.0'} # This won't be used, but it's here to make code syncs easier diff --git a/sync/client/client.go b/sync/client/client.go index 5168aa8dda..001989f934 100644 --- a/sync/client/client.go +++ b/sync/client/client.go @@ -351,7 +351,7 @@ func (c *client) get(ctx context.Context, request message.Request, parseFn parse responseIntf, numElements, err = parseFn(c.codec, request, response) if err != nil { lastErr = err - log.Info("could not validate response, retrying", "nodeID", nodeID, "attempt", attempt, "request", request, "err", err) + log.Debug("could not validate response, retrying", "nodeID", nodeID, "attempt", attempt, "request", request, "err", err) c.networkClient.TrackBandwidth(nodeID, 0) metric.IncFailed() metric.IncInvalidResponse() diff --git a/tests/utils/runner/network_manager.go b/tests/utils/runner/network_manager.go index ba0cf1e668..138ce73034 100644 --- a/tests/utils/runner/network_manager.go +++ b/tests/utils/runner/network_manager.go @@ -66,7 +66,7 @@ func NewDefaultANRConfig() ANRConfig { 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-display-level":"info", + "log-level":"info", "proposervm-use-current-height":true }`, GlobalCChainConfig: `{