diff --git a/core/vm/zk_batch_counters.go b/core/vm/zk_batch_counters.go index b40da0a02c1..83b2c36ed96 100644 --- a/core/vm/zk_batch_counters.go +++ b/core/vm/zk_batch_counters.go @@ -142,6 +142,10 @@ func (bcc *BatchCounterCollector) processBatchLevelData() error { // CheckForOverflow returns true in the case that any counter has less than 0 remaining func (bcc *BatchCounterCollector) CheckForOverflow(verifyMerkleProof bool) (bool, error) { + // unlimited counters shouldn't overflow + if bcc.unlimitedCounters { + return false, nil + } combined, err := bcc.CombineCollectors(verifyMerkleProof) if err != nil { return false, err diff --git a/zk/stages/stage_sequence_execute.go b/zk/stages/stage_sequence_execute.go index 3673d90f8cb..43883d8f82a 100644 --- a/zk/stages/stage_sequence_execute.go +++ b/zk/stages/stage_sequence_execute.go @@ -346,12 +346,21 @@ func sequencingBatchStep( return err } } else if !batchState.isL1Recovery() { + var allConditionsOK bool - batchState.blockState.transactionsForInclusion, allConditionsOK, err = getNextPoolTransactions(ctx, cfg, executionAt, batchState.forkId, batchState.yieldedTransactions) + var newTransactions []types.Transaction + var newIds []common.Hash + + newTransactions, newIds, allConditionsOK, err = getNextPoolTransactions(ctx, cfg, executionAt, batchState.forkId, batchState.yieldedTransactions) if err != nil { return err } + batchState.blockState.transactionsForInclusion = append(batchState.blockState.transactionsForInclusion, newTransactions...) + for idx, tx := range newTransactions { + batchState.blockState.transactionHashesToSlots[tx.Hash()] = newIds[idx] + } + if len(batchState.blockState.transactionsForInclusion) == 0 { if allConditionsOK { time.Sleep(batchContext.cfg.zk.SequencerTimeoutOnEmptyTxPool) @@ -513,6 +522,8 @@ func sequencingBatchStep( return err } + cfg.txPool.RemoveMinedTransactions(batchState.blockState.builtBlockElements.txSlots) + if batchState.isLimboRecovery() { stateRoot := block.Root() cfg.txPool.UpdateLimboRootByTxHash(batchState.limboRecoveryData.limboTxHash, &stateRoot) diff --git a/zk/stages/stage_sequence_execute_state.go b/zk/stages/stage_sequence_execute_state.go index 4e74f6210a8..4ce99180806 100644 --- a/zk/stages/stage_sequence_execute_state.go +++ b/zk/stages/stage_sequence_execute_state.go @@ -165,7 +165,11 @@ func (bs *BatchState) getCoinbase(cfg *SequenceBlockCfg) common.Address { } func (bs *BatchState) onAddedTransaction(transaction types.Transaction, receipt *types.Receipt, execResult *core.ExecutionResult, effectiveGas uint8) { - bs.blockState.builtBlockElements.onFinishAddingTransaction(transaction, receipt, execResult, effectiveGas) + slotId, ok := bs.blockState.transactionHashesToSlots[transaction.Hash()] + if !ok { + log.Warn("[batchState] transaction hash not found in transaction hashes to slots map", "hash", transaction.Hash()) + } + bs.blockState.builtBlockElements.onFinishAddingTransaction(transaction, receipt, execResult, effectiveGas, slotId) bs.hasAnyTransactionsInThisBatch = true } @@ -250,12 +254,15 @@ func newLimboRecoveryData(limboHeaderTimestamp uint64, limboTxHash *common.Hash) // TYPE BLOCK STATE type BlockState struct { transactionsForInclusion []types.Transaction + transactionHashesToSlots map[common.Hash]common.Hash builtBlockElements BuiltBlockElements blockL1RecoveryData *zktx.DecodedBatchL2Data } func newBlockState() *BlockState { - return &BlockState{} + return &BlockState{ + transactionHashesToSlots: make(map[common.Hash]common.Hash), + } } func (bs *BlockState) hasAnyTransactionForInclusion() bool { @@ -294,6 +301,7 @@ type BuiltBlockElements struct { receipts types.Receipts effectiveGases []uint8 executionResults []*core.ExecutionResult + txSlots []common.Hash } func (bbe *BuiltBlockElements) resetBlockBuildingArrays() { @@ -303,11 +311,12 @@ func (bbe *BuiltBlockElements) resetBlockBuildingArrays() { bbe.executionResults = []*core.ExecutionResult{} } -func (bbe *BuiltBlockElements) onFinishAddingTransaction(transaction types.Transaction, receipt *types.Receipt, execResult *core.ExecutionResult, effectiveGas uint8) { +func (bbe *BuiltBlockElements) onFinishAddingTransaction(transaction types.Transaction, receipt *types.Receipt, execResult *core.ExecutionResult, effectiveGas uint8, slotId common.Hash) { bbe.transactions = append(bbe.transactions, transaction) bbe.receipts = append(bbe.receipts, receipt) bbe.executionResults = append(bbe.executionResults, execResult) bbe.effectiveGases = append(bbe.effectiveGases, effectiveGas) + bbe.txSlots = append(bbe.txSlots, slotId) } type resequenceTxMetadata struct { diff --git a/zk/stages/stage_sequence_execute_transactions.go b/zk/stages/stage_sequence_execute_transactions.go index 2b14891d3fb..713dc462d39 100644 --- a/zk/stages/stage_sequence_execute_transactions.go +++ b/zk/stages/stage_sequence_execute_transactions.go @@ -19,10 +19,11 @@ import ( "github.com/ledgerwatch/log/v3" ) -func getNextPoolTransactions(ctx context.Context, cfg SequenceBlockCfg, executionAt, forkId uint64, alreadyYielded mapset.Set[[32]byte]) ([]types.Transaction, bool, error) { +func getNextPoolTransactions(ctx context.Context, cfg SequenceBlockCfg, executionAt, forkId uint64, alreadyYielded mapset.Set[[32]byte]) ([]types.Transaction, []common.Hash, bool, error) { cfg.txPool.LockFlusher() defer cfg.txPool.UnlockFlusher() + var ids []common.Hash var transactions []types.Transaction var allConditionsOk bool var err error @@ -37,7 +38,7 @@ func getNextPoolTransactions(ctx context.Context, cfg SequenceBlockCfg, executio if allConditionsOk, _, err = cfg.txPool.YieldBest(cfg.yieldSize, &slots, poolTx, executionAt, gasLimit, 0, alreadyYielded); err != nil { return err } - yieldedTxs, toRemove, err := extractTransactionsFromSlot(&slots) + yieldedTxs, yieldedIds, toRemove, err := extractTransactionsFromSlot(&slots) if err != nil { return err } @@ -45,12 +46,13 @@ func getNextPoolTransactions(ctx context.Context, cfg SequenceBlockCfg, executio cfg.txPool.MarkForDiscardFromPendingBest(txId) } transactions = append(transactions, yieldedTxs...) + ids = append(ids, yieldedIds...) return nil }); err != nil { - return nil, allConditionsOk, err + return nil, nil, allConditionsOk, err } - return transactions, allConditionsOk, err + return transactions, ids, allConditionsOk, err } func getLimboTransaction(ctx context.Context, cfg SequenceBlockCfg, txHash *common.Hash) ([]types.Transaction, error) { @@ -68,7 +70,7 @@ func getLimboTransaction(ctx context.Context, cfg SequenceBlockCfg, txHash *comm if slots != nil { // ignore the toRemove value here, we know the RLP will be sound as we had to read it from the pool // in the first place to get it into limbo - transactions, _, err = extractTransactionsFromSlot(slots) + transactions, _, _, err = extractTransactionsFromSlot(slots) if err != nil { return err } @@ -82,7 +84,8 @@ func getLimboTransaction(ctx context.Context, cfg SequenceBlockCfg, txHash *comm return transactions, nil } -func extractTransactionsFromSlot(slot *types2.TxsRlp) ([]types.Transaction, []common.Hash, error) { +func extractTransactionsFromSlot(slot *types2.TxsRlp) ([]types.Transaction, []common.Hash, []common.Hash, error) { + ids := make([]common.Hash, 0, len(slot.TxIds)) transactions := make([]types.Transaction, 0, len(slot.Txs)) toRemove := make([]common.Hash, 0) for idx, txBytes := range slot.Txs { @@ -101,8 +104,9 @@ func extractTransactionsFromSlot(slot *types2.TxsRlp) ([]types.Transaction, []co copy(sender[:], slot.Senders.At(idx)) transaction.SetSender(sender) transactions = append(transactions, transaction) + ids = append(ids, slot.TxIds[idx]) } - return transactions, toRemove, nil + return transactions, ids, toRemove, nil } type overflowType uint8 diff --git a/zk/txpool/pool_zk.go b/zk/txpool/pool_zk.go index d9c52bfc81d..950b1990a02 100644 --- a/zk/txpool/pool_zk.go +++ b/zk/txpool/pool_zk.go @@ -267,6 +267,36 @@ func (p *TxPool) MarkForDiscardFromPendingBest(txHash common.Hash) { } } +func (p *TxPool) RemoveMinedTransactions(ids []common.Hash) { + p.lock.Lock() + defer p.lock.Unlock() + + toDelete := make([]*metaTx, 0) + + p.all.ascendAll(func(mt *metaTx) bool { + for _, id := range ids { + if bytes.Equal(mt.Tx.IDHash[:], id[:]) { + toDelete = append(toDelete, mt) + switch mt.currentSubPool { + case PendingSubPool: + p.pending.Remove(mt) + case BaseFeeSubPool: + p.baseFee.Remove(mt) + case QueuedSubPool: + p.queued.Remove(mt) + default: + //already removed + } + } + } + return true + }) + + for _, mt := range toDelete { + p.discardLocked(mt, Mined) + } +} + // discards the transactions that are in overflowZkCoutners from pending // executes the discard function on them // deletes the tx from the sendersWithChangedState map