Skip to content

Commit

Permalink
fix addBundle issue (#143)
Browse files Browse the repository at this point in the history
  • Loading branch information
redhdx authored Aug 22, 2024
1 parent 0effa90 commit f032dca
Show file tree
Hide file tree
Showing 2 changed files with 165 additions and 12 deletions.
41 changes: 29 additions & 12 deletions core/txpool/bundlepool/bundlepool.go
Original file line number Diff line number Diff line change
Expand Up @@ -171,20 +171,22 @@ func (p *BundlePool) AddBundle(bundle *types.Bundle, originBundle *types.SendBun
if err != nil {
return err
}
bundle.Price = price

p.mu.Lock()
defer p.mu.Unlock()

if price.Cmp(p.minimalBundleGasPrice()) <= 0 && p.slots+numSlots(bundle) > p.config.GlobalSlots {
return ErrBundleGasPriceLow
}
bundle.Price = price

hash := bundle.Hash()
if _, ok := p.bundles[hash]; ok {
return ErrBundleAlreadyExist
}

if p.slots+numSlots(bundle) > p.config.GlobalSlots {
if !p.drop(bundle) {
return ErrBundleGasPriceLow
}
}

for url, cli := range p.bundleReceiverClients {
cli := cli
url := url
Expand All @@ -199,9 +201,6 @@ func (p *BundlePool) AddBundle(bundle *types.Bundle, originBundle *types.SendBun
bundleDeliverAll.Inc(1)
}

for p.slots+numSlots(bundle) > p.config.GlobalSlots {
p.drop()
}
p.bundles[hash] = bundle
heap.Push(&p.bundleHeap, bundle)
p.slots += numSlots(bundle)
Expand Down Expand Up @@ -397,16 +396,34 @@ func (p *BundlePool) deleteBundle(hash common.Hash) {
}

// drop removes the bundle with the lowest gas price from the pool.
func (p *BundlePool) drop() {
func (p *BundlePool) drop(bundle *types.Bundle) bool {
dropBundles := make([]*types.Bundle, 0, numSlots(bundle))
dropSlots := uint64(0)
for len(p.bundleHeap) > 0 {
if dropSlots >= numSlots(bundle) {
for _, dropBundle := range dropBundles {
p.deleteBundle(dropBundle.Hash())
}
return true
}
// Pop the bundle with the lowest gas price
// the min element in the heap may not exist in the pool as it may be pruned
leastPriceBundleHash := heap.Pop(&p.bundleHeap).(*types.Bundle).Hash()
if _, ok := p.bundles[leastPriceBundleHash]; ok {
p.deleteBundle(leastPriceBundleHash)
break
if leastPriceBundle, ok := p.bundles[leastPriceBundleHash]; ok {
if leastPriceBundle.Price.Cmp(bundle.Price) < 0 {
dropBundles = append(dropBundles, leastPriceBundle)
dropSlots = dropSlots + numSlots(leastPriceBundle)
continue
} else {
heap.Push(&p.bundleHeap, leastPriceBundle)
for _, dropBundle := range dropBundles {
heap.Push(&p.bundleHeap, dropBundle)
}
return false
}
}
}
return false
}

// minimalBundleGasPrice return the lowest gas price from the pool.
Expand Down
136 changes: 136 additions & 0 deletions core/txpool/bundlepool/bundlepool_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,136 @@
package bundlepool

import (
"container/heap"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core/rawdb"
"github.com/ethereum/go-ethereum/core/state"
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/miner"
"github.com/ethereum/go-ethereum/params"
"math/big"
"testing"
)

type testBlockChain struct {
config *params.ChainConfig
gasLimit uint64
statedb *state.StateDB
}

func (bc *testBlockChain) Config() *params.ChainConfig {
return bc.config
}

func (bc *testBlockChain) CurrentBlock() *types.Header {
return &types.Header{
Number: new(big.Int),
}
}

func (bc *testBlockChain) GetBlock(hash common.Hash, number uint64) *types.Block {
return nil
}

func (bc *testBlockChain) StateAt(root common.Hash) (*state.StateDB, error) {
return bc.statedb, nil
}

func setupBundlePool(config Config, mevConfig miner.MevConfig) *BundlePool {
return setupBundlePoolWithConfig(config, mevConfig)
}

func setupBundlePoolWithConfig(config Config, mevConfig miner.MevConfig) *BundlePool {
statedb, _ := state.New(types.EmptyRootHash, state.NewDatabase(rawdb.NewMemoryDatabase()), nil)
blockchain := newTestBlockChain(params.TestChainConfig, 100000000, statedb)

pool := New(config, mevConfig, blockchain)
return pool
}

func newTestBlockChain(config *params.ChainConfig, gasLimit uint64, statedb *state.StateDB) *testBlockChain {
bc := testBlockChain{config: config, gasLimit: gasLimit, statedb: statedb}
return &bc
}

func TestBundlePoolDrop(t *testing.T) {
bundleConfig := Config{GlobalSlots: 5}
mevConfig := miner.MevConfig{MevEnabled: true}
bundlepool := setupBundlePool(bundleConfig, mevConfig)
for i := 0; i < 5; i++ {
bundle := &types.Bundle{
Price: big.NewInt(int64(i)),
}
hash := bundle.Hash()
bundlepool.bundles[hash] = bundle
heap.Push(&bundlepool.bundleHeap, bundle)
bundlepool.slots += numSlots(bundle)
}

// now bundle pool order by price [0, 1, 2, 3, 4]
// test drop, one bundle with one slot replace success
bundle := &types.Bundle{
Txs: nil,
Price: big.NewInt(5),
}
if !bundlepool.drop(bundle) {
t.Errorf("bundle pool drop expect success, but failed")
}
// check old least price bundle (bundle price is 0) not exist in bundle pool
leastPriceBundleHash := heap.Pop(&bundlepool.bundleHeap).(*types.Bundle).Hash()
if leastPriceBundle, ok := bundlepool.bundles[leastPriceBundleHash]; ok {
if leastPriceBundle.Price.Uint64() == 0 {
t.Errorf("old bundle expect not in bundlepool, but in")
}
heap.Push(&bundlepool.bundleHeap, leastPriceBundle)
}
hash := bundle.Hash()
bundlepool.bundles[hash] = bundle
heap.Push(&bundlepool.bundleHeap, bundle)
bundlepool.slots += numSlots(bundle)

// now bundle pool as price [1, 2, 3, 4, 5]
// test drop, one bundle with 2 slot replace failed
data := make([]byte, bundleSlotSize+1)
for i := range data {
data[i] = 0xFF
}
txs := types.Transactions{
types.NewTx(&types.LegacyTx{
Data: data,
}),
}
bundle = &types.Bundle{
Txs: txs,
Price: big.NewInt(2),
}
if bundlepool.drop(bundle) {
t.Errorf("bundle pool drop expect failed, but success")
}
// check old least price bundle (bundle price is 1) exist in bundle pool, not dropped
leastPriceBundleHash = heap.Pop(&bundlepool.bundleHeap).(*types.Bundle).Hash()
if leastPriceBundle, ok := bundlepool.bundles[leastPriceBundleHash]; ok {
if leastPriceBundle.Price.Uint64() != 1 {
t.Errorf("old bundle expect in bundlepool, but dropped")
}
heap.Push(&bundlepool.bundleHeap, leastPriceBundle)
}

// now bundle pool as price [1, 2, 3, 4, 5]
// test drop, one bundle with 2 slot replace success
bundle = &types.Bundle{
Txs: txs,
Price: big.NewInt(3),
}
if !bundlepool.drop(bundle) {
t.Errorf("bundle pool drop expect success, but failed")
}
// check old least bundle (bundle price is 1 and 2) not exist in bundle pool, dropped
leastPriceBundleHash = heap.Pop(&bundlepool.bundleHeap).(*types.Bundle).Hash()
if leastPriceBundle, ok := bundlepool.bundles[leastPriceBundleHash]; ok {
if leastPriceBundle.Price.Uint64() != 3 {
t.Errorf("old bundle expect not in bundlepool, but in")
}
heap.Push(&bundlepool.bundleHeap, leastPriceBundle)
}
}

0 comments on commit f032dca

Please sign in to comment.