diff --git a/eth/tracers/api.go b/eth/tracers/api.go index 5a28d6889e88..1670c4a69ad5 100644 --- a/eth/tracers/api.go +++ b/eth/tracers/api.go @@ -23,6 +23,7 @@ import ( "errors" "fmt" "io/ioutil" + "math/big" "os" "runtime" "sync" @@ -720,7 +721,7 @@ func (api *API) TraceTransaction(ctx context.Context, hash common.Hash, config * // created during the execution of EVM if the given transaction was added on // top of the provided block and returns them as a JSON object. // You can provide -2 as a block number to trace on top of the pending block. -func (api *API) TraceCall(ctx context.Context, args ethapi.CallArgs, blockNrOrHash rpc.BlockNumberOrHash, config *TraceConfig) (interface{}, error) { +func (api *API) TraceCall(ctx context.Context, args ethapi.CallArgs, blockNrOrHash rpc.BlockNumberOrHash, config *TraceConfig, overrides *map[common.Address]ethapi.Account) (interface{}, error) { // Try to retrieve the specified block var ( err error @@ -743,6 +744,38 @@ func (api *API) TraceCall(ctx context.Context, args ethapi.CallArgs, blockNrOrHa if err != nil { return nil, err } + + if overrides != nil { + // Override the fields of specified contracts before execution. + for addr, account := range *overrides { + // Override account nonce. + if account.Nonce != nil { + statedb.SetNonce(addr, uint64(*account.Nonce)) + } + // Override account(contract) code. + if account.Code != nil { + statedb.SetCode(addr, *account.Code) + } + // Override account balance. + if account.Balance != nil { + statedb.SetBalance(addr, (*big.Int)(*account.Balance)) + } + if account.State != nil && account.StateDiff != nil { + return nil, fmt.Errorf("account %s has both 'state' and 'stateDiff'", addr.Hex()) + } + // Replace entire state if caller requires. + if account.State != nil { + statedb.SetStorage(addr, *account.State) + } + // Apply state diff into specified accounts. + if account.StateDiff != nil { + for key, value := range *account.StateDiff { + statedb.SetState(addr, key, value) + } + } + } + } + // Execute the trace msg := args.ToMessage(api.backend.RPCGasCap()) vmctx := core.NewEVMBlockContext(block.Header(), api.chainContext(ctx), nil) diff --git a/eth/tracers/api_test.go b/eth/tracers/api_test.go index 7ca90a660851..3b9e314f14d0 100644 --- a/eth/tracers/api_test.go +++ b/eth/tracers/api_test.go @@ -284,7 +284,7 @@ func TestTraceCall(t *testing.T) { }, } for _, testspec := range testSuite { - result, err := api.TraceCall(context.Background(), testspec.call, rpc.BlockNumberOrHash{BlockNumber: &testspec.blockNumber}, testspec.config) + result, err := api.TraceCall(context.Background(), testspec.call, rpc.BlockNumberOrHash{BlockNumber: &testspec.blockNumber}, testspec.config, nil) if testspec.expectErr != nil { if err == nil { t.Errorf("Expect error %v, get nothing", testspec.expectErr) diff --git a/internal/ethapi/api.go b/internal/ethapi/api.go index fe5c3388b57e..8d520fa23d24 100644 --- a/internal/ethapi/api.go +++ b/internal/ethapi/api.go @@ -808,7 +808,7 @@ func (args *CallArgs) ToMessage(globalGasCap uint64) types.Message { // set, message execution will only use the data in the given state. Otherwise // if statDiff is set, all diff will be applied first and then execute the call // message. -type account struct { +type Account struct { Nonce *hexutil.Uint64 `json:"nonce"` Code *hexutil.Bytes `json:"code"` Balance **hexutil.Big `json:"balance"` @@ -816,7 +816,7 @@ type account struct { StateDiff *map[common.Hash]common.Hash `json:"stateDiff"` } -func DoCall(ctx context.Context, b Backend, args CallArgs, blockNrOrHash rpc.BlockNumberOrHash, overrides map[common.Address]account, vmCfg vm.Config, timeout time.Duration, globalGasCap uint64) (*core.ExecutionResult, error) { +func DoCall(ctx context.Context, b Backend, args CallArgs, blockNrOrHash rpc.BlockNumberOrHash, overrides map[common.Address]Account, vmCfg vm.Config, timeout time.Duration, globalGasCap uint64) (*core.ExecutionResult, error) { defer func(start time.Time) { log.Debug("Executing EVM call finished", "runtime", time.Since(start)) }(time.Now()) state, header, err := b.StateAndHeaderByNumberOrHash(ctx, blockNrOrHash) @@ -929,8 +929,8 @@ func (e *revertError) ErrorData() interface{} { // // Note, this function doesn't make and changes in the state/blockchain and is // useful to execute and retrieve values. -func (s *PublicBlockChainAPI) Call(ctx context.Context, args CallArgs, blockNrOrHash rpc.BlockNumberOrHash, overrides *map[common.Address]account) (hexutil.Bytes, error) { - var accounts map[common.Address]account +func (s *PublicBlockChainAPI) Call(ctx context.Context, args CallArgs, blockNrOrHash rpc.BlockNumberOrHash, overrides *map[common.Address]Account) (hexutil.Bytes, error) { + var accounts map[common.Address]Account if overrides != nil { accounts = *overrides }