From c3f149425a77e8bc9e16cfa952ae686a769f063d Mon Sep 17 00:00:00 2001 From: Sina Mahmoodi Date: Tue, 6 Jun 2023 11:16:37 +0200 Subject: [PATCH] add ecrecover override --- internal/ethapi/api.go | 19 ++++++++----- internal/ethapi/api_test.go | 55 +++++++++++++++++++++++++++++++++++++ 2 files changed, 67 insertions(+), 7 deletions(-) diff --git a/internal/ethapi/api.go b/internal/ethapi/api.go index 1abca0aef9..9a74777bad 100644 --- a/internal/ethapi/api.go +++ b/internal/ethapi/api.go @@ -1012,16 +1012,16 @@ func DoCall(ctx context.Context, b Backend, args TransactionArgs, blockNrOrHash // Make sure the context is cancelled when the call has completed // this makes sure resources are cleaned up. defer cancel() - return doCall(ctx, b, args, state, header, timeout, new(core.GasPool).AddGas(globalGasCap), &blockCtx) + return doCall(ctx, b, args, state, header, timeout, new(core.GasPool).AddGas(globalGasCap), &blockCtx, &vm.Config{NoBaseFee: true}) } -func doCall(ctx context.Context, b Backend, args TransactionArgs, state *state.StateDB, header *types.Header, timeout time.Duration, gp *core.GasPool, blockContext *vm.BlockContext) (*core.ExecutionResult, error) { +func doCall(ctx context.Context, b Backend, args TransactionArgs, state *state.StateDB, header *types.Header, timeout time.Duration, gp *core.GasPool, blockContext *vm.BlockContext, vmConfig *vm.Config) (*core.ExecutionResult, error) { // Get a new instance of the EVM. msg, err := args.ToMessage(gp.Gas(), header.BaseFee) if err != nil { return nil, err } - evm, vmError := b.GetEVM(ctx, msg, state, header, &vm.Config{NoBaseFee: true}, blockContext) + evm, vmError := b.GetEVM(ctx, msg, state, header, vmConfig, blockContext) // Wait for the context to be done and cancel the evm. Even if the // EVM has finished, cancelling may be done (repeatedly) @@ -1096,9 +1096,10 @@ func (s *BlockChainAPI) Call(ctx context.Context, args TransactionArgs, blockNrO // CallBatch is a batch of calls to be simulated sequentially. type CallBatch struct { - BlockOverrides *BlockOverrides - StateOverrides *StateOverride - Calls []TransactionArgs + BlockOverrides *BlockOverrides + StateOverrides *StateOverride + ECRecoverOverride *hexutil.Bytes // Override bytecode for ecrecover precompile. + Calls []TransactionArgs } type callResult struct { @@ -1146,6 +1147,10 @@ func (s *BlockChainAPI) Multicall(ctx context.Context, blocks []CallBatch, block if err := block.StateOverrides.Apply(state); err != nil { return nil, err } + // ECRecover replacement code will be fetched from statedb and executed as a normal EVM bytecode. + if block.ECRecoverOverride != nil { + state.SetCode(common.BytesToAddress([]byte{1}), *block.ECRecoverOverride) + } blockContext := core.NewEVMBlockContext(header, NewChainContext(ctx, s.b), nil) if block.BlockOverrides != nil { block.BlockOverrides.Apply(&blockContext) @@ -1155,7 +1160,7 @@ func (s *BlockChainAPI) Multicall(ctx context.Context, blocks []CallBatch, block // Hack to get logs from statedb which stores logs by txhash. txhash := common.BigToHash(big.NewInt(int64(i))) state.SetTxContext(txhash, i) - result, err := doCall(ctx, s.b, call, state, header, timeout, gp, &blockContext) + result, err := doCall(ctx, s.b, call, state, header, timeout, gp, &blockContext, &vm.Config{NoBaseFee: true, DisableECRecover: true}) if err != nil { return nil, err } diff --git a/internal/ethapi/api_test.go b/internal/ethapi/api_test.go index 57de5c6ab4..17d3ce1e14 100644 --- a/internal/ethapi/api_test.go +++ b/internal/ethapi/api_test.go @@ -26,6 +26,7 @@ import ( "math/big" "reflect" "sort" + "strings" "testing" "time" @@ -776,6 +777,60 @@ func TestMulticall(t *testing.T) { GasUsed: "0x5508", }}}, }, + // Test ecrecover override + { + tag: latest, + blocks: []CallBatch{{ + StateOverrides: &StateOverride{ + randomAccounts[2].addr: OverrideAccount{ + // Yul code that returns ecrecover(0, 0, 0, 0). + // object "Test" { + // code { + // // Free memory pointer + // let free_ptr := mload(0x40) + // + // // Initialize inputs with zeros + // mstore(free_ptr, 0) // Hash + // mstore(add(free_ptr, 0x20), 0) // v + // mstore(add(free_ptr, 0x40), 0) // r + // mstore(add(free_ptr, 0x60), 0) // s + // + // // Call ecrecover precompile (at address 1) with all 0 inputs + // let success := staticcall(gas(), 1, free_ptr, 0x80, free_ptr, 0x20) + // + // // Check if the call was successful + // if eq(success, 0) { + // revert(0, 0) + // } + // + // // Return the recovered address + // return(free_ptr, 0x14) + // } + // } + Code: hex2Bytes("6040516000815260006020820152600060408201526000606082015260208160808360015afa60008103603157600080fd5b601482f3"), + }, + }, + BlockOverrides: &BlockOverrides{}, + // Yul code that returns the address of the caller. + // object "Test" { + // code { + // let c := caller() + // mstore(0, c) + // return(0xc, 0x14) + // } + // } + ECRecoverOverride: hex2Bytes("33806000526014600cf3"), + Calls: []TransactionArgs{{ + From: &randomAccounts[0].addr, + To: &randomAccounts[2].addr, + }}, + }}, + want: [][]res{{{ + // Caller is in this case the contract that invokes ecrecover. + ReturnValue: strings.ToLower(randomAccounts[2].addr.String()), + GasUsed: "0x52f6", + }}}, + }, } for i, tc := range testSuite {