diff --git a/miner/payload_building_test.go b/miner/payload_building_test.go index 3f0ddddae..230d6adca 100644 --- a/miner/payload_building_test.go +++ b/miner/payload_building_test.go @@ -144,6 +144,192 @@ func newTestWorker(t *testing.T, chainConfig *params.ChainConfig, engine consens return w, backend } +func TestBuildPayloadNotEnoughGas(t *testing.T) { + var ( + db = rawdb.NewMemoryDatabase() + recipient = common.HexToAddress("0xdeadbeef") + ) + w, b := newTestWorker(t, params.TestChainConfig, ethash.NewFaker(), db, 0) + + timestamp := uint64(time.Now().Unix()) + + txGasPrice := big.NewInt(10 * params.InitialBaseFee) + + gasLimit := b.chain.GasLimit() + + contains := func(transactions types.Transactions, tx *types.Transaction) bool { + for _, t := range transactions { + if t.Hash() == tx.Hash() { + return true + } + } + return false + } + + txsToAdd := types.Transactions{} + nonceCounter := uint64(0) + // keep adding txs until the gas pool goes below 21000 and then add 5 more txs. these 5 txs have to be + // excluded from the block + for { + tx := types.NewTransaction(b.txPool.Nonce(testBankAddress)+nonceCounter, testUserAddress, big.NewInt(1000), params.TxGas, txGasPrice, nil) + txsToAdd = append(txsToAdd, tx) + gasLimit -= params.TxGas + nonceCounter += 1 + + if gasLimit < 21000 { + // add 5 more such txs which have to be excluded from the block + for i := 0; i < 5; i++ { + tx := types.NewTransaction(b.txPool.Nonce(testBankAddress)+nonceCounter, testUserAddress, big.NewInt(1000), params.TxGas, txGasPrice, nil) + txsToAdd = append(txsToAdd, tx) + nonceCounter += 1 + } + break + } + } + + signedTxs := types.Transactions{} + for _, tx := range txsToAdd { + signedTx, err := types.SignTx(tx, types.HomesteadSigner{}, testBankKey) + if err != nil { + t.Fatalf("Failed to sign tx %v", err) + } + signedTxs = append(signedTxs, signedTx) + } + + // set the astria ordered txsToBuildPayload + b.TxPool().SetAstriaOrdered(signedTxs) + astriaTxs := b.TxPool().AstriaOrdered() + + if astriaTxs.Len() != len(txsToAdd) { + t.Fatalf("Unexpected number of astria ordered transactions: %d", astriaTxs.Len()) + } + + txs := types.TxDifference(*astriaTxs, signedTxs) + if txs.Len() != 0 { + t.Fatalf("Unexpected transactions in astria ordered transactions: %v", txs) + } + + args := &BuildPayloadArgs{ + Parent: b.chain.CurrentBlock().Hash(), + Timestamp: timestamp, + Random: common.Hash{}, + FeeRecipient: recipient, + } + + payload, err := w.buildPayload(args) + if err != nil { + t.Fatalf("Failed to build payload %v", err) + } + full := payload.ResolveFull() + astriaExcludedFromBlock := b.TxPool().AstriaExcludedFromBlock() + + // ensure that the transactions not included in the block are included in astriaExcludedFromBlock + excludedTxsCount := len(txsToAdd) - len(full.ExecutionPayload.Transactions) + if astriaExcludedFromBlock.Len() != excludedTxsCount { + t.Fatalf("Unexpected number of transactions in astria excluded from block: %d", astriaExcludedFromBlock.Len()) + } + + // ensure that only the excluded txs are in the list + if astriaExcludedFromBlock.Len() > 0 { + for _, binaryTx := range full.ExecutionPayload.Transactions { + tx := new(types.Transaction) + err = tx.UnmarshalBinary(binaryTx) + if err != nil { + t.Fatalf("Failed to unmarshal binary transaction %v", err) + } + if contains(*astriaExcludedFromBlock, tx) { + t.Fatalf("Transaction %v should not be in the astria excluded from block list", tx) + } + } + } +} + +func TestBuildPayloadTimeout(t *testing.T) { + var ( + db = rawdb.NewMemoryDatabase() + recipient = common.HexToAddress("0xdeadbeef") + ) + w, b := newTestWorker(t, params.TestChainConfig, ethash.NewFaker(), db, 0) + + timestamp := uint64(time.Now().Unix()) + + contains := func(transactions types.Transactions, tx *types.Transaction) bool { + for _, t := range transactions { + if t.Hash() == tx.Hash() { + return true + } + } + return false + } + + txGasPrice := big.NewInt(10 * params.InitialBaseFee) + + txsToAdd := types.Transactions{ + types.NewTransaction(b.txPool.Nonce(testBankAddress), testUserAddress, big.NewInt(1000), params.TxGas, txGasPrice, nil), + types.NewTransaction(b.txPool.Nonce(testBankAddress)+1, testUserAddress, big.NewInt(2000), params.TxGas, txGasPrice, nil), + types.NewTransaction(b.txPool.Nonce(testBankAddress)+2, testUserAddress, big.NewInt(2000), params.TxGas, txGasPrice, nil), + types.NewTransaction(b.txPool.Nonce(testBankAddress)+3, testUserAddress, big.NewInt(2000), params.TxGas, txGasPrice, nil), + types.NewTransaction(b.txPool.Nonce(testBankAddress)+4, testUserAddress, big.NewInt(2000), params.TxGas, txGasPrice, nil), + } + signedTxs := types.Transactions{} + for _, tx := range txsToAdd { + signedTx, err := types.SignTx(tx, types.HomesteadSigner{}, testBankKey) + if err != nil { + t.Fatalf("Failed to sign tx %v", err) + } + signedTxs = append(signedTxs, signedTx) + } + + // set the astria ordered txsToBuildPayload + b.TxPool().SetAstriaOrdered(signedTxs) + astriaTxs := b.TxPool().AstriaOrdered() + + if astriaTxs.Len() != len(txsToAdd) { + t.Fatalf("Unexpected number of astria ordered transactions: %d", astriaTxs.Len()) + } + + txs := types.TxDifference(*astriaTxs, signedTxs) + if txs.Len() != 0 { + t.Fatalf("Unexpected transactions in astria ordered transactions: %v", txs) + } + + // a very small recommit to reliably timeout the payload building + w.config.Recommit = time.Nanosecond + args := &BuildPayloadArgs{ + Parent: b.chain.CurrentBlock().Hash(), + Timestamp: timestamp, + Random: common.Hash{}, + FeeRecipient: recipient, + } + + payload, err := w.buildPayload(args) + if err != nil { + t.Fatalf("Failed to build payload %v", err) + } + full := payload.ResolveFull() + astriaExcludedFromBlock := b.TxPool().AstriaExcludedFromBlock() + + // ensure that the transactions not included in the block are included in astriaExcludedFromBlock + excludedTxsCount := len(txsToAdd) - len(full.ExecutionPayload.Transactions) + if astriaExcludedFromBlock.Len() != excludedTxsCount { + t.Fatalf("Unexpected number of transactions in astria excluded from block: %d", astriaExcludedFromBlock.Len()) + } + + // ensure that only the excluded txs are in the list + if astriaExcludedFromBlock.Len() > 0 { + for _, binaryTx := range full.ExecutionPayload.Transactions { + tx := new(types.Transaction) + err = tx.UnmarshalBinary(binaryTx) + if err != nil { + t.Fatalf("Failed to unmarshal binary transaction %v", err) + } + if contains(*astriaExcludedFromBlock, tx) { + t.Fatalf("Transaction %v should not be in the astria excluded from block list", tx) + } + } + } +} + func TestBuildPayload(t *testing.T) { var ( db = rawdb.NewMemoryDatabase() diff --git a/miner/worker.go b/miner/worker.go index f9b1950a4..7b36a2e87 100644 --- a/miner/worker.go +++ b/miner/worker.go @@ -246,10 +246,14 @@ func (miner *Miner) commitAstriaTransactions(env *environment, txs *types.Transa env.gasPool = new(core.GasPool).AddGas(gasLimit) } - for _, tx := range *txs { + for i, tx := range *txs { // Check interruption signal and abort building if it's fired. if interrupt != nil { if signal := interrupt.Load(); signal != commitInterruptNone { + // remove the subsequent txs from the mempool if block building has been interrupted + for _, txToRemove := range (*txs)[i:] { + miner.txpool.AddToAstriaExcludedFromBlock(txToRemove) + } return signalToErr(signal) } } @@ -257,7 +261,9 @@ func (miner *Miner) commitAstriaTransactions(env *environment, txs *types.Transa if env.gasPool.Gas() < params.TxGas { log.Trace("Not enough gas for further transactions", "have", env.gasPool, "want", params.TxGas) // remove txs from the mempool if they are too big for this block - miner.txpool.AddToAstriaExcludedFromBlock(tx) + for _, txToRemove := range (*txs)[i:] { + miner.txpool.AddToAstriaExcludedFromBlock(txToRemove) + } break }