diff --git a/cmd/rpcdaemon/commands/zkevm_api.go b/cmd/rpcdaemon/commands/zkevm_api.go index c89c01c5586..8e7bd6e3265 100644 --- a/cmd/rpcdaemon/commands/zkevm_api.go +++ b/cmd/rpcdaemon/commands/zkevm_api.go @@ -314,43 +314,59 @@ func (api *ZkEvmAPIImpl) GetBatchDataByNumbers(ctx context.Context, batchNumbers return nil, err } - // last batch last block for deltaTimestamp calc - lastBlockNoInPreviousBatch := batchBlocks[0].NumberU64() - 1 - lastBlockInPreviousBatch, err := rawdb.ReadBlockByNumber(tx, lastBlockNoInPreviousBatch) + batchL2Data, err := generateBatchData(tx, hermezDb, batchBlocks, forkId) if err != nil { return nil, err } - var batchL2Data []byte - for i := 0; i < len(batchBlocks); i++ { - var dTs uint32 - if i == 0 { - dTs = uint32(batchBlocks[i].Time() - lastBlockInPreviousBatch.Time()) - } else { - dTs = uint32(batchBlocks[i].Time() - batchBlocks[i-1].Time()) - } - iti, err := hermezDb.GetBlockL1InfoTreeIndex(batchBlocks[i].NumberU64()) - - egTx := make(map[common.Hash]uint8) - for _, txn := range batchBlocks[i].Transactions() { - eg, err := hermezDb.GetEffectiveGasPricePercentage(txn.Hash()) - if err != nil { - return nil, err - } - egTx[txn.Hash()] = eg - } + bd.BatchL2Data = batchL2Data + bds = append(bds, bd) + } - bl2d, err := zktx.GenerateBlockBatchL2Data(uint16(forkId), dTs, uint32(iti), batchBlocks[i].Transactions(), egTx) + return populateBatchDataSlimDetails(bds) +} + +func generateBatchData( + tx kv.Tx, + hermezDb *hermez_db.HermezDbReader, + batchBlocks []*eritypes.Block, + forkId uint64, +) (batchL2Data []byte, err error) { + lastBlockNoInPreviousBatch := batchBlocks[0].NumberU64() - 1 + lastBlockInPreviousBatch, err := rawdb.ReadBlockByNumber(tx, lastBlockNoInPreviousBatch) + if err != nil { + return nil, err + } + + batchL2Data = []byte{} + for i := 0; i < len(batchBlocks); i++ { + var dTs uint32 + if i == 0 { + dTs = uint32(batchBlocks[i].Time() - lastBlockInPreviousBatch.Time()) + } else { + dTs = uint32(batchBlocks[i].Time() - batchBlocks[i-1].Time()) + } + iti, err := hermezDb.GetBlockL1InfoTreeIndex(batchBlocks[i].NumberU64()) + if err != nil { + return nil, err + } + egTx := make(map[common.Hash]uint8) + for _, txn := range batchBlocks[i].Transactions() { + eg, err := hermezDb.GetEffectiveGasPricePercentage(txn.Hash()) if err != nil { return nil, err } - batchL2Data = append(batchL2Data, bl2d...) + egTx[txn.Hash()] = eg } - bd.BatchL2Data = batchL2Data - bds = append(bds, bd) + + bl2d, err := zktx.GenerateBlockBatchL2Data(uint16(forkId), dTs, uint32(iti), batchBlocks[i].Transactions(), egTx) + if err != nil { + return nil, err + } + batchL2Data = append(batchL2Data, bl2d...) } - return populateBatchDataSlimDetails(bds) + return batchL2Data, err } // GetBatchByNumber returns a batch from the current canonical chain. If number is nil, the @@ -529,38 +545,10 @@ func (api *ZkEvmAPIImpl) GetBatchByNumber(ctx context.Context, batchNumber rpc.B return nil, err } - // last batch last block for deltaTimestamp calc - lastBlockNoInPreviousBatch := batchBlocks[0].NumberU64() - 1 - lastBlockInPreviousBatch, err := rawdb.ReadBlockByNumber(tx, lastBlockNoInPreviousBatch) + batchL2Data, err := generateBatchData(tx, hermezDb, batchBlocks, forkId) if err != nil { return nil, err } - - var batchL2Data []byte - for i := 0; i < len(batchBlocks); i++ { - var dTs uint32 - if i == 0 { - dTs = uint32(batchBlocks[i].Time() - lastBlockInPreviousBatch.Time()) - } else { - dTs = uint32(batchBlocks[i].Time() - batchBlocks[i-1].Time()) - } - iti, err := hermezDb.GetBlockL1InfoTreeIndex(batchBlocks[i].NumberU64()) - - egTx := make(map[common.Hash]uint8) - for _, txn := range batchBlocks[i].Transactions() { - eg, err := hermezDb.GetEffectiveGasPricePercentage(txn.Hash()) - if err != nil { - return nil, err - } - egTx[txn.Hash()] = eg - } - - bl2d, err := zktx.GenerateBlockBatchL2Data(uint16(forkId), dTs, uint32(iti), batchBlocks[i].Transactions(), egTx) - if err != nil { - return nil, err - } - batchL2Data = append(batchL2Data, bl2d...) - } batch.BatchL2Data = batchL2Data // currently gives 'error execution reverted' when calling the L1 diff --git a/zk/stages/stage_l1syncer.go b/zk/stages/stage_l1syncer.go index 87e8d9efada..cedfcdfdbfa 100644 --- a/zk/stages/stage_l1syncer.go +++ b/zk/stages/stage_l1syncer.go @@ -18,6 +18,7 @@ import ( "github.com/ledgerwatch/erigon/eth/stagedsync/stages" "github.com/ledgerwatch/erigon/zk/contracts" "github.com/ledgerwatch/erigon/zk/hermez_db" + "github.com/ledgerwatch/erigon/zk/sequencer" "github.com/ledgerwatch/erigon/zk/types" ) @@ -413,12 +414,13 @@ func verifyAgainstLocalBlocks(tx kv.RwTx, hermezDb *hermez_db.HermezDb, logPrefi return nil } - err = blockComparison(tx, hermezDb, blockToCheck, logPrefix) - - if err == nil { - log.Info(fmt.Sprintf("[%s] State root verified in block %d", logPrefix, blockToCheck)) - if err := stages.SaveStageProgress(tx, stages.VerificationsStateRootCheck, verifiedBlockNo); err != nil { - return fmt.Errorf("failed to save stage progress, %w", err) + if !sequencer.IsSequencer() { + err = blockComparison(tx, hermezDb, blockToCheck, logPrefix) + if err == nil { + log.Info(fmt.Sprintf("[%s] State root verified in block %d", logPrefix, blockToCheck)) + if err := stages.SaveStageProgress(tx, stages.VerificationsStateRootCheck, verifiedBlockNo); err != nil { + return fmt.Errorf("failed to save stage progress, %w", err) + } } } diff --git a/zk/stages/stage_sequence_execute.go b/zk/stages/stage_sequence_execute.go index 0e0bf060029..c0d5b8c086c 100644 --- a/zk/stages/stage_sequence_execute.go +++ b/zk/stages/stage_sequence_execute.go @@ -20,9 +20,9 @@ import ( "github.com/ledgerwatch/erigon/eth/stagedsync/stages" "github.com/ledgerwatch/erigon/zk" "github.com/ledgerwatch/erigon/zk/datastream/server" + "github.com/ledgerwatch/erigon/zk/l1_data" zktx "github.com/ledgerwatch/erigon/zk/tx" "github.com/ledgerwatch/erigon/zk/utils" - "github.com/ledgerwatch/erigon/zk/l1_data" ) var SpecialZeroIndexHash = common.HexToHash("0x27AE5BA08D7291C96C8CBDDCC148BF48A6D68C7974B94356F53754EF6171D757") @@ -207,6 +207,11 @@ func SpawnSequencingStage( } } + blockDataSizeChecker := NewBlockDataChecker() + + prevHeader := rawdb.ReadHeaderByNumber(tx, executionAt) + batchDataOverflow := false + log.Info(fmt.Sprintf("[%s] Starting batch %d...", logPrefix, thisBatch)) for blockNumber := executionAt; runLoopBlocks; blockNumber++ { @@ -223,6 +228,11 @@ func SpawnSequencingStage( blockTransactions = decodedBlock.Transactions } + l1InfoIndex, err := sdb.hermezDb.GetBlockL1InfoTreeIndex(lastStartedBn) + if err != nil { + return err + } + log.Info(fmt.Sprintf("[%s] Starting block %d...", logPrefix, blockNumber+1)) reRunBlockAfterOverflow := blockNumber == lastStartedBn @@ -237,6 +247,12 @@ func SpawnSequencingStage( if err != nil { return err } + + // run this only once the first time, do not add it on rerun + if batchDataOverflow = blockDataSizeChecker.AddBlockStartData(uint16(forkId), uint32(prevHeader.Time-header.Time), uint32(l1InfoIndex)); batchDataOverflow { + log.Info(fmt.Sprintf("[%s] BatchL2Data limit reached. Stopping.", logPrefix), "blockNumber", blockNumber) + break + } } else { batchCounters = clonedBatchCounters @@ -251,11 +267,6 @@ func SpawnSequencingStage( } } - l1InfoIndex, err := sdb.hermezDb.GetBlockL1InfoTreeIndex(lastStartedBn) - if err != nil { - return err - } - overflowOnNewBlock, err := batchCounters.StartNewBlock(l1InfoIndex != 0) if err != nil { return err @@ -357,26 +368,38 @@ func SpawnSequencingStage( effectiveGas = DeriveEffectiveGasPrice(cfg, transaction) } - receipt, overflow, err = attemptAddTransaction(cfg, sdb, ibs, batchCounters, &blockContext, header, transaction, effectiveGas, l1Recovery, forkId, l1InfoIndex) - if err != nil { - if limboRecovery { - panic("limbo transaction has already been executed once so they must not fail while re-executing") - } + // run this only once the first time, do not add it on rerun + if batchDataOverflow, err = blockDataSizeChecker.AddTransactionData(transaction, uint16(forkId), effectiveGas); err != nil { + return err + } - // if we are in recovery just log the error as a warning. If the data is on the L1 then we should consider it as confirmed. - // The executor/prover would simply skip a TX with an invalid nonce for example so we don't need to worry about that here. - if l1Recovery { - log.Warn(fmt.Sprintf("[%s] error adding transaction to batch during recovery: %v", logPrefix, err), - "hash", transaction.Hash(), - "to", transaction.GetTo(), - ) - continue - } + if !batchDataOverflow { + receipt, overflow, err = attemptAddTransaction(cfg, sdb, ibs, batchCounters, &blockContext, header, transaction, effectiveGas, l1Recovery, forkId, l1InfoIndex) + if err != nil { + if limboRecovery { + panic("limbo transaction has already been executed once so they must not fail while re-executing") + } - i++ // leave current tx in yielded set - reRunBlock = true + // if we are in recovery just log the error as a warning. If the data is on the L1 then we should consider it as confirmed. + // The executor/prover would simply skip a TX with an invalid nonce for example so we don't need to worry about that here. + if l1Recovery { + log.Warn(fmt.Sprintf("[%s] error adding transaction to batch during recovery: %v", logPrefix, err), + "hash", transaction.Hash(), + "to", transaction.GetTo(), + ) + continue + } + + i++ // leave current tx in yielded set + reRunBlock = true + } + } else { + log.Info(fmt.Sprintf("[%s] BatchL2Data limit reached. Not adding last transaction", logPrefix), "txHash", transaction.Hash()) } - if !reRunBlock && overflow { + + anyOverflow := overflow || batchDataOverflow + + if !reRunBlock && anyOverflow { if limboRecovery { panic("limbo transaction has already been executed once so they must not overflow counters while re-executing") } diff --git a/zk/stages/stage_sequence_execute_utils.go b/zk/stages/stage_sequence_execute_utils.go index db3269f6211..647758c98cb 100644 --- a/zk/stages/stage_sequence_execute_utils.go +++ b/zk/stages/stage_sequence_execute_utils.go @@ -32,11 +32,12 @@ import ( "github.com/ledgerwatch/erigon/turbo/shards" "github.com/ledgerwatch/erigon/turbo/stages/headerdownload" "github.com/ledgerwatch/erigon/zk/hermez_db" + "github.com/ledgerwatch/erigon/zk/tx" zktx "github.com/ledgerwatch/erigon/zk/tx" "github.com/ledgerwatch/erigon/zk/txpool" zktypes "github.com/ledgerwatch/erigon/zk/types" - "github.com/ledgerwatch/log/v3" "github.com/ledgerwatch/erigon/zk/utils" + "github.com/ledgerwatch/log/v3" ) const ( @@ -421,3 +422,45 @@ func checkForBadBatch( return false, nil } + +var ( + LIMIT_128_KB = uint64(128 * 1024) +) + +type BlockDataChecker struct { + limit uint64 // amount of bytes + bytes []byte +} + +func NewBlockDataChecker() *BlockDataChecker { + return &BlockDataChecker{ + limit: LIMIT_128_KB, + bytes: make([]byte, 0), + } +} + +// adds bytes amounting to the block data and checks if the limit is reached +// if the limit is reached, the data is not added, so this can be reused again for next check +func (bdc *BlockDataChecker) AddBlockStartData(forkId uint16, deltaTimestamp, l1InfoTreeIndex uint32) bool { + newBytes := tx.GenerateStartBlockBatchL2Data(forkId, deltaTimestamp, l1InfoTreeIndex) + + if uint64(len(bdc.bytes)+len(newBytes)) > bdc.limit { + return true + } + + bdc.bytes = append(bdc.bytes, newBytes...) + return false +} + +func (bdc *BlockDataChecker) AddTransactionData(transaction types.Transaction, forkId uint16, effectiveGasPrice uint8) (bool, error) { + encoded, err := tx.TransactionToL2Data(transaction, forkId, effectiveGasPrice) + if err != nil { + return false, err + } + if uint64(len(bdc.bytes)+len(encoded)) > bdc.limit { + return true, nil + } + + bdc.bytes = append(bdc.bytes, encoded...) + return false, nil +} diff --git a/zk/tx/tx.go b/zk/tx/tx.go index f8cc75727aa..656389437f7 100644 --- a/zk/tx/tx.go +++ b/zk/tx/tx.go @@ -383,12 +383,8 @@ func GetDecodedV(tx types.Transaction, v *uint256.Int) *uint256.Int { } func GenerateBlockBatchL2Data(forkId uint16, deltaTimestamp uint32, l1InfoTreeIndex uint32, transactions []types.Transaction, egTx map[common.Hash]uint8) ([]byte, error) { - var result []byte - // add in the changeL2Block transaction - result = append(result, changeL2BlockTxType) - result = binary.BigEndian.AppendUint32(result, deltaTimestamp) - result = binary.BigEndian.AppendUint32(result, l1InfoTreeIndex) + result := GenerateStartBlockBatchL2Data(forkId, deltaTimestamp, l1InfoTreeIndex) for _, transaction := range transactions { encoded, err := TransactionToL2Data(transaction, forkId, egTx[transaction.Hash()]) @@ -401,6 +397,17 @@ func GenerateBlockBatchL2Data(forkId uint16, deltaTimestamp uint32, l1InfoTreeIn return result, nil } +func GenerateStartBlockBatchL2Data(forkId uint16, deltaTimestamp uint32, l1InfoTreeIndex uint32) []byte { + var result []byte + + // add in the changeL2Block transaction + result = append(result, changeL2BlockTxType) + result = binary.BigEndian.AppendUint32(result, deltaTimestamp) + result = binary.BigEndian.AppendUint32(result, l1InfoTreeIndex) + + return result +} + func ComputeL2TxHash( chainId *big.Int, value, gasPrice *uint256.Int,