diff --git a/eth/tracers/api.go b/eth/tracers/api.go index 696202334369..b50a532b6071 100644 --- a/eth/tracers/api.go +++ b/eth/tracers/api.go @@ -953,40 +953,53 @@ func (api *API) TraceCall(ctx context.Context, args ethapi.TransactionArgs, bloc } defer release() - vmctx := core.NewEVMBlockContext(block.Header(), api.chainContext(ctx), nil) + h := block.Header() + blockContext := core.NewEVMBlockContext(h, api.chainContext(ctx), nil) + // Apply the customization rules if required. if config != nil { - if overrideErr := config.BlockOverrides.Apply(&vmctx); overrideErr != nil { - return nil, overrideErr + if config.BlockOverrides != nil && config.BlockOverrides.Number.ToInt().Uint64() == h.Number.Uint64()+1 { + // Overriding the block number to n+1 is a common way for wallets to + // simulate transactions, however without the following fix, a contract + // can assert it is being simulated by checking if blockhash(n) == 0x0 and + // can behave differently during the simulation. (#32175 for more info) + // -- + // Modify the parent hash and number so that downstream, blockContext's + // GetHash function can correctly return n. + h.ParentHash = h.Hash() + h.Number.Add(h.Number, big.NewInt(1)) } - rules := api.backend.ChainConfig().Rules(vmctx.BlockNumber, vmctx.Random != nil, vmctx.Time) - + if err := config.BlockOverrides.Apply(&blockContext); err != nil { + return nil, err + } + rules := api.backend.ChainConfig().Rules(blockContext.BlockNumber, blockContext.Random != nil, blockContext.Time) precompiles = vm.ActivePrecompiledContracts(rules) if err := config.StateOverrides.Apply(statedb, precompiles); err != nil { return nil, err } } - // Execute the trace - if err := args.CallDefaults(api.backend.RPCGasCap(), vmctx.BaseFee, api.backend.ChainConfig().ChainID); err != nil { + + // Execute the trace. + if err := args.CallDefaults(api.backend.RPCGasCap(), blockContext.BaseFee, api.backend.ChainConfig().ChainID); err != nil { return nil, err } var ( - msg = args.ToMessage(vmctx.BaseFee, true, true) + msg = args.ToMessage(blockContext.BaseFee, true, true) tx = args.ToTransaction(types.LegacyTxType) traceConfig *TraceConfig ) // Lower the basefee to 0 to avoid breaking EVM // invariants (basefee < feecap). if msg.GasPrice.Sign() == 0 { - vmctx.BaseFee = new(big.Int) + blockContext.BaseFee = new(big.Int) } if msg.BlobGasFeeCap != nil && msg.BlobGasFeeCap.BitLen() == 0 { - vmctx.BlobBaseFee = new(big.Int) + blockContext.BlobBaseFee = new(big.Int) } if config != nil { traceConfig = &config.TraceConfig } - return api.traceTx(ctx, tx, msg, new(Context), vmctx, statedb, traceConfig, precompiles) + return api.traceTx(ctx, tx, msg, new(Context), blockContext, statedb, traceConfig, precompiles) } // traceTx configures a new tracer according to the provided configuration, and diff --git a/eth/tracers/api_test.go b/eth/tracers/api_test.go index 076e6fd8d4f9..7ed2a5936e1f 100644 --- a/eth/tracers/api_test.go +++ b/eth/tracers/api_test.go @@ -689,6 +689,7 @@ func TestTracingWithOverrides(t *testing.T) { Failed bool ReturnValue string } + var testSuite = []struct { blockNumber rpc.BlockNumber call ethapi.TransactionArgs @@ -788,6 +789,25 @@ func TestTracingWithOverrides(t *testing.T) { }, want: `{"gas":72666,"failed":false,"returnValue":"0x000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"}`, }, + { // Override blocknumber with block n+1 and query a blockhash (resolves issue #32175) + blockNumber: rpc.LatestBlockNumber, + call: ethapi.TransactionArgs{ + From: &accounts[0].addr, + Input: newRPCBytes([]byte{ + byte(vm.PUSH1), byte(genBlocks), + byte(vm.BLOCKHASH), + byte(vm.PUSH1), 0x00, + byte(vm.MSTORE), + byte(vm.PUSH1), 0x20, + byte(vm.PUSH1), 0x00, + byte(vm.RETURN), + }), + }, + config: &TraceCallConfig{ + BlockOverrides: &override.BlockOverrides{Number: (*hexutil.Big)(big.NewInt(int64(genBlocks + 1)))}, + }, + want: fmt.Sprintf(`{"gas":59590,"failed":false,"returnValue":"%s"}`, backend.chain.GetHeaderByNumber(uint64(genBlocks)).Hash().Hex()), + }, /* pragma solidity =0.8.12;