diff --git a/core/blockchain.go b/core/blockchain.go index a3c2fa648437..859eaae0241d 100644 --- a/core/blockchain.go +++ b/core/blockchain.go @@ -205,6 +205,7 @@ type BlockChain struct { chainHeadFeed event.Feed logsFeed event.Feed blockProcFeed event.Feed + tracesFeed event.Feed scope event.SubscriptionScope genesisBlock *types.Block @@ -254,13 +255,6 @@ func NewBlockChain(db ethdb.Database, cacheConfig *CacheConfig, genesis *Genesis Preimages: cacheConfig.Preimages, }) var logger BlockchainLogger - if vmConfig.Tracer != nil { - l, ok := vmConfig.Tracer.(BlockchainLogger) - if !ok { - return nil, fmt.Errorf("only extended tracers are supported for live mode") - } - logger = l - } // Setup the genesis block, commit the provided genesis specification // to database if the genesis block is not present yet, or load the // stored one from database. @@ -293,7 +287,14 @@ func NewBlockChain(db ethdb.Database, cacheConfig *CacheConfig, genesis *Genesis futureBlocks: lru.NewCache[common.Hash, *types.Block](maxFutureBlocks), engine: engine, vmConfig: vmConfig, - logger: logger, + } + if vmConfig.Tracer != nil { + l, ok := vmConfig.Tracer.(BlockchainLogger) + if !ok { + return nil, fmt.Errorf("only extended tracers are supported for live mode") + } + bc.logger = l + go vmConfig.Tracer.EventLoop(bc) } bc.flushInterval.Store(int64(cacheConfig.TrieTimeLimit)) bc.forker = NewForkChoice(bc, shouldPreserve) diff --git a/core/blockchain_reader.go b/core/blockchain_reader.go index fd65cb2db32f..811c1f284f60 100644 --- a/core/blockchain_reader.go +++ b/core/blockchain_reader.go @@ -409,3 +409,8 @@ func (bc *BlockChain) SubscribeLogsEvent(ch chan<- []*types.Log) event.Subscript func (bc *BlockChain) SubscribeBlockProcessingEvent(ch chan<- bool) event.Subscription { return bc.scope.Track(bc.blockProcFeed.Subscribe(ch)) } + +// SubscribeTracesEvent registers a subscription of TracesEvent. +func (bc *BlockChain) SubscribeTracesEvent(ch chan<- *types.Trace) event.Subscription { + return bc.scope.Track(bc.tracesFeed.Subscribe(ch)) +} \ No newline at end of file diff --git a/core/types/trace.go b/core/types/trace.go new file mode 100644 index 000000000000..46bcf1c0d38c --- /dev/null +++ b/core/types/trace.go @@ -0,0 +1,192 @@ +// Copyright 2014 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 . + +package types + +import ( + "math/big" + + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/core/vm" +) + +//go:generate go run github.com/fjl/gencodec -type Log -field-override logMarshaling -out gen_log_json.go + +// CaptureStartData stores data when a new transaction starts executing +type CaptureStartData struct { + From common.Address // Sender of the transaction + To common.Address // Recipient of the transaction + Create bool // Indicates whether the transaction creates a contract + Input []byte // Input data of the transaction + Gas uint64 // Gas provided for the transaction execution + Value *big.Int // Amount of ether transferred in the transaction +} + +// CaptureEndData stores data when a transaction finishes executing +type CaptureEndData struct { + Output []byte // Output data from the transaction execution + GasUsed uint64 // Amount of gas consumed by the transaction + Err error // Error that occurred during transaction execution, if any +} + +// CaptureStateData stores data at each step during a transaction's execution +type CaptureStateData struct { + Pc uint64 // Current program counter in the EVM code + Op vm.OpCode // Opcode being executed at the current step + Gas uint64 // Remaining gas for the transaction + Cost uint64 // Cost of the current operation + Scope *vm.ScopeContext // Contextual information about the execution environment + RData []byte // Return data from executed operations + Depth int // Current call depth + Err error // Error that occurred during execution, if any +} + +// CaptureFaultData stores data when an execution fault occurs during a transaction's execution +type CaptureFaultData struct { + Pc uint64 // Current program counter in the EVM code + Op vm.OpCode // Opcode being executed at the fault + Gas uint64 // Remaining gas for the transaction + Cost uint64 // Cost of the faulted operation + Depth int // Current call depth + Err error // Error that occurred leading to the fault +} + +// CaptureKeccakPreimageData stores the data input to the KECCAK256 opcode. +type CaptureKeccakPreimageData struct { + Hash common.Hash // The KECCAK256 hash of the data + Data []byte // The original data +} + +// CaptureEnterData stores data when the EVM enters a new execution scope +type CaptureEnterData struct { + Type vm.OpCode // Opcode that caused the new scope + From common.Address // Address of the scope creator + To common.Address // Address of the new scope + Input []byte // Input data to the new scope + Gas uint64 // Gas provided to the new scope + Value *big.Int // Value transferred into the new scope +} + +// CaptureExitData stores data when the EVM exits a scope +type CaptureExitData struct { + Output []byte // Output data from the scope + GasUsed uint64 // Amount of gas consumed in the scope + Err error // Error that occurred during scope execution, if any +} + +// CaptureTxStartData stores data when a transaction begins to execute +type CaptureTxStartData struct { + Env *vm.EVM // The EVM environment + Tx *Transaction // The transaction being executed +} + +// CaptureTxEndData stores data when a transaction finishes executing +type CaptureTxEndData struct { + Receipt *Receipt // The receipt generated from the transaction execution +} + +// OnBlockStartData stores data when a new block begins processing +type OnBlockStartData struct { + Block *Block // The block being processed +} + +// OnBlockEndData stores data when a block has finished processing +type OnBlockEndData struct { + Td *big.Int // Total difficulty of the blockchain after the block + Err error // Any error that occurred during block processing +} + +// OnBlockValidationErrorData stores data when a block fails validation +type OnBlockValidationErrorData struct { + Block *Block // The block that failed validation + Err error // The error that caused the validation failure +} + +// OnGenesisBlockData stores data when the genesis block is processed +type OnGenesisBlockData struct { + Block *Block // The genesis block +} + +// OnBalanceChangeData stores data when the balance of an account changes +type OnBalanceChangeData struct { + Address common.Address // The account whose balance changed + Prev *big.Int // The previous balance + New *big.Int // The new balance +} + +// OnNonceChangeData stores data when the nonce of an account changes +type OnNonceChangeData struct { + Address common.Address // The account whose nonce changed + Prev uint64 // The previous nonce + New uint64 // The new nonce +} + +// OnCodeChangeData stores data when the code of an account changes +type OnCodeChangeData struct { + Address common.Address // The account whose code changed + PrevCodeHash common.Hash // The KECCAK256 hash of the previous code + Prev []byte // The previous code + CodeHash common.Hash // The KECCAK256 hash of the new code + Code []byte // The new code +} + +// OnStorageChangeData stores data when the storage of an account changes +type OnStorageChangeData struct { + Address common.Address // The account whose storage changed + Key common.Hash // The storage key that changed + Prev common.Hash // The previous value at the key + New common.Hash // The new value at the key +} + +// OnLogData stores data when a new log is created +type OnLogData struct { + Log *Log // The new log +} + +// OnNewAccountData stores data when a new account is created +type OnNewAccountData struct { + Address common.Address // The address of the new account +} + +// OnGasConsumedData stores data when gas is consumed during execution +type OnGasConsumedData struct { + Gas uint64 // The remaining gas after consumption + Amount uint64 // The amount of gas consumed +} + +// Trace represents chain processing operations. These events are generated by the chain operations. +type Trace struct { + CaptureStart []CaptureStartData + CaptureEnd []CaptureEndData + CaptureState []CaptureStateData + CaptureFault []CaptureFaultData + CaptureKeccakPreimage []CaptureKeccakPreimageData + CaptureEnter []CaptureEnterData + CaptureExit []CaptureExitData + CaptureTxStart []CaptureTxStartData + CaptureTxEnd []CaptureTxEndData + OnBlockStart []OnBlockStartData + OnBlockEnd []OnBlockEndData + OnBlockValidationError []OnBlockValidationErrorData + OnGenesisBlock []OnGenesisBlockData + OnBalanceChange []OnBalanceChangeData + OnNonceChange []OnNonceChangeData + OnCodeChange []OnCodeChangeData + OnStorageChange []OnStorageChangeData + OnLog []OnLogData + OnNewAccount []OnNewAccountData + OnGasConsumed []OnGasConsumedData +} diff --git a/core/vm/logger.go b/core/vm/logger.go index b7b0fc4d8e20..8833b8b00449 100644 --- a/core/vm/logger.go +++ b/core/vm/logger.go @@ -21,6 +21,7 @@ import ( "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/core/types" + "github.com/ethereum/go-ethereum/core/" ) // EVMLogger is used to collect execution traces from an EVM transaction @@ -44,4 +45,5 @@ type EVMLogger interface { CaptureKeccakPreimage(hash common.Hash, data []byte) // Misc OnGasConsumed(gas, amount uint64) + EventLoop(bc core.BlockChain) } diff --git a/eth/api_backend.go b/eth/api_backend.go index 18aea2d039ae..bcaf42c6f6c4 100644 --- a/eth/api_backend.go +++ b/eth/api_backend.go @@ -277,6 +277,10 @@ func (b *EthAPIBackend) SubscribePendingLogsEvent(ch chan<- []*types.Log) event. return b.eth.miner.SubscribePendingLogs(ch) } +func (b *EthAPIBackend) SubscribeTracesEvent(ch chan<- *types.Trace) event.Subscription { + return b.eth.BlockChain().SubscribeChainEvent(ch) +} + func (b *EthAPIBackend) SubscribeChainEvent(ch chan<- core.ChainEvent) event.Subscription { return b.eth.BlockChain().SubscribeChainEvent(ch) } diff --git a/eth/filters/api.go b/eth/filters/api.go index cc08b442e850..6b2c8c8a897f 100644 --- a/eth/filters/api.go +++ b/eth/filters/api.go @@ -284,6 +284,36 @@ func (api *FilterAPI) Logs(ctx context.Context, crit FilterCriteria) (*rpc.Subsc return rpcSub, nil } +// Logs creates a subscription that fires for all new log that match the given filter criteria. +func (api *FilterAPI) Traces(ctx context.Context) (*rpc.Subscription, error) { + notifier, supported := rpc.NotifierFromContext(ctx) + if !supported { + return &rpc.Subscription{}, rpc.ErrNotificationsUnsupported + } + + rpcSub := notifier.CreateSubscription() + + go func() { + traces := make(chan *types.Trace) + tracesSub := api.events.SubscribeTraces(traces) + + for { + select { + case s := <-traces: + notifier.Notify(rpcSub.ID, s) + case <-rpcSub.Err(): + tracesSub.Unsubscribe() + return + case <-notifier.Closed(): + tracesSub.Unsubscribe() + return + } + } + }() + + return rpcSub, nil +} + // FilterCriteria represents a request to create a new filter. // Same as ethereum.FilterQuery but with UnmarshalJSON() method. type FilterCriteria ethereum.FilterQuery diff --git a/eth/filters/filter_system.go b/eth/filters/filter_system.go index 35e396c23e75..4fc46a8563ef 100644 --- a/eth/filters/filter_system.go +++ b/eth/filters/filter_system.go @@ -72,6 +72,7 @@ type Backend interface { SubscribeRemovedLogsEvent(ch chan<- core.RemovedLogsEvent) event.Subscription SubscribeLogsEvent(ch chan<- []*types.Log) event.Subscription SubscribePendingLogsEvent(ch chan<- []*types.Log) event.Subscription + SubscribeTracesEvent(ch chan<- *types.Trace) event.Subscription BloomStatus() (uint64, uint64) ServiceFilter(ctx context.Context, session *bloombits.MatcherSession) @@ -164,6 +165,8 @@ const ( BlocksSubscription // LastIndexSubscription keeps track of the last index LastIndexSubscription + // TracesSubscription keeps track of the normal chain processing operations + TracesSubscription ) const ( @@ -185,6 +188,7 @@ type subscription struct { logsCrit ethereum.FilterQuery logs chan []*types.Log txs chan []*types.Transaction + traces chan *types.Trace headers chan *types.Header installed chan struct{} // closed when the filter is installed err chan error // closed when the filter is uninstalled @@ -204,6 +208,7 @@ type EventSystem struct { rmLogsSub event.Subscription // Subscription for removed log event pendingLogsSub event.Subscription // Subscription for pending log event chainSub event.Subscription // Subscription for new chain event + tracesSub event.Subscription // Subscription for traces event // Channels install chan *subscription // install filter for event notification @@ -213,6 +218,7 @@ type EventSystem struct { pendingLogsCh chan []*types.Log // Channel to receive new log event rmLogsCh chan core.RemovedLogsEvent // Channel to receive removed log event chainCh chan core.ChainEvent // Channel to receive new chain event + tracesCh chan *types.Trace } // NewEventSystem creates a new manager that listens for event on the given mux, @@ -241,7 +247,7 @@ func NewEventSystem(sys *FilterSystem, lightMode bool) *EventSystem { m.rmLogsSub = m.backend.SubscribeRemovedLogsEvent(m.rmLogsCh) m.chainSub = m.backend.SubscribeChainEvent(m.chainCh) m.pendingLogsSub = m.backend.SubscribePendingLogsEvent(m.pendingLogsCh) - + m.tracesSub = m.backend.SubscribeTracesEvent(m.tracesCh) // Make sure none of the subscriptions are empty if m.txsSub == nil || m.logsSub == nil || m.rmLogsSub == nil || m.chainSub == nil || m.pendingLogsSub == nil { log.Crit("Subscribe for event system failed") @@ -418,6 +424,23 @@ func (es *EventSystem) SubscribePendingTxs(txs chan []*types.Transaction) *Subsc return es.subscribe(sub) } +// SubscribePendingTxs creates a subscription that writes transactions for +// transactions that enter the transaction pool. +func (es *EventSystem) SubscribeTraces(traces chan *types.Trace) *Subscription { + sub := &subscription{ + id: rpc.NewID(), + typ: TracesSubscription, + created: time.Now(), + logs: make(chan []*types.Log), + txs: make(chan []*types.Transaction), + traces: traces, + headers: make(chan *types.Header), + installed: make(chan struct{}), + err: make(chan error), + } + return es.subscribe(sub) +} + type filterIndex map[Type]map[rpc.ID]*subscription func (es *EventSystem) handleLogs(filters filterIndex, ev []*types.Log) { @@ -471,6 +494,15 @@ func (es *EventSystem) handleChainEvent(filters filterIndex, ev core.ChainEvent) } } +func (es *EventSystem) handleTraces(filters filterIndex, ev *types.Trace) { + if len(ev.CaptureStart) == 0 { + return + } + for _, f := range filters[TracesSubscription] { + f.traces <- ev + } +} + func (es *EventSystem) lightFilterNewHead(newHeader *types.Header, callBack func(*types.Header, bool)) { oldh := es.lastHead es.lastHead = newHeader @@ -569,6 +601,8 @@ func (es *EventSystem) eventLoop() { es.handlePendingLogs(index, ev) case ev := <-es.chainCh: es.handleChainEvent(index, ev) + case ev := <-es.tracesCh: + es.handleTraces(index, ev) case f := <-es.install: if f.typ == MinedAndPendingLogsSubscription { diff --git a/eth/tracers/printer.go b/eth/tracers/printer.go index 42855de9bd3c..1378bafb6360 100644 --- a/eth/tracers/printer.go +++ b/eth/tracers/printer.go @@ -7,97 +7,327 @@ import ( "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/core/vm" + "github.com/ethereum/go-ethereum/core" + ) -type Printer struct{} +type Printer struct { + bc core.BlockChain + Trace types.Trace + CaptureStartChan chan types.CaptureStartData + CaptureEndChan chan types.CaptureEndData + CaptureStateChan chan types.CaptureStateData + CaptureFaultChan chan types.CaptureFaultData + CaptureKeccakPreimageChan chan types.CaptureKeccakPreimageData + CaptureEnterChan chan types.CaptureEnterData + CaptureExitChan chan types.CaptureExitData + CaptureTxStartChan chan types.CaptureTxStartData + CaptureTxEndChan chan types.CaptureTxEndData + OnBlockStartChan chan types.OnBlockStartData + OnBlockEndChan chan types.OnBlockEndData + OnBlockValidationErrorChan chan types.OnBlockValidationErrorData + OnGenesisBlockChan chan types.OnGenesisBlockData + OnBalanceChangeChan chan types.OnBalanceChangeData + OnNonceChangeChan chan types.OnNonceChangeData + OnCodeChangeChan chan types.OnCodeChangeData + OnStorageChangeChan chan types.OnStorageChangeData + OnLogChan chan types.OnLogData + OnNewAccountChan chan types.OnNewAccountData + OnGasConsumedChan chan types.OnGasConsumedData + doneChan chan struct{} +} +// TODO: Determine the appropriate channel capacity size. func NewPrinter() *Printer { - return &Printer{} + return &Printer{ + Trace: types.Trace{}, + CaptureStartChan: make(chan types.CaptureStartData, 100), + CaptureEndChan: make(chan types.CaptureEndData, 100), + CaptureStateChan: make(chan types.CaptureStateData, 100), + CaptureFaultChan: make(chan types.CaptureFaultData, 100), + CaptureKeccakPreimageChan: make(chan types.CaptureKeccakPreimageData, 100), + CaptureEnterChan: make(chan types.CaptureEnterData, 100), + CaptureExitChan: make(chan types.CaptureExitData, 100), + CaptureTxStartChan: make(chan types.CaptureTxStartData, 100), + CaptureTxEndChan: make(chan types.CaptureTxEndData, 100), + OnBlockStartChan: make(chan types.OnBlockStartData, 100), + OnBlockEndChan: make(chan types.OnBlockEndData, 100), + OnBlockValidationErrorChan: make(chan types.OnBlockValidationErrorData, 100), + OnGenesisBlockChan: make(chan types.OnGenesisBlockData, 100), + OnBalanceChangeChan: make(chan types.OnBalanceChangeData, 100), + OnNonceChangeChan: make(chan types.OnNonceChangeData, 100), + OnCodeChangeChan: make(chan types.OnCodeChangeData, 100), + OnStorageChangeChan: make(chan types.OnStorageChangeData, 100), + OnLogChan: make(chan types.OnLogData, 100), + OnNewAccountChan: make(chan types.OnNewAccountData, 100), + OnGasConsumedChan: make(chan types.OnGasConsumedData, 100), + } } // CaptureStart implements the EVMLogger interface to initialize the tracing operation. func (p *Printer) CaptureStart(from common.Address, to common.Address, create bool, input []byte, gas uint64, value *big.Int) { + // Initialize CaptureStartData with provided arguments + data := types.CaptureStartData{ + From: from, + To: to, + Create: create, + Input: input, + Gas: gas, + Value: value, + } + + // Send the data to the channel + p.CaptureStartChan <- data fmt.Printf("CaptureStart: from=%v, to=%v, create=%v, input=%v, gas=%v, value=%v\n", from, to, create, input, gas, value) } // CaptureEnd is called after the call finishes to finalize the tracing. func (p *Printer) CaptureEnd(output []byte, gasUsed uint64, err error) { + data := types.CaptureEndData{ + Output: output, + GasUsed: gasUsed, + Err: err, + } + p.CaptureEndChan <- data fmt.Printf("CaptureEnd: output=%v, gasUsed=%v, err=%v\n", output, gasUsed, err) } // CaptureState implements the EVMLogger interface to trace a single step of VM execution. func (p *Printer) CaptureState(pc uint64, op vm.OpCode, gas, cost uint64, scope *vm.ScopeContext, rData []byte, depth int, err error) { +/* data := types.CaptureStateData{ + Pc: pc, + Op: op, + Gas: gas, + Cost: cost, + Scope: scope, + RData: rData, + Depth: depth, + Err: err, + } + p.CaptureStateChan <- data */ //fmt.Printf("CaptureState: pc=%v, op=%v, gas=%v, cost=%v, scope=%v, rData=%v, depth=%v, err=%v\n", pc, op, gas, cost, scope, rData, depth, err) } // CaptureFault implements the EVMLogger interface to trace an execution fault. func (p *Printer) CaptureFault(pc uint64, op vm.OpCode, gas, cost uint64, _ *vm.ScopeContext, depth int, err error) { + data := types.CaptureFaultData{ + Pc: pc, + Op: op, + Gas: gas, + Cost: cost, + Depth: depth, + Err: err, + } + p.CaptureFaultChan <- data fmt.Printf("CaptureFault: pc=%v, op=%v, gas=%v, cost=%v, depth=%v, err=%v\n", pc, op, gas, cost, depth, err) } // CaptureKeccakPreimage is called during the KECCAK256 opcode. -func (p *Printer) CaptureKeccakPreimage(hash common.Hash, data []byte) {} +func (p *Printer) CaptureKeccakPreimage(hash common.Hash, data []byte) { +/* preImageData := types.CaptureKeccakPreimageData{ + Hash: hash, + Data: data, + } + p.CaptureKeccakPreimageChan <- preImageData */ +} // CaptureEnter is called when EVM enters a new scope (via call, create or selfdestruct). func (p *Printer) CaptureEnter(typ vm.OpCode, from common.Address, to common.Address, input []byte, gas uint64, value *big.Int) { + data := types.CaptureEnterData{ + Type: typ, + From: from, + To: to, + Input: input, + Gas: gas, + Value: value, + } + p.CaptureEnterChan <- data fmt.Printf("CaptureEnter: typ=%v, from=%v, to=%v, input=%v, gas=%v, value=%v\n", typ, from, to, input, gas, value) } // CaptureExit is called when EVM exits a scope, even if the scope didn't // execute any code. func (p *Printer) CaptureExit(output []byte, gasUsed uint64, err error) { + data := types.CaptureExitData{ + Output: output, + GasUsed: gasUsed, + Err: err, + } + p.CaptureExitChan <- data fmt.Printf("CaptureExit: output=%v, gasUsed=%v, err=%v\n", output, gasUsed, err) } func (p *Printer) CaptureTxStart(env *vm.EVM, tx *types.Transaction) { + data := types.CaptureTxStartData{ + Env: env, + Tx: tx, + } + p.CaptureTxStartChan <- data fmt.Printf("CaptureTxStart: tx=%v\n", tx) } func (p *Printer) CaptureTxEnd(receipt *types.Receipt) { + data := types.CaptureTxEndData{ + Receipt: receipt, + } + p.CaptureTxEndChan <- data fmt.Printf("CaptureTxEnd: receipt=%v\n", receipt) } func (p *Printer) OnBlockStart(b *types.Block) { + data := types.OnBlockStartData{ + Block: b, + } + p.OnBlockStartChan <- data fmt.Printf("OnBlockStart: b=%v\n", b.NumberU64()) } func (p *Printer) OnBlockEnd(td *big.Int, err error) { + data := types.OnBlockEndData{ + Td: td, + Err: err, + } + p.OnBlockEndChan <- data fmt.Printf("OnBlockEnd: td=%v, err=%v\n", td, err) } func (p *Printer) OnBlockValidationError(block *types.Block, err error) { + data := types.OnBlockValidationErrorData{ + Block: block, + Err: err, + } + p.OnBlockValidationErrorChan <- data fmt.Printf("OnBlockValidationError: b=%v, err=%v\n", block.NumberU64(), err) } func (p *Printer) OnGenesisBlock(b *types.Block) { + data := types.OnGenesisBlockData{ + Block: b, + } + p.OnGenesisBlockChan <- data fmt.Printf("OnGenesisBlock: b=%v\n", b.NumberU64()) } func (p *Printer) OnBalanceChange(a common.Address, prev, new *big.Int) { + data := types.OnBalanceChangeData{ + Address: a, + Prev: prev, + New: new, + } + p.OnBalanceChangeChan <- data fmt.Printf("OnBalanceChange: a=%v, prev=%v, new=%v\n", a, prev, new) } func (p *Printer) OnNonceChange(a common.Address, prev, new uint64) { + data := types.OnNonceChangeData{ + Address: a, + Prev: prev, + New: new, + } + p.OnNonceChangeChan <- data fmt.Printf("OnNonceChange: a=%v, prev=%v, new=%v\n", a, prev, new) } func (p *Printer) OnCodeChange(a common.Address, prevCodeHash common.Hash, prev []byte, codeHash common.Hash, code []byte) { + data := types.OnCodeChangeData{ + Address: a, + PrevCodeHash: prevCodeHash, + Prev: prev, + CodeHash: codeHash, + Code: code, + } + p.OnCodeChangeChan <- data fmt.Printf("OnCodeChange: a=%v, prevCodeHash=%v, prev=%v, codeHash=%v, code=%v\n", a, prevCodeHash, prev, codeHash, code) } func (p *Printer) OnStorageChange(a common.Address, k, prev, new common.Hash) { + data := types.OnStorageChangeData{ + Address: a, + Key: k, + Prev: prev, + New: new, + } + p.OnStorageChangeChan <- data fmt.Printf("OnStorageChange: a=%v, k=%v, prev=%v, new=%v\n", a, k, prev, new) } func (p *Printer) OnLog(l *types.Log) { + data := types.OnLogData{ + Log: l, + } + p.OnLogChan <- data fmt.Printf("OnLog: l=%v\n", l) } func (p *Printer) OnNewAccount(a common.Address) { + data := types.OnNewAccountData{ + Address: a, + } + p.OnNewAccountChan <- data fmt.Printf("OnNewAccount: a=%v\n", a) } func (p *Printer) OnGasConsumed(gas, amount uint64) { + data := types.OnGasConsumedData{ + Gas: gas, + Amount: amount, + } + p.OnGasConsumedChan <- data fmt.Printf("OnGasConsumed: gas=%v, amount=%v\n", gas, amount) } + +// EventLoop receives data from channels, adds them to Trace, +// and sends Trace when the OnBlockEnd event occurs. This function operates +// in a loop and should typically be run in a separate goroutine. +func (p *Printer) EventLoop(bc core.BlockChain) { + for { + select { + case data := <-p.CaptureStartChan: + p.Trace.CaptureStart = append(p.Trace.CaptureStart, data) + case data := <-p.CaptureEndChan: + p.Trace.CaptureEnd = append(p.Trace.CaptureEnd, data) + case data := <-p.CaptureStateChan: + p.Trace.CaptureState = append(p.Trace.CaptureState, data) + case data := <-p.CaptureFaultChan: + p.Trace.CaptureFault = append(p.Trace.CaptureFault, data) + case data := <-p.CaptureKeccakPreimageChan: + p.Trace.CaptureKeccakPreimage = append(p.Trace.CaptureKeccakPreimage, data) + case data := <-p.CaptureEnterChan: + p.Trace.CaptureEnter = append(p.Trace.CaptureEnter, data) + case data := <-p.CaptureExitChan: + p.Trace.CaptureExit = append(p.Trace.CaptureExit, data) + case data := <-p.CaptureTxStartChan: + p.Trace.CaptureTxStart = append(p.Trace.CaptureTxStart, data) + case data := <-p.CaptureTxEndChan: + p.Trace.CaptureTxEnd = append(p.Trace.CaptureTxEnd, data) + case data := <-p.OnBlockStartChan: + p.Trace.OnBlockStart = append(p.Trace.OnBlockStart, data) + case data := <-p.OnBlockEndChan: + p.Trace.OnBlockEnd = append(p.Trace.OnBlockEnd, data) + bc.tracesFeed.send(p.Trace) + p.Trace = types.Trace{} + case data := <-p.OnBlockValidationErrorChan: + p.Trace.OnBlockValidationError = append(p.Trace.OnBlockValidationError, data) + case data := <-p.OnGenesisBlockChan: + p.Trace.OnGenesisBlock = append(p.Trace.OnGenesisBlock, data) + case data := <-p.OnBalanceChangeChan: + p.Trace.OnBalanceChange = append(p.Trace.OnBalanceChange, data) + case data := <-p.OnNonceChangeChan: + p.Trace.OnNonceChange = append(p.Trace.OnNonceChange, data) + case data := <-p.OnCodeChangeChan: + p.Trace.OnCodeChange = append(p.Trace.OnCodeChange, data) + case data := <-p.OnStorageChangeChan: + p.Trace.OnStorageChange = append(p.Trace.OnStorageChange, data) + case data := <-p.OnLogChan: + p.Trace.OnLog = append(p.Trace.OnLog, data) + case data := <-p.OnNewAccountChan: + p.Trace.OnNewAccount = append(p.Trace.OnNewAccount, data) + case data := <-p.OnGasConsumedChan: + p.Trace.OnGasConsumed = append(p.Trace.OnGasConsumed, data) + case <-bc.quit: + return + + } + } +}