Skip to content

Commit 9b62e98

Browse files
committed
Discard reverting megabundle blocks and head change interrupted blocks (#123)
* Discard reverting megabundle blocks and head change interrupted blocks * Discard all blocks with incomplete bundles * Run reverting megabundles regression test separately from bundle tests
1 parent b33eb5c commit 9b62e98

File tree

2 files changed

+55
-21
lines changed

2 files changed

+55
-21
lines changed

.github/workflows/go.yml

Lines changed: 22 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -56,10 +56,31 @@ jobs:
5656
path: e2e
5757

5858
- run: cd e2e && yarn install
59-
- run: |
59+
- name: Run single node e2e
60+
run: |
6061
cd e2e
6162
GETH=`pwd`/../build/bin/geth ./run.sh &
6263
sleep 15
6364
yarn run demo-simple
65+
yarn run e2e-reverting-bundles
6466
yarn run demo-contract
67+
pkill -9 geth || true
68+
- name: Run private tx with two nodes
69+
run: |
70+
cd e2e
71+
GETH=`pwd`/../build/bin/geth ./run.sh &
72+
# Second node, not mining
73+
P2P_PORT=30302 DATADIR=datadir2 HTTP_PORT=8546 MINER_ARGS='--nodiscover' GETH=`pwd`/../build/bin/geth ./run.sh &
74+
sleep 15
75+
DATADIR1=datadir DATADIR2=datadir2 GETH=`pwd`/../build/bin/geth ./peer_nodes.sh
76+
sleep 15
6577
yarn run demo-private-tx
78+
pkill -9 geth || true
79+
- name: Run megabundle-only node checking for reverts
80+
run: |
81+
cd e2e
82+
# Disable bundle workers
83+
MINER_ARGS='--miner.etherbase=0xd912aecb07e9f4e1ea8e6b4779e7fb6aa1c3e4d8 --miner.trustedrelays=0xfb11e78C4DaFec86237c2862441817701fdf197F --mine --miner.threads=2 --miner.maxmergedbundles=0' GETH=`pwd`/../build/bin/geth ./run.sh &
84+
sleep 15
85+
yarn run e2e-reverting-megabundle
86+
pkill -9 geth || true

miner/worker.go

Lines changed: 33 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -925,6 +925,7 @@ func (w *worker) commitTransaction(env *environment, tx *types.Transaction) ([]*
925925
return receipt.Logs, nil
926926
}
927927

928+
// Returns whether the block should be discarded
928929
func (w *worker) commitBundle(env *environment, txs types.Transactions, interrupt *int32) bool {
929930
gasLimit := env.header.GasLimit
930931
if env.gasPool == nil {
@@ -938,8 +939,7 @@ func (w *worker) commitBundle(env *environment, txs types.Transactions, interrup
938939
// (1) new head block event arrival, the interrupt signal is 1
939940
// (2) worker start or restart, the interrupt signal is 1
940941
// (3) worker recreate the sealing block with any newly arrived transactions, the interrupt signal is 2.
941-
// For the first two cases, the semi-finished work will be discarded.
942-
// For the third case, the semi-finished work will be submitted to the consensus engine.
942+
// Discard the interrupted work, since it is incomplete and contains partial bundles
943943
if interrupt != nil && atomic.LoadInt32(interrupt) != commitInterruptNone {
944944
// Notify resubmit loop to increase resubmitting interval due to too frequent commits.
945945
if atomic.LoadInt32(interrupt) == commitInterruptResubmit {
@@ -953,12 +953,13 @@ func (w *worker) commitBundle(env *environment, txs types.Transactions, interrup
953953
}
954954
}
955955

956-
return atomic.LoadInt32(interrupt) == commitInterruptNewHead
956+
return true
957957
}
958-
// If we don't have enough gas for any further transactions then we're done
958+
// If we don't have enough gas for any further transactions discard the block
959+
// since not all bundles of the were applied
959960
if env.gasPool.Gas() < params.TxGas {
960961
log.Trace("Not enough gas for further transactions", "have", env.gasPool, "want", params.TxGas)
961-
break
962+
return true
962963
}
963964

964965
// Error may be ignored here. The error has already been checked
@@ -1245,7 +1246,8 @@ func (w *worker) prepareWork(genParams *generateParams) (*environment, error) {
12451246
// fillTransactions retrieves the pending transactions from the txpool and fills them
12461247
// into the given sealing block. The transaction selection and ordering strategy can
12471248
// be customized with the plugin in the future.
1248-
func (w *worker) fillTransactions(interrupt *int32, env *environment) {
1249+
// Returns whether the block should be discarded.
1250+
func (w *worker) fillTransactions(interrupt *int32, env *environment) bool {
12491251
// Split the pending transactions into locals and remotes
12501252
// Fill the block with all available pending transactions.
12511253
pending := w.eth.TxPool().Pending(true)
@@ -1260,36 +1262,36 @@ func (w *worker) fillTransactions(interrupt *int32, env *environment) {
12601262
bundles, err := w.eth.TxPool().MevBundles(env.header.Number, env.header.Time)
12611263
if err != nil {
12621264
log.Error("Failed to fetch pending transactions", "err", err)
1263-
return
1265+
return true
12641266
}
12651267

12661268
bundleTxs, bundle, numBundles, err := w.generateFlashbotsBundle(env, bundles, pending)
12671269
if err != nil {
12681270
log.Error("Failed to generate flashbots bundle", "err", err)
1269-
return
1271+
return true
12701272
}
12711273
log.Info("Flashbots bundle", "ethToCoinbase", ethIntToFloat(bundle.totalEth), "gasUsed", bundle.totalGasUsed, "bundleScore", bundle.mevGasPrice, "bundleLength", len(bundleTxs), "numBundles", numBundles, "worker", w.flashbots.maxMergedBundles)
12721274
if len(bundleTxs) == 0 {
1273-
return
1275+
return true
12741276
}
12751277
if w.commitBundle(env, bundleTxs, interrupt) {
1276-
return
1278+
return true
12771279
}
12781280
env.profit.Add(env.profit, bundle.ethSentToCoinbase)
12791281
}
12801282
if w.flashbots.isMegabundleWorker {
12811283
megabundle, err := w.eth.TxPool().GetMegabundle(w.flashbots.relayAddr, env.header.Number, env.header.Time)
12821284
log.Info("Starting to process a Megabundle", "relay", w.flashbots.relayAddr, "megabundle", megabundle, "error", err)
12831285
if err != nil {
1284-
return // no valid megabundle for this relay, nothing to do
1286+
return true // no valid megabundle for this relay, nothing to do
12851287
}
12861288

12871289
// Flashbots bundle merging duplicates work by simulating TXes and then committing them once more.
12881290
// Megabundles API focuses on speed and runs everything in one cycle.
12891291
coinbaseBalanceBefore := env.state.GetBalance(env.coinbase)
12901292
if w.commitBundle(env, megabundle.Txs, interrupt) {
12911293
log.Info("Could not commit a Megabundle", "relay", w.flashbots.relayAddr, "megabundle", megabundle)
1292-
return
1294+
return true
12931295
}
12941296
var txStatuses = map[common.Hash]bool{}
12951297
for _, receipt := range env.receipts {
@@ -1299,11 +1301,11 @@ func (w *worker) fillTransactions(interrupt *int32, env *environment) {
12991301
status, ok := txStatuses[tx.Hash()]
13001302
if !ok {
13011303
log.Error("No TX receipt after megabundle simulation", "TxHash", tx.Hash())
1302-
return
1304+
return true
13031305
}
13041306
if !status && !containsHash(megabundle.RevertingTxHashes, tx.Hash()) {
13051307
log.Info("Ignoring megabundle because of failing TX", "relay", w.flashbots.relayAddr, "TxHash", tx.Hash())
1306-
return
1308+
return true
13071309
}
13081310
}
13091311
coinbaseBalanceAfter := env.state.GetBalance(env.coinbase)
@@ -1315,15 +1317,17 @@ func (w *worker) fillTransactions(interrupt *int32, env *environment) {
13151317
if len(localTxs) > 0 {
13161318
txs := types.NewTransactionsByPriceAndNonce(env.signer, localTxs, env.header.BaseFee)
13171319
if w.commitTransactions(env, txs, interrupt) {
1318-
return
1320+
return true
13191321
}
13201322
}
13211323
if len(remoteTxs) > 0 {
13221324
txs := types.NewTransactionsByPriceAndNonce(env.signer, remoteTxs, env.header.BaseFee)
13231325
if w.commitTransactions(env, txs, interrupt) {
1324-
return
1326+
return true
13251327
}
13261328
}
1329+
1330+
return false
13271331
}
13281332

13291333
// generateWork generates a sealing block based on the given parameters.
@@ -1334,7 +1338,11 @@ func (w *worker) generateWork(params *generateParams) (*types.Block, error) {
13341338
}
13351339
defer work.discard()
13361340

1337-
w.fillTransactions(nil, work)
1341+
shouldDiscard := w.fillTransactions(nil, work)
1342+
if shouldDiscard {
1343+
return nil, errors.New("could not generate valid block")
1344+
}
1345+
13381346
return w.engine.FinalizeAndAssemble(w.chain, work.header, work.state, work.txs, work.unclelist(), work.receipts)
13391347
}
13401348

@@ -1365,7 +1373,12 @@ func (w *worker) commitWork(interrupt *int32, noempty bool, timestamp int64) {
13651373
w.commit(work.copy(), nil, false, start)
13661374
}
13671375
// Fill pending transactions from the txpool
1368-
w.fillTransactions(interrupt, work)
1376+
shouldDiscard := w.fillTransactions(interrupt, work)
1377+
if shouldDiscard {
1378+
work.discard()
1379+
return
1380+
}
1381+
13691382
w.commit(work.copy(), w.fullTaskHook, true, start)
13701383

13711384
// Swap out the old work with the new one, terminating any leftover
@@ -1525,11 +1538,11 @@ func (w *worker) simulateBundles(env *environment, bundles []types.MevBundle, pe
15251538
simulatedBundles := []simulatedBundle{}
15261539

15271540
for _, bundle := range bundles {
1528-
state := env.state.Copy()
1529-
gasPool := new(core.GasPool).AddGas(env.header.GasLimit)
15301541
if len(bundle.Txs) == 0 {
15311542
continue
15321543
}
1544+
state := env.state.Copy()
1545+
gasPool := new(core.GasPool).AddGas(env.header.GasLimit)
15331546
simmed, err := w.computeBundleGas(env, bundle, state, gasPool, pendingTxs, 0)
15341547

15351548
if err != nil {

0 commit comments

Comments
 (0)