Upgrade TxStart and TxEnd hooks

This commit is contained in:
Sina Mahmoodi 2023-06-28 13:28:53 +02:00
parent a7de17ed21
commit 378781654f
24 changed files with 181 additions and 93 deletions

View File

@ -183,6 +183,7 @@ func (pre *Prestate) Apply(vmConfig vm.Config, chainConfig *params.ChainConfig,
)
evm := vm.NewEVM(vmContext, txContext, statedb, chainConfig, vmConfig)
tracer.CaptureTxStart(tx)
// (ret []byte, usedGas uint64, failed bool, err error)
msgResult, err := core.ApplyMessage(evm, msg, gaspool)
if err != nil {
@ -231,6 +232,7 @@ func (pre *Prestate) Apply(vmConfig vm.Config, chainConfig *params.ChainConfig,
//receipt.BlockNumber
receipt.TransactionIndex = uint(txIndex)
receipts = append(receipts, receipt)
tracer.CaptureTxEnd(receipt)
}
txIndex++

View File

@ -87,7 +87,13 @@ func (p *StateProcessor) Process(block *types.Block, statedb *state.StateDB, cfg
return nil, nil, 0, fmt.Errorf("could not apply tx %d [%v]: %w", i, tx.Hash().Hex(), err)
}
statedb.SetTxContext(tx.Hash(), i)
if vmenv.Config.Tracer != nil {
vmenv.Config.Tracer.CaptureTxStart(tx)
}
receipt, err := applyTransaction(msg, p.config, gp, statedb, blockNumber, blockHash, tx, usedGas, vmenv)
if vmenv.Config.Tracer != nil {
vmenv.Config.Tracer.CaptureTxEnd(receipt)
}
if err != nil {
return nil, nil, 0, fmt.Errorf("could not apply tx %d [%v]: %w", i, tx.Hash().Hex(), err)
}

View File

@ -326,13 +326,6 @@ func (st *StateTransition) TransitionDb() (*ExecutionResult, error) {
return nil, err
}
if tracer := st.evm.Config.Tracer; tracer != nil {
tracer.CaptureTxStart(st.initialGas)
defer func() {
tracer.CaptureTxEnd(st.gasRemaining)
}()
}
var (
msg = st.msg
sender = vm.AccountRef(msg.From)

View File

@ -20,6 +20,7 @@ import (
"math/big"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core/types"
)
// EVMLogger is used to collect execution traces from an EVM transaction
@ -29,8 +30,8 @@ import (
// if you need to retain them beyond the current call.
type EVMLogger interface {
// Transaction level
CaptureTxStart(gasLimit uint64)
CaptureTxEnd(restGas uint64)
CaptureTxStart(tx *types.Transaction)
CaptureTxEnd(receipt *types.Receipt)
// Top call frame
CaptureStart(env *EVM, from common.Address, to common.Address, create bool, input []byte, gas uint64, value *big.Int)
CaptureEnd(output []byte, gasUsed uint64, err error)

View File

@ -414,6 +414,6 @@ func (b *EthAPIBackend) StateAtBlock(ctx context.Context, block *types.Block, re
return b.eth.StateAtBlock(ctx, block, reexec, base, readOnly, preferDisk)
}
func (b *EthAPIBackend) StateAtTransaction(ctx context.Context, block *types.Block, txIndex int, reexec uint64) (*core.Message, vm.BlockContext, *state.StateDB, tracers.StateReleaseFunc, error) {
func (b *EthAPIBackend) StateAtTransaction(ctx context.Context, block *types.Block, txIndex int, reexec uint64) (*types.Transaction, vm.BlockContext, *state.StateDB, tracers.StateReleaseFunc, error) {
return b.eth.stateAtTransaction(ctx, block, txIndex, reexec)
}

View File

@ -189,7 +189,7 @@ func (eth *Ethereum) StateAtBlock(ctx context.Context, block *types.Block, reexe
}
// stateAtTransaction returns the execution environment of a certain transaction.
func (eth *Ethereum) stateAtTransaction(ctx context.Context, block *types.Block, txIndex int, reexec uint64) (*core.Message, vm.BlockContext, *state.StateDB, tracers.StateReleaseFunc, error) {
func (eth *Ethereum) stateAtTransaction(ctx context.Context, block *types.Block, txIndex int, reexec uint64) (*types.Transaction, vm.BlockContext, *state.StateDB, tracers.StateReleaseFunc, error) {
// Short circuit if it's genesis block.
if block.NumberU64() == 0 {
return nil, vm.BlockContext{}, nil, nil, errors.New("no transaction in genesis")
@ -216,7 +216,7 @@ func (eth *Ethereum) stateAtTransaction(ctx context.Context, block *types.Block,
txContext := core.NewEVMTxContext(msg)
context := core.NewEVMBlockContext(block.Header(), eth.blockchain, nil)
if idx == txIndex {
return msg, context, statedb, release, nil
return tx, context, statedb, release, nil
}
// Not yet the searched for transaction, execute on top of the current state
vmenv := vm.NewEVM(context, txContext, statedb, eth.blockchain.Config(), vm.Config{})

View File

@ -23,6 +23,7 @@ import (
"encoding/json"
"errors"
"fmt"
"math/big"
"os"
"runtime"
"sync"
@ -87,7 +88,7 @@ type Backend interface {
Engine() consensus.Engine
ChainDb() ethdb.Database
StateAtBlock(ctx context.Context, block *types.Block, reexec uint64, base *state.StateDB, readOnly bool, preferDisk bool) (*state.StateDB, StateReleaseFunc, error)
StateAtTransaction(ctx context.Context, block *types.Block, txIndex int, reexec uint64) (*core.Message, vm.BlockContext, *state.StateDB, StateReleaseFunc, error)
StateAtTransaction(ctx context.Context, block *types.Block, txIndex int, reexec uint64) (*types.Transaction, vm.BlockContext, *state.StateDB, StateReleaseFunc, error)
}
// API is the collection of tracing APIs exposed over the private debugging endpoint.
@ -277,7 +278,7 @@ func (api *API) traceChain(start, end *types.Block, config *TraceConfig, closed
TxIndex: i,
TxHash: tx.Hash(),
}
res, err := api.traceTx(ctx, msg, txctx, blockCtx, task.statedb, config)
res, err := api.traceTx(ctx, tx, msg, txctx, blockCtx, task.statedb, config)
if err != nil {
task.results[i] = &txTraceResult{TxHash: tx.Hash(), Error: err.Error()}
log.Warn("Tracing failed", "hash", tx.Hash(), "block", task.block.NumberU64(), "err", err)
@ -612,7 +613,7 @@ func (api *API) traceBlock(ctx context.Context, block *types.Block, config *Trac
TxIndex: i,
TxHash: tx.Hash(),
}
res, err := api.traceTx(ctx, msg, txctx, blockCtx, statedb, config)
res, err := api.traceTx(ctx, tx, msg, txctx, blockCtx, statedb, config)
if err != nil {
return nil, err
}
@ -655,7 +656,7 @@ func (api *API) traceBlockParallel(ctx context.Context, block *types.Block, stat
TxIndex: task.index,
TxHash: txs[task.index].Hash(),
}
res, err := api.traceTx(ctx, msg, txctx, blockCtx, task.statedb, config)
res, err := api.traceTx(ctx, txs[task.index], msg, txctx, blockCtx, task.statedb, config)
if err != nil {
results[task.index] = &txTraceResult{TxHash: txs[task.index].Hash(), Error: err.Error()}
continue
@ -789,7 +790,9 @@ func (api *API) standardTraceBlockToFile(ctx context.Context, block *types.Block
// Execute the transaction and flush any traces to disk
vmenv := vm.NewEVM(vmctx, txContext, statedb, chainConfig, vmConf)
statedb.SetTxContext(tx.Hash(), i)
_, err = core.ApplyMessage(vmenv, msg, new(core.GasPool).AddGas(msg.GasLimit))
vmConf.Tracer.CaptureTxStart(tx)
vmRet, err := core.ApplyMessage(vmenv, msg, new(core.GasPool).AddGas(msg.GasLimit))
vmConf.Tracer.CaptureTxEnd(&types.Receipt{GasUsed: vmRet.UsedGas})
if writer != nil {
writer.Flush()
}
@ -846,11 +849,15 @@ func (api *API) TraceTransaction(ctx context.Context, hash common.Hash, config *
if err != nil {
return nil, err
}
msg, vmctx, statedb, release, err := api.backend.StateAtTransaction(ctx, block, int(index), reexec)
tx, vmctx, statedb, release, err := api.backend.StateAtTransaction(ctx, block, int(index), reexec)
if err != nil {
return nil, err
}
defer release()
msg, err := core.TransactionToMessage(tx, types.MakeSigner(api.backend.ChainConfig(), block.Number(), block.Time()), block.BaseFee())
if err != nil {
return nil, err
}
txctx := &Context{
BlockHash: blockHash,
@ -858,7 +865,7 @@ func (api *API) TraceTransaction(ctx context.Context, hash common.Hash, config *
TxIndex: int(index),
TxHash: hash,
}
return api.traceTx(ctx, msg, txctx, vmctx, statedb, config)
return api.traceTx(ctx, tx, msg, txctx, vmctx, statedb, config)
}
// TraceCall lets you trace a given eth_call. It collects the structured logs
@ -912,18 +919,21 @@ func (api *API) TraceCall(ctx context.Context, args ethapi.TransactionArgs, bloc
if err != nil {
return nil, err
}
tx, err := argsToTransaction(api.backend, &args, msg)
if err != nil {
return nil, err
}
var traceConfig *TraceConfig
if config != nil {
traceConfig = &config.TraceConfig
}
return api.traceTx(ctx, msg, new(Context), vmctx, statedb, traceConfig)
return api.traceTx(ctx, tx, msg, new(Context), vmctx, statedb, traceConfig)
}
// traceTx configures a new tracer according to the provided configuration, and
// executes the given message in the provided environment. The return value will
// be tracer dependent.
func (api *API) traceTx(ctx context.Context, message *core.Message, txctx *Context, vmctx vm.BlockContext, statedb *state.StateDB, config *TraceConfig) (interface{}, error) {
func (api *API) traceTx(ctx context.Context, tx *types.Transaction, message *core.Message, txctx *Context, vmctx vm.BlockContext, statedb *state.StateDB, config *TraceConfig) (interface{}, error) {
var (
tracer Tracer
err error
@ -962,9 +972,13 @@ func (api *API) traceTx(ctx context.Context, message *core.Message, txctx *Conte
// Call Prepare to clear out the statedb access list
statedb.SetTxContext(txctx.TxHash, txctx.TxIndex)
if _, err = core.ApplyMessage(vmenv, message, new(core.GasPool).AddGas(message.GasLimit)); err != nil {
tracer.CaptureTxStart(tx)
res, err := core.ApplyMessage(vmenv, message, new(core.GasPool).AddGas(message.GasLimit))
if err != nil {
return nil, fmt.Errorf("tracing failed: %w", err)
}
r := &types.Receipt{GasUsed: res.UsedGas}
tracer.CaptureTxEnd(r)
return tracer.GetResult()
}
@ -1023,3 +1037,54 @@ func overrideConfig(original *params.ChainConfig, override *params.ChainConfig)
return copy, canon
}
// argsToTransaction produces a Transaction object given call arguments.
// The `msg` field must be converted from the same arguments.
func argsToTransaction(b Backend, args *ethapi.TransactionArgs, msg *core.Message) (*types.Transaction, error) {
chainID := b.ChainConfig().ChainID
if args.ChainID != nil {
if have := (*big.Int)(args.ChainID); have.Cmp(chainID) != 0 {
return nil, fmt.Errorf("chainId does not match node's (have=%v, want=%v)", have, chainID)
}
}
var data types.TxData
switch {
case args.MaxFeePerGas != nil:
al := types.AccessList{}
if args.AccessList != nil {
al = *args.AccessList
}
data = &types.DynamicFeeTx{
To: args.To,
ChainID: chainID,
Nonce: msg.Nonce,
Gas: msg.GasLimit,
GasFeeCap: msg.GasFeeCap,
GasTipCap: msg.GasTipCap,
Value: msg.Value,
Data: msg.Data,
AccessList: al,
}
case args.AccessList != nil:
data = &types.AccessListTx{
To: args.To,
ChainID: chainID,
Nonce: msg.Nonce,
Gas: msg.GasLimit,
GasPrice: msg.GasPrice,
Value: msg.Value,
Data: msg.Data,
AccessList: *args.AccessList,
}
default:
data = &types.LegacyTx{
To: args.To,
Nonce: msg.Nonce,
Gas: msg.GasLimit,
GasPrice: msg.GasPrice,
Value: msg.Value,
Data: msg.Data,
}
}
return types.NewTx(data), nil
}

View File

@ -156,7 +156,7 @@ func (b *testBackend) StateAtBlock(ctx context.Context, block *types.Block, reex
return statedb, release, nil
}
func (b *testBackend) StateAtTransaction(ctx context.Context, block *types.Block, txIndex int, reexec uint64) (*core.Message, vm.BlockContext, *state.StateDB, StateReleaseFunc, error) {
func (b *testBackend) StateAtTransaction(ctx context.Context, block *types.Block, txIndex int, reexec uint64) (*types.Transaction, vm.BlockContext, *state.StateDB, StateReleaseFunc, error) {
parent := b.chain.GetBlock(block.ParentHash(), block.NumberU64()-1)
if parent == nil {
return nil, vm.BlockContext{}, nil, nil, errBlockNotFound
@ -175,7 +175,7 @@ func (b *testBackend) StateAtTransaction(ctx context.Context, block *types.Block
txContext := core.NewEVMTxContext(msg)
context := core.NewEVMBlockContext(block.Header(), b.chain, nil)
if idx == txIndex {
return msg, context, statedb, release, nil
return tx, context, statedb, release, nil
}
vmenv := vm.NewEVM(context, txContext, statedb, b.chainConfig, vm.Config{})
if _, err := core.ApplyMessage(vmenv, msg, new(core.GasPool).AddGas(tx.Gas())); err != nil {

View File

@ -18,6 +18,7 @@ package tracetest
import (
"encoding/json"
"fmt"
"math/big"
"os"
"path/filepath"
@ -31,6 +32,7 @@ import (
"github.com/ethereum/go-ethereum/core/rawdb"
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/core/vm"
"github.com/ethereum/go-ethereum/crypto"
"github.com/ethereum/go-ethereum/eth/tracers"
"github.com/ethereum/go-ethereum/params"
"github.com/ethereum/go-ethereum/rlp"
@ -148,10 +150,12 @@ func testCallTracer(tracerName string, dirPath string, t *testing.T) {
if err != nil {
t.Fatalf("failed to prepare transaction for tracing: %v", err)
}
tracer.CaptureTxStart(tx)
vmRet, err := core.ApplyMessage(evm, msg, new(core.GasPool).AddGas(tx.Gas()))
if err != nil {
t.Fatalf("failed to execute transaction: %v", err)
}
tracer.CaptureTxEnd(&types.Receipt{GasUsed: vmRet.UsedGas})
// Retrieve the trace result and compare against the expected.
res, err := tracer.GetResult()
if err != nil {
@ -261,8 +265,12 @@ func benchTracer(tracerName string, test *callTracerTest, b *testing.B) {
func TestInternals(t *testing.T) {
var (
config = params.AllEthashProtocolChanges
to = common.HexToAddress("0x00000000000000000000000000000000deadbeef")
origin = common.HexToAddress("0x00000000000000000000000000000000feed")
originHex = "0x71562b71999873db5b286df957af199ec94617f7"
origin = common.HexToAddress(originHex)
signer = types.LatestSigner(config)
key, _ = crypto.HexToECDSA("b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291")
txContext = vm.TxContext{
Origin: origin,
GasPrice: big.NewInt(1),
@ -302,13 +310,13 @@ func TestInternals(t *testing.T) {
byte(vm.CALL),
},
tracer: mkTracer("callTracer", nil),
want: `{"from":"0x000000000000000000000000000000000000feed","gas":"0xc350","gasUsed":"0x54d8","to":"0x00000000000000000000000000000000deadbeef","input":"0x","calls":[{"from":"0x00000000000000000000000000000000deadbeef","gas":"0x6cbf","gasUsed":"0x0","to":"0x00000000000000000000000000000000000000ff","input":"0x","value":"0x0","type":"CALL"}],"value":"0x0","type":"CALL"}`,
want: fmt.Sprintf(`{"from":"%s","gas":"0xc350","gasUsed":"0x54d8","to":"0x00000000000000000000000000000000deadbeef","input":"0x","calls":[{"from":"0x00000000000000000000000000000000deadbeef","gas":"0x6cbf","gasUsed":"0x0","to":"0x00000000000000000000000000000000000000ff","input":"0x","value":"0x0","type":"CALL"}],"value":"0x0","type":"CALL"}`, originHex),
},
{
name: "Stack depletion in LOG0",
code: []byte{byte(vm.LOG3)},
tracer: mkTracer("callTracer", json.RawMessage(`{ "withLog": true }`)),
want: `{"from":"0x000000000000000000000000000000000000feed","gas":"0xc350","gasUsed":"0xc350","to":"0x00000000000000000000000000000000deadbeef","input":"0x","error":"stack underflow (0 \u003c=\u003e 5)","value":"0x0","type":"CALL"}`,
want: fmt.Sprintf(`{"from":"%s","gas":"0xc350","gasUsed":"0xc350","to":"0x00000000000000000000000000000000deadbeef","input":"0x","error":"stack underflow (0 \u003c=\u003e 5)","value":"0x0","type":"CALL"}`, originHex),
},
{
name: "Mem expansion in LOG0",
@ -321,7 +329,7 @@ func TestInternals(t *testing.T) {
byte(vm.LOG0),
},
tracer: mkTracer("callTracer", json.RawMessage(`{ "withLog": true }`)),
want: `{"from":"0x000000000000000000000000000000000000feed","gas":"0xc350","gasUsed":"0x5b9e","to":"0x00000000000000000000000000000000deadbeef","input":"0x","logs":[{"address":"0x00000000000000000000000000000000deadbeef","topics":[],"data":"0x000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"}],"value":"0x0","type":"CALL"}`,
want: fmt.Sprintf(`{"from":"%s","gas":"0xc350","gasUsed":"0x5b9e","to":"0x00000000000000000000000000000000deadbeef","input":"0x","logs":[{"address":"0x00000000000000000000000000000000deadbeef","topics":[],"data":"0x000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"}],"value":"0x0","type":"CALL"}`, originHex),
},
{
// Leads to OOM on the prestate tracer
@ -340,7 +348,7 @@ func TestInternals(t *testing.T) {
byte(vm.LOG0),
},
tracer: mkTracer("prestateTracer", json.RawMessage(`{ "withLog": true }`)),
want: `{"0x0000000000000000000000000000000000000000":{"balance":"0x0"},"0x000000000000000000000000000000000000feed":{"balance":"0x1c6bf52640350"},"0x00000000000000000000000000000000deadbeef":{"balance":"0x0","code":"0x6001600052600164ffffffffff60016000f560ff6000a0"}}`,
want: fmt.Sprintf(`{"0x0000000000000000000000000000000000000000":{"balance":"0x0"},"0x00000000000000000000000000000000deadbeef":{"balance":"0x0","code":"0x6001600052600164ffffffffff60016000f560ff6000a0"},"%s":{"balance":"0x1c6bf52640350"}}`, originHex),
},
} {
_, statedb := tests.MakePreState(rawdb.NewMemoryDatabase(),
@ -353,20 +361,25 @@ func TestInternals(t *testing.T) {
},
}, false)
evm := vm.NewEVM(context, txContext, statedb, params.MainnetChainConfig, vm.Config{Tracer: tc.tracer})
msg := &core.Message{
To: &to,
From: origin,
Value: big.NewInt(0),
GasLimit: 50000,
GasPrice: big.NewInt(0),
GasFeeCap: big.NewInt(0),
GasTipCap: big.NewInt(0),
SkipAccountChecks: false,
tx, err := types.SignNewTx(key, signer, &types.LegacyTx{
To: &to,
Value: big.NewInt(0),
Gas: 50000,
GasPrice: big.NewInt(0),
})
if err != nil {
t.Fatalf("test %v: failed to sign transaction: %v", tc.name, err)
}
st := core.NewStateTransition(evm, msg, new(core.GasPool).AddGas(msg.GasLimit))
if _, err := st.TransitionDb(); err != nil {
msg, err := core.TransactionToMessage(tx, signer, big.NewInt(0))
if err != nil {
t.Fatalf("test %v: failed to create message: %v", tc.name, err)
}
tc.tracer.CaptureTxStart(tx)
vmRet, err := core.ApplyMessage(evm, msg, new(core.GasPool).AddGas(tx.Gas()))
if err != nil {
t.Fatalf("test %v: failed to execute transaction: %v", tc.name, err)
}
tc.tracer.CaptureTxEnd(&types.Receipt{GasUsed: vmRet.UsedGas})
// Retrieve the trace result and compare against the expected
res, err := tc.tracer.GetResult()
if err != nil {

View File

@ -113,11 +113,12 @@ func flatCallTracerTestRunner(tracerName string, filename string, dirPath string
if err != nil {
return fmt.Errorf("failed to prepare transaction for tracing: %v", err)
}
st := core.NewStateTransition(evm, msg, new(core.GasPool).AddGas(tx.Gas()))
if _, err = st.TransitionDb(); err != nil {
tracer.CaptureTxStart(tx)
vmRet, err := core.ApplyMessage(evm, msg, new(core.GasPool).AddGas(tx.Gas()))
if err != nil {
return fmt.Errorf("failed to execute transaction: %v", err)
}
tracer.CaptureTxEnd(&types.Receipt{GasUsed: vmRet.UsedGas})
// Retrieve the trace result and compare against the etalon
res, err := tracer.GetResult()

View File

@ -119,10 +119,12 @@ func testPrestateDiffTracer(tracerName string, dirPath string, t *testing.T) {
if err != nil {
t.Fatalf("failed to prepare transaction for tracing: %v", err)
}
st := core.NewStateTransition(evm, msg, new(core.GasPool).AddGas(tx.Gas()))
if _, err = st.TransitionDb(); err != nil {
tracer.CaptureTxStart(tx)
vmRet, err := core.ApplyMessage(evm, msg, new(core.GasPool).AddGas(tx.Gas()))
if err != nil {
t.Fatalf("failed to execute transaction: %v", err)
}
tracer.CaptureTxEnd(&types.Receipt{GasUsed: vmRet.UsedGas})
// Retrieve the trace result and compare against the expected
res, err := tracer.GetResult()
if err != nil {

View File

@ -23,6 +23,7 @@ import (
"math/big"
"github.com/dop251/goja"
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/common/hexutil"
@ -214,14 +215,14 @@ func newJsTracer(code string, ctx *tracers.Context, cfg json.RawMessage) (tracer
// CaptureTxStart implements the Tracer interface and is invoked at the beginning of
// transaction processing.
func (t *jsTracer) CaptureTxStart(gasLimit uint64) {
t.gasLimit = gasLimit
func (t *jsTracer) CaptureTxStart(tx *types.Transaction) {
t.gasLimit = tx.Gas()
}
// CaptureTxEnd implements the Tracer interface and is invoked at the end of
// transaction processing.
func (t *jsTracer) CaptureTxEnd(restGas uint64) {
t.ctx["gasUsed"] = t.vm.ToValue(t.gasLimit - restGas)
func (t *jsTracer) CaptureTxEnd(receipt *types.Receipt) {
t.ctx["gasUsed"] = t.vm.ToValue(receipt.GasUsed)
}
// CaptureStart implements the Tracer interface to initialize the tracing operation.

View File

@ -26,6 +26,7 @@ import (
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core/state"
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/core/vm"
"github.com/ethereum/go-ethereum/eth/tracers"
"github.com/ethereum/go-ethereum/params"
@ -73,12 +74,12 @@ func runTrace(tracer tracers.Tracer, vmctx *vmContext, chaincfg *params.ChainCon
contract.Code = contractCode
}
tracer.CaptureTxStart(gasLimit)
tracer.CaptureTxStart(types.NewTx(&types.LegacyTx{Gas: gasLimit}))
tracer.CaptureStart(env, contract.Caller(), contract.Address(), false, []byte{}, startGas, value)
ret, err := env.Interpreter().Run(contract, []byte{}, false)
tracer.CaptureEnd(ret, startGas-contract.Gas, err)
// Rest gas assumes no refund
tracer.CaptureTxEnd(contract.Gas)
tracer.CaptureTxEnd(&types.Receipt{GasUsed: gasLimit - contract.Gas})
if err != nil {
return nil, err
}

View File

@ -172,9 +172,9 @@ func (*AccessListTracer) CaptureEnter(typ vm.OpCode, from common.Address, to com
func (*AccessListTracer) CaptureExit(output []byte, gasUsed uint64, err error) {}
func (*AccessListTracer) CaptureTxStart(gasLimit uint64) {}
func (*AccessListTracer) CaptureTxStart(tx *types.Transaction) {}
func (*AccessListTracer) CaptureTxEnd(restGas uint64) {}
func (*AccessListTracer) CaptureTxEnd(receipt *types.Receipt) {}
// AccessList returns the current accesslist maintained by the tracer.
func (a *AccessListTracer) AccessList() types.AccessList {

View File

@ -109,12 +109,11 @@ type StructLogger struct {
cfg Config
env *vm.EVM
storage map[common.Address]Storage
logs []StructLog
output []byte
err error
gasLimit uint64
usedGas uint64
storage map[common.Address]Storage
logs []StructLog
output []byte
err error
usedGas uint64
interrupt atomic.Bool // Atomic flag to signal execution interruption
reason error // Textual reason for the interruption
@ -266,12 +265,10 @@ func (l *StructLogger) Stop(err error) {
l.interrupt.Store(true)
}
func (l *StructLogger) CaptureTxStart(gasLimit uint64) {
l.gasLimit = gasLimit
}
func (l *StructLogger) CaptureTxStart(tx *types.Transaction) {}
func (l *StructLogger) CaptureTxEnd(restGas uint64) {
l.usedGas = l.gasLimit - restGas
func (l *StructLogger) CaptureTxEnd(receipt *types.Receipt) {
l.usedGas = receipt.GasUsed
}
// StructLogs returns the captured log entries.
@ -403,9 +400,9 @@ func (t *mdLogger) CaptureEnter(typ vm.OpCode, from common.Address, to common.Ad
func (t *mdLogger) CaptureExit(output []byte, gasUsed uint64, err error) {}
func (*mdLogger) CaptureTxStart(gasLimit uint64) {}
func (*mdLogger) CaptureTxStart(tx *types.Transaction) {}
func (*mdLogger) CaptureTxEnd(restGas uint64) {}
func (*mdLogger) CaptureTxEnd(receipt *types.Receipt) {}
// ExecutionResult groups all structured logs emitted by the EVM
// while replaying a transaction in debug mode as well as transaction

View File

@ -23,6 +23,7 @@ import (
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/common/math"
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/core/vm"
)
@ -102,6 +103,6 @@ func (l *JSONLogger) CaptureEnter(typ vm.OpCode, from common.Address, to common.
func (l *JSONLogger) CaptureExit(output []byte, gasUsed uint64, err error) {}
func (l *JSONLogger) CaptureTxStart(gasLimit uint64) {}
func (l *JSONLogger) CaptureTxStart(tx *types.Transaction) {}
func (l *JSONLogger) CaptureTxEnd(restGas uint64) {}
func (l *JSONLogger) CaptureTxEnd(receipt *types.Receipt) {}

View File

@ -25,6 +25,7 @@ import (
"github.com/ethereum/go-ethereum/accounts/abi"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/common/hexutil"
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/core/vm"
"github.com/ethereum/go-ethereum/eth/tracers"
)
@ -233,12 +234,12 @@ func (t *callTracer) CaptureExit(output []byte, gasUsed uint64, err error) {
t.callstack[size-1].Calls = append(t.callstack[size-1].Calls, call)
}
func (t *callTracer) CaptureTxStart(gasLimit uint64) {
t.gasLimit = gasLimit
func (t *callTracer) CaptureTxStart(tx *types.Transaction) {
t.gasLimit = tx.Gas()
}
func (t *callTracer) CaptureTxEnd(restGas uint64) {
t.callstack[0].GasUsed = t.gasLimit - restGas
func (t *callTracer) CaptureTxEnd(receipt *types.Receipt) {
t.callstack[0].GasUsed = receipt.GasUsed
if t.config.WithLog {
// Logs are not emitted when the call fails
clearFailedLogs(&t.callstack[0], false)

View File

@ -25,6 +25,7 @@ import (
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/common/hexutil"
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/core/vm"
"github.com/ethereum/go-ethereum/eth/tracers"
)
@ -202,12 +203,12 @@ func (t *flatCallTracer) CaptureExit(output []byte, gasUsed uint64, err error) {
}
}
func (t *flatCallTracer) CaptureTxStart(gasLimit uint64) {
t.tracer.CaptureTxStart(gasLimit)
func (t *flatCallTracer) CaptureTxStart(tx *types.Transaction) {
t.tracer.CaptureTxStart(tx)
}
func (t *flatCallTracer) CaptureTxEnd(restGas uint64) {
t.tracer.CaptureTxEnd(restGas)
func (t *flatCallTracer) CaptureTxEnd(receipt *types.Receipt) {
t.tracer.CaptureTxEnd(receipt)
}
// GetResult returns an empty json object.

View File

@ -21,6 +21,7 @@ import (
"math/big"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/core/vm"
"github.com/ethereum/go-ethereum/eth/tracers"
)
@ -115,15 +116,15 @@ func (t *muxTracer) CaptureExit(output []byte, gasUsed uint64, err error) {
}
}
func (t *muxTracer) CaptureTxStart(gasLimit uint64) {
func (t *muxTracer) CaptureTxStart(tx *types.Transaction) {
for _, t := range t.tracers {
t.CaptureTxStart(gasLimit)
t.CaptureTxStart(tx)
}
}
func (t *muxTracer) CaptureTxEnd(restGas uint64) {
func (t *muxTracer) CaptureTxEnd(receipt *types.Receipt) {
for _, t := range t.tracers {
t.CaptureTxEnd(restGas)
t.CaptureTxEnd(receipt)
}
}

View File

@ -24,6 +24,7 @@ import (
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/common/hexutil"
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/core/vm"
"github.com/ethereum/go-ethereum/crypto"
"github.com/ethereum/go-ethereum/eth/tracers"
@ -174,11 +175,11 @@ func (t *prestateTracer) CaptureState(pc uint64, op vm.OpCode, gas, cost uint64,
}
}
func (t *prestateTracer) CaptureTxStart(gasLimit uint64) {
t.gasLimit = gasLimit
func (t *prestateTracer) CaptureTxStart(tx *types.Transaction) {
t.gasLimit = tx.Gas()
}
func (t *prestateTracer) CaptureTxEnd(restGas uint64) {
func (t *prestateTracer) CaptureTxEnd(receipt *types.Receipt) {
if !t.config.DiffMode {
return
}

View File

@ -21,6 +21,7 @@ import (
"math/big"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/core/vm"
)
@ -68,9 +69,9 @@ func (t *NoopTracer) CaptureEnter(typ vm.OpCode, from common.Address, to common.
func (t *NoopTracer) CaptureExit(output []byte, gasUsed uint64, err error) {
}
func (*NoopTracer) CaptureTxStart(gasLimit uint64) {}
func (*NoopTracer) CaptureTxStart(tx *types.Transaction) {}
func (*NoopTracer) CaptureTxEnd(restGas uint64) {}
func (*NoopTracer) CaptureTxEnd(receipt *types.Receipt) {}
// GetResult returns an empty json object.
func (t *NoopTracer) GetResult() (json.RawMessage, error) {

View File

@ -49,13 +49,13 @@ func (p *Printer) CaptureExit(output []byte, gasUsed uint64, err error) {
fmt.Printf("CaptureExit: output=%v, gasUsed=%v, err=%v\n", output, gasUsed, err)
}
func (p *Printer) CaptureTxStart(gasLimit uint64) {
fmt.Printf("CaptureTxStart: gasLimit=%v\n", gasLimit)
func (p *Printer) CaptureTxStart(tx *types.Transaction) {
fmt.Printf("CaptureTxStart: tx=%v\n", tx)
}
func (p *Printer) CaptureTxEnd(restGas uint64) {
fmt.Printf("CaptureTxEnd: restGas=%v\n", restGas)
func (p *Printer) CaptureTxEnd(receipt *types.Receipt) {
fmt.Printf("CaptureTxEnd: receipt=%v\n", receipt)
}
func (p *Printer) CaptureBlockStart(b *types.Block) {

View File

@ -333,6 +333,6 @@ func (b *LesApiBackend) StateAtBlock(ctx context.Context, block *types.Block, re
return b.eth.stateAtBlock(ctx, block, reexec)
}
func (b *LesApiBackend) StateAtTransaction(ctx context.Context, block *types.Block, txIndex int, reexec uint64) (*core.Message, vm.BlockContext, *state.StateDB, tracers.StateReleaseFunc, error) {
func (b *LesApiBackend) StateAtTransaction(ctx context.Context, block *types.Block, txIndex int, reexec uint64) (*types.Transaction, vm.BlockContext, *state.StateDB, tracers.StateReleaseFunc, error) {
return b.eth.stateAtTransaction(ctx, block, txIndex, reexec)
}

View File

@ -39,7 +39,7 @@ func (leth *LightEthereum) stateAtBlock(ctx context.Context, block *types.Block,
}
// stateAtTransaction returns the execution environment of a certain transaction.
func (leth *LightEthereum) stateAtTransaction(ctx context.Context, block *types.Block, txIndex int, reexec uint64) (*core.Message, vm.BlockContext, *state.StateDB, tracers.StateReleaseFunc, error) {
func (leth *LightEthereum) stateAtTransaction(ctx context.Context, block *types.Block, txIndex int, reexec uint64) (*types.Transaction, vm.BlockContext, *state.StateDB, tracers.StateReleaseFunc, error) {
// Short circuit if it's genesis block.
if block.NumberU64() == 0 {
return nil, vm.BlockContext{}, nil, nil, errors.New("no transaction in genesis")
@ -65,7 +65,7 @@ func (leth *LightEthereum) stateAtTransaction(ctx context.Context, block *types.
context := core.NewEVMBlockContext(block.Header(), leth.blockchain, nil)
statedb.SetTxContext(tx.Hash(), idx)
if idx == txIndex {
return msg, context, statedb, release, nil
return tx, context, statedb, release, nil
}
// Not yet the searched for transaction, execute on top of the current state
vmenv := vm.NewEVM(context, txContext, statedb, leth.blockchain.Config(), vm.Config{})