From 74c050ab41863ee107809f555e74c88ae4576dd1 Mon Sep 17 00:00:00 2001 From: Ardit Marku Date: Mon, 2 Dec 2024 17:50:27 +0200 Subject: [PATCH 1/5] Support block overrides for eth_call & debug_traceCall endpoints --- api/api.go | 4 +- api/debug.go | 12 +- bootstrap/bootstrap.go | 3 +- services/requester/blocks_provider.go | 108 +++++++++++ services/requester/requester.go | 54 +++--- tests/e2e_web3js_test.go | 4 + tests/web3js/contract_call_overrides_test.js | 191 +++++++++++++++++++ 7 files changed, 348 insertions(+), 28 deletions(-) create mode 100644 services/requester/blocks_provider.go create mode 100644 tests/web3js/contract_call_overrides_test.js diff --git a/api/api.go b/api/api.go index b3b7db62..5dd85ee9 100644 --- a/api/api.go +++ b/api/api.go @@ -539,7 +539,7 @@ func (b *BlockChainAPI) Call( args ethTypes.TransactionArgs, blockNumberOrHash *rpc.BlockNumberOrHash, stateOverrides *ethTypes.StateOverride, - _ *ethTypes.BlockOverrides, + blockOverrides *ethTypes.BlockOverrides, ) (hexutil.Bytes, error) { l := b.logger.With(). Str("endpoint", "call"). @@ -576,7 +576,7 @@ func (b *BlockChainAPI) Call( from = *args.From } - res, err := b.evm.Call(tx, from, height, stateOverrides) + res, err := b.evm.Call(tx, from, height, stateOverrides, blockOverrides) if err != nil { return handleError[hexutil.Bytes](err, l, b.collector) } diff --git a/api/debug.go b/api/debug.go index 6e22b0ea..c19f7687 100644 --- a/api/debug.go +++ b/api/debug.go @@ -175,11 +175,19 @@ func (d *DebugAPI) TraceCall( return nil, err } - blocksProvider := replayer.NewBlocksProvider( + blocksProvider := requester.NewBlocksProvider( d.blocks, d.config.FlowNetworkID, - tracer, ) + blocksProvider.SetTracer(tracer) + if config.BlockOverrides != nil { + blocksProvider.SetBlockOverrides(ðTypes.BlockOverrides{ + Number: config.BlockOverrides.Number, + Time: config.BlockOverrides.Time, + Coinbase: config.BlockOverrides.Coinbase, + Random: config.BlockOverrides.Random, + }) + } viewProvider := query.NewViewProvider( d.config.FlowNetworkID, flowEVM.StorageAccountAddress(d.config.FlowNetworkID), diff --git a/bootstrap/bootstrap.go b/bootstrap/bootstrap.go index c31f8409..223bea42 100644 --- a/bootstrap/bootstrap.go +++ b/bootstrap/bootstrap.go @@ -208,10 +208,9 @@ func (b *Bootstrap) StartAPIServer(ctx context.Context) error { b.config, ) - blocksProvider := replayer.NewBlocksProvider( + blocksProvider := requester.NewBlocksProvider( b.storages.Blocks, b.config.FlowNetworkID, - nil, ) accountKeys := make([]*requester.AccountKey, 0) diff --git a/services/requester/blocks_provider.go b/services/requester/blocks_provider.go new file mode 100644 index 00000000..48d3948b --- /dev/null +++ b/services/requester/blocks_provider.go @@ -0,0 +1,108 @@ +package requester + +import ( + ethTypes "github.com/onflow/flow-evm-gateway/eth/types" + "github.com/onflow/flow-evm-gateway/models" + "github.com/onflow/flow-evm-gateway/storage" + "github.com/onflow/flow-go/fvm/evm/offchain/blocks" + evmTypes "github.com/onflow/flow-go/fvm/evm/types" + flowGo "github.com/onflow/flow-go/model/flow" + gethCommon "github.com/onflow/go-ethereum/common" + "github.com/onflow/go-ethereum/eth/tracers" +) + +type blockSnapshot struct { + *BlocksProvider + block models.Block +} + +var _ evmTypes.BlockSnapshot = (*blockSnapshot)(nil) + +func (bs *blockSnapshot) BlockContext() (evmTypes.BlockContext, error) { + blockContext, err := blocks.NewBlockContext( + bs.chainID, + bs.block.Height, + bs.block.Timestamp, + func(n uint64) gethCommon.Hash { + block, err := bs.blocks.GetByHeight(n) + if err != nil { + return gethCommon.Hash{} + } + blockHash, err := block.Hash() + if err != nil { + return gethCommon.Hash{} + } + + return blockHash + }, + bs.block.PrevRandao, + bs.tracer, + ) + if err != nil { + return evmTypes.BlockContext{}, err + } + + if bs.blockOverrides == nil { + return blockContext, nil + } + + if bs.blockOverrides.Number != nil { + blockContext.BlockNumber = bs.blockOverrides.Number.ToInt().Uint64() + } + + if bs.blockOverrides.Time != nil { + blockContext.BlockTimestamp = uint64(*bs.blockOverrides.Time) + } + + if bs.blockOverrides.Random != nil { + blockContext.Random = *bs.blockOverrides.Random + } + + if bs.blockOverrides.Coinbase != nil { + blockContext.GasFeeCollector = evmTypes.NewAddress(*bs.blockOverrides.Coinbase) + } + + return blockContext, nil +} + +type BlocksProvider struct { + blocks storage.BlockIndexer + chainID flowGo.ChainID + tracer *tracers.Tracer + blockOverrides *ethTypes.BlockOverrides +} + +var _ evmTypes.BlockSnapshotProvider = (*BlocksProvider)(nil) + +func NewBlocksProvider( + blocks storage.BlockIndexer, + chainID flowGo.ChainID, +) *BlocksProvider { + return &BlocksProvider{ + blocks: blocks, + chainID: chainID, + } +} + +func (bp *BlocksProvider) SetTracer(tracer *tracers.Tracer) { + bp.tracer = tracer +} + +func (bp *BlocksProvider) SetBlockOverrides(blockOverrides *ethTypes.BlockOverrides) { + bp.blockOverrides = blockOverrides +} + +func (bp *BlocksProvider) GetSnapshotAt(height uint64) ( + evmTypes.BlockSnapshot, + error, +) { + block, err := bp.blocks.GetByHeight(height) + if err != nil { + return nil, err + } + + return &blockSnapshot{ + BlocksProvider: bp, + block: *block, + }, nil +} diff --git a/services/requester/requester.go b/services/requester/requester.go index 98a24298..d1ae7a23 100644 --- a/services/requester/requester.go +++ b/services/requester/requester.go @@ -27,7 +27,6 @@ import ( "github.com/onflow/flow-evm-gateway/metrics" "github.com/onflow/flow-evm-gateway/models" errs "github.com/onflow/flow-evm-gateway/models/errors" - "github.com/onflow/flow-evm-gateway/services/replayer" "github.com/onflow/flow-evm-gateway/storage" "github.com/onflow/flow-evm-gateway/storage/pebble" @@ -66,6 +65,7 @@ type Requester interface { from common.Address, height uint64, stateOverrides *ethTypes.StateOverride, + blockOverrides *ethTypes.BlockOverrides, ) ([]byte, error) // EstimateGas executes the given signed transaction data on the state for the given EVM block height. @@ -95,15 +95,16 @@ type Requester interface { var _ Requester = &EVM{} type EVM struct { - registerStore *pebble.RegisterStorage - blocksProvider *replayer.BlocksProvider - client *CrossSporkClient - config config.Config - txPool *TxPool - logger zerolog.Logger - blocks storage.BlockIndexer - mux sync.Mutex - keystore *KeyStore + registerStore *pebble.RegisterStorage + blocksProvider *BlocksProvider + client *CrossSporkClient + config config.Config + txPool *TxPool + logger zerolog.Logger + blocks storage.BlockIndexer + mux sync.Mutex + keystore *KeyStore + head *types.Header evmSigner types.Signer validationOptions *txpool.ValidationOptions @@ -112,7 +113,7 @@ type EVM struct { func NewEVM( registerStore *pebble.RegisterStorage, - blocksProvider *replayer.BlocksProvider, + blocksProvider *BlocksProvider, client *CrossSporkClient, config config.Config, logger zerolog.Logger, @@ -250,7 +251,7 @@ func (e *EVM) GetBalance( address common.Address, height uint64, ) (*big.Int, error) { - view, err := e.getBlockView(height) + view, err := e.getBlockView(height, nil) if err != nil { return nil, err } @@ -262,7 +263,7 @@ func (e *EVM) GetNonce( address common.Address, height uint64, ) (uint64, error) { - view, err := e.getBlockView(height) + view, err := e.getBlockView(height, nil) if err != nil { return 0, err } @@ -275,7 +276,7 @@ func (e *EVM) GetStorageAt( hash common.Hash, height uint64, ) (common.Hash, error) { - view, err := e.getBlockView(height) + view, err := e.getBlockView(height, nil) if err != nil { return common.Hash{}, err } @@ -288,8 +289,9 @@ func (e *EVM) Call( from common.Address, height uint64, stateOverrides *ethTypes.StateOverride, + blockOverrides *ethTypes.BlockOverrides, ) ([]byte, error) { - result, err := e.dryRunTx(tx, from, height, stateOverrides) + result, err := e.dryRunTx(tx, from, height, stateOverrides, blockOverrides) if err != nil { return nil, err } @@ -327,7 +329,7 @@ func (e *EVM) EstimateGas( tx.Gas = passingGasLimit // We first execute the transaction at the highest allowable gas limit, // since if this fails we can return the error immediately. - result, err := e.dryRunTx(tx, from, height, stateOverrides) + result, err := e.dryRunTx(tx, from, height, stateOverrides, nil) if err != nil { return 0, err } @@ -352,7 +354,7 @@ func (e *EVM) EstimateGas( optimisticGasLimit := (result.GasConsumed + result.GasRefund + gethParams.CallStipend) * 64 / 63 if optimisticGasLimit < passingGasLimit { tx.Gas = optimisticGasLimit - result, err = e.dryRunTx(tx, from, height, stateOverrides) + result, err = e.dryRunTx(tx, from, height, stateOverrides, nil) if err != nil { // This should not happen under normal conditions since if we make it this far the // transaction had run without error at least once before. @@ -382,7 +384,7 @@ func (e *EVM) EstimateGas( mid = failingGasLimit * 2 } tx.Gas = mid - result, err = e.dryRunTx(tx, from, height, stateOverrides) + result, err = e.dryRunTx(tx, from, height, stateOverrides, nil) if err != nil { return 0, err } @@ -405,7 +407,7 @@ func (e *EVM) GetCode( address common.Address, height uint64, ) ([]byte, error) { - view, err := e.getBlockView(height) + view, err := e.getBlockView(height, nil) if err != nil { return nil, err } @@ -437,7 +439,14 @@ func (e *EVM) GetLatestEVMHeight(ctx context.Context) (uint64, error) { return height, nil } -func (e *EVM) getBlockView(height uint64) (*query.View, error) { +func (e *EVM) getBlockView( + height uint64, + blockOverrides *ethTypes.BlockOverrides, +) (*query.View, error) { + if blockOverrides != nil { + e.blocksProvider.SetBlockOverrides(blockOverrides) + } + viewProvider := query.NewViewProvider( e.config.FlowNetworkID, evm.StorageAccountAddress(e.config.FlowNetworkID), @@ -467,8 +476,9 @@ func (e *EVM) dryRunTx( from common.Address, height uint64, stateOverrides *ethTypes.StateOverride, + blockOverrides *ethTypes.BlockOverrides, ) (*evmTypes.Result, error) { - view, err := e.getBlockView(height) + view, err := e.getBlockView(height, blockOverrides) if err != nil { return nil, err } @@ -592,7 +602,7 @@ func (e *EVM) validateTransactionWithState( if err != nil { return err } - view, err := e.getBlockView(height) + view, err := e.getBlockView(height, nil) if err != nil { return err } diff --git a/tests/e2e_web3js_test.go b/tests/e2e_web3js_test.go index 96ba5d9e..3ca77ed8 100644 --- a/tests/e2e_web3js_test.go +++ b/tests/e2e_web3js_test.go @@ -40,6 +40,10 @@ func TestWeb3_E2E(t *testing.T) { runWeb3Test(t, "debug_util_test") }) + t.Run("test contract call overrides", func(t *testing.T) { + runWeb3Test(t, "contract_call_overrides_test") + }) + t.Run("test setup sanity check", func(t *testing.T) { runWeb3Test(t, "setup_test") }) diff --git a/tests/web3js/contract_call_overrides_test.js b/tests/web3js/contract_call_overrides_test.js new file mode 100644 index 00000000..fde3837d --- /dev/null +++ b/tests/web3js/contract_call_overrides_test.js @@ -0,0 +1,191 @@ +const { assert } = require('chai') +const conf = require('./config') +const helpers = require('./helpers') +const web3 = conf.web3 + +let deployed = null +let contractAddress = null + +before(async () => { + deployed = await helpers.deployContract('storage') + contractAddress = deployed.receipt.contractAddress + + assert.equal(deployed.receipt.status, conf.successStatus) +}) + +it('should apply block overrides on eth_call', async () => { + assert.equal(deployed.receipt.status, conf.successStatus) + + let receipt = await web3.eth.getTransactionReceipt(deployed.receipt.transactionHash) + assert.equal(receipt.contractAddress, contractAddress) + + let latestBlockNumber = await web3.eth.getBlockNumber() + + // Check the `block.number` value, without overrides + let blockNumberSelector = deployed.contract.methods.blockNumber().encodeABI() + let call = { + from: conf.eoa.address, + to: contractAddress, + gas: '0x75ab', + gasPrice: web3.utils.toHex(conf.minGasPrice), + value: '0x0', + data: blockNumberSelector, + } + + let response = await helpers.callRPCMethod( + 'eth_call', + [call, 'latest', null, null] + ) + assert.equal(response.status, 200) + assert.isDefined(response.body) + assert.equal(web3.utils.hexToNumber(response.body.result), latestBlockNumber) + + // Override the `block.number` value to `2`. + response = await helpers.callRPCMethod( + 'eth_call', + [call, 'latest', null, { number: '0x2' }] + ) + assert.equal(response.status, 200) + assert.isDefined(response.body) + assert.equal(web3.utils.hexToNumber(response.body.result), 2n) + + // Check the `block.timestamp` value, without overrides + let block = await web3.eth.getBlock(latestBlockNumber) + let blockTimeSelector = deployed.contract.methods.blockTime().encodeABI() + call.data = blockTimeSelector + + response = await helpers.callRPCMethod( + 'eth_call', + [call, 'latest', null, null] + ) + assert.equal(response.status, 200) + assert.isDefined(response.body) + assert.equal(web3.utils.hexToNumber(response.body.result), block.timestamp) + + // Override the `block.timestamp` value to `0x674DB1E1`. + response = await helpers.callRPCMethod( + 'eth_call', + [call, 'latest', null, { time: '0x674DB1E1' }] + ) + assert.equal(response.status, 200) + assert.isDefined(response.body) + assert.equal(web3.utils.hexToNumber(response.body.result), 1733145057n) + + // Check the `block.prevrandao` value, without overrides + let randomSelector = deployed.contract.methods.random().encodeABI() + call.data = randomSelector + + response = await helpers.callRPCMethod( + 'eth_call', + [call, 'latest', null, null] + ) + assert.equal(response.status, 200) + assert.isDefined(response.body) + let currentPrevRandao = web3.utils.hexToNumber(response.body.result) + + // Override the `block.prevrandao` value to `0x7914bb5b13bac6f621bc37bbf6e406fbf4472aaaaf17ec2f309a92aca4e27fc0`. + let random = '0x7914bb5b13bac6f621bc37bbf6e406fbf4472aaaaf17ec2f309a92aca4e27fc0' + response = await helpers.callRPCMethod( + 'eth_call', + [call, 'latest', null, { random: random }] + ) + assert.equal(response.status, 200) + assert.isDefined(response.body) + assert.equal(response.body.result, random) + assert.notEqual(web3.utils.hexToNumber(response.body.result), currentPrevRandao) +}) + +it('should apply block overrides on debug_traceCall', async () => { + assert.equal(deployed.receipt.status, conf.successStatus) + + let receipt = await web3.eth.getTransactionReceipt(deployed.receipt.transactionHash) + assert.equal(receipt.contractAddress, contractAddress) + + let callTracer = { + tracer: 'callTracer', + tracerConfig: { + withLog: false, + onlyTopCall: true + } + } + + let latestBlockNumber = await web3.eth.getBlockNumber() + + // Check the `block.number` value, without overrides + let blockNumberSelector = deployed.contract.methods.blockNumber().encodeABI() + let call = { + from: conf.eoa.address, + to: contractAddress, + gas: '0x75ab', + gasPrice: web3.utils.toHex(conf.minGasPrice), + value: '0x0', + data: blockNumberSelector, + } + + let response = await helpers.callRPCMethod( + 'debug_traceCall', + [call, 'latest', callTracer] + ) + assert.equal(response.status, 200) + assert.isDefined(response.body) + assert.equal(web3.utils.hexToNumber(response.body.result.output), latestBlockNumber) + + // Override the `block.number` value to `2`. + callTracer.blockOverrides = { number: '0x2' } + response = await helpers.callRPCMethod( + 'debug_traceCall', + [call, 'latest', callTracer] + ) + assert.equal(response.status, 200) + assert.isDefined(response.body) + assert.equal(web3.utils.hexToNumber(response.body.result.output), 2n) + + // Check the `block.timestamp` value, without overrides + let block = await web3.eth.getBlock(latestBlockNumber) + let blockTimeSelector = deployed.contract.methods.blockTime().encodeABI() + call.data = blockTimeSelector + + callTracer.blockOverrides = {} + response = await helpers.callRPCMethod( + 'debug_traceCall', + [call, 'latest', callTracer] + ) + assert.equal(response.status, 200) + assert.isDefined(response.body) + assert.equal(web3.utils.hexToNumber(response.body.result.output), block.timestamp) + + // Override the `block.timestamp` value to `0x674DB1E1`. + callTracer.blockOverrides = { time: '0x674DB1E1' } + response = await helpers.callRPCMethod( + 'debug_traceCall', + [call, 'latest', callTracer] + ) + assert.equal(response.status, 200) + assert.isDefined(response.body) + assert.equal(web3.utils.hexToNumber(response.body.result.output), 1733145057n) + + // Check the `block.prevrandao` value, without overrides + let randomSelector = deployed.contract.methods.random().encodeABI() + call.data = randomSelector + + callTracer.blockOverrides = {} + response = await helpers.callRPCMethod( + 'debug_traceCall', + [call, 'latest', callTracer] + ) + assert.equal(response.status, 200) + assert.isDefined(response.body) + let currentPrevRandao = web3.utils.hexToNumber(response.body.result.output) + + // Override the `block.prevrandao` value to `0x7914bb5b13bac6f621bc37bbf6e406fbf4472aaaaf17ec2f309a92aca4e27fc0`. + let random = '0x7914bb5b13bac6f621bc37bbf6e406fbf4472aaaaf17ec2f309a92aca4e27fc0' + callTracer.blockOverrides = { random: random } + response = await helpers.callRPCMethod( + 'debug_traceCall', + [call, 'latest', callTracer] + ) + assert.equal(response.status, 200) + assert.isDefined(response.body) + assert.equal(response.body.result.output, random) + assert.notEqual(web3.utils.hexToNumber(response.body.result.output), currentPrevRandao) +}) From cf5aaf2d74396a1ef9e5ad502828a03963aae361 Mon Sep 17 00:00:00 2001 From: Ardit Marku Date: Wed, 8 Jan 2025 16:59:33 +0200 Subject: [PATCH 2/5] Update API of requester.BlocksProvider type to avoid concurrency issues --- api/debug.go | 5 ++--- bootstrap/bootstrap.go | 6 ------ services/replayer/blocks_provider.go | 7 +++++++ services/requester/blocks_provider.go | 24 ++++++++++++++++++++---- services/requester/requester.go | 25 ++++++++++++------------- 5 files changed, 41 insertions(+), 26 deletions(-) diff --git a/api/debug.go b/api/debug.go index c19f7687..113f99e9 100644 --- a/api/debug.go +++ b/api/debug.go @@ -178,10 +178,9 @@ func (d *DebugAPI) TraceCall( blocksProvider := requester.NewBlocksProvider( d.blocks, d.config.FlowNetworkID, - ) - blocksProvider.SetTracer(tracer) + ).WithTracer(tracer) if config.BlockOverrides != nil { - blocksProvider.SetBlockOverrides(ðTypes.BlockOverrides{ + blocksProvider = blocksProvider.WithBlockOverrides(ðTypes.BlockOverrides{ Number: config.BlockOverrides.Number, Time: config.BlockOverrides.Time, Coinbase: config.BlockOverrides.Coinbase, diff --git a/bootstrap/bootstrap.go b/bootstrap/bootstrap.go index 223bea42..78a57083 100644 --- a/bootstrap/bootstrap.go +++ b/bootstrap/bootstrap.go @@ -208,11 +208,6 @@ func (b *Bootstrap) StartAPIServer(ctx context.Context) error { b.config, ) - blocksProvider := requester.NewBlocksProvider( - b.storages.Blocks, - b.config.FlowNetworkID, - ) - accountKeys := make([]*requester.AccountKey, 0) if !b.config.IndexOnly { account, err := b.client.GetAccount(ctx, b.config.COAAddress) @@ -245,7 +240,6 @@ func (b *Bootstrap) StartAPIServer(ctx context.Context) error { evm, err := requester.NewEVM( b.storages.Registers, - blocksProvider, b.client, b.config, b.logger, diff --git a/services/replayer/blocks_provider.go b/services/replayer/blocks_provider.go index 9cb2dfbe..ab1ed0b5 100644 --- a/services/replayer/blocks_provider.go +++ b/services/replayer/blocks_provider.go @@ -41,6 +41,13 @@ func (bs *blockSnapshot) BlockContext() (evmTypes.BlockContext, error) { ) } +// This BlocksProvider implementation is used in the EVM events ingestion pipeline. +// The ingestion module notifies the BlocksProvider of incoming EVM blocks, by +// calling the `OnBlockReceived` method. This method guarantees that blocks are +// processed sequentially, and keeps track of the latest block, which is used +// for generating the proper `BlockContext`. This is necessary for replaying +// EVM blocks/transactions locally, and verifying that there are no state +// mismatches. type BlocksProvider struct { blocks storage.BlockIndexer chainID flowGo.ChainID diff --git a/services/requester/blocks_provider.go b/services/requester/blocks_provider.go index 48d3948b..e465e034 100644 --- a/services/requester/blocks_provider.go +++ b/services/requester/blocks_provider.go @@ -65,6 +65,10 @@ func (bs *blockSnapshot) BlockContext() (evmTypes.BlockContext, error) { return blockContext, nil } +// This BlocksProvider implementation is only used for the `eth_call` & +// `debug_traceCall` JSON-RPC endpoints. It accepts optional `Tracer` & +// `BlockOverrides` objects, which are used when constructing the +// `BlockContext` object. type BlocksProvider struct { blocks storage.BlockIndexer chainID flowGo.ChainID @@ -84,12 +88,24 @@ func NewBlocksProvider( } } -func (bp *BlocksProvider) SetTracer(tracer *tracers.Tracer) { - bp.tracer = tracer +func (bp *BlocksProvider) WithTracer(tracer *tracers.Tracer) *BlocksProvider { + return &BlocksProvider{ + blocks: bp.blocks, + chainID: bp.chainID, + tracer: tracer, + blockOverrides: bp.blockOverrides, + } } -func (bp *BlocksProvider) SetBlockOverrides(blockOverrides *ethTypes.BlockOverrides) { - bp.blockOverrides = blockOverrides +func (bp *BlocksProvider) WithBlockOverrides( + blockOverrides *ethTypes.BlockOverrides, +) *BlocksProvider { + return &BlocksProvider{ + blocks: bp.blocks, + chainID: bp.chainID, + tracer: bp.tracer, + blockOverrides: blockOverrides, + } } func (bp *BlocksProvider) GetSnapshotAt(height uint64) ( diff --git a/services/requester/requester.go b/services/requester/requester.go index d1ae7a23..78ddacc7 100644 --- a/services/requester/requester.go +++ b/services/requester/requester.go @@ -95,15 +95,14 @@ type Requester interface { var _ Requester = &EVM{} type EVM struct { - registerStore *pebble.RegisterStorage - blocksProvider *BlocksProvider - client *CrossSporkClient - config config.Config - txPool *TxPool - logger zerolog.Logger - blocks storage.BlockIndexer - mux sync.Mutex - keystore *KeyStore + registerStore *pebble.RegisterStorage + client *CrossSporkClient + config config.Config + txPool *TxPool + logger zerolog.Logger + blocks storage.BlockIndexer + mux sync.Mutex + keystore *KeyStore head *types.Header evmSigner types.Signer @@ -113,7 +112,6 @@ type EVM struct { func NewEVM( registerStore *pebble.RegisterStorage, - blocksProvider *BlocksProvider, client *CrossSporkClient, config config.Config, logger zerolog.Logger, @@ -168,7 +166,6 @@ func NewEVM( evm := &EVM{ registerStore: registerStore, - blocksProvider: blocksProvider, client: client, config: config, logger: logger, @@ -443,15 +440,17 @@ func (e *EVM) getBlockView( height uint64, blockOverrides *ethTypes.BlockOverrides, ) (*query.View, error) { + blocksProvider := NewBlocksProvider(e.blocks, e.config.FlowNetworkID) + if blockOverrides != nil { - e.blocksProvider.SetBlockOverrides(blockOverrides) + blocksProvider = blocksProvider.WithBlockOverrides(blockOverrides) } viewProvider := query.NewViewProvider( e.config.FlowNetworkID, evm.StorageAccountAddress(e.config.FlowNetworkID), e.registerStore, - e.blocksProvider, + blocksProvider, blockGasLimit, ) From c34aeccf9a05e970dbd9828dd126d1b77829b044 Mon Sep 17 00:00:00 2001 From: Ardit Marku Date: Fri, 10 Jan 2025 12:16:06 +0200 Subject: [PATCH 3/5] Rename requester implementation of BlocksProvider to OverridableBlocksProvider --- api/debug.go | 2 +- ...ider.go => overridable_blocks_provider.go} | 30 +++++++++---------- services/requester/requester.go | 2 +- 3 files changed, 17 insertions(+), 17 deletions(-) rename services/requester/{blocks_provider.go => overridable_blocks_provider.go} (77%) diff --git a/api/debug.go b/api/debug.go index 113f99e9..50876717 100644 --- a/api/debug.go +++ b/api/debug.go @@ -175,7 +175,7 @@ func (d *DebugAPI) TraceCall( return nil, err } - blocksProvider := requester.NewBlocksProvider( + blocksProvider := requester.NewOverridableBlocksProvider( d.blocks, d.config.FlowNetworkID, ).WithTracer(tracer) diff --git a/services/requester/blocks_provider.go b/services/requester/overridable_blocks_provider.go similarity index 77% rename from services/requester/blocks_provider.go rename to services/requester/overridable_blocks_provider.go index e465e034..1c7582a7 100644 --- a/services/requester/blocks_provider.go +++ b/services/requester/overridable_blocks_provider.go @@ -12,7 +12,7 @@ import ( ) type blockSnapshot struct { - *BlocksProvider + *OverridableBlocksProvider block models.Block } @@ -65,31 +65,31 @@ func (bs *blockSnapshot) BlockContext() (evmTypes.BlockContext, error) { return blockContext, nil } -// This BlocksProvider implementation is only used for the `eth_call` & +// This OverridableBlocksProvider implementation is only used for the `eth_call` & // `debug_traceCall` JSON-RPC endpoints. It accepts optional `Tracer` & // `BlockOverrides` objects, which are used when constructing the // `BlockContext` object. -type BlocksProvider struct { +type OverridableBlocksProvider struct { blocks storage.BlockIndexer chainID flowGo.ChainID tracer *tracers.Tracer blockOverrides *ethTypes.BlockOverrides } -var _ evmTypes.BlockSnapshotProvider = (*BlocksProvider)(nil) +var _ evmTypes.BlockSnapshotProvider = (*OverridableBlocksProvider)(nil) -func NewBlocksProvider( +func NewOverridableBlocksProvider( blocks storage.BlockIndexer, chainID flowGo.ChainID, -) *BlocksProvider { - return &BlocksProvider{ +) *OverridableBlocksProvider { + return &OverridableBlocksProvider{ blocks: blocks, chainID: chainID, } } -func (bp *BlocksProvider) WithTracer(tracer *tracers.Tracer) *BlocksProvider { - return &BlocksProvider{ +func (bp *OverridableBlocksProvider) WithTracer(tracer *tracers.Tracer) *OverridableBlocksProvider { + return &OverridableBlocksProvider{ blocks: bp.blocks, chainID: bp.chainID, tracer: tracer, @@ -97,10 +97,10 @@ func (bp *BlocksProvider) WithTracer(tracer *tracers.Tracer) *BlocksProvider { } } -func (bp *BlocksProvider) WithBlockOverrides( +func (bp *OverridableBlocksProvider) WithBlockOverrides( blockOverrides *ethTypes.BlockOverrides, -) *BlocksProvider { - return &BlocksProvider{ +) *OverridableBlocksProvider { + return &OverridableBlocksProvider{ blocks: bp.blocks, chainID: bp.chainID, tracer: bp.tracer, @@ -108,7 +108,7 @@ func (bp *BlocksProvider) WithBlockOverrides( } } -func (bp *BlocksProvider) GetSnapshotAt(height uint64) ( +func (bp *OverridableBlocksProvider) GetSnapshotAt(height uint64) ( evmTypes.BlockSnapshot, error, ) { @@ -118,7 +118,7 @@ func (bp *BlocksProvider) GetSnapshotAt(height uint64) ( } return &blockSnapshot{ - BlocksProvider: bp, - block: *block, + OverridableBlocksProvider: bp, + block: *block, }, nil } diff --git a/services/requester/requester.go b/services/requester/requester.go index 78ddacc7..9ec3abc4 100644 --- a/services/requester/requester.go +++ b/services/requester/requester.go @@ -440,7 +440,7 @@ func (e *EVM) getBlockView( height uint64, blockOverrides *ethTypes.BlockOverrides, ) (*query.View, error) { - blocksProvider := NewBlocksProvider(e.blocks, e.config.FlowNetworkID) + blocksProvider := NewOverridableBlocksProvider(e.blocks, e.config.FlowNetworkID) if blockOverrides != nil { blocksProvider = blocksProvider.WithBlockOverrides(blockOverrides) From a3f62ee8ac2f8f0970b27f0f779b6542dfa4312d Mon Sep 17 00:00:00 2001 From: Ardit Marku Date: Mon, 13 Jan 2025 10:20:30 +0200 Subject: [PATCH 4/5] Accept optional tracer in NewOverridableBlocksProvider constructor method --- api/debug.go | 4 +++- services/requester/overridable_blocks_provider.go | 11 ++--------- services/requester/requester.go | 6 +++++- 3 files changed, 10 insertions(+), 11 deletions(-) diff --git a/api/debug.go b/api/debug.go index 50876717..e2551344 100644 --- a/api/debug.go +++ b/api/debug.go @@ -178,7 +178,9 @@ func (d *DebugAPI) TraceCall( blocksProvider := requester.NewOverridableBlocksProvider( d.blocks, d.config.FlowNetworkID, - ).WithTracer(tracer) + tracer, + ) + if config.BlockOverrides != nil { blocksProvider = blocksProvider.WithBlockOverrides(ðTypes.BlockOverrides{ Number: config.BlockOverrides.Number, diff --git a/services/requester/overridable_blocks_provider.go b/services/requester/overridable_blocks_provider.go index 1c7582a7..3976aa8d 100644 --- a/services/requester/overridable_blocks_provider.go +++ b/services/requester/overridable_blocks_provider.go @@ -81,19 +81,12 @@ var _ evmTypes.BlockSnapshotProvider = (*OverridableBlocksProvider)(nil) func NewOverridableBlocksProvider( blocks storage.BlockIndexer, chainID flowGo.ChainID, + tracer *tracers.Tracer, ) *OverridableBlocksProvider { return &OverridableBlocksProvider{ blocks: blocks, chainID: chainID, - } -} - -func (bp *OverridableBlocksProvider) WithTracer(tracer *tracers.Tracer) *OverridableBlocksProvider { - return &OverridableBlocksProvider{ - blocks: bp.blocks, - chainID: bp.chainID, - tracer: tracer, - blockOverrides: bp.blockOverrides, + tracer: tracer, } } diff --git a/services/requester/requester.go b/services/requester/requester.go index 9ec3abc4..bb07ef55 100644 --- a/services/requester/requester.go +++ b/services/requester/requester.go @@ -440,7 +440,11 @@ func (e *EVM) getBlockView( height uint64, blockOverrides *ethTypes.BlockOverrides, ) (*query.View, error) { - blocksProvider := NewOverridableBlocksProvider(e.blocks, e.config.FlowNetworkID) + blocksProvider := NewOverridableBlocksProvider( + e.blocks, + e.config.FlowNetworkID, + nil, + ) if blockOverrides != nil { blocksProvider = blocksProvider.WithBlockOverrides(blockOverrides) From adee2f306dca9dc331a61f4d51c7b62d372422d3 Mon Sep 17 00:00:00 2001 From: Ardit Marku Date: Tue, 14 Jan 2025 10:22:37 +0200 Subject: [PATCH 5/5] Fix linting issue --- bootstrap/bootstrap_test.go | 1 - 1 file changed, 1 deletion(-) diff --git a/bootstrap/bootstrap_test.go b/bootstrap/bootstrap_test.go index 350f630d..73be33a3 100644 --- a/bootstrap/bootstrap_test.go +++ b/bootstrap/bootstrap_test.go @@ -56,7 +56,6 @@ func TestRetryInterceptor(t *testing.T) { } for _, tc := range testCases { - tc := tc t.Run(tc.name, func(t *testing.T) { t.Parallel()