diff --git a/eth/tracers/api.go b/eth/tracers/api.go index 81d852edfa80..632ff43c8f76 100644 --- a/eth/tracers/api.go +++ b/eth/tracers/api.go @@ -671,7 +671,7 @@ func (api *API) traceTx(ctx context.Context, message core.Message, txctx *Contex owner := common.Address{} result, err := core.ApplyMessage(vmenv, message, new(core.GasPool).AddGas(message.Gas()), owner) if err != nil { - return nil, fmt.Errorf("tracing failed: %v", err) + return nil, fmt.Errorf("tracing failed: %w", err) } // Depending on the tracer type, format and return the output diff --git a/eth/tracers/api_test.go b/eth/tracers/api_test.go index a14b5e5dcbf5..ab01807cc50d 100644 --- a/eth/tracers/api_test.go +++ b/eth/tracers/api_test.go @@ -20,6 +20,7 @@ import ( "bytes" "context" "crypto/ecdsa" + "encoding/json" "errors" "fmt" "math/big" @@ -305,6 +306,108 @@ func TestTraceCall(t *testing.T) { } } +func TestOverridenTraceCall(t *testing.T) { + t.Parallel() + + // Initialize test accounts + accounts := newAccounts(3) + genesis := &core.Genesis{ + Alloc: types.GenesisAlloc{ + accounts[0].addr: {Balance: big.NewInt(params.Ether)}, + accounts[1].addr: {Balance: big.NewInt(params.Ether)}, + accounts[2].addr: {Balance: big.NewInt(params.Ether)}, + }, + } + genBlocks := 10 + signer := types.HomesteadSigner{} + api := NewAPI(newTestBackend(t, genBlocks, genesis, func(i int, b *core.BlockGen) { + // Transfer from account[0] to account[1] + // value: 1000 wei + // fee: 0 wei + tx, _ := types.SignTx(types.NewTransaction(uint64(i), accounts[1].addr, big.NewInt(1000), params.TxGas, big.NewInt(0), nil), signer, accounts[0].key) + b.AddTx(tx) + })) + randomAccounts, tracer := newAccounts(3), "callTracer" + + var testSuite = []struct { + blockNumber rpc.BlockNumber + call ethapi.TransactionArgs + config *TraceCallConfig + expectErr error + expect *callTrace + }{ + // Succcessful call with state overriding + { + blockNumber: rpc.LatestBlockNumber, + call: ethapi.TransactionArgs{ + From: &randomAccounts[0].addr, + To: &randomAccounts[1].addr, + Value: (*hexutil.Big)(big.NewInt(1000)), + }, + config: &TraceCallConfig{ + TraceConfig: TraceConfig{Tracer: &tracer}, + StateOverrides: ðapi.StateOverride{ + randomAccounts[0].addr: ethapi.OverrideAccount{Balance: newRPCBalance(big.NewInt(9000000000000000000))}, + }, + }, + expectErr: nil, + expect: &callTrace{ + Type: "CALL", + From: randomAccounts[0].addr, + To: randomAccounts[1].addr, + Gas: newRPCUint64(24979000), + GasUsed: newRPCUint64(0), + Value: (*hexutil.Big)(big.NewInt(1000)), + }, + }, + // Invalid call without state overriding + { + blockNumber: rpc.LatestBlockNumber, + call: ethapi.TransactionArgs{ + From: &randomAccounts[0].addr, + To: &randomAccounts[1].addr, + Value: (*hexutil.Big)(big.NewInt(1000)), + }, + config: &TraceCallConfig{ + TraceConfig: TraceConfig{Tracer: &tracer}, + StateOverrides: ðapi.StateOverride{ + randomAccounts[0].addr: ethapi.OverrideAccount{Balance: newRPCBalance(big.NewInt(1250000000000000000))}, + }, + }, + expectErr: core.ErrInsufficientFunds, + expect: nil, + }, + } + for _, testspec := range testSuite { + result, err := api.TraceCall(context.Background(), testspec.call, rpc.BlockNumberOrHash{BlockNumber: &testspec.blockNumber}, testspec.config) + if testspec.expectErr != nil { + if err == nil { + t.Errorf("Expect error %v, get nothing", testspec.expectErr) + continue + } + if !errors.Is(err, testspec.expectErr) { + t.Errorf("Error mismatch, want %v, get %v", testspec.expectErr, err) + } + } else { + if err != nil { + t.Errorf("Expect no error, get %v", err) + continue + } + ret := new(callTrace) + if err := json.Unmarshal(result.(json.RawMessage), ret); err != nil { + t.Fatalf("failed to unmarshal trace result: %v", err) + } + if !jsonEqual(ret, testspec.expect) { + // uncomment this for easier debugging + //have, _ := json.MarshalIndent(ret, "", " ") + //want, _ := json.MarshalIndent(testspec.expect, "", " ") + //t.Fatalf("trace mismatch: \nhave %+v\nwant %+v", string(have), string(want)) + t.Fatalf("trace mismatch: \nhave %+v\nwant %+v", ret, testspec.expect) + } + } + } +} + func TestTraceTransaction(t *testing.T) { t.Parallel() @@ -469,3 +572,29 @@ func newAccounts(n int) (accounts Accounts) { sort.Sort(accounts) return accounts } + +func newRPCBalance(balance *big.Int) **hexutil.Big { + rpcBalance := (*hexutil.Big)(balance) + return &rpcBalance +} + +func newRPCUint64(number uint64) *hexutil.Uint64 { + rpcUint64 := hexutil.Uint64(number) + return &rpcUint64 +} + +func newRPCBytes(bytes []byte) *hexutil.Bytes { + rpcBytes := hexutil.Bytes(bytes) + return &rpcBytes +} + +func newStates(keys []common.Hash, vals []common.Hash) *map[common.Hash]common.Hash { + if len(keys) != len(vals) { + panic("invalid input") + } + m := make(map[common.Hash]common.Hash) + for i := 0; i < len(keys); i++ { + m[keys[i]] = vals[i] + } + return &m +}