Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,6 @@ require (
github.com/antithesishq/antithesis-sdk-go v0.3.8
github.com/ava-labs/avalanchego/graft/coreth v0.16.0-rc.0
github.com/ava-labs/libevm v1.13.15-0.20251016142715-1bccf4f2ddb2
github.com/ava-labs/subnet-evm v0.8.1-0.20251124174652-9114d48a927d
github.com/btcsuite/btcd/btcutil v1.1.3
github.com/cespare/xxhash/v2 v2.3.0
github.com/cockroachdb/pebble v0.0.0-20230928194634-aa077af62593
Expand Down Expand Up @@ -86,6 +85,8 @@ require (
k8s.io/utils v0.0.0-20230726121419-3b25d923346b
)

require github.com/golang-jwt/jwt/v4 v4.5.2 // indirect

require (
github.com/Microsoft/go-winio v0.6.1 // indirect
github.com/VictoriaMetrics/fastcache v1.12.1 // indirect
Expand Down
3 changes: 0 additions & 3 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -77,8 +77,6 @@ github.com/ava-labs/libevm v1.13.15-0.20251016142715-1bccf4f2ddb2 h1:hQ15IJxY7WO
github.com/ava-labs/libevm v1.13.15-0.20251016142715-1bccf4f2ddb2/go.mod h1:DqSotSn4Dx/UJV+d3svfW8raR+cH7+Ohl9BpsQ5HlGU=
github.com/ava-labs/simplex v0.0.0-20250919142550-9cdfff10fd19 h1:S6oFasZsplNmw8B2S8cMJQMa62nT5ZKGzZRdCpd+5qQ=
github.com/ava-labs/simplex v0.0.0-20250919142550-9cdfff10fd19/go.mod h1:GVzumIo3zR23/qGRN2AdnVkIPHcKMq/D89EGWZfMGQ0=
github.com/ava-labs/subnet-evm v0.8.1-0.20251124174652-9114d48a927d h1:7pjEE0BXLjzQlq5uKP5B2BTl9jTgDKaOfJx2Qfb01Jo=
github.com/ava-labs/subnet-evm v0.8.1-0.20251124174652-9114d48a927d/go.mod h1:JTvIe8YbCjHpy8vy9uZBSpDXxawNXSnIe/Wlf3I09Tk=
github.com/aymerick/raymond v2.0.3-0.20180322193309-b565731e1464+incompatible/go.mod h1:osfaiScAUVup+UC9Nfq76eWqDhXlp+4UYaA8uhTBO6g=
github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM=
github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw=
Expand Down Expand Up @@ -274,7 +272,6 @@ github.com/gogo/protobuf v1.2.0/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7a
github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q=
github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q=
github.com/gogo/status v1.1.0/go.mod h1:BFv9nrluPLmrS0EmGVvLaPNmRosr9KapBYd5/hpY1WM=
github.com/golang-jwt/jwt v3.2.2+incompatible h1:IfV12K8xAKAnZqdXVzCZ+TOjboZ2keLg81eXfW3O+oY=
github.com/golang-jwt/jwt v3.2.2+incompatible/go.mod h1:8pz2t5EyA70fFQQSrl6XZXzqecmYZeUEB8OUGHkxJ+I=
github.com/golang-jwt/jwt/v4 v4.5.2 h1:YtQM7lnr8iZ+j5q71MGKkNw9Mn7AjHM68uc9g5fXeUI=
github.com/golang-jwt/jwt/v4 v4.5.2/go.mod h1:m21LjoU+eqJr34lmDMbreY2eSTRJ1cv77w39/MY0Ch0=
Expand Down
46 changes: 2 additions & 44 deletions graft/coreth/plugin/evm/atomic/txpool/mempool.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,8 @@ import (

"github.com/ava-labs/libevm/log"
"github.com/holiman/uint256"
"github.com/prometheus/client_golang/prometheus"

"github.com/ava-labs/avalanchego/graft/coreth/plugin/evm/atomic"
"github.com/ava-labs/avalanchego/graft/coreth/plugin/evm/config"
"github.com/ava-labs/avalanchego/ids"
"github.com/ava-labs/avalanchego/network/p2p/gossip"
)
Expand All @@ -39,30 +37,17 @@ var (
// Mempool is a simple mempool for atomic transactions
type Mempool struct {
*Txs
// bloom is a bloom filter containing the txs in the mempool
bloom *gossip.BloomFilter
verify func(tx *atomic.Tx) error
}

func NewMempool(
txs *Txs,
registerer prometheus.Registerer,
verify func(tx *atomic.Tx) error,
) (*Mempool, error) {
bloom, err := gossip.NewBloomFilter(registerer, "atomic_mempool_bloom_filter",
config.TxGossipBloomMinTargetElements,
config.TxGossipBloomTargetFalsePositiveRate,
config.TxGossipBloomResetFalsePositiveRate,
)
if err != nil {
return nil, fmt.Errorf("failed to initialize bloom filter: %w", err)
}

) *Mempool {
return &Mempool{
Txs: txs,
bloom: bloom,
verify: verify,
}, nil
}
}

// Add attempts to add tx to the mempool as a Remote transaction. It is assumed
Expand Down Expand Up @@ -269,26 +254,6 @@ func (m *Mempool) addTx(tx *atomic.Tx, local bool, force bool) error {
m.utxoSpenders[utxoID] = tx
}

m.bloom.Add(tx)
reset, err := gossip.ResetBloomFilterIfNeeded(m.bloom, m.length()*config.TxGossipBloomChurnMultiplier)
if err != nil {
return err
}

if reset {
log.Debug("resetting bloom filter", "reason", "reached max filled ratio")

for _, pendingTx := range m.pendingTxs.minHeap.items {
m.bloom.Add(pendingTx.tx)
}
// Current transactions must be added to the bloom filter as well
// because they could be added back into the pending set without going
// through addTx again.
for _, currentTx := range m.currentTxs {
m.bloom.Add(currentTx)
}
}

// When adding a transaction to the mempool, we make sure that there is an
// item in Pending to signal the VM to produce a block.
select {
Expand All @@ -297,10 +262,3 @@ func (m *Mempool) addTx(tx *atomic.Tx, local bool, force bool) error {
}
return nil
}

func (m *Mempool) GetFilter() ([]byte, []byte) {
m.lock.RLock()
defer m.lock.RUnlock()

return m.bloom.Marshal()
}
88 changes: 11 additions & 77 deletions graft/coreth/plugin/evm/atomic/txpool/mempool_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,57 +4,45 @@
package txpool

import (
"math"
"testing"

"github.com/prometheus/client_golang/prometheus"
"github.com/stretchr/testify/require"

"github.com/ava-labs/avalanchego/graft/coreth/plugin/evm/atomic"
"github.com/ava-labs/avalanchego/graft/coreth/plugin/evm/atomic/atomictest"
"github.com/ava-labs/avalanchego/graft/coreth/plugin/evm/config"
"github.com/ava-labs/avalanchego/snow/snowtest"
"github.com/ava-labs/avalanchego/utils/bloom"
)

func TestMempoolAddTx(t *testing.T) {
require := require.New(t)

ctx := snowtest.Context(t, snowtest.CChainID)
m, err := NewMempool(
m := NewMempool(
NewTxs(ctx, 5_000),
prometheus.NewRegistry(),
nil,
)
require.NoError(err)

txs := make([]*atomic.Tx, 0)
for i := 0; i < 3_000; i++ {
tx := atomictest.GenerateTestImportTxWithGas(1, 1)
txs = append(txs, tx)
require.NoError(m.Add(tx))
}

for _, tx := range txs {
require.True(m.bloom.Has(tx))
}
}

// Add should return an error if a tx is already known
func TestMempoolAdd(t *testing.T) {
require := require.New(t)

ctx := snowtest.Context(t, snowtest.CChainID)
m, err := NewMempool(
m := NewMempool(
NewTxs(ctx, 5_000),
prometheus.NewRegistry(),
nil,
)
require.NoError(err)

tx := atomictest.GenerateTestImportTxWithGas(1, 1)
require.NoError(m.Add(tx))
err = m.Add(tx)
err := m.Add(tx)
require.ErrorIs(err, ErrAlreadyKnown)
}

Expand All @@ -63,62 +51,16 @@ func TestMempoolAddNoGas(t *testing.T) {
require := require.New(t)

ctx := snowtest.Context(t, snowtest.CChainID)
m, err := NewMempool(
m := NewMempool(
NewTxs(ctx, 5_000),
prometheus.NewRegistry(),
nil,
)
require.NoError(err)

tx := atomictest.GenerateTestImportTxWithGas(0, 1)
err = m.Add(tx)
err := m.Add(tx)
require.ErrorIs(err, atomic.ErrNoGasUsed)
}

// Add should return an error if a tx doesn't consume any gas
func TestMempoolAddBloomReset(t *testing.T) {
require := require.New(t)

ctx := snowtest.Context(t, snowtest.CChainID)
m, err := NewMempool(
NewTxs(ctx, 2),
prometheus.NewRegistry(),
nil,
)
require.NoError(err)

maxFeeTx := atomictest.GenerateTestImportTxWithGas(1, math.MaxUint64)
require.NoError(m.Add(maxFeeTx))

// Mark maxFeeTx as Current
tx, ok := m.NextTx()
require.True(ok)
require.Equal(maxFeeTx, tx)

numHashes, numEntries := bloom.OptimalParameters(
config.TxGossipBloomMinTargetElements,
config.TxGossipBloomTargetFalsePositiveRate,
)
txsToAdd := bloom.EstimateCount(
numHashes,
numEntries,
config.TxGossipBloomResetFalsePositiveRate,
)
for fee := range txsToAdd {
// Keep increasing the fee to evict older transactions
tx := atomictest.GenerateTestImportTxWithGas(1, uint64(fee))
require.NoError(m.Add(tx))
}

// Mark maxFeeTx as Pending
m.CancelCurrentTxs()

m.Iterate(func(tx *atomic.Tx) bool {
require.True(m.bloom.Has(tx))
return true // Iterate over the whole mempool
})
}

func TestAtomicMempoolIterate(t *testing.T) {
txs := []*atomic.Tx{
atomictest.GenerateTestImportTxWithGas(1, 1),
Expand Down Expand Up @@ -162,12 +104,10 @@ func TestAtomicMempoolIterate(t *testing.T) {
require := require.New(t)

ctx := snowtest.Context(t, snowtest.CChainID)
m, err := NewMempool(
m := NewMempool(
NewTxs(ctx, 10),
prometheus.NewRegistry(),
nil,
)
require.NoError(err)

for _, add := range tt.add {
require.NoError(m.Add(add))
Expand Down Expand Up @@ -197,12 +137,10 @@ func TestMempoolMaxSizeHandling(t *testing.T) {
require := require.New(t)

ctx := snowtest.Context(t, snowtest.CChainID)
mempool, err := NewMempool(
mempool := NewMempool(
NewTxs(ctx, 1),
prometheus.NewRegistry(),
nil,
)
require.NoError(err)

lowFeeTx := atomictest.GenerateTestImportTxWithGas(1, 1)
highFeeTx := atomictest.GenerateTestImportTxWithGas(1, 2)
Expand All @@ -216,7 +154,7 @@ func TestMempoolMaxSizeHandling(t *testing.T) {

// Because Current transactions can not be evicted, the mempool should
// report full.
err = mempool.Add(highFeeTx)
err := mempool.Add(highFeeTx)
require.ErrorIs(err, ErrMempoolFull)

// Mark the lowFeeTx as Issued
Expand All @@ -236,19 +174,17 @@ func TestMempoolPriorityDrop(t *testing.T) {
require := require.New(t)

ctx := snowtest.Context(t, snowtest.CChainID)
mempool, err := NewMempool(
mempool := NewMempool(
NewTxs(ctx, 1),
prometheus.NewRegistry(),
nil,
)
require.NoError(err)

tx1 := atomictest.GenerateTestImportTxWithGas(1, 2) // lower fee
require.NoError(mempool.AddRemoteTx(tx1))
require.True(mempool.Has(tx1.ID()))

tx2 := atomictest.GenerateTestImportTxWithGas(1, 2) // lower fee
err = mempool.AddRemoteTx(tx2)
err := mempool.AddRemoteTx(tx2)
require.ErrorIs(err, ErrInsufficientFee)
require.True(mempool.Has(tx1.ID()))
require.False(mempool.Has(tx2.ID()))
Expand All @@ -266,12 +202,10 @@ func TestMempoolPendingLen(t *testing.T) {
require := require.New(t)

ctx := snowtest.Context(t, snowtest.CChainID)
mempool, err := NewMempool(
mempool := NewMempool(
NewTxs(ctx, 2),
prometheus.NewRegistry(),
nil,
)
require.NoError(err)

tx1 := atomictest.GenerateTestImportTxWithGas(1, 1)
tx2 := atomictest.GenerateTestImportTxWithGas(1, 2)
Expand Down
19 changes: 16 additions & 3 deletions graft/coreth/plugin/evm/atomic/txpool/txs.go
Original file line number Diff line number Diff line change
Expand Up @@ -75,16 +75,24 @@ func NewTxs(ctx *snow.Context, maxSize int) *Txs {
}
}

// PendingLen returns the number of pending transactions.
// PendingLen returns the number of Pending transactions.
func (t *Txs) PendingLen() int {
t.lock.RLock()
defer t.lock.RUnlock()

return t.pendingTxs.Len()
}

// Iterate applies f to all Pending transactions. If f returns false, the
// iteration stops early.
// Len returns the number of Current and Pending transactions.
func (t *Txs) Len() int {
t.lock.RLock()
defer t.lock.RUnlock()

return len(t.currentTxs) + t.pendingTxs.Len()
}

// Iterate applies f to all Current and Pending transactions. If f returns
// false, the iteration stops early.
func (t *Txs) Iterate(f func(tx *atomic.Tx) bool) {
t.lock.RLock()
defer t.lock.RUnlock()
Expand All @@ -94,6 +102,11 @@ func (t *Txs) Iterate(f func(tx *atomic.Tx) bool) {
return
}
}
for _, tx := range t.currentTxs {
if !f(tx) {
return
}
}
}

// NextTx returns the highest paying Pending transaction from the mempool and
Expand Down
11 changes: 9 additions & 2 deletions graft/coreth/plugin/evm/atomic/vm/api.go
Original file line number Diff line number Diff line change
Expand Up @@ -141,7 +141,8 @@ func (service *AvaxAPI) IssueTx(_ *http.Request, args *api.FormattedTx, response
return fmt.Errorf("problem initializing transaction: %w", err)
}

response.TxID = tx.ID()
txID := tx.ID()
response.TxID = txID

service.vm.Ctx.Lock.Lock()
defer service.vm.Ctx.Lock.Unlock()
Expand All @@ -156,7 +157,13 @@ func (service *AvaxAPI) IssueTx(_ *http.Request, args *api.FormattedTx, response
// to the mempool through p2p gossip, this will ensure this node also pushes
// it to the network.
service.vm.AtomicTxPushGossiper.Add(tx)
return nil
if err != nil {
return nil
}

// If we just added the tx to the mempool, add it to the gossip bloom
// filter.
return service.vm.atomicGossipSet.AddToBloom(txID)
}

// GetAtomicTxStatus returns the status of the specified transaction
Expand Down
Loading
Loading