Backported a bunch more of eth/tracers

This commit is contained in:
Matthieu Vachon 2024-03-15 14:06:11 -04:00
parent 17fcdfaadf
commit f596461a23
21 changed files with 558 additions and 88 deletions

View file

@ -23,6 +23,8 @@ import (
"math/big"
"reflect"
"strconv"
"github.com/holiman/uint256"
)
var (
@ -30,6 +32,7 @@ var (
bigT = reflect.TypeOf((*Big)(nil))
uintT = reflect.TypeOf(Uint(0))
uint64T = reflect.TypeOf(Uint64(0))
u256T = reflect.TypeOf((*uint256.Int)(nil))
)
// Bytes marshals/unmarshals as a JSON string with 0x prefix.
@ -91,6 +94,48 @@ func (b *Bytes) UnmarshalGraphQL(input interface{}) error {
return err
}
// U256 marshals/unmarshals as a JSON string with 0x prefix.
// The zero value marshals as "0x0".
type U256 uint256.Int
// MarshalText implements encoding.TextMarshaler
func (b U256) MarshalText() ([]byte, error) {
u256 := (*uint256.Int)(&b)
return []byte(u256.Hex()), nil
}
// UnmarshalJSON implements json.Unmarshaler.
func (b *U256) UnmarshalJSON(input []byte) error {
// The uint256.Int.UnmarshalJSON method accepts "dec", "0xhex"; we must be
// more strict, hence we check string and invoke SetFromHex directly.
if !isString(input) {
return errNonString(u256T)
}
// The hex decoder needs to accept empty string ("") as '0', which uint256.Int
// would reject.
if len(input) == 2 {
(*uint256.Int)(b).Clear()
return nil
}
err := (*uint256.Int)(b).SetFromHex(string(input[1 : len(input)-1]))
if err != nil {
return &json.UnmarshalTypeError{Value: err.Error(), Type: u256T}
}
return nil
}
// UnmarshalText implements encoding.TextUnmarshaler
func (b *U256) UnmarshalText(input []byte) error {
// The uint256.Int.UnmarshalText method accepts "dec", "0xhex"; we must be
// more strict, hence we check string and invoke SetFromHex directly.
return (*uint256.Int)(b).SetFromHex(string(input))
}
// String returns the hex encoding of b.
func (b *U256) String() string {
return (*uint256.Int)(b).Hex()
}
// UnmarshalFixedJSON decodes the input as a string with 0x prefix. The length of out
// determines the required input length. This function is commonly used to implement the
// UnmarshalJSON method for fixed-size types.

View file

@ -43,6 +43,12 @@ import (
var errGenesisNoConfig = errors.New("genesis has no chain configuration")
// use types.GenesisAccount instead (Deprecation not ported from latest Geth for now).
type GenesisAccount = types.Account
// use types.GenesisAlloc instead (Deprecation not ported from latest Geth for now).
type GenesisAlloc = types.GenesisAlloc
// Genesis specifies the header fields, state of a genesis block. It also defines hard
// fork switch-over blocks through the chain configuration.
type Genesis struct {
@ -104,12 +110,6 @@ func ReadGenesis(db ethdb.Database) (*Genesis, error) {
return &genesis, nil
}
// Deprecated: use types.GenesisAccount instead.
type GenesisAccount = types.Account
// Deprecated: use types.GenesisAlloc instead.
type GenesisAlloc = types.GenesisAlloc
// hashAlloc computes the state root according to the genesis specification.
func hashAlloc(ga *types.GenesisAlloc) (common.Hash, error) {
// Create an ephemeral in-memory database for computing hash,

View file

@ -49,14 +49,15 @@ type DumpCollector interface {
// DumpAccount represents an account in the state.
type DumpAccount struct {
Balance string `json:"balance"`
Nonce uint64 `json:"nonce"`
Root hexutil.Bytes `json:"root"`
CodeHash hexutil.Bytes `json:"codeHash"`
Code hexutil.Bytes `json:"code,omitempty"`
Storage map[common.Hash]string `json:"storage,omitempty"`
Address *common.Address `json:"address,omitempty"` // Address only present in iterative (line-by-line) mode
AddressHash hexutil.Bytes `json:"key,omitempty"` // If we don't have address, we can output the key
Balance string `json:"balance"`
Nonce uint64 `json:"nonce"`
Root hexutil.Bytes `json:"root"`
CodeHash hexutil.Bytes `json:"codeHash"`
Code hexutil.Bytes `json:"code,omitempty"`
Storage map[common.Hash]string `json:"storage,omitempty"`
Address *common.Address `json:"address,omitempty"` // Address only present in iterative (line-by-line) mode
SecureKey hexutil.Bytes `json:"key,omitempty"` // If we don't have address, we can output the key
}
// Dump represents the full dump in a collected format, as one large map.
@ -104,13 +105,14 @@ type iterativeDump struct {
// OnAccount implements DumpCollector interface
func (d iterativeDump) OnAccount(addr *common.Address, account DumpAccount) {
dumpAccount := &DumpAccount{
Balance: account.Balance,
Nonce: account.Nonce,
Root: account.Root,
CodeHash: account.CodeHash,
Code: account.Code,
Storage: account.Storage,
Address: addr,
Balance: account.Balance,
Nonce: account.Nonce,
Root: account.Root,
CodeHash: account.CodeHash,
Code: account.Code,
Storage: account.Storage,
SecureKey: account.SecureKey,
Address: addr,
}
d.Encode(dumpAccount)
}
@ -149,10 +151,11 @@ func (s *StateDB) DumpToCollector(c DumpCollector, conf *DumpConfig) (nextKey []
panic(err)
}
account := DumpAccount{
Balance: data.Balance.String(),
Nonce: data.Nonce,
Root: data.Root[:],
CodeHash: data.CodeHash,
Balance: data.Balance.String(),
Nonce: data.Nonce,
Root: data.Root[:],
CodeHash: data.CodeHash,
SecureKey: it.Key,
}
var (
addrBytes = s.trie.GetKey(it.Key)

View file

@ -412,6 +412,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

@ -216,7 +216,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")
@ -243,7 +243,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

@ -88,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.
@ -166,6 +166,7 @@ type TraceCallConfig struct {
TraceConfig
StateOverrides *ethapi.StateOverride
BlockOverrides *ethapi.BlockOverrides
TxIndex *hexutil.Uint
}
// StdTraceConfig holds extra parameters to standard-json trace functions.
@ -227,7 +228,7 @@ func (api *API) TraceChain(ctx context.Context, start, end rpc.BlockNumber, conf
}
sub := notifier.CreateSubscription()
resCh := api.traceChain(from, to, config, notifier.Closed())
resCh := api.traceChain(from, to, config, sub.Err())
go func() {
for result := range resCh {
notifier.Notify(sub.ID, result)
@ -241,7 +242,7 @@ func (api *API) TraceChain(ctx context.Context, start, end rpc.BlockNumber, conf
// the end block but excludes the start one. The return value will be one item per
// transaction, dependent on the requested tracer.
// The tracing procedure should be aborted in case the closed signal is received.
func (api *API) traceChain(start, end *types.Block, config *TraceConfig, closed <-chan interface{}) chan *blockTraceResult {
func (api *API) traceChain(start, end *types.Block, config *TraceConfig, closed <-chan error) chan *blockTraceResult {
reexec := defaultTraceReexec
if config != nil && config.Reexec != nil {
reexec = *config.Reexec
@ -278,7 +279,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)
@ -610,7 +611,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
}
@ -627,7 +628,6 @@ func (api *API) traceBlockParallel(ctx context.Context, block *types.Block, stat
var (
txs = block.Transactions()
blockHash = block.Hash()
blockCtx = core.NewEVMBlockContext(block.Header(), api.chainContext(ctx), nil)
signer = types.MakeSigner(api.backend.ChainConfig(), block.Number(), block.Time())
results = make([]*txTraceResult, len(txs))
pend sync.WaitGroup
@ -650,7 +650,12 @@ 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)
// Reconstruct the block context for each transaction
// as the GetHash function of BlockContext is not safe for
// concurrent use.
// See: https://github.com/ethereum/go-ethereum/issues/29114
blockCtx := core.NewEVMBlockContext(block.Header(), api.chainContext(ctx), nil)
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
@ -662,6 +667,7 @@ func (api *API) traceBlockParallel(ctx context.Context, block *types.Block, stat
// Feed the transactions into the tracers and return
var failed error
blockCtx := core.NewEVMBlockContext(block.Header(), api.chainContext(ctx), nil)
txloop:
for i, tx := range txs {
// Send the trace task over for execution
@ -843,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 := &directory.Context{
BlockHash: blockHash,
@ -855,17 +865,23 @@ 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
// 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 (
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)
@ -890,7 +906,12 @@ 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
}
@ -905,26 +926,30 @@ func (api *API) TraceCall(ctx context.Context, args ethapi.TransactionArgs, bloc
config.BlockOverrides.Apply(&vmctx)
}
// Execute the trace
msg, err := args.ToMessage(api.backend.RPCGasCap(), block.BaseFee())
msg, err := args.ToMessage(api.backend.RPCGasCap(), vmctx.BaseFee)
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(directory.Context), vmctx, statedb, traceConfig)
return api.traceTx(ctx, tx, msg, new(directory.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 *directory.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 *directory.Context, vmctx vm.BlockContext, statedb *state.StateDB, config *TraceConfig) (interface{}, error) {
var (
tracer *directory.Tracer
err error
timeout = defaultTraceTimeout
usedGas uint64
)
if config == nil {
config = &TraceConfig{}
@ -959,7 +984,8 @@ func (api *API) traceTx(ctx context.Context, message *core.Message, txctx *direc
// 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 {
_, err = core.ApplyTransactionWithEVM(message, api.backend.ChainConfig(), new(core.GasPool).AddGas(message.GasLimit), statedb, vmctx.BlockNumber, txctx.BlockHash, tx, &usedGas, vmenv)
if err != nil {
return nil, fmt.Errorf("tracing failed: %w", err)
}
return tracer.GetResult()
@ -1024,3 +1050,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

@ -155,7 +155,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
@ -174,7 +174,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 {
@ -192,7 +192,7 @@ func TestTraceCall(t *testing.T) {
accounts := newAccounts(3)
genesis := &core.Genesis{
Config: params.TestChainConfig,
Alloc: core.GenesisAlloc{
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)},
@ -200,13 +200,51 @@ 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.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.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.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++
}
})
uintPtr := func(i int) *hexutil.Uint { x := hexutil.Uint(i); return &x }
defer backend.teardown()
api := NewAPI(backend)
var testSuite = []struct {
@ -240,6 +278,51 @@ func TestTraceCall(t *testing.T) {
expectErr: nil,
expect: `{"gas":21000,"failed":false,"returnValue":"","structLogs":[]}`,
},
// Upon the last state, default to the post block's state
{
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("tracing failed: insufficient funds for gas * price + value: address %s have 1000000000000000000 want 1000000000000000100", accounts[2].addr),
},
// Before the target 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(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":[]}`,
},
// Standard JSON trace upon the non-existent block, error expects
{
blockNumber: rpc.BlockNumber(genBlocks + 1),
@ -297,8 +380,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, git %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 {
@ -327,7 +410,7 @@ func TestTraceTransaction(t *testing.T) {
accounts := newAccounts(2)
genesis := &core.Genesis{
Config: params.TestChainConfig,
Alloc: core.GenesisAlloc{
Alloc: types.GenesisAlloc{
accounts[0].addr: {Balance: big.NewInt(params.Ether)},
accounts[1].addr: {Balance: big.NewInt(params.Ether)},
},
@ -338,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()
})
@ -375,7 +465,7 @@ func TestTraceBlock(t *testing.T) {
accounts := newAccounts(3)
genesis := &core.Genesis{
Config: params.TestChainConfig,
Alloc: core.GenesisAlloc{
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)},
@ -388,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()
})
@ -458,7 +555,7 @@ func TestTracingWithOverrides(t *testing.T) {
storageAccount := common.Address{0x13, 37}
genesis := &core.Genesis{
Config: params.TestChainConfig,
Alloc: core.GenesisAlloc{
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)},
@ -478,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()
@ -819,7 +923,7 @@ func TestTraceChain(t *testing.T) {
accounts := newAccounts(3)
genesis := &core.Genesis{
Config: params.TestChainConfig,
Alloc: core.GenesisAlloc{
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)},

View file

@ -33,9 +33,9 @@ func TestMemCopying(t *testing.T) {
{0, 100, 0, "", 0}, // No need to pad (0 size)
{100, 50, 100, "", 100}, // Should pad 100-150
{100, 50, 5, "", 5}, // Wanted range fully within memory
{100, -50, 0, "offset or size must not be negative", 0}, // Errror
{0, 1, 1024*1024 + 1, "reached limit for padding memory slice: 1048578", 0}, // Errror
{10, 0, 1024*1024 + 100, "reached limit for padding memory slice: 1048666", 0}, // Errror
{100, -50, 0, "offset or size must not be negative", 0}, // Error
{0, 1, 1024*1024 + 1, "reached limit for padding memory slice: 1048578", 0}, // Error
{10, 0, 1024*1024 + 100, "reached limit for padding memory slice: 1048666", 0}, // Error
} {
mem := vm.NewMemory()

View file

@ -223,10 +223,6 @@ func benchTracer(tracerName string, test *callTracerTest, b *testing.B) {
b.Fatalf("failed to parse testcase input: %v", err)
}
signer := types.MakeSigner(test.Genesis.Config, new(big.Int).SetUint64(uint64(test.Context.Number)), uint64(test.Context.Time))
msg, err := core.TransactionToMessage(tx, signer, nil)
if err != nil {
b.Fatalf("failed to prepare transaction for tracing: %v", err)
}
origin, _ := signer.Sender(tx)
txContext := vm.TxContext{
Origin: origin,
@ -241,6 +237,10 @@ func benchTracer(tracerName string, test *callTracerTest, b *testing.B) {
Difficulty: (*big.Int)(test.Context.Difficulty),
GasLimit: uint64(test.Context.GasLimit),
}
msg, err := core.TransactionToMessage(tx, signer, context.BaseFee)
if err != nil {
b.Fatalf("failed to prepare transaction for tracing: %v", err)
}
triedb, _, statedb := tests.MakePreState(rawdb.NewMemoryDatabase(), test.Genesis.Alloc, false, rawdb.HashScheme)
defer triedb.Close()
@ -370,11 +370,11 @@ func TestInternals(t *testing.T) {
} {
t.Run(tc.name, func(t *testing.T) {
triedb, _, statedb := tests.MakePreState(rawdb.NewMemoryDatabase(),
core.GenesisAlloc{
to: core.GenesisAccount{
types.GenesisAlloc{
to: types.Account{
Code: tc.code,
},
origin: core.GenesisAccount{
origin: types.Account{
Balance: big.NewInt(500000000000000),
},
}, false, rawdb.HashScheme)

View file

@ -25,6 +25,7 @@ import (
"testing"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/consensus/misc/eip4844"
"github.com/ethereum/go-ethereum/core"
"github.com/ethereum/go-ethereum/core/rawdb"
"github.com/ethereum/go-ethereum/core/types"
@ -107,6 +108,11 @@ func testPrestateDiffTracer(tracerName string, dirPath string, t *testing.T) {
)
defer triedb.Close()
if test.Genesis.ExcessBlobGas != nil && test.Genesis.BlobGasUsed != nil {
excessBlobGas := eip4844.CalcExcessBlobGas(*test.Genesis.ExcessBlobGas, *test.Genesis.BlobGasUsed)
context.BlobBaseFee = eip4844.CalcBlobFee(excessBlobGas)
}
tracer, err := directory.DefaultDirectory.New(tracerName, new(directory.Context), test.TracerConfig)
if err != nil {
t.Fatalf("failed to create call tracer: %v", err)

View file

@ -0,0 +1,63 @@
{
"genesis": {
"baseFeePerGas": "7",
"blobGasUsed": "0",
"difficulty": "0",
"excessBlobGas": "36306944",
"extraData": "0xd983010e00846765746888676f312e32312e308664617277696e",
"gasLimit": "15639172",
"hash": "0xc682259fda061bb9ce8ccb491d5b2d436cb73daf04e1025dd116d045ce4ad28c",
"miner": "0x0000000000000000000000000000000000000000",
"mixHash": "0xae1a5ba939a4c9ac38aabeff361169fb55a6fc2c9511457e0be6eff9514faec0",
"nonce": "0x0000000000000000",
"number": "315",
"parentBeaconBlockRoot": "0x0000000000000000000000000000000000000000000000000000000000000000",
"stateRoot": "0x577f42ab21ccfd946511c57869ace0bdf7c217c36f02b7cd3459df0ed1cffc1a",
"timestamp": "1709626771",
"totalDifficulty": "1",
"withdrawals": [],
"withdrawalsRoot": "0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421",
"alloc": {
"0x0000000000000000000000000000000000000000": {
"balance": "0x272e0528"
},
"0x0c2c51a0990aee1d73c1228de158688341557508": {
"balance": "0xde0b6b3a7640000"
}
},
"config": {
"chainId": 1337,
"homesteadBlock": 0,
"eip150Block": 0,
"eip155Block": 0,
"eip158Block": 0,
"byzantiumBlock": 0,
"constantinopleBlock": 0,
"petersburgBlock": 0,
"istanbulBlock": 0,
"muirGlacierBlock": 0,
"berlinBlock": 0,
"londonBlock": 0,
"arrowGlacierBlock": 0,
"grayGlacierBlock": 0,
"shanghaiTime": 0,
"cancunTime": 0,
"terminalTotalDifficulty": 0,
"terminalTotalDifficultyPassed": true
}
},
"context": {
"number": "316",
"difficulty": "0",
"timestamp": "1709626785",
"gasLimit": "15654443",
"miner": "0x0000000000000000000000000000000000000000"
},
"input": "0x03f8b1820539806485174876e800825208940c2c51a0990aee1d73c1228de1586883415575088080c083020000f842a00100c9fbdf97f747e85847b4f3fff408f89c26842f77c882858bf2c89923849aa00138e3896f3c27f2389147507f8bcec52028b0efca6ee842ed83c9158873943880a0dbac3f97a532c9b00e6239b29036245a5bfbb96940b9d848634661abee98b945a03eec8525f261c2e79798f7b45a5d6ccaefa24576d53ba5023e919b86841c0675",
"result": {
"0x0000000000000000000000000000000000000000": { "balance": "0x272e0528" },
"0x0c2c51a0990aee1d73c1228de158688341557508": {
"balance": "0xde0b6b3a7640000"
}
}
}

View file

@ -0,0 +1,64 @@
{
"genesis": {
"baseFeePerGas": "7",
"difficulty": "2",
"extraData": "0xd983010d0e846765746888676f312e32312e318664617277696e0000000000001713699f05f79a59abec177c7a87b90ceda79b72ff5edc9197dd7627a447cde45b079bbc3765a236cdf680e2d4d2247135d0e6bb6fd92b50638b92504ddb274f00",
"gasLimit": "30000000",
"hash": "0x6ad5258175c66f4e883d238a92a08428d8ebcbeac631ab7b972634cc05effab3",
"miner": "0x2445e8c26a2bf3d1e59f1bb9b1d442caf90768e0",
"mixHash": "0x0000000000000000000000000000000000000000000000000000000000000000",
"nonce": "0x0000000000000000",
"number": "39137",
"stateRoot": "0x715f00df764dbadd4863247a215ac44b5420beafde3ec458b15db7aafa89be0c",
"timestamp": "1709022192",
"totalDifficulty": "78275",
"alloc": {
"0x2445e8c26a2bf3d1e59f1bb9b1d442caf90768e0": {
"balance": "0x10f06447a8d44dba190",
"nonce": "2"
},
"0x82211934c340b29561381392348d48413e15adc8": {
"balance": "0x6abd7a808913ed2",
"nonce": "64"
}
},
"config": {
"chainId": 12345,
"homesteadBlock": 0,
"eip150Block": 0,
"eip155Block": 0,
"eip158Block": 0,
"byzantiumBlock": 0,
"constantinopleBlock": 0,
"petersburgBlock": 0,
"istanbulBlock": 0,
"muirGlacierBlock": 0,
"berlinBlock": 0,
"londonBlock": 0,
"arrowGlacierBlock": 0,
"grayGlacierBlock": 0,
"clique": {
"period": 5,
"epoch": 30000
}
}
},
"context": {
"number": "39138",
"difficulty": "2",
"timestamp": "1709022197",
"gasLimit": "30000000",
"miner": "0x2445e8c26a2bf3d1e59f1bb9b1d442caf90768e0"
},
"input": "0x02f902af823039408459682f008459682f088302b3538080b90254608060405234801561001057600080fd5b50610234806100206000396000f3fe608060405234801561001057600080fd5b50600436106100365760003560e01c806309ce9ccb1461003b5780633fb5c1cb14610059575b600080fd5b610043610075565b60405161005091906100e2565b60405180910390f35b610073600480360381019061006e919061012e565b61007b565b005b60005481565b80600081905550600a8111156100c6576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016100bd906101de565b60405180910390fd5b50565b6000819050919050565b6100dc816100c9565b82525050565b60006020820190506100f760008301846100d3565b92915050565b600080fd5b61010b816100c9565b811461011657600080fd5b50565b60008135905061012881610102565b92915050565b600060208284031215610144576101436100fd565b5b600061015284828501610119565b91505092915050565b600082825260208201905092915050565b7f4e756d6265722069732067726561746572207468616e2031302c207472616e7360008201527f616374696f6e2072657665727465642e00000000000000000000000000000000602082015250565b60006101c860308361015b565b91506101d38261016c565b604082019050919050565b600060208201905081810360008301526101f7816101bb565b905091905056fea264697066735822122069018995fecf03bda91a88b6eafe41641709dee8b4a706fe301c8a569fe8c1b364736f6c63430008130033c001a0a8cf4729b7e4664687abb3e2559853d7d489eb441519be2a17493061fb4c3a03a04b5a904ba8a6e59c6c40049c4d14a73233aeb8a45b38403199f304630dc0d453",
"result": {
"0x2445e8c26a2bf3d1e59f1bb9b1d442caf90768e0": {
"balance": "0x10f06447a8d44dba190",
"nonce": 2
},
"0x82211934c340b29561381392348d48413e15adc8": {
"balance": "0x6abd7a808913ed2",
"nonce": 64
}
}
}

View file

@ -83,7 +83,7 @@
},
"post": {
"0x808b4da0be6c9512e948521452227efc619bea52": {
"balance": "0x2cd72a36dd031f089",
"balance": "0x2cd987071ba2346b6",
"nonce": 1223933
},
"0x8f03f1a3f10c05e7cccf75c1fd10168e06659be7": {

View file

@ -0,0 +1,82 @@
{
"genesis": {
"baseFeePerGas": "7",
"difficulty": "2",
"extraData": "0xd983010d0e846765746888676f312e32312e318664617277696e0000000000001713699f05f79a59abec177c7a87b90ceda79b72ff5edc9197dd7627a447cde45b079bbc3765a236cdf680e2d4d2247135d0e6bb6fd92b50638b92504ddb274f00",
"gasLimit": "30000000",
"hash": "0x6ad5258175c66f4e883d238a92a08428d8ebcbeac631ab7b972634cc05effab3",
"miner": "0x2445e8c26a2bf3d1e59f1bb9b1d442caf90768e0",
"mixHash": "0x0000000000000000000000000000000000000000000000000000000000000000",
"nonce": "0x0000000000000000",
"number": "39137",
"stateRoot": "0x715f00df764dbadd4863247a215ac44b5420beafde3ec458b15db7aafa89be0c",
"timestamp": "1709022192",
"totalDifficulty": "78275",
"alloc": {
"0x2445e8c26a2bf3d1e59f1bb9b1d442caf90768e0": {
"balance": "0x10f06447a8d44dba190",
"nonce": "2"
},
"0x82211934c340b29561381392348d48413e15adc8": {
"balance": "0x6abd7a808913ed2",
"nonce": "64"
}
},
"config": {
"chainId": 12345,
"homesteadBlock": 0,
"eip150Block": 0,
"eip155Block": 0,
"eip158Block": 0,
"byzantiumBlock": 0,
"constantinopleBlock": 0,
"petersburgBlock": 0,
"istanbulBlock": 0,
"muirGlacierBlock": 0,
"berlinBlock": 0,
"londonBlock": 0,
"arrowGlacierBlock": 0,
"grayGlacierBlock": 0,
"clique": {
"period": 5,
"epoch": 30000
}
}
},
"context": {
"number": "39138",
"difficulty": "2",
"timestamp": "1709022197",
"gasLimit": "30000000",
"miner": "0x2445e8c26a2bf3d1e59f1bb9b1d442caf90768e0"
},
"input": "0x02f902af823039408459682f008459682f088302b3538080b90254608060405234801561001057600080fd5b50610234806100206000396000f3fe608060405234801561001057600080fd5b50600436106100365760003560e01c806309ce9ccb1461003b5780633fb5c1cb14610059575b600080fd5b610043610075565b60405161005091906100e2565b60405180910390f35b610073600480360381019061006e919061012e565b61007b565b005b60005481565b80600081905550600a8111156100c6576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016100bd906101de565b60405180910390fd5b50565b6000819050919050565b6100dc816100c9565b82525050565b60006020820190506100f760008301846100d3565b92915050565b600080fd5b61010b816100c9565b811461011657600080fd5b50565b60008135905061012881610102565b92915050565b600060208284031215610144576101436100fd565b5b600061015284828501610119565b91505092915050565b600082825260208201905092915050565b7f4e756d6265722069732067726561746572207468616e2031302c207472616e7360008201527f616374696f6e2072657665727465642e00000000000000000000000000000000602082015250565b60006101c860308361015b565b91506101d38261016c565b604082019050919050565b600060208201905081810360008301526101f7816101bb565b905091905056fea264697066735822122069018995fecf03bda91a88b6eafe41641709dee8b4a706fe301c8a569fe8c1b364736f6c63430008130033c001a0a8cf4729b7e4664687abb3e2559853d7d489eb441519be2a17493061fb4c3a03a04b5a904ba8a6e59c6c40049c4d14a73233aeb8a45b38403199f304630dc0d453",
"tracerConfig": {
"diffMode": true
},
"result": {
"post": {
"0x1bda2f8e4735507930bd6cfe873bf0bf0f4ab1de": {
"code": "0x608060405234801561001057600080fd5b50600436106100365760003560e01c806309ce9ccb1461003b5780633fb5c1cb14610059575b600080fd5b610043610075565b60405161005091906100e2565b60405180910390f35b610073600480360381019061006e919061012e565b61007b565b005b60005481565b80600081905550600a8111156100c6576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016100bd906101de565b60405180910390fd5b50565b6000819050919050565b6100dc816100c9565b82525050565b60006020820190506100f760008301846100d3565b92915050565b600080fd5b61010b816100c9565b811461011657600080fd5b50565b60008135905061012881610102565b92915050565b600060208284031215610144576101436100fd565b5b600061015284828501610119565b91505092915050565b600082825260208201905092915050565b7f4e756d6265722069732067726561746572207468616e2031302c207472616e7360008201527f616374696f6e2072657665727465642e00000000000000000000000000000000602082015250565b60006101c860308361015b565b91506101d38261016c565b604082019050919050565b600060208201905081810360008301526101f7816101bb565b905091905056fea264697066735822122069018995fecf03bda91a88b6eafe41641709dee8b4a706fe301c8a569fe8c1b364736f6c63430008130033",
"nonce": 1
},
"0x2445e8c26a2bf3d1e59f1bb9b1d442caf90768e0": {
"balance": "0x10f0645688331eb5690"
},
"0x82211934c340b29561381392348d48413e15adc8": {
"balance": "0x6aae9b21b6ee855",
"nonce": 65
}
},
"pre": {
"0x2445e8c26a2bf3d1e59f1bb9b1d442caf90768e0": {
"balance": "0x10f06447a8d44dba190",
"nonce": 2
},
"0x82211934c340b29561381392348d48413e15adc8": {
"balance": "0x6abd7a808913ed2",
"nonce": 64
}
}
}
}

View file

@ -144,19 +144,29 @@ func newJsTracer(code string, ctx *directory.Context, cfg json.RawMessage) (*dir
vm: vm,
ctx: make(map[string]goja.Value),
}
t.setTypeConverters()
t.setBuiltinFunctions()
if ctx == nil {
ctx = new(directory.Context)
}
if ctx.BlockHash != (common.Hash{}) {
t.ctx["blockHash"] = vm.ToValue(ctx.BlockHash.Bytes())
blockHash, err := t.toBuf(vm, ctx.BlockHash.Bytes())
if err != nil {
return nil, err
}
t.ctx["blockHash"] = blockHash
if ctx.TxHash != (common.Hash{}) {
t.ctx["txIndex"] = vm.ToValue(ctx.TxIndex)
t.ctx["txHash"] = vm.ToValue(ctx.TxHash.Bytes())
txHash, err := t.toBuf(vm, ctx.TxHash.Bytes())
if err != nil {
return nil, err
}
t.ctx["txHash"] = txHash
}
}
t.setTypeConverters()
t.setBuiltinFunctions()
ret, err := vm.RunString("(" + code + ")")
if err != nil {
return nil, err
@ -341,6 +351,12 @@ func (t *jsTracer) onEnd(output []byte, gasUsed uint64, err error, reverted bool
if err != nil {
t.ctx["error"] = t.vm.ToValue(err.Error())
}
outputVal, err := t.toBuf(t.vm, output)
if err != nil {
t.err = err
return
}
t.ctx["output"] = outputVal
}
// OnEnter is called when EVM enters a new scope (via call, create or selfdestruct).
@ -520,13 +536,13 @@ func (t *jsTracer) setBuiltinFunctions() {
}
return false
})
vm.Set("slice", func(slice goja.Value, start, end int) goja.Value {
vm.Set("slice", func(slice goja.Value, start, end int64) goja.Value {
b, err := t.fromBuf(vm, slice, false)
if err != nil {
vm.Interrupt(err)
return nil
}
if start < 0 || start > end || end > len(b) {
if start < 0 || start > end || end > int64(len(b)) {
vm.Interrupt(fmt.Sprintf("Tracer accessed out of bound memory: available %d, offset %d, size %d", len(b), start, end-start))
return nil
}

View file

@ -219,7 +219,7 @@
return this.finalize(result);
},
// finalize recreates a call object using the final desired field oder for json
// finalize recreates a call object using the final desired field order for json
// serialization. This is a nicety feature to pass meaningfully ordered results
// to users who don't interpret it, just display it.
finalize: function(call) {

View file

@ -23,7 +23,7 @@ func (s StructLog) MarshalJSON() ([]byte, error) {
GasCost math.HexOrDecimal64 `json:"gasCost"`
Memory hexutil.Bytes `json:"memory,omitempty"`
MemorySize int `json:"memSize"`
Stack []uint256.Int `json:"stack"`
Stack []hexutil.U256 `json:"stack"`
ReturnData hexutil.Bytes `json:"returnData,omitempty"`
Storage map[common.Hash]common.Hash `json:"-"`
Depth int `json:"depth"`
@ -39,7 +39,12 @@ func (s StructLog) MarshalJSON() ([]byte, error) {
enc.GasCost = math.HexOrDecimal64(s.GasCost)
enc.Memory = s.Memory
enc.MemorySize = s.MemorySize
enc.Stack = s.Stack
if s.Stack != nil {
enc.Stack = make([]hexutil.U256, len(s.Stack))
for k, v := range s.Stack {
enc.Stack[k] = hexutil.U256(v)
}
}
enc.ReturnData = s.ReturnData
enc.Storage = s.Storage
enc.Depth = s.Depth
@ -59,7 +64,7 @@ func (s *StructLog) UnmarshalJSON(input []byte) error {
GasCost *math.HexOrDecimal64 `json:"gasCost"`
Memory *hexutil.Bytes `json:"memory,omitempty"`
MemorySize *int `json:"memSize"`
Stack []uint256.Int `json:"stack"`
Stack []hexutil.U256 `json:"stack"`
ReturnData *hexutil.Bytes `json:"returnData,omitempty"`
Storage map[common.Hash]common.Hash `json:"-"`
Depth *int `json:"depth"`
@ -89,7 +94,10 @@ func (s *StructLog) UnmarshalJSON(input []byte) error {
s.MemorySize = *dec.MemorySize
}
if dec.Stack != nil {
s.Stack = dec.Stack
s.Stack = make([]uint256.Int, len(dec.Stack))
for k, v := range dec.Stack {
s.Stack[k] = uint256.Int(v)
}
}
if dec.ReturnData != nil {
s.ReturnData = *dec.ReturnData

View file

@ -85,6 +85,7 @@ type structLogMarshaling struct {
GasCost math.HexOrDecimal64
Memory hexutil.Bytes
ReturnData hexutil.Bytes
Stack []hexutil.U256
OpName string `json:"opName"` // adds call to OpName() in MarshalJSON
ErrorString string `json:"error,omitempty"` // adds call to ErrorString() in MarshalJSON
}
@ -418,11 +419,6 @@ func (t *mdLogger) OnFault(pc uint64, op byte, gas, cost uint64, scope tracing.O
fmt.Fprintf(t.out, "\nError: at pc=%d, op=%v: %v\n", pc, op, err)
}
func (t *mdLogger) CaptureEnd(output []byte, gasUsed uint64, err error, reverted bool) {
fmt.Fprintf(t.out, "\nOutput: `%#x`\nConsumed gas: `%d`\nError: `%v`\n",
output, gasUsed, err)
}
// ExecutionResult groups all structured logs emitted by the EVM
// while replaying a transaction in debug mode as well as transaction
// execution status, the amount of gas used and the return value

View file

@ -155,6 +155,9 @@ func newFlatCallTracer(ctx *directory.Context, cfg json.RawMessage) (*directory.
func (t *flatCallTracer) OnEnter(depth int, typ byte, from common.Address, to common.Address, input []byte, gas uint64, value *big.Int) {
t.tracer.OnEnter(depth, typ, from, to, input, gas, value)
if depth == 0 {
return
}
// Child calls must have a value, even if it's zero.
// Practically speaking, only STATICCALL has nil value. Set it to zero.
if t.tracer.callstack[len(t.tracer.callstack)-1].Value == nil && value == nil {
@ -167,6 +170,9 @@ func (t *flatCallTracer) OnEnter(depth int, typ byte, from common.Address, to co
func (t *flatCallTracer) OnExit(depth int, output []byte, gasUsed uint64, err error, reverted bool) {
t.tracer.OnExit(depth, output, gasUsed, err, reverted)
if depth == 0 {
return
}
// Parity traces don't include CALL/STATICCALLs to precompiles.
// By default we remove them from the callstack.
if t.config.IncludePrecompiles {

View file

@ -332,6 +332,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{})