From 2fdd559275d5e107ccd882c16c35ea26b9b553bc Mon Sep 17 00:00:00 2001 From: Joshua Kim <20001595+joshua-kim@users.noreply.github.com> Date: Tue, 6 Jun 2023 11:22:36 -0400 Subject: [PATCH 01/36] evm gossip --- core/txpool/txpool.go | 13 + go.mod | 4 +- go.sum | 13 + plugin/evm/gossip_mempool.go | 156 ++++++++++++ plugin/evm/gossip_mempool_test.go | 86 +++++++ plugin/evm/gossiper_atomic_gossiping_test.go | 8 +- plugin/evm/mempool.go | 55 +++- plugin/evm/mempool_atomic_gossiping_test.go | 12 +- plugin/evm/mempool_test.go | 35 +++ plugin/evm/tx_gossip_test.go | 252 +++++++++++++++++++ plugin/evm/tx_test.go | 4 +- plugin/evm/vm.go | 102 +++++++- 12 files changed, 720 insertions(+), 20 deletions(-) create mode 100644 plugin/evm/gossip_mempool.go create mode 100644 plugin/evm/gossip_mempool_test.go create mode 100644 plugin/evm/mempool_test.go create mode 100644 plugin/evm/tx_gossip_test.go diff --git a/core/txpool/txpool.go b/core/txpool/txpool.go index 29be40f192..5095396794 100644 --- a/core/txpool/txpool.go +++ b/core/txpool/txpool.go @@ -630,6 +630,19 @@ func (pool *TxPool) PendingSize() int { return count } +// IteratePending iterates over the [pool.pending] until [f] returns false. +func (pool *TxPool) IteratePending(f func(tx *types.Transaction) bool) { + pending := pool.Pending(true) + + for _, list := range pending { + for _, tx := range list { + if !f(tx) { + return + } + } + } +} + // Locals retrieves the accounts currently considered local by the pool. func (pool *TxPool) Locals() []common.Address { pool.mu.Lock() diff --git a/go.mod b/go.mod index ed7007a6f0..10ced6c0fd 100644 --- a/go.mod +++ b/go.mod @@ -4,7 +4,7 @@ go 1.19 require ( github.com/VictoriaMetrics/fastcache v1.10.0 - github.com/ava-labs/avalanchego v1.10.9-rc.4 + github.com/ava-labs/avalanchego v1.10.10-rc.0 github.com/cespare/cp v0.1.0 github.com/cockroachdb/pebble v0.0.0-20230209160836-829675f94811 github.com/davecgh/go-spew v1.1.1 @@ -44,6 +44,7 @@ require ( golang.org/x/sys v0.8.0 golang.org/x/text v0.8.0 golang.org/x/time v0.0.0-20220922220347-f3bd1da661af + google.golang.org/protobuf v1.30.0 gopkg.in/natefinch/lumberjack.v2 v2.0.0 ) @@ -134,7 +135,6 @@ require ( gonum.org/v1/gonum v0.11.0 // indirect google.golang.org/genproto v0.0.0-20230306155012-7f2fa6fef1f4 // indirect google.golang.org/grpc v1.55.0 // indirect - google.golang.org/protobuf v1.30.0 // indirect gopkg.in/ini.v1 v1.67.0 // indirect gopkg.in/natefinch/npipe.v2 v2.0.0-20160621034901-c1b8fa8bdcce // indirect gopkg.in/yaml.v2 v2.4.0 // indirect diff --git a/go.sum b/go.sum index 651ec8cf22..e83a87d369 100644 --- a/go.sum +++ b/go.sum @@ -51,12 +51,16 @@ github.com/VictoriaMetrics/fastcache v1.10.0 h1:5hDJnLsKLpnUEToub7ETuRu8RCkb40wo github.com/VictoriaMetrics/fastcache v1.10.0/go.mod h1:tjiYeEfYXCqacuvYw/7UoDIeJaNxq6132xHICNP77w8= github.com/aead/siphash v1.0.1/go.mod h1:Nywa3cDsYNNK3gaciGTWPwHt0wlpNV15vwmswBAUSII= github.com/ajg/form v1.5.1/go.mod h1:uL1WgH+h2mgNtvBq0339dVnzXdBETtL2LeUXaIv25UY= +github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= +github.com/alecthomas/units v0.0.0-20190924025748-f65c72e2690d/go.mod h1:rBZYJk541a8SKzHPHnH3zbiI+7dagKZ0cgpgrD7Fyho= github.com/allegro/bigcache v1.2.1-0.20190218064605-e24eb225f156 h1:eMwmnE/GDgah4HI848JfFxHt+iPb26b4zyfspmqY0/8= github.com/allegro/bigcache v1.2.1-0.20190218064605-e24eb225f156/go.mod h1:Cb/ax3seSYIx7SuZdm2G2xzfwmv3TPSk2ucNfQESPXM= github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY= github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5doyWs3UAsr3K4I6qtAmlQcZDesFNEHPZAzj8= github.com/ava-labs/avalanchego v1.10.9-rc.4 h1:vtavPfRiF6r1Zc6RV8/arEfVpe9GQsLWHbMfIWkHbMI= github.com/ava-labs/avalanchego v1.10.9-rc.4/go.mod h1:vTBLl1zK36olfLRA7IUfdbvphWqlkuarIoXxvZTHZVw= +github.com/ava-labs/avalanchego v1.10.10-rc.0 h1:6VjkpwhAJ0tDNJK+UIUD8WIb5VelgH3w61mgk7JAkDQ= +github.com/ava-labs/avalanchego v1.10.10-rc.0/go.mod h1:C8R5uiltpc8MQ62ixxgODR+15mesWF0aAw3H+Qrl9Iw= github.com/aymerick/raymond v2.0.3-0.20180322193309-b565731e1464+incompatible/go.mod h1:osfaiScAUVup+UC9Nfq76eWqDhXlp+4UYaA8uhTBO6g= github.com/benbjohnson/clock v1.3.0 h1:ip6w0uFQkncKQ979AypyG0ER7mqUSBdKLOgAle/AT8A= github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= @@ -123,6 +127,7 @@ github.com/cockroachdb/pebble v0.0.0-20230209160836-829675f94811 h1:ytcWPaNPhNoG github.com/cockroachdb/pebble v0.0.0-20230209160836-829675f94811/go.mod h1:Nb5lgvnQ2+oGlE/EyZy4+2/CxRh9KfvCXnag1vtpxVM= github.com/cockroachdb/redact v1.1.3 h1:AKZds10rFSIj7qADf0g46UixK8NNLwWTNdCIGS5wfSQ= github.com/cockroachdb/redact v1.1.3/go.mod h1:BVNblN9mBWFyMyqK1k3AAiSxhvhfK2oOZZ2lK+dpvRg= +github.com/cockroachdb/sentry-go v0.6.1-cockroachdb.2/go.mod h1:8BT+cPK6xvFOcRlk0R8eg+OTkcqI6baNH4xAkpiYVvQ= 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= @@ -209,6 +214,7 @@ github.com/go-errors/errors v1.4.2 h1:J6MZopCL4uSllY1OfXM374weqZFFItUbrImctkmUxI github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU= 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-logfmt/logfmt v0.5.1/go.mod h1:WYhtIu8zTZfxdn5+rREduYbwxfcBr/Vr6KEVveWlfTs= 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= @@ -370,13 +376,16 @@ github.com/jackpal/go-nat-pmp v1.0.2 h1:KzKSgb7qkJvOUTqYl9/Hg/me3pWgBmERKrTGD7Bd github.com/jackpal/go-nat-pmp v1.0.2/go.mod h1:QPH045xvCAeXUZOxsnwmrtiCoxIr9eob+4orBN1SBKc= github.com/jessevdk/go-flags v0.0.0-20141203071132-1679536dcc89/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI= github.com/jessevdk/go-flags v1.4.0/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI= +github.com/jpillora/backoff v1.0.0/go.mod h1:J/6gKK9jxlEcS3zixgDgUAsiuZ7yrSoa/FX5e0EB2j4= github.com/jrick/logrotate v1.0.0/go.mod h1:LNinyqDIJnpAur+b8yyulnQw/wDuN1+BYKlTRt3OuAQ= github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU= github.com/json-iterator/go v1.1.9/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= +github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo= github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU= github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/XSXhF0NWZEnDohbsk= github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU= github.com/julienschmidt/httprouter v1.3.0 h1:U0609e9tgbseu3rBINet9P48AI/D3oJs4dN7jwJOQ1U= +github.com/julienschmidt/httprouter v1.3.0/go.mod h1:JR6WtHb+2LUe8TCKY3cZOxFyyO8IZAc4RVcycCCAKdM= github.com/k0kubun/colorstring v0.0.0-20150214042306-9440f1994b88/go.mod h1:3w7q1U84EfirKl04SVQ/s7nPm1ZPhiXd34z40TNz36k= github.com/kataras/golog v0.0.10/go.mod h1:yJ8YKCmyL+nWjERB90Qwn+bdyBZsaQwU3bTVFgkFIp8= github.com/kataras/iris/v12 v12.1.8/go.mod h1:LMYy4VlP67TQ3Zgriz8RE2h2kMZV2SgMYbq3UhfoFmE= @@ -407,6 +416,7 @@ github.com/labstack/echo/v4 v4.2.1/go.mod h1:AA49e0DZ8kk5jTOOCKNuPR6oTnBS0dYiM4F github.com/labstack/echo/v4 v4.5.0/go.mod h1:czIriw4a0C1dFun+ObrXp7ok03xON0N1awStJ6ArI7Y= github.com/labstack/gommon v0.3.0/go.mod h1:MULnywXg0yavhxWKc+lOruYdAhDwPK9wf0OL7NoOu+k= github.com/leanovate/gopter v0.2.9 h1:fQjYxZaynp97ozCzfOyOuAGOU4aU/z37zf/tOujFk7c= +github.com/leanovate/gopter v0.2.9/go.mod h1:U2L/78B+KVFIx2VmW6onHJQzXtFb+p5y3y2Sh+Jxxv8= github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ= github.com/magiconair/properties v1.8.6 h1:5ibWZ6iY0NctNGWo87LalDlEZ6R41TqbbDamhfG/Qzo= github.com/magiconair/properties v1.8.6/go.mod h1:y3VJvCyxH9uVvJTWEGAELF3aiYNyPKd5NZ3oSwXrF60= @@ -448,9 +458,11 @@ github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJ github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= +github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= github.com/moul/http2curl v1.0.0/go.mod h1:8UbvGypXm98wA/IqH45anm5Y2Z6ep6O31QGOAZ3H0fQ= github.com/mr-tron/base58 v1.2.0 h1:T/HDJBh4ZCPbU39/+c3rRvE0uKBQlU27+QI8LJ4t64o= github.com/mr-tron/base58 v1.2.0/go.mod h1:BinMc/sQntlIE1frQmRFPUoPA1Zkr8VRgBdjWI2mNwc= +github.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= github.com/nats-io/jwt v0.3.0/go.mod h1:fRYCDE99xlTsqUzISS1Bi75UBJ6ljOJQOAAu5VglpSg= github.com/nats-io/nats.go v1.9.1/go.mod h1:ZjDU1L/7fJ09jvUSRVBR2e7+RnLiiIQyqyzEE/Zbp4w= github.com/nats-io/nkeys v0.1.0/go.mod h1:xpnFELMwJABBLVhffcfd1MZx6VsNRFpEugbxziKVo7w= @@ -1023,6 +1035,7 @@ google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQ google.golang.org/protobuf v1.27.1/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= google.golang.org/protobuf v1.30.0 h1:kPPoIgf3TsEvrm0PFe15JQ+570QVxYzEvvHqChK+cng= google.golang.org/protobuf v1.30.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= +gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= diff --git a/plugin/evm/gossip_mempool.go b/plugin/evm/gossip_mempool.go new file mode 100644 index 0000000000..c14e67f67f --- /dev/null +++ b/plugin/evm/gossip_mempool.go @@ -0,0 +1,156 @@ +// Copyright (C) 2019-2023, Ava Labs, Inc. All rights reserved. +// See the file LICENSE for licensing terms. + +package evm + +import ( + "fmt" + "sync" + + "github.com/ethereum/go-ethereum/log" + + "github.com/ava-labs/avalanchego/network/p2p/gossip" + + "github.com/ava-labs/coreth/core" + "github.com/ava-labs/coreth/core/txpool" + "github.com/ava-labs/coreth/core/types" + "github.com/ava-labs/coreth/plugin/evm/message" +) + +var ( + _ gossip.Gossipable = (*GossipEthTx)(nil) + _ gossip.Gossipable = (*GossipAtomicTx)(nil) + _ gossip.Set[*GossipEthTx] = (*GossipEthTxPool)(nil) +) + +type GossipAtomicTx struct { + Tx *Tx `serialize:"true"` +} + +func (tx *GossipAtomicTx) GetHash() gossip.Hash { + id := tx.Tx.ID() + hash := gossip.Hash{} + copy(hash[:], id[:]) + + return hash +} + +func (tx *GossipAtomicTx) Marshal() ([]byte, error) { + return Codec.Marshal(message.Version, tx) +} + +func (tx *GossipAtomicTx) Unmarshal(bytes []byte) error { + _, err := Codec.Unmarshal(bytes, tx) + return err +} + +func NewGossipEthTxPool(mempool *txpool.TxPool) (*GossipEthTxPool, error) { + bloom, err := gossip.NewBloomFilter(txGossipBloomMaxItems, txGossipBloomFalsePositiveRate) + 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(shutdownChan chan struct{}, shutdownWg *sync.WaitGroup) { + defer shutdownWg.Done() + g.mempool.SubscribeNewTxsEvent(g.pendingTxs) + + for { + select { + case <-shutdownChan: + log.Debug("shutting down subscription") + return + case pendingTxs := <-g.pendingTxs: + g.lock.Lock() + for _, pendingTx := range pendingTxs.Txs { + tx := &GossipEthTx{Tx: pendingTx} + g.bloom.Add(tx) + if gossip.ResetBloomFilterIfNeeded(g.bloom, txGossipBloomMaxFilledRatio) { + log.Debug("resetting bloom filter", "reason", "reached max filled ratio") + + pending := g.mempool.Pending(false) + for _, pendingTxs := range pending { + for _, pendingTx := range pendingTxs { + g.bloom.Add(&GossipEthTx{Tx: pendingTx}) + } + } + } + } + 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 { + if err := g.mempool.AddRemotes([]*types.Transaction{tx.Tx})[0]; err != nil { + return err + } + + return nil +} + +func (g *GossipEthTxPool) Get(filter func(tx *GossipEthTx) bool) []*GossipEthTx { + limit := 1000 + resultSize := 0 + result := make([]*GossipEthTx, 0) + + g.mempool.IteratePending(func(tx *types.Transaction) bool { + resultSize += int(tx.Size()) + if resultSize > limit { + return false + } + + gossipTx := &GossipEthTx{ + Tx: tx, + } + result = append(result, gossipTx) + return true + }) + + return result +} + +func (g *GossipEthTxPool) GetFilter() ([]byte, []byte, error) { + g.lock.RLock() + defer g.lock.RUnlock() + + bloom, err := g.bloom.Bloom.MarshalBinary() + return bloom, g.bloom.Salt[:], err +} + +type GossipEthTx struct { + Tx *types.Transaction +} + +func (tx *GossipEthTx) GetHash() gossip.Hash { + txHash := tx.Tx.Hash() + hash := gossip.Hash{} + copy(hash[:], txHash[:]) + + return hash +} + +func (tx *GossipEthTx) Marshal() ([]byte, error) { + return tx.Tx.MarshalBinary() +} + +func (tx *GossipEthTx) Unmarshal(bytes []byte) error { + tx.Tx = &types.Transaction{} + return tx.Tx.UnmarshalBinary(bytes) +} diff --git a/plugin/evm/gossip_mempool_test.go b/plugin/evm/gossip_mempool_test.go new file mode 100644 index 0000000000..f5dbb3a697 --- /dev/null +++ b/plugin/evm/gossip_mempool_test.go @@ -0,0 +1,86 @@ +// Copyright (C) 2019-2023, Ava Labs, Inc. All rights reserved. +// See the file LICENSE for licensing terms. + +package evm + +import ( + "testing" + + "github.com/stretchr/testify/require" + + "github.com/ava-labs/avalanchego/ids" +) + +func TestAtomicMempoolAddTx(t *testing.T) { + txs := []*GossipAtomicTx{ + { + Tx: &Tx{ + UnsignedAtomicTx: &TestUnsignedTx{ + IDV: ids.GenerateTestID(), + }, + }, + }, + { + Tx: &Tx{ + UnsignedAtomicTx: &TestUnsignedTx{ + IDV: ids.GenerateTestID(), + }, + }, + }, + } + + tests := []struct { + name string + add []*GossipAtomicTx + filter func(tx *GossipAtomicTx) bool + expected []*GossipAtomicTx + }{ + { + name: "empty", + }, + { + name: "filter matches nothing", + add: txs, + filter: func(*GossipAtomicTx) bool { + return false + }, + expected: nil, + }, + { + name: "filter matches all", + add: txs, + filter: func(*GossipAtomicTx) bool { + return true + }, + expected: txs, + }, + { + name: "filter matches subset", + add: txs, + filter: func(tx *GossipAtomicTx) bool { + return tx.Tx == txs[0].Tx + }, + expected: txs[:1], + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + require := require.New(t) + + m, err := NewMempool(ids.Empty, 10) + require.NoError(err) + + for _, add := range tt.add { + require.NoError(m.Add(add)) + } + + txs := m.Get(tt.filter) + require.Len(txs, len(tt.expected)) + + for _, expected := range tt.expected { + require.Contains(txs, expected) + } + }) + } +} diff --git a/plugin/evm/gossiper_atomic_gossiping_test.go b/plugin/evm/gossiper_atomic_gossiping_test.go index 73e3ce17b3..6ded11967b 100644 --- a/plugin/evm/gossiper_atomic_gossiping_test.go +++ b/plugin/evm/gossiper_atomic_gossiping_test.go @@ -11,8 +11,8 @@ import ( "time" "github.com/ava-labs/avalanchego/ids" + "github.com/ava-labs/avalanchego/snow" "github.com/ava-labs/avalanchego/utils/set" - "github.com/stretchr/testify/assert" "github.com/ava-labs/coreth/plugin/evm/message" @@ -22,10 +22,11 @@ import ( func TestMempoolAtmTxsIssueTxAndGossiping(t *testing.T) { assert := assert.New(t) - _, vm, _, sharedMemory, sender := GenesisVM(t, true, "", "", "") + _, vm, _, sharedMemory, sender := GenesisVM(t, false, "", "", "") defer func() { assert.NoError(vm.Shutdown(context.Background())) }() + assert.NoError(vm.Connected(context.Background(), ids.GenerateTestNodeID(), nil)) // Create conflicting transactions importTxs := createImportTxOptions(t, vm, sharedMemory) @@ -56,12 +57,15 @@ func TestMempoolAtmTxsIssueTxAndGossiping(t *testing.T) { return nil } + assert.NoError(vm.SetState(context.Background(), snow.NormalOp)) + // Optimistically gossip raw tx assert.NoError(vm.issueTx(tx, true /*=local*/)) time.Sleep(500 * time.Millisecond) gossipedLock.Lock() assert.Equal(1, gossiped) gossipedLock.Unlock() + assert.True(vm.mempool.bloom.Has(&GossipAtomicTx{Tx: tx})) // Test hash on retry assert.NoError(vm.gossiper.GossipAtomicTxs([]*Tx{tx})) diff --git a/plugin/evm/mempool.go b/plugin/evm/mempool.go index 25b67298f4..97e8ac5b2e 100644 --- a/plugin/evm/mempool.go +++ b/plugin/evm/mempool.go @@ -8,10 +8,13 @@ import ( "fmt" "sync" + "github.com/ethereum/go-ethereum/log" + "github.com/ava-labs/avalanchego/cache" "github.com/ava-labs/avalanchego/ids" + "github.com/ava-labs/avalanchego/network/p2p/gossip" + "github.com/ava-labs/coreth/metrics" - "github.com/ethereum/go-ethereum/log" ) const ( @@ -69,12 +72,19 @@ type Mempool struct { txHeap *txHeap // utxoSpenders maps utxoIDs to the transaction consuming them in the mempool utxoSpenders map[ids.ID]*Tx + // bloom is a bloom filter containing the txs in the mempool + bloom *gossip.BloomFilter metrics *mempoolMetrics } // NewMempool returns a Mempool with [maxSize] -func NewMempool(AVAXAssetID ids.ID, maxSize int) *Mempool { +func NewMempool(AVAXAssetID ids.ID, maxSize int) (*Mempool, error) { + bloom, err := gossip.NewBloomFilter(txGossipBloomMaxItems, txGossipBloomFalsePositiveRate) + if err != nil { + return nil, fmt.Errorf("failed to initialize bloom filter: %w", err) + } + return &Mempool{ AVAXAssetID: AVAXAssetID, issuedTxs: make(map[ids.ID]*Tx), @@ -84,8 +94,9 @@ func NewMempool(AVAXAssetID ids.ID, maxSize int) *Mempool { txHeap: newTxHeap(maxSize), maxSize: maxSize, utxoSpenders: make(map[ids.ID]*Tx), + bloom: bloom, metrics: newMempoolMetrics(), - } + }, nil } // Len returns the number of transactions in the mempool @@ -125,6 +136,10 @@ func (m *Mempool) atomicTxGasPrice(tx *Tx) (uint64, error) { return burned / gasUsed, nil } +func (m *Mempool) Add(tx *GossipAtomicTx) error { + return m.AddTx(tx.Tx) +} + // Add attempts to add [tx] to the mempool and returns an error if // it could not be addeed to the mempool. func (m *Mempool) AddTx(tx *Tx) error { @@ -266,9 +281,43 @@ func (m *Mempool) addTx(tx *Tx, force bool) error { // and CancelCurrentTx. m.newTxs = append(m.newTxs, tx) m.addPending() + + m.bloom.Add(&GossipAtomicTx{Tx: tx}) + if gossip.ResetBloomFilterIfNeeded(m.bloom, txGossipBloomMaxFilledRatio) { + log.Debug("resetting bloom filter", "reason", "reached max filled ratio") + + for _, pendingTx := range m.txHeap.minHeap.items { + m.bloom.Add(&GossipAtomicTx{Tx: pendingTx.tx}) + } + } + return nil } +func (m *Mempool) Get(filter func(tx *GossipAtomicTx) bool) []*GossipAtomicTx { + m.lock.RLock() + defer m.lock.RUnlock() + + gossipTxs := make([]*GossipAtomicTx, 0, len(m.txHeap.maxHeap.items)) + for _, item := range m.txHeap.maxHeap.items { + gossipTx := &GossipAtomicTx{Tx: item.tx} + if !filter(gossipTx) { + continue + } + gossipTxs = append(gossipTxs, gossipTx) + } + + return gossipTxs +} + +func (m *Mempool) GetFilter() ([]byte, []byte, error) { + m.lock.RLock() + defer m.lock.RUnlock() + + bloom, err := m.bloom.Bloom.MarshalBinary() + return bloom, m.bloom.Salt[:], err +} + // NextTx returns a transaction to be issued from the mempool. func (m *Mempool) NextTx() (*Tx, bool) { m.lock.Lock() diff --git a/plugin/evm/mempool_atomic_gossiping_test.go b/plugin/evm/mempool_atomic_gossiping_test.go index a0f82a8c01..a2e05e6286 100644 --- a/plugin/evm/mempool_atomic_gossiping_test.go +++ b/plugin/evm/mempool_atomic_gossiping_test.go @@ -108,13 +108,15 @@ func TestMempoolMaxMempoolSizeHandling(t *testing.T) { // shortcut to simulated almost filled mempool mempool.maxSize = 0 - assert.ErrorIs(mempool.AddTx(tx), errTooManyAtomicTx) + err := mempool.AddTx(tx) + assert.ErrorIs(err, errTooManyAtomicTx) assert.False(mempool.has(tx.ID())) // shortcut to simulated empty mempool mempool.maxSize = defaultMempoolSize - assert.NoError(mempool.AddTx(tx)) + err = mempool.AddTx(tx) + assert.NoError(err) assert.True(mempool.has(tx.ID())) } @@ -193,10 +195,12 @@ func TestMempoolPriorityDrop(t *testing.T) { mempool.maxSize = 1 tx1 := createImportTx(t, vm, ids.ID{1}, params.AvalancheAtomicTxFee) - assert.NoError(mempool.AddTx(tx1)) + err := mempool.AddTx(tx1) + assert.NoError(err) assert.True(mempool.has(tx1.ID())) tx2 := createImportTx(t, vm, ids.ID{2}, params.AvalancheAtomicTxFee) - assert.ErrorIs(mempool.AddTx(tx2), errInsufficientAtomicTxFee) + err = mempool.AddTx(tx2) + assert.ErrorIs(err, errInsufficientAtomicTxFee) assert.True(mempool.has(tx1.ID())) assert.False(mempool.has(tx2.ID())) tx3 := createImportTx(t, vm, ids.ID{3}, 2*params.AvalancheAtomicTxFee) diff --git a/plugin/evm/mempool_test.go b/plugin/evm/mempool_test.go new file mode 100644 index 0000000000..6d719a0ee2 --- /dev/null +++ b/plugin/evm/mempool_test.go @@ -0,0 +1,35 @@ +// Copyright (C) 2019-2023, Ava Labs, Inc. All rights reserved. +// See the file LICENSE for licensing terms. + +package evm + +import ( + "testing" + + "github.com/ava-labs/avalanchego/ids" + "github.com/stretchr/testify/require" +) + +func TestMempoolAddTx(t *testing.T) { + require := require.New(t) + m, err := NewMempool(ids.Empty, 5_000) + require.NoError(err) + + txs := make([]*GossipAtomicTx, 0) + for i := 0; i < 3_000; i++ { + tx := &GossipAtomicTx{ + Tx: &Tx{ + UnsignedAtomicTx: &TestUnsignedTx{ + IDV: ids.GenerateTestID(), + }, + }, + } + + txs = append(txs, tx) + require.NoError(m.Add(tx)) + } + + for _, tx := range txs { + require.True(m.bloom.Has(tx)) + } +} diff --git a/plugin/evm/tx_gossip_test.go b/plugin/evm/tx_gossip_test.go new file mode 100644 index 0000000000..70575d8e5c --- /dev/null +++ b/plugin/evm/tx_gossip_test.go @@ -0,0 +1,252 @@ +// Copyright (C) 2019-2023, Ava Labs, Inc. All rights reserved. +// See the file LICENSE for licensing terms. + +package evm + +import ( + "context" + "math/big" + "sync" + "testing" + "time" + + "github.com/ava-labs/avalanchego/p2p/gossip" + "github.com/ava-labs/avalanchego/p2p/gossip/proto/pb" + "github.com/stretchr/testify/require" + "go.uber.org/mock/gomock" + "google.golang.org/protobuf/proto" + + "github.com/ava-labs/avalanchego/ids" + "github.com/ava-labs/avalanchego/network/p2p" + commonEng "github.com/ava-labs/avalanchego/snow/engine/common" + "github.com/ava-labs/avalanchego/snow/validators" + "github.com/ava-labs/avalanchego/utils" + "github.com/ava-labs/avalanchego/utils/crypto/secp256k1" + "github.com/ava-labs/avalanchego/utils/logging" + "github.com/ava-labs/avalanchego/utils/set" + + "github.com/ava-labs/coreth/core" + "github.com/ava-labs/coreth/core/types" + "github.com/ava-labs/coreth/params" +) + +func TestEthTxGossip(t *testing.T) { + require := require.New(t) + + // set up prefunded address + importAmount := uint64(1_000_000_000) + issuer, vm, _, _, sender := GenesisVMWithUTXOs(t, true, genesisJSONLatest, "", "", map[ids.ShortID]uint64{ + testShortIDAddrs[0]: importAmount, + }) + defer func() { + require.NoError(vm.Shutdown(context.Background())) + }() + + importAccepted := make(chan core.NewTxPoolHeadEvent) + vm.txPool.SubscribeNewHeadEvent(importAccepted) + + importTx, err := vm.newImportTx(vm.ctx.XChainID, testEthAddrs[0], initialBaseFee, []*secp256k1.PrivateKey{testKeys[0]}) + require.NoError(err) + require.NoError(vm.issueTx(importTx, true)) + <-issuer + + blk, err := vm.BuildBlock(context.Background()) + require.NoError(err) + + require.NoError(blk.Verify(context.Background())) + require.NoError(vm.SetPreference(context.Background(), blk.ID())) + require.NoError(blk.Accept(context.Background())) + <-importAccepted + + // sender for the peer requesting gossip from [vm] + ctrl := gomock.NewController(t) + peerSender := commonEng.NewMockSender(ctrl) + router := p2p.NewRouter(logging.NoLog{}, peerSender) + + // we're only making client requests, so we don't need a server handler + client, err := router.RegisterAppProtocol(ethTxGossipProtocol, nil, nil) + require.NoError(err) + + emptyBloomFilter, err := gossip.NewBloomFilter(txGossipBloomMaxItems, txGossipBloomFalsePositiveRate) + require.NoError(err) + emptyBloomFilterBytes, err := emptyBloomFilter.Bloom.MarshalBinary() + require.NoError(err) + request := &pb.PullGossipRequest{ + Filter: emptyBloomFilterBytes, + Salt: utils.RandomBytes(32), + } + + requestBytes, err := proto.Marshal(request) + require.NoError(err) + + wg := &sync.WaitGroup{} + + requestingNodeID := ids.GenerateTestNodeID() + peerSender.EXPECT().SendAppRequest(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Do(func(ctx context.Context, nodeIDs set.Set[ids.NodeID], requestID uint32, appRequestBytes []byte) { + go func() { + require.NoError(vm.AppRequest(ctx, requestingNodeID, requestID, time.Time{}, appRequestBytes)) + }() + }).AnyTimes() + + 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 + 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(nodeID ids.NodeID, responseBytes []byte, err error) { + require.NoError(err) + + response := &pb.PullGossipResponse{} + require.NoError(proto.Unmarshal(responseBytes, response)) + require.Empty(response.Gossip) + wg.Done() + } + require.NoError(client.AppRequest(context.Background(), set.Set[ids.NodeID]{vm.ctx.NodeID: struct{}{}}, requestBytes, onResponse)) + wg.Wait() + + // Issue a tx to the VM + address := testEthAddrs[0] + key := testKeys[0].ToECDSA() + tx := types.NewTransaction(0, address, big.NewInt(10), 100_000, big.NewInt(params.LaunchMinGasPrice), nil) + signedTx, err := types.SignTx(tx, types.NewEIP155Signer(vm.chainID), key) + require.NoError(err) + + errs := vm.txPool.AddLocals([]*types.Transaction{signedTx}) + require.Len(errs, 1) + require.Nil(errs[0]) + + // wait so we aren't throttled by the vm + time.Sleep(throttlingPeriod / throttlingLimit) + + // Ask the VM for new transactions. We should get the newly issued tx. + wg.Add(1) + onResponse = func(nodeID ids.NodeID, responseBytes []byte, err error) { + require.NoError(err) + + response := &pb.PullGossipResponse{} + require.NoError(proto.Unmarshal(responseBytes, response)) + require.Len(response.Gossip, 1) + + gotTx := &GossipEthTx{} + require.NoError(gotTx.Unmarshal(response.Gossip[0])) + 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)) + wg.Wait() +} + +func TestAtomicTxGossip(t *testing.T) { + require := require.New(t) + + // set up prefunded address + importAmount := uint64(1_000_000_000) + issuer, vm, _, _, sender := GenesisVMWithUTXOs(t, true, genesisJSONApricotPhase0, "", "", map[ids.ShortID]uint64{ + testShortIDAddrs[0]: importAmount, + }) + + defer func() { + require.NoError(vm.Shutdown(context.Background())) + }() + + // sender for the peer requesting gossip from [vm] + ctrl := gomock.NewController(t) + peerSender := commonEng.NewMockSender(ctrl) + router := p2p.NewRouter(logging.NoLog{}, peerSender) + + // we're only making client requests, so we don't need a server handler + client, err := router.RegisterAppProtocol(atomicTxGossipProtocol, nil, nil) + require.NoError(err) + + emptyBloomFilter, err := gossip.NewBloomFilter(txGossipBloomMaxItems, txGossipBloomFalsePositiveRate) + require.NoError(err) + bloomBytes, err := emptyBloomFilter.Bloom.MarshalBinary() + require.NoError(err) + request := &pb.PullGossipRequest{ + Filter: bloomBytes, + Salt: emptyBloomFilter.Salt[:], + } + requestBytes, err := proto.Marshal(request) + require.NoError(err) + + requestingNodeID := ids.GenerateTestNodeID() + wg := &sync.WaitGroup{} + peerSender.EXPECT().SendAppRequest(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Do(func(ctx context.Context, nodeIDs set.Set[ids.NodeID], requestID uint32, appRequestBytes []byte) { + go func() { + require.NoError(vm.AppRequest(ctx, requestingNodeID, requestID, time.Time{}, appRequestBytes)) + }() + }).AnyTimes() + + 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 + 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(nodeID ids.NodeID, responseBytes []byte, err error) { + require.NoError(err) + + response := &pb.PullGossipResponse{} + require.NoError(proto.Unmarshal(responseBytes, response)) + require.Empty(response.Gossip) + wg.Done() + } + require.NoError(client.AppRequest(context.Background(), set.Set[ids.NodeID]{vm.ctx.NodeID: struct{}{}}, requestBytes, onResponse)) + wg.Wait() + + // issue a new tx to the vm + importTx, err := vm.newImportTx(vm.ctx.XChainID, testEthAddrs[0], initialBaseFee, []*secp256k1.PrivateKey{testKeys[0]}) + require.NoError(err) + + require.NoError(vm.issueTx(importTx, true /*=local*/)) + <-issuer + + // wait so we aren't throttled by the vm + time.Sleep(throttlingPeriod / throttlingLimit) + + // Ask the VM for new transactions. We should get the newly issued tx. + wg.Add(1) + onResponse = func(nodeID ids.NodeID, responseBytes []byte, err error) { + require.NoError(err) + + response := &pb.PullGossipResponse{} + require.NoError(proto.Unmarshal(responseBytes, response)) + require.Len(response.Gossip, 1) + + gotTx := &GossipAtomicTx{} + require.NoError(gotTx.Unmarshal(response.Gossip[0])) + require.Equal(importTx.InputUTXOs(), gotTx.Tx.InputUTXOs()) + + wg.Done() + } + require.NoError(client.AppRequest(context.Background(), set.Set[ids.NodeID]{vm.ctx.NodeID: struct{}{}}, requestBytes, onResponse)) + wg.Wait() +} diff --git a/plugin/evm/tx_test.go b/plugin/evm/tx_test.go index 3438f16296..769690d272 100644 --- a/plugin/evm/tx_test.go +++ b/plugin/evm/tx_test.go @@ -150,7 +150,9 @@ func executeTxTest(t *testing.T, test atomicTxTest) { // If this test simulates processing txs during bootstrapping (where some verification is skipped), // initialize the block building goroutines normally initialized in SetState(snow.NormalOps). // This ensures that the VM can build a block correctly during the test. - vm.initBlockBuilding() + if err := vm.initBlockBuilding(); err != nil { + t.Fatal(err) + } } if err := vm.issueTx(tx, true /*=local*/); err != nil { diff --git a/plugin/evm/vm.go b/plugin/evm/vm.go index 2e5a3f81b6..d2146d28b2 100644 --- a/plugin/evm/vm.go +++ b/plugin/evm/vm.go @@ -128,8 +128,26 @@ const ( bytesToIDCacheSize = 5 * units.MiB targetAtomicTxsSize = 40 * units.KiB + + // p2p app protocols + ethTxGossipProtocol = 0x0 + atomicTxGossipProtocol = 0x1 + + // gossip constants + txGossipMaxResponseSize = 20 * units.KiB + txGossipBloomMaxFilledRatio = 0.75 + txGossipBloomMaxItems = 8 * 1024 + txGossipBloomFalsePositiveRate = 0.01 + maxValidatorSetStaleness = time.Minute + throttlingPeriod = 10 * time.Second + throttlingLimit = 2 ) +var txGossipConfig = gossip.Config{ + Frequency: 10 * time.Second, + PollSize: 10, +} + // Define the API endpoints for the VM const ( avaxEndpoint = "/avax" @@ -210,7 +228,9 @@ func init() { // VM implements the snowman.ChainVM interface type VM struct { - ctx *snow.Context + ctx *snow.Context // TODO rename to snowCtx + backgroundCtx context.Context // TODO rename to ctx + cancel context.CancelFunc // *chain.State helps to implement the VM interface by wrapping blocks // with an efficient caching layer. *chain.State @@ -257,7 +277,9 @@ type VM struct { builder *blockBuilder - gossiper Gossiper + gossiper Gossiper + ethTxGossiper *gossip.Gossiper[GossipEthTx, *GossipEthTx] + atomicTxGossiper *gossip.Gossiper[GossipAtomicTx, *GossipAtomicTx] baseCodec codec.Registry codec codec.Manager @@ -277,7 +299,10 @@ type VM struct { client peer.NetworkClient networkCodec codec.Manager - router *p2p.Router + validators *p2p.Validators + router *p2p.Router + ethTxGossipClient *p2p.Client + atomicTxGossipClient *p2p.Client // Metrics multiGatherer avalanchegoMetrics.MultiGatherer @@ -316,7 +341,7 @@ func (vm *VM) GetActivationTime() time.Time { // Initialize implements the snowman.ChainVM interface func (vm *VM) Initialize( - _ context.Context, + ctx context.Context, chainCtx *snow.Context, dbManager manager.Manager, genesisBytes []byte, @@ -341,6 +366,9 @@ func (vm *VM) Initialize( deprecateMsg := vm.config.Deprecate() vm.ctx = chainCtx + ctx, cancel := context.WithCancel(ctx) + vm.backgroundCtx = ctx + vm.cancel = cancel // Create logger alias, err := vm.ctx.BCLookup.PrimaryAlias(vm.ctx.ChainID) @@ -502,13 +530,17 @@ func (vm *VM) Initialize( vm.codec = Codec // TODO: read size from settings - vm.mempool = NewMempool(chainCtx.AVAXAssetID, defaultMempoolSize) + vm.mempool, err = NewMempool(chainCtx.AVAXAssetID, defaultMempoolSize) + if err != nil { + return fmt.Errorf("failed to initialize mempool: %w", err) + } if err := vm.initializeMetrics(); err != nil { return err } // initialize peer network + vm.validators = p2p.NewValidators(vm.ctx.Log, vm.ctx.SubnetID, vm.ctx.ValidatorState, maxValidatorSetStaleness) vm.router = p2p.NewRouter(vm.ctx.Log, appSender) vm.networkCodec = message.Codec vm.Network = peer.NewNetwork(vm.router, appSender, vm.networkCodec, message.CrossChainCodec, chainCtx.NodeID, vm.config.MaxOutboundActiveRequests, vm.config.MaxOutboundActiveCrossChainRequests) @@ -937,7 +969,9 @@ func (vm *VM) SetState(_ context.Context, state snow.State) error { return vm.fx.Bootstrapping() case snow.NormalOp: // Initialize goroutines related to block building once we enter normal operation as there is no need to handle mempool gossip before this point. - vm.initBlockBuilding() + if err := vm.initBlockBuilding(); err != nil { + return fmt.Errorf("failed to initialize block building: %w", err) + } vm.bootstrapped = true return vm.fx.Bootstrapped() default: @@ -946,13 +980,64 @@ func (vm *VM) SetState(_ context.Context, state snow.State) error { } // initBlockBuilding starts goroutines to manage block building -func (vm *VM) initBlockBuilding() { +func (vm *VM) initBlockBuilding() error { // NOTE: gossip network must be initialized first otherwise ETH tx gossip will not work. gossipStats := NewGossipStats() vm.gossiper = vm.createGossiper(gossipStats) vm.builder = vm.NewBlockBuilder(vm.toEngine) vm.builder.awaitSubmittedTxs() vm.Network.SetGossipHandler(NewGossipHandler(vm, gossipStats)) + + ethTxPool, err := NewGossipEthTxPool(vm.txPool) + if err != nil { + return err + } + vm.shutdownWg.Add(1) + go ethTxPool.Subscribe(vm.shutdownChan, &vm.shutdownWg) + + ethTxGossipHandler := &p2p.ValidatorHandler{ + ValidatorSet: vm.validators, + Handler: &p2p.ThrottlerHandler{ + Throttler: p2p.NewSlidingWindowThrottler(throttlingPeriod, throttlingLimit), + Handler: gossip.NewHandler[*GossipEthTx](ethTxPool, txGossipMaxResponseSize), + }, + } + ethTxGossipClient, err := vm.router.RegisterAppProtocol(ethTxGossipProtocol, ethTxGossipHandler, vm.validators) + if err != nil { + return err + } + vm.ethTxGossipClient = ethTxGossipClient + + atomicTxGossipHandler := &p2p.ValidatorHandler{ + ValidatorSet: vm.validators, + Handler: &p2p.ThrottlerHandler{ + Throttler: p2p.NewSlidingWindowThrottler(throttlingPeriod, throttlingLimit), + Handler: gossip.NewHandler[*GossipAtomicTx](vm.mempool, txGossipMaxResponseSize), + }, + } + atomicTxGossipClient, err := vm.router.RegisterAppProtocol(atomicTxGossipProtocol, atomicTxGossipHandler, vm.validators) + if err != nil { + return err + } + vm.atomicTxGossipClient = atomicTxGossipClient + + vm.ethTxGossiper = gossip.NewGossiper[GossipEthTx, *GossipEthTx]( + txGossipConfig, + vm.ctx.Log, + ethTxPool, + vm.ethTxGossipClient, + ) + go vm.ethTxGossiper.Gossip(vm.backgroundCtx) + + vm.atomicTxGossiper = gossip.NewGossiper[GossipAtomicTx, *GossipAtomicTx]( + txGossipConfig, + vm.ctx.Log, + vm.mempool, + vm.atomicTxGossipClient, + ) + go vm.atomicTxGossiper.Gossip(vm.backgroundCtx) + + return nil } // setAppRequestHandlers sets the request handlers for the VM to serve state sync @@ -990,6 +1075,7 @@ func (vm *VM) Shutdown(context.Context) error { if vm.ctx == nil { return nil } + vm.cancel() vm.Network.Shutdown() if err := vm.StateSyncClient.Shutdown(); err != nil { log.Error("error stopping state syncer", "err", err) @@ -1337,7 +1423,7 @@ func (vm *VM) issueTx(tx *Tx, local bool) error { } return err } - // NOTE: Gossiping of the issued [Tx] is handled in [AddTx] + return nil } From ca37577bbb549f769122b7aa990644bc793a67f6 Mon Sep 17 00:00:00 2001 From: Joshua Kim <20001595+joshua-kim@users.noreply.github.com> Date: Wed, 6 Sep 2023 03:10:59 -0400 Subject: [PATCH 02/36] nit --- go.mod | 2 +- go.sum | 2 ++ peer/network_test.go | 2 +- plugin/evm/gossip_mempool.go | 44 +++++++++----------------- plugin/evm/gossip_mempool_test.go | 52 ++++++++++++++++++------------- plugin/evm/mempool.go | 33 ++++++++++---------- plugin/evm/tx_gossip_test.go | 31 +++++++++--------- plugin/evm/vm.go | 1 + 8 files changed, 82 insertions(+), 85 deletions(-) diff --git a/go.mod b/go.mod index 10ced6c0fd..97cc1e1440 100644 --- a/go.mod +++ b/go.mod @@ -4,7 +4,7 @@ go 1.19 require ( github.com/VictoriaMetrics/fastcache v1.10.0 - github.com/ava-labs/avalanchego v1.10.10-rc.0 + github.com/ava-labs/avalanchego v1.10.10-rc.1 github.com/cespare/cp v0.1.0 github.com/cockroachdb/pebble v0.0.0-20230209160836-829675f94811 github.com/davecgh/go-spew v1.1.1 diff --git a/go.sum b/go.sum index e83a87d369..fab801edea 100644 --- a/go.sum +++ b/go.sum @@ -61,6 +61,8 @@ github.com/ava-labs/avalanchego v1.10.9-rc.4 h1:vtavPfRiF6r1Zc6RV8/arEfVpe9GQsLW github.com/ava-labs/avalanchego v1.10.9-rc.4/go.mod h1:vTBLl1zK36olfLRA7IUfdbvphWqlkuarIoXxvZTHZVw= github.com/ava-labs/avalanchego v1.10.10-rc.0 h1:6VjkpwhAJ0tDNJK+UIUD8WIb5VelgH3w61mgk7JAkDQ= github.com/ava-labs/avalanchego v1.10.10-rc.0/go.mod h1:C8R5uiltpc8MQ62ixxgODR+15mesWF0aAw3H+Qrl9Iw= +github.com/ava-labs/avalanchego v1.10.10-rc.1 h1:dPJISEWqL3tdUShe6RuB8CFuXl3rsH8617sXbLBjkIE= +github.com/ava-labs/avalanchego v1.10.10-rc.1/go.mod h1:C8R5uiltpc8MQ62ixxgODR+15mesWF0aAw3H+Qrl9Iw= github.com/aymerick/raymond v2.0.3-0.20180322193309-b565731e1464+incompatible/go.mod h1:osfaiScAUVup+UC9Nfq76eWqDhXlp+4UYaA8uhTBO6g= github.com/benbjohnson/clock v1.3.0 h1:ip6w0uFQkncKQ979AypyG0ER7mqUSBdKLOgAle/AT8A= github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= diff --git a/peer/network_test.go b/peer/network_test.go index add57616de..c130bd5136 100644 --- a/peer/network_test.go +++ b/peer/network_test.go @@ -665,7 +665,7 @@ func TestSDKRouting(t *testing.T) { protocol := 0 handler := &testSDKHandler{} router := p2p.NewRouter(logging.NoLog{}, sender) - _, err := router.RegisterAppProtocol(uint64(protocol), handler) + _, err := router.RegisterAppProtocol(uint64(protocol), handler, &p2p.Peers{}) require.NoError(err) networkCodec := codec.NewManager(0) diff --git a/plugin/evm/gossip_mempool.go b/plugin/evm/gossip_mempool.go index c14e67f67f..d6a1cba044 100644 --- a/plugin/evm/gossip_mempool.go +++ b/plugin/evm/gossip_mempool.go @@ -7,6 +7,7 @@ import ( "fmt" "sync" + "github.com/ava-labs/avalanchego/ids" "github.com/ethereum/go-ethereum/log" "github.com/ava-labs/avalanchego/network/p2p/gossip" @@ -27,12 +28,8 @@ type GossipAtomicTx struct { Tx *Tx `serialize:"true"` } -func (tx *GossipAtomicTx) GetHash() gossip.Hash { - id := tx.Tx.ID() - hash := gossip.Hash{} - copy(hash[:], id[:]) - - return hash +func (tx *GossipAtomicTx) GetID() ids.ID { + return tx.Tx.ID() } func (tx *GossipAtomicTx) Marshal() ([]byte, error) { @@ -79,7 +76,13 @@ func (g *GossipEthTxPool) Subscribe(shutdownChan chan struct{}, shutdownWg *sync for _, pendingTx := range pendingTxs.Txs { tx := &GossipEthTx{Tx: pendingTx} g.bloom.Add(tx) - if gossip.ResetBloomFilterIfNeeded(g.bloom, txGossipBloomMaxFilledRatio) { + reset, err := gossip.ResetBloomFilterIfNeeded(g.bloom, txGossipBloomMaxFilledRatio) + 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") pending := g.mempool.Pending(false) @@ -105,25 +108,10 @@ func (g *GossipEthTxPool) Add(tx *GossipEthTx) error { return nil } -func (g *GossipEthTxPool) Get(filter func(tx *GossipEthTx) bool) []*GossipEthTx { - limit := 1000 - resultSize := 0 - result := make([]*GossipEthTx, 0) - +func (g *GossipEthTxPool) Iterate(f func(tx *GossipEthTx) bool) { g.mempool.IteratePending(func(tx *types.Transaction) bool { - resultSize += int(tx.Size()) - if resultSize > limit { - return false - } - - gossipTx := &GossipEthTx{ - Tx: tx, - } - result = append(result, gossipTx) - return true + return f(&GossipEthTx{Tx: tx}) }) - - return result } func (g *GossipEthTxPool) GetFilter() ([]byte, []byte, error) { @@ -138,12 +126,8 @@ type GossipEthTx struct { Tx *types.Transaction } -func (tx *GossipEthTx) GetHash() gossip.Hash { - txHash := tx.Tx.Hash() - hash := gossip.Hash{} - copy(hash[:], txHash[:]) - - return hash +func (tx *GossipEthTx) GetID() ids.ID { + return ids.ID(tx.Tx.Hash()) } func (tx *GossipEthTx) Marshal() ([]byte, error) { diff --git a/plugin/evm/gossip_mempool_test.go b/plugin/evm/gossip_mempool_test.go index f5dbb3a697..fcc15c74ff 100644 --- a/plugin/evm/gossip_mempool_test.go +++ b/plugin/evm/gossip_mempool_test.go @@ -11,7 +11,7 @@ import ( "github.com/ava-labs/avalanchego/ids" ) -func TestAtomicMempoolAddTx(t *testing.T) { +func TestAtomicMempoolIterate(t *testing.T) { txs := []*GossipAtomicTx{ { Tx: &Tx{ @@ -30,44 +30,43 @@ func TestAtomicMempoolAddTx(t *testing.T) { } tests := []struct { - name string - add []*GossipAtomicTx - filter func(tx *GossipAtomicTx) bool - expected []*GossipAtomicTx + name string + add []*GossipAtomicTx + f func(tx *GossipAtomicTx) bool + possibleValues []*GossipAtomicTx + expectedLen int }{ { - name: "empty", - }, - { - name: "filter matches nothing", + name: "func matches nothing", add: txs, - filter: func(*GossipAtomicTx) bool { + f: func(*GossipAtomicTx) bool { return false }, - expected: nil, + possibleValues: nil, }, { - name: "filter matches all", + name: "func matches all", add: txs, - filter: func(*GossipAtomicTx) bool { + f: func(*GossipAtomicTx) bool { return true }, - expected: txs, + possibleValues: txs, + expectedLen: 2, }, { - name: "filter matches subset", + name: "func matches subset", add: txs, - filter: func(tx *GossipAtomicTx) bool { + f: func(tx *GossipAtomicTx) bool { return tx.Tx == txs[0].Tx }, - expected: txs[:1], + possibleValues: txs, + expectedLen: 1, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { require := require.New(t) - m, err := NewMempool(ids.Empty, 10) require.NoError(err) @@ -75,12 +74,21 @@ func TestAtomicMempoolAddTx(t *testing.T) { require.NoError(m.Add(add)) } - txs := m.Get(tt.filter) - require.Len(txs, len(tt.expected)) + matches := make([]*GossipAtomicTx, 0) + f := func(tx *GossipAtomicTx) bool { + match := tt.f(tx) - for _, expected := range tt.expected { - require.Contains(txs, expected) + if match { + matches = append(matches, tx) + } + + return match } + + m.Iterate(f) + + require.Len(matches, tt.expectedLen) + require.Subset(tt.possibleValues, matches) }) } } diff --git a/plugin/evm/mempool.go b/plugin/evm/mempool.go index 97e8ac5b2e..9d180c6956 100644 --- a/plugin/evm/mempool.go +++ b/plugin/evm/mempool.go @@ -274,16 +274,14 @@ func (m *Mempool) addTx(tx *Tx, force bool) error { for utxoID := range utxoSet { m.utxoSpenders[utxoID] = tx } - // When adding [tx] to the mempool make sure that there is an item in Pending - // to signal the VM to produce a block. Note: if the VM's buildStatus has already - // been set to something other than [dontBuild], this will be ignored and won't be - // reset until the engine calls BuildBlock. This case is handled in IssueCurrentTx - // and CancelCurrentTx. - m.newTxs = append(m.newTxs, tx) - m.addPending() m.bloom.Add(&GossipAtomicTx{Tx: tx}) - if gossip.ResetBloomFilterIfNeeded(m.bloom, txGossipBloomMaxFilledRatio) { + reset, err := gossip.ResetBloomFilterIfNeeded(m.bloom, txGossipBloomMaxFilledRatio) + if err != nil { + return err + } + + if reset { log.Debug("resetting bloom filter", "reason", "reached max filled ratio") for _, pendingTx := range m.txHeap.minHeap.items { @@ -291,23 +289,26 @@ func (m *Mempool) addTx(tx *Tx, force bool) error { } } + // When adding [tx] to the mempool make sure that there is an item in Pending + // to signal the VM to produce a block. Note: if the VM's buildStatus has already + // been set to something other than [dontBuild], this will be ignored and won't be + // reset until the engine calls BuildBlock. This case is handled in IssueCurrentTx + // and CancelCurrentTx. + m.newTxs = append(m.newTxs, tx) + m.addPending() + return nil } -func (m *Mempool) Get(filter func(tx *GossipAtomicTx) bool) []*GossipAtomicTx { +func (m *Mempool) Iterate(f func(tx *GossipAtomicTx) bool) { m.lock.RLock() defer m.lock.RUnlock() - gossipTxs := make([]*GossipAtomicTx, 0, len(m.txHeap.maxHeap.items)) for _, item := range m.txHeap.maxHeap.items { - gossipTx := &GossipAtomicTx{Tx: item.tx} - if !filter(gossipTx) { - continue + if !f(&GossipAtomicTx{Tx: item.tx}) { + return } - gossipTxs = append(gossipTxs, gossipTx) } - - return gossipTxs } func (m *Mempool) GetFilter() ([]byte, []byte, error) { diff --git a/plugin/evm/tx_gossip_test.go b/plugin/evm/tx_gossip_test.go index 70575d8e5c..1ad2857911 100644 --- a/plugin/evm/tx_gossip_test.go +++ b/plugin/evm/tx_gossip_test.go @@ -10,20 +10,21 @@ import ( "testing" "time" - "github.com/ava-labs/avalanchego/p2p/gossip" - "github.com/ava-labs/avalanchego/p2p/gossip/proto/pb" - "github.com/stretchr/testify/require" - "go.uber.org/mock/gomock" - "google.golang.org/protobuf/proto" - "github.com/ava-labs/avalanchego/ids" "github.com/ava-labs/avalanchego/network/p2p" - commonEng "github.com/ava-labs/avalanchego/snow/engine/common" + "github.com/ava-labs/avalanchego/network/p2p/gossip" + "github.com/ava-labs/avalanchego/proto/pb/sdk" + "github.com/ava-labs/avalanchego/snow/engine/common" "github.com/ava-labs/avalanchego/snow/validators" "github.com/ava-labs/avalanchego/utils" "github.com/ava-labs/avalanchego/utils/crypto/secp256k1" "github.com/ava-labs/avalanchego/utils/logging" "github.com/ava-labs/avalanchego/utils/set" + "github.com/stretchr/testify/require" + + "go.uber.org/mock/gomock" + + "google.golang.org/protobuf/proto" "github.com/ava-labs/coreth/core" "github.com/ava-labs/coreth/core/types" @@ -60,7 +61,7 @@ func TestEthTxGossip(t *testing.T) { // sender for the peer requesting gossip from [vm] ctrl := gomock.NewController(t) - peerSender := commonEng.NewMockSender(ctrl) + peerSender := common.NewMockSender(ctrl) router := p2p.NewRouter(logging.NoLog{}, peerSender) // we're only making client requests, so we don't need a server handler @@ -71,7 +72,7 @@ func TestEthTxGossip(t *testing.T) { require.NoError(err) emptyBloomFilterBytes, err := emptyBloomFilter.Bloom.MarshalBinary() require.NoError(err) - request := &pb.PullGossipRequest{ + request := &sdk.PullGossipRequest{ Filter: emptyBloomFilterBytes, Salt: utils.RandomBytes(32), } @@ -110,7 +111,7 @@ func TestEthTxGossip(t *testing.T) { onResponse := func(nodeID ids.NodeID, responseBytes []byte, err error) { require.NoError(err) - response := &pb.PullGossipResponse{} + response := &sdk.PullGossipResponse{} require.NoError(proto.Unmarshal(responseBytes, response)) require.Empty(response.Gossip) wg.Done() @@ -137,7 +138,7 @@ func TestEthTxGossip(t *testing.T) { onResponse = func(nodeID ids.NodeID, responseBytes []byte, err error) { require.NoError(err) - response := &pb.PullGossipResponse{} + response := &sdk.PullGossipResponse{} require.NoError(proto.Unmarshal(responseBytes, response)) require.Len(response.Gossip, 1) @@ -166,7 +167,7 @@ func TestAtomicTxGossip(t *testing.T) { // sender for the peer requesting gossip from [vm] ctrl := gomock.NewController(t) - peerSender := commonEng.NewMockSender(ctrl) + peerSender := common.NewMockSender(ctrl) router := p2p.NewRouter(logging.NoLog{}, peerSender) // we're only making client requests, so we don't need a server handler @@ -177,7 +178,7 @@ func TestAtomicTxGossip(t *testing.T) { require.NoError(err) bloomBytes, err := emptyBloomFilter.Bloom.MarshalBinary() require.NoError(err) - request := &pb.PullGossipRequest{ + request := &sdk.PullGossipRequest{ Filter: bloomBytes, Salt: emptyBloomFilter.Salt[:], } @@ -214,7 +215,7 @@ func TestAtomicTxGossip(t *testing.T) { onResponse := func(nodeID ids.NodeID, responseBytes []byte, err error) { require.NoError(err) - response := &pb.PullGossipResponse{} + response := &sdk.PullGossipResponse{} require.NoError(proto.Unmarshal(responseBytes, response)) require.Empty(response.Gossip) wg.Done() @@ -237,7 +238,7 @@ func TestAtomicTxGossip(t *testing.T) { onResponse = func(nodeID ids.NodeID, responseBytes []byte, err error) { require.NoError(err) - response := &pb.PullGossipResponse{} + response := &sdk.PullGossipResponse{} require.NoError(proto.Unmarshal(responseBytes, response)) require.Len(response.Gossip, 1) diff --git a/plugin/evm/vm.go b/plugin/evm/vm.go index d2146d28b2..c4fdd6b905 100644 --- a/plugin/evm/vm.go +++ b/plugin/evm/vm.go @@ -18,6 +18,7 @@ import ( avalanchegoMetrics "github.com/ava-labs/avalanchego/api/metrics" "github.com/ava-labs/avalanchego/network/p2p" + "github.com/ava-labs/avalanchego/network/p2p/gossip" "github.com/ava-labs/coreth/consensus/dummy" corethConstants "github.com/ava-labs/coreth/constants" From c93ee73f29fbd7e44f3a208ad4fd3f9989f030d9 Mon Sep 17 00:00:00 2001 From: Joshua Kim <20001595+joshua-kim@users.noreply.github.com> Date: Fri, 8 Sep 2023 02:57:44 -0400 Subject: [PATCH 03/36] Update plugin/evm/gossip_mempool.go Co-authored-by: Stephen Buttolph Signed-off-by: Joshua Kim <20001595+joshua-kim@users.noreply.github.com> --- plugin/evm/gossip_mempool.go | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/plugin/evm/gossip_mempool.go b/plugin/evm/gossip_mempool.go index d6a1cba044..fa4b8c408b 100644 --- a/plugin/evm/gossip_mempool.go +++ b/plugin/evm/gossip_mempool.go @@ -101,11 +101,7 @@ func (g *GossipEthTxPool) Subscribe(shutdownChan chan struct{}, shutdownWg *sync // 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 { - if err := g.mempool.AddRemotes([]*types.Transaction{tx.Tx})[0]; err != nil { - return err - } - - return nil + return g.mempool.AddRemotes([]*types.Transaction{tx.Tx})[0] } func (g *GossipEthTxPool) Iterate(f func(tx *GossipEthTx) bool) { From bb513d64b1f191aef2c7ef8687e5587f74963964 Mon Sep 17 00:00:00 2001 From: Joshua Kim <20001595+joshua-kim@users.noreply.github.com> Date: Fri, 8 Sep 2023 15:01:31 -0400 Subject: [PATCH 04/36] nits --- core/txpool/txpool.go | 10 ++++++---- go.mod | 2 +- go.sum | 15 --------------- plugin/evm/gossip_mempool.go | 21 +++++++++++---------- plugin/evm/mempool.go | 5 ++++- plugin/evm/vm.go | 10 +++++++--- 6 files changed, 29 insertions(+), 34 deletions(-) diff --git a/core/txpool/txpool.go b/core/txpool/txpool.go index 5095396794..bc3dd52eb6 100644 --- a/core/txpool/txpool.go +++ b/core/txpool/txpool.go @@ -630,12 +630,14 @@ func (pool *TxPool) PendingSize() int { return count } -// IteratePending iterates over the [pool.pending] until [f] returns false. +// IteratePending iterates over [pool.pending] until [f] returns false. +// The caller must not modify [tx]. func (pool *TxPool) IteratePending(f func(tx *types.Transaction) bool) { - pending := pool.Pending(true) + pool.mu.RLock() + defer pool.mu.RUnlock() - for _, list := range pending { - for _, tx := range list { + for _, list := range pool.pending { + for _, tx := range list.txs.items { if !f(tx) { return } diff --git a/go.mod b/go.mod index 97cc1e1440..bd2dbad37c 100644 --- a/go.mod +++ b/go.mod @@ -38,6 +38,7 @@ 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.1.0 golang.org/x/exp v0.0.0-20230206171751-46f607a40771 golang.org/x/sync v0.1.0 @@ -127,7 +128,6 @@ require ( go.opentelemetry.io/otel/trace v1.11.0 // indirect go.opentelemetry.io/proto/otlp v0.19.0 // indirect go.uber.org/atomic v1.10.0 // indirect - go.uber.org/mock v0.2.0 // indirect go.uber.org/multierr v1.8.0 // indirect go.uber.org/zap v1.24.0 // indirect golang.org/x/net v0.8.0 // indirect diff --git a/go.sum b/go.sum index fab801edea..5ad8973b3e 100644 --- a/go.sum +++ b/go.sum @@ -51,16 +51,10 @@ github.com/VictoriaMetrics/fastcache v1.10.0 h1:5hDJnLsKLpnUEToub7ETuRu8RCkb40wo github.com/VictoriaMetrics/fastcache v1.10.0/go.mod h1:tjiYeEfYXCqacuvYw/7UoDIeJaNxq6132xHICNP77w8= github.com/aead/siphash v1.0.1/go.mod h1:Nywa3cDsYNNK3gaciGTWPwHt0wlpNV15vwmswBAUSII= github.com/ajg/form v1.5.1/go.mod h1:uL1WgH+h2mgNtvBq0339dVnzXdBETtL2LeUXaIv25UY= -github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= -github.com/alecthomas/units v0.0.0-20190924025748-f65c72e2690d/go.mod h1:rBZYJk541a8SKzHPHnH3zbiI+7dagKZ0cgpgrD7Fyho= github.com/allegro/bigcache v1.2.1-0.20190218064605-e24eb225f156 h1:eMwmnE/GDgah4HI848JfFxHt+iPb26b4zyfspmqY0/8= github.com/allegro/bigcache v1.2.1-0.20190218064605-e24eb225f156/go.mod h1:Cb/ax3seSYIx7SuZdm2G2xzfwmv3TPSk2ucNfQESPXM= github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY= github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5doyWs3UAsr3K4I6qtAmlQcZDesFNEHPZAzj8= -github.com/ava-labs/avalanchego v1.10.9-rc.4 h1:vtavPfRiF6r1Zc6RV8/arEfVpe9GQsLWHbMfIWkHbMI= -github.com/ava-labs/avalanchego v1.10.9-rc.4/go.mod h1:vTBLl1zK36olfLRA7IUfdbvphWqlkuarIoXxvZTHZVw= -github.com/ava-labs/avalanchego v1.10.10-rc.0 h1:6VjkpwhAJ0tDNJK+UIUD8WIb5VelgH3w61mgk7JAkDQ= -github.com/ava-labs/avalanchego v1.10.10-rc.0/go.mod h1:C8R5uiltpc8MQ62ixxgODR+15mesWF0aAw3H+Qrl9Iw= github.com/ava-labs/avalanchego v1.10.10-rc.1 h1:dPJISEWqL3tdUShe6RuB8CFuXl3rsH8617sXbLBjkIE= github.com/ava-labs/avalanchego v1.10.10-rc.1/go.mod h1:C8R5uiltpc8MQ62ixxgODR+15mesWF0aAw3H+Qrl9Iw= github.com/aymerick/raymond v2.0.3-0.20180322193309-b565731e1464+incompatible/go.mod h1:osfaiScAUVup+UC9Nfq76eWqDhXlp+4UYaA8uhTBO6g= @@ -129,7 +123,6 @@ github.com/cockroachdb/pebble v0.0.0-20230209160836-829675f94811 h1:ytcWPaNPhNoG github.com/cockroachdb/pebble v0.0.0-20230209160836-829675f94811/go.mod h1:Nb5lgvnQ2+oGlE/EyZy4+2/CxRh9KfvCXnag1vtpxVM= github.com/cockroachdb/redact v1.1.3 h1:AKZds10rFSIj7qADf0g46UixK8NNLwWTNdCIGS5wfSQ= github.com/cockroachdb/redact v1.1.3/go.mod h1:BVNblN9mBWFyMyqK1k3AAiSxhvhfK2oOZZ2lK+dpvRg= -github.com/cockroachdb/sentry-go v0.6.1-cockroachdb.2/go.mod h1:8BT+cPK6xvFOcRlk0R8eg+OTkcqI6baNH4xAkpiYVvQ= 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= @@ -216,7 +209,6 @@ github.com/go-errors/errors v1.4.2 h1:J6MZopCL4uSllY1OfXM374weqZFFItUbrImctkmUxI github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU= 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-logfmt/logfmt v0.5.1/go.mod h1:WYhtIu8zTZfxdn5+rREduYbwxfcBr/Vr6KEVveWlfTs= 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= @@ -378,16 +370,13 @@ github.com/jackpal/go-nat-pmp v1.0.2 h1:KzKSgb7qkJvOUTqYl9/Hg/me3pWgBmERKrTGD7Bd github.com/jackpal/go-nat-pmp v1.0.2/go.mod h1:QPH045xvCAeXUZOxsnwmrtiCoxIr9eob+4orBN1SBKc= github.com/jessevdk/go-flags v0.0.0-20141203071132-1679536dcc89/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI= github.com/jessevdk/go-flags v1.4.0/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI= -github.com/jpillora/backoff v1.0.0/go.mod h1:J/6gKK9jxlEcS3zixgDgUAsiuZ7yrSoa/FX5e0EB2j4= github.com/jrick/logrotate v1.0.0/go.mod h1:LNinyqDIJnpAur+b8yyulnQw/wDuN1+BYKlTRt3OuAQ= github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU= github.com/json-iterator/go v1.1.9/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= -github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo= github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU= github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/XSXhF0NWZEnDohbsk= github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU= github.com/julienschmidt/httprouter v1.3.0 h1:U0609e9tgbseu3rBINet9P48AI/D3oJs4dN7jwJOQ1U= -github.com/julienschmidt/httprouter v1.3.0/go.mod h1:JR6WtHb+2LUe8TCKY3cZOxFyyO8IZAc4RVcycCCAKdM= github.com/k0kubun/colorstring v0.0.0-20150214042306-9440f1994b88/go.mod h1:3w7q1U84EfirKl04SVQ/s7nPm1ZPhiXd34z40TNz36k= github.com/kataras/golog v0.0.10/go.mod h1:yJ8YKCmyL+nWjERB90Qwn+bdyBZsaQwU3bTVFgkFIp8= github.com/kataras/iris/v12 v12.1.8/go.mod h1:LMYy4VlP67TQ3Zgriz8RE2h2kMZV2SgMYbq3UhfoFmE= @@ -418,7 +407,6 @@ github.com/labstack/echo/v4 v4.2.1/go.mod h1:AA49e0DZ8kk5jTOOCKNuPR6oTnBS0dYiM4F github.com/labstack/echo/v4 v4.5.0/go.mod h1:czIriw4a0C1dFun+ObrXp7ok03xON0N1awStJ6ArI7Y= github.com/labstack/gommon v0.3.0/go.mod h1:MULnywXg0yavhxWKc+lOruYdAhDwPK9wf0OL7NoOu+k= github.com/leanovate/gopter v0.2.9 h1:fQjYxZaynp97ozCzfOyOuAGOU4aU/z37zf/tOujFk7c= -github.com/leanovate/gopter v0.2.9/go.mod h1:U2L/78B+KVFIx2VmW6onHJQzXtFb+p5y3y2Sh+Jxxv8= github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ= github.com/magiconair/properties v1.8.6 h1:5ibWZ6iY0NctNGWo87LalDlEZ6R41TqbbDamhfG/Qzo= github.com/magiconair/properties v1.8.6/go.mod h1:y3VJvCyxH9uVvJTWEGAELF3aiYNyPKd5NZ3oSwXrF60= @@ -460,11 +448,9 @@ github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJ github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= -github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= github.com/moul/http2curl v1.0.0/go.mod h1:8UbvGypXm98wA/IqH45anm5Y2Z6ep6O31QGOAZ3H0fQ= github.com/mr-tron/base58 v1.2.0 h1:T/HDJBh4ZCPbU39/+c3rRvE0uKBQlU27+QI8LJ4t64o= github.com/mr-tron/base58 v1.2.0/go.mod h1:BinMc/sQntlIE1frQmRFPUoPA1Zkr8VRgBdjWI2mNwc= -github.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= github.com/nats-io/jwt v0.3.0/go.mod h1:fRYCDE99xlTsqUzISS1Bi75UBJ6ljOJQOAAu5VglpSg= github.com/nats-io/nats.go v1.9.1/go.mod h1:ZjDU1L/7fJ09jvUSRVBR2e7+RnLiiIQyqyzEE/Zbp4w= github.com/nats-io/nkeys v0.1.0/go.mod h1:xpnFELMwJABBLVhffcfd1MZx6VsNRFpEugbxziKVo7w= @@ -1037,7 +1023,6 @@ google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQ google.golang.org/protobuf v1.27.1/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= google.golang.org/protobuf v1.30.0 h1:kPPoIgf3TsEvrm0PFe15JQ+570QVxYzEvvHqChK+cng= google.golang.org/protobuf v1.30.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= -gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= diff --git a/plugin/evm/gossip_mempool.go b/plugin/evm/gossip_mempool.go index fa4b8c408b..bfc8396e75 100644 --- a/plugin/evm/gossip_mempool.go +++ b/plugin/evm/gossip_mempool.go @@ -4,6 +4,7 @@ package evm import ( + "context" "fmt" "sync" @@ -62,13 +63,12 @@ type GossipEthTxPool struct { lock sync.RWMutex } -func (g *GossipEthTxPool) Subscribe(shutdownChan chan struct{}, shutdownWg *sync.WaitGroup) { - defer shutdownWg.Done() +func (g *GossipEthTxPool) Subscribe(ctx context.Context) { g.mempool.SubscribeNewTxsEvent(g.pendingTxs) for { select { - case <-shutdownChan: + case <-ctx.Done(): log.Debug("shutting down subscription") return case pendingTxs := <-g.pendingTxs: @@ -85,12 +85,10 @@ func (g *GossipEthTxPool) Subscribe(shutdownChan chan struct{}, shutdownWg *sync if reset { log.Debug("resetting bloom filter", "reason", "reached max filled ratio") - pending := g.mempool.Pending(false) - for _, pendingTxs := range pending { - for _, pendingTx := range pendingTxs { - g.bloom.Add(&GossipEthTx{Tx: pendingTx}) - } - } + g.mempool.IteratePending(func(tx *types.Transaction) bool { + g.bloom.Add(&GossipEthTx{Tx: pendingTx}) + return true + }) } } g.lock.Unlock() @@ -115,7 +113,10 @@ func (g *GossipEthTxPool) GetFilter() ([]byte, []byte, error) { defer g.lock.RUnlock() bloom, err := g.bloom.Bloom.MarshalBinary() - return bloom, g.bloom.Salt[:], err + salt := make([]byte, len(g.bloom.Salt)) + copy(salt, g.bloom.Salt[:]) + + return bloom, salt, err } type GossipEthTx struct { diff --git a/plugin/evm/mempool.go b/plugin/evm/mempool.go index 9d180c6956..acff97e7e6 100644 --- a/plugin/evm/mempool.go +++ b/plugin/evm/mempool.go @@ -316,7 +316,10 @@ func (m *Mempool) GetFilter() ([]byte, []byte, error) { defer m.lock.RUnlock() bloom, err := m.bloom.Bloom.MarshalBinary() - return bloom, m.bloom.Salt[:], err + salt := make([]byte, len(m.bloom.Salt)) + copy(salt, m.bloom.Salt[:]) + + return bloom, salt, err } // NextTx returns a transaction to be issued from the mempool. diff --git a/plugin/evm/vm.go b/plugin/evm/vm.go index c4fdd6b905..f3e49e975b 100644 --- a/plugin/evm/vm.go +++ b/plugin/evm/vm.go @@ -342,7 +342,7 @@ func (vm *VM) GetActivationTime() time.Time { // Initialize implements the snowman.ChainVM interface func (vm *VM) Initialize( - ctx context.Context, + _ context.Context, chainCtx *snow.Context, dbManager manager.Manager, genesisBytes []byte, @@ -367,7 +367,8 @@ func (vm *VM) Initialize( deprecateMsg := vm.config.Deprecate() vm.ctx = chainCtx - ctx, cancel := context.WithCancel(ctx) + + ctx, cancel := context.WithCancel(context.TODO()) vm.backgroundCtx = ctx vm.cancel = cancel @@ -994,7 +995,10 @@ func (vm *VM) initBlockBuilding() error { return err } vm.shutdownWg.Add(1) - go ethTxPool.Subscribe(vm.shutdownChan, &vm.shutdownWg) + go func() { + ethTxPool.Subscribe(vm.backgroundCtx) + vm.shutdownWg.Done() + }() ethTxGossipHandler := &p2p.ValidatorHandler{ ValidatorSet: vm.validators, From cdb96491e091c8991ebdaa2a7745f6ac15dfa8ca Mon Sep 17 00:00:00 2001 From: Joshua Kim <20001595+joshua-kim@users.noreply.github.com> Date: Fri, 8 Sep 2023 15:03:11 -0400 Subject: [PATCH 05/36] nit --- plugin/evm/gossip_mempool.go | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/plugin/evm/gossip_mempool.go b/plugin/evm/gossip_mempool.go index bfc8396e75..1ea143e74b 100644 --- a/plugin/evm/gossip_mempool.go +++ b/plugin/evm/gossip_mempool.go @@ -16,7 +16,6 @@ import ( "github.com/ava-labs/coreth/core" "github.com/ava-labs/coreth/core/txpool" "github.com/ava-labs/coreth/core/types" - "github.com/ava-labs/coreth/plugin/evm/message" ) var ( @@ -26,7 +25,7 @@ var ( ) type GossipAtomicTx struct { - Tx *Tx `serialize:"true"` + Tx *Tx } func (tx *GossipAtomicTx) GetID() ids.ID { @@ -34,11 +33,11 @@ func (tx *GossipAtomicTx) GetID() ids.ID { } func (tx *GossipAtomicTx) Marshal() ([]byte, error) { - return Codec.Marshal(message.Version, tx) + return tx.Tx.SignedBytes(), nil } func (tx *GossipAtomicTx) Unmarshal(bytes []byte) error { - _, err := Codec.Unmarshal(bytes, tx) + _, err := Codec.Unmarshal(bytes, tx.Tx) return err } From 33b8a0650ae16e3dd41b7ee74598e528d4542b0c Mon Sep 17 00:00:00 2001 From: Joshua Kim <20001595+joshua-kim@users.noreply.github.com> Date: Fri, 8 Sep 2023 15:06:46 -0400 Subject: [PATCH 06/36] nit --- peer/network_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/peer/network_test.go b/peer/network_test.go index c130bd5136..13615630e7 100644 --- a/peer/network_test.go +++ b/peer/network_test.go @@ -665,7 +665,7 @@ func TestSDKRouting(t *testing.T) { protocol := 0 handler := &testSDKHandler{} router := p2p.NewRouter(logging.NoLog{}, sender) - _, err := router.RegisterAppProtocol(uint64(protocol), handler, &p2p.Peers{}) + _, err := router.RegisterAppProtocol(uint64(protocol), handler, nil) require.NoError(err) networkCodec := codec.NewManager(0) From 845b93dd4f2b06b8459a27edd838af83919b7597 Mon Sep 17 00:00:00 2001 From: Joshua Kim <20001595+joshua-kim@users.noreply.github.com> Date: Fri, 8 Sep 2023 15:55:29 -0400 Subject: [PATCH 07/36] nit --- plugin/evm/gossip_mempool.go | 14 ++++++++++++-- plugin/evm/gossip_mempool_test.go | 25 +++++++++++++++++++++++++ 2 files changed, 37 insertions(+), 2 deletions(-) diff --git a/plugin/evm/gossip_mempool.go b/plugin/evm/gossip_mempool.go index 1ea143e74b..17078930c8 100644 --- a/plugin/evm/gossip_mempool.go +++ b/plugin/evm/gossip_mempool.go @@ -37,8 +37,18 @@ func (tx *GossipAtomicTx) Marshal() ([]byte, error) { } func (tx *GossipAtomicTx) Unmarshal(bytes []byte) error { - _, err := Codec.Unmarshal(bytes, tx.Tx) - return err + result := &Tx{} + + _, err := Codec.Unmarshal(bytes, result) + unsignedBytes, err := Codec.Marshal(codecVersion, result.UnsignedAtomicTx) + if err != nil { + return err + } + + result.Initialize(unsignedBytes, bytes) + tx.Tx = result + + return nil } func NewGossipEthTxPool(mempool *txpool.TxPool) (*GossipEthTxPool, error) { diff --git a/plugin/evm/gossip_mempool_test.go b/plugin/evm/gossip_mempool_test.go index fcc15c74ff..b206219295 100644 --- a/plugin/evm/gossip_mempool_test.go +++ b/plugin/evm/gossip_mempool_test.go @@ -6,11 +6,36 @@ package evm import ( "testing" + "github.com/ava-labs/avalanchego/utils/crypto/secp256k1" + "github.com/ava-labs/avalanchego/vms/components/verify" "github.com/stretchr/testify/require" "github.com/ava-labs/avalanchego/ids" ) +func TestGossipAtomicTxMarshal(t *testing.T) { + require := require.New(t) + + expected := &GossipAtomicTx{ + Tx: &Tx{ + UnsignedAtomicTx: &UnsignedImportTx{}, + Creds: []verify.Verifiable{}, + }, + } + + key0 := testKeys[0] + require.NoError(expected.Tx.Sign(Codec, [][]*secp256k1.PrivateKey{{key0}})) + + bytes, err := expected.Marshal() + require.NoError(err) + + actual := &GossipAtomicTx{} + require.NoError(actual.Unmarshal(bytes)) + + require.NoError(err) + require.Equal(expected.GetID(), actual.GetID()) +} + func TestAtomicMempoolIterate(t *testing.T) { txs := []*GossipAtomicTx{ { From 76cc2d2b66505888de76d07f0575c057ac54ada1 Mon Sep 17 00:00:00 2001 From: Joshua Kim <20001595+joshua-kim@users.noreply.github.com> Date: Fri, 8 Sep 2023 16:32:52 -0400 Subject: [PATCH 08/36] Update plugin/evm/gossip_mempool.go Co-authored-by: Stephen Buttolph Signed-off-by: Joshua Kim <20001595+joshua-kim@users.noreply.github.com> --- plugin/evm/gossip_mempool.go | 3 +++ 1 file changed, 3 insertions(+) diff --git a/plugin/evm/gossip_mempool.go b/plugin/evm/gossip_mempool.go index 17078930c8..8181e65203 100644 --- a/plugin/evm/gossip_mempool.go +++ b/plugin/evm/gossip_mempool.go @@ -40,6 +40,9 @@ func (tx *GossipAtomicTx) Unmarshal(bytes []byte) error { result := &Tx{} _, err := Codec.Unmarshal(bytes, result) + if err != nil { + return err + } unsignedBytes, err := Codec.Marshal(codecVersion, result.UnsignedAtomicTx) if err != nil { return err From 954f60fda28b7c9ad85195dcb097803753b7b996 Mon Sep 17 00:00:00 2001 From: Joshua Kim <20001595+joshua-kim@users.noreply.github.com> Date: Fri, 8 Sep 2023 16:33:47 -0400 Subject: [PATCH 09/36] Update plugin/evm/gossip_mempool.go Co-authored-by: Stephen Buttolph Signed-off-by: Joshua Kim <20001595+joshua-kim@users.noreply.github.com> --- plugin/evm/gossip_mempool.go | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/plugin/evm/gossip_mempool.go b/plugin/evm/gossip_mempool.go index 8181e65203..6fa017c436 100644 --- a/plugin/evm/gossip_mempool.go +++ b/plugin/evm/gossip_mempool.go @@ -125,10 +125,9 @@ func (g *GossipEthTxPool) GetFilter() ([]byte, []byte, error) { defer g.lock.RUnlock() bloom, err := g.bloom.Bloom.MarshalBinary() - salt := make([]byte, len(g.bloom.Salt)) - copy(salt, g.bloom.Salt[:]) + salt := g.bloom.Salt - return bloom, salt, err + return bloom, salt[:], err } type GossipEthTx struct { From c83364f1936e8cf5fb061170ac1bd68e21e154b6 Mon Sep 17 00:00:00 2001 From: Joshua Kim <20001595+joshua-kim@users.noreply.github.com> Date: Fri, 8 Sep 2023 16:34:05 -0400 Subject: [PATCH 10/36] Update plugin/evm/mempool.go Co-authored-by: Stephen Buttolph Signed-off-by: Joshua Kim <20001595+joshua-kim@users.noreply.github.com> --- plugin/evm/mempool.go | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/plugin/evm/mempool.go b/plugin/evm/mempool.go index acff97e7e6..97ad7bdd7f 100644 --- a/plugin/evm/mempool.go +++ b/plugin/evm/mempool.go @@ -316,10 +316,9 @@ func (m *Mempool) GetFilter() ([]byte, []byte, error) { defer m.lock.RUnlock() bloom, err := m.bloom.Bloom.MarshalBinary() - salt := make([]byte, len(m.bloom.Salt)) - copy(salt, m.bloom.Salt[:]) + salt := m.bloom.Salt - return bloom, salt, err + return bloom, salt[:], err } // NextTx returns a transaction to be issued from the mempool. From 021f8bfc2b78ff4d7cb477d78c3333276852d6e3 Mon Sep 17 00:00:00 2001 From: Joshua Kim <20001595+joshua-kim@users.noreply.github.com> Date: Fri, 8 Sep 2023 17:15:53 -0400 Subject: [PATCH 11/36] try go 1.20 --- Dockerfile | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Dockerfile b/Dockerfile index 5f088fb36c..17f4913f66 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,5 +1,5 @@ # ============= Compilation Stage ================ -FROM golang:1.19.12-bullseye AS builder +FROM golang:1.20.8-bullseye AS builder ARG AVALANCHE_VERSION @@ -17,7 +17,7 @@ WORKDIR $GOPATH/src/github.com/ava-labs/avalanchego RUN go mod download # Replace the coreth dependency RUN go mod edit -replace github.com/ava-labs/coreth=../coreth -RUN go mod download && go mod tidy -compat=1.19 +RUN go mod download && go mod tidy -compat=1.20 # Build the AvalancheGo binary with local version of coreth. RUN ./scripts/build_avalanche.sh From 8c561416ce053b830eae877800b6225216e6e115 Mon Sep 17 00:00:00 2001 From: Joshua Kim <20001595+joshua-kim@users.noreply.github.com> Date: Fri, 8 Sep 2023 17:18:27 -0400 Subject: [PATCH 12/36] Revert "try go 1.20" This reverts commit 021f8bfc2b78ff4d7cb477d78c3333276852d6e3. --- Dockerfile | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Dockerfile b/Dockerfile index 17f4913f66..5f088fb36c 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,5 +1,5 @@ # ============= Compilation Stage ================ -FROM golang:1.20.8-bullseye AS builder +FROM golang:1.19.12-bullseye AS builder ARG AVALANCHE_VERSION @@ -17,7 +17,7 @@ WORKDIR $GOPATH/src/github.com/ava-labs/avalanchego RUN go mod download # Replace the coreth dependency RUN go mod edit -replace github.com/ava-labs/coreth=../coreth -RUN go mod download && go mod tidy -compat=1.20 +RUN go mod download && go mod tidy -compat=1.19 # Build the AvalancheGo binary with local version of coreth. RUN ./scripts/build_avalanche.sh From 90398eb518320e4dc0b69a054ac64cfb3d8c6572 Mon Sep 17 00:00:00 2001 From: Joshua Kim <20001595+joshua-kim@users.noreply.github.com> Date: Fri, 8 Sep 2023 17:19:15 -0400 Subject: [PATCH 13/36] Update plugin/evm/gossip_mempool.go Co-authored-by: Stephen Buttolph Signed-off-by: Joshua Kim <20001595+joshua-kim@users.noreply.github.com> --- plugin/evm/gossip_mempool.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugin/evm/gossip_mempool.go b/plugin/evm/gossip_mempool.go index 6fa017c436..d3f7ef20dd 100644 --- a/plugin/evm/gossip_mempool.go +++ b/plugin/evm/gossip_mempool.go @@ -43,7 +43,7 @@ func (tx *GossipAtomicTx) Unmarshal(bytes []byte) error { if err != nil { return err } - unsignedBytes, err := Codec.Marshal(codecVersion, result.UnsignedAtomicTx) + unsignedBytes, err := Codec.Marshal(codecVersion, &result.UnsignedAtomicTx) if err != nil { return err } From 673f5ab4b246bf7dc3c41cca5eb359129623a17e Mon Sep 17 00:00:00 2001 From: Joshua Kim <20001595+joshua-kim@users.noreply.github.com> Date: Fri, 8 Sep 2023 17:21:02 -0400 Subject: [PATCH 14/36] clean mod cache --- Dockerfile | 1 + 1 file changed, 1 insertion(+) diff --git a/Dockerfile b/Dockerfile index 5f088fb36c..d7fe16a30c 100644 --- a/Dockerfile +++ b/Dockerfile @@ -13,6 +13,7 @@ COPY . coreth # Set the workdir to AvalancheGo and update coreth dependency to local version WORKDIR $GOPATH/src/github.com/ava-labs/avalanchego +RUN go clean -modcache # Run go mod download here to improve caching of AvalancheGo specific depednencies RUN go mod download # Replace the coreth dependency From 234bdb1649190ab24557a209c8b40ec519c41523 Mon Sep 17 00:00:00 2001 From: Joshua Kim <20001595+joshua-kim@users.noreply.github.com> Date: Fri, 8 Sep 2023 17:23:32 -0400 Subject: [PATCH 15/36] Revert "clean mod cache" This reverts commit 673f5ab4b246bf7dc3c41cca5eb359129623a17e. --- Dockerfile | 1 - 1 file changed, 1 deletion(-) diff --git a/Dockerfile b/Dockerfile index d7fe16a30c..5f088fb36c 100644 --- a/Dockerfile +++ b/Dockerfile @@ -13,7 +13,6 @@ COPY . coreth # Set the workdir to AvalancheGo and update coreth dependency to local version WORKDIR $GOPATH/src/github.com/ava-labs/avalanchego -RUN go clean -modcache # Run go mod download here to improve caching of AvalancheGo specific depednencies RUN go mod download # Replace the coreth dependency From 61a1c16db7eff3ab7965b1e12a5f683d05085205 Mon Sep 17 00:00:00 2001 From: Joshua Kim <20001595+joshua-kim@users.noreply.github.com> Date: Fri, 8 Sep 2023 17:30:13 -0400 Subject: [PATCH 16/36] version --- scripts/versions.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/versions.sh b/scripts/versions.sh index 999778cf5f..0e50203069 100644 --- a/scripts/versions.sh +++ b/scripts/versions.sh @@ -1,4 +1,4 @@ #!/usr/bin/env bash # Don't export them as they're used in the context of other calls -avalanche_version=${AVALANCHE_VERSION:-'v1.10.8'} +avalanche_version=${AVALANCHE_VERSION:-'v1.10.10-rc.1'} From 2898ee576ddcc892555d64c8a9aa2203e1b786f8 Mon Sep 17 00:00:00 2001 From: Joshua Kim <20001595+joshua-kim@users.noreply.github.com> Date: Fri, 8 Sep 2023 19:07:56 -0400 Subject: [PATCH 17/36] nits --- plugin/evm/mempool.go | 3 +-- plugin/evm/vm.go | 2 +- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/plugin/evm/mempool.go b/plugin/evm/mempool.go index 97ad7bdd7f..8cab1de229 100644 --- a/plugin/evm/mempool.go +++ b/plugin/evm/mempool.go @@ -8,13 +8,12 @@ import ( "fmt" "sync" - "github.com/ethereum/go-ethereum/log" - "github.com/ava-labs/avalanchego/cache" "github.com/ava-labs/avalanchego/ids" "github.com/ava-labs/avalanchego/network/p2p/gossip" "github.com/ava-labs/coreth/metrics" + "github.com/ethereum/go-ethereum/log" ) const ( diff --git a/plugin/evm/vm.go b/plugin/evm/vm.go index f3e49e975b..8ae1bcd0fc 100644 --- a/plugin/evm/vm.go +++ b/plugin/evm/vm.go @@ -136,7 +136,7 @@ const ( // gossip constants txGossipMaxResponseSize = 20 * units.KiB - txGossipBloomMaxFilledRatio = 0.75 + txGossipBloomMaxFilledRatio = 0.05 txGossipBloomMaxItems = 8 * 1024 txGossipBloomFalsePositiveRate = 0.01 maxValidatorSetStaleness = time.Minute From 248a1ca365aec5286842bb1f418cc276e0ff2e3d Mon Sep 17 00:00:00 2001 From: Joshua Kim <20001595+joshua-kim@users.noreply.github.com> Date: Fri, 8 Sep 2023 19:10:24 -0400 Subject: [PATCH 18/36] nit --- plugin/evm/gossip_mempool.go | 2 +- plugin/evm/mempool.go | 2 +- plugin/evm/vm.go | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/plugin/evm/gossip_mempool.go b/plugin/evm/gossip_mempool.go index d3f7ef20dd..ee6cedf89f 100644 --- a/plugin/evm/gossip_mempool.go +++ b/plugin/evm/gossip_mempool.go @@ -88,7 +88,7 @@ func (g *GossipEthTxPool) Subscribe(ctx context.Context) { for _, pendingTx := range pendingTxs.Txs { tx := &GossipEthTx{Tx: pendingTx} g.bloom.Add(tx) - reset, err := gossip.ResetBloomFilterIfNeeded(g.bloom, txGossipBloomMaxFilledRatio) + reset, err := gossip.ResetBloomFilterIfNeeded(g.bloom, txGossipMaxFalsePositiveRate) if err != nil { log.Error("failed to reset bloom filter", "err", err) continue diff --git a/plugin/evm/mempool.go b/plugin/evm/mempool.go index 8cab1de229..8c1c56474e 100644 --- a/plugin/evm/mempool.go +++ b/plugin/evm/mempool.go @@ -275,7 +275,7 @@ func (m *Mempool) addTx(tx *Tx, force bool) error { } m.bloom.Add(&GossipAtomicTx{Tx: tx}) - reset, err := gossip.ResetBloomFilterIfNeeded(m.bloom, txGossipBloomMaxFilledRatio) + reset, err := gossip.ResetBloomFilterIfNeeded(m.bloom, txGossipMaxFalsePositiveRate) if err != nil { return err } diff --git a/plugin/evm/vm.go b/plugin/evm/vm.go index 8ae1bcd0fc..ce43a0f0ce 100644 --- a/plugin/evm/vm.go +++ b/plugin/evm/vm.go @@ -136,9 +136,9 @@ const ( // gossip constants txGossipMaxResponseSize = 20 * units.KiB - txGossipBloomMaxFilledRatio = 0.05 txGossipBloomMaxItems = 8 * 1024 txGossipBloomFalsePositiveRate = 0.01 + txGossipMaxFalsePositiveRate = 0.05 maxValidatorSetStaleness = time.Minute throttlingPeriod = 10 * time.Second throttlingLimit = 2 From 0393cfb959b116bcfc80f43a793ec9c332faf695 Mon Sep 17 00:00:00 2001 From: Joshua Kim <20001595+joshua-kim@users.noreply.github.com> Date: Mon, 11 Sep 2023 12:12:59 -0400 Subject: [PATCH 19/36] nit --- plugin/evm/vm.go | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/plugin/evm/vm.go b/plugin/evm/vm.go index ce43a0f0ce..090a425986 100644 --- a/plugin/evm/vm.go +++ b/plugin/evm/vm.go @@ -135,8 +135,8 @@ const ( atomicTxGossipProtocol = 0x1 // gossip constants - txGossipMaxResponseSize = 20 * units.KiB - txGossipBloomMaxItems = 8 * 1024 + txGossipTargetResponseSize = 20 * units.KiB + txGossipBloomMaxItems = 8 * 1024 txGossipBloomFalsePositiveRate = 0.01 txGossipMaxFalsePositiveRate = 0.05 maxValidatorSetStaleness = time.Minute @@ -1004,7 +1004,7 @@ func (vm *VM) initBlockBuilding() error { ValidatorSet: vm.validators, Handler: &p2p.ThrottlerHandler{ Throttler: p2p.NewSlidingWindowThrottler(throttlingPeriod, throttlingLimit), - Handler: gossip.NewHandler[*GossipEthTx](ethTxPool, txGossipMaxResponseSize), + Handler: gossip.NewHandler[*GossipEthTx](ethTxPool, txGossipTargetResponseSize), }, } ethTxGossipClient, err := vm.router.RegisterAppProtocol(ethTxGossipProtocol, ethTxGossipHandler, vm.validators) @@ -1017,7 +1017,7 @@ func (vm *VM) initBlockBuilding() error { ValidatorSet: vm.validators, Handler: &p2p.ThrottlerHandler{ Throttler: p2p.NewSlidingWindowThrottler(throttlingPeriod, throttlingLimit), - Handler: gossip.NewHandler[*GossipAtomicTx](vm.mempool, txGossipMaxResponseSize), + Handler: gossip.NewHandler[*GossipAtomicTx](vm.mempool, txGossipTargetResponseSize), }, } atomicTxGossipClient, err := vm.router.RegisterAppProtocol(atomicTxGossipProtocol, atomicTxGossipHandler, vm.validators) From 474a03caf017ead19b3f4c0ede490927861beeeb Mon Sep 17 00:00:00 2001 From: Joshua Kim <20001595+joshua-kim@users.noreply.github.com> Date: Mon, 11 Sep 2023 12:35:54 -0400 Subject: [PATCH 20/36] nit --- plugin/evm/vm.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/plugin/evm/vm.go b/plugin/evm/vm.go index 090a425986..294f333cda 100644 --- a/plugin/evm/vm.go +++ b/plugin/evm/vm.go @@ -135,8 +135,8 @@ const ( atomicTxGossipProtocol = 0x1 // gossip constants - txGossipTargetResponseSize = 20 * units.KiB - txGossipBloomMaxItems = 8 * 1024 + txGossipTargetResponseSize = 20 * units.KiB + txGossipBloomMaxItems = 8 * 1024 txGossipBloomFalsePositiveRate = 0.01 txGossipMaxFalsePositiveRate = 0.05 maxValidatorSetStaleness = time.Minute From 844ccd2f8ec09f75ee7eafa45bbb5e118e0ea4a0 Mon Sep 17 00:00:00 2001 From: Joshua Kim <20001595+joshua-kim@users.noreply.github.com> Date: Mon, 11 Sep 2023 14:11:27 -0400 Subject: [PATCH 21/36] nit --- plugin/evm/mempool_atomic_gossiping_test.go | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/plugin/evm/mempool_atomic_gossiping_test.go b/plugin/evm/mempool_atomic_gossiping_test.go index a2e05e6286..a0f82a8c01 100644 --- a/plugin/evm/mempool_atomic_gossiping_test.go +++ b/plugin/evm/mempool_atomic_gossiping_test.go @@ -108,15 +108,13 @@ func TestMempoolMaxMempoolSizeHandling(t *testing.T) { // shortcut to simulated almost filled mempool mempool.maxSize = 0 - err := mempool.AddTx(tx) - assert.ErrorIs(err, errTooManyAtomicTx) + assert.ErrorIs(mempool.AddTx(tx), errTooManyAtomicTx) assert.False(mempool.has(tx.ID())) // shortcut to simulated empty mempool mempool.maxSize = defaultMempoolSize - err = mempool.AddTx(tx) - assert.NoError(err) + assert.NoError(mempool.AddTx(tx)) assert.True(mempool.has(tx.ID())) } @@ -195,12 +193,10 @@ func TestMempoolPriorityDrop(t *testing.T) { mempool.maxSize = 1 tx1 := createImportTx(t, vm, ids.ID{1}, params.AvalancheAtomicTxFee) - err := mempool.AddTx(tx1) - assert.NoError(err) + assert.NoError(mempool.AddTx(tx1)) assert.True(mempool.has(tx1.ID())) tx2 := createImportTx(t, vm, ids.ID{2}, params.AvalancheAtomicTxFee) - err = mempool.AddTx(tx2) - assert.ErrorIs(err, errInsufficientAtomicTxFee) + assert.ErrorIs(mempool.AddTx(tx2), errInsufficientAtomicTxFee) assert.True(mempool.has(tx1.ID())) assert.False(mempool.has(tx2.ID())) tx3 := createImportTx(t, vm, ids.ID{3}, 2*params.AvalancheAtomicTxFee) From db51063784cd039441643c95e9b0fd41d29a77bd Mon Sep 17 00:00:00 2001 From: Joshua Kim <20001595+joshua-kim@users.noreply.github.com> Date: Mon, 11 Sep 2023 18:30:14 -0400 Subject: [PATCH 22/36] nits --- go.mod | 2 +- go.sum | 2 + peer/network_test.go | 27 ++++++------ plugin/evm/gossip_mempool.go | 17 ++------ plugin/evm/tx_gossip_test.go | 13 +++--- plugin/evm/vm.go | 79 ++++++++++++++++++++++++------------ 6 files changed, 81 insertions(+), 59 deletions(-) diff --git a/go.mod b/go.mod index bd2dbad37c..a4e09d8e78 100644 --- a/go.mod +++ b/go.mod @@ -4,7 +4,7 @@ go 1.19 require ( github.com/VictoriaMetrics/fastcache v1.10.0 - github.com/ava-labs/avalanchego v1.10.10-rc.1 + github.com/ava-labs/avalanchego v1.10.10-rc.2 github.com/cespare/cp v0.1.0 github.com/cockroachdb/pebble v0.0.0-20230209160836-829675f94811 github.com/davecgh/go-spew v1.1.1 diff --git a/go.sum b/go.sum index 5ad8973b3e..810df82f35 100644 --- a/go.sum +++ b/go.sum @@ -57,6 +57,8 @@ github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kd github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5doyWs3UAsr3K4I6qtAmlQcZDesFNEHPZAzj8= github.com/ava-labs/avalanchego v1.10.10-rc.1 h1:dPJISEWqL3tdUShe6RuB8CFuXl3rsH8617sXbLBjkIE= github.com/ava-labs/avalanchego v1.10.10-rc.1/go.mod h1:C8R5uiltpc8MQ62ixxgODR+15mesWF0aAw3H+Qrl9Iw= +github.com/ava-labs/avalanchego v1.10.10-rc.2 h1:nlHc1JwKb5TEc9oqPU2exvOpazhxr11N2ym/LzYxv4k= +github.com/ava-labs/avalanchego v1.10.10-rc.2/go.mod h1:BN97sZppDSvIMIfEjrLTjdPTFkGLkb0ISJHEcoxMMNk= github.com/aymerick/raymond v2.0.3-0.20180322193309-b565731e1464+incompatible/go.mod h1:osfaiScAUVup+UC9Nfq76eWqDhXlp+4UYaA8uhTBO6g= github.com/benbjohnson/clock v1.3.0 h1:ip6w0uFQkncKQ979AypyG0ER7mqUSBdKLOgAle/AT8A= github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= diff --git a/peer/network_test.go b/peer/network_test.go index 13615630e7..039ad94ea4 100644 --- a/peer/network_test.go +++ b/peer/network_test.go @@ -17,6 +17,7 @@ import ( "github.com/ava-labs/avalanchego/utils/logging" "github.com/ava-labs/avalanchego/utils/set" ethcommon "github.com/ethereum/go-ethereum/common" + "github.com/prometheus/client_golang/prometheus" "github.com/ava-labs/coreth/plugin/evm/message" @@ -57,7 +58,7 @@ var ( func TestNetworkDoesNotConnectToItself(t *testing.T) { selfNodeID := ids.GenerateTestNodeID() - n := NewNetwork(p2p.NewRouter(logging.NoLog{}, nil), nil, nil, nil, selfNodeID, 1, 1) + n := NewNetwork(p2p.NewRouter(logging.NoLog{}, nil, prometheus.NewRegistry(), ""), nil, nil, nil, selfNodeID, 1, 1) assert.NoError(t, n.Connected(context.Background(), selfNodeID, defaultPeerVersion)) assert.EqualValues(t, 0, n.Size()) } @@ -93,7 +94,7 @@ func TestRequestAnyRequestsRoutingAndResponse(t *testing.T) { codecManager := buildCodec(t, HelloRequest{}, HelloResponse{}) crossChainCodecManager := buildCodec(t, ExampleCrossChainRequest{}, ExampleCrossChainResponse{}) - net = NewNetwork(p2p.NewRouter(logging.NoLog{}, nil), sender, codecManager, crossChainCodecManager, ids.EmptyNodeID, 16, 16) + net = NewNetwork(p2p.NewRouter(logging.NoLog{}, nil, prometheus.NewRegistry(), ""), sender, codecManager, crossChainCodecManager, ids.EmptyNodeID, 16, 16) net.SetRequestHandler(&HelloGreetingRequestHandler{codec: codecManager}) client := NewNetworkClient(net) nodeID := ids.GenerateTestNodeID() @@ -168,7 +169,7 @@ func TestRequestRequestsRoutingAndResponse(t *testing.T) { codecManager := buildCodec(t, HelloRequest{}, HelloResponse{}) crossChainCodecManager := buildCodec(t, ExampleCrossChainRequest{}, ExampleCrossChainResponse{}) - net = NewNetwork(p2p.NewRouter(logging.NoLog{}, nil), sender, codecManager, crossChainCodecManager, ids.EmptyNodeID, 16, 16) + net = NewNetwork(p2p.NewRouter(logging.NoLog{}, nil, prometheus.NewRegistry(), ""), sender, codecManager, crossChainCodecManager, ids.EmptyNodeID, 16, 16) net.SetRequestHandler(&HelloGreetingRequestHandler{codec: codecManager}) client := NewNetworkClient(net) @@ -248,7 +249,7 @@ func TestAppRequestOnShutdown(t *testing.T) { codecManager := buildCodec(t, HelloRequest{}, HelloResponse{}) crossChainCodecManager := buildCodec(t, ExampleCrossChainRequest{}, ExampleCrossChainResponse{}) - net = NewNetwork(p2p.NewRouter(logging.NoLog{}, nil), sender, codecManager, crossChainCodecManager, ids.EmptyNodeID, 1, 1) + net = NewNetwork(p2p.NewRouter(logging.NoLog{}, nil, prometheus.NewRegistry(), ""), sender, codecManager, crossChainCodecManager, ids.EmptyNodeID, 1, 1) client := NewNetworkClient(net) nodeID := ids.GenerateTestNodeID() require.NoError(t, net.Connected(context.Background(), nodeID, defaultPeerVersion)) @@ -297,7 +298,7 @@ func TestRequestMinVersion(t *testing.T) { } // passing nil as codec works because the net.AppRequest is never called - net = NewNetwork(p2p.NewRouter(logging.NoLog{}, nil), sender, codecManager, crossChainCodecManager, ids.EmptyNodeID, 1, 16) + net = NewNetwork(p2p.NewRouter(logging.NoLog{}, nil, prometheus.NewRegistry(), ""), sender, codecManager, crossChainCodecManager, ids.EmptyNodeID, 1, 16) client := NewNetworkClient(net) requestMessage := TestMessage{Message: "this is a request"} requestBytes, err := message.RequestToBytes(codecManager, requestMessage) @@ -360,7 +361,7 @@ func TestOnRequestHonoursDeadline(t *testing.T) { processingDuration: 500 * time.Millisecond, } - net = NewNetwork(p2p.NewRouter(logging.NoLog{}, nil), sender, codecManager, crossChainCodecManager, ids.EmptyNodeID, 1, 1) + net = NewNetwork(p2p.NewRouter(logging.NoLog{}, nil, prometheus.NewRegistry(), ""), sender, codecManager, crossChainCodecManager, ids.EmptyNodeID, 1, 1) net.SetRequestHandler(requestHandler) nodeID := ids.GenerateTestNodeID() @@ -400,7 +401,7 @@ func TestGossip(t *testing.T) { } gossipHandler := &testGossipHandler{} - clientNetwork = NewNetwork(p2p.NewRouter(logging.NoLog{}, nil), sender, codecManager, crossChainCodecManager, ids.EmptyNodeID, 1, 1) + clientNetwork = NewNetwork(p2p.NewRouter(logging.NoLog{}, nil, prometheus.NewRegistry(), ""), sender, codecManager, crossChainCodecManager, ids.EmptyNodeID, 1, 1) clientNetwork.SetGossipHandler(gossipHandler) assert.NoError(t, clientNetwork.Connected(context.Background(), nodeID, defaultPeerVersion)) @@ -427,7 +428,7 @@ func TestHandleInvalidMessages(t *testing.T) { requestID := uint32(1) sender := testAppSender{} - clientNetwork := NewNetwork(p2p.NewRouter(logging.NoLog{}, nil), sender, codecManager, crossChainCodecManager, ids.EmptyNodeID, 1, 1) + clientNetwork := NewNetwork(p2p.NewRouter(logging.NoLog{}, nil, prometheus.NewRegistry(), ""), sender, codecManager, crossChainCodecManager, ids.EmptyNodeID, 1, 1) clientNetwork.SetGossipHandler(message.NoopMempoolGossipHandler{}) clientNetwork.SetRequestHandler(&testRequestHandler{}) @@ -476,7 +477,7 @@ func TestNetworkPropagatesRequestHandlerError(t *testing.T) { requestID := uint32(1) sender := testAppSender{} - clientNetwork := NewNetwork(p2p.NewRouter(logging.NoLog{}, nil), sender, codecManager, crossChainCodecManager, ids.EmptyNodeID, 1, 1) + clientNetwork := NewNetwork(p2p.NewRouter(logging.NoLog{}, nil, prometheus.NewRegistry(), ""), 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 @@ -516,7 +517,7 @@ func TestCrossChainAppRequest(t *testing.T) { }, } - net = NewNetwork(p2p.NewRouter(logging.NoLog{}, nil), sender, codecManager, crossChainCodecManager, ids.EmptyNodeID, 1, 1) + net = NewNetwork(p2p.NewRouter(logging.NoLog{}, nil, prometheus.NewRegistry(), ""), sender, codecManager, crossChainCodecManager, ids.EmptyNodeID, 1, 1) net.SetCrossChainRequestHandler(&testCrossChainHandler{codec: crossChainCodecManager}) client := NewNetworkClient(net) @@ -571,7 +572,7 @@ func TestCrossChainRequestRequestsRoutingAndResponse(t *testing.T) { codecManager := buildCodec(t, TestMessage{}) crossChainCodecManager := buildCodec(t, ExampleCrossChainRequest{}, ExampleCrossChainResponse{}) - net = NewNetwork(p2p.NewRouter(logging.NoLog{}, nil), sender, codecManager, crossChainCodecManager, ids.EmptyNodeID, 1, 1) + net = NewNetwork(p2p.NewRouter(logging.NoLog{}, nil, prometheus.NewRegistry(), ""), sender, codecManager, crossChainCodecManager, ids.EmptyNodeID, 1, 1) net.SetCrossChainRequestHandler(&testCrossChainHandler{codec: crossChainCodecManager}) client := NewNetworkClient(net) @@ -631,7 +632,7 @@ func TestCrossChainRequestOnShutdown(t *testing.T) { } codecManager := buildCodec(t, TestMessage{}) crossChainCodecManager := buildCodec(t, ExampleCrossChainRequest{}, ExampleCrossChainResponse{}) - net = NewNetwork(p2p.NewRouter(logging.NoLog{}, nil), sender, codecManager, crossChainCodecManager, ids.EmptyNodeID, 1, 1) + net = NewNetwork(p2p.NewRouter(logging.NoLog{}, nil, prometheus.NewRegistry(), ""), sender, codecManager, crossChainCodecManager, ids.EmptyNodeID, 1, 1) client := NewNetworkClient(net) exampleCrossChainRequest := ExampleCrossChainRequest{ @@ -664,7 +665,7 @@ func TestSDKRouting(t *testing.T) { } protocol := 0 handler := &testSDKHandler{} - router := p2p.NewRouter(logging.NoLog{}, sender) + router := p2p.NewRouter(logging.NoLog{}, sender, prometheus.NewRegistry(), "") _, err := router.RegisterAppProtocol(uint64(protocol), handler, nil) require.NoError(err) diff --git a/plugin/evm/gossip_mempool.go b/plugin/evm/gossip_mempool.go index ee6cedf89f..bd74d8c5e0 100644 --- a/plugin/evm/gossip_mempool.go +++ b/plugin/evm/gossip_mempool.go @@ -37,21 +37,10 @@ func (tx *GossipAtomicTx) Marshal() ([]byte, error) { } func (tx *GossipAtomicTx) Unmarshal(bytes []byte) error { - result := &Tx{} + atomicTx, err := ExtractAtomicTx(bytes, Codec) + tx.Tx = atomicTx - _, err := Codec.Unmarshal(bytes, result) - if err != nil { - return err - } - unsignedBytes, err := Codec.Marshal(codecVersion, &result.UnsignedAtomicTx) - if err != nil { - return err - } - - result.Initialize(unsignedBytes, bytes) - tx.Tx = result - - return nil + return err } func NewGossipEthTxPool(mempool *txpool.TxPool) (*GossipEthTxPool, error) { diff --git a/plugin/evm/tx_gossip_test.go b/plugin/evm/tx_gossip_test.go index 1ad2857911..f2dc6cf1b6 100644 --- a/plugin/evm/tx_gossip_test.go +++ b/plugin/evm/tx_gossip_test.go @@ -20,6 +20,7 @@ import ( "github.com/ava-labs/avalanchego/utils/crypto/secp256k1" "github.com/ava-labs/avalanchego/utils/logging" "github.com/ava-labs/avalanchego/utils/set" + "github.com/prometheus/client_golang/prometheus" "github.com/stretchr/testify/require" "go.uber.org/mock/gomock" @@ -62,7 +63,7 @@ func TestEthTxGossip(t *testing.T) { // sender for the peer requesting gossip from [vm] ctrl := gomock.NewController(t) peerSender := common.NewMockSender(ctrl) - router := p2p.NewRouter(logging.NoLog{}, peerSender) + router := p2p.NewRouter(logging.NoLog{}, peerSender, prometheus.NewRegistry(), "") // we're only making client requests, so we don't need a server handler client, err := router.RegisterAppProtocol(ethTxGossipProtocol, nil, nil) @@ -108,7 +109,7 @@ func TestEthTxGossip(t *testing.T) { // Ask the VM for any new transactions. We should get nothing at first. wg.Add(1) - onResponse := func(nodeID ids.NodeID, responseBytes []byte, err error) { + onResponse := func(_ context.Context, nodeID ids.NodeID, responseBytes []byte, err error) { require.NoError(err) response := &sdk.PullGossipResponse{} @@ -135,7 +136,7 @@ func TestEthTxGossip(t *testing.T) { // Ask the VM for new transactions. We should get the newly issued tx. wg.Add(1) - onResponse = func(nodeID ids.NodeID, responseBytes []byte, err error) { + onResponse = func(_ context.Context, nodeID ids.NodeID, responseBytes []byte, err error) { require.NoError(err) response := &sdk.PullGossipResponse{} @@ -168,7 +169,7 @@ func TestAtomicTxGossip(t *testing.T) { // sender for the peer requesting gossip from [vm] ctrl := gomock.NewController(t) peerSender := common.NewMockSender(ctrl) - router := p2p.NewRouter(logging.NoLog{}, peerSender) + router := p2p.NewRouter(logging.NoLog{}, peerSender, prometheus.NewRegistry(), "") // we're only making client requests, so we don't need a server handler client, err := router.RegisterAppProtocol(atomicTxGossipProtocol, nil, nil) @@ -212,7 +213,7 @@ func TestAtomicTxGossip(t *testing.T) { // Ask the VM for any new transactions. We should get nothing at first. wg.Add(1) - onResponse := func(nodeID ids.NodeID, responseBytes []byte, err error) { + onResponse := func(_ context.Context, nodeID ids.NodeID, responseBytes []byte, err error) { require.NoError(err) response := &sdk.PullGossipResponse{} @@ -235,7 +236,7 @@ func TestAtomicTxGossip(t *testing.T) { // Ask the VM for new transactions. We should get the newly issued tx. wg.Add(1) - onResponse = func(nodeID ids.NodeID, responseBytes []byte, err error) { + onResponse = func(_ context.Context, nodeID ids.NodeID, responseBytes []byte, err error) { require.NoError(err) response := &sdk.PullGossipResponse{} diff --git a/plugin/evm/vm.go b/plugin/evm/vm.go index 294f333cda..e0c59d7f3b 100644 --- a/plugin/evm/vm.go +++ b/plugin/evm/vm.go @@ -135,7 +135,6 @@ const ( atomicTxGossipProtocol = 0x1 // gossip constants - txGossipTargetResponseSize = 20 * units.KiB txGossipBloomMaxItems = 8 * 1024 txGossipBloomFalsePositiveRate = 0.01 txGossipMaxFalsePositiveRate = 0.05 @@ -144,10 +143,16 @@ const ( throttlingLimit = 2 ) -var txGossipConfig = gossip.Config{ - Frequency: 10 * time.Second, - PollSize: 10, -} +var ( + txGossipConfig = gossip.Config{ + Frequency: 10 * time.Second, + PollSize: 10, + } + + gossipHandlerConfig = gossip.HandlerConfig{ + TargetResponseSize: 20 * units.KiB, + } +) // Define the API endpoints for the VM const ( @@ -278,9 +283,7 @@ type VM struct { builder *blockBuilder - gossiper Gossiper - ethTxGossiper *gossip.Gossiper[GossipEthTx, *GossipEthTx] - atomicTxGossiper *gossip.Gossiper[GossipAtomicTx, *GossipAtomicTx] + gossiper Gossiper baseCodec codec.Registry codec codec.Manager @@ -300,13 +303,12 @@ type VM struct { client peer.NetworkClient networkCodec codec.Manager - validators *p2p.Validators - router *p2p.Router - ethTxGossipClient *p2p.Client - atomicTxGossipClient *p2p.Client + validators *p2p.Validators + router *p2p.Router // Metrics multiGatherer avalanchegoMetrics.MultiGatherer + metrics *prometheus.Registry bootstrapped bool IsPlugin bool @@ -543,7 +545,7 @@ func (vm *VM) Initialize( // initialize peer network vm.validators = p2p.NewValidators(vm.ctx.Log, vm.ctx.SubnetID, vm.ctx.ValidatorState, maxValidatorSetStaleness) - vm.router = p2p.NewRouter(vm.ctx.Log, appSender) + vm.router = p2p.NewRouter(vm.ctx.Log, appSender, vm.metrics, "p2p") vm.networkCodec = message.Codec vm.Network = peer.NewNetwork(vm.router, appSender, vm.networkCodec, message.CrossChainCodec, chainCtx.NodeID, vm.config.MaxOutboundActiveRequests, vm.config.MaxOutboundActiveCrossChainRequests) vm.client = peer.NewNetworkClient(vm.Network) @@ -595,6 +597,7 @@ func (vm *VM) Initialize( } func (vm *VM) initializeMetrics() error { + vm.metrics = prometheus.NewRegistry() vm.multiGatherer = avalanchegoMetrics.NewMultiGatherer() // If metrics are enabled, register the default metrics regitry if metrics.Enabled { @@ -606,6 +609,9 @@ func (vm *VM) initializeMetrics() error { if err := vm.ctx.Metrics.Register(vm.multiGatherer); err != nil { return err } + if err := vm.ctx.Metrics.Register(vm.metrics); err != nil { + return err + } } return nil } @@ -1000,47 +1006,70 @@ func (vm *VM) initBlockBuilding() error { vm.shutdownWg.Done() }() - ethTxGossipHandler := &p2p.ValidatorHandler{ + var ( + ethTxGossipHandler p2p.Handler + atomicTxGossipHandler p2p.Handler + ) + + ethTxGossipHandler, err = gossip.NewHandler[*GossipEthTx](ethTxPool, gossipHandlerConfig, vm.metrics) + if err != nil { + return err + } + ethTxGossipHandler = &p2p.ValidatorHandler{ ValidatorSet: vm.validators, Handler: &p2p.ThrottlerHandler{ Throttler: p2p.NewSlidingWindowThrottler(throttlingPeriod, throttlingLimit), - Handler: gossip.NewHandler[*GossipEthTx](ethTxPool, txGossipTargetResponseSize), + Handler: ethTxGossipHandler, }, } ethTxGossipClient, err := vm.router.RegisterAppProtocol(ethTxGossipProtocol, ethTxGossipHandler, vm.validators) if err != nil { return err } - vm.ethTxGossipClient = ethTxGossipClient - atomicTxGossipHandler := &p2p.ValidatorHandler{ + atomicTxGossipHandler, err = gossip.NewHandler[*GossipAtomicTx](vm.mempool, gossipHandlerConfig, vm.metrics) + if err != nil { + return err + } + + atomicTxGossipHandler = &p2p.ValidatorHandler{ ValidatorSet: vm.validators, Handler: &p2p.ThrottlerHandler{ Throttler: p2p.NewSlidingWindowThrottler(throttlingPeriod, throttlingLimit), - Handler: gossip.NewHandler[*GossipAtomicTx](vm.mempool, txGossipTargetResponseSize), + Handler: atomicTxGossipHandler, }, } + atomicTxGossipClient, err := vm.router.RegisterAppProtocol(atomicTxGossipProtocol, atomicTxGossipHandler, vm.validators) if err != nil { return err } - vm.atomicTxGossipClient = atomicTxGossipClient - vm.ethTxGossiper = gossip.NewGossiper[GossipEthTx, *GossipEthTx]( + ethTxGossiper, err := gossip.NewGossiper[GossipEthTx, *GossipEthTx]( txGossipConfig, vm.ctx.Log, ethTxPool, - vm.ethTxGossipClient, + ethTxGossipClient, + vm.metrics, ) - go vm.ethTxGossiper.Gossip(vm.backgroundCtx) + if err != nil { + return err + } + + go ethTxGossiper.Gossip(vm.backgroundCtx) - vm.atomicTxGossiper = gossip.NewGossiper[GossipAtomicTx, *GossipAtomicTx]( + atomicTxGossiper, err := gossip.NewGossiper[GossipAtomicTx, *GossipAtomicTx]( txGossipConfig, vm.ctx.Log, vm.mempool, - vm.atomicTxGossipClient, + atomicTxGossipClient, + vm.metrics, ) - go vm.atomicTxGossiper.Gossip(vm.backgroundCtx) + if err != nil { + return err + } + + go atomicTxGossiper.Gossip(vm.backgroundCtx) return nil } From fbfa3726fda8ba53184cc4fef4fb82621f2328a7 Mon Sep 17 00:00:00 2001 From: Joshua Kim <20001595+joshua-kim@users.noreply.github.com> Date: Mon, 11 Sep 2023 18:49:42 -0400 Subject: [PATCH 23/36] nit --- plugin/evm/vm.go | 42 +++++++++++++++++++++++++++--------------- 1 file changed, 27 insertions(+), 15 deletions(-) diff --git a/plugin/evm/vm.go b/plugin/evm/vm.go index e0c59d7f3b..06e4f9da9f 100644 --- a/plugin/evm/vm.go +++ b/plugin/evm/vm.go @@ -138,19 +138,31 @@ const ( txGossipBloomMaxItems = 8 * 1024 txGossipBloomFalsePositiveRate = 0.01 txGossipMaxFalsePositiveRate = 0.05 + txGossipTargetResponseSize = 20 * units.KiB maxValidatorSetStaleness = time.Minute throttlingPeriod = 10 * time.Second throttlingLimit = 2 ) var ( - txGossipConfig = gossip.Config{ + ethTxGossipConfig = gossip.Config{ + Namespace: "eth_tx_gossip", Frequency: 10 * time.Second, PollSize: 10, } + ethTxGossipHandlerConfig = gossip.HandlerConfig{ + Namespace: "eth_tx_gossip", + TargetResponseSize: txGossipTargetResponseSize, + } - gossipHandlerConfig = gossip.HandlerConfig{ - TargetResponseSize: 20 * units.KiB, + atomicTxGossipConfig = gossip.Config{ + Namespace: "atomic_tx_gossip", + Frequency: 10 * time.Second, + PollSize: 10, + } + atomicTxGossipHandlerConfig = gossip.HandlerConfig{ + Namespace: "atomic_tx_gossip", + TargetResponseSize: txGossipTargetResponseSize, } ) @@ -308,7 +320,7 @@ type VM struct { // Metrics multiGatherer avalanchegoMetrics.MultiGatherer - metrics *prometheus.Registry + sdkMetrics *prometheus.Registry bootstrapped bool IsPlugin bool @@ -545,7 +557,7 @@ func (vm *VM) Initialize( // initialize peer network vm.validators = p2p.NewValidators(vm.ctx.Log, vm.ctx.SubnetID, vm.ctx.ValidatorState, maxValidatorSetStaleness) - vm.router = p2p.NewRouter(vm.ctx.Log, appSender, vm.metrics, "p2p") + vm.router = p2p.NewRouter(vm.ctx.Log, appSender, vm.sdkMetrics, "sdk") vm.networkCodec = message.Codec vm.Network = peer.NewNetwork(vm.router, appSender, vm.networkCodec, message.CrossChainCodec, chainCtx.NodeID, vm.config.MaxOutboundActiveRequests, vm.config.MaxOutboundActiveCrossChainRequests) vm.client = peer.NewNetworkClient(vm.Network) @@ -597,7 +609,7 @@ func (vm *VM) Initialize( } func (vm *VM) initializeMetrics() error { - vm.metrics = prometheus.NewRegistry() + vm.sdkMetrics = prometheus.NewRegistry() vm.multiGatherer = avalanchegoMetrics.NewMultiGatherer() // If metrics are enabled, register the default metrics regitry if metrics.Enabled { @@ -605,11 +617,11 @@ func (vm *VM) initializeMetrics() error { if err := vm.multiGatherer.Register(ethMetricsPrefix, gatherer); err != nil { return err } - // Register [multiGatherer] after registerers have been registered to it - if err := vm.ctx.Metrics.Register(vm.multiGatherer); err != nil { + if err := vm.multiGatherer.Register("sdk", vm.sdkMetrics); err != nil { return err } - if err := vm.ctx.Metrics.Register(vm.metrics); err != nil { + // Register [multiGatherer] after registerers have been registered to it + if err := vm.ctx.Metrics.Register(vm.multiGatherer); err != nil { return err } } @@ -1011,7 +1023,7 @@ func (vm *VM) initBlockBuilding() error { atomicTxGossipHandler p2p.Handler ) - ethTxGossipHandler, err = gossip.NewHandler[*GossipEthTx](ethTxPool, gossipHandlerConfig, vm.metrics) + ethTxGossipHandler, err = gossip.NewHandler[*GossipEthTx](ethTxPool, ethTxGossipHandlerConfig, vm.sdkMetrics) if err != nil { return err } @@ -1027,7 +1039,7 @@ func (vm *VM) initBlockBuilding() error { return err } - atomicTxGossipHandler, err = gossip.NewHandler[*GossipAtomicTx](vm.mempool, gossipHandlerConfig, vm.metrics) + atomicTxGossipHandler, err = gossip.NewHandler[*GossipAtomicTx](vm.mempool, atomicTxGossipHandlerConfig, vm.sdkMetrics) if err != nil { return err } @@ -1046,11 +1058,11 @@ func (vm *VM) initBlockBuilding() error { } ethTxGossiper, err := gossip.NewGossiper[GossipEthTx, *GossipEthTx]( - txGossipConfig, + ethTxGossipConfig, vm.ctx.Log, ethTxPool, ethTxGossipClient, - vm.metrics, + vm.sdkMetrics, ) if err != nil { return err @@ -1059,11 +1071,11 @@ func (vm *VM) initBlockBuilding() error { go ethTxGossiper.Gossip(vm.backgroundCtx) atomicTxGossiper, err := gossip.NewGossiper[GossipAtomicTx, *GossipAtomicTx]( - txGossipConfig, + atomicTxGossipConfig, vm.ctx.Log, vm.mempool, atomicTxGossipClient, - vm.metrics, + vm.sdkMetrics, ) if err != nil { return err From ec525c9e98e19061027403218a3343142206540e Mon Sep 17 00:00:00 2001 From: Joshua Kim <20001595+joshua-kim@users.noreply.github.com> Date: Mon, 11 Sep 2023 18:58:20 -0400 Subject: [PATCH 24/36] nit --- scripts/versions.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/versions.sh b/scripts/versions.sh index 0e50203069..7b3a476121 100644 --- a/scripts/versions.sh +++ b/scripts/versions.sh @@ -1,4 +1,4 @@ #!/usr/bin/env bash # Don't export them as they're used in the context of other calls -avalanche_version=${AVALANCHE_VERSION:-'v1.10.10-rc.1'} +avalanche_version=${AVALANCHE_VERSION:-'v1.10.10-rc.2'} From 25d0bf3a4754899a518a61889af19092391e6322 Mon Sep 17 00:00:00 2001 From: Joshua Kim <20001595+joshua-kim@users.noreply.github.com> Date: Mon, 11 Sep 2023 19:01:22 -0400 Subject: [PATCH 25/36] fix ci --- Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Dockerfile b/Dockerfile index 5f088fb36c..0187070cf5 100644 --- a/Dockerfile +++ b/Dockerfile @@ -17,7 +17,7 @@ WORKDIR $GOPATH/src/github.com/ava-labs/avalanchego RUN go mod download # Replace the coreth dependency RUN go mod edit -replace github.com/ava-labs/coreth=../coreth -RUN go mod download && go mod tidy -compat=1.19 +RUN go mod download && go mod tidy -compat=1.20 # Build the AvalancheGo binary with local version of coreth. RUN ./scripts/build_avalanche.sh From 78698af5dbe31e9999e15ae3c75e0c428d6762fb Mon Sep 17 00:00:00 2001 From: Joshua Kim <20001595+joshua-kim@users.noreply.github.com> Date: Mon, 11 Sep 2023 19:04:45 -0400 Subject: [PATCH 26/36] fix docker version --- Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Dockerfile b/Dockerfile index 0187070cf5..17f4913f66 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,5 +1,5 @@ # ============= Compilation Stage ================ -FROM golang:1.19.12-bullseye AS builder +FROM golang:1.20.8-bullseye AS builder ARG AVALANCHE_VERSION From 6d8f21e87a8174f46a0869b2b2b50f41ba485b18 Mon Sep 17 00:00:00 2001 From: Joshua Kim <20001595+joshua-kim@users.noreply.github.com> Date: Mon, 11 Sep 2023 19:06:33 -0400 Subject: [PATCH 27/36] nit --- go.mod | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/go.mod b/go.mod index a4e09d8e78..b4ee923abf 100644 --- a/go.mod +++ b/go.mod @@ -1,6 +1,6 @@ module github.com/ava-labs/coreth -go 1.19 +go 1.20 require ( github.com/VictoriaMetrics/fastcache v1.10.0 From d4b722c0897f986281f9c2655736fca56b51bd3d Mon Sep 17 00:00:00 2001 From: Joshua Kim <20001595+joshua-kim@users.noreply.github.com> Date: Mon, 11 Sep 2023 19:11:19 -0400 Subject: [PATCH 28/36] update go version in ci --- .github/workflows/ci.yml | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index ceb4d32e6a..89438dc90a 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -31,7 +31,7 @@ jobs: token: ${{ secrets.AVALANCHE_PAT }} - uses: actions/setup-go@v3 with: - go-version: '~1.19.12' + go-version: '~1.20' check-latest: true - name: change avalanchego dep if: ${{ github.event_name == 'workflow_dispatch' }} @@ -65,7 +65,7 @@ jobs: token: ${{ secrets.AVALANCHE_PAT }} - uses: actions/setup-go@v3 with: - go-version: '~1.19.12' + go-version: '~1.20' check-latest: true - name: change avalanchego dep if: ${{ github.event_name == 'workflow_dispatch' }} @@ -98,7 +98,7 @@ jobs: token: ${{ secrets.AVALANCHE_PAT }} - uses: actions/setup-go@v3 with: - go-version: '~1.19.12' + go-version: '~1.20' check-latest: true - name: change avalanchego dep if: ${{ github.event_name == 'workflow_dispatch' }} @@ -129,7 +129,7 @@ jobs: token: ${{ secrets.AVALANCHE_PAT }} - uses: actions/setup-go@v3 with: - go-version: '~1.19.12' + go-version: '~1.20' check-latest: true - name: change avalanchego dep if: ${{ github.event_name == 'workflow_dispatch' }} @@ -166,7 +166,7 @@ jobs: token: ${{ secrets.AVALANCHE_PAT }} - uses: actions/setup-go@v3 with: - go-version: '~1.19.12' + go-version: '~1.20' check-latest: true - name: Run e2e tests run: E2E_SERIAL=1 ./scripts/tests.e2e.sh From 2094e8d061067ffb6fe29c5bdd6a822bed75632d Mon Sep 17 00:00:00 2001 From: Joshua Kim <20001595+joshua-kim@users.noreply.github.com> Date: Mon, 11 Sep 2023 19:15:39 -0400 Subject: [PATCH 29/36] nit --- .github/workflows/ci.yml | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 89438dc90a..dd67e75351 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -31,7 +31,7 @@ jobs: token: ${{ secrets.AVALANCHE_PAT }} - uses: actions/setup-go@v3 with: - go-version: '~1.20' + go-version: '~1.20.8' check-latest: true - name: change avalanchego dep if: ${{ github.event_name == 'workflow_dispatch' }} @@ -65,7 +65,7 @@ jobs: token: ${{ secrets.AVALANCHE_PAT }} - uses: actions/setup-go@v3 with: - go-version: '~1.20' + go-version: '~1.20.8' check-latest: true - name: change avalanchego dep if: ${{ github.event_name == 'workflow_dispatch' }} @@ -98,7 +98,7 @@ jobs: token: ${{ secrets.AVALANCHE_PAT }} - uses: actions/setup-go@v3 with: - go-version: '~1.20' + go-version: '~1.20.8' check-latest: true - name: change avalanchego dep if: ${{ github.event_name == 'workflow_dispatch' }} @@ -129,7 +129,7 @@ jobs: token: ${{ secrets.AVALANCHE_PAT }} - uses: actions/setup-go@v3 with: - go-version: '~1.20' + go-version: '~1.20.8' check-latest: true - name: change avalanchego dep if: ${{ github.event_name == 'workflow_dispatch' }} @@ -166,7 +166,7 @@ jobs: token: ${{ secrets.AVALANCHE_PAT }} - uses: actions/setup-go@v3 with: - go-version: '~1.20' + go-version: '~1.20.8' check-latest: true - name: Run e2e tests run: E2E_SERIAL=1 ./scripts/tests.e2e.sh From fe04ca8ba361963145711f93591ed9944e23635b Mon Sep 17 00:00:00 2001 From: Joshua Kim <20001595+joshua-kim@users.noreply.github.com> Date: Tue, 12 Sep 2023 10:36:32 -0400 Subject: [PATCH 30/36] nit --- plugin/evm/tx_gossip_test.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/plugin/evm/tx_gossip_test.go b/plugin/evm/tx_gossip_test.go index f2dc6cf1b6..890a9e2d06 100644 --- a/plugin/evm/tx_gossip_test.go +++ b/plugin/evm/tx_gossip_test.go @@ -132,7 +132,7 @@ func TestEthTxGossip(t *testing.T) { require.Nil(errs[0]) // wait so we aren't throttled by the vm - time.Sleep(throttlingPeriod / throttlingLimit) + time.Sleep(5 * time.Second) // Ask the VM for new transactions. We should get the newly issued tx. wg.Add(1) @@ -232,7 +232,7 @@ func TestAtomicTxGossip(t *testing.T) { <-issuer // wait so we aren't throttled by the vm - time.Sleep(throttlingPeriod / throttlingLimit) + time.Sleep(5 * time.Second) // Ask the VM for new transactions. We should get the newly issued tx. wg.Add(1) From 678f640138adf6193c17aba89b279a11f9d4b4ad Mon Sep 17 00:00:00 2001 From: Joshua Kim <20001595+joshua-kim@users.noreply.github.com> Date: Tue, 12 Sep 2023 11:09:21 -0400 Subject: [PATCH 31/36] Squashed commit of the following: commit 2089a736d573e88d3b8d72679433809d51526d88 Author: Joshua Kim <20001595+joshua-kim@users.noreply.github.com> Date: Tue Sep 12 10:55:19 2023 -0400 nit commit 95fca6d21f9ae4b9d421fd6204c5ccc04f7447ba Author: Joshua Kim <20001595+joshua-kim@users.noreply.github.com> Date: Tue Sep 12 10:53:11 2023 -0400 nits commit 0b9deb01161666161cc949fbbe45a5d5bcb438a1 Author: Joshua Kim <20001595+joshua-kim@users.noreply.github.com> Date: Mon Sep 11 18:58:20 2023 -0400 update to avalanchego 1.10.10-rc.2 --- plugin/evm/vm.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugin/evm/vm.go b/plugin/evm/vm.go index 06e4f9da9f..5905073d5a 100644 --- a/plugin/evm/vm.go +++ b/plugin/evm/vm.go @@ -557,7 +557,7 @@ func (vm *VM) Initialize( // initialize peer network vm.validators = p2p.NewValidators(vm.ctx.Log, vm.ctx.SubnetID, vm.ctx.ValidatorState, maxValidatorSetStaleness) - vm.router = p2p.NewRouter(vm.ctx.Log, appSender, vm.sdkMetrics, "sdk") + vm.router = p2p.NewRouter(vm.ctx.Log, appSender, vm.sdkMetrics, "p2p") vm.networkCodec = message.Codec vm.Network = peer.NewNetwork(vm.router, appSender, vm.networkCodec, message.CrossChainCodec, chainCtx.NodeID, vm.config.MaxOutboundActiveRequests, vm.config.MaxOutboundActiveCrossChainRequests) vm.client = peer.NewNetworkClient(vm.Network) From d487de1266ee8f15cc6423e2164e0861b353b51a Mon Sep 17 00:00:00 2001 From: Joshua Kim <20001595+joshua-kim@users.noreply.github.com> Date: Tue, 12 Sep 2023 11:20:58 -0400 Subject: [PATCH 32/36] remove ctx field --- plugin/evm/vm.go | 18 ++++++++---------- 1 file changed, 8 insertions(+), 10 deletions(-) diff --git a/plugin/evm/vm.go b/plugin/evm/vm.go index 5905073d5a..0d25362ce5 100644 --- a/plugin/evm/vm.go +++ b/plugin/evm/vm.go @@ -246,9 +246,8 @@ func init() { // VM implements the snowman.ChainVM interface type VM struct { - ctx *snow.Context // TODO rename to snowCtx - backgroundCtx context.Context // TODO rename to ctx - cancel context.CancelFunc + ctx *snow.Context + cancel context.CancelFunc // *chain.State helps to implement the VM interface by wrapping blocks // with an efficient caching layer. *chain.State @@ -382,10 +381,6 @@ func (vm *VM) Initialize( vm.ctx = chainCtx - ctx, cancel := context.WithCancel(context.TODO()) - vm.backgroundCtx = ctx - vm.cancel = cancel - // Create logger alias, err := vm.ctx.BCLookup.PrimaryAlias(vm.ctx.ChainID) if err != nil { @@ -1001,6 +996,9 @@ func (vm *VM) SetState(_ context.Context, state snow.State) error { // initBlockBuilding starts goroutines to manage block building func (vm *VM) initBlockBuilding() error { + ctx, cancel := context.WithCancel(context.TODO()) + vm.cancel = cancel + // NOTE: gossip network must be initialized first otherwise ETH tx gossip will not work. gossipStats := NewGossipStats() vm.gossiper = vm.createGossiper(gossipStats) @@ -1014,7 +1012,7 @@ func (vm *VM) initBlockBuilding() error { } vm.shutdownWg.Add(1) go func() { - ethTxPool.Subscribe(vm.backgroundCtx) + ethTxPool.Subscribe(ctx) vm.shutdownWg.Done() }() @@ -1068,7 +1066,7 @@ func (vm *VM) initBlockBuilding() error { return err } - go ethTxGossiper.Gossip(vm.backgroundCtx) + go ethTxGossiper.Gossip(ctx) atomicTxGossiper, err := gossip.NewGossiper[GossipAtomicTx, *GossipAtomicTx]( atomicTxGossipConfig, @@ -1081,7 +1079,7 @@ func (vm *VM) initBlockBuilding() error { return err } - go atomicTxGossiper.Gossip(vm.backgroundCtx) + go atomicTxGossiper.Gossip(ctx) return nil } From 0c5a462d1af095c35049a03a000cc553bb8e58e9 Mon Sep 17 00:00:00 2001 From: Joshua Kim <20001595+joshua-kim@users.noreply.github.com> Date: Tue, 12 Sep 2023 11:30:26 -0400 Subject: [PATCH 33/36] add nil check --- plugin/evm/vm.go | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/plugin/evm/vm.go b/plugin/evm/vm.go index 0d25362ce5..5c2a8760d5 100644 --- a/plugin/evm/vm.go +++ b/plugin/evm/vm.go @@ -1119,7 +1119,9 @@ func (vm *VM) Shutdown(context.Context) error { if vm.ctx == nil { return nil } - vm.cancel() + if vm.cancel != nil { + vm.cancel() + } vm.Network.Shutdown() if err := vm.StateSyncClient.Shutdown(); err != nil { log.Error("error stopping state syncer", "err", err) From 0e347441439a9ebff4e9bd21b47278e3f1e82b01 Mon Sep 17 00:00:00 2001 From: Joshua Kim <20001595+joshua-kim@users.noreply.github.com> Date: Tue, 12 Sep 2023 11:31:52 -0400 Subject: [PATCH 34/36] oops --- plugin/evm/vm.go | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/plugin/evm/vm.go b/plugin/evm/vm.go index 5c2a8760d5..aedb63df28 100644 --- a/plugin/evm/vm.go +++ b/plugin/evm/vm.go @@ -1066,7 +1066,11 @@ func (vm *VM) initBlockBuilding() error { return err } - go ethTxGossiper.Gossip(ctx) + vm.shutdownWg.Add(1) + go func() { + ethTxGossiper.Gossip(ctx) + vm.shutdownWg.Done() + }() atomicTxGossiper, err := gossip.NewGossiper[GossipAtomicTx, *GossipAtomicTx]( atomicTxGossipConfig, @@ -1079,7 +1083,11 @@ func (vm *VM) initBlockBuilding() error { return err } - go atomicTxGossiper.Gossip(ctx) + vm.shutdownWg.Add(1) + go func() { + atomicTxGossiper.Gossip(ctx) + vm.shutdownWg.Done() + }() return nil } From 461170e7ac9bde57eec3c13b2536ca809fa49bbb Mon Sep 17 00:00:00 2001 From: Joshua Kim <20001595+joshua-kim@users.noreply.github.com> Date: Tue, 12 Sep 2023 11:32:54 -0400 Subject: [PATCH 35/36] nit --- plugin/evm/vm.go | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/plugin/evm/vm.go b/plugin/evm/vm.go index aedb63df28..ab5e523079 100644 --- a/plugin/evm/vm.go +++ b/plugin/evm/vm.go @@ -246,7 +246,8 @@ func init() { // VM implements the snowman.ChainVM interface type VM struct { - ctx *snow.Context + ctx *snow.Context + // [cancel] may be nil until [snow.NormalOp] starts cancel context.CancelFunc // *chain.State helps to implement the VM interface by wrapping blocks // with an efficient caching layer. From 5a9a7e75783b2146d05201feff844e5ff524e934 Mon Sep 17 00:00:00 2001 From: Joshua Kim <20001595+joshua-kim@users.noreply.github.com> Date: Tue, 12 Sep 2023 11:36:55 -0400 Subject: [PATCH 36/36] Squashed commit of the following: commit 0af32656d49e0e1eaaf8a9be50dcc8cbf6c6ec3f Author: Joshua Kim <20001595+joshua-kim@users.noreply.github.com> Date: Tue Sep 12 11:25:07 2023 -0400 Update to 1.10.10-rc.2 (#328) * update to avalanchego 1.10.10-rc.2 * nits * nit commit 545b4f170fd812aa2ac7eabe90476f429f26b695 Author: Darioush Jalali Date: Mon Sep 11 09:20:23 2023 -0700 Fix TestVMShutdownWhileSyncing (#323) * Fix TestVMShutdownWhileSyncing * fix commit 347fe530aab2c95103ee48e23b9ffaff986a0ca5 Author: Joshua Kim <20001595+joshua-kim@users.noreply.github.com> Date: Mon Sep 11 10:53:44 2023 -0400 Fix hanging requests after Shutdown (#326) * fix requests hanging after shutdown * fix build --------- Signed-off-by: Stephen Buttolph Co-authored-by: Stephen Buttolph --- peer/network.go | 2 + peer/network_test.go | 20 ++++ plugin/evm/syncervm_test.go | 226 ++++++++++++++---------------------- 3 files changed, 107 insertions(+), 141 deletions(-) diff --git a/peer/network.go b/peer/network.go index 6aaa962b3b..a5aa4b1393 100644 --- a/peer/network.go +++ b/peer/network.go @@ -175,6 +175,7 @@ func (n *network) SendAppRequest(nodeID ids.NodeID, request []byte, responseHand // Assumes write lock is held func (n *network) sendAppRequest(nodeID ids.NodeID, request []byte, responseHandler message.ResponseHandler) error { if n.closed.Get() { + n.activeAppRequests.Release(1) return nil } @@ -212,6 +213,7 @@ func (n *network) SendCrossChainRequest(chainID ids.ID, request []byte, handler defer n.lock.Unlock() if n.closed.Get() { + n.activeCrossChainRequests.Release(1) return nil } diff --git a/peer/network_test.go b/peer/network_test.go index 039ad94ea4..7a6228ca45 100644 --- a/peer/network_test.go +++ b/peer/network_test.go @@ -653,6 +653,26 @@ func TestCrossChainRequestOnShutdown(t *testing.T) { require.True(t, called) } +func TestNetworkAppRequestAfterShutdown(t *testing.T) { + require := require.New(t) + + net := NewNetwork(nil, nil, nil, nil, ids.EmptyNodeID, 1, 0) + net.Shutdown() + + require.NoError(net.SendAppRequest(ids.GenerateTestNodeID(), nil, nil)) + require.NoError(net.SendAppRequest(ids.GenerateTestNodeID(), nil, nil)) +} + +func TestNetworkCrossChainAppRequestAfterShutdown(t *testing.T) { + require := require.New(t) + + net := NewNetwork(nil, nil, nil, nil, ids.EmptyNodeID, 0, 1) + net.Shutdown() + + require.NoError(net.SendCrossChainRequest(ids.GenerateTestID(), nil, nil)) + require.NoError(net.SendCrossChainRequest(ids.GenerateTestID(), nil, nil)) +} + func TestSDKRouting(t *testing.T) { require := require.New(t) sender := &testAppSender{ diff --git a/plugin/evm/syncervm_test.go b/plugin/evm/syncervm_test.go index 5af91a5474..dab0fd505a 100644 --- a/plugin/evm/syncervm_test.go +++ b/plugin/evm/syncervm_test.go @@ -17,6 +17,7 @@ import ( "github.com/ava-labs/avalanchego/chains/atomic" "github.com/ava-labs/avalanchego/database/manager" + "github.com/ava-labs/avalanchego/database/prefixdb" "github.com/ava-labs/avalanchego/ids" "github.com/ava-labs/avalanchego/snow" "github.com/ava-labs/avalanchego/snow/choices" @@ -52,7 +53,6 @@ func TestSkipStateSync(t *testing.T) { syncMode: block.StateSyncSkipped, } vmSetup := createSyncServerAndClientVMs(t, test) - defer vmSetup.Teardown(t) testSyncerVM(t, vmSetup, test) } @@ -65,7 +65,6 @@ func TestStateSyncFromScratch(t *testing.T) { syncMode: block.StateSyncStatic, } vmSetup := createSyncServerAndClientVMs(t, test) - defer vmSetup.Teardown(t) testSyncerVM(t, vmSetup, test) } @@ -107,7 +106,6 @@ func TestStateSyncToggleEnabledToDisabled(t *testing.T) { expectedErr: context.Canceled, } vmSetup := createSyncServerAndClientVMs(t, test) - defer vmSetup.Teardown(t) // Perform sync resulting in early termination. testSyncerVM(t, vmSetup, test) @@ -250,61 +248,34 @@ func TestVMShutdownWhileSyncing(t *testing.T) { // Shutdown the VM after 50 requests to interrupt the sync if reqCount == 50 { // Note this verifies the VM shutdown does not time out while syncing. - require.NoError(t, vmSetup.syncerVM.Shutdown(context.Background())) + require.NoError(t, vmSetup.shutdownOnceSyncerVM.Shutdown(context.Background())) } else if reqCount < 50 { - syncerVM.AppResponse(context.Background(), nodeID, requestID, response) + err := syncerVM.AppResponse(context.Background(), nodeID, requestID, response) + require.NoError(t, err) } }, expectedErr: context.Canceled, } vmSetup = createSyncServerAndClientVMs(t, test) - defer func() { - require.NoError(t, vmSetup.serverVM.Shutdown(context.Background())) - }() - // Perform sync resulting in early termination. testSyncerVM(t, vmSetup, test) } func createSyncServerAndClientVMs(t *testing.T, test syncTest) *syncVMSetup { var ( - serverVM, syncerVM *VM - ) - // If there is an error shutdown the VMs if they have been instantiated - defer func() { - // If the test has not already failed, shut down the VMs since the caller - // will not get the chance to shut them down. - if !t.Failed() { - return - } - - // If the test already failed, shut down the VMs if they were instantiated. - if serverVM != nil { - log.Info("Shutting down server VM") - if err := serverVM.Shutdown(context.Background()); err != nil { - t.Fatal(err) - } - } - if syncerVM != nil { - log.Info("Shutting down syncerVM") - if err := syncerVM.Shutdown(context.Background()); err != nil { - t.Fatal(err) - } + require = require.New(t) + importAmount = 2000000 * units.Avax // 2M avax + alloc = map[ids.ShortID]uint64{ + testShortIDAddrs[0]: importAmount, } - }() - - // configure [serverVM] - importAmount := 2000000 * units.Avax // 2M avax + ) _, serverVM, _, serverAtomicMemory, serverAppSender := GenesisVMWithUTXOs( - t, - true, - "", - "", - "", - map[ids.ShortID]uint64{ - testShortIDAddrs[0]: importAmount, - }, + t, true, "", "", "", alloc, ) + t.Cleanup(func() { + log.Info("Shutting down server VM") + require.NoError(serverVM.Shutdown(context.Background())) + }) var ( importTx, exportTx *Tx @@ -315,12 +286,8 @@ func createSyncServerAndClientVMs(t *testing.T, test syncTest) *syncVMSetup { case 0: // spend the UTXOs from shared memory importTx, err = serverVM.newImportTx(serverVM.ctx.XChainID, testEthAddrs[0], initialBaseFee, []*secp256k1.PrivateKey{testKeys[0]}) - if err != nil { - t.Fatal(err) - } - if err := serverVM.issueTx(importTx, true /*=local*/); err != nil { - t.Fatal(err) - } + require.NoError(err) + require.NoError(serverVM.issueTx(importTx, true /*=local*/)) case 1: // export some of the imported UTXOs to test exportTx is properly synced exportTx, err = serverVM.newExportTx( @@ -331,19 +298,13 @@ func createSyncServerAndClientVMs(t *testing.T, test syncTest) *syncVMSetup { initialBaseFee, []*secp256k1.PrivateKey{testKeys[0]}, ) - if err != nil { - t.Fatal(err) - } - if err := serverVM.issueTx(exportTx, true /*=local*/); err != nil { - t.Fatal(err) - } + require.NoError(err) + require.NoError(serverVM.issueTx(exportTx, true /*=local*/)) default: // Generate simple transfer transactions. pk := testKeys[0].ToECDSA() tx := types.NewTransaction(gen.TxNonce(testEthAddrs[0]), testEthAddrs[1], common.Big1, params.TxGas, initialBaseFee, nil) signedTx, err := types.SignTx(tx, types.NewEIP155Signer(serverVM.chainID), pk) - if err != nil { - t.Fatal(t) - } + require.NoError(err) gen.AddTx(signedTx) } }) @@ -353,8 +314,8 @@ func createSyncServerAndClientVMs(t *testing.T, test syncTest) *syncVMSetup { // fetching a state summary. serverAtomicTrie := serverVM.atomicTrie.(*atomicTrie) serverAtomicTrie.commitInterval = test.syncableInterval - assert.NoError(t, serverAtomicTrie.commit(test.syncableInterval, serverAtomicTrie.LastAcceptedRoot())) - assert.NoError(t, serverVM.db.Commit()) + require.NoError(serverAtomicTrie.commit(test.syncableInterval, serverAtomicTrie.LastAcceptedRoot())) + require.NoError(serverVM.db.Commit()) serverSharedMemories := newSharedMemories(serverAtomicMemory, serverVM.ctx.ChainID, serverVM.ctx.XChainID) serverSharedMemories.assertOpsApplied(t, importTx.mustAtomicOps()) @@ -370,37 +331,28 @@ func createSyncServerAndClientVMs(t *testing.T, test syncTest) *syncVMSetup { lastAccepted := serverVM.blockChain.LastAcceptedBlock() patchedBlock := patchBlock(lastAccepted, root, serverVM.chaindb) blockBytes, err := rlp.EncodeToBytes(patchedBlock) - if err != nil { - t.Fatal(err) - } + require.NoError(err) internalBlock, err := serverVM.parseBlock(context.Background(), blockBytes) - if err != nil { - t.Fatal(err) - } + require.NoError(err) internalBlock.(*Block).SetStatus(choices.Accepted) - assert.NoError(t, serverVM.State.SetLastAcceptedBlock(internalBlock)) + require.NoError(serverVM.State.SetLastAcceptedBlock(internalBlock)) // patch syncableInterval for test serverVM.StateSyncServer.(*stateSyncServer).syncableInterval = test.syncableInterval // initialise [syncerVM] with blank genesis state - stateSyncEnabledJSON := fmt.Sprintf("{\"state-sync-enabled\":true, \"state-sync-min-blocks\": %d}", test.stateSyncMinBlocks) + stateSyncEnabledJSON := fmt.Sprintf(`{"state-sync-enabled":true, "state-sync-min-blocks": %d}`, test.stateSyncMinBlocks) syncerEngineChan, syncerVM, syncerDBManager, syncerAtomicMemory, syncerAppSender := GenesisVMWithUTXOs( - t, - false, - "", - stateSyncEnabledJSON, - "", - map[ids.ShortID]uint64{ - testShortIDAddrs[0]: importAmount, - }, + t, false, "", stateSyncEnabledJSON, "", alloc, ) - if err := syncerVM.SetState(context.Background(), snow.StateSyncing); err != nil { - t.Fatal(err) - } + shutdownOnceSyncerVM := &shutdownOnceVM{VM: syncerVM} + t.Cleanup(func() { + require.NoError(shutdownOnceSyncerVM.Shutdown(context.Background())) + }) + require.NoError(syncerVM.SetState(context.Background(), snow.StateSyncing)) enabled, err := syncerVM.StateSyncEnabled(context.Background()) - assert.NoError(t, err) - assert.True(t, enabled) + require.NoError(err) + require.True(enabled) // override [syncerVM]'s commit interval so the atomic trie works correctly. syncerVM.atomicTrie.(*atomicTrie).commitInterval = test.syncableInterval @@ -417,19 +369,20 @@ func createSyncServerAndClientVMs(t *testing.T, test syncTest) *syncVMSetup { } // connect peer to [syncerVM] - assert.NoError(t, syncerVM.Connected( - context.Background(), - serverVM.ctx.NodeID, - statesyncclient.StateSyncVersion, - )) + require.NoError( + syncerVM.Connected( + context.Background(), + serverVM.ctx.NodeID, + statesyncclient.StateSyncVersion, + ), + ) // override [syncerVM]'s SendAppRequest function to trigger AppRequest on [serverVM] syncerAppSender.SendAppRequestF = func(ctx context.Context, nodeSet set.Set[ids.NodeID], requestID uint32, request []byte) error { nodeID, hasItem := nodeSet.Pop() - if !hasItem { - t.Fatal("expected nodeSet to contain at least 1 nodeID") - } - go serverVM.AppRequest(ctx, nodeID, requestID, time.Now().Add(1*time.Second), request) + require.True(hasItem, "expected nodeSet to contain at least 1 nodeID") + err := serverVM.AppRequest(ctx, nodeID, requestID, time.Now().Add(1*time.Second), request) + require.NoError(err) return nil } @@ -440,11 +393,12 @@ func createSyncServerAndClientVMs(t *testing.T, test syncTest) *syncVMSetup { importTx, exportTx, }, - fundedAccounts: accounts, - syncerVM: syncerVM, - syncerDBManager: syncerDBManager, - syncerEngineChan: syncerEngineChan, - syncerAtomicMemory: syncerAtomicMemory, + fundedAccounts: accounts, + syncerVM: syncerVM, + syncerDBManager: syncerDBManager, + syncerEngineChan: syncerEngineChan, + syncerAtomicMemory: syncerAtomicMemory, + shutdownOnceSyncerVM: shutdownOnceSyncerVM, } } @@ -457,17 +411,22 @@ type syncVMSetup struct { includedAtomicTxs []*Tx fundedAccounts map[*keystore.Key]*types.StateAccount - syncerVM *VM - syncerDBManager manager.Manager - syncerEngineChan <-chan commonEng.Message - syncerAtomicMemory *atomic.Memory + syncerVM *VM + syncerDBManager manager.Manager + syncerEngineChan <-chan commonEng.Message + syncerAtomicMemory *atomic.Memory + shutdownOnceSyncerVM *shutdownOnceVM } -// Teardown shuts down both VMs and asserts that both exit without error. -// Note: assumes both serverVM and sycnerVM have been initialized. -func (s *syncVMSetup) Teardown(t *testing.T) { - assert.NoError(t, s.serverVM.Shutdown(context.Background())) - assert.NoError(t, s.syncerVM.Shutdown(context.Background())) +type shutdownOnceVM struct { + *VM + shutdownOnce sync.Once +} + +func (vm *shutdownOnceVM) Shutdown(ctx context.Context) error { + var err error + vm.shutdownOnce.Do(func() { err = vm.VM.Shutdown(ctx) }) + return err } // syncTest contains both the actual VMs as well as the parameters with the expected output. @@ -482,6 +441,7 @@ type syncTest struct { func testSyncerVM(t *testing.T, vmSetup *syncVMSetup, test syncTest) { t.Helper() var ( + require = require.New(t) serverVM = vmSetup.serverVM includedAtomicTxs = vmSetup.includedAtomicTxs fundedAccounts = vmSetup.fundedAccounts @@ -489,54 +449,42 @@ func testSyncerVM(t *testing.T, vmSetup *syncVMSetup, test syncTest) { syncerEngineChan = vmSetup.syncerEngineChan syncerAtomicMemory = vmSetup.syncerAtomicMemory ) - // get last summary and test related methods summary, err := serverVM.GetLastStateSummary(context.Background()) - if err != nil { - t.Fatal("error getting state sync last summary", "err", err) - } + require.NoError(err, "error getting state sync last summary") parsedSummary, err := syncerVM.ParseStateSummary(context.Background(), summary.Bytes()) - if err != nil { - t.Fatal("error getting state sync last summary", "err", err) - } + require.NoError(err, "error parsing state summary") retrievedSummary, err := serverVM.GetStateSummary(context.Background(), parsedSummary.Height()) - if err != nil { - t.Fatal("error when checking if summary is accepted", "err", err) - } - assert.Equal(t, summary, retrievedSummary) + require.NoError(err, "error getting state sync summary at height") + require.Equal(summary, retrievedSummary) syncMode, err := parsedSummary.Accept(context.Background()) - if err != nil { - t.Fatal("unexpected error accepting state summary", "err", err) - } - if syncMode != test.syncMode { - t.Fatal("unexpected value returned from accept", "expected", test.syncMode, "got", syncMode) - } + require.NoError(err, "error accepting state summary") + require.Equal(syncMode, test.syncMode) if syncMode == block.StateSyncSkipped { return } + msg := <-syncerEngineChan - assert.Equal(t, commonEng.StateSyncDone, msg) + require.Equal(commonEng.StateSyncDone, msg) // If the test is expected to error, assert the correct error is returned and finish the test. err = syncerVM.StateSyncClient.Error() if test.expectedErr != nil { - assert.ErrorIs(t, err, test.expectedErr) - assertSyncPerformedHeights(t, syncerVM.chaindb, map[uint64]struct{}{}) + require.ErrorIs(err, test.expectedErr) + // Note we re-open the database here to avoid a closed error when the test is for a shutdown VM. + chaindb := Database{prefixdb.NewNested(ethDBPrefix, syncerVM.db)} + assertSyncPerformedHeights(t, chaindb, map[uint64]struct{}{}) return } - if err != nil { - t.Fatal("state sync failed", err) - } + require.NoError(err, "state sync failed") // set [syncerVM] to bootstrapping and verify the last accepted block has been updated correctly // and that we can bootstrap and process some blocks. - if err := syncerVM.SetState(context.Background(), snow.Bootstrapping); err != nil { - t.Fatal(err) - } - assert.Equal(t, serverVM.LastAcceptedBlock().Height(), syncerVM.LastAcceptedBlock().Height(), "block height mismatch between syncer and server") - assert.Equal(t, serverVM.LastAcceptedBlock().ID(), syncerVM.LastAcceptedBlock().ID(), "blockID mismatch between syncer and server") - assert.True(t, syncerVM.blockChain.HasState(syncerVM.blockChain.LastAcceptedBlock().Root()), "unavailable state for last accepted block") + require.NoError(syncerVM.SetState(context.Background(), snow.Bootstrapping)) + require.Equal(serverVM.LastAcceptedBlock().Height(), syncerVM.LastAcceptedBlock().Height(), "block height mismatch between syncer and server") + require.Equal(serverVM.LastAcceptedBlock().ID(), syncerVM.LastAcceptedBlock().ID(), "blockID mismatch between syncer and server") + require.True(syncerVM.blockChain.HasState(syncerVM.blockChain.LastAcceptedBlock().Root()), "unavailable state for last accepted block") assertSyncPerformedHeights(t, syncerVM.chaindb, map[uint64]struct{}{retrievedSummary.Height(): {}}) blocksToBuild := 10 @@ -547,9 +495,7 @@ func testSyncerVM(t *testing.T, vmSetup *syncVMSetup, test syncTest) { for k := range fundedAccounts { tx := types.NewTransaction(gen.TxNonce(k.Address), toAddress, big.NewInt(1), 21000, initialBaseFee, nil) signedTx, err := types.SignTx(tx, types.NewEIP155Signer(serverVM.chainID), k.PrivateKey) - if err != nil { - t.Fatal(err) - } + require.NoError(err) gen.AddTx(signedTx) i++ if i >= txsPerBlock { @@ -559,8 +505,8 @@ func testSyncerVM(t *testing.T, vmSetup *syncVMSetup, test syncTest) { }) // check we can transition to [NormalOp] state and continue to process blocks. - assert.NoError(t, syncerVM.SetState(context.Background(), snow.NormalOp)) - assert.True(t, syncerVM.bootstrapped) + require.NoError(syncerVM.SetState(context.Background(), snow.NormalOp)) + require.True(syncerVM.bootstrapped) // check atomic memory was synced properly syncerSharedMemories := newSharedMemories(syncerAtomicMemory, syncerVM.ctx.ChainID, syncerVM.ctx.XChainID) @@ -575,9 +521,7 @@ func testSyncerVM(t *testing.T, vmSetup *syncVMSetup, test syncTest) { for k := range fundedAccounts { tx := types.NewTransaction(gen.TxNonce(k.Address), toAddress, big.NewInt(1), 21000, initialBaseFee, nil) signedTx, err := types.SignTx(tx, types.NewEIP155Signer(serverVM.chainID), k.PrivateKey) - if err != nil { - t.Fatal(err) - } + require.NoError(err) gen.AddTx(signedTx) i++ if i >= txsPerBlock {