mirror of
https://github.com/ethereum/go-ethereum.git
synced 2026-08-13 07:23:46 +00:00
Merge pull request #572 from cffls/qa
Add state sync transaction to debug_trace functions.
This commit is contained in:
commit
cabd736df9
5 changed files with 326 additions and 53 deletions
|
|
@ -30,22 +30,22 @@ func (c ChainContext) GetHeader(hash common.Hash, number uint64) *types.Header {
|
||||||
}
|
}
|
||||||
|
|
||||||
// callmsg implements core.Message to allow passing it as a transaction simulator.
|
// callmsg implements core.Message to allow passing it as a transaction simulator.
|
||||||
type callmsg struct {
|
type Callmsg struct {
|
||||||
ethereum.CallMsg
|
ethereum.CallMsg
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m callmsg) From() common.Address { return m.CallMsg.From }
|
func (m Callmsg) From() common.Address { return m.CallMsg.From }
|
||||||
func (m callmsg) Nonce() uint64 { return 0 }
|
func (m Callmsg) Nonce() uint64 { return 0 }
|
||||||
func (m callmsg) CheckNonce() bool { return false }
|
func (m Callmsg) CheckNonce() bool { return false }
|
||||||
func (m callmsg) To() *common.Address { return m.CallMsg.To }
|
func (m Callmsg) To() *common.Address { return m.CallMsg.To }
|
||||||
func (m callmsg) GasPrice() *big.Int { return m.CallMsg.GasPrice }
|
func (m Callmsg) GasPrice() *big.Int { return m.CallMsg.GasPrice }
|
||||||
func (m callmsg) Gas() uint64 { return m.CallMsg.Gas }
|
func (m Callmsg) Gas() uint64 { return m.CallMsg.Gas }
|
||||||
func (m callmsg) Value() *big.Int { return m.CallMsg.Value }
|
func (m Callmsg) Value() *big.Int { return m.CallMsg.Value }
|
||||||
func (m callmsg) Data() []byte { return m.CallMsg.Data }
|
func (m Callmsg) Data() []byte { return m.CallMsg.Data }
|
||||||
|
|
||||||
// get system message
|
// get system message
|
||||||
func GetSystemMessage(toAddress common.Address, data []byte) callmsg {
|
func GetSystemMessage(toAddress common.Address, data []byte) Callmsg {
|
||||||
return callmsg{
|
return Callmsg{
|
||||||
ethereum.CallMsg{
|
ethereum.CallMsg{
|
||||||
From: systemAddress,
|
From: systemAddress,
|
||||||
Gas: math.MaxUint64 / 2,
|
Gas: math.MaxUint64 / 2,
|
||||||
|
|
@ -59,7 +59,7 @@ func GetSystemMessage(toAddress common.Address, data []byte) callmsg {
|
||||||
|
|
||||||
// apply message
|
// apply message
|
||||||
func ApplyMessage(
|
func ApplyMessage(
|
||||||
msg callmsg,
|
msg Callmsg,
|
||||||
state *state.StateDB,
|
state *state.StateDB,
|
||||||
header *types.Header,
|
header *types.Header,
|
||||||
chainConfig *params.ChainConfig,
|
chainConfig *params.ChainConfig,
|
||||||
|
|
@ -91,3 +91,28 @@ func ApplyMessage(
|
||||||
|
|
||||||
return gasUsed, nil
|
return gasUsed, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func ApplyBorMessage(vmenv vm.EVM, msg Callmsg) (*core.ExecutionResult, error) {
|
||||||
|
initialGas := msg.Gas()
|
||||||
|
|
||||||
|
// Apply the transaction to the current state (included in the env)
|
||||||
|
ret, gasLeft, err := vmenv.Call(
|
||||||
|
vm.AccountRef(msg.From()),
|
||||||
|
*msg.To(),
|
||||||
|
msg.Data(),
|
||||||
|
msg.Gas(),
|
||||||
|
msg.Value(),
|
||||||
|
)
|
||||||
|
// Update the state with pending changes
|
||||||
|
if err != nil {
|
||||||
|
vmenv.StateDB.Finalise(true)
|
||||||
|
}
|
||||||
|
|
||||||
|
gasUsed := initialGas - gasLeft
|
||||||
|
|
||||||
|
return &core.ExecutionResult{
|
||||||
|
UsedGas: gasUsed,
|
||||||
|
Err: err,
|
||||||
|
ReturnData: ret,
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -74,6 +74,8 @@ type StateDB interface {
|
||||||
AddPreimage(common.Hash, []byte)
|
AddPreimage(common.Hash, []byte)
|
||||||
|
|
||||||
ForEachStorage(common.Address, func(common.Hash, common.Hash) bool) error
|
ForEachStorage(common.Address, func(common.Hash, common.Hash) bool) error
|
||||||
|
|
||||||
|
Finalise(bool)
|
||||||
}
|
}
|
||||||
|
|
||||||
// CallContext provides a basic interface for the EVM calling conventions. The EVM
|
// CallContext provides a basic interface for the EVM calling conventions. The EVM
|
||||||
|
|
|
||||||
|
|
@ -28,9 +28,11 @@ import (
|
||||||
"sync"
|
"sync"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"github.com/ethereum/go-ethereum"
|
||||||
"github.com/ethereum/go-ethereum/common"
|
"github.com/ethereum/go-ethereum/common"
|
||||||
"github.com/ethereum/go-ethereum/common/hexutil"
|
"github.com/ethereum/go-ethereum/common/hexutil"
|
||||||
"github.com/ethereum/go-ethereum/consensus"
|
"github.com/ethereum/go-ethereum/consensus"
|
||||||
|
"github.com/ethereum/go-ethereum/consensus/bor/statefull"
|
||||||
"github.com/ethereum/go-ethereum/core"
|
"github.com/ethereum/go-ethereum/core"
|
||||||
"github.com/ethereum/go-ethereum/core/rawdb"
|
"github.com/ethereum/go-ethereum/core/rawdb"
|
||||||
"github.com/ethereum/go-ethereum/core/state"
|
"github.com/ethereum/go-ethereum/core/state"
|
||||||
|
|
@ -63,6 +65,8 @@ const (
|
||||||
defaultTracechainMemLimit = common.StorageSize(500 * 1024 * 1024)
|
defaultTracechainMemLimit = common.StorageSize(500 * 1024 * 1024)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
var defaultBorTraceEnabled = newBoolPtr(false)
|
||||||
|
|
||||||
// Backend interface provides the common API services (that are provided by
|
// Backend interface provides the common API services (that are provided by
|
||||||
// both full and light clients) with access to necessary functions.
|
// both full and light clients) with access to necessary functions.
|
||||||
type Backend interface {
|
type Backend interface {
|
||||||
|
|
@ -80,6 +84,9 @@ type Backend interface {
|
||||||
// so this method should be called with the parent.
|
// so this method should be called with the parent.
|
||||||
StateAtBlock(ctx context.Context, block *types.Block, reexec uint64, base *state.StateDB, checkLive, preferDisk bool) (*state.StateDB, error)
|
StateAtBlock(ctx context.Context, block *types.Block, reexec uint64, base *state.StateDB, checkLive, preferDisk bool) (*state.StateDB, error)
|
||||||
StateAtTransaction(ctx context.Context, block *types.Block, txIndex int, reexec uint64) (core.Message, vm.BlockContext, *state.StateDB, error)
|
StateAtTransaction(ctx context.Context, block *types.Block, txIndex int, reexec uint64) (core.Message, vm.BlockContext, *state.StateDB, error)
|
||||||
|
|
||||||
|
// Bor related APIs
|
||||||
|
GetBorBlockTransactionWithBlockHash(ctx context.Context, txHash common.Hash, blockHash common.Hash) (*types.Transaction, common.Hash, uint64, uint64, error)
|
||||||
}
|
}
|
||||||
|
|
||||||
// API is the collection of tracing APIs exposed over the private debugging endpoint.
|
// API is the collection of tracing APIs exposed over the private debugging endpoint.
|
||||||
|
|
@ -164,12 +171,33 @@ func (api *API) blockByNumberAndHash(ctx context.Context, number rpc.BlockNumber
|
||||||
return api.blockByHash(ctx, hash)
|
return api.blockByHash(ctx, hash)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// returns block transactions along with state-sync transaction if present
|
||||||
|
func (api *API) getAllBlockTransactions(ctx context.Context, block *types.Block) (types.Transactions, bool) {
|
||||||
|
txs := block.Transactions()
|
||||||
|
|
||||||
|
stateSyncPresent := false
|
||||||
|
|
||||||
|
borReceipt := rawdb.ReadBorReceipt(api.backend.ChainDb(), block.Hash(), block.NumberU64())
|
||||||
|
if borReceipt != nil {
|
||||||
|
txHash := types.GetDerivedBorTxHash(types.BorReceiptKey(block.Number().Uint64(), block.Hash()))
|
||||||
|
if txHash != (common.Hash{}) {
|
||||||
|
borTx, _, _, _, _ := api.backend.GetBorBlockTransactionWithBlockHash(ctx, txHash, block.Hash())
|
||||||
|
txs = append(txs, borTx)
|
||||||
|
stateSyncPresent = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return txs, stateSyncPresent
|
||||||
|
}
|
||||||
|
|
||||||
// TraceConfig holds extra parameters to trace functions.
|
// TraceConfig holds extra parameters to trace functions.
|
||||||
type TraceConfig struct {
|
type TraceConfig struct {
|
||||||
*logger.Config
|
*logger.Config
|
||||||
Tracer *string
|
Tracer *string
|
||||||
Timeout *string
|
Timeout *string
|
||||||
Reexec *uint64
|
Reexec *uint64
|
||||||
|
BorTraceEnabled *bool
|
||||||
|
BorTx *bool
|
||||||
}
|
}
|
||||||
|
|
||||||
// TraceCallConfig is the config for traceCall API. It holds one more
|
// TraceCallConfig is the config for traceCall API. It holds one more
|
||||||
|
|
@ -185,8 +213,9 @@ type TraceCallConfig struct {
|
||||||
// StdTraceConfig holds extra parameters to standard-json trace functions.
|
// StdTraceConfig holds extra parameters to standard-json trace functions.
|
||||||
type StdTraceConfig struct {
|
type StdTraceConfig struct {
|
||||||
logger.Config
|
logger.Config
|
||||||
Reexec *uint64
|
Reexec *uint64
|
||||||
TxHash common.Hash
|
TxHash common.Hash
|
||||||
|
BorTraceEnabled *bool
|
||||||
}
|
}
|
||||||
|
|
||||||
// txTraceResult is the result of a single transaction trace.
|
// txTraceResult is the result of a single transaction trace.
|
||||||
|
|
@ -240,6 +269,16 @@ func (api *API) TraceChain(ctx context.Context, start, end rpc.BlockNumber, conf
|
||||||
// executes all the transactions contained within. The return value will be one item
|
// executes all the transactions contained within. The return value will be one item
|
||||||
// per transaction, dependent on the requested tracer.
|
// per transaction, dependent on the requested tracer.
|
||||||
func (api *API) traceChain(ctx context.Context, start, end *types.Block, config *TraceConfig) (*rpc.Subscription, error) {
|
func (api *API) traceChain(ctx context.Context, start, end *types.Block, config *TraceConfig) (*rpc.Subscription, error) {
|
||||||
|
if config == nil {
|
||||||
|
config = &TraceConfig{
|
||||||
|
BorTraceEnabled: defaultBorTraceEnabled,
|
||||||
|
BorTx: newBoolPtr(false),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if config.BorTraceEnabled == nil {
|
||||||
|
config.BorTraceEnabled = defaultBorTraceEnabled
|
||||||
|
}
|
||||||
// Tracing a chain is a **long** operation, only do with subscriptions
|
// Tracing a chain is a **long** operation, only do with subscriptions
|
||||||
notifier, supported := rpc.NotifierFromContext(ctx)
|
notifier, supported := rpc.NotifierFromContext(ctx)
|
||||||
if !supported {
|
if !supported {
|
||||||
|
|
@ -274,19 +313,39 @@ func (api *API) traceChain(ctx context.Context, start, end *types.Block, config
|
||||||
signer := types.MakeSigner(api.backend.ChainConfig(), task.block.Number())
|
signer := types.MakeSigner(api.backend.ChainConfig(), task.block.Number())
|
||||||
blockCtx := core.NewEVMBlockContext(task.block.Header(), api.chainContext(localctx), nil)
|
blockCtx := core.NewEVMBlockContext(task.block.Header(), api.chainContext(localctx), nil)
|
||||||
// Trace all the transactions contained within
|
// Trace all the transactions contained within
|
||||||
for i, tx := range task.block.Transactions() {
|
txs, stateSyncPresent := api.getAllBlockTransactions(ctx, task.block)
|
||||||
|
if !*config.BorTraceEnabled && stateSyncPresent {
|
||||||
|
txs = txs[:len(txs)-1]
|
||||||
|
stateSyncPresent = false
|
||||||
|
}
|
||||||
|
|
||||||
|
for i, tx := range txs {
|
||||||
msg, _ := tx.AsMessage(signer, task.block.BaseFee())
|
msg, _ := tx.AsMessage(signer, task.block.BaseFee())
|
||||||
txctx := &Context{
|
txctx := &Context{
|
||||||
BlockHash: task.block.Hash(),
|
BlockHash: task.block.Hash(),
|
||||||
TxIndex: i,
|
TxIndex: i,
|
||||||
TxHash: tx.Hash(),
|
TxHash: tx.Hash(),
|
||||||
}
|
}
|
||||||
res, err := api.traceTx(localctx, msg, txctx, blockCtx, task.statedb, config)
|
|
||||||
|
var res interface{}
|
||||||
|
|
||||||
|
var err error
|
||||||
|
|
||||||
|
if stateSyncPresent && i == len(txs)-1 {
|
||||||
|
if *config.BorTraceEnabled {
|
||||||
|
config.BorTx = newBoolPtr(true)
|
||||||
|
res, err = api.traceTx(localctx, msg, txctx, blockCtx, task.statedb, config)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
res, err = api.traceTx(localctx, msg, txctx, blockCtx, task.statedb, config)
|
||||||
|
}
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
task.results[i] = &txTraceResult{Error: err.Error()}
|
task.results[i] = &txTraceResult{Error: err.Error()}
|
||||||
log.Warn("Tracing failed", "hash", tx.Hash(), "block", task.block.NumberU64(), "err", err)
|
log.Warn("Tracing failed", "hash", tx.Hash(), "block", task.block.NumberU64(), "err", err)
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
|
|
||||||
// Only delete empty objects if EIP158/161 (a.k.a Spurious Dragon) is in effect
|
// Only delete empty objects if EIP158/161 (a.k.a Spurious Dragon) is in effect
|
||||||
task.statedb.Finalise(api.backend.ChainConfig().IsEIP158(task.block.Number()))
|
task.statedb.Finalise(api.backend.ChainConfig().IsEIP158(task.block.Number()))
|
||||||
task.results[i] = &txTraceResult{Result: res}
|
task.results[i] = &txTraceResult{Result: res}
|
||||||
|
|
@ -430,6 +489,11 @@ func (api *API) traceChain(ctx context.Context, start, end *types.Block, config
|
||||||
return sub, nil
|
return sub, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func newBoolPtr(bb bool) *bool {
|
||||||
|
b := bb
|
||||||
|
return &b
|
||||||
|
}
|
||||||
|
|
||||||
// TraceBlockByNumber returns the structured logs created during the execution of
|
// TraceBlockByNumber returns the structured logs created during the execution of
|
||||||
// EVM and returns them as a JSON object.
|
// EVM and returns them as a JSON object.
|
||||||
func (api *API) TraceBlockByNumber(ctx context.Context, number rpc.BlockNumber, config *TraceConfig) ([]*txTraceResult, error) {
|
func (api *API) TraceBlockByNumber(ctx context.Context, number rpc.BlockNumber, config *TraceConfig) ([]*txTraceResult, error) {
|
||||||
|
|
@ -492,9 +556,35 @@ func (api *API) StandardTraceBlockToFile(ctx context.Context, hash common.Hash,
|
||||||
return api.standardTraceBlockToFile(ctx, block, config)
|
return api.standardTraceBlockToFile(ctx, block, config)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func prepareCallMessage(msg core.Message) statefull.Callmsg {
|
||||||
|
return statefull.Callmsg{
|
||||||
|
CallMsg: ethereum.CallMsg{
|
||||||
|
From: msg.From(),
|
||||||
|
To: msg.To(),
|
||||||
|
Gas: msg.Gas(),
|
||||||
|
GasPrice: msg.GasPrice(),
|
||||||
|
GasFeeCap: msg.GasFeeCap(),
|
||||||
|
GasTipCap: msg.GasTipCap(),
|
||||||
|
Value: msg.Value(),
|
||||||
|
Data: msg.Data(),
|
||||||
|
AccessList: msg.AccessList(),
|
||||||
|
}}
|
||||||
|
}
|
||||||
|
|
||||||
// IntermediateRoots executes a block (bad- or canon- or side-), and returns a list
|
// IntermediateRoots executes a block (bad- or canon- or side-), and returns a list
|
||||||
// of intermediate roots: the stateroot after each transaction.
|
// of intermediate roots: the stateroot after each transaction.
|
||||||
func (api *API) IntermediateRoots(ctx context.Context, hash common.Hash, config *TraceConfig) ([]common.Hash, error) {
|
func (api *API) IntermediateRoots(ctx context.Context, hash common.Hash, config *TraceConfig) ([]common.Hash, error) {
|
||||||
|
if config == nil {
|
||||||
|
config = &TraceConfig{
|
||||||
|
BorTraceEnabled: defaultBorTraceEnabled,
|
||||||
|
BorTx: newBoolPtr(false),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if config.BorTraceEnabled == nil {
|
||||||
|
config.BorTraceEnabled = defaultBorTraceEnabled
|
||||||
|
}
|
||||||
|
|
||||||
block, _ := api.blockByHash(ctx, hash)
|
block, _ := api.blockByHash(ctx, hash)
|
||||||
if block == nil {
|
if block == nil {
|
||||||
// Check in the bad blocks
|
// Check in the bad blocks
|
||||||
|
|
@ -525,23 +615,47 @@ func (api *API) IntermediateRoots(ctx context.Context, hash common.Hash, config
|
||||||
vmctx = core.NewEVMBlockContext(block.Header(), api.chainContext(ctx), nil)
|
vmctx = core.NewEVMBlockContext(block.Header(), api.chainContext(ctx), nil)
|
||||||
deleteEmptyObjects = chainConfig.IsEIP158(block.Number())
|
deleteEmptyObjects = chainConfig.IsEIP158(block.Number())
|
||||||
)
|
)
|
||||||
for i, tx := range block.Transactions() {
|
|
||||||
|
txs, stateSyncPresent := api.getAllBlockTransactions(ctx, block)
|
||||||
|
for i, tx := range txs {
|
||||||
var (
|
var (
|
||||||
msg, _ = tx.AsMessage(signer, block.BaseFee())
|
msg, _ = tx.AsMessage(signer, block.BaseFee())
|
||||||
txContext = core.NewEVMTxContext(msg)
|
txContext = core.NewEVMTxContext(msg)
|
||||||
vmenv = vm.NewEVM(vmctx, txContext, statedb, chainConfig, vm.Config{})
|
vmenv = vm.NewEVM(vmctx, txContext, statedb, chainConfig, vm.Config{})
|
||||||
)
|
)
|
||||||
statedb.Prepare(tx.Hash(), i)
|
statedb.Prepare(tx.Hash(), i)
|
||||||
if _, err := core.ApplyMessage(vmenv, msg, new(core.GasPool).AddGas(msg.Gas())); err != nil {
|
//nolint: nestif
|
||||||
log.Warn("Tracing intermediate roots did not complete", "txindex", i, "txhash", tx.Hash(), "err", err)
|
if stateSyncPresent && i == len(txs)-1 {
|
||||||
// We intentionally don't return the error here: if we do, then the RPC server will not
|
if *config.BorTraceEnabled {
|
||||||
// return the roots. Most likely, the caller already knows that a certain transaction fails to
|
callmsg := prepareCallMessage(msg)
|
||||||
// be included, but still want the intermediate roots that led to that point.
|
|
||||||
// It may happen the tx_N causes an erroneous state, which in turn causes tx_N+M to not be
|
if _, err := statefull.ApplyMessage(callmsg, statedb, block.Header(), api.backend.ChainConfig(), api.chainContext(ctx)); err != nil {
|
||||||
// executable.
|
log.Warn("Tracing intermediate roots did not complete", "txindex", i, "txhash", tx.Hash(), "err", err)
|
||||||
// N.B: This should never happen while tracing canon blocks, only when tracing bad blocks.
|
// We intentionally don't return the error here: if we do, then the RPC server will not
|
||||||
return roots, nil
|
// return the roots. Most likely, the caller already knows that a certain transaction fails to
|
||||||
|
// be included, but still want the intermediate roots that led to that point.
|
||||||
|
// It may happen the tx_N causes an erroneous state, which in turn causes tx_N+M to not be
|
||||||
|
// executable.
|
||||||
|
// N.B: This should never happen while tracing canon blocks, only when tracing bad blocks.
|
||||||
|
return roots, nil
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if _, err := core.ApplyMessage(vmenv, msg, new(core.GasPool).AddGas(msg.Gas())); err != nil {
|
||||||
|
log.Warn("Tracing intermediate roots did not complete", "txindex", i, "txhash", tx.Hash(), "err", err)
|
||||||
|
// We intentionally don't return the error here: if we do, then the RPC server will not
|
||||||
|
// return the roots. Most likely, the caller already knows that a certain transaction fails to
|
||||||
|
// be included, but still want the intermediate roots that led to that point.
|
||||||
|
// It may happen the tx_N causes an erroneous state, which in turn causes tx_N+M to not be
|
||||||
|
// executable.
|
||||||
|
// N.B: This should never happen while tracing canon blocks, only when tracing bad blocks.
|
||||||
|
return roots, nil
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// calling IntermediateRoot will internally call Finalize on the state
|
// calling IntermediateRoot will internally call Finalize on the state
|
||||||
// so any modifications are written to the trie
|
// so any modifications are written to the trie
|
||||||
roots = append(roots, statedb.IntermediateRoot(deleteEmptyObjects))
|
roots = append(roots, statedb.IntermediateRoot(deleteEmptyObjects))
|
||||||
|
|
@ -564,6 +678,18 @@ func (api *API) StandardTraceBadBlockToFile(ctx context.Context, hash common.Has
|
||||||
// executes all the transactions contained within. The return value will be one item
|
// executes all the transactions contained within. The return value will be one item
|
||||||
// per transaction, dependent on the requestd tracer.
|
// per transaction, dependent on the requestd tracer.
|
||||||
func (api *API) traceBlock(ctx context.Context, block *types.Block, config *TraceConfig) ([]*txTraceResult, error) {
|
func (api *API) traceBlock(ctx context.Context, block *types.Block, config *TraceConfig) ([]*txTraceResult, error) {
|
||||||
|
|
||||||
|
if config == nil {
|
||||||
|
config = &TraceConfig{
|
||||||
|
BorTraceEnabled: defaultBorTraceEnabled,
|
||||||
|
BorTx: newBoolPtr(false),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if config.BorTraceEnabled == nil {
|
||||||
|
config.BorTraceEnabled = defaultBorTraceEnabled
|
||||||
|
}
|
||||||
|
|
||||||
if block.NumberU64() == 0 {
|
if block.NumberU64() == 0 {
|
||||||
return nil, errors.New("genesis is not traceable")
|
return nil, errors.New("genesis is not traceable")
|
||||||
}
|
}
|
||||||
|
|
@ -581,9 +707,9 @@ func (api *API) traceBlock(ctx context.Context, block *types.Block, config *Trac
|
||||||
}
|
}
|
||||||
// Execute all the transaction contained within the block concurrently
|
// Execute all the transaction contained within the block concurrently
|
||||||
var (
|
var (
|
||||||
signer = types.MakeSigner(api.backend.ChainConfig(), block.Number())
|
signer = types.MakeSigner(api.backend.ChainConfig(), block.Number())
|
||||||
txs = block.Transactions()
|
txs, stateSyncPresent = api.getAllBlockTransactions(ctx, block)
|
||||||
results = make([]*txTraceResult, len(txs))
|
results = make([]*txTraceResult, len(txs))
|
||||||
|
|
||||||
pend = new(sync.WaitGroup)
|
pend = new(sync.WaitGroup)
|
||||||
jobs = make(chan *txTraceTask, len(txs))
|
jobs = make(chan *txTraceTask, len(txs))
|
||||||
|
|
@ -606,7 +732,21 @@ func (api *API) traceBlock(ctx context.Context, block *types.Block, config *Trac
|
||||||
TxIndex: task.index,
|
TxIndex: task.index,
|
||||||
TxHash: txs[task.index].Hash(),
|
TxHash: txs[task.index].Hash(),
|
||||||
}
|
}
|
||||||
res, err := api.traceTx(ctx, msg, txctx, blockCtx, task.statedb, config)
|
|
||||||
|
var res interface{}
|
||||||
|
|
||||||
|
var err error
|
||||||
|
|
||||||
|
if stateSyncPresent && task.index == len(txs)-1 {
|
||||||
|
if *config.BorTraceEnabled {
|
||||||
|
config.BorTx = newBoolPtr(true)
|
||||||
|
res, err = api.traceTx(ctx, msg, txctx, blockCtx, task.statedb, config)
|
||||||
|
} else {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
res, err = api.traceTx(ctx, msg, txctx, blockCtx, task.statedb, config)
|
||||||
|
}
|
||||||
if err != nil {
|
if err != nil {
|
||||||
results[task.index] = &txTraceResult{Error: err.Error()}
|
results[task.index] = &txTraceResult{Error: err.Error()}
|
||||||
continue
|
continue
|
||||||
|
|
@ -625,11 +765,26 @@ func (api *API) traceBlock(ctx context.Context, block *types.Block, config *Trac
|
||||||
// Generate the next state snapshot fast without tracing
|
// Generate the next state snapshot fast without tracing
|
||||||
msg, _ := tx.AsMessage(signer, block.BaseFee())
|
msg, _ := tx.AsMessage(signer, block.BaseFee())
|
||||||
statedb.Prepare(tx.Hash(), i)
|
statedb.Prepare(tx.Hash(), i)
|
||||||
|
|
||||||
vmenv := vm.NewEVM(blockCtx, core.NewEVMTxContext(msg), statedb, api.backend.ChainConfig(), vm.Config{})
|
vmenv := vm.NewEVM(blockCtx, core.NewEVMTxContext(msg), statedb, api.backend.ChainConfig(), vm.Config{})
|
||||||
if _, err := core.ApplyMessage(vmenv, msg, new(core.GasPool).AddGas(msg.Gas())); err != nil {
|
//nolint: nestif
|
||||||
failed = err
|
if stateSyncPresent && i == len(txs)-1 {
|
||||||
break
|
if *config.BorTraceEnabled {
|
||||||
|
callmsg := prepareCallMessage(msg)
|
||||||
|
if _, err := statefull.ApplyBorMessage(*vmenv, callmsg); err != nil {
|
||||||
|
failed = err
|
||||||
|
break
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if _, err := core.ApplyMessage(vmenv, msg, new(core.GasPool).AddGas(msg.Gas())); err != nil {
|
||||||
|
failed = err
|
||||||
|
break
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Finalize the state so any modifications are written to the trie
|
// Finalize the state so any modifications are written to the trie
|
||||||
// Only delete empty objects if EIP158/161 (a.k.a Spurious Dragon) is in effect
|
// Only delete empty objects if EIP158/161 (a.k.a Spurious Dragon) is in effect
|
||||||
statedb.Finalise(vmenv.ChainConfig().IsEIP158(block.Number()))
|
statedb.Finalise(vmenv.ChainConfig().IsEIP158(block.Number()))
|
||||||
|
|
@ -641,16 +796,30 @@ func (api *API) traceBlock(ctx context.Context, block *types.Block, config *Trac
|
||||||
if failed != nil {
|
if failed != nil {
|
||||||
return nil, failed
|
return nil, failed
|
||||||
}
|
}
|
||||||
return results, nil
|
|
||||||
|
if !*config.BorTraceEnabled && stateSyncPresent {
|
||||||
|
return results[:len(results)-1], nil
|
||||||
|
} else {
|
||||||
|
return results, nil
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// standardTraceBlockToFile configures a new tracer which uses standard JSON output,
|
// standardTraceBlockToFile configures a new tracer which uses standard JSON output,
|
||||||
// and traces either a full block or an individual transaction. The return value will
|
// and traces either a full block or an individual transaction. The return value will
|
||||||
// be one filename per transaction traced.
|
// be one filename per transaction traced.
|
||||||
func (api *API) standardTraceBlockToFile(ctx context.Context, block *types.Block, config *StdTraceConfig) ([]string, error) {
|
func (api *API) standardTraceBlockToFile(ctx context.Context, block *types.Block, config *StdTraceConfig) ([]string, error) {
|
||||||
|
if config == nil {
|
||||||
|
config = &StdTraceConfig{
|
||||||
|
BorTraceEnabled: defaultBorTraceEnabled,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if config.BorTraceEnabled == nil {
|
||||||
|
config.BorTraceEnabled = defaultBorTraceEnabled
|
||||||
|
}
|
||||||
// If we're tracing a single transaction, make sure it's present
|
// If we're tracing a single transaction, make sure it's present
|
||||||
if config != nil && config.TxHash != (common.Hash{}) {
|
if config != nil && config.TxHash != (common.Hash{}) {
|
||||||
if !containsTx(block, config.TxHash) {
|
if !api.containsTx(ctx, block, config.TxHash) {
|
||||||
return nil, fmt.Errorf("transaction %#x not found in block", config.TxHash)
|
return nil, fmt.Errorf("transaction %#x not found in block", config.TxHash)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -705,7 +874,14 @@ func (api *API) standardTraceBlockToFile(ctx context.Context, block *types.Block
|
||||||
canon = false
|
canon = false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
for i, tx := range block.Transactions() {
|
|
||||||
|
txs, stateSyncPresent := api.getAllBlockTransactions(ctx, block)
|
||||||
|
if !*config.BorTraceEnabled && stateSyncPresent {
|
||||||
|
txs = txs[:len(txs)-1]
|
||||||
|
stateSyncPresent = false
|
||||||
|
}
|
||||||
|
|
||||||
|
for i, tx := range txs {
|
||||||
// Prepare the trasaction for un-traced execution
|
// Prepare the trasaction for un-traced execution
|
||||||
var (
|
var (
|
||||||
msg, _ = tx.AsMessage(signer, block.BaseFee())
|
msg, _ = tx.AsMessage(signer, block.BaseFee())
|
||||||
|
|
@ -739,10 +915,23 @@ func (api *API) standardTraceBlockToFile(ctx context.Context, block *types.Block
|
||||||
// Execute the transaction and flush any traces to disk
|
// Execute the transaction and flush any traces to disk
|
||||||
vmenv := vm.NewEVM(vmctx, txContext, statedb, chainConfig, vmConf)
|
vmenv := vm.NewEVM(vmctx, txContext, statedb, chainConfig, vmConf)
|
||||||
statedb.Prepare(tx.Hash(), i)
|
statedb.Prepare(tx.Hash(), i)
|
||||||
_, err = core.ApplyMessage(vmenv, msg, new(core.GasPool).AddGas(msg.Gas()))
|
//nolint: nestif
|
||||||
if writer != nil {
|
if stateSyncPresent && i == len(txs)-1 {
|
||||||
writer.Flush()
|
if *config.BorTraceEnabled {
|
||||||
|
callmsg := prepareCallMessage(msg)
|
||||||
|
_, err = statefull.ApplyBorMessage(*vmenv, callmsg)
|
||||||
|
|
||||||
|
if writer != nil {
|
||||||
|
writer.Flush()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
_, err = core.ApplyMessage(vmenv, msg, new(core.GasPool).AddGas(msg.Gas()))
|
||||||
|
if writer != nil {
|
||||||
|
writer.Flush()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if dump != nil {
|
if dump != nil {
|
||||||
dump.Close()
|
dump.Close()
|
||||||
log.Info("Wrote standard trace", "file", dump.Name())
|
log.Info("Wrote standard trace", "file", dump.Name())
|
||||||
|
|
@ -764,8 +953,9 @@ func (api *API) standardTraceBlockToFile(ctx context.Context, block *types.Block
|
||||||
|
|
||||||
// containsTx reports whether the transaction with a certain hash
|
// containsTx reports whether the transaction with a certain hash
|
||||||
// is contained within the specified block.
|
// is contained within the specified block.
|
||||||
func containsTx(block *types.Block, hash common.Hash) bool {
|
func (api *API) containsTx(ctx context.Context, block *types.Block, hash common.Hash) bool {
|
||||||
for _, tx := range block.Transactions() {
|
txs, _ := api.getAllBlockTransactions(ctx, block)
|
||||||
|
for _, tx := range txs {
|
||||||
if tx.Hash() == hash {
|
if tx.Hash() == hash {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
@ -776,6 +966,17 @@ func containsTx(block *types.Block, hash common.Hash) bool {
|
||||||
// TraceTransaction returns the structured logs created during the execution of EVM
|
// TraceTransaction returns the structured logs created during the execution of EVM
|
||||||
// and returns them as a JSON object.
|
// and returns them as a JSON object.
|
||||||
func (api *API) TraceTransaction(ctx context.Context, hash common.Hash, config *TraceConfig) (interface{}, error) {
|
func (api *API) TraceTransaction(ctx context.Context, hash common.Hash, config *TraceConfig) (interface{}, error) {
|
||||||
|
if config == nil {
|
||||||
|
config = &TraceConfig{
|
||||||
|
BorTraceEnabled: defaultBorTraceEnabled,
|
||||||
|
BorTx: newBoolPtr(false),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if config.BorTraceEnabled == nil {
|
||||||
|
config.BorTraceEnabled = defaultBorTraceEnabled
|
||||||
|
}
|
||||||
|
|
||||||
tx, blockHash, blockNumber, index, err := api.backend.GetTransaction(ctx, hash)
|
tx, blockHash, blockNumber, index, err := api.backend.GetTransaction(ctx, hash)
|
||||||
if tx == nil {
|
if tx == nil {
|
||||||
// For BorTransaction, there will be no trace available
|
// For BorTransaction, there will be no trace available
|
||||||
|
|
@ -811,6 +1012,7 @@ func (api *API) TraceTransaction(ctx context.Context, hash common.Hash, config *
|
||||||
TxIndex: int(index),
|
TxIndex: int(index),
|
||||||
TxHash: hash,
|
TxHash: hash,
|
||||||
}
|
}
|
||||||
|
|
||||||
return api.traceTx(ctx, msg, txctx, vmctx, statedb, config)
|
return api.traceTx(ctx, msg, txctx, vmctx, statedb, config)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -865,6 +1067,7 @@ func (api *API) TraceCall(ctx context.Context, args ethapi.TransactionArgs, bloc
|
||||||
Reexec: config.Reexec,
|
Reexec: config.Reexec,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return api.traceTx(ctx, msg, new(Context), vmctx, statedb, traceConfig)
|
return api.traceTx(ctx, msg, new(Context), vmctx, statedb, traceConfig)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -872,6 +1075,18 @@ func (api *API) TraceCall(ctx context.Context, args ethapi.TransactionArgs, bloc
|
||||||
// executes the given message in the provided environment. The return value will
|
// executes the given message in the provided environment. The return value will
|
||||||
// be tracer dependent.
|
// be tracer dependent.
|
||||||
func (api *API) traceTx(ctx context.Context, message core.Message, txctx *Context, vmctx vm.BlockContext, statedb *state.StateDB, config *TraceConfig) (interface{}, error) {
|
func (api *API) traceTx(ctx context.Context, message core.Message, txctx *Context, vmctx vm.BlockContext, statedb *state.StateDB, config *TraceConfig) (interface{}, error) {
|
||||||
|
|
||||||
|
if config == nil {
|
||||||
|
config = &TraceConfig{
|
||||||
|
BorTraceEnabled: defaultBorTraceEnabled,
|
||||||
|
BorTx: newBoolPtr(false),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if config.BorTraceEnabled == nil {
|
||||||
|
config.BorTraceEnabled = defaultBorTraceEnabled
|
||||||
|
}
|
||||||
|
|
||||||
// Assemble the structured logger or the JavaScript tracer
|
// Assemble the structured logger or the JavaScript tracer
|
||||||
var (
|
var (
|
||||||
tracer vm.EVMLogger
|
tracer vm.EVMLogger
|
||||||
|
|
@ -911,9 +1126,22 @@ func (api *API) traceTx(ctx context.Context, message core.Message, txctx *Contex
|
||||||
// Call Prepare to clear out the statedb access list
|
// Call Prepare to clear out the statedb access list
|
||||||
statedb.Prepare(txctx.TxHash, txctx.TxIndex)
|
statedb.Prepare(txctx.TxHash, txctx.TxIndex)
|
||||||
|
|
||||||
result, err := core.ApplyMessage(vmenv, message, new(core.GasPool).AddGas(message.Gas()))
|
var result *core.ExecutionResult
|
||||||
if err != nil {
|
|
||||||
return nil, fmt.Errorf("tracing failed: %w", err)
|
if config.BorTx == nil {
|
||||||
|
config.BorTx = newBoolPtr(false)
|
||||||
|
}
|
||||||
|
|
||||||
|
if *config.BorTx {
|
||||||
|
callmsg := prepareCallMessage(message)
|
||||||
|
if result, err = statefull.ApplyBorMessage(*vmenv, callmsg); err != nil {
|
||||||
|
return nil, fmt.Errorf("tracing failed: %w", err)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
result, err = core.ApplyMessage(vmenv, message, new(core.GasPool).AddGas(message.Gas()))
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("tracing failed: %w", err)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Depending on the tracer type, format and return the output.
|
// Depending on the tracer type, format and return the output.
|
||||||
|
|
|
||||||
|
|
@ -5,6 +5,7 @@ import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
|
||||||
"github.com/ethereum/go-ethereum/common"
|
"github.com/ethereum/go-ethereum/common"
|
||||||
|
"github.com/ethereum/go-ethereum/consensus/bor/statefull"
|
||||||
"github.com/ethereum/go-ethereum/core"
|
"github.com/ethereum/go-ethereum/core"
|
||||||
"github.com/ethereum/go-ethereum/core/types"
|
"github.com/ethereum/go-ethereum/core/types"
|
||||||
"github.com/ethereum/go-ethereum/core/vm"
|
"github.com/ethereum/go-ethereum/core/vm"
|
||||||
|
|
@ -68,14 +69,14 @@ func (api *API) traceBorBlock(ctx context.Context, block *types.Block, config *T
|
||||||
|
|
||||||
// Execute all the transaction contained within the block concurrently
|
// Execute all the transaction contained within the block concurrently
|
||||||
var (
|
var (
|
||||||
signer = types.MakeSigner(api.backend.ChainConfig(), block.Number())
|
signer = types.MakeSigner(api.backend.ChainConfig(), block.Number())
|
||||||
txs = block.Transactions()
|
txs, stateSyncPresent = api.getAllBlockTransactions(ctx, block)
|
||||||
deleteEmptyObjects = api.backend.ChainConfig().IsEIP158(block.Number())
|
deleteEmptyObjects = api.backend.ChainConfig().IsEIP158(block.Number())
|
||||||
)
|
)
|
||||||
|
|
||||||
blockCtx := core.NewEVMBlockContext(block.Header(), api.chainContext(ctx), nil)
|
blockCtx := core.NewEVMBlockContext(block.Header(), api.chainContext(ctx), nil)
|
||||||
|
|
||||||
traceTxn := func(indx int, tx *types.Transaction) *TxTraceResult {
|
traceTxn := func(indx int, tx *types.Transaction, borTx bool) *TxTraceResult {
|
||||||
message, _ := tx.AsMessage(signer, block.BaseFee())
|
message, _ := tx.AsMessage(signer, block.BaseFee())
|
||||||
txContext := core.NewEVMTxContext(message)
|
txContext := core.NewEVMTxContext(message)
|
||||||
|
|
||||||
|
|
@ -88,7 +89,15 @@ func (api *API) traceBorBlock(ctx context.Context, block *types.Block, config *T
|
||||||
// Not sure if we need to do this
|
// Not sure if we need to do this
|
||||||
statedb.Prepare(tx.Hash(), indx)
|
statedb.Prepare(tx.Hash(), indx)
|
||||||
|
|
||||||
execRes, err := core.ApplyMessage(vmenv, message, new(core.GasPool).AddGas(message.Gas()))
|
var execRes *core.ExecutionResult
|
||||||
|
|
||||||
|
if borTx {
|
||||||
|
callmsg := prepareCallMessage(message)
|
||||||
|
execRes, err = statefull.ApplyBorMessage(*vmenv, callmsg)
|
||||||
|
} else {
|
||||||
|
execRes, err = core.ApplyMessage(vmenv, message, new(core.GasPool).AddGas(message.Gas()))
|
||||||
|
}
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return &TxTraceResult{
|
return &TxTraceResult{
|
||||||
Error: err.Error(),
|
Error: err.Error(),
|
||||||
|
|
@ -115,7 +124,11 @@ func (api *API) traceBorBlock(ctx context.Context, block *types.Block, config *T
|
||||||
}
|
}
|
||||||
|
|
||||||
for indx, tx := range txs {
|
for indx, tx := range txs {
|
||||||
res.Transactions = append(res.Transactions, traceTxn(indx, tx))
|
if stateSyncPresent && indx == len(txs)-1 {
|
||||||
|
res.Transactions = append(res.Transactions, traceTxn(indx, tx, true))
|
||||||
|
} else {
|
||||||
|
res.Transactions = append(res.Transactions, traceTxn(indx, tx, false))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return res, nil
|
return res, nil
|
||||||
|
|
|
||||||
|
|
@ -176,6 +176,11 @@ func (b *testBackend) StateAtTransaction(ctx context.Context, block *types.Block
|
||||||
return nil, vm.BlockContext{}, nil, fmt.Errorf("transaction index %d out of range for block %#x", txIndex, block.Hash())
|
return nil, vm.BlockContext{}, nil, fmt.Errorf("transaction index %d out of range for block %#x", txIndex, block.Hash())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (b *testBackend) GetBorBlockTransactionWithBlockHash(ctx context.Context, txHash common.Hash, blockHash common.Hash) (*types.Transaction, common.Hash, uint64, uint64, error) {
|
||||||
|
tx, blockHash, blockNumber, index := rawdb.ReadBorTransactionWithBlockHash(b.ChainDb(), txHash, blockHash)
|
||||||
|
return tx, blockHash, blockNumber, index, nil
|
||||||
|
}
|
||||||
|
|
||||||
func TestTraceCall(t *testing.T) {
|
func TestTraceCall(t *testing.T) {
|
||||||
t.Parallel()
|
t.Parallel()
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue