@@ -24,6 +24,10 @@ import (
24
24
"sync"
25
25
"time"
26
26
27
+ "github.com/ethereum/go-ethereum/rlp"
28
+
29
+ "github.com/ethereum/go-ethereum/crypto"
30
+
27
31
"github.com/ethereum/go-ethereum/common"
28
32
"github.com/ethereum/go-ethereum/common/prque"
29
33
"github.com/ethereum/go-ethereum/core/state"
83
87
// than some meaningful limit a user might use. This is not a consensus error
84
88
// making the transaction invalid, rather a DOS protection.
85
89
ErrOversizedData = errors .New ("oversized data" )
90
+
91
+ // ErrorBundlePoolIsFull is returned if the number of bundle exceed the limit
92
+ ErrorBundlePoolIsFull = errors .New ("bundle pool is full" )
86
93
)
87
94
88
95
var (
@@ -115,6 +122,8 @@ var (
115
122
queuedGauge = metrics .NewRegisteredGauge ("txpool/queued" , nil )
116
123
localGauge = metrics .NewRegisteredGauge ("txpool/local" , nil )
117
124
slotsGauge = metrics .NewRegisteredGauge ("txpool/slots" , nil )
125
+
126
+ bundleGauge = metrics .NewRegisteredGauge ("txpool/bundles" , nil )
118
127
)
119
128
120
129
// TxStatus is the current status of a transaction as seen by the pool.
@@ -137,6 +146,10 @@ type blockChain interface {
137
146
SubscribeChainHeadEvent (ch chan <- ChainHeadEvent ) event.Subscription
138
147
}
139
148
149
+ type BundleSimulator interface {
150
+ SimulateBundle (bundle types.MevBundle ) (* big.Int , error )
151
+ }
152
+
140
153
// TxPoolConfig are the configuration parameters of the transaction pool.
141
154
type TxPoolConfig struct {
142
155
Locals []common.Address // Addresses that should be treated by default as local
@@ -147,12 +160,12 @@ type TxPoolConfig struct {
147
160
PriceLimit uint64 // Minimum gas price to enforce for acceptance into the pool
148
161
PriceBump uint64 // Minimum price bump percentage to replace an already existing transaction (nonce)
149
162
150
- AccountSlots uint64 // Number of executable transaction slots guaranteed per account
151
- GlobalSlots uint64 // Maximum number of executable transaction slots for all accounts
152
- AccountQueue uint64 // Maximum number of non-executable transaction slots permitted per account
153
- GlobalQueue uint64 // Maximum number of non-executable transaction slots for all accounts
154
-
155
- Lifetime time.Duration // Maximum amount of time non-executable transaction are queued
163
+ AccountSlots uint64 // Number of executable transaction slots guaranteed per account
164
+ GlobalSlots uint64 // Maximum number of executable transaction slots for all accounts
165
+ AccountQueue uint64 // Maximum number of non-executable transaction slots permitted per account
166
+ GlobalQueue uint64 // Maximum number of non-executable transaction slots for all accounts
167
+ BundleSlot uint64 // Maximum number of bundle slots for all accounts
168
+ Lifetime time.Duration // Maximum amount of time non-executable transaction are queued
156
169
}
157
170
158
171
// DefaultTxPoolConfig contains the default configurations for the transaction
@@ -226,6 +239,7 @@ type TxPool struct {
226
239
txFeed event.Feed
227
240
scope event.SubscriptionScope
228
241
signer types.Signer
242
+ simulator BundleSimulator
229
243
mu sync.RWMutex
230
244
231
245
istanbul bool // Fork indicator whether we are in the istanbul stage.
@@ -238,11 +252,12 @@ type TxPool struct {
238
252
locals * accountSet // Set of local transaction to exempt from eviction rules
239
253
journal * txJournal // Journal of local transaction to back up to disk
240
254
241
- pending map [common.Address ]* txList // All currently processable transactions
242
- queue map [common.Address ]* txList // Queued but non-processable transactions
243
- beats map [common.Address ]time.Time // Last heartbeat from each known account
244
- all * txLookup // All transactions to allow lookups
245
- priced * txPricedList // All transactions sorted by price
255
+ pending map [common.Address ]* txList // All currently processable transactions
256
+ queue map [common.Address ]* txList // Queued but non-processable transactions
257
+ beats map [common.Address ]time.Time // Last heartbeat from each known account
258
+ mevBundles map [common.Hash ]* types.MevBundle
259
+ all * txLookup // All transactions to allow lookups
260
+ priced * txPricedList // All transactions sorted by price
246
261
247
262
chainHeadCh chan ChainHeadEvent
248
263
chainHeadSub event.Subscription
@@ -281,6 +296,7 @@ func NewTxPool(config TxPoolConfig, chainconfig *params.ChainConfig, chain block
281
296
reorgDoneCh : make (chan chan struct {}),
282
297
reorgShutdownCh : make (chan struct {}),
283
298
gasPrice : new (big.Int ).SetUint64 (config .PriceLimit ),
299
+ mevBundles : make (map [common.Hash ]* types.MevBundle ),
284
300
}
285
301
pool .locals = newAccountSet (pool .signer )
286
302
for _ , addr := range config .Locals {
@@ -406,6 +422,10 @@ func (pool *TxPool) Stop() {
406
422
log .Info ("Transaction pool stopped" )
407
423
}
408
424
425
+ func (pool * TxPool ) SetBundleSimulator (simulator BundleSimulator ) {
426
+ pool .simulator = simulator
427
+ }
428
+
409
429
// SubscribeNewTxsEvent registers a subscription of NewTxsEvent and
410
430
// starts sending event to the given channel.
411
431
func (pool * TxPool ) SubscribeNewTxsEvent (ch chan <- NewTxsEvent ) event.Subscription {
@@ -496,6 +516,111 @@ func (pool *TxPool) Pending() (map[common.Address]types.Transactions, error) {
496
516
return pending , nil
497
517
}
498
518
519
+ /// AllMevBundles returns all the MEV Bundles currently in the pool
520
+ func (pool * TxPool ) AllMevBundles () []* types.MevBundle {
521
+ pool .mu .Lock ()
522
+ defer pool .mu .Unlock ()
523
+ bundles := make ([]* types.MevBundle , 0 , len (pool .mevBundles ))
524
+ for _ , bundle := range pool .mevBundles {
525
+ bundles = append (bundles , bundle )
526
+ }
527
+ return bundles
528
+ }
529
+
530
+ func (pool * TxPool ) GetMevBundles (hash common.Hash ) * types.MevBundle {
531
+ pool .mu .Lock ()
532
+ defer pool .mu .Unlock ()
533
+
534
+ return pool .mevBundles [hash ]
535
+ }
536
+
537
+ // MevBundles returns a list of bundles valid for the given blockNumber/blockTimestamp
538
+ // also prunes bundles that are outdated
539
+ func (pool * TxPool ) MevBundles (blockNumber * big.Int , blockTimestamp uint64 ) ([]types.MevBundle , error ) {
540
+ pool .mu .Lock ()
541
+ defer pool .mu .Unlock ()
542
+
543
+ // returned values
544
+ var ret []types.MevBundle
545
+ // rolled over values
546
+ bundles := make (map [common.Hash ]* types.MevBundle )
547
+
548
+ for uid , bundle := range pool .mevBundles {
549
+ // Prune outdated bundles
550
+ if (bundle .MaxTimestamp != 0 && blockTimestamp > bundle .MaxTimestamp ) || (bundle .MaxBlockNumber != nil && bundle .MaxBlockNumber .Int64 () != 0 && blockNumber .Cmp (bundle .MaxBlockNumber ) > 0 ) {
551
+ continue
552
+ }
553
+
554
+ // Roll over future bundles
555
+ if bundle .MinTimestamp != 0 && blockTimestamp < bundle .MinTimestamp {
556
+ bundles [uid ] = bundle
557
+ continue
558
+ }
559
+
560
+ // return the ones which are in time
561
+ ret = append (ret , * bundle )
562
+ // keep the bundles around internally until they need to be pruned
563
+ bundles [uid ] = bundle
564
+ }
565
+
566
+ pool .mevBundles = bundles
567
+ bundleGauge .Update (int64 (len (pool .mevBundles )))
568
+ return ret , nil
569
+ }
570
+
571
+ func (pool * TxPool ) PruneBundle (bundle common.Hash ) {
572
+ pool .mu .Lock ()
573
+ defer pool .mu .Unlock ()
574
+ delete (pool .mevBundles , bundle )
575
+ }
576
+
577
+ // AddMevBundle adds a mev bundle to the pool
578
+ func (pool * TxPool ) AddMevBundle (txs types.Transactions , maxBlockNumber * big.Int , minTimestamp , maxTimestamp uint64 , revertingTxHashes []common.Hash ) (common.Hash , error ) {
579
+ if pool .simulator == nil {
580
+ return common.Hash {}, errors .New ("bundle simulator is nil" )
581
+ }
582
+ bundle := types.MevBundle {
583
+ Txs : txs ,
584
+ MaxBlockNumber : maxBlockNumber ,
585
+ MinTimestamp : minTimestamp ,
586
+ MaxTimestamp : maxTimestamp ,
587
+ RevertingTxHashes : revertingTxHashes ,
588
+ }
589
+ bz , err := rlp .EncodeToBytes (bundle )
590
+ if err != nil {
591
+ return common.Hash {}, err
592
+ }
593
+ hash := crypto .Keccak256Hash (bz )
594
+ bundle .Hash = hash
595
+ price , err := pool .simulator .SimulateBundle (bundle )
596
+ if err != nil {
597
+ return common.Hash {}, err
598
+ }
599
+ bundle .Price = price
600
+ pool .mu .Lock ()
601
+ defer pool .mu .Unlock ()
602
+ if _ , ok := pool .mevBundles [hash ]; ok {
603
+ return common.Hash {}, errors .New ("bundle already exist" )
604
+ }
605
+ if len (pool .mevBundles ) > int (pool .config .BundleSlot ) {
606
+ leastPrice := big .NewInt (math .MaxInt64 )
607
+ leastBundleHash := common.Hash {}
608
+ for h , b := range pool .mevBundles {
609
+ if b .Price .Cmp (leastPrice ) < 0 {
610
+ leastPrice = b .Price
611
+ leastBundleHash = h
612
+ }
613
+ }
614
+ if bundle .Price .Cmp (leastPrice ) < 0 {
615
+ return common.Hash {}, ErrorBundlePoolIsFull
616
+ }
617
+ delete (pool .mevBundles , leastBundleHash )
618
+ }
619
+ pool .mevBundles [hash ] = & bundle
620
+ bundleGauge .Update (int64 (len (pool .mevBundles )))
621
+ return hash , nil
622
+ }
623
+
499
624
// Locals retrieves the accounts currently considered local by the pool.
500
625
func (pool * TxPool ) Locals () []common.Address {
501
626
pool .mu .Lock ()
0 commit comments