From eadf01626bef1d8719353ace8c2e297c26684922 Mon Sep 17 00:00:00 2001 From: Ardit Marku Date: Mon, 16 Dec 2024 15:00:01 +0200 Subject: [PATCH 1/2] Use current block data when constructing BlockContext for trace generation --- api/debug.go | 38 +++++++++++++++------------------ services/evm/executor.go | 45 ++++++++++++++++++++-------------------- 2 files changed, 39 insertions(+), 44 deletions(-) diff --git a/api/debug.go b/api/debug.go index cfeceb9a..bacec716 100644 --- a/api/debug.go +++ b/api/debug.go @@ -29,6 +29,8 @@ import ( "github.com/onflow/flow-evm-gateway/storage/pebble" flowEVM "github.com/onflow/flow-go/fvm/evm" + offchain "github.com/onflow/flow-go/fvm/evm/offchain/storage" + // this import is needed for side-effects, because the // tracers.DefaultDirectory is relying on the init function _ "github.com/onflow/go-ethereum/eth/tracers/js" @@ -302,15 +304,7 @@ func (d *DebugAPI) traceTransaction( return nil, err } - // We need to re-execute the given transaction and all the - // transactions that precede it in the same block, based on - // the previous block state, to generate the correct trace. - previousBlock, err := d.blocks.GetByHeight(block.Height - 1) - if err != nil { - return nil, err - } - - blockExecutor, err := d.executorAtBlock(previousBlock) + blockExecutor, err := d.executorAtBlock(block) if err != nil { return nil, err } @@ -383,14 +377,7 @@ func (d *DebugAPI) traceBlockByNumber( return results, nil } - // We need to re-execute all the transactions from the given block, - // on top of the previous block state, to generate the correct traces. - previousBlock, err := d.blocks.GetByHeight(block.Height - 1) - if err != nil { - return nil, err - } - - blockExecutor, err := d.executorAtBlock(previousBlock) + blockExecutor, err := d.executorAtBlock(block) if err != nil { return nil, err } @@ -424,19 +411,28 @@ func (d *DebugAPI) traceBlockByNumber( } func (d *DebugAPI) executorAtBlock(block *models.Block) (*evm.BlockExecutor, error) { - snapshot, err := d.registerStore.GetSnapshotAt(block.Height) + previousBlock, err := d.blocks.GetByHeight(block.Height - 1) + if err != nil { + return nil, err + } + + // We need to re-execute all the transactions from the given block, + // on top of the previous block state, to generate the correct traces. + snapshot, err := d.registerStore.GetSnapshotAt(previousBlock.Height) if err != nil { return nil, fmt.Errorf( "failed to get register snapshot at block height %d: %w", - block.Height, + previousBlock.Height, err, ) } - ledger := storage.NewRegisterDelta(snapshot) + + // create storage + state := offchain.NewEphemeralStorage(offchain.NewReadOnlyStorage(snapshot)) return evm.NewBlockExecutor( block, - ledger, + state, d.config.FlowNetworkID, d.blocks, d.receipts, diff --git a/services/evm/executor.go b/services/evm/executor.go index a4ce19de..ba011924 100644 --- a/services/evm/executor.go +++ b/services/evm/executor.go @@ -6,6 +6,7 @@ import ( "github.com/onflow/atree" "github.com/onflow/flow-go/fvm/evm" "github.com/onflow/flow-go/fvm/evm/emulator" + "github.com/onflow/flow-go/fvm/evm/offchain/blocks" "github.com/onflow/flow-go/fvm/evm/precompiles" "github.com/onflow/flow-go/fvm/evm/types" flowGo "github.com/onflow/flow-go/model/flow" @@ -98,6 +99,13 @@ func (s *BlockExecutor) Run( s.gasUsed += res.GasConsumed s.txIndex++ + if res.GasConsumed != receipt.GasUsed { + return fmt.Errorf( + "used gas mismatch, expected: %d, got: %d", + receipt.GasUsed, + res.GasConsumed, + ) + } l.Debug().Msg("transaction executed successfully") return nil @@ -110,25 +118,11 @@ func (s *BlockExecutor) blockContext( receipt *models.Receipt, tracer *tracers.Tracer, ) (types.BlockContext, error) { - ctx := types.BlockContext{ - ChainID: types.EVMChainIDFromFlowChainID(s.chainID), - BlockNumber: s.block.Height, - BlockTimestamp: s.block.Timestamp, - DirectCallBaseGasUsage: types.DefaultDirectCallBaseGasUsage, - DirectCallGasPrice: types.DefaultDirectCallGasPrice, - GasFeeCollector: types.CoinbaseAddress, - GetHashFunc: func(n uint64) common.Hash { - // For block heights greater than or equal to the current, - // return an empty block hash. - if n >= s.block.Height { - return common.Hash{} - } - // If the given block height, is more than 256 blocks - // in the past, return an empty block hash. - if s.block.Height-n > 256 { - return common.Hash{} - } - + ctx, err := blocks.NewBlockContext( + s.chainID, + s.block.Height, + s.block.Timestamp, + func(n uint64) common.Hash { block, err := s.blocks.GetByHeight(n) if err != nil { return common.Hash{} @@ -140,12 +134,17 @@ func (s *BlockExecutor) blockContext( return blockHash }, - Random: s.block.PrevRandao, - TxCountSoFar: s.txIndex, - TotalGasUsedSoFar: s.gasUsed, - Tracer: tracer, + s.block.PrevRandao, + tracer, + ) + + if err != nil { + return types.BlockContext{}, err } + ctx.TxCountSoFar = s.txIndex + ctx.TotalGasUsedSoFar = s.gasUsed + // only add precompile cadence arch contract if we have a receipt if receipt != nil { calls, err := types.AggregatedPrecompileCallsFromEncoded(receipt.PrecompiledCalls) From 59e4f2036eaec6e83b7aaaa0c73611ac16b9666a Mon Sep 17 00:00:00 2001 From: Ardit Marku Date: Mon, 16 Dec 2024 15:11:45 +0200 Subject: [PATCH 2/2] Improve logic for detecting the default tracer config --- api/debug.go | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/api/debug.go b/api/debug.go index bacec716..a225308e 100644 --- a/api/debug.go +++ b/api/debug.go @@ -5,7 +5,7 @@ import ( "errors" "fmt" "math/big" - "slices" + "strings" "github.com/goccy/go-json" "github.com/onflow/flow-go/fvm/evm/offchain/query" @@ -480,6 +480,6 @@ func isDefaultCallTracer(config *tracers.TraceConfig) bool { return false } - tracerConfig := json.RawMessage(replayer.TracerConfig) - return slices.Equal(config.TracerConfig, tracerConfig) + trimmedConfig := strings.ReplaceAll(string(config.TracerConfig), " ", "") + return trimmedConfig == replayer.TracerConfig }