Skip to content

Commit

Permalink
Support block overrides for eth_call & debug_traceCall endpoints
Browse files Browse the repository at this point in the history
  • Loading branch information
m-Peter committed Dec 3, 2024
1 parent 4b32ea8 commit a4f893f
Show file tree
Hide file tree
Showing 7 changed files with 245 additions and 19 deletions.
4 changes: 2 additions & 2 deletions api/api.go
Original file line number Diff line number Diff line change
Expand Up @@ -607,7 +607,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").
Expand Down Expand Up @@ -644,7 +644,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)
}
Expand Down
12 changes: 10 additions & 2 deletions api/debug.go
Original file line number Diff line number Diff line change
Expand Up @@ -291,11 +291,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(&ethTypes.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),
Expand Down
3 changes: 1 addition & 2 deletions bootstrap/bootstrap.go
Original file line number Diff line number Diff line change
Expand Up @@ -213,10 +213,9 @@ func (b *Bootstrap) StartAPIServer(ctx context.Context) error {
b.logger,
)

blocksProvider := replayer.NewBlocksProvider(
blocksProvider := requester.NewBlocksProvider(
b.storages.Blocks,
b.config.FlowNetworkID,
nil,
)

evm, err := requester.NewEVM(
Expand Down
108 changes: 108 additions & 0 deletions services/requester/blocks_provider.go
Original file line number Diff line number Diff line change
@@ -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
}
35 changes: 22 additions & 13 deletions services/requester/requester.go
Original file line number Diff line number Diff line change
Expand Up @@ -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"

Expand Down Expand Up @@ -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.
Expand Down Expand Up @@ -96,7 +96,7 @@ var _ Requester = &EVM{}

type EVM struct {
registerStore *pebble.RegisterStorage
blocksProvider *replayer.BlocksProvider
blocksProvider *BlocksProvider
client *CrossSporkClient
config config.Config
signer crypto.Signer
Expand All @@ -114,7 +114,7 @@ type EVM struct {

func NewEVM(
registerStore *pebble.RegisterStorage,
blocksProvider *replayer.BlocksProvider,
blocksProvider *BlocksProvider,
client *CrossSporkClient,
config config.Config,
signer crypto.Signer,
Expand Down Expand Up @@ -294,7 +294,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
}
Expand All @@ -306,7 +306,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
}
Expand All @@ -319,7 +319,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
}
Expand All @@ -332,8 +332,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
}
Expand Down Expand Up @@ -371,7 +372,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
}
Expand All @@ -396,7 +397,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.
Expand Down Expand Up @@ -426,7 +427,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
}
Expand All @@ -449,7 +450,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
}
Expand Down Expand Up @@ -508,7 +509,14 @@ func (e *EVM) getSignerNetworkInfo(ctx context.Context) (uint32, uint64, error)
)
}

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),
Expand Down Expand Up @@ -538,8 +546,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
}
Expand Down
4 changes: 4 additions & 0 deletions tests/e2e_web3js_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,10 @@ func TestWeb3_E2E(t *testing.T) {
runWeb3Test(t, "debug_util_test")
})

t.Run("test eth_call overrides", func(t *testing.T) {
runWeb3Test(t, "eth_call_overrides_test")
})

t.Run("test setup sanity check", func(t *testing.T) {
runWeb3Test(t, "setup_test")
})
Expand Down
Loading

0 comments on commit a4f893f

Please sign in to comment.