Skip to content

Commit ba5cf88

Browse files
holimanjagdeep sidhu
authored andcommitted
eth/tracers: add support for block overrides in debug_traceCall (ethereum#24871)
This PR adds support for block overrides when doing debug_traceCall. - Previously, debug_traceCall against pending erroneously used a common.Hash{} stateroot when looking up the state, meaning that a totally empty state was used -- so it always failed, - With this change, we reject executing debug_traceCall against pending. - And we add ability to override all evm-visible header fields.
1 parent 7f691b2 commit ba5cf88

File tree

4 files changed

+120
-42
lines changed

4 files changed

+120
-42
lines changed

core/evm.go

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -89,6 +89,11 @@ func GetHashFn(ref *types.Header, chain ChainContext) func(n uint64) common.Hash
8989
var cache []common.Hash
9090

9191
return func(n uint64) common.Hash {
92+
if ref.Number.Uint64() <= n {
93+
// This situation can happen if we're doing tracing and using
94+
// block overrides.
95+
return common.Hash{}
96+
}
9297
// If there's no hash cache yet, make one
9398
if len(cache) == 0 {
9499
cache = append(cache, ref.ParentHash)

eth/tracers/api.go

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -196,6 +196,7 @@ type TraceCallConfig struct {
196196
Timeout *string
197197
Reexec *uint64
198198
StateOverrides *ethapi.StateOverride
199+
BlockOverrides *ethapi.BlockOverrides
199200
}
200201

201202
// StdTraceConfig holds extra parameters to standard-json trace functions.
@@ -823,7 +824,6 @@ func (api *API) TraceTransaction(ctx context.Context, hash common.Hash, config *
823824
// TraceCall lets you trace a given eth_call. It collects the structured logs
824825
// created during the execution of EVM if the given transaction was added on
825826
// top of the provided block and returns them as a JSON object.
826-
// You can provide -2 as a block number to trace on top of the pending block.
827827
func (api *API) TraceCall(ctx context.Context, args ethapi.TransactionArgs, blockNrOrHash rpc.BlockNumberOrHash, config *TraceCallConfig) (interface{}, error) {
828828
// Try to retrieve the specified block
829829
var (
@@ -833,6 +833,14 @@ func (api *API) TraceCall(ctx context.Context, args ethapi.TransactionArgs, bloc
833833
if hash, ok := blockNrOrHash.Hash(); ok {
834834
block, err = api.blockByHash(ctx, hash)
835835
} else if number, ok := blockNrOrHash.Number(); ok {
836+
if number == rpc.PendingBlockNumber {
837+
// We don't have access to the miner here. For tracing 'future' transactions,
838+
// it can be done with block- and state-overrides instead, which offers
839+
// more flexibility and stability than trying to trace on 'pending', since
840+
// the contents of 'pending' is unstable and probably not a true representation
841+
// of what the next actual block is likely to contain.
842+
return nil, errors.New("tracing on top of pending is not supported")
843+
}
836844
block, err = api.blockByNumber(ctx, number)
837845
} else {
838846
return nil, errors.New("invalid arguments; neither block nor hash specified")
@@ -849,18 +857,19 @@ func (api *API) TraceCall(ctx context.Context, args ethapi.TransactionArgs, bloc
849857
if err != nil {
850858
return nil, err
851859
}
852-
// Apply the customized state rules if required.
860+
vmctx := core.NewEVMBlockContext(block.Header(), api.chainContext(ctx), nil)
861+
// Apply the customization rules if required.
853862
if config != nil {
854863
if err := config.StateOverrides.Apply(statedb); err != nil {
855864
return nil, err
856865
}
866+
config.BlockOverrides.Apply(&vmctx)
857867
}
858868
// Execute the trace
859869
msg, err := args.ToMessage(api.backend.RPCGasCap(), block.BaseFee())
860870
if err != nil {
861871
return nil, err
862872
}
863-
vmctx := core.NewEVMBlockContext(block.Header(), api.chainContext(ctx), nil)
864873

865874
var traceConfig *TraceConfig
866875
if config != nil {

eth/tracers/api_test.go

Lines changed: 68 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -202,13 +202,12 @@ func TestTraceCall(t *testing.T) {
202202
tx, _ := types.SignTx(types.NewTransaction(uint64(i), accounts[1].addr, big.NewInt(1000), params.TxGas, b.BaseFee(), nil), signer, accounts[0].key)
203203
b.AddTx(tx)
204204
}))
205-
206205
var testSuite = []struct {
207206
blockNumber rpc.BlockNumber
208207
call ethapi.TransactionArgs
209208
config *TraceCallConfig
210209
expectErr error
211-
expect interface{}
210+
expect string
212211
}{
213212
// Standard JSON trace upon the genesis, plain transfer.
214213
{
@@ -220,12 +219,7 @@ func TestTraceCall(t *testing.T) {
220219
},
221220
config: nil,
222221
expectErr: nil,
223-
expect: &logger.ExecutionResult{
224-
Gas: params.TxGas,
225-
Failed: false,
226-
ReturnValue: "",
227-
StructLogs: []logger.StructLogRes{},
228-
},
222+
expect: `{"gas":21000,"failed":false,"returnValue":"","structLogs":[]}`,
229223
},
230224
// Standard JSON trace upon the head, plain transfer.
231225
{
@@ -237,12 +231,7 @@ func TestTraceCall(t *testing.T) {
237231
},
238232
config: nil,
239233
expectErr: nil,
240-
expect: &logger.ExecutionResult{
241-
Gas: params.TxGas,
242-
Failed: false,
243-
ReturnValue: "",
244-
StructLogs: []logger.StructLogRes{},
245-
},
234+
expect: `{"gas":21000,"failed":false,"returnValue":"","structLogs":[]}`,
246235
},
247236
// Standard JSON trace upon the non-existent block, error expects
248237
{
@@ -254,7 +243,7 @@ func TestTraceCall(t *testing.T) {
254243
},
255244
config: nil,
256245
expectErr: fmt.Errorf("block #%d not found", genBlocks+1),
257-
expect: nil,
246+
//expect: nil,
258247
},
259248
// Standard JSON trace upon the latest block
260249
{
@@ -266,14 +255,9 @@ func TestTraceCall(t *testing.T) {
266255
},
267256
config: nil,
268257
expectErr: nil,
269-
expect: &logger.ExecutionResult{
270-
Gas: params.TxGas,
271-
Failed: false,
272-
ReturnValue: "",
273-
StructLogs: []logger.StructLogRes{},
274-
},
258+
expect: `{"gas":21000,"failed":false,"returnValue":"","structLogs":[]}`,
275259
},
276-
// Standard JSON trace upon the pending block
260+
// Tracing on 'pending' should fail:
277261
{
278262
blockNumber: rpc.PendingBlockNumber,
279263
call: ethapi.TransactionArgs{
@@ -282,36 +266,48 @@ func TestTraceCall(t *testing.T) {
282266
Value: (*hexutil.Big)(big.NewInt(1000)),
283267
},
284268
config: nil,
285-
expectErr: nil,
286-
expect: &logger.ExecutionResult{
287-
Gas: params.TxGas,
288-
Failed: false,
289-
ReturnValue: "",
290-
StructLogs: []logger.StructLogRes{},
269+
expectErr: errors.New("tracing on top of pending is not supported"),
270+
},
271+
{
272+
blockNumber: rpc.LatestBlockNumber,
273+
call: ethapi.TransactionArgs{
274+
From: &accounts[0].addr,
275+
Input: &hexutil.Bytes{0x43}, // blocknumber
276+
},
277+
config: &TraceCallConfig{
278+
BlockOverrides: &ethapi.BlockOverrides{Number: (*hexutil.Big)(big.NewInt(0x1337))},
291279
},
280+
expectErr: nil,
281+
expect: ` {"gas":53018,"failed":false,"returnValue":"","structLogs":[
282+
{"pc":0,"op":"NUMBER","gas":24946984,"gasCost":2,"depth":1,"stack":[]},
283+
{"pc":1,"op":"STOP","gas":24946982,"gasCost":0,"depth":1,"stack":["0x1337"]}]}`,
292284
},
293285
}
294-
for _, testspec := range testSuite {
286+
for i, testspec := range testSuite {
295287
result, err := api.TraceCall(context.Background(), testspec.call, rpc.BlockNumberOrHash{BlockNumber: &testspec.blockNumber}, testspec.config)
296288
if testspec.expectErr != nil {
297289
if err == nil {
298-
t.Errorf("Expect error %v, get nothing", testspec.expectErr)
290+
t.Errorf("test %d: expect error %v, got nothing", i, testspec.expectErr)
299291
continue
300292
}
301293
if !reflect.DeepEqual(err, testspec.expectErr) {
302-
t.Errorf("Error mismatch, want %v, get %v", testspec.expectErr, err)
294+
t.Errorf("test %d: error mismatch, want %v, git %v", i, testspec.expectErr, err)
303295
}
304296
} else {
305297
if err != nil {
306-
t.Errorf("Expect no error, get %v", err)
298+
t.Errorf("test %d: expect no error, got %v", i, err)
307299
continue
308300
}
309301
var have *logger.ExecutionResult
310302
if err := json.Unmarshal(result.(json.RawMessage), &have); err != nil {
311-
t.Errorf("failed to unmarshal result %v", err)
303+
t.Errorf("test %d: failed to unmarshal result %v", i, err)
304+
}
305+
var want *logger.ExecutionResult
306+
if err := json.Unmarshal([]byte(testspec.expect), &want); err != nil {
307+
t.Errorf("test %d: failed to unmarshal result %v", i, err)
312308
}
313-
if !reflect.DeepEqual(have, testspec.expect) {
314-
t.Errorf("Result mismatch, want %v, get %v", testspec.expect, have)
309+
if !reflect.DeepEqual(have, want) {
310+
t.Errorf("test %d: result mismatch, want %v, got %v", i, testspec.expect, string(result.(json.RawMessage)))
315311
}
316312
}
317313
}
@@ -452,7 +448,7 @@ func TestTracingWithOverrides(t *testing.T) {
452448
type res struct {
453449
Gas int
454450
Failed bool
455-
returnValue string
451+
ReturnValue string
456452
}
457453
var testSuite = []struct {
458454
blockNumber rpc.BlockNumber
@@ -463,7 +459,7 @@ func TestTracingWithOverrides(t *testing.T) {
463459
}{
464460
// Call which can only succeed if state is state overridden
465461
{
466-
blockNumber: rpc.PendingBlockNumber,
462+
blockNumber: rpc.LatestBlockNumber,
467463
call: ethapi.TransactionArgs{
468464
From: &randomAccounts[0].addr,
469465
To: &randomAccounts[1].addr,
@@ -478,7 +474,7 @@ func TestTracingWithOverrides(t *testing.T) {
478474
},
479475
// Invalid call without state overriding
480476
{
481-
blockNumber: rpc.PendingBlockNumber,
477+
blockNumber: rpc.LatestBlockNumber,
482478
call: ethapi.TransactionArgs{
483479
From: &randomAccounts[0].addr,
484480
To: &randomAccounts[1].addr,
@@ -504,7 +500,7 @@ func TestTracingWithOverrides(t *testing.T) {
504500
// }
505501
// }
506502
{
507-
blockNumber: rpc.PendingBlockNumber,
503+
blockNumber: rpc.LatestBlockNumber,
508504
call: ethapi.TransactionArgs{
509505
From: &randomAccounts[0].addr,
510506
To: &randomAccounts[2].addr,
@@ -521,6 +517,39 @@ func TestTracingWithOverrides(t *testing.T) {
521517
},
522518
want: `{"gas":23347,"failed":false,"returnValue":"000000000000000000000000000000000000000000000000000000000000007b"}`,
523519
},
520+
{ // Override blocknumber
521+
blockNumber: rpc.LatestBlockNumber,
522+
call: ethapi.TransactionArgs{
523+
From: &accounts[0].addr,
524+
// BLOCKNUMBER PUSH1 MSTORE
525+
Input: newRPCBytes(common.Hex2Bytes("4360005260206000f3")),
526+
//&hexutil.Bytes{0x43}, // blocknumber
527+
},
528+
config: &TraceCallConfig{
529+
BlockOverrides: &ethapi.BlockOverrides{Number: (*hexutil.Big)(big.NewInt(0x1337))},
530+
},
531+
want: `{"gas":59537,"failed":false,"returnValue":"0000000000000000000000000000000000000000000000000000000000001337"}`,
532+
},
533+
{ // Override blocknumber, and query a blockhash
534+
blockNumber: rpc.LatestBlockNumber,
535+
call: ethapi.TransactionArgs{
536+
From: &accounts[0].addr,
537+
Input: &hexutil.Bytes{
538+
0x60, 0x00, 0x40, // BLOCKHASH(0)
539+
0x60, 0x00, 0x52, // STORE memory offset 0
540+
0x61, 0x13, 0x36, 0x40, // BLOCKHASH(0x1336)
541+
0x60, 0x20, 0x52, // STORE memory offset 32
542+
0x61, 0x13, 0x37, 0x40, // BLOCKHASH(0x1337)
543+
0x60, 0x40, 0x52, // STORE memory offset 64
544+
0x60, 0x60, 0x60, 0x00, 0xf3, // RETURN (0-96)
545+
546+
}, // blocknumber
547+
},
548+
config: &TraceCallConfig{
549+
BlockOverrides: &ethapi.BlockOverrides{Number: (*hexutil.Big)(big.NewInt(0x1337))},
550+
},
551+
want: `{"gas":72666,"failed":false,"returnValue":"000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"}`,
552+
},
524553
}
525554
for i, tc := range testSuite {
526555
result, err := api.TraceCall(context.Background(), tc.call, rpc.BlockNumberOrHash{BlockNumber: &tc.blockNumber}, tc.config)

internal/ethapi/api.go

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -888,6 +888,41 @@ func (diff *StateOverride) Apply(state *state.StateDB) error {
888888
return nil
889889
}
890890

891+
// BlockOverrides is a set of header fields to override.
892+
type BlockOverrides struct {
893+
Number *hexutil.Big
894+
Difficulty *hexutil.Big
895+
Time *hexutil.Big
896+
GasLimit *hexutil.Uint64
897+
Coinbase *common.Address
898+
Random *common.Hash
899+
}
900+
901+
// Apply overrides the given header fields into the given block context.
902+
func (diff *BlockOverrides) Apply(blockCtx *vm.BlockContext) {
903+
if diff == nil {
904+
return
905+
}
906+
if diff.Number != nil {
907+
blockCtx.BlockNumber = diff.Number.ToInt()
908+
}
909+
if diff.Difficulty != nil {
910+
blockCtx.Difficulty = diff.Difficulty.ToInt()
911+
}
912+
if diff.Time != nil {
913+
blockCtx.Time = diff.Time.ToInt()
914+
}
915+
if diff.GasLimit != nil {
916+
blockCtx.GasLimit = uint64(*diff.GasLimit)
917+
}
918+
if diff.Coinbase != nil {
919+
blockCtx.Coinbase = *diff.Coinbase
920+
}
921+
if diff.Random != nil {
922+
blockCtx.Random = diff.Random
923+
}
924+
}
925+
891926
func DoCall(ctx context.Context, b Backend, args TransactionArgs, blockNrOrHash rpc.BlockNumberOrHash, overrides *StateOverride, timeout time.Duration, globalGasCap uint64) (*core.ExecutionResult, error) {
892927
defer func(start time.Time) { log.Debug("Executing EVM call finished", "runtime", time.Since(start)) }(time.Now())
893928

0 commit comments

Comments
 (0)