mirror of
https://github.com/ethereum/go-ethereum.git
synced 2026-07-28 07:36:44 +00:00
full struct-based tracing infra
This commit is contained in:
parent
8997f51d49
commit
fc35780124
41 changed files with 585 additions and 454 deletions
|
|
@ -191,8 +191,7 @@ func makeFullNode(ctx *cli.Context) (*node.Node, ethapi.Backend) {
|
|||
utils.Fatalf("Failed to create tracer %q: %v", name, err)
|
||||
}
|
||||
|
||||
cfg.Eth.VMTracer = t.VMLogger
|
||||
cfg.Eth.LiveLogger = t
|
||||
cfg.Eth.VMTracer = t
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -2141,8 +2141,7 @@ func MakeChain(ctx *cli.Context, stack *node.Node, readonly bool) (*core.BlockCh
|
|||
if err != nil {
|
||||
Fatalf("Failed to create tracer %q: %v", name, err)
|
||||
}
|
||||
vmcfg.LiveLogger = t
|
||||
vmcfg.Tracer = t.VMLogger
|
||||
vmcfg.Tracer = t
|
||||
}
|
||||
}
|
||||
// Disable transaction indexing/unindexing by default.
|
||||
|
|
|
|||
|
|
@ -27,6 +27,7 @@ import (
|
|||
"github.com/ethereum/go-ethereum/consensus/misc/eip4844"
|
||||
"github.com/ethereum/go-ethereum/core/state"
|
||||
"github.com/ethereum/go-ethereum/core/types"
|
||||
"github.com/ethereum/go-ethereum/eth/tracers/directory/live"
|
||||
"github.com/ethereum/go-ethereum/params"
|
||||
"github.com/ethereum/go-ethereum/rpc"
|
||||
"github.com/ethereum/go-ethereum/trie"
|
||||
|
|
@ -358,7 +359,7 @@ func (beacon *Beacon) Finalize(chain consensus.ChainHeaderReader, header *types.
|
|||
// Convert amount from gwei to wei.
|
||||
amount := new(uint256.Int).SetUint64(w.Amount)
|
||||
amount = amount.Mul(amount, uint256.NewInt(params.GWei))
|
||||
stateDB.AddBalance(w.Address, amount, state.BalanceIncreaseWithdrawal)
|
||||
stateDB.AddBalance(w.Address, amount, live.BalanceIncreaseWithdrawal)
|
||||
}
|
||||
// No block reward which is issued by consensus layer instead.
|
||||
}
|
||||
|
|
|
|||
|
|
@ -30,6 +30,7 @@ import (
|
|||
"github.com/ethereum/go-ethereum/consensus/misc/eip1559"
|
||||
"github.com/ethereum/go-ethereum/core/state"
|
||||
"github.com/ethereum/go-ethereum/core/types"
|
||||
"github.com/ethereum/go-ethereum/eth/tracers/directory/live"
|
||||
"github.com/ethereum/go-ethereum/params"
|
||||
"github.com/ethereum/go-ethereum/rlp"
|
||||
"github.com/ethereum/go-ethereum/trie"
|
||||
|
|
@ -589,10 +590,10 @@ func accumulateRewards(config *params.ChainConfig, stateDB *state.StateDB, heade
|
|||
r.Sub(r, hNum)
|
||||
r.Mul(r, blockReward)
|
||||
r.Div(r, u256_8)
|
||||
stateDB.AddBalance(uncle.Coinbase, r, state.BalanceIncreaseRewardMineUncle)
|
||||
stateDB.AddBalance(uncle.Coinbase, r, live.BalanceIncreaseRewardMineUncle)
|
||||
|
||||
r.Div(blockReward, u256_32)
|
||||
reward.Add(reward, r)
|
||||
}
|
||||
stateDB.AddBalance(header.Coinbase, reward, state.BalanceIncreaseRewardMineBlock)
|
||||
stateDB.AddBalance(header.Coinbase, reward, live.BalanceIncreaseRewardMineBlock)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -23,6 +23,7 @@ import (
|
|||
|
||||
"github.com/ethereum/go-ethereum/core/state"
|
||||
"github.com/ethereum/go-ethereum/core/types"
|
||||
"github.com/ethereum/go-ethereum/eth/tracers/directory/live"
|
||||
"github.com/ethereum/go-ethereum/params"
|
||||
"github.com/holiman/uint256"
|
||||
)
|
||||
|
|
@ -81,7 +82,7 @@ func ApplyDAOHardFork(statedb *state.StateDB) {
|
|||
|
||||
// Move every DAO account and extra-balance account funds into the refund contract
|
||||
for _, addr := range params.DAODrainList() {
|
||||
statedb.AddBalance(params.DAORefundContract, statedb.GetBalance(addr), state.BalanceIncreaseDaoContract)
|
||||
statedb.SetBalance(addr, new(uint256.Int), state.BalanceDecreaseDaoAccount)
|
||||
statedb.AddBalance(params.DAORefundContract, statedb.GetBalance(addr), live.BalanceIncreaseDaoContract)
|
||||
statedb.SetBalance(addr, new(uint256.Int), live.BalanceDecreaseDaoAccount)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -186,32 +186,6 @@ func DefaultCacheConfigWithScheme(scheme string) *CacheConfig {
|
|||
return &config
|
||||
}
|
||||
|
||||
// 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
|
||||
}
|
||||
|
||||
// BlockchainLogger is used to collect traces during chain processing.
|
||||
// Please make a copy of the referenced types if you intend to retain them.
|
||||
type BlockchainLogger interface {
|
||||
vm.EVMLogger
|
||||
state.StateLogger
|
||||
OnBlockchainInit(chainConfig *params.ChainConfig)
|
||||
// OnBlockStart is called before executing `block`.
|
||||
// `td` is the total difficulty prior to `block`.
|
||||
OnBlockStart(event BlockEvent)
|
||||
OnBlockEnd(err error)
|
||||
// OnSkippedBlock indicates a block was skipped during processing
|
||||
// due to it being known previously. This can happen e.g. when recovering
|
||||
// from a crash.
|
||||
OnSkippedBlock(event BlockEvent)
|
||||
OnGenesisBlock(genesis *types.Block, alloc GenesisAlloc)
|
||||
}
|
||||
|
||||
// txLookup is wrapper over transaction lookup along with the corresponding
|
||||
// transaction object.
|
||||
type txLookup struct {
|
||||
|
|
@ -330,7 +304,7 @@ func NewBlockChain(db ethdb.Database, cacheConfig *CacheConfig, genesis *Genesis
|
|||
futureBlocks: lru.NewCache[common.Hash, *types.Block](maxFutureBlocks),
|
||||
engine: engine,
|
||||
vmConfig: vmConfig,
|
||||
logger: vmConfig.LiveLogger,
|
||||
logger: vmConfig.Tracer,
|
||||
}
|
||||
bc.flushInterval.Store(int64(cacheConfig.TrieTimeLimit))
|
||||
bc.forker = NewForkChoice(bc, shouldPreserve)
|
||||
|
|
@ -1771,7 +1745,7 @@ func (bc *BlockChain) insertChain(chain types.Blocks, setHead bool) (int, error)
|
|||
}
|
||||
stats.processed++
|
||||
if bc.logger != nil && bc.logger.OnSkippedBlock != nil {
|
||||
bc.logger.OnSkippedBlock(BlockEvent{
|
||||
bc.logger.OnSkippedBlock(live.BlockEvent{
|
||||
Block: block,
|
||||
TD: bc.GetTd(block.ParentHash(), block.NumberU64()-1),
|
||||
Finalized: bc.CurrentFinalBlock(),
|
||||
|
|
@ -1904,7 +1878,7 @@ type blockProcessingResult struct {
|
|||
func (bc *BlockChain) processBlock(block *types.Block, statedb *state.StateDB, start time.Time, setHead bool) (_ *blockProcessingResult, blockEndErr error) {
|
||||
if bc.logger != nil && bc.logger.OnBlockStart != nil {
|
||||
td := bc.GetTd(block.ParentHash(), block.NumberU64()-1)
|
||||
bc.logger.OnBlockStart(BlockEvent{
|
||||
bc.logger.OnBlockStart(live.BlockEvent{
|
||||
Block: block,
|
||||
TD: td,
|
||||
Finalized: bc.CurrentFinalBlock(),
|
||||
|
|
|
|||
|
|
@ -101,7 +101,7 @@ func (b *BlockGen) SetParentBeaconRoot(root common.Hash) {
|
|||
blockContext = NewEVMBlockContext(b.header, b.cm, &b.header.Coinbase)
|
||||
vmenv = vm.NewEVM(blockContext, vm.TxContext{}, b.statedb, b.cm.config, vm.Config{})
|
||||
)
|
||||
ProcessBeaconBlockRoot(root, vmenv, b.statedb, nil)
|
||||
ProcessBeaconBlockRoot(root, vmenv, b.statedb)
|
||||
}
|
||||
|
||||
// addTx adds a transaction to the generated block. If no coinbase has
|
||||
|
|
|
|||
|
|
@ -22,9 +22,9 @@ import (
|
|||
"github.com/ethereum/go-ethereum/common"
|
||||
"github.com/ethereum/go-ethereum/consensus"
|
||||
"github.com/ethereum/go-ethereum/consensus/misc/eip4844"
|
||||
"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/directory/live"
|
||||
"github.com/holiman/uint256"
|
||||
)
|
||||
|
||||
|
|
@ -137,6 +137,6 @@ func CanTransfer(db vm.StateDB, addr common.Address, amount *uint256.Int) bool {
|
|||
|
||||
// Transfer subtracts amount from sender and adds amount to recipient using the given Db
|
||||
func Transfer(db vm.StateDB, sender, recipient common.Address, amount *uint256.Int) {
|
||||
db.SubBalance(sender, amount, state.BalanceChangeTransfer)
|
||||
db.AddBalance(recipient, amount, state.BalanceChangeTransfer)
|
||||
db.SubBalance(sender, amount, live.BalanceChangeTransfer)
|
||||
db.AddBalance(recipient, amount, live.BalanceChangeTransfer)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -31,6 +31,7 @@ import (
|
|||
"github.com/ethereum/go-ethereum/core/state"
|
||||
"github.com/ethereum/go-ethereum/core/types"
|
||||
"github.com/ethereum/go-ethereum/crypto"
|
||||
"github.com/ethereum/go-ethereum/eth/tracers/directory/live"
|
||||
"github.com/ethereum/go-ethereum/ethdb"
|
||||
"github.com/ethereum/go-ethereum/log"
|
||||
"github.com/ethereum/go-ethereum/params"
|
||||
|
|
@ -133,7 +134,7 @@ func hashAlloc(ga *types.GenesisAlloc, isVerkle bool) (common.Hash, error) {
|
|||
}
|
||||
for addr, account := range *ga {
|
||||
if account.Balance != nil {
|
||||
statedb.AddBalance(addr, uint256.MustFromBig(account.Balance), state.BalanceIncreaseGenesisBalance)
|
||||
statedb.AddBalance(addr, uint256.MustFromBig(account.Balance), live.BalanceIncreaseGenesisBalance)
|
||||
}
|
||||
statedb.SetCode(addr, account.Code)
|
||||
statedb.SetNonce(addr, account.Nonce)
|
||||
|
|
@ -156,7 +157,7 @@ func flushAlloc(ga *types.GenesisAlloc, db ethdb.Database, triedb *triedb.Databa
|
|||
if account.Balance != nil {
|
||||
// This is not actually logged via tracer because OnGenesisBlock
|
||||
// already captures the allocations.
|
||||
statedb.AddBalance(addr, uint256.MustFromBig(account.Balance), state.BalanceIncreaseGenesisBalance)
|
||||
statedb.AddBalance(addr, uint256.MustFromBig(account.Balance), live.BalanceIncreaseGenesisBalance)
|
||||
}
|
||||
statedb.SetCode(addr, account.Code)
|
||||
statedb.SetNonce(addr, account.Nonce)
|
||||
|
|
|
|||
|
|
@ -57,7 +57,6 @@ type DumpAccount struct {
|
|||
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
|
||||
|
||||
}
|
||||
|
||||
// Dump represents the full dump in a collected format, as one large map.
|
||||
|
|
|
|||
|
|
@ -1,66 +0,0 @@
|
|||
// 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 state
|
||||
|
||||
// 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
|
||||
)
|
||||
|
|
@ -25,6 +25,7 @@ import (
|
|||
"github.com/ethereum/go-ethereum/common"
|
||||
"github.com/ethereum/go-ethereum/core/types"
|
||||
"github.com/ethereum/go-ethereum/crypto"
|
||||
"github.com/ethereum/go-ethereum/eth/tracers/directory/live"
|
||||
"github.com/ethereum/go-ethereum/metrics"
|
||||
"github.com/ethereum/go-ethereum/rlp"
|
||||
"github.com/ethereum/go-ethereum/trie/trienode"
|
||||
|
|
@ -408,7 +409,7 @@ func (s *stateObject) commit() (*trienode.NodeSet, error) {
|
|||
|
||||
// AddBalance adds amount to s's balance.
|
||||
// It is used to add funds to the destination account of a transfer.
|
||||
func (s *stateObject) AddBalance(amount *uint256.Int, reason BalanceChangeReason) {
|
||||
func (s *stateObject) AddBalance(amount *uint256.Int, reason live.BalanceChangeReason) {
|
||||
// EIP161: We must check emptiness for the objects such that the account
|
||||
// clearing (0,0,0 objects) can take effect.
|
||||
if amount.IsZero() {
|
||||
|
|
@ -422,14 +423,14 @@ func (s *stateObject) AddBalance(amount *uint256.Int, reason BalanceChangeReason
|
|||
|
||||
// SubBalance removes amount from s's balance.
|
||||
// It is used to remove funds from the origin account of a transfer.
|
||||
func (s *stateObject) SubBalance(amount *uint256.Int, reason BalanceChangeReason) {
|
||||
func (s *stateObject) SubBalance(amount *uint256.Int, reason live.BalanceChangeReason) {
|
||||
if amount.IsZero() {
|
||||
return
|
||||
}
|
||||
s.SetBalance(new(uint256.Int).Sub(s.Balance(), amount), reason)
|
||||
}
|
||||
|
||||
func (s *stateObject) SetBalance(amount *uint256.Int, reason BalanceChangeReason) {
|
||||
func (s *stateObject) SetBalance(amount *uint256.Int, reason live.BalanceChangeReason) {
|
||||
s.db.journal.append(balanceChange{
|
||||
account: &s.address,
|
||||
prev: new(uint256.Int).Set(s.data.Balance),
|
||||
|
|
|
|||
|
|
@ -49,20 +49,6 @@ type revision struct {
|
|||
journalIndex int
|
||||
}
|
||||
|
||||
// StateLogger is used to collect state update traces from EVM transaction
|
||||
// execution.
|
||||
// The following hooks are invoked post execution. I.e. looking up state
|
||||
// after the hook should reflect the new value.
|
||||
// Note that reference types are actual VM data structures; make copies
|
||||
// if you need to retain them beyond the current call.
|
||||
type StateLogger interface {
|
||||
OnBalanceChange(addr common.Address, prev, new *big.Int, reason BalanceChangeReason)
|
||||
OnNonceChange(addr common.Address, prev, new uint64)
|
||||
OnCodeChange(addr common.Address, prevCodeHash common.Hash, prevCode []byte, codeHash common.Hash, code []byte)
|
||||
OnStorageChange(addr common.Address, slot common.Hash, prev, new common.Hash)
|
||||
OnLog(log *types.Log)
|
||||
}
|
||||
|
||||
// StateDB structs within the ethereum protocol are used to store anything
|
||||
// within the merkle trie. StateDBs take care of caching and storing
|
||||
// nested states. It's the general query interface to retrieve:
|
||||
|
|
@ -398,7 +384,7 @@ func (s *StateDB) HasSelfDestructed(addr common.Address) bool {
|
|||
*/
|
||||
|
||||
// AddBalance adds amount to the account associated with addr.
|
||||
func (s *StateDB) AddBalance(addr common.Address, amount *uint256.Int, reason BalanceChangeReason) {
|
||||
func (s *StateDB) AddBalance(addr common.Address, amount *uint256.Int, reason live.BalanceChangeReason) {
|
||||
stateObject := s.getOrNewStateObject(addr)
|
||||
if stateObject != nil {
|
||||
stateObject.AddBalance(amount, reason)
|
||||
|
|
@ -406,14 +392,14 @@ func (s *StateDB) AddBalance(addr common.Address, amount *uint256.Int, reason Ba
|
|||
}
|
||||
|
||||
// SubBalance subtracts amount from the account associated with addr.
|
||||
func (s *StateDB) SubBalance(addr common.Address, amount *uint256.Int, reason BalanceChangeReason) {
|
||||
func (s *StateDB) SubBalance(addr common.Address, amount *uint256.Int, reason live.BalanceChangeReason) {
|
||||
stateObject := s.getOrNewStateObject(addr)
|
||||
if stateObject != nil {
|
||||
stateObject.SubBalance(amount, reason)
|
||||
}
|
||||
}
|
||||
|
||||
func (s *StateDB) SetBalance(addr common.Address, amount *uint256.Int, reason BalanceChangeReason) {
|
||||
func (s *StateDB) SetBalance(addr common.Address, amount *uint256.Int, reason live.BalanceChangeReason) {
|
||||
stateObject := s.getOrNewStateObject(addr)
|
||||
if stateObject != nil {
|
||||
stateObject.SetBalance(amount, reason)
|
||||
|
|
@ -482,7 +468,7 @@ func (s *StateDB) SelfDestruct(addr common.Address) {
|
|||
prevbalance: prev,
|
||||
})
|
||||
if s.logger != nil && s.logger.OnBalanceChange != nil && prev.Sign() > 0 {
|
||||
s.logger.OnBalanceChange(addr, prev.ToBig(), n.ToBig(), BalanceDecreaseSelfdestruct)
|
||||
s.logger.OnBalanceChange(addr, prev.ToBig(), n.ToBig(), live.BalanceDecreaseSelfdestruct)
|
||||
}
|
||||
stateObject.markSelfdestructed()
|
||||
stateObject.data.Balance = n
|
||||
|
|
@ -868,7 +854,7 @@ func (s *StateDB) Finalise(deleteEmptyObjects bool) {
|
|||
|
||||
// If ether was sent to account post-selfdestruct it is burnt.
|
||||
if bal := obj.Balance(); s.logger != nil && s.logger.OnBalanceChange != nil && obj.selfDestructed && bal.Sign() != 0 {
|
||||
s.logger.OnBalanceChange(obj.address, bal.ToBig(), new(big.Int), BalanceDecreaseSelfdestructBurn)
|
||||
s.logger.OnBalanceChange(obj.address, bal.ToBig(), new(big.Int), live.BalanceDecreaseSelfdestructBurn)
|
||||
}
|
||||
// We need to maintain account deletions explicitly (will remain
|
||||
// set indefinitely). Note only the first occurred self-destruct
|
||||
|
|
|
|||
|
|
@ -28,6 +28,7 @@ import (
|
|||
"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/directory/live"
|
||||
"github.com/ethereum/go-ethereum/params"
|
||||
)
|
||||
|
||||
|
|
@ -78,7 +79,7 @@ func (p *StateProcessor) Process(block *types.Block, statedb *state.StateDB, cfg
|
|||
signer = types.MakeSigner(p.config, header.Number, header.Time)
|
||||
)
|
||||
if beaconRoot := block.BeaconRoot(); beaconRoot != nil {
|
||||
ProcessBeaconBlockRoot(*beaconRoot, vmenv, statedb, p.bc.logger)
|
||||
ProcessBeaconBlockRoot(*beaconRoot, vmenv, statedb)
|
||||
}
|
||||
// Iterate over and process the individual transactions
|
||||
for i, tx := range block.Transactions() {
|
||||
|
|
@ -110,11 +111,20 @@ func (p *StateProcessor) Process(block *types.Block, statedb *state.StateDB, cfg
|
|||
// and uses the input parameters for its environment similar to ApplyTransaction. However,
|
||||
// this method takes an already created EVM instance as input.
|
||||
func ApplyTransactionWithEVM(msg *Message, config *params.ChainConfig, gp *GasPool, statedb *state.StateDB, blockNumber *big.Int, blockHash common.Hash, tx *types.Transaction, usedGas *uint64, evm *vm.EVM) (receipt *types.Receipt, err error) {
|
||||
if evm.Config.Tracer != nil {
|
||||
evm.Config.Tracer.CaptureTxStart(evm, tx, msg.From)
|
||||
defer func() {
|
||||
evm.Config.Tracer.CaptureTxEnd(receipt, err)
|
||||
}()
|
||||
if evm.Config.Tracer != nil && evm.Config.Tracer.CaptureTxStart != nil {
|
||||
evm.Config.Tracer.CaptureTxStart(&live.VMContext{
|
||||
ChainConfig: evm.ChainConfig(),
|
||||
StateDB: statedb,
|
||||
BlockNumber: evm.Context.BlockNumber,
|
||||
Time: evm.Context.Time,
|
||||
Coinbase: evm.Context.Coinbase,
|
||||
Random: evm.Context.Random,
|
||||
}, tx, msg.From)
|
||||
if evm.Config.Tracer.CaptureTxEnd != nil {
|
||||
defer func() {
|
||||
evm.Config.Tracer.CaptureTxEnd(receipt, err)
|
||||
}()
|
||||
}
|
||||
}
|
||||
// Create a new context to be used in the EVM environment.
|
||||
txContext := NewEVMTxContext(msg)
|
||||
|
|
@ -183,7 +193,7 @@ func ApplyTransaction(config *params.ChainConfig, bc ChainContext, author *commo
|
|||
|
||||
// ProcessBeaconBlockRoot applies the EIP-4788 system call to the beacon block root
|
||||
// contract. This method is exported to be used in tests.
|
||||
func ProcessBeaconBlockRoot(beaconRoot common.Hash, vmenv *vm.EVM, statedb *state.StateDB, logger BlockchainLogger) {
|
||||
func ProcessBeaconBlockRoot(beaconRoot common.Hash, vmenv *vm.EVM, statedb *state.StateDB) {
|
||||
// If EIP-4788 is enabled, we need to invoke the beaconroot storage contract with
|
||||
// the new root
|
||||
msg := &Message{
|
||||
|
|
|
|||
|
|
@ -23,10 +23,10 @@ import (
|
|||
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
cmath "github.com/ethereum/go-ethereum/common/math"
|
||||
"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/crypto/kzg4844"
|
||||
"github.com/ethereum/go-ethereum/eth/tracers/directory/live"
|
||||
"github.com/ethereum/go-ethereum/params"
|
||||
"github.com/holiman/uint256"
|
||||
)
|
||||
|
|
@ -268,15 +268,15 @@ func (st *StateTransition) buyGas() error {
|
|||
return err
|
||||
}
|
||||
|
||||
if st.evm.Config.Tracer != nil {
|
||||
st.evm.Config.Tracer.OnGasChange(0, st.msg.GasLimit, vm.GasChangeTxInitialBalance)
|
||||
if st.evm.Config.Tracer != nil && st.evm.Config.Tracer.OnGasChange != nil {
|
||||
st.evm.Config.Tracer.OnGasChange(0, st.msg.GasLimit, live.GasChangeTxInitialBalance)
|
||||
}
|
||||
|
||||
st.gasRemaining += st.msg.GasLimit
|
||||
|
||||
st.initialGas = st.msg.GasLimit
|
||||
mgvalU256, _ := uint256.FromBig(mgval)
|
||||
st.state.SubBalance(st.msg.From, mgvalU256, state.BalanceDecreaseGasBuy)
|
||||
st.state.SubBalance(st.msg.From, mgvalU256, live.BalanceDecreaseGasBuy)
|
||||
return nil
|
||||
}
|
||||
|
||||
|
|
@ -404,8 +404,8 @@ func (st *StateTransition) TransitionDb() (*ExecutionResult, error) {
|
|||
if st.gasRemaining < gas {
|
||||
return nil, fmt.Errorf("%w: have %d, want %d", ErrIntrinsicGas, st.gasRemaining, gas)
|
||||
}
|
||||
if t := st.evm.Config.Tracer; t != nil {
|
||||
t.OnGasChange(st.gasRemaining, st.gasRemaining-gas, vm.GasChangeTxIntrinsicGas)
|
||||
if t := st.evm.Config.Tracer; t != nil && t.OnGasChange != nil {
|
||||
t.OnGasChange(st.gasRemaining, st.gasRemaining-gas, live.GasChangeTxIntrinsicGas)
|
||||
}
|
||||
st.gasRemaining -= gas
|
||||
|
||||
|
|
@ -461,7 +461,7 @@ func (st *StateTransition) TransitionDb() (*ExecutionResult, error) {
|
|||
} else {
|
||||
fee := new(uint256.Int).SetUint64(st.gasUsed())
|
||||
fee.Mul(fee, effectiveTipU256)
|
||||
st.state.AddBalance(st.evm.Context.Coinbase, fee, state.BalanceIncreaseRewardTransactionFee)
|
||||
st.state.AddBalance(st.evm.Context.Coinbase, fee, live.BalanceIncreaseRewardTransactionFee)
|
||||
}
|
||||
|
||||
return &ExecutionResult{
|
||||
|
|
@ -479,8 +479,8 @@ func (st *StateTransition) refundGas(refundQuotient uint64) uint64 {
|
|||
refund = st.state.GetRefund()
|
||||
}
|
||||
|
||||
if st.evm.Config.Tracer != nil && refund > 0 {
|
||||
st.evm.Config.Tracer.OnGasChange(st.gasRemaining, st.gasRemaining+refund, vm.GasChangeTxRefunds)
|
||||
if st.evm.Config.Tracer != nil && st.evm.Config.Tracer.OnGasChange != nil && refund > 0 {
|
||||
st.evm.Config.Tracer.OnGasChange(st.gasRemaining, st.gasRemaining+refund, live.GasChangeTxRefunds)
|
||||
}
|
||||
|
||||
st.gasRemaining += refund
|
||||
|
|
@ -488,10 +488,10 @@ func (st *StateTransition) refundGas(refundQuotient uint64) uint64 {
|
|||
// Return ETH for remaining gas, exchanged at the original rate.
|
||||
remaining := uint256.NewInt(st.gasRemaining)
|
||||
remaining = remaining.Mul(remaining, uint256.MustFromBig(st.msg.GasPrice))
|
||||
st.state.AddBalance(st.msg.From, remaining, state.BalanceIncreaseGasReturn)
|
||||
st.state.AddBalance(st.msg.From, remaining, live.BalanceIncreaseGasReturn)
|
||||
|
||||
if st.evm.Config.Tracer != nil && st.gasRemaining > 0 {
|
||||
st.evm.Config.Tracer.OnGasChange(st.gasRemaining, 0, vm.GasChangeTxLeftOverReturned)
|
||||
if st.evm.Config.Tracer != nil && st.evm.Config.Tracer.OnGasChange != nil && st.gasRemaining > 0 {
|
||||
st.evm.Config.Tracer.OnGasChange(st.gasRemaining, 0, live.GasChangeTxLeftOverReturned)
|
||||
}
|
||||
|
||||
// Also return remaining gas to the block gas counter so it is
|
||||
|
|
|
|||
|
|
@ -18,6 +18,7 @@ package vm
|
|||
|
||||
import (
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
"github.com/ethereum/go-ethereum/eth/tracers/directory/live"
|
||||
"github.com/holiman/uint256"
|
||||
)
|
||||
|
||||
|
|
@ -157,11 +158,11 @@ func (c *Contract) Caller() common.Address {
|
|||
}
|
||||
|
||||
// UseGas attempts the use gas and subtracts it and returns true on success
|
||||
func (c *Contract) UseGas(gas uint64, logger EVMLogger, reason GasChangeReason) (ok bool) {
|
||||
func (c *Contract) UseGas(gas uint64, logger *live.LiveLogger, reason live.GasChangeReason) (ok bool) {
|
||||
if c.Gas < gas {
|
||||
return false
|
||||
}
|
||||
if logger != nil && reason != GasChangeIgnored {
|
||||
if logger != nil && logger.OnGasChange != nil && reason != live.GasChangeIgnored {
|
||||
logger.OnGasChange(c.Gas, c.Gas-gas, reason)
|
||||
}
|
||||
c.Gas -= gas
|
||||
|
|
|
|||
|
|
@ -30,6 +30,7 @@ import (
|
|||
"github.com/ethereum/go-ethereum/crypto/bls12381"
|
||||
"github.com/ethereum/go-ethereum/crypto/bn256"
|
||||
"github.com/ethereum/go-ethereum/crypto/kzg4844"
|
||||
"github.com/ethereum/go-ethereum/eth/tracers/directory/live"
|
||||
"github.com/ethereum/go-ethereum/params"
|
||||
"golang.org/x/crypto/ripemd160"
|
||||
)
|
||||
|
|
@ -168,13 +169,13 @@ func ActivePrecompiles(rules params.Rules) []common.Address {
|
|||
// - the returned bytes,
|
||||
// - the _remaining_ gas,
|
||||
// - any error that occurred
|
||||
func RunPrecompiledContract(p PrecompiledContract, input []byte, suppliedGas uint64, logger EVMLogger) (ret []byte, remainingGas uint64, err error) {
|
||||
func RunPrecompiledContract(p PrecompiledContract, input []byte, suppliedGas uint64, logger *live.LiveLogger) (ret []byte, remainingGas uint64, err error) {
|
||||
gasCost := p.RequiredGas(input)
|
||||
if suppliedGas < gasCost {
|
||||
return nil, 0, ErrOutOfGas
|
||||
}
|
||||
if logger != nil {
|
||||
logger.OnGasChange(suppliedGas, suppliedGas-gasCost, GasChangeCallPrecompiledContract)
|
||||
if logger != nil && logger.OnGasChange != nil {
|
||||
logger.OnGasChange(suppliedGas, suppliedGas-gasCost, live.GasChangeCallPrecompiledContract)
|
||||
}
|
||||
suppliedGas -= gasCost
|
||||
output, err := p.Run(input)
|
||||
|
|
|
|||
|
|
@ -22,9 +22,9 @@ import (
|
|||
"sync/atomic"
|
||||
|
||||
"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/crypto"
|
||||
"github.com/ethereum/go-ethereum/eth/tracers/directory/live"
|
||||
"github.com/ethereum/go-ethereum/params"
|
||||
"github.com/holiman/uint256"
|
||||
)
|
||||
|
|
@ -230,8 +230,8 @@ func (evm *EVM) Call(caller ContractRef, addr common.Address, input []byte, gas
|
|||
if err != nil {
|
||||
evm.StateDB.RevertToSnapshot(snapshot)
|
||||
if err != ErrExecutionReverted {
|
||||
if evm.Config.Tracer != nil {
|
||||
evm.Config.Tracer.OnGasChange(gas, 0, GasChangeCallFailedExecution)
|
||||
if evm.Config.Tracer != nil && evm.Config.Tracer.OnGasChange != nil {
|
||||
evm.Config.Tracer.OnGasChange(gas, 0, live.GasChangeCallFailedExecution)
|
||||
}
|
||||
|
||||
gas = 0
|
||||
|
|
@ -286,8 +286,8 @@ func (evm *EVM) CallCode(caller ContractRef, addr common.Address, input []byte,
|
|||
if err != nil {
|
||||
evm.StateDB.RevertToSnapshot(snapshot)
|
||||
if err != ErrExecutionReverted {
|
||||
if evm.Config.Tracer != nil {
|
||||
evm.Config.Tracer.OnGasChange(gas, 0, GasChangeCallFailedExecution)
|
||||
if evm.Config.Tracer != nil && evm.Config.Tracer.OnGasChange != nil {
|
||||
evm.Config.Tracer.OnGasChange(gas, 0, live.GasChangeCallFailedExecution)
|
||||
}
|
||||
|
||||
gas = 0
|
||||
|
|
@ -333,8 +333,8 @@ func (evm *EVM) DelegateCall(caller ContractRef, addr common.Address, input []by
|
|||
if err != nil {
|
||||
evm.StateDB.RevertToSnapshot(snapshot)
|
||||
if err != ErrExecutionReverted {
|
||||
if evm.Config.Tracer != nil {
|
||||
evm.Config.Tracer.OnGasChange(gas, 0, GasChangeCallFailedExecution)
|
||||
if evm.Config.Tracer != nil && evm.Config.Tracer.OnGasChange != nil {
|
||||
evm.Config.Tracer.OnGasChange(gas, 0, live.GasChangeCallFailedExecution)
|
||||
}
|
||||
gas = 0
|
||||
}
|
||||
|
|
@ -369,7 +369,7 @@ func (evm *EVM) StaticCall(caller ContractRef, addr common.Address, input []byte
|
|||
// This doesn't matter on Mainnet, where all empties are gone at the time of Byzantium,
|
||||
// but is the correct thing to do and matters on other networks, in tests, and potential
|
||||
// future scenarios
|
||||
evm.StateDB.AddBalance(addr, new(uint256.Int), state.BalanceChangeTouchAccount)
|
||||
evm.StateDB.AddBalance(addr, new(uint256.Int), live.BalanceChangeTouchAccount)
|
||||
|
||||
if p, isPrecompile := evm.precompile(addr); isPrecompile {
|
||||
ret, gas, err = RunPrecompiledContract(p, input, gas, evm.Config.Tracer)
|
||||
|
|
@ -391,8 +391,8 @@ func (evm *EVM) StaticCall(caller ContractRef, addr common.Address, input []byte
|
|||
if err != nil {
|
||||
evm.StateDB.RevertToSnapshot(snapshot)
|
||||
if err != ErrExecutionReverted {
|
||||
if evm.Config.Tracer != nil {
|
||||
evm.Config.Tracer.OnGasChange(gas, 0, GasChangeCallFailedExecution)
|
||||
if evm.Config.Tracer != nil && evm.Config.Tracer.OnGasChange != nil {
|
||||
evm.Config.Tracer.OnGasChange(gas, 0, live.GasChangeCallFailedExecution)
|
||||
}
|
||||
|
||||
gas = 0
|
||||
|
|
@ -442,8 +442,8 @@ func (evm *EVM) create(caller ContractRef, codeAndHash *codeAndHash, gas uint64,
|
|||
// Ensure there's no existing contract already at the designated address
|
||||
contractHash := evm.StateDB.GetCodeHash(address)
|
||||
if evm.StateDB.GetNonce(address) != 0 || (contractHash != (common.Hash{}) && contractHash != types.EmptyCodeHash) {
|
||||
if evm.Config.Tracer != nil {
|
||||
evm.Config.Tracer.OnGasChange(gas, 0, GasChangeCallFailedExecution)
|
||||
if evm.Config.Tracer != nil && evm.Config.Tracer.OnGasChange != nil {
|
||||
evm.Config.Tracer.OnGasChange(gas, 0, live.GasChangeCallFailedExecution)
|
||||
}
|
||||
|
||||
return nil, common.Address{}, 0, ErrContractAddressCollision
|
||||
|
|
@ -479,7 +479,7 @@ func (evm *EVM) create(caller ContractRef, codeAndHash *codeAndHash, gas uint64,
|
|||
// by the error checking condition below.
|
||||
if err == nil {
|
||||
createDataGas := uint64(len(ret)) * params.CreateDataGas
|
||||
if contract.UseGas(createDataGas, evm.Config.Tracer, GasChangeCallCodeStorage) {
|
||||
if contract.UseGas(createDataGas, evm.Config.Tracer, live.GasChangeCallCodeStorage) {
|
||||
evm.StateDB.SetCode(address, ret)
|
||||
} else {
|
||||
err = ErrCodeStoreOutOfGas
|
||||
|
|
@ -492,7 +492,7 @@ func (evm *EVM) create(caller ContractRef, codeAndHash *codeAndHash, gas uint64,
|
|||
if err != nil && (evm.chainRules.IsHomestead || err != ErrCodeStoreOutOfGas) {
|
||||
evm.StateDB.RevertToSnapshot(snapshot)
|
||||
if err != ErrExecutionReverted {
|
||||
contract.UseGas(contract.Gas, evm.Config.Tracer, GasChangeCallFailedExecution)
|
||||
contract.UseGas(contract.Gas, evm.Config.Tracer, live.GasChangeCallFailedExecution)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -522,19 +522,23 @@ func (evm *EVM) captureBegin(isRoot bool, typ OpCode, from common.Address, to co
|
|||
tracer := evm.Config.Tracer
|
||||
|
||||
if isRoot {
|
||||
tracer.CaptureStart(from, to, typ == CREATE || typ == CREATE2, input, startGas, value)
|
||||
} else {
|
||||
tracer.CaptureEnter(typ, from, to, input, startGas, value)
|
||||
if tracer.CaptureStart != nil {
|
||||
tracer.CaptureStart(from, to, typ == CREATE || typ == CREATE2, input, startGas, value)
|
||||
}
|
||||
} else if tracer.CaptureEnter != nil {
|
||||
tracer.CaptureEnter(live.OpCode(typ), from, to, input, startGas, value)
|
||||
}
|
||||
|
||||
tracer.OnGasChange(0, startGas, GasChangeCallInitialBalance)
|
||||
if tracer.OnGasChange != nil {
|
||||
tracer.OnGasChange(0, startGas, live.GasChangeCallInitialBalance)
|
||||
}
|
||||
}
|
||||
|
||||
func (evm *EVM) captureEnd(isRoot bool, typ OpCode, startGas uint64, leftOverGas uint64, ret []byte, err error) {
|
||||
tracer := evm.Config.Tracer
|
||||
|
||||
if leftOverGas != 0 {
|
||||
tracer.OnGasChange(leftOverGas, 0, GasChangeCallLeftOverReturned)
|
||||
if leftOverGas != 0 && tracer.OnGasChange != nil {
|
||||
tracer.OnGasChange(leftOverGas, 0, live.GasChangeCallLeftOverReturned)
|
||||
}
|
||||
var reverted bool
|
||||
if err != nil {
|
||||
|
|
@ -544,8 +548,22 @@ func (evm *EVM) captureEnd(isRoot bool, typ OpCode, startGas uint64, leftOverGas
|
|||
reverted = false
|
||||
}
|
||||
if isRoot {
|
||||
tracer.CaptureEnd(ret, startGas-leftOverGas, VMErrorFromErr(err), reverted)
|
||||
} else {
|
||||
if tracer.CaptureEnd != nil {
|
||||
tracer.CaptureEnd(ret, startGas-leftOverGas, VMErrorFromErr(err), reverted)
|
||||
}
|
||||
} else if tracer.CaptureExit != nil {
|
||||
tracer.CaptureExit(ret, startGas-leftOverGas, VMErrorFromErr(err), reverted)
|
||||
}
|
||||
}
|
||||
|
||||
func (evm *EVM) GetVMContext() *live.VMContext {
|
||||
return &live.VMContext{
|
||||
Coinbase: evm.Context.Coinbase,
|
||||
BlockNumber: evm.Context.BlockNumber,
|
||||
Time: evm.Context.Time,
|
||||
Random: evm.Context.Random,
|
||||
GasPrice: evm.TxContext.GasPrice,
|
||||
ChainConfig: evm.ChainConfig(),
|
||||
StateDB: evm.StateDB,
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -18,9 +18,9 @@ package vm
|
|||
|
||||
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/crypto"
|
||||
"github.com/ethereum/go-ethereum/eth/tracers/directory/live"
|
||||
"github.com/ethereum/go-ethereum/params"
|
||||
"github.com/holiman/uint256"
|
||||
)
|
||||
|
|
@ -591,7 +591,7 @@ func opCreate(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]b
|
|||
// reuse size int for stackvalue
|
||||
stackvalue := size
|
||||
|
||||
scope.Contract.UseGas(gas, interpreter.evm.Config.Tracer, GasChangeCallContractCreation)
|
||||
scope.Contract.UseGas(gas, interpreter.evm.Config.Tracer, live.GasChangeCallContractCreation)
|
||||
|
||||
res, addr, returnGas, suberr := interpreter.evm.Create(scope.Contract, input, gas, &value)
|
||||
// Push item on the stack based on the returned error. If the ruleset is
|
||||
|
|
@ -608,7 +608,7 @@ func opCreate(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]b
|
|||
scope.Stack.push(&stackvalue)
|
||||
|
||||
if interpreter.evm.Config.Tracer != nil && returnGas > 0 {
|
||||
interpreter.evm.Config.Tracer.OnGasChange(scope.Contract.Gas, scope.Contract.Gas+returnGas, GasChangeCallLeftOverRefunded)
|
||||
interpreter.evm.Config.Tracer.OnGasChange(scope.Contract.Gas, scope.Contract.Gas+returnGas, live.GasChangeCallLeftOverRefunded)
|
||||
}
|
||||
|
||||
scope.Contract.Gas += returnGas
|
||||
|
|
@ -634,7 +634,7 @@ func opCreate2(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]
|
|||
)
|
||||
// Apply EIP150
|
||||
gas -= gas / 64
|
||||
scope.Contract.UseGas(gas, interpreter.evm.Config.Tracer, GasChangeCallContractCreation2)
|
||||
scope.Contract.UseGas(gas, interpreter.evm.Config.Tracer, live.GasChangeCallContractCreation2)
|
||||
// reuse size int for stackvalue
|
||||
stackvalue := size
|
||||
res, addr, returnGas, suberr := interpreter.evm.Create2(scope.Contract, input, gas,
|
||||
|
|
@ -648,7 +648,7 @@ func opCreate2(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]
|
|||
scope.Stack.push(&stackvalue)
|
||||
|
||||
if interpreter.evm.Config.Tracer != nil && returnGas > 0 {
|
||||
interpreter.evm.Config.Tracer.OnGasChange(scope.Contract.Gas, scope.Contract.Gas+returnGas, GasChangeCallLeftOverRefunded)
|
||||
interpreter.evm.Config.Tracer.OnGasChange(scope.Contract.Gas, scope.Contract.Gas+returnGas, live.GasChangeCallLeftOverRefunded)
|
||||
}
|
||||
|
||||
scope.Contract.Gas += returnGas
|
||||
|
|
@ -692,7 +692,7 @@ func opCall(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byt
|
|||
}
|
||||
|
||||
if interpreter.evm.Config.Tracer != nil && returnGas > 0 {
|
||||
interpreter.evm.Config.Tracer.OnGasChange(scope.Contract.Gas, scope.Contract.Gas+returnGas, GasChangeCallLeftOverRefunded)
|
||||
interpreter.evm.Config.Tracer.OnGasChange(scope.Contract.Gas, scope.Contract.Gas+returnGas, live.GasChangeCallLeftOverRefunded)
|
||||
}
|
||||
|
||||
scope.Contract.Gas += returnGas
|
||||
|
|
@ -729,7 +729,7 @@ func opCallCode(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([
|
|||
}
|
||||
|
||||
if interpreter.evm.Config.Tracer != nil && returnGas > 0 {
|
||||
interpreter.evm.Config.Tracer.OnGasChange(scope.Contract.Gas, scope.Contract.Gas+returnGas, GasChangeCallLeftOverRefunded)
|
||||
interpreter.evm.Config.Tracer.OnGasChange(scope.Contract.Gas, scope.Contract.Gas+returnGas, live.GasChangeCallLeftOverRefunded)
|
||||
}
|
||||
|
||||
scope.Contract.Gas += returnGas
|
||||
|
|
@ -762,7 +762,7 @@ func opDelegateCall(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext
|
|||
}
|
||||
|
||||
if interpreter.evm.Config.Tracer != nil && returnGas > 0 {
|
||||
interpreter.evm.Config.Tracer.OnGasChange(scope.Contract.Gas, scope.Contract.Gas+returnGas, GasChangeCallLeftOverRefunded)
|
||||
interpreter.evm.Config.Tracer.OnGasChange(scope.Contract.Gas, scope.Contract.Gas+returnGas, live.GasChangeCallLeftOverRefunded)
|
||||
}
|
||||
|
||||
scope.Contract.Gas += returnGas
|
||||
|
|
@ -795,7 +795,7 @@ func opStaticCall(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext)
|
|||
}
|
||||
|
||||
if interpreter.evm.Config.Tracer != nil && returnGas > 0 {
|
||||
interpreter.evm.Config.Tracer.OnGasChange(scope.Contract.Gas, scope.Contract.Gas+returnGas, GasChangeCallLeftOverRefunded)
|
||||
interpreter.evm.Config.Tracer.OnGasChange(scope.Contract.Gas, scope.Contract.Gas+returnGas, live.GasChangeCallLeftOverRefunded)
|
||||
}
|
||||
|
||||
scope.Contract.Gas += returnGas
|
||||
|
|
@ -833,10 +833,10 @@ func opSelfdestruct(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext
|
|||
}
|
||||
beneficiary := scope.Stack.pop()
|
||||
balance := interpreter.evm.StateDB.GetBalance(scope.Contract.Address())
|
||||
interpreter.evm.StateDB.AddBalance(beneficiary.Bytes20(), balance, state.BalanceIncreaseSelfdestruct)
|
||||
interpreter.evm.StateDB.AddBalance(beneficiary.Bytes20(), balance, live.BalanceIncreaseSelfdestruct)
|
||||
interpreter.evm.StateDB.SelfDestruct(scope.Contract.Address())
|
||||
if tracer := interpreter.evm.Config.Tracer; tracer != nil {
|
||||
tracer.CaptureEnter(SELFDESTRUCT, scope.Contract.Address(), beneficiary.Bytes20(), []byte{}, 0, balance.ToBig())
|
||||
tracer.CaptureEnter(live.OpCode(SELFDESTRUCT), scope.Contract.Address(), beneficiary.Bytes20(), []byte{}, 0, balance.ToBig())
|
||||
tracer.CaptureExit([]byte{}, 0, nil, false)
|
||||
}
|
||||
return nil, errStopToken
|
||||
|
|
@ -848,11 +848,11 @@ func opSelfdestruct6780(pc *uint64, interpreter *EVMInterpreter, scope *ScopeCon
|
|||
}
|
||||
beneficiary := scope.Stack.pop()
|
||||
balance := interpreter.evm.StateDB.GetBalance(scope.Contract.Address())
|
||||
interpreter.evm.StateDB.SubBalance(scope.Contract.Address(), balance, state.BalanceDecreaseSelfdestruct)
|
||||
interpreter.evm.StateDB.AddBalance(beneficiary.Bytes20(), balance, state.BalanceIncreaseSelfdestruct)
|
||||
interpreter.evm.StateDB.SubBalance(scope.Contract.Address(), balance, live.BalanceDecreaseSelfdestruct)
|
||||
interpreter.evm.StateDB.AddBalance(beneficiary.Bytes20(), balance, live.BalanceIncreaseSelfdestruct)
|
||||
interpreter.evm.StateDB.Selfdestruct6780(scope.Contract.Address())
|
||||
if tracer := interpreter.evm.Config.Tracer; tracer != nil {
|
||||
tracer.CaptureEnter(SELFDESTRUCT, scope.Contract.Address(), beneficiary.Bytes20(), []byte{}, 0, balance.ToBig())
|
||||
tracer.CaptureEnter(live.OpCode(SELFDESTRUCT), scope.Contract.Address(), beneficiary.Bytes20(), []byte{}, 0, balance.ToBig())
|
||||
tracer.CaptureExit([]byte{}, 0, nil, false)
|
||||
}
|
||||
return nil, errStopToken
|
||||
|
|
|
|||
|
|
@ -20,8 +20,8 @@ import (
|
|||
"math/big"
|
||||
|
||||
"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/eth/tracers/directory/live"
|
||||
"github.com/ethereum/go-ethereum/params"
|
||||
"github.com/holiman/uint256"
|
||||
)
|
||||
|
|
@ -30,8 +30,8 @@ import (
|
|||
type StateDB interface {
|
||||
CreateAccount(common.Address)
|
||||
|
||||
SubBalance(common.Address, *uint256.Int, state.BalanceChangeReason)
|
||||
AddBalance(common.Address, *uint256.Int, state.BalanceChangeReason)
|
||||
SubBalance(common.Address, *uint256.Int, live.BalanceChangeReason)
|
||||
AddBalance(common.Address, *uint256.Int, live.BalanceChangeReason)
|
||||
GetBalance(common.Address) *uint256.Int
|
||||
|
||||
GetNonce(common.Address) uint64
|
||||
|
|
|
|||
|
|
@ -22,12 +22,12 @@ import (
|
|||
"github.com/ethereum/go-ethereum/crypto"
|
||||
"github.com/ethereum/go-ethereum/eth/tracers/directory/live"
|
||||
"github.com/ethereum/go-ethereum/log"
|
||||
"github.com/holiman/uint256"
|
||||
)
|
||||
|
||||
// Config are the configuration options for the Interpreter
|
||||
type Config struct {
|
||||
Tracer EVMLogger // Opcode logger
|
||||
LiveLogger *live.LiveLogger
|
||||
Tracer *live.LiveLogger
|
||||
NoBaseFee bool // Forces the EIP-1559 baseFee to 0 (needed for 0 price calls)
|
||||
EnablePreimageRecording bool // Enables recording of SHA3/keccak preimages
|
||||
ExtraEips []int // Additional EIPS that are to be enabled
|
||||
|
|
@ -41,6 +41,30 @@ type ScopeContext struct {
|
|||
Contract *Contract
|
||||
}
|
||||
|
||||
func (ctx *ScopeContext) GetMemoryData() []byte {
|
||||
return ctx.Memory.Data()
|
||||
}
|
||||
|
||||
func (ctx *ScopeContext) GetStackData() []uint256.Int {
|
||||
return ctx.Stack.Data()
|
||||
}
|
||||
|
||||
func (ctx *ScopeContext) GetCaller() common.Address {
|
||||
return ctx.Contract.Caller()
|
||||
}
|
||||
|
||||
func (ctx *ScopeContext) GetAddress() common.Address {
|
||||
return ctx.Contract.Address()
|
||||
}
|
||||
|
||||
func (ctx *ScopeContext) GetCallValue() *uint256.Int {
|
||||
return ctx.Contract.Value()
|
||||
}
|
||||
|
||||
func (ctx *ScopeContext) GetCallInput() []byte {
|
||||
return ctx.Contract.Input
|
||||
}
|
||||
|
||||
// EVMInterpreter represents an EVM interpreter
|
||||
type EVMInterpreter struct {
|
||||
evm *EVM
|
||||
|
|
@ -160,9 +184,11 @@ func (in *EVMInterpreter) Run(contract *Contract, input []byte, readOnly bool) (
|
|||
defer func() {
|
||||
if err != nil {
|
||||
if !logged {
|
||||
in.evm.Config.Tracer.CaptureState(pcCopy, op, gasCopy, cost, callContext, in.returnData, in.evm.depth, VMErrorFromErr(err))
|
||||
} else {
|
||||
in.evm.Config.Tracer.CaptureFault(pcCopy, op, gasCopy, cost, callContext, in.evm.depth, VMErrorFromErr(err))
|
||||
if in.evm.Config.Tracer.CaptureState != nil {
|
||||
in.evm.Config.Tracer.CaptureState(pcCopy, live.OpCode(op), gasCopy, cost, callContext, in.returnData, in.evm.depth, VMErrorFromErr(err))
|
||||
}
|
||||
} else if in.evm.Config.Tracer.CaptureFault != nil {
|
||||
in.evm.Config.Tracer.CaptureFault(pcCopy, live.OpCode(op), gasCopy, cost, callContext, in.evm.depth, VMErrorFromErr(err))
|
||||
}
|
||||
}
|
||||
}()
|
||||
|
|
@ -187,7 +213,7 @@ func (in *EVMInterpreter) Run(contract *Contract, input []byte, readOnly bool) (
|
|||
} else if sLen > operation.maxStack {
|
||||
return nil, &ErrStackOverflow{stackLen: sLen, limit: operation.maxStack}
|
||||
}
|
||||
if !contract.UseGas(cost, in.evm.Config.Tracer, GasChangeIgnored) {
|
||||
if !contract.UseGas(cost, in.evm.Config.Tracer, live.GasChangeIgnored) {
|
||||
return nil, ErrOutOfGas
|
||||
}
|
||||
|
||||
|
|
@ -214,23 +240,31 @@ func (in *EVMInterpreter) Run(contract *Contract, input []byte, readOnly bool) (
|
|||
var dynamicCost uint64
|
||||
dynamicCost, err = operation.dynamicGas(in.evm, contract, stack, mem, memorySize)
|
||||
cost += dynamicCost // for tracing
|
||||
if err != nil || !contract.UseGas(dynamicCost, in.evm.Config.Tracer, GasChangeIgnored) {
|
||||
if err != nil || !contract.UseGas(dynamicCost, in.evm.Config.Tracer, live.GasChangeIgnored) {
|
||||
return nil, ErrOutOfGas
|
||||
}
|
||||
|
||||
// Do tracing before memory expansion
|
||||
if debug {
|
||||
in.evm.Config.Tracer.OnGasChange(gasCopy, gasCopy-cost, GasChangeCallOpCode)
|
||||
in.evm.Config.Tracer.CaptureState(pc, op, gasCopy, cost, callContext, in.returnData, in.evm.depth, VMErrorFromErr(err))
|
||||
logged = true
|
||||
if in.evm.Config.Tracer.OnGasChange != nil {
|
||||
in.evm.Config.Tracer.OnGasChange(gasCopy, gasCopy-cost, live.GasChangeCallOpCode)
|
||||
}
|
||||
if in.evm.Config.Tracer.CaptureState != nil {
|
||||
in.evm.Config.Tracer.CaptureState(pc, live.OpCode(op), gasCopy, cost, callContext, in.returnData, in.evm.depth, VMErrorFromErr(err))
|
||||
logged = true
|
||||
}
|
||||
}
|
||||
if memorySize > 0 {
|
||||
mem.Resize(memorySize)
|
||||
}
|
||||
} else if debug {
|
||||
in.evm.Config.Tracer.OnGasChange(gasCopy, gasCopy-cost, GasChangeCallOpCode)
|
||||
in.evm.Config.Tracer.CaptureState(pc, op, gasCopy, cost, callContext, in.returnData, in.evm.depth, VMErrorFromErr(err))
|
||||
logged = true
|
||||
if in.evm.Config.Tracer.OnGasChange != nil {
|
||||
in.evm.Config.Tracer.OnGasChange(gasCopy, gasCopy-cost, live.GasChangeCallOpCode)
|
||||
}
|
||||
if in.evm.Config.Tracer.CaptureState != nil {
|
||||
in.evm.Config.Tracer.CaptureState(pc, live.OpCode(op), gasCopy, cost, callContext, in.returnData, in.evm.depth, VMErrorFromErr(err))
|
||||
logged = true
|
||||
}
|
||||
}
|
||||
|
||||
// execute the operation
|
||||
|
|
|
|||
|
|
@ -16,19 +16,12 @@
|
|||
|
||||
package vm
|
||||
|
||||
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
|
||||
// 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 {
|
||||
/*type EVMLogger interface {
|
||||
// Transaction level
|
||||
// Call simulations don't come with a valid signature. `from` field
|
||||
// to be used for address of the caller.
|
||||
|
|
@ -55,63 +48,4 @@ type EVMLogger interface {
|
|||
// Misc
|
||||
OnGasChange(old, new uint64, reason GasChangeReason)
|
||||
}
|
||||
|
||||
// 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 = iota
|
||||
|
||||
// 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
|
||||
// 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
|
||||
// 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
|
||||
// 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
|
||||
|
||||
// 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
|
||||
// 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
|
||||
// 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
|
||||
// GasChangeCallContractCreation is the amount of gas that will be burned for a CREATE.
|
||||
GasChangeCallContractCreation
|
||||
// GasChangeContractCreation is the amount of gas that will be burned for a CREATE2.
|
||||
GasChangeCallContractCreation2
|
||||
// GasChangeCallCodeStorage is the amount of gas that will be charged for code storage.
|
||||
GasChangeCallCodeStorage
|
||||
// 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 `CaptureState` handling.
|
||||
GasChangeCallOpCode
|
||||
// GasChangeCallPrecompiledContract is the amount of gas that will be charged for a precompiled contract execution.
|
||||
GasChangeCallPrecompiledContract
|
||||
// GasChangeCallStorageColdAccess is the amount of gas that will be charged for a cold storage access as controlled by EIP2929 rules.
|
||||
GasChangeCallStorageColdAccess
|
||||
// GasChangeCallFailedExecution is the burning of the remaining gas when the execution failed without a revert.
|
||||
GasChangeCallFailedExecution
|
||||
|
||||
// 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
|
||||
)
|
||||
*/
|
||||
|
|
|
|||
|
|
@ -21,6 +21,7 @@ import (
|
|||
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
"github.com/ethereum/go-ethereum/common/math"
|
||||
"github.com/ethereum/go-ethereum/eth/tracers/directory/live"
|
||||
"github.com/ethereum/go-ethereum/params"
|
||||
)
|
||||
|
||||
|
|
@ -169,7 +170,7 @@ func makeCallVariantGasCallEIP2929(oldCalculator gasFunc) gasFunc {
|
|||
evm.StateDB.AddAddressToAccessList(addr)
|
||||
// Charge the remaining difference here already, to correctly calculate available
|
||||
// gas for call
|
||||
if !contract.UseGas(coldCost, evm.Config.Tracer, GasChangeCallStorageColdAccess) {
|
||||
if !contract.UseGas(coldCost, evm.Config.Tracer, live.GasChangeCallStorageColdAccess) {
|
||||
return 0, ErrOutOfGas
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -193,7 +193,6 @@ func New(stack *node.Node, config *ethconfig.Config) (*Ethereum, error) {
|
|||
vmConfig = vm.Config{
|
||||
EnablePreimageRecording: config.EnablePreimageRecording,
|
||||
Tracer: config.VMTracer,
|
||||
LiveLogger: config.LiveLogger,
|
||||
}
|
||||
cacheConfig = &core.CacheConfig{
|
||||
TrieCleanLimit: config.TrieCleanCache,
|
||||
|
|
|
|||
|
|
@ -29,7 +29,6 @@ import (
|
|||
"github.com/ethereum/go-ethereum/core"
|
||||
"github.com/ethereum/go-ethereum/core/txpool/blobpool"
|
||||
"github.com/ethereum/go-ethereum/core/txpool/legacypool"
|
||||
"github.com/ethereum/go-ethereum/core/vm"
|
||||
"github.com/ethereum/go-ethereum/eth/downloader"
|
||||
"github.com/ethereum/go-ethereum/eth/gasprice"
|
||||
"github.com/ethereum/go-ethereum/eth/tracers/directory/live"
|
||||
|
|
@ -144,8 +143,7 @@ type Config struct {
|
|||
EnablePreimageRecording bool
|
||||
|
||||
// Enables VM tracing
|
||||
VMTracer vm.EVMLogger
|
||||
LiveLogger *live.LiveLogger
|
||||
VMTracer *live.LiveLogger
|
||||
|
||||
// Miscellaneous options
|
||||
DocRoot string `toml:"-"`
|
||||
|
|
|
|||
|
|
@ -778,14 +778,14 @@ func (api *API) standardTraceBlockToFile(ctx context.Context, block *types.Block
|
|||
// Swap out the noop logger to the standard tracer
|
||||
writer = bufio.NewWriter(dump)
|
||||
vmConf = vm.Config{
|
||||
Tracer: logger.NewJSONLogger(&logConfig, writer),
|
||||
Tracer: logger.NewJSONLogger(&logConfig, writer).GetLogger(),
|
||||
EnablePreimageRecording: true,
|
||||
}
|
||||
}
|
||||
// Execute the transaction and flush any traces to disk
|
||||
vmenv := vm.NewEVM(vmctx, txContext, statedb, chainConfig, vmConf)
|
||||
statedb.SetTxContext(tx.Hash(), i)
|
||||
vmConf.Tracer.CaptureTxStart(vmenv, tx, msg.From)
|
||||
vmConf.Tracer.CaptureTxStart(vmenv.GetVMContext(), tx, msg.From)
|
||||
vmRet, err := core.ApplyMessage(vmenv, msg, new(core.GasPool).AddGas(msg.GasLimit))
|
||||
vmConf.Tracer.CaptureTxEnd(&types.Receipt{GasUsed: vmRet.UsedGas}, err)
|
||||
if writer != nil {
|
||||
|
|
@ -941,7 +941,7 @@ func (api *API) TraceCall(ctx context.Context, args ethapi.TransactionArgs, bloc
|
|||
// be tracer dependent.
|
||||
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
|
||||
tracer *directory.Tracer
|
||||
err error
|
||||
timeout = defaultTraceTimeout
|
||||
usedGas uint64
|
||||
|
|
@ -950,15 +950,15 @@ func (api *API) traceTx(ctx context.Context, tx *types.Transaction, message *cor
|
|||
config = &TraceConfig{}
|
||||
}
|
||||
// Default tracer is the struct logger
|
||||
tracer = logger.NewStructLogger(config.Config)
|
||||
tracer = logger.NewStructLogger(config.Config).GetTracer()
|
||||
if config.Tracer != nil {
|
||||
tracer, err = directory.DefaultDirectory.New(*config.Tracer, txctx, config.TracerConfig)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
vmenv := vm.NewEVM(vmctx, vm.TxContext{GasPrice: big.NewInt(0)}, statedb, api.backend.ChainConfig(), vm.Config{Tracer: tracer, NoBaseFee: true})
|
||||
statedb.SetLogger(tracer)
|
||||
vmenv := vm.NewEVM(vmctx, vm.TxContext{GasPrice: big.NewInt(0)}, statedb, api.backend.ChainConfig(), vm.Config{Tracer: tracer.LiveLogger, NoBaseFee: true})
|
||||
statedb.SetLogger(tracer.LiveLogger)
|
||||
|
||||
// Define a meaningful timeout of a single transaction trace
|
||||
if config.Timeout != nil {
|
||||
|
|
|
|||
|
|
@ -6,15 +6,82 @@ import (
|
|||
"math/big"
|
||||
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
"github.com/ethereum/go-ethereum/core"
|
||||
"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/params"
|
||||
"github.com/holiman/uint256"
|
||||
)
|
||||
|
||||
type ScopeContext interface {
|
||||
GetMemoryData() []byte
|
||||
GetStackData() []uint256.Int
|
||||
GetCaller() common.Address
|
||||
GetAddress() common.Address
|
||||
GetCallValue() *uint256.Int
|
||||
GetCallInput() []byte
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
// OpCode is an EVM opcode
|
||||
// TODO: provide utils for consumers
|
||||
type OpCode byte
|
||||
|
||||
type LiveLogger struct {
|
||||
VMLogger vm.EVMLogger
|
||||
/*
|
||||
- VM events -
|
||||
*/
|
||||
// Transaction level
|
||||
// Call simulations don't come with a valid signature. `from` field
|
||||
// to be used for address of the caller.
|
||||
CaptureTxStart func(vm *VMContext, tx *types.Transaction, from common.Address)
|
||||
CaptureTxEnd func(receipt *types.Receipt, err error)
|
||||
// Top call frame
|
||||
CaptureStart func(from common.Address, to common.Address, create bool, input []byte, gas uint64, value *big.Int)
|
||||
// CaptureEnd is invoked when the processing of the top call ends.
|
||||
// See docs for `CaptureExit` for info on the `reverted` parameter.
|
||||
CaptureEnd func(output []byte, gasUsed uint64, err error, reverted bool)
|
||||
// Rest of call frames
|
||||
CaptureEnter func(typ OpCode, from common.Address, to common.Address, input []byte, gas uint64, value *big.Int)
|
||||
// CaptureExit 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`.
|
||||
CaptureExit func(output []byte, gasUsed uint64, err error, reverted bool)
|
||||
// Opcode level
|
||||
CaptureState func(pc uint64, op OpCode, gas, cost uint64, scope ScopeContext, rData []byte, depth int, err error)
|
||||
CaptureFault func(pc uint64, op OpCode, gas, cost uint64, scope ScopeContext, depth int, err error)
|
||||
CaptureKeccakPreimage func(hash common.Hash, data []byte)
|
||||
// Misc
|
||||
OnGasChange func(old, new uint64, reason GasChangeReason)
|
||||
|
||||
/*
|
||||
- Chain events -
|
||||
|
|
@ -22,18 +89,18 @@ type LiveLogger struct {
|
|||
OnBlockchainInit func(chainConfig *params.ChainConfig)
|
||||
// OnBlockStart is called before executing `block`.
|
||||
// `td` is the total difficulty prior to `block`.
|
||||
OnBlockStart func(event core.BlockEvent)
|
||||
OnBlockStart func(event BlockEvent)
|
||||
OnBlockEnd func(err error)
|
||||
// OnSkippedBlock indicates a block was skipped during processing
|
||||
// due to it being known previously. This can happen e.g. when recovering
|
||||
// from a crash.
|
||||
OnSkippedBlock func(event core.BlockEvent)
|
||||
OnGenesisBlock func(genesis *types.Block, alloc core.GenesisAlloc)
|
||||
OnSkippedBlock func(event BlockEvent)
|
||||
OnGenesisBlock func(genesis *types.Block, alloc types.GenesisAlloc)
|
||||
|
||||
/*
|
||||
- State events -
|
||||
*/
|
||||
OnBalanceChange func(addr common.Address, prev, new *big.Int, reason state.BalanceChangeReason)
|
||||
OnBalanceChange func(addr common.Address, prev, new *big.Int, reason BalanceChangeReason)
|
||||
OnNonceChange func(addr common.Address, prev, new uint64)
|
||||
OnCodeChange func(addr common.Address, prevCodeHash common.Hash, prevCode []byte, codeHash common.Hash, code []byte)
|
||||
OnStorageChange func(addr common.Address, slot common.Hash, prev, new common.Hash)
|
||||
|
|
|
|||
|
|
@ -21,9 +21,8 @@ import (
|
|||
"math/big"
|
||||
|
||||
"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/directory/live"
|
||||
)
|
||||
|
||||
func init() {
|
||||
|
|
@ -35,8 +34,29 @@ func init() {
|
|||
type NoopTracer struct{}
|
||||
|
||||
// newNoopTracer returns a new noop tracer.
|
||||
func newNoopTracer(ctx *Context, _ json.RawMessage) (Tracer, error) {
|
||||
return &NoopTracer{}, nil
|
||||
func newNoopTracer(ctx *Context, _ json.RawMessage) (*Tracer, error) {
|
||||
t := &NoopTracer{}
|
||||
return &Tracer{
|
||||
LiveLogger: &live.LiveLogger{
|
||||
CaptureTxStart: t.CaptureTxStart,
|
||||
CaptureTxEnd: t.CaptureTxEnd,
|
||||
CaptureStart: t.CaptureStart,
|
||||
CaptureEnd: t.CaptureEnd,
|
||||
CaptureEnter: t.CaptureEnter,
|
||||
CaptureExit: t.CaptureExit,
|
||||
CaptureState: t.CaptureState,
|
||||
CaptureFault: t.CaptureFault,
|
||||
CaptureKeccakPreimage: t.CaptureKeccakPreimage,
|
||||
OnGasChange: t.OnGasChange,
|
||||
OnBalanceChange: t.OnBalanceChange,
|
||||
OnNonceChange: t.OnNonceChange,
|
||||
OnCodeChange: t.OnCodeChange,
|
||||
OnStorageChange: t.OnStorageChange,
|
||||
OnLog: t.OnLog,
|
||||
},
|
||||
GetResult: t.GetResult,
|
||||
Stop: t.Stop,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// CaptureStart implements the EVMLogger interface to initialize the tracing operation.
|
||||
|
|
@ -48,21 +68,21 @@ func (t *NoopTracer) CaptureEnd(output []byte, gasUsed uint64, err error, revert
|
|||
}
|
||||
|
||||
// CaptureState implements the EVMLogger interface to trace a single step of VM execution.
|
||||
func (t *NoopTracer) CaptureState(pc uint64, op vm.OpCode, gas, cost uint64, scope *vm.ScopeContext, rData []byte, depth int, err error) {
|
||||
func (t *NoopTracer) CaptureState(pc uint64, op live.OpCode, gas, cost uint64, scope live.ScopeContext, rData []byte, depth int, err error) {
|
||||
}
|
||||
|
||||
// CaptureFault implements the EVMLogger interface to trace an execution fault.
|
||||
func (t *NoopTracer) CaptureFault(pc uint64, op vm.OpCode, gas, cost uint64, _ *vm.ScopeContext, depth int, err error) {
|
||||
func (t *NoopTracer) CaptureFault(pc uint64, op live.OpCode, gas, cost uint64, _ live.ScopeContext, depth int, err error) {
|
||||
}
|
||||
|
||||
// CaptureKeccakPreimage is called during the KECCAK256 opcode.
|
||||
func (t *NoopTracer) CaptureKeccakPreimage(hash common.Hash, data []byte) {}
|
||||
|
||||
// OnGasChange is called when gas is either consumed or refunded.
|
||||
func (t *NoopTracer) OnGasChange(old, new uint64, reason vm.GasChangeReason) {}
|
||||
func (t *NoopTracer) OnGasChange(old, new uint64, reason live.GasChangeReason) {}
|
||||
|
||||
// CaptureEnter is called when EVM enters a new scope (via call, create or selfdestruct).
|
||||
func (t *NoopTracer) CaptureEnter(typ vm.OpCode, from common.Address, to common.Address, input []byte, gas uint64, value *big.Int) {
|
||||
func (t *NoopTracer) CaptureEnter(typ live.OpCode, from common.Address, to common.Address, input []byte, gas uint64, value *big.Int) {
|
||||
}
|
||||
|
||||
// CaptureExit is called when EVM exits a scope, even if the scope didn't
|
||||
|
|
@ -70,11 +90,11 @@ func (t *NoopTracer) CaptureEnter(typ vm.OpCode, from common.Address, to common.
|
|||
func (t *NoopTracer) CaptureExit(output []byte, gasUsed uint64, err error, reverted bool) {
|
||||
}
|
||||
|
||||
func (*NoopTracer) CaptureTxStart(env *vm.EVM, tx *types.Transaction, from common.Address) {}
|
||||
func (*NoopTracer) CaptureTxStart(env *live.VMContext, tx *types.Transaction, from common.Address) {}
|
||||
|
||||
func (*NoopTracer) CaptureTxEnd(receipt *types.Receipt, err error) {}
|
||||
|
||||
func (*NoopTracer) OnBalanceChange(a common.Address, prev, new *big.Int, reason state.BalanceChangeReason) {
|
||||
func (*NoopTracer) OnBalanceChange(a common.Address, prev, new *big.Int, reason live.BalanceChangeReason) {
|
||||
}
|
||||
|
||||
func (*NoopTracer) OnNonceChange(a common.Address, prev, new uint64) {}
|
||||
|
|
|
|||
|
|
@ -24,8 +24,7 @@ import (
|
|||
"math/big"
|
||||
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
"github.com/ethereum/go-ethereum/core/state"
|
||||
"github.com/ethereum/go-ethereum/core/vm"
|
||||
"github.com/ethereum/go-ethereum/eth/tracers/directory/live"
|
||||
)
|
||||
|
||||
// Context contains some contextual infos for a transaction execution that is not
|
||||
|
|
@ -41,16 +40,15 @@ type Context struct {
|
|||
// for it to be available through the RPC interface.
|
||||
// This involves a method to retrieve results and one to
|
||||
// stop tracing.
|
||||
type Tracer interface {
|
||||
vm.EVMLogger
|
||||
state.StateLogger
|
||||
GetResult() (json.RawMessage, error)
|
||||
type Tracer struct {
|
||||
*live.LiveLogger
|
||||
GetResult func() (json.RawMessage, error)
|
||||
// Stop terminates execution of the tracer at the first opportune moment.
|
||||
Stop(err error)
|
||||
Stop func(err error)
|
||||
}
|
||||
|
||||
type ctorFn func(*Context, json.RawMessage) (Tracer, error)
|
||||
type jsCtorFn func(string, *Context, json.RawMessage) (Tracer, error)
|
||||
type ctorFn func(*Context, json.RawMessage) (*Tracer, error)
|
||||
type jsCtorFn func(string, *Context, json.RawMessage) (*Tracer, error)
|
||||
|
||||
type elem struct {
|
||||
ctor ctorFn
|
||||
|
|
@ -83,7 +81,7 @@ func (d *directory) RegisterJSEval(f jsCtorFn) {
|
|||
// New returns a new instance of a tracer, by iterating through the
|
||||
// registered lookups. Name is either name of an existing tracer
|
||||
// or an arbitrary JS code.
|
||||
func (d *directory) New(name string, ctx *Context, cfg json.RawMessage) (Tracer, error) {
|
||||
func (d *directory) New(name string, ctx *Context, cfg json.RawMessage) (*Tracer, error) {
|
||||
if elem, ok := d.elems[name]; ok {
|
||||
return elem.ctor(ctx, cfg)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -25,6 +25,7 @@ import (
|
|||
"github.com/dop251/goja"
|
||||
"github.com/ethereum/go-ethereum/core/types"
|
||||
"github.com/ethereum/go-ethereum/eth/tracers/directory"
|
||||
"github.com/ethereum/go-ethereum/eth/tracers/directory/live"
|
||||
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
"github.com/ethereum/go-ethereum/common/hexutil"
|
||||
|
|
@ -42,9 +43,9 @@ func init() {
|
|||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
type ctorFn = func(*directory.Context, json.RawMessage) (directory.Tracer, error)
|
||||
type ctorFn = func(*directory.Context, json.RawMessage) (*directory.Tracer, error)
|
||||
lookup := func(code string) ctorFn {
|
||||
return func(ctx *directory.Context, cfg json.RawMessage) (directory.Tracer, error) {
|
||||
return func(ctx *directory.Context, cfg json.RawMessage) (*directory.Tracer, error) {
|
||||
return newJsTracer(code, ctx, cfg)
|
||||
}
|
||||
}
|
||||
|
|
@ -99,7 +100,7 @@ type jsTracer struct {
|
|||
directory.NoopTracer
|
||||
|
||||
vm *goja.Runtime
|
||||
env *vm.EVM
|
||||
env *live.VMContext
|
||||
toBig toBigFn // Converts a hex string into a JS bigint
|
||||
toBuf toBufFn // Converts a []byte into a JS buffer
|
||||
fromBuf fromBufFn // Converts an array, hex string or Uint8Array to a []byte
|
||||
|
|
@ -136,7 +137,7 @@ type jsTracer struct {
|
|||
// The methods `result` and `fault` are required to be present.
|
||||
// The methods `step`, `enter`, and `exit` are optional, but note that
|
||||
// `enter` and `exit` always go together.
|
||||
func newJsTracer(code string, ctx *directory.Context, cfg json.RawMessage) (directory.Tracer, error) {
|
||||
func newJsTracer(code string, ctx *directory.Context, cfg json.RawMessage) (*directory.Tracer, error) {
|
||||
vm := goja.New()
|
||||
// By default field names are exported to JS as is, i.e. capitalized.
|
||||
vm.SetFieldNameMapper(goja.UncapFieldNameMapper())
|
||||
|
|
@ -219,22 +220,36 @@ func newJsTracer(code string, ctx *directory.Context, cfg json.RawMessage) (dire
|
|||
t.frameValue = t.frame.setupObject()
|
||||
t.frameResultValue = t.frameResult.setupObject()
|
||||
t.logValue = t.log.setupObject()
|
||||
return t, nil
|
||||
|
||||
return &directory.Tracer{
|
||||
LiveLogger: &live.LiveLogger{
|
||||
CaptureTxStart: t.CaptureTxStart,
|
||||
CaptureTxEnd: t.CaptureTxEnd,
|
||||
CaptureStart: t.CaptureStart,
|
||||
CaptureEnd: t.CaptureEnd,
|
||||
CaptureEnter: t.CaptureEnter,
|
||||
CaptureExit: t.CaptureExit,
|
||||
CaptureState: t.CaptureState,
|
||||
CaptureFault: t.CaptureFault,
|
||||
},
|
||||
GetResult: t.GetResult,
|
||||
Stop: t.Stop,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// CaptureTxStart implements the Tracer interface and is invoked at the beginning of
|
||||
// transaction processing.
|
||||
func (t *jsTracer) CaptureTxStart(env *vm.EVM, tx *types.Transaction, from common.Address) {
|
||||
func (t *jsTracer) CaptureTxStart(env *live.VMContext, tx *types.Transaction, from common.Address) {
|
||||
t.env = env
|
||||
// Need statedb access for db object
|
||||
db := &dbObj{db: env.StateDB, vm: t.vm, toBig: t.toBig, toBuf: t.toBuf, fromBuf: t.fromBuf}
|
||||
t.dbValue = db.setupObject()
|
||||
// Update list of precompiles based on current block
|
||||
rules := env.ChainConfig().Rules(env.Context.BlockNumber, env.Context.Random != nil, env.Context.Time)
|
||||
rules := env.ChainConfig.Rules(env.BlockNumber, env.Random != nil, env.Time)
|
||||
t.activePrecompiles = vm.ActivePrecompiles(rules)
|
||||
t.ctx["block"] = t.vm.ToValue(t.env.Context.BlockNumber.Uint64())
|
||||
t.ctx["block"] = t.vm.ToValue(t.env.BlockNumber.Uint64())
|
||||
t.ctx["gas"] = t.vm.ToValue(tx.Gas())
|
||||
gasPriceBig, err := t.toBig(t.vm, env.TxContext.GasPrice.String())
|
||||
gasPriceBig, err := t.toBig(t.vm, env.GasPrice.String())
|
||||
if err != nil {
|
||||
t.err = err
|
||||
t.env.Cancel()
|
||||
|
|
@ -294,7 +309,7 @@ func (t *jsTracer) CaptureStart(from common.Address, to common.Address, create b
|
|||
}
|
||||
|
||||
// CaptureState implements the Tracer interface to trace a single step of VM execution.
|
||||
func (t *jsTracer) CaptureState(pc uint64, op vm.OpCode, gas, cost uint64, scope *vm.ScopeContext, rData []byte, depth int, err error) {
|
||||
func (t *jsTracer) CaptureState(pc uint64, op live.OpCode, gas, cost uint64, scope live.ScopeContext, rData []byte, depth int, err error) {
|
||||
if !t.traceStep {
|
||||
return
|
||||
}
|
||||
|
|
@ -303,7 +318,7 @@ func (t *jsTracer) CaptureState(pc uint64, op vm.OpCode, gas, cost uint64, scope
|
|||
}
|
||||
|
||||
log := t.log
|
||||
log.op.op = op
|
||||
log.op.op = vm.OpCode(op)
|
||||
log.memory.memory = scope.Memory
|
||||
log.stack.stack = scope.Stack
|
||||
log.contract.contract = scope.Contract
|
||||
|
|
@ -319,7 +334,7 @@ func (t *jsTracer) CaptureState(pc uint64, op vm.OpCode, gas, cost uint64, scope
|
|||
}
|
||||
|
||||
// CaptureFault implements the Tracer interface to trace an execution fault
|
||||
func (t *jsTracer) CaptureFault(pc uint64, op vm.OpCode, gas, cost uint64, scope *vm.ScopeContext, depth int, err error) {
|
||||
func (t *jsTracer) CaptureFault(pc uint64, op live.OpCode, gas, cost uint64, scope live.ScopeContext, depth int, err error) {
|
||||
if t.err != nil {
|
||||
return
|
||||
}
|
||||
|
|
@ -344,7 +359,7 @@ func (t *jsTracer) CaptureEnd(output []byte, gasUsed uint64, err error, reverted
|
|||
}
|
||||
|
||||
// CaptureEnter is called when EVM enters a new scope (via call, create or selfdestruct).
|
||||
func (t *jsTracer) CaptureEnter(typ vm.OpCode, from common.Address, to common.Address, input []byte, gas uint64, value *big.Int) {
|
||||
func (t *jsTracer) CaptureEnter(typ live.OpCode, from common.Address, to common.Address, input []byte, gas uint64, value *big.Int) {
|
||||
if !t.traceFrame {
|
||||
return
|
||||
}
|
||||
|
|
@ -352,7 +367,7 @@ func (t *jsTracer) CaptureEnter(typ vm.OpCode, from common.Address, to common.Ad
|
|||
return
|
||||
}
|
||||
|
||||
t.frame.typ = typ.String()
|
||||
t.frame.typ = vm.OpCode(typ).String()
|
||||
t.frame.from = from
|
||||
t.frame.to = to
|
||||
t.frame.input = common.CopyBytes(input)
|
||||
|
|
@ -697,7 +712,7 @@ func (s *stackObj) setupObject() *goja.Object {
|
|||
}
|
||||
|
||||
type dbObj struct {
|
||||
db vm.StateDB
|
||||
db live.StateDB
|
||||
vm *goja.Runtime
|
||||
toBig toBigFn
|
||||
toBuf toBufFn
|
||||
|
|
|
|||
|
|
@ -5,10 +5,7 @@ import (
|
|||
"math/big"
|
||||
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
"github.com/ethereum/go-ethereum/core"
|
||||
"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/directory/live"
|
||||
"github.com/ethereum/go-ethereum/params"
|
||||
)
|
||||
|
|
@ -26,17 +23,26 @@ type noop struct{}
|
|||
func newNoopTracer(_ json.RawMessage) (*live.LiveLogger, error) {
|
||||
t := &noop{}
|
||||
return &live.LiveLogger{
|
||||
VMLogger: t,
|
||||
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,
|
||||
CaptureTxStart: t.CaptureTxStart,
|
||||
CaptureTxEnd: t.CaptureTxEnd,
|
||||
CaptureStart: t.CaptureStart,
|
||||
CaptureEnd: t.CaptureEnd,
|
||||
CaptureEnter: t.CaptureEnter,
|
||||
CaptureExit: t.CaptureExit,
|
||||
CaptureState: t.CaptureState,
|
||||
CaptureFault: t.CaptureFault,
|
||||
CaptureKeccakPreimage: t.CaptureKeccakPreimage,
|
||||
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
|
||||
}
|
||||
|
||||
|
|
@ -49,18 +55,18 @@ func (t *noop) CaptureEnd(output []byte, gasUsed uint64, err error, reverted boo
|
|||
}
|
||||
|
||||
// CaptureState implements the EVMLogger interface to trace a single step of VM execution.
|
||||
func (t *noop) CaptureState(pc uint64, op vm.OpCode, gas, cost uint64, scope *vm.ScopeContext, rData []byte, depth int, err error) {
|
||||
func (t *noop) CaptureState(pc uint64, op live.OpCode, gas, cost uint64, scope live.ScopeContext, rData []byte, depth int, err error) {
|
||||
}
|
||||
|
||||
// CaptureFault implements the EVMLogger interface to trace an execution fault.
|
||||
func (t *noop) CaptureFault(pc uint64, op vm.OpCode, gas, cost uint64, _ *vm.ScopeContext, depth int, err error) {
|
||||
func (t *noop) CaptureFault(pc uint64, op live.OpCode, gas, cost uint64, _ live.ScopeContext, depth int, err error) {
|
||||
}
|
||||
|
||||
// CaptureKeccakPreimage is called during the KECCAK256 opcode.
|
||||
func (t *noop) CaptureKeccakPreimage(hash common.Hash, data []byte) {}
|
||||
|
||||
// CaptureEnter is called when EVM enters a new scope (via call, create or selfdestruct).
|
||||
func (t *noop) CaptureEnter(typ vm.OpCode, from common.Address, to common.Address, input []byte, gas uint64, value *big.Int) {
|
||||
func (t *noop) CaptureEnter(typ live.OpCode, from common.Address, to common.Address, input []byte, gas uint64, value *big.Int) {
|
||||
}
|
||||
|
||||
// CaptureExit is called when EVM exits a scope, even if the scope didn't
|
||||
|
|
@ -68,27 +74,27 @@ func (t *noop) CaptureEnter(typ vm.OpCode, from common.Address, to common.Addres
|
|||
func (t *noop) CaptureExit(output []byte, gasUsed uint64, err error, reverted bool) {
|
||||
}
|
||||
|
||||
func (t *noop) CaptureTxStart(env *vm.EVM, tx *types.Transaction, from common.Address) {
|
||||
func (t *noop) CaptureTxStart(vm *live.VMContext, tx *types.Transaction, from common.Address) {
|
||||
}
|
||||
|
||||
func (t *noop) CaptureTxEnd(receipt *types.Receipt, err error) {
|
||||
}
|
||||
|
||||
func (t *noop) OnBlockStart(ev core.BlockEvent) {
|
||||
func (t *noop) OnBlockStart(ev live.BlockEvent) {
|
||||
}
|
||||
|
||||
func (t *noop) OnBlockEnd(err error) {
|
||||
}
|
||||
|
||||
func (t *noop) OnSkippedBlock(ev core.BlockEvent) {}
|
||||
func (t *noop) OnSkippedBlock(ev live.BlockEvent) {}
|
||||
|
||||
func (t *noop) OnBlockchainInit(chainConfig *params.ChainConfig) {
|
||||
}
|
||||
|
||||
func (t *noop) OnGenesisBlock(b *types.Block, alloc core.GenesisAlloc) {
|
||||
func (t *noop) OnGenesisBlock(b *types.Block, alloc types.GenesisAlloc) {
|
||||
}
|
||||
|
||||
func (t *noop) OnBalanceChange(a common.Address, prev, new *big.Int, reason state.BalanceChangeReason) {
|
||||
func (t *noop) OnBalanceChange(a common.Address, prev, new *big.Int, reason live.BalanceChangeReason) {
|
||||
}
|
||||
|
||||
func (t *noop) OnNonceChange(a common.Address, prev, new uint64) {
|
||||
|
|
@ -104,5 +110,5 @@ func (t *noop) OnLog(l *types.Log) {
|
|||
|
||||
}
|
||||
|
||||
func (t *noop) OnGasChange(old, new uint64, reason vm.GasChangeReason) {
|
||||
func (t *noop) OnGasChange(old, new uint64, reason live.GasChangeReason) {
|
||||
}
|
||||
|
|
|
|||
|
|
@ -21,6 +21,7 @@ import (
|
|||
"github.com/ethereum/go-ethereum/core/types"
|
||||
"github.com/ethereum/go-ethereum/core/vm"
|
||||
"github.com/ethereum/go-ethereum/eth/tracers/directory"
|
||||
"github.com/ethereum/go-ethereum/eth/tracers/directory/live"
|
||||
)
|
||||
|
||||
// accessList is an accumulator for the set of accounts and storage slots an EVM
|
||||
|
|
@ -132,14 +133,20 @@ func NewAccessListTracer(acl types.AccessList, from, to common.Address, precompi
|
|||
}
|
||||
}
|
||||
|
||||
func (a *AccessListTracer) GetLogger() *live.LiveLogger {
|
||||
return &live.LiveLogger{
|
||||
CaptureState: a.CaptureState,
|
||||
}
|
||||
}
|
||||
|
||||
// CaptureState captures all opcodes that touch storage or addresses and adds them to the accesslist.
|
||||
func (a *AccessListTracer) CaptureState(pc uint64, op vm.OpCode, gas, cost uint64, scope *vm.ScopeContext, rData []byte, depth int, err error) {
|
||||
stack := scope.Stack
|
||||
stackData := stack.Data()
|
||||
func (a *AccessListTracer) CaptureState(pc uint64, opcode live.OpCode, gas, cost uint64, scope live.ScopeContext, rData []byte, depth int, err error) {
|
||||
stackData := scope.GetStackData()
|
||||
stackLen := len(stackData)
|
||||
op := vm.OpCode(opcode)
|
||||
if (op == vm.SLOAD || op == vm.SSTORE) && stackLen >= 1 {
|
||||
slot := common.Hash(stackData[stackLen-1].Bytes32())
|
||||
a.list.addSlot(scope.Contract.Address(), slot)
|
||||
a.list.addSlot(scope.GetAddress(), slot)
|
||||
}
|
||||
if (op == vm.EXTCODECOPY || op == vm.EXTCODEHASH || op == vm.EXTCODESIZE || op == vm.BALANCE || op == vm.SELFDESTRUCT) && stackLen >= 1 {
|
||||
addr := common.Address(stackData[stackLen-1].Bytes20())
|
||||
|
|
|
|||
|
|
@ -31,6 +31,7 @@ import (
|
|||
"github.com/ethereum/go-ethereum/core/types"
|
||||
"github.com/ethereum/go-ethereum/core/vm"
|
||||
"github.com/ethereum/go-ethereum/eth/tracers/directory"
|
||||
"github.com/ethereum/go-ethereum/eth/tracers/directory/live"
|
||||
"github.com/ethereum/go-ethereum/params"
|
||||
"github.com/holiman/uint256"
|
||||
)
|
||||
|
|
@ -110,7 +111,7 @@ func (s *StructLog) ErrorString() string {
|
|||
type StructLogger struct {
|
||||
directory.NoopTracer
|
||||
cfg Config
|
||||
env *vm.EVM
|
||||
env *live.VMContext
|
||||
|
||||
storage map[common.Address]Storage
|
||||
logs []StructLog
|
||||
|
|
@ -133,6 +134,19 @@ func NewStructLogger(cfg *Config) *StructLogger {
|
|||
return logger
|
||||
}
|
||||
|
||||
func (l *StructLogger) GetTracer() *directory.Tracer {
|
||||
return &directory.Tracer{
|
||||
LiveLogger: &live.LiveLogger{
|
||||
CaptureTxStart: l.CaptureTxStart,
|
||||
CaptureTxEnd: l.CaptureTxEnd,
|
||||
CaptureEnd: l.CaptureEnd,
|
||||
CaptureState: l.CaptureState,
|
||||
},
|
||||
GetResult: l.GetResult,
|
||||
Stop: l.Stop,
|
||||
}
|
||||
}
|
||||
|
||||
// Reset clears the data held by the logger.
|
||||
func (l *StructLogger) Reset() {
|
||||
l.storage = make(map[common.Address]Storage)
|
||||
|
|
@ -144,7 +158,7 @@ func (l *StructLogger) Reset() {
|
|||
// CaptureState logs a new structured log message and pushes it out to the environment
|
||||
//
|
||||
// CaptureState also tracks SLOAD/SSTORE ops to track storage change.
|
||||
func (l *StructLogger) CaptureState(pc uint64, op vm.OpCode, gas, cost uint64, scope *vm.ScopeContext, rData []byte, depth int, err error) {
|
||||
func (l *StructLogger) CaptureState(pc uint64, opcode live.OpCode, gas, cost uint64, scope live.ScopeContext, rData []byte, depth int, err error) {
|
||||
// If tracing was interrupted, set the error and stop
|
||||
if l.interrupt.Load() {
|
||||
return
|
||||
|
|
@ -154,49 +168,49 @@ func (l *StructLogger) CaptureState(pc uint64, op vm.OpCode, gas, cost uint64, s
|
|||
return
|
||||
}
|
||||
|
||||
memory := scope.Memory
|
||||
stack := scope.Stack
|
||||
contract := scope.Contract
|
||||
op := vm.OpCode(opcode)
|
||||
memory := scope.GetMemoryData()
|
||||
stack := scope.GetStackData()
|
||||
// Copy a snapshot of the current memory state to a new buffer
|
||||
var mem []byte
|
||||
if l.cfg.EnableMemory {
|
||||
mem = make([]byte, len(memory.Data()))
|
||||
copy(mem, memory.Data())
|
||||
mem = make([]byte, len(memory))
|
||||
copy(mem, memory)
|
||||
}
|
||||
// Copy a snapshot of the current stack state to a new buffer
|
||||
var stck []uint256.Int
|
||||
if !l.cfg.DisableStack {
|
||||
stck = make([]uint256.Int, len(stack.Data()))
|
||||
for i, item := range stack.Data() {
|
||||
stck = make([]uint256.Int, len(stack))
|
||||
for i, item := range stack {
|
||||
stck[i] = item
|
||||
}
|
||||
}
|
||||
stackData := stack.Data()
|
||||
stackLen := len(stackData)
|
||||
contractAddr := scope.GetAddress()
|
||||
stackLen := len(stack)
|
||||
// Copy a snapshot of the current storage to a new container
|
||||
var storage Storage
|
||||
if !l.cfg.DisableStorage && (op == vm.SLOAD || op == vm.SSTORE) {
|
||||
// initialise new changed values storage container for this contract
|
||||
// if not present.
|
||||
if l.storage[contract.Address()] == nil {
|
||||
l.storage[contract.Address()] = make(Storage)
|
||||
if l.storage[contractAddr] == nil {
|
||||
l.storage[contractAddr] = make(Storage)
|
||||
}
|
||||
// capture SLOAD opcodes and record the read entry in the local storage
|
||||
if op == vm.SLOAD && stackLen >= 1 {
|
||||
var (
|
||||
address = common.Hash(stackData[stackLen-1].Bytes32())
|
||||
value = l.env.StateDB.GetState(contract.Address(), address)
|
||||
address = common.Hash(stack[stackLen-1].Bytes32())
|
||||
value = l.env.StateDB.GetState(contractAddr, address)
|
||||
)
|
||||
l.storage[contract.Address()][address] = value
|
||||
storage = l.storage[contract.Address()].Copy()
|
||||
l.storage[contractAddr][address] = value
|
||||
storage = l.storage[contractAddr].Copy()
|
||||
} else if op == vm.SSTORE && stackLen >= 2 {
|
||||
// capture SSTORE opcodes and record the written entry in the local storage.
|
||||
var (
|
||||
value = common.Hash(stackData[stackLen-2].Bytes32())
|
||||
address = common.Hash(stackData[stackLen-1].Bytes32())
|
||||
value = common.Hash(stack[stackLen-2].Bytes32())
|
||||
address = common.Hash(stack[stackLen-1].Bytes32())
|
||||
)
|
||||
l.storage[contract.Address()][address] = value
|
||||
storage = l.storage[contract.Address()].Copy()
|
||||
l.storage[contractAddr][address] = value
|
||||
storage = l.storage[contractAddr].Copy()
|
||||
}
|
||||
}
|
||||
var rdata []byte
|
||||
|
|
@ -205,7 +219,7 @@ func (l *StructLogger) CaptureState(pc uint64, op vm.OpCode, gas, cost uint64, s
|
|||
copy(rdata, rData)
|
||||
}
|
||||
// create a new snapshot of the EVM.
|
||||
log := StructLog{pc, op, gas, cost, mem, memory.Len(), stck, rdata, storage, depth, l.env.StateDB.GetRefund(), err}
|
||||
log := StructLog{pc, op, gas, cost, mem, len(memory), stck, rdata, storage, depth, l.env.StateDB.GetRefund(), err}
|
||||
l.logs = append(l.logs, log)
|
||||
}
|
||||
|
||||
|
|
@ -247,7 +261,7 @@ func (l *StructLogger) Stop(err error) {
|
|||
l.interrupt.Store(true)
|
||||
}
|
||||
|
||||
func (l *StructLogger) CaptureTxStart(env *vm.EVM, tx *types.Transaction, from common.Address) {
|
||||
func (l *StructLogger) CaptureTxStart(env *live.VMContext, tx *types.Transaction, from common.Address) {
|
||||
l.env = env
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -25,13 +25,14 @@ import (
|
|||
"github.com/ethereum/go-ethereum/core/types"
|
||||
"github.com/ethereum/go-ethereum/core/vm"
|
||||
"github.com/ethereum/go-ethereum/eth/tracers/directory"
|
||||
"github.com/ethereum/go-ethereum/eth/tracers/directory/live"
|
||||
)
|
||||
|
||||
type JSONLogger struct {
|
||||
directory.NoopTracer
|
||||
encoder *json.Encoder
|
||||
cfg *Config
|
||||
env *vm.EVM
|
||||
env *live.VMContext
|
||||
}
|
||||
|
||||
// NewJSONLogger creates a new EVM tracer that prints execution steps as JSON objects
|
||||
|
|
@ -44,31 +45,40 @@ func NewJSONLogger(cfg *Config, writer io.Writer) *JSONLogger {
|
|||
return l
|
||||
}
|
||||
|
||||
func (l *JSONLogger) CaptureFault(pc uint64, op vm.OpCode, gas uint64, cost uint64, scope *vm.ScopeContext, depth int, err error) {
|
||||
func (l *JSONLogger) GetLogger() *live.LiveLogger {
|
||||
return &live.LiveLogger{
|
||||
CaptureTxStart: l.CaptureTxStart,
|
||||
CaptureEnd: l.CaptureEnd,
|
||||
CaptureState: l.CaptureState,
|
||||
CaptureFault: l.CaptureFault,
|
||||
}
|
||||
}
|
||||
|
||||
func (l *JSONLogger) CaptureFault(pc uint64, op live.OpCode, gas uint64, cost uint64, scope live.ScopeContext, depth int, err error) {
|
||||
// TODO: Add rData to this interface as well
|
||||
l.CaptureState(pc, op, gas, cost, scope, nil, depth, err)
|
||||
}
|
||||
|
||||
// CaptureState outputs state information on the logger.
|
||||
func (l *JSONLogger) CaptureState(pc uint64, op vm.OpCode, gas, cost uint64, scope *vm.ScopeContext, rData []byte, depth int, err error) {
|
||||
memory := scope.Memory
|
||||
stack := scope.Stack
|
||||
func (l *JSONLogger) CaptureState(pc uint64, op live.OpCode, gas, cost uint64, scope live.ScopeContext, rData []byte, depth int, err error) {
|
||||
memory := scope.GetMemoryData()
|
||||
stack := scope.GetStackData()
|
||||
|
||||
log := StructLog{
|
||||
Pc: pc,
|
||||
Op: op,
|
||||
Op: vm.OpCode(op),
|
||||
Gas: gas,
|
||||
GasCost: cost,
|
||||
MemorySize: memory.Len(),
|
||||
MemorySize: len(memory),
|
||||
Depth: depth,
|
||||
RefundCounter: l.env.StateDB.GetRefund(),
|
||||
Err: err,
|
||||
}
|
||||
if l.cfg.EnableMemory {
|
||||
log.Memory = memory.Data()
|
||||
log.Memory = memory
|
||||
}
|
||||
if !l.cfg.DisableStack {
|
||||
log.Stack = stack.Data()
|
||||
log.Stack = stack
|
||||
}
|
||||
if l.cfg.EnableReturnData {
|
||||
log.ReturnData = rData
|
||||
|
|
@ -90,6 +100,6 @@ func (l *JSONLogger) CaptureEnd(output []byte, gasUsed uint64, err error, revert
|
|||
l.encoder.Encode(endLog{common.Bytes2Hex(output), math.HexOrDecimal64(gasUsed), errMsg})
|
||||
}
|
||||
|
||||
func (l *JSONLogger) CaptureTxStart(env *vm.EVM, tx *types.Transaction, from common.Address) {
|
||||
func (l *JSONLogger) CaptureTxStart(env *live.VMContext, tx *types.Transaction, from common.Address) {
|
||||
l.env = env
|
||||
}
|
||||
|
|
|
|||
|
|
@ -26,6 +26,7 @@ import (
|
|||
"github.com/ethereum/go-ethereum/core/types"
|
||||
"github.com/ethereum/go-ethereum/core/vm"
|
||||
"github.com/ethereum/go-ethereum/eth/tracers/directory"
|
||||
"github.com/ethereum/go-ethereum/eth/tracers/directory/live"
|
||||
)
|
||||
|
||||
func init() {
|
||||
|
|
@ -48,7 +49,6 @@ func init() {
|
|||
// }
|
||||
type fourByteTracer struct {
|
||||
directory.NoopTracer
|
||||
env *vm.EVM
|
||||
ids map[string]int // ids aggregates the 4byte ids found
|
||||
interrupt atomic.Bool // Atomic flag to signal execution interruption
|
||||
reason error // Textual reason for the interruption
|
||||
|
|
@ -57,11 +57,19 @@ type fourByteTracer struct {
|
|||
|
||||
// newFourByteTracer returns a native go tracer which collects
|
||||
// 4 byte-identifiers of a tx, and implements vm.EVMLogger.
|
||||
func newFourByteTracer(ctx *directory.Context, _ json.RawMessage) (directory.Tracer, error) {
|
||||
func newFourByteTracer(ctx *directory.Context, _ json.RawMessage) (*directory.Tracer, error) {
|
||||
t := &fourByteTracer{
|
||||
ids: make(map[string]int),
|
||||
}
|
||||
return t, nil
|
||||
return &directory.Tracer{
|
||||
LiveLogger: &live.LiveLogger{
|
||||
CaptureTxStart: t.CaptureTxStart,
|
||||
CaptureStart: t.CaptureStart,
|
||||
CaptureEnter: t.CaptureEnter,
|
||||
},
|
||||
GetResult: t.GetResult,
|
||||
Stop: t.Stop,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// isPrecompiled returns whether the addr is a precompile. Logic borrowed from newJsTracer in eth/tracers/js/tracer.go
|
||||
|
|
@ -80,10 +88,9 @@ func (t *fourByteTracer) store(id []byte, size int) {
|
|||
t.ids[key] += 1
|
||||
}
|
||||
|
||||
func (t *fourByteTracer) CaptureTxStart(env *vm.EVM, tx *types.Transaction, from common.Address) {
|
||||
t.env = env
|
||||
func (t *fourByteTracer) CaptureTxStart(env *live.VMContext, tx *types.Transaction, from common.Address) {
|
||||
// Update list of precompiles based on current block
|
||||
rules := t.env.ChainConfig().Rules(t.env.Context.BlockNumber, t.env.Context.Random != nil, t.env.Context.Time)
|
||||
rules := env.ChainConfig.Rules(env.BlockNumber, env.Random != nil, env.Time)
|
||||
t.activePrecompiles = vm.ActivePrecompiles(rules)
|
||||
}
|
||||
|
||||
|
|
@ -96,7 +103,7 @@ func (t *fourByteTracer) CaptureStart(from common.Address, to common.Address, cr
|
|||
}
|
||||
|
||||
// CaptureEnter is called when EVM enters a new scope (via call, create or selfdestruct).
|
||||
func (t *fourByteTracer) CaptureEnter(op vm.OpCode, from common.Address, to common.Address, input []byte, gas uint64, value *big.Int) {
|
||||
func (t *fourByteTracer) CaptureEnter(opcode live.OpCode, from common.Address, to common.Address, input []byte, gas uint64, value *big.Int) {
|
||||
// Skip if tracing was interrupted
|
||||
if t.interrupt.Load() {
|
||||
return
|
||||
|
|
@ -104,6 +111,7 @@ func (t *fourByteTracer) CaptureEnter(op vm.OpCode, from common.Address, to comm
|
|||
if len(input) < 4 {
|
||||
return
|
||||
}
|
||||
op := vm.OpCode(opcode)
|
||||
// primarily we want to avoid CREATE/CREATE2/SELFDESTRUCT
|
||||
if op != vm.DELEGATECALL && op != vm.STATICCALL &&
|
||||
op != vm.CALL && op != vm.CALLCODE {
|
||||
|
|
|
|||
|
|
@ -28,6 +28,7 @@ import (
|
|||
"github.com/ethereum/go-ethereum/core/types"
|
||||
"github.com/ethereum/go-ethereum/core/vm"
|
||||
"github.com/ethereum/go-ethereum/eth/tracers/directory"
|
||||
"github.com/ethereum/go-ethereum/eth/tracers/directory/live"
|
||||
)
|
||||
|
||||
//go:generate go run github.com/fjl/gencodec -type callFrame -field-override callFrameMarshaling -out gen_callframe_json.go
|
||||
|
|
@ -120,7 +121,28 @@ type callTracerConfig struct {
|
|||
|
||||
// newCallTracer returns a native go tracer which tracks
|
||||
// call frames of a tx, and implements vm.EVMLogger.
|
||||
func newCallTracer(ctx *directory.Context, cfg json.RawMessage) (directory.Tracer, error) {
|
||||
func newCallTracer(ctx *directory.Context, cfg json.RawMessage) (*directory.Tracer, error) {
|
||||
t, err := newCallTracerObject(ctx, cfg)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &directory.Tracer{
|
||||
LiveLogger: &live.LiveLogger{
|
||||
CaptureTxStart: t.CaptureTxStart,
|
||||
CaptureTxEnd: t.CaptureTxEnd,
|
||||
CaptureStart: t.CaptureStart,
|
||||
CaptureEnd: t.CaptureEnd,
|
||||
CaptureEnter: t.CaptureEnter,
|
||||
CaptureExit: t.CaptureExit,
|
||||
CaptureState: t.CaptureState,
|
||||
OnLog: t.OnLog,
|
||||
},
|
||||
GetResult: t.GetResult,
|
||||
Stop: t.Stop,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func newCallTracerObject(ctx *directory.Context, cfg json.RawMessage) (*callTracer, error) {
|
||||
var config callTracerConfig
|
||||
if cfg != nil {
|
||||
if err := json.Unmarshal(cfg, &config); err != nil {
|
||||
|
|
@ -154,11 +176,11 @@ func (t *callTracer) CaptureEnd(output []byte, gasUsed uint64, err error, revert
|
|||
}
|
||||
|
||||
// CaptureState implements the EVMLogger interface to trace a single step of VM execution.
|
||||
func (t *callTracer) CaptureState(pc uint64, op vm.OpCode, gas, cost uint64, scope *vm.ScopeContext, rData []byte, depth int, err error) {
|
||||
func (t *callTracer) CaptureState(pc uint64, op live.OpCode, gas, cost uint64, scope live.ScopeContext, rData []byte, depth int, err error) {
|
||||
}
|
||||
|
||||
// CaptureEnter is called when EVM enters a new scope (via call, create or selfdestruct).
|
||||
func (t *callTracer) CaptureEnter(typ vm.OpCode, from common.Address, to common.Address, input []byte, gas uint64, value *big.Int) {
|
||||
func (t *callTracer) CaptureEnter(typ live.OpCode, from common.Address, to common.Address, input []byte, gas uint64, value *big.Int) {
|
||||
t.depth++
|
||||
if t.config.OnlyTopCall {
|
||||
return
|
||||
|
|
@ -170,7 +192,7 @@ func (t *callTracer) CaptureEnter(typ vm.OpCode, from common.Address, to common.
|
|||
|
||||
toCopy := to
|
||||
call := callFrame{
|
||||
Type: typ,
|
||||
Type: vm.OpCode(typ),
|
||||
From: from,
|
||||
To: &toCopy,
|
||||
Input: common.CopyBytes(input),
|
||||
|
|
@ -201,7 +223,7 @@ func (t *callTracer) CaptureExit(output []byte, gasUsed uint64, err error, rever
|
|||
t.callstack[size-1].Calls = append(t.callstack[size-1].Calls, call)
|
||||
}
|
||||
|
||||
func (t *callTracer) CaptureTxStart(env *vm.EVM, tx *types.Transaction, from common.Address) {
|
||||
func (t *callTracer) CaptureTxStart(env *live.VMContext, tx *types.Transaction, from common.Address) {
|
||||
t.gasLimit = tx.Gas()
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -28,6 +28,7 @@ import (
|
|||
"github.com/ethereum/go-ethereum/core/types"
|
||||
"github.com/ethereum/go-ethereum/core/vm"
|
||||
"github.com/ethereum/go-ethereum/eth/tracers/directory"
|
||||
"github.com/ethereum/go-ethereum/eth/tracers/directory/live"
|
||||
)
|
||||
|
||||
//go:generate go run github.com/fjl/gencodec -type flatCallAction -field-override flatCallActionMarshaling -out gen_flatcallaction_json.go
|
||||
|
|
@ -123,7 +124,7 @@ type flatCallTracerConfig struct {
|
|||
}
|
||||
|
||||
// newFlatCallTracer returns a new flatCallTracer.
|
||||
func newFlatCallTracer(ctx *directory.Context, cfg json.RawMessage) (directory.Tracer, error) {
|
||||
func newFlatCallTracer(ctx *directory.Context, cfg json.RawMessage) (*directory.Tracer, error) {
|
||||
var config flatCallTracerConfig
|
||||
if cfg != nil {
|
||||
if err := json.Unmarshal(cfg, &config); err != nil {
|
||||
|
|
@ -133,16 +134,26 @@ func newFlatCallTracer(ctx *directory.Context, cfg json.RawMessage) (directory.T
|
|||
|
||||
// Create inner call tracer with default configuration, don't forward
|
||||
// the OnlyTopCall or WithLog to inner for now
|
||||
tracer, err := directory.DefaultDirectory.New("callTracer", ctx, nil)
|
||||
t, err := newCallTracerObject(ctx, nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
t, ok := tracer.(*callTracer)
|
||||
if !ok {
|
||||
return nil, errors.New("internal error: embedded tracer has wrong type")
|
||||
}
|
||||
|
||||
return &flatCallTracer{tracer: t, ctx: ctx, config: config}, nil
|
||||
ft := &flatCallTracer{tracer: t, ctx: ctx, config: config}
|
||||
return &directory.Tracer{
|
||||
LiveLogger: &live.LiveLogger{
|
||||
CaptureTxStart: ft.CaptureTxStart,
|
||||
CaptureTxEnd: ft.CaptureTxEnd,
|
||||
CaptureStart: ft.CaptureStart,
|
||||
CaptureEnd: ft.CaptureEnd,
|
||||
CaptureEnter: ft.CaptureEnter,
|
||||
CaptureExit: ft.CaptureExit,
|
||||
CaptureState: ft.CaptureState,
|
||||
CaptureFault: ft.CaptureFault,
|
||||
},
|
||||
Stop: ft.Stop,
|
||||
GetResult: ft.GetResult,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// CaptureStart implements the EVMLogger interface to initialize the tracing operation.
|
||||
|
|
@ -156,17 +167,17 @@ func (t *flatCallTracer) CaptureEnd(output []byte, gasUsed uint64, err error, re
|
|||
}
|
||||
|
||||
// CaptureState implements the EVMLogger interface to trace a single step of VM execution.
|
||||
func (t *flatCallTracer) CaptureState(pc uint64, op vm.OpCode, gas, cost uint64, scope *vm.ScopeContext, rData []byte, depth int, err error) {
|
||||
func (t *flatCallTracer) CaptureState(pc uint64, op live.OpCode, gas, cost uint64, scope live.ScopeContext, rData []byte, depth int, err error) {
|
||||
t.tracer.CaptureState(pc, op, gas, cost, scope, rData, depth, err)
|
||||
}
|
||||
|
||||
// CaptureFault implements the EVMLogger interface to trace an execution fault.
|
||||
func (t *flatCallTracer) CaptureFault(pc uint64, op vm.OpCode, gas, cost uint64, scope *vm.ScopeContext, depth int, err error) {
|
||||
func (t *flatCallTracer) CaptureFault(pc uint64, op live.OpCode, gas, cost uint64, scope live.ScopeContext, depth int, err error) {
|
||||
t.tracer.CaptureFault(pc, op, gas, cost, scope, depth, err)
|
||||
}
|
||||
|
||||
// CaptureEnter is called when EVM enters a new scope (via call, create or selfdestruct).
|
||||
func (t *flatCallTracer) CaptureEnter(typ vm.OpCode, from common.Address, to common.Address, input []byte, gas uint64, value *big.Int) {
|
||||
func (t *flatCallTracer) CaptureEnter(typ live.OpCode, from common.Address, to common.Address, input []byte, gas uint64, value *big.Int) {
|
||||
t.tracer.CaptureEnter(typ, from, to, input, gas, value)
|
||||
|
||||
// Child calls must have a value, even if it's zero.
|
||||
|
|
@ -200,10 +211,10 @@ func (t *flatCallTracer) CaptureExit(output []byte, gasUsed uint64, err error, r
|
|||
}
|
||||
}
|
||||
|
||||
func (t *flatCallTracer) CaptureTxStart(env *vm.EVM, tx *types.Transaction, from common.Address) {
|
||||
func (t *flatCallTracer) CaptureTxStart(env *live.VMContext, tx *types.Transaction, from common.Address) {
|
||||
t.tracer.CaptureTxStart(env, tx, from)
|
||||
// Update list of precompiles based on current block
|
||||
rules := env.ChainConfig().Rules(env.Context.BlockNumber, env.Context.Random != nil, env.Context.Time)
|
||||
rules := env.ChainConfig.Rules(env.BlockNumber, env.Random != nil, env.Time)
|
||||
t.activePrecompiles = vm.ActivePrecompiles(rules)
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -21,10 +21,9 @@ import (
|
|||
"math/big"
|
||||
|
||||
"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/directory"
|
||||
"github.com/ethereum/go-ethereum/eth/tracers/directory/live"
|
||||
)
|
||||
|
||||
func init() {
|
||||
|
|
@ -35,18 +34,18 @@ func init() {
|
|||
// runs multiple tracers in one go.
|
||||
type muxTracer struct {
|
||||
names []string
|
||||
tracers []directory.Tracer
|
||||
tracers []*directory.Tracer
|
||||
}
|
||||
|
||||
// newMuxTracer returns a new mux tracer.
|
||||
func newMuxTracer(ctx *directory.Context, cfg json.RawMessage) (directory.Tracer, error) {
|
||||
func newMuxTracer(ctx *directory.Context, cfg json.RawMessage) (*directory.Tracer, error) {
|
||||
var config map[string]json.RawMessage
|
||||
if cfg != nil {
|
||||
if err := json.Unmarshal(cfg, &config); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
objects := make([]directory.Tracer, 0, len(config))
|
||||
objects := make([]*directory.Tracer, 0, len(config))
|
||||
names := make([]string, 0, len(config))
|
||||
for k, v := range config {
|
||||
t, err := directory.DefaultDirectory.New(k, ctx, v)
|
||||
|
|
@ -57,55 +56,90 @@ func newMuxTracer(ctx *directory.Context, cfg json.RawMessage) (directory.Tracer
|
|||
names = append(names, k)
|
||||
}
|
||||
|
||||
return &muxTracer{names: names, tracers: objects}, nil
|
||||
t := &muxTracer{names: names, tracers: objects}
|
||||
return &directory.Tracer{
|
||||
LiveLogger: &live.LiveLogger{
|
||||
CaptureTxStart: t.CaptureTxStart,
|
||||
CaptureTxEnd: t.CaptureTxEnd,
|
||||
CaptureStart: t.CaptureStart,
|
||||
CaptureEnd: t.CaptureEnd,
|
||||
CaptureEnter: t.CaptureEnter,
|
||||
CaptureExit: t.CaptureExit,
|
||||
CaptureState: t.CaptureState,
|
||||
CaptureFault: t.CaptureFault,
|
||||
CaptureKeccakPreimage: t.CaptureKeccakPreimage,
|
||||
OnGasChange: t.OnGasChange,
|
||||
OnBalanceChange: t.OnBalanceChange,
|
||||
OnNonceChange: t.OnNonceChange,
|
||||
OnCodeChange: t.OnCodeChange,
|
||||
OnStorageChange: t.OnStorageChange,
|
||||
OnLog: t.OnLog,
|
||||
},
|
||||
GetResult: t.GetResult,
|
||||
Stop: t.Stop,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// CaptureStart implements the EVMLogger interface to initialize the tracing operation.
|
||||
func (t *muxTracer) CaptureStart(from common.Address, to common.Address, create bool, input []byte, gas uint64, value *big.Int) {
|
||||
for _, t := range t.tracers {
|
||||
t.CaptureStart(from, to, create, input, gas, value)
|
||||
if t.CaptureStart != nil {
|
||||
t.CaptureStart(from, to, create, input, gas, value)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// CaptureEnd is called after the call finishes to finalize the tracing.
|
||||
func (t *muxTracer) CaptureEnd(output []byte, gasUsed uint64, err error, reverted bool) {
|
||||
for _, t := range t.tracers {
|
||||
t.CaptureEnd(output, gasUsed, err, reverted)
|
||||
if t.CaptureEnd != nil {
|
||||
t.CaptureEnd(output, gasUsed, err, reverted)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// CaptureState implements the EVMLogger interface to trace a single step of VM execution.
|
||||
func (t *muxTracer) CaptureState(pc uint64, op vm.OpCode, gas, cost uint64, scope *vm.ScopeContext, rData []byte, depth int, err error) {
|
||||
func (t *muxTracer) CaptureState(pc uint64, op live.OpCode, gas, cost uint64, scope live.ScopeContext, rData []byte, depth int, err error) {
|
||||
for _, t := range t.tracers {
|
||||
t.CaptureState(pc, op, gas, cost, scope, rData, depth, err)
|
||||
if t.CaptureState != nil {
|
||||
t.CaptureState(pc, op, gas, cost, scope, rData, depth, err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// CaptureFault implements the EVMLogger interface to trace an execution fault.
|
||||
func (t *muxTracer) CaptureFault(pc uint64, op vm.OpCode, gas, cost uint64, scope *vm.ScopeContext, depth int, err error) {
|
||||
func (t *muxTracer) CaptureFault(pc uint64, op live.OpCode, gas, cost uint64, scope live.ScopeContext, depth int, err error) {
|
||||
for _, t := range t.tracers {
|
||||
t.CaptureFault(pc, op, gas, cost, scope, depth, err)
|
||||
if t.CaptureFault != nil {
|
||||
t.CaptureFault(pc, op, gas, cost, scope, depth, err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// CaptureKeccakPreimage is called during the KECCAK256 opcode.
|
||||
func (t *muxTracer) CaptureKeccakPreimage(hash common.Hash, data []byte) {
|
||||
for _, t := range t.tracers {
|
||||
t.CaptureKeccakPreimage(hash, data)
|
||||
if t.CaptureKeccakPreimage != nil {
|
||||
t.CaptureKeccakPreimage(hash, data)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// CaptureGasConsumed is called when gas is consumed.
|
||||
func (t *muxTracer) OnGasChange(old, new uint64, reason vm.GasChangeReason) {
|
||||
func (t *muxTracer) OnGasChange(old, new uint64, reason live.GasChangeReason) {
|
||||
for _, t := range t.tracers {
|
||||
t.OnGasChange(old, new, reason)
|
||||
if t.OnGasChange != nil {
|
||||
t.OnGasChange(old, new, reason)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// CaptureEnter is called when EVM enters a new scope (via call, create or selfdestruct).
|
||||
func (t *muxTracer) CaptureEnter(typ vm.OpCode, from common.Address, to common.Address, input []byte, gas uint64, value *big.Int) {
|
||||
func (t *muxTracer) CaptureEnter(typ live.OpCode, from common.Address, to common.Address, input []byte, gas uint64, value *big.Int) {
|
||||
for _, t := range t.tracers {
|
||||
t.CaptureEnter(typ, from, to, input, gas, value)
|
||||
if t.CaptureEnter != nil {
|
||||
t.CaptureEnter(typ, from, to, input, gas, value)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -113,49 +147,65 @@ func (t *muxTracer) CaptureEnter(typ vm.OpCode, from common.Address, to common.A
|
|||
// execute any code.
|
||||
func (t *muxTracer) CaptureExit(output []byte, gasUsed uint64, err error, reverted bool) {
|
||||
for _, t := range t.tracers {
|
||||
t.CaptureExit(output, gasUsed, err, reverted)
|
||||
if t.CaptureExit != nil {
|
||||
t.CaptureExit(output, gasUsed, err, reverted)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (t *muxTracer) CaptureTxStart(env *vm.EVM, tx *types.Transaction, from common.Address) {
|
||||
func (t *muxTracer) CaptureTxStart(env *live.VMContext, tx *types.Transaction, from common.Address) {
|
||||
for _, t := range t.tracers {
|
||||
t.CaptureTxStart(env, tx, from)
|
||||
if t.CaptureTxStart != nil {
|
||||
t.CaptureTxStart(env, tx, from)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (t *muxTracer) CaptureTxEnd(receipt *types.Receipt, err error) {
|
||||
for _, t := range t.tracers {
|
||||
t.CaptureTxEnd(receipt, err)
|
||||
if t.CaptureTxEnd != nil {
|
||||
t.CaptureTxEnd(receipt, err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (t *muxTracer) OnBalanceChange(a common.Address, prev, new *big.Int, reason state.BalanceChangeReason) {
|
||||
func (t *muxTracer) OnBalanceChange(a common.Address, prev, new *big.Int, reason live.BalanceChangeReason) {
|
||||
for _, t := range t.tracers {
|
||||
t.OnBalanceChange(a, prev, new, reason)
|
||||
if t.OnBalanceChange != nil {
|
||||
t.OnBalanceChange(a, prev, new, reason)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (t *muxTracer) OnNonceChange(a common.Address, prev, new uint64) {
|
||||
for _, t := range t.tracers {
|
||||
t.OnNonceChange(a, prev, new)
|
||||
if t.OnNonceChange != nil {
|
||||
t.OnNonceChange(a, prev, new)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (t *muxTracer) OnCodeChange(a common.Address, prevCodeHash common.Hash, prev []byte, codeHash common.Hash, code []byte) {
|
||||
for _, t := range t.tracers {
|
||||
t.OnCodeChange(a, prevCodeHash, prev, codeHash, code)
|
||||
if t.OnCodeChange != nil {
|
||||
t.OnCodeChange(a, prevCodeHash, prev, codeHash, code)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (t *muxTracer) OnStorageChange(a common.Address, k, prev, new common.Hash) {
|
||||
for _, t := range t.tracers {
|
||||
t.OnStorageChange(a, k, prev, new)
|
||||
if t.OnStorageChange != nil {
|
||||
t.OnStorageChange(a, k, prev, new)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (t *muxTracer) OnLog(log *types.Log) {
|
||||
for _, t := range t.tracers {
|
||||
t.OnLog(log)
|
||||
if t.OnLog != nil {
|
||||
t.OnLog(log)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -28,6 +28,7 @@ import (
|
|||
"github.com/ethereum/go-ethereum/core/vm"
|
||||
"github.com/ethereum/go-ethereum/crypto"
|
||||
"github.com/ethereum/go-ethereum/eth/tracers/directory"
|
||||
"github.com/ethereum/go-ethereum/eth/tracers/directory/live"
|
||||
"github.com/ethereum/go-ethereum/log"
|
||||
)
|
||||
|
||||
|
|
@ -58,7 +59,7 @@ type accountMarshaling struct {
|
|||
|
||||
type prestateTracer struct {
|
||||
directory.NoopTracer
|
||||
env *vm.EVM
|
||||
env *live.VMContext
|
||||
pre stateMap
|
||||
post stateMap
|
||||
to common.Address
|
||||
|
|
@ -73,24 +74,33 @@ type prestateTracerConfig struct {
|
|||
DiffMode bool `json:"diffMode"` // If true, this tracer will return state modifications
|
||||
}
|
||||
|
||||
func newPrestateTracer(ctx *directory.Context, cfg json.RawMessage) (directory.Tracer, error) {
|
||||
func newPrestateTracer(ctx *directory.Context, cfg json.RawMessage) (*directory.Tracer, error) {
|
||||
var config prestateTracerConfig
|
||||
if cfg != nil {
|
||||
if err := json.Unmarshal(cfg, &config); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
return &prestateTracer{
|
||||
t := &prestateTracer{
|
||||
pre: stateMap{},
|
||||
post: stateMap{},
|
||||
config: config,
|
||||
created: make(map[common.Address]bool),
|
||||
deleted: make(map[common.Address]bool),
|
||||
}
|
||||
return &directory.Tracer{
|
||||
LiveLogger: &live.LiveLogger{
|
||||
CaptureTxStart: t.CaptureTxStart,
|
||||
CaptureTxEnd: t.CaptureTxEnd,
|
||||
CaptureState: t.CaptureState,
|
||||
},
|
||||
GetResult: t.GetResult,
|
||||
Stop: t.Stop,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// CaptureState implements the EVMLogger interface to trace a single step of VM execution.
|
||||
func (t *prestateTracer) CaptureState(pc uint64, op vm.OpCode, gas, cost uint64, scope *vm.ScopeContext, rData []byte, depth int, err error) {
|
||||
func (t *prestateTracer) CaptureState(pc uint64, opcode live.OpCode, gas, cost uint64, scope live.ScopeContext, rData []byte, depth int, err error) {
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
|
@ -98,10 +108,10 @@ func (t *prestateTracer) CaptureState(pc uint64, op vm.OpCode, gas, cost uint64,
|
|||
if t.interrupt.Load() {
|
||||
return
|
||||
}
|
||||
stack := scope.Stack
|
||||
stackData := stack.Data()
|
||||
op := vm.OpCode(opcode)
|
||||
stackData := scope.GetStackData()
|
||||
stackLen := len(stackData)
|
||||
caller := scope.Contract.Address()
|
||||
caller := scope.GetAddress()
|
||||
switch {
|
||||
case stackLen >= 1 && (op == vm.SLOAD || op == vm.SSTORE):
|
||||
slot := common.Hash(stackData[stackLen-1].Bytes32())
|
||||
|
|
@ -136,7 +146,7 @@ func (t *prestateTracer) CaptureState(pc uint64, op vm.OpCode, gas, cost uint64,
|
|||
}
|
||||
}
|
||||
|
||||
func (t *prestateTracer) CaptureTxStart(env *vm.EVM, tx *types.Transaction, from common.Address) {
|
||||
func (t *prestateTracer) CaptureTxStart(env *live.VMContext, tx *types.Transaction, from common.Address) {
|
||||
t.env = env
|
||||
if tx.To() == nil {
|
||||
t.to = crypto.CreateAddress(from, env.StateDB.GetNonce(from))
|
||||
|
|
@ -147,7 +157,7 @@ func (t *prestateTracer) CaptureTxStart(env *vm.EVM, tx *types.Transaction, from
|
|||
|
||||
t.lookupAccount(from)
|
||||
t.lookupAccount(t.to)
|
||||
t.lookupAccount(env.Context.Coinbase)
|
||||
t.lookupAccount(env.Coinbase)
|
||||
}
|
||||
|
||||
func (t *prestateTracer) CaptureTxEnd(receipt *types.Receipt, err error) {
|
||||
|
|
|
|||
|
|
@ -40,6 +40,7 @@ import (
|
|||
"github.com/ethereum/go-ethereum/core/vm"
|
||||
"github.com/ethereum/go-ethereum/crypto"
|
||||
"github.com/ethereum/go-ethereum/eth/gasestimator"
|
||||
"github.com/ethereum/go-ethereum/eth/tracers/directory/live"
|
||||
"github.com/ethereum/go-ethereum/eth/tracers/logger"
|
||||
"github.com/ethereum/go-ethereum/log"
|
||||
"github.com/ethereum/go-ethereum/p2p"
|
||||
|
|
@ -978,7 +979,7 @@ func (diff *StateOverride) Apply(statedb *state.StateDB) error {
|
|||
// Override account balance.
|
||||
if account.Balance != nil {
|
||||
u256Balance, _ := uint256.FromBig((*big.Int)(*account.Balance))
|
||||
statedb.SetBalance(addr, u256Balance, state.BalanceChangeUnspecified)
|
||||
statedb.SetBalance(addr, u256Balance, live.BalanceChangeUnspecified)
|
||||
}
|
||||
if account.State != nil && account.StateDiff != nil {
|
||||
return fmt.Errorf("account %s has both 'state' and 'stateDiff'", addr.Hex())
|
||||
|
|
@ -1526,7 +1527,7 @@ func AccessList(ctx context.Context, b Backend, blockNrOrHash rpc.BlockNumberOrH
|
|||
|
||||
// Apply the transaction with the access list tracer
|
||||
tracer := logger.NewAccessListTracer(accessList, args.from(), to, precompiles)
|
||||
config := vm.Config{Tracer: tracer, NoBaseFee: true}
|
||||
config := vm.Config{Tracer: tracer.GetLogger(), NoBaseFee: true}
|
||||
vmenv := b.GetEVM(ctx, msg, statedb, header, &config, nil)
|
||||
res, err := core.ApplyMessage(vmenv, msg, new(core.GasPool).AddGas(msg.GasLimit))
|
||||
if err != nil {
|
||||
|
|
|
|||
|
|
@ -990,7 +990,7 @@ func (w *worker) prepareWork(genParams *generateParams) (*environment, error) {
|
|||
if header.ParentBeaconRoot != nil {
|
||||
context := core.NewEVMBlockContext(header, w.chain, nil)
|
||||
vmenv := vm.NewEVM(context, vm.TxContext{}, env.state, w.chainConfig, vm.Config{})
|
||||
core.ProcessBeaconBlockRoot(*header.ParentBeaconRoot, vmenv, env.state, nil)
|
||||
core.ProcessBeaconBlockRoot(*header.ParentBeaconRoot, vmenv, env.state)
|
||||
}
|
||||
return env, nil
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue