diff --git a/zk/debug_tools/test-contracts/contracts/GasBurner.sol b/zk/debug_tools/test-contracts/contracts/GasBurner.sol new file mode 100644 index 00000000000..bdc7a8d8db8 --- /dev/null +++ b/zk/debug_tools/test-contracts/contracts/GasBurner.sol @@ -0,0 +1,12 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.15; + +contract GasBurner { + constructor() { + //dynamic array + uint[] memory a = new uint[](12000); + for (uint i = 0; i < 2000; i++) { + a[i%10000] = i; + } + } +} \ No newline at end of file diff --git a/zk/debug_tools/test-contracts/package.json b/zk/debug_tools/test-contracts/package.json index 7022ac7abf5..3164dd36bc4 100644 --- a/zk/debug_tools/test-contracts/package.json +++ b/zk/debug_tools/test-contracts/package.json @@ -21,7 +21,8 @@ "chainCall:local": "npx hardhat compile && npx hardhat run scripts/chain-call.js --network local", "chainCall:sepolia": "npx hardhat compile && npx hardhat run scripts/chain-call.js --network sepolia", "create:local": "npx hardhat compile && npx hardhat run scripts/create.js --network local", - "keccak:local": "npx hardhat compile && npx hardhat run scripts/keccak-loop.js --network local" + "keccak:local": "npx hardhat compile && npx hardhat run scripts/keccak-loop.js --network local", + "gasBurner:local": "npx hardhat compile && npx hardhat run scripts/gas-burner.js --network local" }, "keywords": [], "author": "", diff --git a/zk/debug_tools/test-contracts/scripts/gas-burner.js b/zk/debug_tools/test-contracts/scripts/gas-burner.js new file mode 100644 index 00000000000..e3564d84582 --- /dev/null +++ b/zk/debug_tools/test-contracts/scripts/gas-burner.js @@ -0,0 +1,26 @@ +async function main() { +try { + // Get the ContractFactory of your BigLoopContract + const GasBurnerContract = await hre.ethers.getContractFactory("GasBurner"); + + // Deploy the contract + const contract = await GasBurnerContract.deploy(); + // Wait for the deployment transaction to be mined + await contract.waitForDeployment(); + + console.log(`GasBurner deployed to: ${await contract.getAddress()}`); + + // const result = await contract.bigLoop(10000); + // console.log(result); + } catch (error) { + console.error(error); + process.exit(1); + } +} + +main() + .then(() => process.exit(0)) + .catch(error => { + console.error(error); + process.exit(1); + }); \ No newline at end of file diff --git a/zk/stages/stage_sequence_execute.go b/zk/stages/stage_sequence_execute.go index 72718589b88..3710ed6025d 100644 --- a/zk/stages/stage_sequence_execute.go +++ b/zk/stages/stage_sequence_execute.go @@ -307,8 +307,14 @@ func sequencingBatchStep( log.Info(fmt.Sprintf("[%s] Waiting for txs from the pool...", logPrefix)) } - LOOP_TRANSACTIONS: + innerBreak := false + emptyBlockOverflow := false + + OuterLoopTransactions: for { + if innerBreak { + break + } select { case <-logTicker.C: if !batchState.isAnyRecovery() { @@ -316,7 +322,7 @@ func sequencingBatchStep( } case <-blockTicker.C: if !batchState.isAnyRecovery() { - break LOOP_TRANSACTIONS + break OuterLoopTransactions } case <-batchTicker.C: if !batchState.isAnyRecovery() { @@ -380,7 +386,19 @@ func sequencingBatchStep( badTxHashes := make([]common.Hash, 0) minedTxHashes := make([]common.Hash, 0) + + InnerLoopTransactions: for i, transaction := range batchState.blockState.transactionsForInclusion { + // quick check if we should stop handling transactions + select { + case <-blockTicker.C: + if !batchState.isAnyRecovery() { + innerBreak = true + break InnerLoopTransactions + } + default: + } + txHash := transaction.Hash() effectiveGas := batchState.blockState.getL1EffectiveGases(cfg, i) @@ -457,7 +475,10 @@ func sequencingBatchStep( if batchState.reachedOverflowTransactionLimit() || cfg.zk.SealBatchImmediatelyOnOverflow { log.Info(fmt.Sprintf("[%s] closing batch due to counters", logPrefix), "counters: ", batchState.overflowTransactions, "immediate", cfg.zk.SealBatchImmediatelyOnOverflow) runLoopBlocks = false - break LOOP_TRANSACTIONS + if len(batchState.blockState.builtBlockElements.transactions) == 0 { + emptyBlockOverflow = true + } + break OuterLoopTransactions } } @@ -474,7 +495,7 @@ func sequencingBatchStep( } log.Info(fmt.Sprintf("[%s] gas overflowed adding transaction to block", logPrefix), "block", blockNumber, "tx-hash", txHash) runLoopBlocks = false - break LOOP_TRANSACTIONS + break OuterLoopTransactions case overflowNone: } @@ -494,12 +515,12 @@ func sequencingBatchStep( if len(batchState.blockState.transactionsForInclusion) == 0 { // We need to jump to the next block here if there are no transactions in current block batchState.resequenceBatchJob.UpdateLastProcessedTx(batchState.resequenceBatchJob.CurrentBlock().L2Blockhash) - break LOOP_TRANSACTIONS + break OuterLoopTransactions } if batchState.resequenceBatchJob.AtNewBlockBoundary() { // We need to jump to the next block here if we are at the end of the current block - break LOOP_TRANSACTIONS + break OuterLoopTransactions } else { if cfg.zk.SequencerResequenceStrict { return fmt.Errorf("strict mode enabled, but resequenced batch %d has transactions that overflowed counters or failed transactions", batchState.batchNumber) @@ -533,16 +554,23 @@ func sequencingBatchStep( log.Info(fmt.Sprintf("[%s] L1 recovery no more transactions to recover", logPrefix)) } - break LOOP_TRANSACTIONS + break OuterLoopTransactions } if batchState.isLimboRecovery() { runLoopBlocks = false - break LOOP_TRANSACTIONS + break OuterLoopTransactions } } } + // we do not want to commit this block if it has no transactions and we detected an overflow - essentially the batch is too + // full to get any more transactions in it and we don't want to commit an empty block + if emptyBlockOverflow { + log.Info(fmt.Sprintf("[%s] Block %d overflow detected with no transactions added, skipping block for next batch", logPrefix, blockNumber)) + break + } + if block, err = doFinishBlockAndUpdateState(batchContext, ibs, header, parentBlock, batchState, ger, l1BlockHash, l1TreeUpdateIndex, infoTreeIndexProgress, batchCounters); err != nil { return err }