-
Notifications
You must be signed in to change notification settings - Fork 143
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Use SDK Gossip #318
Merged
Merged
Use SDK Gossip #318
Changes from 7 commits
Commits
Show all changes
37 commits
Select commit
Hold shift + click to select a range
2fdd559
evm gossip
joshua-kim ca37577
nit
joshua-kim c93ee73
Update plugin/evm/gossip_mempool.go
joshua-kim bb513d6
nits
joshua-kim cdb9649
nit
joshua-kim 33b8a06
nit
joshua-kim 845b93d
nit
joshua-kim 76cc2d2
Update plugin/evm/gossip_mempool.go
joshua-kim 954f60f
Update plugin/evm/gossip_mempool.go
joshua-kim c83364f
Update plugin/evm/mempool.go
joshua-kim 021f8bf
try go 1.20
joshua-kim 8c56141
Revert "try go 1.20"
joshua-kim 90398eb
Update plugin/evm/gossip_mempool.go
joshua-kim 673f5ab
clean mod cache
joshua-kim 234bdb1
Revert "clean mod cache"
joshua-kim 61a1c16
version
joshua-kim 2898ee5
nits
joshua-kim 248a1ca
nit
joshua-kim 0393cfb
nit
joshua-kim 474a03c
nit
joshua-kim 844ccd2
nit
joshua-kim db51063
nits
joshua-kim fbfa372
nit
joshua-kim ec525c9
nit
joshua-kim 25d0bf3
fix ci
joshua-kim 78698af
fix docker version
joshua-kim 6d8f21e
nit
joshua-kim d4b722c
update go version in ci
joshua-kim 2094e8d
nit
joshua-kim fe04ca8
nit
joshua-kim 678f640
Squashed commit of the following:
joshua-kim d487de1
remove ctx field
joshua-kim 0c5a462
add nil check
joshua-kim 0e34744
oops
joshua-kim 461170e
nit
joshua-kim 5a9a7e7
Squashed commit of the following:
joshua-kim 1ffc8e2
Merge branch 'master' into sdk-gossip
joshua-kim File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,146 @@ | ||
// Copyright (C) 2019-2023, Ava Labs, Inc. All rights reserved. | ||
// See the file LICENSE for licensing terms. | ||
|
||
package evm | ||
|
||
import ( | ||
"context" | ||
"fmt" | ||
"sync" | ||
|
||
"github.com/ava-labs/avalanchego/ids" | ||
"github.com/ethereum/go-ethereum/log" | ||
|
||
"github.com/ava-labs/avalanchego/network/p2p/gossip" | ||
|
||
"github.com/ava-labs/coreth/core" | ||
"github.com/ava-labs/coreth/core/txpool" | ||
"github.com/ava-labs/coreth/core/types" | ||
) | ||
|
||
var ( | ||
_ gossip.Gossipable = (*GossipEthTx)(nil) | ||
_ gossip.Gossipable = (*GossipAtomicTx)(nil) | ||
_ gossip.Set[*GossipEthTx] = (*GossipEthTxPool)(nil) | ||
) | ||
|
||
type GossipAtomicTx struct { | ||
Tx *Tx | ||
} | ||
|
||
func (tx *GossipAtomicTx) GetID() ids.ID { | ||
return tx.Tx.ID() | ||
} | ||
|
||
func (tx *GossipAtomicTx) Marshal() ([]byte, error) { | ||
return tx.Tx.SignedBytes(), nil | ||
} | ||
|
||
func (tx *GossipAtomicTx) Unmarshal(bytes []byte) error { | ||
result := &Tx{} | ||
joshua-kim marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
||
_, err := Codec.Unmarshal(bytes, result) | ||
joshua-kim marked this conversation as resolved.
Show resolved
Hide resolved
|
||
unsignedBytes, err := Codec.Marshal(codecVersion, result.UnsignedAtomicTx) | ||
joshua-kim marked this conversation as resolved.
Show resolved
Hide resolved
|
||
if err != nil { | ||
return err | ||
} | ||
|
||
result.Initialize(unsignedBytes, bytes) | ||
tx.Tx = result | ||
|
||
return nil | ||
} | ||
|
||
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(ctx context.Context) { | ||
g.mempool.SubscribeNewTxsEvent(g.pendingTxs) | ||
|
||
for { | ||
select { | ||
case <-ctx.Done(): | ||
log.Debug("shutting down subscription") | ||
return | ||
case pendingTxs := <-g.pendingTxs: | ||
g.lock.Lock() | ||
for _, pendingTx := range pendingTxs.Txs { | ||
tx := &GossipEthTx{Tx: pendingTx} | ||
g.bloom.Add(tx) | ||
reset, err := gossip.ResetBloomFilterIfNeeded(g.bloom, txGossipBloomMaxFilledRatio) | ||
joshua-kim marked this conversation as resolved.
Show resolved
Hide resolved
|
||
if err != nil { | ||
log.Error("failed to reset bloom filter", "err", err) | ||
continue | ||
} | ||
|
||
if reset { | ||
log.Debug("resetting bloom filter", "reason", "reached max filled ratio") | ||
|
||
g.mempool.IteratePending(func(tx *types.Transaction) bool { | ||
g.bloom.Add(&GossipEthTx{Tx: pendingTx}) | ||
return true | ||
}) | ||
} | ||
} | ||
g.lock.Unlock() | ||
} | ||
} | ||
} | ||
|
||
// Add enqueues the transaction to the mempool. Subscribe should be called | ||
// to receive an event if tx is actually added to the mempool or not. | ||
func (g *GossipEthTxPool) Add(tx *GossipEthTx) error { | ||
return g.mempool.AddRemotes([]*types.Transaction{tx.Tx})[0] | ||
} | ||
|
||
func (g *GossipEthTxPool) Iterate(f func(tx *GossipEthTx) bool) { | ||
g.mempool.IteratePending(func(tx *types.Transaction) bool { | ||
return f(&GossipEthTx{Tx: tx}) | ||
}) | ||
} | ||
|
||
func (g *GossipEthTxPool) GetFilter() ([]byte, []byte, error) { | ||
g.lock.RLock() | ||
defer g.lock.RUnlock() | ||
|
||
bloom, err := g.bloom.Bloom.MarshalBinary() | ||
salt := make([]byte, len(g.bloom.Salt)) | ||
copy(salt, g.bloom.Salt[:]) | ||
|
||
return bloom, salt, err | ||
joshua-kim marked this conversation as resolved.
Show resolved
Hide resolved
|
||
} | ||
|
||
type GossipEthTx struct { | ||
Tx *types.Transaction | ||
} | ||
|
||
func (tx *GossipEthTx) GetID() ids.ID { | ||
return ids.ID(tx.Tx.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) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,119 @@ | ||
// 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/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{ | ||
{ | ||
Tx: &Tx{ | ||
UnsignedAtomicTx: &TestUnsignedTx{ | ||
IDV: ids.GenerateTestID(), | ||
}, | ||
}, | ||
}, | ||
{ | ||
Tx: &Tx{ | ||
UnsignedAtomicTx: &TestUnsignedTx{ | ||
IDV: ids.GenerateTestID(), | ||
}, | ||
}, | ||
}, | ||
} | ||
|
||
tests := []struct { | ||
name string | ||
add []*GossipAtomicTx | ||
f func(tx *GossipAtomicTx) bool | ||
possibleValues []*GossipAtomicTx | ||
expectedLen int | ||
}{ | ||
{ | ||
name: "func matches nothing", | ||
add: txs, | ||
f: func(*GossipAtomicTx) bool { | ||
return false | ||
}, | ||
possibleValues: nil, | ||
}, | ||
{ | ||
name: "func matches all", | ||
add: txs, | ||
f: func(*GossipAtomicTx) bool { | ||
return true | ||
}, | ||
possibleValues: txs, | ||
expectedLen: 2, | ||
}, | ||
{ | ||
name: "func matches subset", | ||
add: txs, | ||
f: func(tx *GossipAtomicTx) bool { | ||
return tx.Tx == txs[0].Tx | ||
}, | ||
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) | ||
|
||
for _, add := range tt.add { | ||
require.NoError(m.Add(add)) | ||
} | ||
|
||
matches := make([]*GossipAtomicTx, 0) | ||
f := func(tx *GossipAtomicTx) bool { | ||
match := tt.f(tx) | ||
|
||
if match { | ||
matches = append(matches, tx) | ||
} | ||
|
||
return match | ||
} | ||
|
||
m.Iterate(f) | ||
|
||
require.Len(matches, tt.expectedLen) | ||
require.Subset(tt.possibleValues, matches) | ||
}) | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Do we want to prioritize transactions by nonce or perhaps local addresses?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
pending
anditems
are both maps which have random-ish iteration orders so I'm not super concerned about starvation but it could be a good follow-upThere was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sounds good, the other concern would be if we end up sending only transactions with future nonces and don't include the necessary transaction to make them executable. This is an edge case when we hit the max size, so I don't think this needs to block the PR.