From 3896a9be49ff35c063e7a73aa387183744f7170e Mon Sep 17 00:00:00 2001 From: Kamen Stoykov <24619432+kstoykov@users.noreply.github.com> Date: Thu, 11 Jul 2024 15:31:19 +0300 Subject: [PATCH] receipts processing (#769) * receipts processing * restrict hacks to l1recovery only --- chain/chain_config.go | 7 ++ core/blockchain_zkevm.go | 89 +++++++++++-------- core/types/log_zkevm.go | 4 +- zk/constants/constants.go | 1 + zk/stages/stage_sequence_execute.go | 13 ++- zk/stages/stage_sequence_execute_blocks.go | 14 ++- .../stage_sequence_execute_injected_batch.go | 21 +++-- .../stage_sequence_execute_transactions.go | 21 ++--- zk/stages/stage_sequence_execute_utils.go | 5 +- 9 files changed, 104 insertions(+), 71 deletions(-) diff --git a/chain/chain_config.go b/chain/chain_config.go index 0452e6d96be..e692bac68d8 100644 --- a/chain/chain_config.go +++ b/chain/chain_config.go @@ -95,6 +95,7 @@ type Config struct { ForkID7EtrogBlock *big.Int `json:"forkID7EtrogBlock,omitempty"` ForkID88ElderberryBlock *big.Int `json:"forkID88ElderberryBlock,omitempty"` ForkID9Elderberry2Block *big.Int `json:"forkID9FeijoaBlock,omitempty"` + ForkID12BananaBlock *big.Int `json:"forkID12BananaBlock,omitempty"` SupportGasless bool `json:"supportGasless,omitempty"` } @@ -140,6 +141,8 @@ func (c *Config) SetForkIdBlock(forkIdNumber constants.ForkId, blockNum uint64) c.ForkID88ElderberryBlock = new(big.Int).SetUint64(blockNum) case constants.ForkID9Elderberry2: c.ForkID9Elderberry2Block = new(big.Int).SetUint64(blockNum) + case constants.ForkID12Banana: + c.ForkID12BananaBlock = new(big.Int).SetUint64(blockNum) default: return fmt.Errorf("unknown fork id number %d", forkIdNumber) } @@ -272,6 +275,10 @@ func (c *Config) IsForkId9Elderberry2(num uint64) bool { return isForked(c.ForkID9Elderberry2Block, num) } +func (c *Config) IsForkID12Banana(num uint64) bool { + return isForked(c.ForkID12BananaBlock, num) +} + // CheckCompatible checks whether scheduled fork transitions have been imported // with a mismatching chain configuration. func (c *Config) CheckCompatible(newcfg *Config, height uint64) *chain.ConfigCompatError { diff --git a/core/blockchain_zkevm.go b/core/blockchain_zkevm.go index 9b38da7eb6e..e211fcbdc2e 100644 --- a/core/blockchain_zkevm.go +++ b/core/blockchain_zkevm.go @@ -116,45 +116,8 @@ func ExecuteBlockEphemerallyZk( vmConfig.Tracer = nil } - //[hack]TODO: remove this after bug is fixed - localReceipt := receipt.Clone() - if !chainConfig.IsForkID8Elderberry(blockNum) && errors.Is(execResult.Err, vm.ErrUnsupportedPrecompile) { - localReceipt.Status = 1 - } - - // forkid8 the poststate is empty - // forkid8 also fixed the bugs with logs and cumulative gas used - if !chainConfig.IsForkID8Elderberry(blockNum) { - // the stateroot in the transactions that comes from the datastream - // is the one after smart contract writes so it can't be used - // but since pre forkid7 blocks have 1 tx only, we can use the block root - if chainConfig.IsForkID7Etrog(blockNum) { - // receipt root holds the intermediate stateroot after the tx - intermediateState, err := roHermezDb.GetIntermediateTxStateRoot(blockNum, tx.Hash()) - if err != nil { - return nil, err - } - receipt.PostState = intermediateState.Bytes() - } else { - receipt.PostState = header.Root.Bytes() - } - - //[hack] log0 pre forkid8 are not included in the rpc logs - // also pre forkid8 comulative gas used is same as gas used - var fixedLogs types.Logs - for _, l := range receipt.Logs { - if len(l.Topics) == 0 && len(l.Data) == 0 { - continue - } - fixedLogs = append(fixedLogs, l) - } - receipt.Logs = fixedLogs - receipt.CumulativeGasUsed = receipt.GasUsed - } - - for _, l := range receipt.Logs { - l.ApplyPaddingToLogsData(chainConfig.IsForkID8Elderberry(blockNum), chainConfig.IsForkId9Elderberry2(blockNum)) - } + localReceipt := CreateReceiptForBlockInfoTree(receipt, chainConfig, blockNum, execResult) + ProcessReceiptForBlockExecution(receipt, roHermezDb, chainConfig, blockNum, header, tx) if err != nil { if !vmConfig.StatelessExec { @@ -363,3 +326,51 @@ func FinalizeBlockExecutionWithHistoryWrite( return newBlock, newTxs, newReceipt, nil } + +func CreateReceiptForBlockInfoTree(receipt *types.Receipt, chainConfig *chain.Config, blockNum uint64, execResult *ExecutionResult) *types.Receipt { + // [hack]TODO: remove this after bug is fixed + localReceipt := receipt.Clone() + if !chainConfig.IsForkID8Elderberry(blockNum) && errors.Is(execResult.Err, vm.ErrUnsupportedPrecompile) { + localReceipt.Status = 1 + } + + return localReceipt +} + +func ProcessReceiptForBlockExecution(receipt *types.Receipt, roHermezDb state.ReadOnlyHermezDb, chainConfig *chain.Config, blockNum uint64, header *types.Header, tx types.Transaction) error { + // forkid8 the poststate is empty + // forkid8 also fixed the bugs with logs and cumulative gas used + if !chainConfig.IsForkID8Elderberry(blockNum) { + // the stateroot in the transactions that comes from the datastream + // is the one after smart contract writes so it can't be used + // but since pre forkid7 blocks have 1 tx only, we can use the block root + if chainConfig.IsForkID7Etrog(blockNum) { + // receipt root holds the intermediate stateroot after the tx + intermediateState, err := roHermezDb.GetIntermediateTxStateRoot(blockNum, tx.Hash()) + if err != nil { + return err + } + receipt.PostState = intermediateState.Bytes() + } else { + receipt.PostState = header.Root.Bytes() + } + + //[hack] log0 pre forkid8 are not included in the rpc logs + // also pre forkid8 comulative gas used is same as gas used + var fixedLogs types.Logs + for _, l := range receipt.Logs { + if len(l.Topics) == 0 && len(l.Data) == 0 { + continue + } + fixedLogs = append(fixedLogs, l) + } + receipt.Logs = fixedLogs + receipt.CumulativeGasUsed = receipt.GasUsed + } + + for _, l := range receipt.Logs { + l.ApplyPaddingToLogsData(chainConfig.IsForkID8Elderberry(blockNum), chainConfig.IsForkID12Banana(blockNum)) + } + + return nil +} diff --git a/core/types/log_zkevm.go b/core/types/log_zkevm.go index c2bd597da7f..9cd0111ad11 100644 --- a/core/types/log_zkevm.go +++ b/core/types/log_zkevm.go @@ -37,11 +37,11 @@ func (_this *Log) Clone() *Log { } } -func (_this *Log) ApplyPaddingToLogsData(isForkId8, isForkId9 bool) { +func (_this *Log) ApplyPaddingToLogsData(isForkId8, isForkId12 bool) { d := _this.Data mSize := len(d) - if isForkId8 || isForkId9 { + if isForkId8 && !isForkId12 { d = applyHexPadBug(d, mSize) } else { // [zkEvm] fill 0 at the end diff --git a/zk/constants/constants.go b/zk/constants/constants.go index 39e99154ac6..f564b02e3f1 100644 --- a/zk/constants/constants.go +++ b/zk/constants/constants.go @@ -11,4 +11,5 @@ const ( ForkID9Elderberry2 ForkId = 9 ForkID10 ForkId = 10 ForkID11 ForkId = 11 + ForkID12Banana ForkId = 12 ) diff --git a/zk/stages/stage_sequence_execute.go b/zk/stages/stage_sequence_execute.go index 336b6c43209..1e045e560b6 100644 --- a/zk/stages/stage_sequence_execute.go +++ b/zk/stages/stage_sequence_execute.go @@ -91,7 +91,7 @@ func SpawnSequencingStage( getHashFn := core.GetHashFn(header, getHeader) blockContext := core.NewEVMBlockContext(header, getHashFn, cfg.engine, &cfg.zk.AddressSequencer, parentBlock.ExcessDataGas()) - if err = processInjectedInitialBatch(ctx, cfg, s, sdb, forkId, header, parentBlock, &blockContext); err != nil { + if err = processInjectedInitialBatch(ctx, cfg, s, sdb, forkId, header, parentBlock, &blockContext, l1Recovery); err != nil { return err } @@ -118,6 +118,7 @@ func SpawnSequencingStage( var addedTransactions []types.Transaction var addedReceipts []*types.Receipt + var addedExecutionResults []*core.ExecutionResult var clonedBatchCounters *vm.BatchCounterCollector var decodedBlock zktx.DecodedBatchL2Data @@ -284,6 +285,7 @@ func SpawnSequencingStage( clonedBatchCounters = batchCounters.Clone() addedTransactions = []types.Transaction{} addedReceipts = []*types.Receipt{} + addedExecutionResults = []*core.ExecutionResult{} effectiveGases = []uint8{} header, parentBlock, err = prepareHeader(tx, blockNumber, deltaTimestamp, limboHeaderTimestamp, forkId, nextBatchData.Coinbase) if err != nil { @@ -401,6 +403,7 @@ func SpawnSequencingStage( } var receipt *types.Receipt + var execResult *core.ExecutionResult for i, transaction := range blockTransactions { var effectiveGas uint8 @@ -416,7 +419,7 @@ func SpawnSequencingStage( } if !batchDataOverflow { - receipt, overflow, err = attemptAddTransaction(cfg, sdb, ibs, batchCounters, &blockContext, header, transaction, effectiveGas, l1Recovery, forkId, l1InfoIndex) + receipt, execResult, 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") @@ -478,6 +481,7 @@ func SpawnSequencingStage( addedTransactions = append(addedTransactions, transaction) addedReceipts = append(addedReceipts, receipt) + addedExecutionResults = append(addedExecutionResults, execResult) effectiveGases = append(effectiveGases, effectiveGas) hasAnyTransactionsInThisBatch = true @@ -507,7 +511,7 @@ func SpawnSequencingStage( } else { for idx, transaction := range addedTransactions { effectiveGas := effectiveGases[idx] - receipt, innerOverflow, err := attemptAddTransaction(cfg, sdb, ibs, batchCounters, &blockContext, header, transaction, effectiveGas, false, forkId, l1InfoIndex) + receipt, execResult, innerOverflow, err := attemptAddTransaction(cfg, sdb, ibs, batchCounters, &blockContext, header, transaction, effectiveGas, false, forkId, l1InfoIndex) if err != nil { return err } @@ -516,6 +520,7 @@ func SpawnSequencingStage( panic(fmt.Sprintf("overflowed twice during execution while adding tx with index %d", idx)) } addedReceipts[idx] = receipt + addedExecutionResults[idx] = execResult } runLoopBlocks = false // close the batch because there are no counters left } @@ -524,7 +529,7 @@ func SpawnSequencingStage( return err } - block, err = doFinishBlockAndUpdateState(ctx, cfg, s, sdb, ibs, header, parentBlock, forkId, thisBatch, ger, l1BlockHash, addedTransactions, addedReceipts, effectiveGases, infoTreeIndexProgress) + block, err = doFinishBlockAndUpdateState(ctx, cfg, s, sdb, ibs, header, parentBlock, forkId, thisBatch, ger, l1BlockHash, addedTransactions, addedReceipts, addedExecutionResults, effectiveGases, infoTreeIndexProgress, l1Recovery) if err != nil { return err } diff --git a/zk/stages/stage_sequence_execute_blocks.go b/zk/stages/stage_sequence_execute_blocks.go index 36c358f2dc7..23637883d4f 100644 --- a/zk/stages/stage_sequence_execute_blocks.go +++ b/zk/stages/stage_sequence_execute_blocks.go @@ -20,8 +20,8 @@ import ( "github.com/ledgerwatch/erigon/zk/erigon_db" "github.com/ledgerwatch/erigon/zk/hermez_db" zktypes "github.com/ledgerwatch/erigon/zk/types" - "github.com/ledgerwatch/secp256k1" "github.com/ledgerwatch/erigon/zk/utils" + "github.com/ledgerwatch/secp256k1" ) func handleStateForNewBlockStarting( @@ -81,7 +81,9 @@ func finaliseBlock( l1BlockHash common.Hash, transactions []types.Transaction, receipts types.Receipts, + execResults []*core.ExecutionResult, effectiveGases []uint8, + l1Recovery bool, ) (*types.Block, error) { stateWriter := state.NewPlainStateWriter(sdb.tx, sdb.tx, newHeader.Number.Uint64()).SetAccumulator(accumulator) chainReader := stagedsync.ChainReader{ @@ -108,17 +110,25 @@ func finaliseBlock( return nil, err } } + localReceipt := core.CreateReceiptForBlockInfoTree(receipts[i], cfg.chainConfig, newHeader.Number.Uint64(), execResults[i]) txInfos = append(txInfos, blockinfo.ExecutedTxInfo{ Tx: tx, EffectiveGasPrice: effectiveGases[i], - Receipt: receipts[i], + Receipt: localReceipt, Signer: &from, }) } + if err := postBlockStateHandling(cfg, ibs, sdb.hermezDb, newHeader, ger, l1BlockHash, parentBlock.Root(), txInfos); err != nil { return nil, err } + if l1Recovery { + for i, receipt := range receipts { + core.ProcessReceiptForBlockExecution(receipt, sdb.hermezDb.HermezDbReader, cfg.chainConfig, newHeader.Number.Uint64(), newHeader, transactions[i]) + } + } + finalBlock, finalTransactions, finalReceipts, err := core.FinalizeBlockExecutionWithHistoryWrite( cfg.engine, sdb.stateReader, diff --git a/zk/stages/stage_sequence_execute_injected_batch.go b/zk/stages/stage_sequence_execute_injected_batch.go index bc55f5460e6..84addf1a529 100644 --- a/zk/stages/stage_sequence_execute_injected_batch.go +++ b/zk/stages/stage_sequence_execute_injected_batch.go @@ -5,6 +5,7 @@ import ( "errors" + "github.com/ledgerwatch/erigon/core" "github.com/ledgerwatch/erigon/core/state" "github.com/ledgerwatch/erigon/core/types" "github.com/ledgerwatch/erigon/core/vm" @@ -29,6 +30,7 @@ func processInjectedInitialBatch( header *types.Header, parentBlock *types.Block, blockContext *evmtypes.BlockContext, + l1Recovery bool, ) error { injected, err := sdb.hermezDb.GetL1InjectedBatch(0) if err != nil { @@ -61,16 +63,17 @@ func processInjectedInitialBatch( return err } - txn, receipt, effectiveGas, err := handleInjectedBatch(cfg, sdb, ibs, blockContext, injected, header, parentBlock, forkId) + txn, receipt, execResult, effectiveGas, err := handleInjectedBatch(cfg, sdb, ibs, blockContext, injected, header, parentBlock, forkId) if err != nil { return err } txns := types.Transactions{*txn} receipts := types.Receipts{receipt} + execResults := []*core.ExecutionResult{execResult} effectiveGases := []uint8{effectiveGas} - _, err = doFinishBlockAndUpdateState(ctx, cfg, s, sdb, ibs, header, parentBlock, forkId, injectedBatchNumber, injected.LastGlobalExitRoot, injected.L1ParentHash, txns, receipts, effectiveGases, 0) + _, err = doFinishBlockAndUpdateState(ctx, cfg, s, sdb, ibs, header, parentBlock, forkId, injectedBatchNumber, injected.LastGlobalExitRoot, injected.L1ParentHash, txns, receipts, execResults, effectiveGases, 0, l1Recovery) return err } @@ -83,26 +86,26 @@ func handleInjectedBatch( header *types.Header, parentBlock *types.Block, forkId uint64, -) (*types.Transaction, *types.Receipt, uint8, error) { +) (*types.Transaction, *types.Receipt, *core.ExecutionResult, uint8, error) { decodedBlocks, err := zktx.DecodeBatchL2Blocks(injected.Transaction, forkId) if err != nil { - return nil, nil, 0, err + return nil, nil, nil, 0, err } if len(decodedBlocks) == 0 || len(decodedBlocks) > 1 { - return nil, nil, 0, errors.New("expected 1 block for the injected batch") + return nil, nil, nil, 0, errors.New("expected 1 block for the injected batch") } if len(decodedBlocks[0].Transactions) == 0 { - return nil, nil, 0, errors.New("expected 1 transaction in the injected batch") + return nil, nil, nil, 0, errors.New("expected 1 transaction in the injected batch") } batchCounters := vm.NewBatchCounterCollector(sdb.smt.GetDepth(), uint16(forkId), cfg.zk.VirtualCountersSmtReduction, cfg.zk.ShouldCountersBeUnlimited(false), nil) // process the tx and we can ignore the counters as an overflow at this stage means no network anyway effectiveGas := DeriveEffectiveGasPrice(cfg, decodedBlocks[0].Transactions[0]) - receipt, _, err := attemptAddTransaction(cfg, sdb, ibs, batchCounters, blockContext, header, decodedBlocks[0].Transactions[0], effectiveGas, false, forkId, 0 /* use 0 for l1InfoIndex in injected batch */) + receipt, execResult, _, err := attemptAddTransaction(cfg, sdb, ibs, batchCounters, blockContext, header, decodedBlocks[0].Transactions[0], effectiveGas, false, forkId, 0 /* use 0 for l1InfoIndex in injected batch */) if err != nil { - return nil, nil, 0, err + return nil, nil, nil, 0, err } - return &decodedBlocks[0].Transactions[0], receipt, effectiveGas, nil + return &decodedBlocks[0].Transactions[0], receipt, execResult, effectiveGas, nil } diff --git a/zk/stages/stage_sequence_execute_transactions.go b/zk/stages/stage_sequence_execute_transactions.go index b3ad69b2745..4b2a264d3c4 100644 --- a/zk/stages/stage_sequence_execute_transactions.go +++ b/zk/stages/stage_sequence_execute_transactions.go @@ -11,8 +11,6 @@ import ( "bytes" "io" - "errors" - mapset "github.com/deckarep/golang-set/v2" "github.com/gateway-fm/cdk-erigon-lib/common/length" types2 "github.com/gateway-fm/cdk-erigon-lib/types" @@ -22,7 +20,6 @@ import ( "github.com/ledgerwatch/erigon/core/vm" "github.com/ledgerwatch/erigon/core/vm/evmtypes" "github.com/ledgerwatch/erigon/rlp" - "github.com/ledgerwatch/erigon/zk/constants" "github.com/ledgerwatch/erigon/zk/hermez_db" zktx "github.com/ledgerwatch/erigon/zk/tx" "github.com/ledgerwatch/erigon/zk/utils" @@ -185,14 +182,14 @@ func attemptAddTransaction( effectiveGasPrice uint8, l1Recovery bool, forkId, l1InfoIndex uint64, -) (*types.Receipt, bool, error) { +) (*types.Receipt, *core.ExecutionResult, bool, error) { txCounters := vm.NewTransactionCounter(transaction, sdb.smt.GetDepth(), uint16(forkId), cfg.zk.VirtualCountersSmtReduction, cfg.zk.ShouldCountersBeUnlimited(l1Recovery)) overflow, err := batchCounters.AddNewTransactionCounters(txCounters) if err != nil { - return nil, false, err + return nil, nil, false, err } if overflow && !l1Recovery { - return nil, true, nil + return nil, nil, true, nil } gasPool := new(core.GasPool).AddGas(transactionGasLimit) @@ -219,26 +216,22 @@ func attemptAddTransaction( ) if err != nil { - return nil, false, err - } - - if forkId <= uint64(constants.ForkID7Etrog) && errors.Is(execResult.Err, vm.ErrUnsupportedPrecompile) { - receipt.Status = 1 + return nil, nil, false, err } // we need to keep hold of the effective percentage used // todo [zkevm] for now we're hard coding to the max value but we need to calc this properly if err = sdb.hermezDb.WriteEffectiveGasPricePercentage(transaction.Hash(), effectiveGasPrice); err != nil { - return nil, false, err + return nil, nil, false, err } err = txCounters.ProcessTx(ibs, execResult.ReturnData) if err != nil { - return nil, false, err + return nil, nil, false, err } // now that we have executed we can check again for an overflow overflow, err = batchCounters.CheckForOverflow(l1InfoIndex != 0) - return receipt, overflow, err + return receipt, execResult, overflow, err } diff --git a/zk/stages/stage_sequence_execute_utils.go b/zk/stages/stage_sequence_execute_utils.go index 01222d39ddd..b9e6b880c58 100644 --- a/zk/stages/stage_sequence_execute_utils.go +++ b/zk/stages/stage_sequence_execute_utils.go @@ -18,6 +18,7 @@ import ( "github.com/ledgerwatch/erigon/chain" "github.com/ledgerwatch/erigon/common/math" "github.com/ledgerwatch/erigon/consensus" + "github.com/ledgerwatch/erigon/core" "github.com/ledgerwatch/erigon/core/rawdb" "github.com/ledgerwatch/erigon/core/state" "github.com/ledgerwatch/erigon/core/types" @@ -365,8 +366,10 @@ func doFinishBlockAndUpdateState( l1BlockHash common.Hash, transactions []types.Transaction, receipts types.Receipts, + execResults []*core.ExecutionResult, effectiveGases []uint8, l1InfoIndex uint64, + l1Recovery bool, ) (*types.Block, error) { thisBlockNumber := header.Number.Uint64() @@ -374,7 +377,7 @@ func doFinishBlockAndUpdateState( cfg.accumulator.StartChange(thisBlockNumber, header.Hash(), nil, false) } - block, err := finaliseBlock(ctx, cfg, s, sdb, ibs, header, parentBlock, forkId, thisBatch, cfg.accumulator, ger, l1BlockHash, transactions, receipts, effectiveGases) + block, err := finaliseBlock(ctx, cfg, s, sdb, ibs, header, parentBlock, forkId, thisBatch, cfg.accumulator, ger, l1BlockHash, transactions, receipts, execResults, effectiveGases, l1Recovery) if err != nil { return nil, err }