Skip to content
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

fix bundle pool issue #136

Merged
Show file tree
Hide file tree
Changes from 3 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
35 changes: 26 additions & 9 deletions core/txpool/bundlepool/bundlepool.go
Original file line number Diff line number Diff line change
Expand Up @@ -175,8 +175,10 @@ func (p *BundlePool) AddBundle(bundle *types.Bundle, originBundle *types.SendBun
p.mu.Lock()
defer p.mu.Unlock()

if price.Cmp(p.minimalBundleGasPrice()) <= 0 && p.slots+numSlots(bundle) > p.config.GlobalSlots {
return ErrBundleGasPriceLow
if p.slots+numSlots(bundle) > p.config.GlobalSlots {
welkin22 marked this conversation as resolved.
Show resolved Hide resolved
if !p.drop(bundle) {
return ErrBundleGasPriceLow
}
}
bundle.Price = price

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)
}
}
Loading