From 74bf61fa99ce80e00aee1c8781af4de412e00e29 Mon Sep 17 00:00:00 2001 From: jsvisa Date: Fri, 3 Nov 2023 13:26:37 +0800 Subject: [PATCH 1/7] eth/tracers: traceCall with txindex Signed-off-by: jsvisa --- eth/tracers/api.go | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/eth/tracers/api.go b/eth/tracers/api.go index 300d904a9970..c8cd0a511705 100644 --- a/eth/tracers/api.go +++ b/eth/tracers/api.go @@ -164,6 +164,7 @@ type TraceCallConfig struct { TraceConfig StateOverrides *ethapi.StateOverride BlockOverrides *ethapi.BlockOverrides + TxIndex *hexutil.Uint } // StdTraceConfig holds extra parameters to standard-json trace functions. @@ -866,8 +867,10 @@ func (api *API) TraceTransaction(ctx context.Context, hash common.Hash, config * func (api *API) TraceCall(ctx context.Context, args ethapi.TransactionArgs, blockNrOrHash rpc.BlockNumberOrHash, config *TraceCallConfig) (interface{}, error) { // Try to retrieve the specified block var ( - err error - block *types.Block + err error + block *types.Block + statedb *state.StateDB + release StateReleaseFunc ) if hash, ok := blockNrOrHash.Hash(); ok { block, err = api.blockByHash(ctx, hash) @@ -892,7 +895,11 @@ func (api *API) TraceCall(ctx context.Context, args ethapi.TransactionArgs, bloc if config != nil && config.Reexec != nil { reexec = *config.Reexec } - statedb, release, err := api.backend.StateAtBlock(ctx, block, reexec, nil, true, false) + if config != nil && config.TxIndex != nil { + _, _, statedb, release, err = api.backend.StateAtTransaction(ctx, block, int(*config.TxIndex), reexec) + } else { + statedb, release, err = api.backend.StateAtBlock(ctx, block, reexec, nil, true, false) + } if err != nil { return nil, err } From a691c08c44412511dad126133cc31770d1921801 Mon Sep 17 00:00:00 2001 From: jsvisa Date: Fri, 3 Nov 2023 13:26:46 +0800 Subject: [PATCH 2/7] eth/tracers: test case Signed-off-by: jsvisa --- eth/tracers/api_test.go | 38 +++++++++++++++++++++++++++++++++++++- 1 file changed, 37 insertions(+), 1 deletion(-) diff --git a/eth/tracers/api_test.go b/eth/tracers/api_test.go index 0f78af9a01e5..3d43ebfc5223 100644 --- a/eth/tracers/api_test.go +++ b/eth/tracers/api_test.go @@ -206,7 +206,20 @@ func TestTraceCall(t *testing.T) { // fee: 0 wei tx, _ := types.SignTx(types.NewTransaction(uint64(i), accounts[1].addr, big.NewInt(1000), params.TxGas, b.BaseFee(), nil), signer, accounts[0].key) b.AddTx(tx) + + if i == genBlocks-1 { + tx, _ := types.SignTx(types.NewTransaction(uint64(i)+1, accounts[2].addr, big.NewInt(1000), params.TxGas, b.BaseFee(), nil), signer, accounts[0].key) + b.AddTx(tx) + } }) + + // for i := 0; i <= genBlocks; i++ { + // block := backend.chain.GetBlockByNumber(uint64(i)) + // fmt.Printf(".......block(%s) %d -> %d \n", block.Hash().String(), i, block.Transactions().Len()) + // } + + uintPtr := func(i int) *hexutil.Uint { x := hexutil.Uint(i); return &x } + defer backend.teardown() api := NewAPI(backend) var testSuite = []struct { @@ -240,6 +253,29 @@ func TestTraceCall(t *testing.T) { expectErr: nil, expect: `{"gas":21000,"failed":false,"returnValue":"","structLogs":[]}`, }, + // Standard JSON trace upon the head, plain transfer. + { + blockNumber: rpc.BlockNumber(genBlocks), + call: ethapi.TransactionArgs{ + From: &accounts[2].addr, + To: &accounts[0].addr, + Value: (*hexutil.Big)(new(big.Int).Add(big.NewInt(params.Ether), big.NewInt(100))), + }, + config: &TraceCallConfig{TxIndex: uintPtr(0)}, + expectErr: fmt.Errorf("insufficient funds for gas * price + value: address %s have 1000000000000000000 want 1000000000000000100", accounts[2].addr), + }, + // Standard JSON trace upon the head + txindex, plain transfer. + { + blockNumber: rpc.BlockNumber(genBlocks), + call: ethapi.TransactionArgs{ + From: &accounts[2].addr, + To: &accounts[0].addr, + Value: (*hexutil.Big)(new(big.Int).Add(big.NewInt(params.Ether), big.NewInt(100))), + }, + config: &TraceCallConfig{TxIndex: uintPtr(1)}, + expectErr: nil, + expect: `{"gas":21000,"failed":false,"returnValue":"","structLogs":[]}`, + }, // Standard JSON trace upon the non-existent block, error expects { blockNumber: rpc.BlockNumber(genBlocks + 1), @@ -298,7 +334,7 @@ func TestTraceCall(t *testing.T) { continue } if !reflect.DeepEqual(err, testspec.expectErr) { - t.Errorf("test %d: error mismatch, want %v, git %v", i, testspec.expectErr, err) + t.Errorf("test %d: error mismatch, want %v, got %v", i, testspec.expectErr, err) } } else { if err != nil { From a5d05ad71f4a1dbab14b1d038fa939e69b90414d Mon Sep 17 00:00:00 2001 From: jsvisa Date: Fri, 3 Nov 2023 17:18:13 +0800 Subject: [PATCH 3/7] eth/tracers: fix testcase Signed-off-by: jsvisa --- eth/tracers/api_test.go | 56 ++++++++++++++++++++++++++++++----------- 1 file changed, 41 insertions(+), 15 deletions(-) diff --git a/eth/tracers/api_test.go b/eth/tracers/api_test.go index 3d43ebfc5223..e402b9168dcd 100644 --- a/eth/tracers/api_test.go +++ b/eth/tracers/api_test.go @@ -200,24 +200,28 @@ func TestTraceCall(t *testing.T) { } genBlocks := 10 signer := types.HomesteadSigner{} + nonce := uint64(0) backend := 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, b.BaseFee(), nil), signer, accounts[0].key) + tx, _ := types.SignTx(types.NewTransaction(nonce, accounts[1].addr, big.NewInt(1000), params.TxGas, b.BaseFee(), nil), signer, accounts[0].key) b.AddTx(tx) + nonce++ + + if i == genBlocks-2 { + // Transfer from account[0] to account[2] + tx, _ = types.SignTx(types.NewTransaction(nonce, accounts[2].addr, big.NewInt(1000), params.TxGas, b.BaseFee(), nil), signer, accounts[0].key) + b.AddTx(tx) + nonce++ - if i == genBlocks-1 { - tx, _ := types.SignTx(types.NewTransaction(uint64(i)+1, accounts[2].addr, big.NewInt(1000), params.TxGas, b.BaseFee(), nil), signer, accounts[0].key) + // Transfer from account[0] to account[1] again + tx, _ = types.SignTx(types.NewTransaction(nonce, accounts[1].addr, big.NewInt(1000), params.TxGas, b.BaseFee(), nil), signer, accounts[0].key) b.AddTx(tx) + nonce++ } }) - // for i := 0; i <= genBlocks; i++ { - // block := backend.chain.GetBlockByNumber(uint64(i)) - // fmt.Printf(".......block(%s) %d -> %d \n", block.Hash().String(), i, block.Transactions().Len()) - // } - uintPtr := func(i int) *hexutil.Uint { x := hexutil.Uint(i); return &x } defer backend.teardown() @@ -253,26 +257,48 @@ func TestTraceCall(t *testing.T) { expectErr: nil, expect: `{"gas":21000,"failed":false,"returnValue":"","structLogs":[]}`, }, - // Standard JSON trace upon the head, plain transfer. + // Upon the last state, default to the post block's state { - blockNumber: rpc.BlockNumber(genBlocks), + blockNumber: rpc.BlockNumber(genBlocks - 1), + call: ethapi.TransactionArgs{ + From: &accounts[2].addr, + To: &accounts[0].addr, + Value: (*hexutil.Big)(new(big.Int).Add(big.NewInt(params.Ether), big.NewInt(100))), + }, + config: nil, + expect: `{"gas":21000,"failed":false,"returnValue":"","structLogs":[]}`, + }, + // Before the first transaction, should be failed + { + blockNumber: rpc.BlockNumber(genBlocks - 1), call: ethapi.TransactionArgs{ From: &accounts[2].addr, To: &accounts[0].addr, Value: (*hexutil.Big)(new(big.Int).Add(big.NewInt(params.Ether), big.NewInt(100))), }, config: &TraceCallConfig{TxIndex: uintPtr(0)}, - expectErr: fmt.Errorf("insufficient funds for gas * price + value: address %s have 1000000000000000000 want 1000000000000000100", accounts[2].addr), + expectErr: fmt.Errorf("tracing failed: insufficient funds for gas * price + value: address %s have 1000000000000000000 want 1000000000000000100", accounts[2].addr), }, - // Standard JSON trace upon the head + txindex, plain transfer. + // Before the target transaction, should be failed { - blockNumber: rpc.BlockNumber(genBlocks), + blockNumber: rpc.BlockNumber(genBlocks - 1), call: ethapi.TransactionArgs{ From: &accounts[2].addr, To: &accounts[0].addr, Value: (*hexutil.Big)(new(big.Int).Add(big.NewInt(params.Ether), big.NewInt(100))), }, config: &TraceCallConfig{TxIndex: uintPtr(1)}, + expectErr: fmt.Errorf("tracing failed: insufficient funds for gas * price + value: address %s have 1000000000000000000 want 1000000000000000100", accounts[2].addr), + }, + // After the target transaction, should be succeed + { + blockNumber: rpc.BlockNumber(genBlocks - 1), + call: ethapi.TransactionArgs{ + From: &accounts[2].addr, + To: &accounts[0].addr, + Value: (*hexutil.Big)(new(big.Int).Add(big.NewInt(params.Ether), big.NewInt(100))), + }, + config: &TraceCallConfig{TxIndex: uintPtr(2)}, expectErr: nil, expect: `{"gas":21000,"failed":false,"returnValue":"","structLogs":[]}`, }, @@ -333,8 +359,8 @@ func TestTraceCall(t *testing.T) { t.Errorf("test %d: expect error %v, got nothing", i, testspec.expectErr) continue } - if !reflect.DeepEqual(err, testspec.expectErr) { - t.Errorf("test %d: error mismatch, want %v, got %v", i, testspec.expectErr, err) + if !reflect.DeepEqual(err.Error(), testspec.expectErr.Error()) { + t.Errorf("test %d: error mismatch, want '%v', got '%v'", i, testspec.expectErr, err) } } else { if err != nil { From d8aad5e8f2a2a1fefdb0d766d8cf36d2f86ad179 Mon Sep 17 00:00:00 2001 From: jsvisa Date: Wed, 29 Nov 2023 22:38:09 +0800 Subject: [PATCH 4/7] eth/tracers: use types.NewTx instead Signed-off-by: jsvisa --- eth/tracers/api_test.go | 27 ++++++++++++++++++++++++--- 1 file changed, 24 insertions(+), 3 deletions(-) diff --git a/eth/tracers/api_test.go b/eth/tracers/api_test.go index e402b9168dcd..c16fbd45efdd 100644 --- a/eth/tracers/api_test.go +++ b/eth/tracers/api_test.go @@ -205,18 +205,39 @@ func TestTraceCall(t *testing.T) { // Transfer from account[0] to account[1] // value: 1000 wei // fee: 0 wei - tx, _ := types.SignTx(types.NewTransaction(nonce, accounts[1].addr, big.NewInt(1000), params.TxGas, b.BaseFee(), nil), signer, accounts[0].key) + tx, _ := types.SignTx(types.NewTx(&types.LegacyTx{ + Nonce: nonce, + To: &accounts[1].addr, + Value: big.NewInt(1000), + Gas: params.TxGas, + GasPrice: b.BaseFee(), + Data: nil}), + signer, accounts[0].key) b.AddTx(tx) nonce++ if i == genBlocks-2 { // Transfer from account[0] to account[2] - tx, _ = types.SignTx(types.NewTransaction(nonce, accounts[2].addr, big.NewInt(1000), params.TxGas, b.BaseFee(), nil), signer, accounts[0].key) + tx, _ = types.SignTx(types.NewTx(&types.LegacyTx{ + Nonce: nonce, + To: &accounts[2].addr, + Value: big.NewInt(1000), + Gas: params.TxGas, + GasPrice: b.BaseFee(), + Data: nil}), + signer, accounts[0].key) b.AddTx(tx) nonce++ // Transfer from account[0] to account[1] again - tx, _ = types.SignTx(types.NewTransaction(nonce, accounts[1].addr, big.NewInt(1000), params.TxGas, b.BaseFee(), nil), signer, accounts[0].key) + tx, _ = types.SignTx(types.NewTx(&types.LegacyTx{ + Nonce: nonce, + To: &accounts[1].addr, + Value: big.NewInt(1000), + Gas: params.TxGas, + GasPrice: b.BaseFee(), + Data: nil}), + signer, accounts[0].key) b.AddTx(tx) nonce++ } From 2adf9757cb3ac7a2c150b749253cc9a4980bede1 Mon Sep 17 00:00:00 2001 From: jsvisa Date: Wed, 29 Nov 2023 22:40:51 +0800 Subject: [PATCH 5/7] eth/tracers: use types.NewTx instead too Signed-off-by: jsvisa --- eth/tracers/api_test.go | 27 ++++++++++++++++++++++++--- 1 file changed, 24 insertions(+), 3 deletions(-) diff --git a/eth/tracers/api_test.go b/eth/tracers/api_test.go index c16fbd45efdd..49c3ebb67d7f 100644 --- a/eth/tracers/api_test.go +++ b/eth/tracers/api_test.go @@ -421,7 +421,14 @@ func TestTraceTransaction(t *testing.T) { // 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, b.BaseFee(), nil), signer, accounts[0].key) + tx, _ := types.SignTx(types.NewTx(&types.LegacyTx{ + Nonce: uint64(i), + To: &accounts[1].addr, + Value: big.NewInt(1000), + Gas: params.TxGas, + GasPrice: b.BaseFee(), + Data: nil}), + signer, accounts[0].key) b.AddTx(tx) target = tx.Hash() }) @@ -471,7 +478,14 @@ func TestTraceBlock(t *testing.T) { // 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, b.BaseFee(), nil), signer, accounts[0].key) + tx, _ := types.SignTx(types.NewTx(&types.LegacyTx{ + Nonce: uint64(i), + To: &accounts[1].addr, + Value: big.NewInt(1000), + Gas: params.TxGas, + GasPrice: b.BaseFee(), + Data: nil}), + signer, accounts[0].key) b.AddTx(tx) txHash = tx.Hash() }) @@ -561,7 +575,14 @@ func TestTracingWithOverrides(t *testing.T) { // 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, b.BaseFee(), nil), signer, accounts[0].key) + tx, _ := types.SignTx(types.NewTx(&types.LegacyTx{ + Nonce: uint64(i), + To: &accounts[1].addr, + Value: big.NewInt(1000), + Gas: params.TxGas, + GasPrice: b.BaseFee(), + Data: nil}), + signer, accounts[0].key) b.AddTx(tx) }) defer backend.chain.Stop() From d7702cfb131d597e23e311598df185312b8a8ab1 Mon Sep 17 00:00:00 2001 From: jsvisa Date: Wed, 29 Nov 2023 22:57:58 +0800 Subject: [PATCH 6/7] eth/tracers: add comment Signed-off-by: jsvisa --- eth/tracers/api.go | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/eth/tracers/api.go b/eth/tracers/api.go index c8cd0a511705..ebe998ce7fa9 100644 --- a/eth/tracers/api.go +++ b/eth/tracers/api.go @@ -895,6 +895,15 @@ func (api *API) TraceCall(ctx context.Context, args ethapi.TransactionArgs, bloc if config != nil && config.Reexec != nil { reexec = *config.Reexec } + + // If no transaction index is specified, the trace will be conducted on the state + // after executing the specified block. However, if a transaction index is provided, + // the trace will be conducted on the state after executing the specified transaction + // within the specified block. + // When "latest" is used without a transaction index, it means that the simulation is + // done on the state after the latest block execution. Conversely, when "latest" is used + // with a transaction index, it indicates simulating on the intermediate state after a + // specific transaction within the most recent block. if config != nil && config.TxIndex != nil { _, _, statedb, release, err = api.backend.StateAtTransaction(ctx, block, int(*config.TxIndex), reexec) } else { From 7eb6b0bc56d038b4bc9cecb27302713b067d3349 Mon Sep 17 00:00:00 2001 From: Sina Mahmoodi <1591639+s1na@users.noreply.github.com> Date: Thu, 30 Nov 2023 18:45:41 +0330 Subject: [PATCH 7/7] move comment --- eth/tracers/api.go | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/eth/tracers/api.go b/eth/tracers/api.go index ebe998ce7fa9..7c0028601dfb 100644 --- a/eth/tracers/api.go +++ b/eth/tracers/api.go @@ -864,6 +864,10 @@ func (api *API) TraceTransaction(ctx context.Context, hash common.Hash, config * // TraceCall lets you trace a given eth_call. It collects the structured logs // 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. +// If no transaction index is specified, the trace will be conducted on the state +// after executing the specified block. However, if a transaction index is provided, +// the trace will be conducted on the state after executing the specified transaction +// within the specified block. func (api *API) TraceCall(ctx context.Context, args ethapi.TransactionArgs, blockNrOrHash rpc.BlockNumberOrHash, config *TraceCallConfig) (interface{}, error) { // Try to retrieve the specified block var ( @@ -896,14 +900,6 @@ func (api *API) TraceCall(ctx context.Context, args ethapi.TransactionArgs, bloc reexec = *config.Reexec } - // If no transaction index is specified, the trace will be conducted on the state - // after executing the specified block. However, if a transaction index is provided, - // the trace will be conducted on the state after executing the specified transaction - // within the specified block. - // When "latest" is used without a transaction index, it means that the simulation is - // done on the state after the latest block execution. Conversely, when "latest" is used - // with a transaction index, it indicates simulating on the intermediate state after a - // specific transaction within the most recent block. if config != nil && config.TxIndex != nil { _, _, statedb, release, err = api.backend.StateAtTransaction(ctx, block, int(*config.TxIndex), reexec) } else {