mirror of https://github.com/ethereum/go-ethereum
eth/tracers: live chain tracing with hooks (#29189)
Here we add a Go API for running tracing plugins within the main block import process. As an advanced user of geth, you can now create a Go file in eth/tracers/live/, and within that file register your custom tracer implementation. Then recompile geth and select your tracer on the command line. Hooks defined in the tracer will run whenever a block is processed. The hook system is defined in package core/tracing. It uses a struct with callbacks, instead of requiring an interface, for several reasons: - We plan to keep this API stable long-term. The core/tracing hook API does not depend on on deep geth internals. - There are a lot of hooks, and tracers will only need some of them. Using a struct allows you to implement only the hooks you want to actually use. All existing tracers in eth/tracers/native have been rewritten to use the new hook system. This change breaks compatibility with the vm.EVMLogger interface that we used to have. If you are a user of vm.EVMLogger, please migrate to core/tracing, and sorry for breaking your stuff. But we just couldn't have both the old and new tracing APIs coexist in the EVM. --------- Co-authored-by: Matthieu Vachon <matthieu.o.vachon@gmail.com> Co-authored-by: Delweng <delweng@gmail.com> Co-authored-by: Martin HS <martin@swende.se>pull/29318/head
parent
38eb8b3e20
commit
064f37d6f6
@ -1,81 +0,0 @@ |
|||||||
// Copyright 2020 The go-ethereum Authors
|
|
||||||
// This file is part of go-ethereum.
|
|
||||||
//
|
|
||||||
// go-ethereum is free software: you can redistribute it and/or modify
|
|
||||||
// it under the terms of the GNU General Public License as published by
|
|
||||||
// the Free Software Foundation, either version 3 of the License, or
|
|
||||||
// (at your option) any later version.
|
|
||||||
//
|
|
||||||
// go-ethereum is distributed in the hope that it will be useful,
|
|
||||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
// GNU General Public License for more details.
|
|
||||||
//
|
|
||||||
// You should have received a copy of the GNU General Public License
|
|
||||||
// along with go-ethereum. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
|
|
||||||
package t8ntool |
|
||||||
|
|
||||||
import ( |
|
||||||
"encoding/json" |
|
||||||
"io" |
|
||||||
"math/big" |
|
||||||
|
|
||||||
"github.com/ethereum/go-ethereum/common" |
|
||||||
"github.com/ethereum/go-ethereum/core/vm" |
|
||||||
"github.com/ethereum/go-ethereum/eth/tracers" |
|
||||||
"github.com/ethereum/go-ethereum/log" |
|
||||||
) |
|
||||||
|
|
||||||
// traceWriter is an vm.EVMLogger which also holds an inner logger/tracer.
|
|
||||||
// When the TxEnd event happens, the inner tracer result is written to the file, and
|
|
||||||
// the file is closed.
|
|
||||||
type traceWriter struct { |
|
||||||
inner vm.EVMLogger |
|
||||||
f io.WriteCloser |
|
||||||
} |
|
||||||
|
|
||||||
// Compile-time interface check
|
|
||||||
var _ = vm.EVMLogger((*traceWriter)(nil)) |
|
||||||
|
|
||||||
func (t *traceWriter) CaptureTxEnd(restGas uint64) { |
|
||||||
t.inner.CaptureTxEnd(restGas) |
|
||||||
defer t.f.Close() |
|
||||||
|
|
||||||
if tracer, ok := t.inner.(tracers.Tracer); ok { |
|
||||||
result, err := tracer.GetResult() |
|
||||||
if err != nil { |
|
||||||
log.Warn("Error in tracer", "err", err) |
|
||||||
return |
|
||||||
} |
|
||||||
err = json.NewEncoder(t.f).Encode(result) |
|
||||||
if err != nil { |
|
||||||
log.Warn("Error writing tracer output", "err", err) |
|
||||||
return |
|
||||||
} |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
func (t *traceWriter) CaptureTxStart(gasLimit uint64) { t.inner.CaptureTxStart(gasLimit) } |
|
||||||
func (t *traceWriter) CaptureStart(env *vm.EVM, from common.Address, to common.Address, create bool, input []byte, gas uint64, value *big.Int) { |
|
||||||
t.inner.CaptureStart(env, from, to, create, input, gas, value) |
|
||||||
} |
|
||||||
|
|
||||||
func (t *traceWriter) CaptureEnd(output []byte, gasUsed uint64, err error) { |
|
||||||
t.inner.CaptureEnd(output, gasUsed, err) |
|
||||||
} |
|
||||||
|
|
||||||
func (t *traceWriter) CaptureEnter(typ vm.OpCode, from common.Address, to common.Address, input []byte, gas uint64, value *big.Int) { |
|
||||||
t.inner.CaptureEnter(typ, from, to, input, gas, value) |
|
||||||
} |
|
||||||
|
|
||||||
func (t *traceWriter) CaptureExit(output []byte, gasUsed uint64, err error) { |
|
||||||
t.inner.CaptureExit(output, gasUsed, err) |
|
||||||
} |
|
||||||
|
|
||||||
func (t *traceWriter) CaptureState(pc uint64, op vm.OpCode, gas, cost uint64, scope *vm.ScopeContext, rData []byte, depth int, err error) { |
|
||||||
t.inner.CaptureState(pc, op, gas, cost, scope, rData, depth, err) |
|
||||||
} |
|
||||||
func (t *traceWriter) CaptureFault(pc uint64, op vm.OpCode, gas, cost uint64, scope *vm.ScopeContext, depth int, err error) { |
|
||||||
t.inner.CaptureFault(pc, op, gas, cost, scope, depth, err) |
|
||||||
} |
|
@ -0,0 +1 @@ |
|||||||
|
This test does some EVM execution, and can be used to test the tracers and trace-outputs. |
@ -0,0 +1,16 @@ |
|||||||
|
{ |
||||||
|
"0xa94f5374fce5edbc8e2a8697c15331677e6ebf0b" : { |
||||||
|
"balance" : "0x016345785d8a0000", |
||||||
|
"code" : "0x", |
||||||
|
"nonce" : "0x00", |
||||||
|
"storage" : { |
||||||
|
} |
||||||
|
}, |
||||||
|
"0x1111111111111111111111111111111111111111" : { |
||||||
|
"balance" : "0x1", |
||||||
|
"code" : "0x604060406040604000", |
||||||
|
"nonce" : "0x00", |
||||||
|
"storage" : { |
||||||
|
} |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,20 @@ |
|||||||
|
{ |
||||||
|
"currentCoinbase" : "0x2adc25665018aa1fe0e6bc666dac8fc2697ff9ba", |
||||||
|
"currentNumber" : "0x01", |
||||||
|
"currentTimestamp" : "0x03e8", |
||||||
|
"currentGasLimit" : "0x1000000000", |
||||||
|
"previousHash" : "0xe4e2a30b340bec696242b67584264f878600dce98354ae0b6328740fd4ff18da", |
||||||
|
"currentDataGasUsed" : "0x2000", |
||||||
|
"parentTimestamp" : "0x00", |
||||||
|
"parentDifficulty" : "0x00", |
||||||
|
"parentUncleHash" : "0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347", |
||||||
|
"parentBeaconBlockRoot" : "0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347", |
||||||
|
"currentRandom" : "0x0000000000000000000000000000000000000000000000000000000000020000", |
||||||
|
"withdrawals" : [ |
||||||
|
], |
||||||
|
"parentBaseFee" : "0x08", |
||||||
|
"parentGasUsed" : "0x00", |
||||||
|
"parentGasLimit" : "0x1000000000", |
||||||
|
"parentExcessBlobGas" : "0x1000", |
||||||
|
"parentBlobGasUsed" : "0x2000" |
||||||
|
} |
@ -0,0 +1 @@ |
|||||||
|
"hello world" |
@ -0,0 +1,6 @@ |
|||||||
|
{"pc":0,"op":96,"gas":"0x13498","gasCost":"0x3","memSize":0,"stack":[],"depth":1,"refund":0,"opName":"PUSH1"} |
||||||
|
{"pc":2,"op":96,"gas":"0x13495","gasCost":"0x3","memSize":0,"stack":["0x40"],"depth":1,"refund":0,"opName":"PUSH1"} |
||||||
|
{"pc":4,"op":96,"gas":"0x13492","gasCost":"0x3","memSize":0,"stack":["0x40","0x40"],"depth":1,"refund":0,"opName":"PUSH1"} |
||||||
|
{"pc":6,"op":96,"gas":"0x1348f","gasCost":"0x3","memSize":0,"stack":["0x40","0x40","0x40"],"depth":1,"refund":0,"opName":"PUSH1"} |
||||||
|
{"pc":8,"op":0,"gas":"0x1348c","gasCost":"0x0","memSize":0,"stack":["0x40","0x40","0x40","0x40"],"depth":1,"refund":0,"opName":"STOP"} |
||||||
|
{"output":"","gasUsed":"0xc"} |
@ -0,0 +1,14 @@ |
|||||||
|
[ |
||||||
|
{ |
||||||
|
"gas": "0x186a0", |
||||||
|
"gasPrice": "0x600", |
||||||
|
"input": "0x", |
||||||
|
"nonce": "0x0", |
||||||
|
"to": "0x1111111111111111111111111111111111111111", |
||||||
|
"value": "0x1", |
||||||
|
"v" : "0x0", |
||||||
|
"r" : "0x0", |
||||||
|
"s" : "0x0", |
||||||
|
"secretKey" : "0x45a915e4d060149eb4365960e6a7a45f334393093061116b197e3240065ff2d8" |
||||||
|
} |
||||||
|
] |
@ -0,0 +1,275 @@ |
|||||||
|
// Copyright 2024 The go-ethereum Authors
|
||||||
|
// This file is part of the go-ethereum library.
|
||||||
|
//
|
||||||
|
// The go-ethereum library is free software: you can redistribute it and/or modify
|
||||||
|
// it under the terms of the GNU Lesser General Public License as published by
|
||||||
|
// the Free Software Foundation, either version 3 of the License, or
|
||||||
|
// (at your option) any later version.
|
||||||
|
//
|
||||||
|
// The go-ethereum library is distributed in the hope that it will be useful,
|
||||||
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
// GNU Lesser General Public License for more details.
|
||||||
|
//
|
||||||
|
// You should have received a copy of the GNU Lesser General Public License
|
||||||
|
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
package tracing |
||||||
|
|
||||||
|
import ( |
||||||
|
"math/big" |
||||||
|
|
||||||
|
"github.com/ethereum/go-ethereum/common" |
||||||
|
"github.com/ethereum/go-ethereum/core/types" |
||||||
|
"github.com/ethereum/go-ethereum/params" |
||||||
|
"github.com/holiman/uint256" |
||||||
|
) |
||||||
|
|
||||||
|
// OpContext provides the context at which the opcode is being
|
||||||
|
// executed in, including the memory, stack and various contract-level information.
|
||||||
|
type OpContext interface { |
||||||
|
MemoryData() []byte |
||||||
|
StackData() []uint256.Int |
||||||
|
Caller() common.Address |
||||||
|
Address() common.Address |
||||||
|
CallValue() *uint256.Int |
||||||
|
CallInput() []byte |
||||||
|
} |
||||||
|
|
||||||
|
// StateDB gives tracers access to the whole state.
|
||||||
|
type StateDB interface { |
||||||
|
GetBalance(common.Address) *uint256.Int |
||||||
|
GetNonce(common.Address) uint64 |
||||||
|
GetCode(common.Address) []byte |
||||||
|
GetState(common.Address, common.Hash) common.Hash |
||||||
|
Exist(common.Address) bool |
||||||
|
GetRefund() uint64 |
||||||
|
} |
||||||
|
|
||||||
|
// VMContext provides the context for the EVM execution.
|
||||||
|
type VMContext struct { |
||||||
|
Coinbase common.Address |
||||||
|
BlockNumber *big.Int |
||||||
|
Time uint64 |
||||||
|
Random *common.Hash |
||||||
|
// Effective tx gas price
|
||||||
|
GasPrice *big.Int |
||||||
|
ChainConfig *params.ChainConfig |
||||||
|
StateDB StateDB |
||||||
|
} |
||||||
|
|
||||||
|
// BlockEvent is emitted upon tracing an incoming block.
|
||||||
|
// It contains the block as well as consensus related information.
|
||||||
|
type BlockEvent struct { |
||||||
|
Block *types.Block |
||||||
|
TD *big.Int |
||||||
|
Finalized *types.Header |
||||||
|
Safe *types.Header |
||||||
|
} |
||||||
|
|
||||||
|
type ( |
||||||
|
/* |
||||||
|
- VM events - |
||||||
|
*/ |
||||||
|
|
||||||
|
// TxStartHook is called before the execution of a transaction starts.
|
||||||
|
// Call simulations don't come with a valid signature. `from` field
|
||||||
|
// to be used for address of the caller.
|
||||||
|
TxStartHook = func(vm *VMContext, tx *types.Transaction, from common.Address) |
||||||
|
|
||||||
|
// TxEndHook is called after the execution of a transaction ends.
|
||||||
|
TxEndHook = func(receipt *types.Receipt, err error) |
||||||
|
|
||||||
|
// EnterHook is invoked when the processing of a message starts.
|
||||||
|
EnterHook = func(depth int, typ byte, from common.Address, to common.Address, input []byte, gas uint64, value *big.Int) |
||||||
|
|
||||||
|
// ExitHook is invoked when the processing of a message ends.
|
||||||
|
// `revert` is true when there was an error during the execution.
|
||||||
|
// Exceptionally, before the homestead hardfork a contract creation that
|
||||||
|
// ran out of gas when attempting to persist the code to database did not
|
||||||
|
// count as a call failure and did not cause a revert of the call. This will
|
||||||
|
// be indicated by `reverted == false` and `err == ErrCodeStoreOutOfGas`.
|
||||||
|
ExitHook = func(depth int, output []byte, gasUsed uint64, err error, reverted bool) |
||||||
|
|
||||||
|
// OpcodeHook is invoked just prior to the execution of an opcode.
|
||||||
|
OpcodeHook = func(pc uint64, op byte, gas, cost uint64, scope OpContext, rData []byte, depth int, err error) |
||||||
|
|
||||||
|
// FaultHook is invoked when an error occurs during the execution of an opcode.
|
||||||
|
FaultHook = func(pc uint64, op byte, gas, cost uint64, scope OpContext, depth int, err error) |
||||||
|
|
||||||
|
// GasChangeHook is invoked when the gas changes.
|
||||||
|
GasChangeHook = func(old, new uint64, reason GasChangeReason) |
||||||
|
|
||||||
|
/* |
||||||
|
- Chain events - |
||||||
|
*/ |
||||||
|
|
||||||
|
// BlockchainInitHook is called when the blockchain is initialized.
|
||||||
|
BlockchainInitHook = func(chainConfig *params.ChainConfig) |
||||||
|
|
||||||
|
// BlockStartHook is called before executing `block`.
|
||||||
|
// `td` is the total difficulty prior to `block`.
|
||||||
|
BlockStartHook = func(event BlockEvent) |
||||||
|
|
||||||
|
// BlockEndHook is called after executing a block.
|
||||||
|
BlockEndHook = func(err error) |
||||||
|
|
||||||
|
// SkippedBlockHook indicates a block was skipped during processing
|
||||||
|
// due to it being known previously. This can happen e.g. when recovering
|
||||||
|
// from a crash.
|
||||||
|
SkippedBlockHook = func(event BlockEvent) |
||||||
|
|
||||||
|
// GenesisBlockHook is called when the genesis block is being processed.
|
||||||
|
GenesisBlockHook = func(genesis *types.Block, alloc types.GenesisAlloc) |
||||||
|
|
||||||
|
/* |
||||||
|
- State events - |
||||||
|
*/ |
||||||
|
|
||||||
|
// BalanceChangeHook is called when the balance of an account changes.
|
||||||
|
BalanceChangeHook = func(addr common.Address, prev, new *big.Int, reason BalanceChangeReason) |
||||||
|
|
||||||
|
// NonceChangeHook is called when the nonce of an account changes.
|
||||||
|
NonceChangeHook = func(addr common.Address, prev, new uint64) |
||||||
|
|
||||||
|
// CodeChangeHook is called when the code of an account changes.
|
||||||
|
CodeChangeHook = func(addr common.Address, prevCodeHash common.Hash, prevCode []byte, codeHash common.Hash, code []byte) |
||||||
|
|
||||||
|
// StorageChangeHook is called when the storage of an account changes.
|
||||||
|
StorageChangeHook = func(addr common.Address, slot common.Hash, prev, new common.Hash) |
||||||
|
|
||||||
|
// LogHook is called when a log is emitted.
|
||||||
|
LogHook = func(log *types.Log) |
||||||
|
) |
||||||
|
|
||||||
|
type Hooks struct { |
||||||
|
// VM events
|
||||||
|
OnTxStart TxStartHook |
||||||
|
OnTxEnd TxEndHook |
||||||
|
OnEnter EnterHook |
||||||
|
OnExit ExitHook |
||||||
|
OnOpcode OpcodeHook |
||||||
|
OnFault FaultHook |
||||||
|
OnGasChange GasChangeHook |
||||||
|
// Chain events
|
||||||
|
OnBlockchainInit BlockchainInitHook |
||||||
|
OnBlockStart BlockStartHook |
||||||
|
OnBlockEnd BlockEndHook |
||||||
|
OnSkippedBlock SkippedBlockHook |
||||||
|
OnGenesisBlock GenesisBlockHook |
||||||
|
// State events
|
||||||
|
OnBalanceChange BalanceChangeHook |
||||||
|
OnNonceChange NonceChangeHook |
||||||
|
OnCodeChange CodeChangeHook |
||||||
|
OnStorageChange StorageChangeHook |
||||||
|
OnLog LogHook |
||||||
|
} |
||||||
|
|
||||||
|
// BalanceChangeReason is used to indicate the reason for a balance change, useful
|
||||||
|
// for tracing and reporting.
|
||||||
|
type BalanceChangeReason byte |
||||||
|
|
||||||
|
const ( |
||||||
|
BalanceChangeUnspecified BalanceChangeReason = 0 |
||||||
|
|
||||||
|
// Issuance
|
||||||
|
// BalanceIncreaseRewardMineUncle is a reward for mining an uncle block.
|
||||||
|
BalanceIncreaseRewardMineUncle BalanceChangeReason = 1 |
||||||
|
// BalanceIncreaseRewardMineBlock is a reward for mining a block.
|
||||||
|
BalanceIncreaseRewardMineBlock BalanceChangeReason = 2 |
||||||
|
// BalanceIncreaseWithdrawal is ether withdrawn from the beacon chain.
|
||||||
|
BalanceIncreaseWithdrawal BalanceChangeReason = 3 |
||||||
|
// BalanceIncreaseGenesisBalance is ether allocated at the genesis block.
|
||||||
|
BalanceIncreaseGenesisBalance BalanceChangeReason = 4 |
||||||
|
|
||||||
|
// Transaction fees
|
||||||
|
// BalanceIncreaseRewardTransactionFee is the transaction tip increasing block builder's balance.
|
||||||
|
BalanceIncreaseRewardTransactionFee BalanceChangeReason = 5 |
||||||
|
// BalanceDecreaseGasBuy is spent to purchase gas for execution a transaction.
|
||||||
|
// Part of this gas will be burnt as per EIP-1559 rules.
|
||||||
|
BalanceDecreaseGasBuy BalanceChangeReason = 6 |
||||||
|
// BalanceIncreaseGasReturn is ether returned for unused gas at the end of execution.
|
||||||
|
BalanceIncreaseGasReturn BalanceChangeReason = 7 |
||||||
|
|
||||||
|
// DAO fork
|
||||||
|
// BalanceIncreaseDaoContract is ether sent to the DAO refund contract.
|
||||||
|
BalanceIncreaseDaoContract BalanceChangeReason = 8 |
||||||
|
// BalanceDecreaseDaoAccount is ether taken from a DAO account to be moved to the refund contract.
|
||||||
|
BalanceDecreaseDaoAccount BalanceChangeReason = 9 |
||||||
|
|
||||||
|
// BalanceChangeTransfer is ether transferred via a call.
|
||||||
|
// it is a decrease for the sender and an increase for the recipient.
|
||||||
|
BalanceChangeTransfer BalanceChangeReason = 10 |
||||||
|
// BalanceChangeTouchAccount is a transfer of zero value. It is only there to
|
||||||
|
// touch-create an account.
|
||||||
|
BalanceChangeTouchAccount BalanceChangeReason = 11 |
||||||
|
|
||||||
|
// BalanceIncreaseSelfdestruct is added to the recipient as indicated by a selfdestructing account.
|
||||||
|
BalanceIncreaseSelfdestruct BalanceChangeReason = 12 |
||||||
|
// BalanceDecreaseSelfdestruct is deducted from a contract due to self-destruct.
|
||||||
|
BalanceDecreaseSelfdestruct BalanceChangeReason = 13 |
||||||
|
// BalanceDecreaseSelfdestructBurn is ether that is sent to an already self-destructed
|
||||||
|
// account within the same tx (captured at end of tx).
|
||||||
|
// Note it doesn't account for a self-destruct which appoints itself as recipient.
|
||||||
|
BalanceDecreaseSelfdestructBurn BalanceChangeReason = 14 |
||||||
|
) |
||||||
|
|
||||||
|
// GasChangeReason is used to indicate the reason for a gas change, useful
|
||||||
|
// for tracing and reporting.
|
||||||
|
//
|
||||||
|
// There is essentially two types of gas changes, those that can be emitted once per transaction
|
||||||
|
// and those that can be emitted on a call basis, so possibly multiple times per transaction.
|
||||||
|
//
|
||||||
|
// They can be recognized easily by their name, those that start with `GasChangeTx` are emitted
|
||||||
|
// once per transaction, while those that start with `GasChangeCall` are emitted on a call basis.
|
||||||
|
type GasChangeReason byte |
||||||
|
|
||||||
|
const ( |
||||||
|
GasChangeUnspecified GasChangeReason = 0 |
||||||
|
|
||||||
|
// GasChangeTxInitialBalance is the initial balance for the call which will be equal to the gasLimit of the call. There is only
|
||||||
|
// one such gas change per transaction.
|
||||||
|
GasChangeTxInitialBalance GasChangeReason = 1 |
||||||
|
// GasChangeTxIntrinsicGas is the amount of gas that will be charged for the intrinsic cost of the transaction, there is
|
||||||
|
// always exactly one of those per transaction.
|
||||||
|
GasChangeTxIntrinsicGas GasChangeReason = 2 |
||||||
|
// GasChangeTxRefunds is the sum of all refunds which happened during the tx execution (e.g. storage slot being cleared)
|
||||||
|
// this generates an increase in gas. There is at most one of such gas change per transaction.
|
||||||
|
GasChangeTxRefunds GasChangeReason = 3 |
||||||
|
// GasChangeTxLeftOverReturned is the amount of gas left over at the end of transaction's execution that will be returned
|
||||||
|
// to the chain. This change will always be a negative change as we "drain" left over gas towards 0. If there was no gas
|
||||||
|
// left at the end of execution, no such even will be emitted. The returned gas's value in Wei is returned to caller.
|
||||||
|
// There is at most one of such gas change per transaction.
|
||||||
|
GasChangeTxLeftOverReturned GasChangeReason = 4 |
||||||
|
|
||||||
|
// GasChangeCallInitialBalance is the initial balance for the call which will be equal to the gasLimit of the call. There is only
|
||||||
|
// one such gas change per call.
|
||||||
|
GasChangeCallInitialBalance GasChangeReason = 5 |
||||||
|
// GasChangeCallLeftOverReturned is the amount of gas left over that will be returned to the caller, this change will always
|
||||||
|
// be a negative change as we "drain" left over gas towards 0. If there was no gas left at the end of execution, no such even
|
||||||
|
// will be emitted.
|
||||||
|
GasChangeCallLeftOverReturned GasChangeReason = 6 |
||||||
|
// GasChangeCallLeftOverRefunded is the amount of gas that will be refunded to the call after the child call execution it
|
||||||
|
// executed completed. This value is always positive as we are giving gas back to the you, the left over gas of the child.
|
||||||
|
// If there was no gas left to be refunded, no such even will be emitted.
|
||||||
|
GasChangeCallLeftOverRefunded GasChangeReason = 7 |
||||||
|
// GasChangeCallContractCreation is the amount of gas that will be burned for a CREATE.
|
||||||
|
GasChangeCallContractCreation GasChangeReason = 8 |
||||||
|
// GasChangeContractCreation is the amount of gas that will be burned for a CREATE2.
|
||||||
|
GasChangeCallContractCreation2 GasChangeReason = 9 |
||||||
|
// GasChangeCallCodeStorage is the amount of gas that will be charged for code storage.
|
||||||
|
GasChangeCallCodeStorage GasChangeReason = 10 |
||||||
|
// GasChangeCallOpCode is the amount of gas that will be charged for an opcode executed by the EVM, exact opcode that was
|
||||||
|
// performed can be check by `OnOpcode` handling.
|
||||||
|
GasChangeCallOpCode GasChangeReason = 11 |
||||||
|
// GasChangeCallPrecompiledContract is the amount of gas that will be charged for a precompiled contract execution.
|
||||||
|
GasChangeCallPrecompiledContract GasChangeReason = 12 |
||||||
|
// GasChangeCallStorageColdAccess is the amount of gas that will be charged for a cold storage access as controlled by EIP2929 rules.
|
||||||
|
GasChangeCallStorageColdAccess GasChangeReason = 13 |
||||||
|
// GasChangeCallFailedExecution is the burning of the remaining gas when the execution failed without a revert.
|
||||||
|
GasChangeCallFailedExecution GasChangeReason = 14 |
||||||
|
|
||||||
|
// GasChangeIgnored is a special value that can be used to indicate that the gas change should be ignored as
|
||||||
|
// it will be "manually" tracked by a direct emit of the gas change event.
|
||||||
|
GasChangeIgnored GasChangeReason = 0xFF |
||||||
|
) |
@ -1,43 +0,0 @@ |
|||||||
// Copyright 2015 The go-ethereum Authors
|
|
||||||
// This file is part of the go-ethereum library.
|
|
||||||
//
|
|
||||||
// The go-ethereum library is free software: you can redistribute it and/or modify
|
|
||||||
// it under the terms of the GNU Lesser General Public License as published by
|
|
||||||
// the Free Software Foundation, either version 3 of the License, or
|
|
||||||
// (at your option) any later version.
|
|
||||||
//
|
|
||||||
// The go-ethereum library is distributed in the hope that it will be useful,
|
|
||||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
// GNU Lesser General Public License for more details.
|
|
||||||
//
|
|
||||||
// You should have received a copy of the GNU Lesser General Public License
|
|
||||||
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
|
|
||||||
package vm |
|
||||||
|
|
||||||
import ( |
|
||||||
"math/big" |
|
||||||
|
|
||||||
"github.com/ethereum/go-ethereum/common" |
|
||||||
) |
|
||||||
|
|
||||||
// EVMLogger is used to collect execution traces from an EVM transaction
|
|
||||||
// execution. CaptureState is called for each step of the VM with the
|
|
||||||
// current VM state.
|
|
||||||
// Note that reference types are actual VM data structures; make copies
|
|
||||||
// if you need to retain them beyond the current call.
|
|
||||||
type EVMLogger interface { |
|
||||||
// Transaction level
|
|
||||||
CaptureTxStart(gasLimit uint64) |
|
||||||
CaptureTxEnd(restGas uint64) |
|
||||||
// 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) |
|
||||||
// Rest of call frames
|
|
||||||
CaptureEnter(typ OpCode, from common.Address, to common.Address, input []byte, gas uint64, value *big.Int) |
|
||||||
CaptureExit(output []byte, gasUsed uint64, err error) |
|
||||||
// Opcode level
|
|
||||||
CaptureState(pc uint64, op OpCode, gas, cost uint64, scope *ScopeContext, rData []byte, depth int, err error) |
|
||||||
CaptureFault(pc uint64, op OpCode, gas, cost uint64, scope *ScopeContext, depth int, err error) |
|
||||||
} |
|
@ -0,0 +1,10 @@ |
|||||||
|
# Filling test cases |
||||||
|
|
||||||
|
To fill test cases for the built-in tracers, the `makeTest.js` script can be used. Given a transaction on a dev/test network, `makeTest.js` will fetch its prestate and then traces with the given configuration. |
||||||
|
In the Geth console do: |
||||||
|
|
||||||
|
```terminal |
||||||
|
let tx = '0x...' |
||||||
|
loadScript('makeTest.js') |
||||||
|
makeTest(tx, { tracer: 'callTracer' }) |
||||||
|
``` |
@ -0,0 +1,48 @@ |
|||||||
|
// makeTest generates a test for the configured tracer by running
|
||||||
|
// a prestate reassembled and a call trace run, assembling all the
|
||||||
|
// gathered information into a test case.
|
||||||
|
var makeTest = function(tx, traceConfig) { |
||||||
|
// Generate the genesis block from the block, transaction and prestate data
|
||||||
|
var block = eth.getBlock(eth.getTransaction(tx).blockHash); |
||||||
|
var genesis = eth.getBlock(block.parentHash); |
||||||
|
|
||||||
|
delete genesis.gasUsed; |
||||||
|
delete genesis.logsBloom; |
||||||
|
delete genesis.parentHash; |
||||||
|
delete genesis.receiptsRoot; |
||||||
|
delete genesis.sha3Uncles; |
||||||
|
delete genesis.size; |
||||||
|
delete genesis.transactions; |
||||||
|
delete genesis.transactionsRoot; |
||||||
|
delete genesis.uncles; |
||||||
|
|
||||||
|
genesis.gasLimit = genesis.gasLimit.toString(); |
||||||
|
genesis.number = genesis.number.toString(); |
||||||
|
genesis.timestamp = genesis.timestamp.toString(); |
||||||
|
|
||||||
|
genesis.alloc = debug.traceTransaction(tx, {tracer: "prestateTracer"}); |
||||||
|
for (var key in genesis.alloc) { |
||||||
|
var nonce = genesis.alloc[key].nonce; |
||||||
|
if (nonce) { |
||||||
|
genesis.alloc[key].nonce = nonce.toString(); |
||||||
|
} |
||||||
|
} |
||||||
|
genesis.config = admin.nodeInfo.protocols.eth.config; |
||||||
|
|
||||||
|
// Generate the call trace and produce the test input
|
||||||
|
var result = debug.traceTransaction(tx, traceConfig); |
||||||
|
delete result.time; |
||||||
|
|
||||||
|
console.log(JSON.stringify({ |
||||||
|
genesis: genesis, |
||||||
|
context: { |
||||||
|
number: block.number.toString(), |
||||||
|
difficulty: block.difficulty, |
||||||
|
timestamp: block.timestamp.toString(), |
||||||
|
gasLimit: block.gasLimit.toString(), |
||||||
|
miner: block.miner, |
||||||
|
}, |
||||||
|
input: eth.getRawTransaction(tx), |
||||||
|
result: result, |
||||||
|
}, null, 2)); |
||||||
|
} |
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@ -0,0 +1,62 @@ |
|||||||
|
{ |
||||||
|
"genesis": { |
||||||
|
"baseFeePerGas": "875000000", |
||||||
|
"difficulty": "0", |
||||||
|
"extraData": "0xd983010d05846765746888676f312e32312e318664617277696e", |
||||||
|
"gasLimit": "11511229", |
||||||
|
"hash": "0xd462585c6c5a3b3bf14850ebcde71b6615b9aaf6541403f9a0457212dd0502e0", |
||||||
|
"miner": "0x0000000000000000000000000000000000000000", |
||||||
|
"mixHash": "0xfa51e868d6a7c0728f18800e4cc8d4cc1c87430cc9975e947eb6c9c03599b4e2", |
||||||
|
"nonce": "0x0000000000000000", |
||||||
|
"number": "1", |
||||||
|
"stateRoot": "0xd2ebe0a7f3572ffe3e5b4c78147376d3fca767f236e4dd23f9151acfec7cb0d1", |
||||||
|
"timestamp": "1699617692", |
||||||
|
"totalDifficulty": "0", |
||||||
|
"withdrawals": [], |
||||||
|
"withdrawalsRoot": "0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421", |
||||||
|
"alloc": { |
||||||
|
"0x0000000000000000000000000000000000000000": { |
||||||
|
"balance": "0x5208" |
||||||
|
}, |
||||||
|
"0xf39fd6e51aad88f6f4ce6ab8827279cfffb92266": { |
||||||
|
"balance": "0x8ac7230489e80000" |
||||||
|
} |
||||||
|
}, |
||||||
|
"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, |
||||||
|
"terminalTotalDifficulty": 0, |
||||||
|
"terminalTotalDifficultyPassed": true, |
||||||
|
"isDev": true |
||||||
|
} |
||||||
|
}, |
||||||
|
"context": { |
||||||
|
"number": "2", |
||||||
|
"difficulty": "0", |
||||||
|
"timestamp": "1699617847", |
||||||
|
"gasLimit": "11522469", |
||||||
|
"miner": "0x0000000000000000000000000000000000000000" |
||||||
|
}, |
||||||
|
"input": "0x02f902b48205398084b2d05e0085011b1f3f8083031ca88080b90258608060405234801561001057600080fd5b5060405161001d906100e3565b604051809103906000f080158015610039573d6000803e3d6000fd5b50600080546001600160a01b0319166001600160a01b039290921691821781556040517fc66247bafd1305823857fb4c3e651e684d918df8554ef560bbbcb025fdd017039190a26000546040516360fe47b160e01b8152600560048201526001600160a01b03909116906360fe47b190602401600060405180830381600087803b1580156100c657600080fd5b505af11580156100da573d6000803e3d6000fd5b505050506100ef565b60ca8061018e83390190565b6091806100fd6000396000f3fe6080604052348015600f57600080fd5b506004361060285760003560e01c806380de699314602d575b600080fd5b600054603f906001600160a01b031681565b6040516001600160a01b03909116815260200160405180910390f3fea2646970667358221220dab781465e7f4cf20304cc388130a763508e20edd25b4bc8ea8f57743a0de8da64736f6c634300081700336080604052348015600f57600080fd5b5060ac8061001e6000396000f3fe6080604052348015600f57600080fd5b506004361060325760003560e01c806360fe47b11460375780636d4ce63c146049575b600080fd5b60476042366004605e565b600055565b005b60005460405190815260200160405180910390f35b600060208284031215606f57600080fd5b503591905056fea264697066735822122049e09da6320793487d58eaa7b97f802618a062cbc35f08ca1ce92c17349141f864736f6c63430008170033c080a01d4fce93ad08bf413052645721f20e6136830cf5a2759fa57e76a134e90899a7a0399a72832d52118991dc04c4f9e1c0fec3d5e441ad7d4b055f0cf03130d8f815", |
||||||
|
"result": { |
||||||
|
"0x0000000000000000000000000000000000000000": { |
||||||
|
"balance": "0x5208" |
||||||
|
}, |
||||||
|
"0xf39fd6e51aad88f6f4ce6ab8827279cfffb92266": { |
||||||
|
"balance": "0x8ac7230489e80000" |
||||||
|
} |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,81 @@ |
|||||||
|
// Copyright 2023 The go-ethereum Authors
|
||||||
|
// This file is part of the go-ethereum library.
|
||||||
|
//
|
||||||
|
// The go-ethereum library is free software: you can redistribute it and/or modify
|
||||||
|
// it under the terms of the GNU Lesser General Public License as published by
|
||||||
|
// the Free Software Foundation, either version 3 of the License, or
|
||||||
|
// (at your option) any later version.
|
||||||
|
//
|
||||||
|
// The go-ethereum library is distributed in the hope that it will be useful,
|
||||||
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
// GNU Lesser General Public License for more details.
|
||||||
|
//
|
||||||
|
// You should have received a copy of the GNU Lesser General Public License
|
||||||
|
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
package internal |
||||||
|
|
||||||
|
import ( |
||||||
|
"errors" |
||||||
|
"fmt" |
||||||
|
|
||||||
|
"github.com/holiman/uint256" |
||||||
|
) |
||||||
|
|
||||||
|
const ( |
||||||
|
memoryPadLimit = 1024 * 1024 |
||||||
|
) |
||||||
|
|
||||||
|
// GetMemoryCopyPadded returns offset + size as a new slice.
|
||||||
|
// It zero-pads the slice if it extends beyond memory bounds.
|
||||||
|
func GetMemoryCopyPadded(m []byte, offset, size int64) ([]byte, error) { |
||||||
|
if offset < 0 || size < 0 { |
||||||
|
return nil, errors.New("offset or size must not be negative") |
||||||
|
} |
||||||
|
length := int64(len(m)) |
||||||
|
if offset+size < length { // slice fully inside memory
|
||||||
|
return memoryCopy(m, offset, size), nil |
||||||
|
} |
||||||
|
paddingNeeded := offset + size - length |
||||||
|
if paddingNeeded > memoryPadLimit { |
||||||
|
return nil, fmt.Errorf("reached limit for padding memory slice: %d", paddingNeeded) |
||||||
|
} |
||||||
|
cpy := make([]byte, size) |
||||||
|
if overlap := length - offset; overlap > 0 { |
||||||
|
copy(cpy, MemoryPtr(m, offset, overlap)) |
||||||
|
} |
||||||
|
return cpy, nil |
||||||
|
} |
||||||
|
|
||||||
|
func memoryCopy(m []byte, offset, size int64) (cpy []byte) { |
||||||
|
if size == 0 { |
||||||
|
return nil |
||||||
|
} |
||||||
|
|
||||||
|
if len(m) > int(offset) { |
||||||
|
cpy = make([]byte, size) |
||||||
|
copy(cpy, m[offset:offset+size]) |
||||||
|
|
||||||
|
return |
||||||
|
} |
||||||
|
|
||||||
|
return |
||||||
|
} |
||||||
|
|
||||||
|
// MemoryPtr returns a pointer to a slice of memory.
|
||||||
|
func MemoryPtr(m []byte, offset, size int64) []byte { |
||||||
|
if size == 0 { |
||||||
|
return nil |
||||||
|
} |
||||||
|
|
||||||
|
if len(m) > int(offset) { |
||||||
|
return m[offset : offset+size] |
||||||
|
} |
||||||
|
|
||||||
|
return nil |
||||||
|
} |
||||||
|
|
||||||
|
// Back returns the n'th item in stack
|
||||||
|
func StackBack(st []uint256.Int, n int) *uint256.Int { |
||||||
|
return &st[len(st)-n-1] |
||||||
|
} |
@ -0,0 +1,60 @@ |
|||||||
|
// Copyright 2023 The go-ethereum Authors
|
||||||
|
// This file is part of the go-ethereum library.
|
||||||
|
//
|
||||||
|
// The go-ethereum library is free software: you can redistribute it and/or modify
|
||||||
|
// it under the terms of the GNU Lesser General Public License as published by
|
||||||
|
// the Free Software Foundation, either version 3 of the License, or
|
||||||
|
// (at your option) any later version.
|
||||||
|
//
|
||||||
|
// The go-ethereum library is distributed in the hope that it will be useful,
|
||||||
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
// GNU Lesser General Public License for more details.
|
||||||
|
//
|
||||||
|
// You should have received a copy of the GNU Lesser General Public License
|
||||||
|
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
package internal |
||||||
|
|
||||||
|
import ( |
||||||
|
"testing" |
||||||
|
|
||||||
|
"github.com/ethereum/go-ethereum/core/vm" |
||||||
|
) |
||||||
|
|
||||||
|
func TestMemCopying(t *testing.T) { |
||||||
|
for i, tc := range []struct { |
||||||
|
memsize int64 |
||||||
|
offset int64 |
||||||
|
size int64 |
||||||
|
wantErr string |
||||||
|
wantSize int |
||||||
|
}{ |
||||||
|
{0, 0, 100, "", 100}, // Should pad up to 100
|
||||||
|
{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}, // 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() |
||||||
|
mem.Resize(uint64(tc.memsize)) |
||||||
|
cpy, err := GetMemoryCopyPadded(mem.Data(), tc.offset, tc.size) |
||||||
|
if want := tc.wantErr; want != "" { |
||||||
|
if err == nil { |
||||||
|
t.Fatalf("test %d: want '%v' have no error", i, want) |
||||||
|
} |
||||||
|
if have := err.Error(); want != have { |
||||||
|
t.Fatalf("test %d: want '%v' have '%v'", i, want, have) |
||||||
|
} |
||||||
|
continue |
||||||
|
} |
||||||
|
if err != nil { |
||||||
|
t.Fatalf("test %d: unexpected error: %v", i, err) |
||||||
|
} |
||||||
|
if want, have := tc.wantSize, len(cpy); have != want { |
||||||
|
t.Fatalf("test %d: want %v have %v", i, want, have) |
||||||
|
} |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,31 @@ |
|||||||
|
package tracers |
||||||
|
|
||||||
|
import ( |
||||||
|
"encoding/json" |
||||||
|
"errors" |
||||||
|
|
||||||
|
"github.com/ethereum/go-ethereum/core/tracing" |
||||||
|
) |
||||||
|
|
||||||
|
type ctorFunc func(config json.RawMessage) (*tracing.Hooks, error) |
||||||
|
|
||||||
|
// LiveDirectory is the collection of tracers which can be used
|
||||||
|
// during normal block import operations.
|
||||||
|
var LiveDirectory = liveDirectory{elems: make(map[string]ctorFunc)} |
||||||
|
|
||||||
|
type liveDirectory struct { |
||||||
|
elems map[string]ctorFunc |
||||||
|
} |
||||||
|
|
||||||
|
// Register registers a tracer constructor by name.
|
||||||
|
func (d *liveDirectory) Register(name string, f ctorFunc) { |
||||||
|
d.elems[name] = f |
||||||
|
} |
||||||
|
|
||||||
|
// New instantiates a tracer by name.
|
||||||
|
func (d *liveDirectory) New(name string, config json.RawMessage) (*tracing.Hooks, error) { |
||||||
|
if f, ok := d.elems[name]; ok { |
||||||
|
return f(config) |
||||||
|
} |
||||||
|
return nil, errors.New("not found") |
||||||
|
} |
@ -0,0 +1,96 @@ |
|||||||
|
package live |
||||||
|
|
||||||
|
import ( |
||||||
|
"encoding/json" |
||||||
|
"math/big" |
||||||
|
|
||||||
|
"github.com/ethereum/go-ethereum/common" |
||||||
|
"github.com/ethereum/go-ethereum/core/tracing" |
||||||
|
"github.com/ethereum/go-ethereum/core/types" |
||||||
|
"github.com/ethereum/go-ethereum/eth/tracers" |
||||||
|
"github.com/ethereum/go-ethereum/params" |
||||||
|
) |
||||||
|
|
||||||
|
func init() { |
||||||
|
tracers.LiveDirectory.Register("noop", newNoopTracer) |
||||||
|
} |
||||||
|
|
||||||
|
// noop is a no-op live tracer. It's there to
|
||||||
|
// catch changes in the tracing interface, as well as
|
||||||
|
// for testing live tracing performance. Can be removed
|
||||||
|
// as soon as we have a real live tracer.
|
||||||
|
type noop struct{} |
||||||
|
|
||||||
|
func newNoopTracer(_ json.RawMessage) (*tracing.Hooks, error) { |
||||||
|
t := &noop{} |
||||||
|
return &tracing.Hooks{ |
||||||
|
OnTxStart: t.OnTxStart, |
||||||
|
OnTxEnd: t.OnTxEnd, |
||||||
|
OnEnter: t.OnEnter, |
||||||
|
OnExit: t.OnExit, |
||||||
|
OnOpcode: t.OnOpcode, |
||||||
|
OnFault: t.OnFault, |
||||||
|
OnGasChange: t.OnGasChange, |
||||||
|
OnBlockchainInit: t.OnBlockchainInit, |
||||||
|
OnBlockStart: t.OnBlockStart, |
||||||
|
OnBlockEnd: t.OnBlockEnd, |
||||||
|
OnSkippedBlock: t.OnSkippedBlock, |
||||||
|
OnGenesisBlock: t.OnGenesisBlock, |
||||||
|
OnBalanceChange: t.OnBalanceChange, |
||||||
|
OnNonceChange: t.OnNonceChange, |
||||||
|
OnCodeChange: t.OnCodeChange, |
||||||
|
OnStorageChange: t.OnStorageChange, |
||||||
|
OnLog: t.OnLog, |
||||||
|
}, nil |
||||||
|
} |
||||||
|
|
||||||
|
func (t *noop) OnOpcode(pc uint64, op byte, gas, cost uint64, scope tracing.OpContext, rData []byte, depth int, err error) { |
||||||
|
} |
||||||
|
|
||||||
|
func (t *noop) OnFault(pc uint64, op byte, gas, cost uint64, _ tracing.OpContext, depth int, err error) { |
||||||
|
} |
||||||
|
|
||||||
|
func (t *noop) OnEnter(depth int, typ byte, from common.Address, to common.Address, input []byte, gas uint64, value *big.Int) { |
||||||
|
} |
||||||
|
|
||||||
|
func (t *noop) OnExit(depth int, output []byte, gasUsed uint64, err error, reverted bool) { |
||||||
|
} |
||||||
|
|
||||||
|
func (t *noop) OnTxStart(vm *tracing.VMContext, tx *types.Transaction, from common.Address) { |
||||||
|
} |
||||||
|
|
||||||
|
func (t *noop) OnTxEnd(receipt *types.Receipt, err error) { |
||||||
|
} |
||||||
|
|
||||||
|
func (t *noop) OnBlockStart(ev tracing.BlockEvent) { |
||||||
|
} |
||||||
|
|
||||||
|
func (t *noop) OnBlockEnd(err error) { |
||||||
|
} |
||||||
|
|
||||||
|
func (t *noop) OnSkippedBlock(ev tracing.BlockEvent) {} |
||||||
|
|
||||||
|
func (t *noop) OnBlockchainInit(chainConfig *params.ChainConfig) { |
||||||
|
} |
||||||
|
|
||||||
|
func (t *noop) OnGenesisBlock(b *types.Block, alloc types.GenesisAlloc) { |
||||||
|
} |
||||||
|
|
||||||
|
func (t *noop) OnBalanceChange(a common.Address, prev, new *big.Int, reason tracing.BalanceChangeReason) { |
||||||
|
} |
||||||
|
|
||||||
|
func (t *noop) OnNonceChange(a common.Address, prev, new uint64) { |
||||||
|
} |
||||||
|
|
||||||
|
func (t *noop) OnCodeChange(a common.Address, prevCodeHash common.Hash, prev []byte, codeHash common.Hash, code []byte) { |
||||||
|
} |
||||||
|
|
||||||
|
func (t *noop) OnStorageChange(a common.Address, k, prev, new common.Hash) { |
||||||
|
} |
||||||
|
|
||||||
|
func (t *noop) OnLog(l *types.Log) { |
||||||
|
|
||||||
|
} |
||||||
|
|
||||||
|
func (t *noop) OnGasChange(old, new uint64, reason tracing.GasChangeReason) { |
||||||
|
} |
Loading…
Reference in new issue