// Copyright 2017 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 eth
import (
"bufio"
"bytes"
"context"
"errors"
"fmt"
"io/ioutil"
"os"
"runtime"
"sync"
"time"
"github.com/ava-labs/go-ethereum/common"
"github.com/ava-labs/go-ethereum/common/hexutil"
"github.com/ava-labs/go-ethereum/core"
"github.com/ava-labs/go-ethereum/core/rawdb"
"github.com/ava-labs/go-ethereum/core/state"
"github.com/ava-labs/go-ethereum/core/types"
"github.com/ava-labs/go-ethereum/core/vm"
"github.com/ava-labs/go-ethereum/eth/tracers"
"github.com/ava-labs/coreth/internal/ethapi"
"github.com/ava-labs/go-ethereum/log"
"github.com/ava-labs/go-ethereum/rlp"
"github.com/ava-labs/go-ethereum/rpc"
"github.com/ava-labs/go-ethereum/trie"
)
const (
// defaultTraceTimeout is the amount of time a single transaction can execute
// by default before being forcefully aborted.
defaultTraceTimeout = 5 * time.Second
// defaultTraceReexec is the number of blocks the tracer is willing to go back
// and reexecute to produce missing historical state necessary to run a specific
// trace.
defaultTraceReexec = uint64(128)
)
// TraceConfig holds extra parameters to trace functions.
type TraceConfig struct {
*vm.LogConfig
Tracer *string
Timeout *string
Reexec *uint64
}
// StdTraceConfig holds extra parameters to standard-json trace functions.
type StdTraceConfig struct {
*vm.LogConfig
Reexec *uint64
TxHash common.Hash
}
// txTraceResult is the result of a single transaction trace.
type txTraceResult struct {
Result interface{} `json:"result,omitempty"` // Trace results produced by the tracer
Error string `json:"error,omitempty"` // Trace failure produced by the tracer
}
// blockTraceTask represents a single block trace task when an entire chain is
// being traced.
type blockTraceTask struct {
statedb *state.StateDB // Intermediate state prepped for tracing
block *types.Block // Block to trace the transactions from
rootref common.Hash // Trie root reference held for this task
results []*txTraceResult // Trace results procudes by the task
}
// blockTraceResult represets the results of tracing a single block when an entire
// chain is being traced.
type blockTraceResult struct {
Block hexutil.Uint64 `json:"block"` // Block number corresponding to this trace
Hash common.Hash `json:"hash"` // Block hash corresponding to this trace
Traces []*txTraceResult `json:"traces"` // Trace results produced by the task
}
// txTraceTask represents a single transaction trace task when an entire block
// is being traced.
type txTraceTask struct {
statedb *state.StateDB // Intermediate state prepped for tracing
index int // Transaction offset in the block
}
// TraceChain returns the structured logs created during the execution of EVM
// between two blocks (excluding start) and returns them as a JSON object.
func (api *PrivateDebugAPI) TraceChain(ctx context.Context, start, end rpc.BlockNumber, config *TraceConfig) (*rpc.Subscription, error) {
// Fetch the block interval that we want to trace
var from, to *types.Block
switch start {
case rpc.PendingBlockNumber:
from = api.eth.miner.PendingBlock()
case rpc.LatestBlockNumber:
from = api.eth.blockchain.CurrentBlock()
default:
from = api.eth.blockchain.GetBlockByNumber(uint64(start))
}
switch end {
case rpc.PendingBlockNumber:
to = api.eth.miner.PendingBlock()
case rpc.LatestBlockNumber:
to = api.eth.blockchain.CurrentBlock()
default:
to = api.eth.blockchain.GetBlockByNumber(uint64(end))
}
// Trace the chain if we've found all our blocks
if from == nil {
return nil, fmt.Errorf("starting block #%d not found", start)
}
if to == nil {
return nil, fmt.Errorf("end block #%d not found", end)
}
if from.Number().Cmp(to.Number()) >= 0 {
return nil, fmt.Errorf("end block (#%d) needs to come after start block (#%d)", end, start)
}
return api.traceChain(ctx, from, to, config)
}
// traceChain configures a new tracer according to the provided configuration, and
// executes all the transactions contained within. The return value will be one item
// per transaction, dependent on the requested tracer.
func (api *PrivateDebugAPI) traceChain(ctx context.Context, start, end *types.Block, config *TraceConfig) (*rpc.Subscription, error) {
// Tracing a chain is a **long** operation, only do with subscriptions
notifier, supported := rpc.NotifierFromContext(ctx)
if !supported {
return &rpc.Subscription{}, rpc.ErrNotificationsUnsupported
}
sub := notifier.CreateSubscription()
// Ensure we have a valid starting state before doing any work
origin := start.NumberU64()
database := state.NewDatabaseWithCache(api.eth.ChainDb(), 16) // Chain tracing will probably start at genesis
if number := start.NumberU64(); number > 0 {
start = api.eth.blockchain.GetBlock(start.ParentHash(), start.NumberU64()-1)
if start == nil {
return nil, fmt.Errorf("parent block #%d not found", number-1)
}
}
statedb, err := state.New(start.Root(), database)
if err != nil {
// If the starting state is missing, allow some number of blocks to be reexecuted
reexec := defaultTraceReexec
if config != nil && config.Reexec != nil {
reexec = *config.Reexec
}
// Find the most recent block that has the state available
for i := uint64(0); i < reexec; i++ {
start = api.eth.blockchain.GetBlock(start.ParentHash(),