mirror of
https://github.com/ethereum/go-ethereum.git
synced 2026-07-26 06:36:43 +00:00
604 lines
20 KiB
Go
604 lines
20 KiB
Go
package tracing
|
|
|
|
import (
|
|
"bytes"
|
|
"errors"
|
|
"fmt"
|
|
"runtime"
|
|
"sync"
|
|
"time"
|
|
|
|
"github.com/scroll-tech/go-ethereum/common"
|
|
"github.com/scroll-tech/go-ethereum/common/hexutil"
|
|
"github.com/scroll-tech/go-ethereum/consensus"
|
|
"github.com/scroll-tech/go-ethereum/core"
|
|
"github.com/scroll-tech/go-ethereum/core/rawdb"
|
|
"github.com/scroll-tech/go-ethereum/core/state"
|
|
"github.com/scroll-tech/go-ethereum/core/types"
|
|
"github.com/scroll-tech/go-ethereum/core/vm"
|
|
"github.com/scroll-tech/go-ethereum/eth/tracers"
|
|
_ "github.com/scroll-tech/go-ethereum/eth/tracers/native"
|
|
"github.com/scroll-tech/go-ethereum/ethdb"
|
|
"github.com/scroll-tech/go-ethereum/log"
|
|
"github.com/scroll-tech/go-ethereum/metrics"
|
|
"github.com/scroll-tech/go-ethereum/params"
|
|
"github.com/scroll-tech/go-ethereum/rollup/fees"
|
|
"github.com/scroll-tech/go-ethereum/rollup/rcfg"
|
|
"github.com/scroll-tech/go-ethereum/rollup/withdrawtrie"
|
|
)
|
|
|
|
var (
|
|
getTxResultTimer = metrics.NewRegisteredTimer("rollup/tracing/get_tx_result", nil)
|
|
getTxResultApplyMessageTimer = metrics.NewRegisteredTimer("rollup/tracing/get_tx_result/apply_message", nil)
|
|
getTxResultZkTrieBuildTimer = metrics.NewRegisteredTimer("rollup/tracing/get_tx_result/zk_trie_build", nil)
|
|
getTxResultTracerResultTimer = metrics.NewRegisteredTimer("rollup/tracing/get_tx_result/tracer_result", nil)
|
|
feedTxToTracerTimer = metrics.NewRegisteredTimer("rollup/tracing/feed_tx_to_tracer", nil)
|
|
fillBlockTraceTimer = metrics.NewRegisteredTimer("rollup/tracing/fill_block_trace", nil)
|
|
)
|
|
|
|
// TracerWrapper implements ScrollTracerWrapper interface
|
|
type TracerWrapper struct{}
|
|
|
|
// NewTracerWrapper TracerWrapper creates a new TracerWrapper
|
|
func NewTracerWrapper() *TracerWrapper {
|
|
return &TracerWrapper{}
|
|
}
|
|
|
|
// CreateTraceEnvAndGetBlockTrace wraps the whole block tracing logic for a block
|
|
func (tw *TracerWrapper) CreateTraceEnvAndGetBlockTrace(chainConfig *params.ChainConfig, chainContext core.ChainContext, engine consensus.Engine, chaindb ethdb.Database, statedb *state.StateDB, parent *types.Block, block *types.Block, commitAfterApply bool) (*types.BlockTrace, error) {
|
|
traceEnv, err := CreateTraceEnv(chainConfig, chainContext, engine, chaindb, statedb, parent, block, commitAfterApply)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
return traceEnv.GetBlockTrace(block)
|
|
}
|
|
|
|
type TraceEnv struct {
|
|
logConfig *vm.LogConfig
|
|
commitAfterApply bool
|
|
chainConfig *params.ChainConfig
|
|
|
|
coinbase common.Address
|
|
|
|
// rMu lock is used to protect txs executed in parallel.
|
|
signer types.Signer
|
|
state *state.StateDB
|
|
blockCtx vm.BlockContext
|
|
|
|
// pMu lock is used to protect Proofs' read and write mutual exclusion,
|
|
// since txs are executed in parallel, so this lock is required.
|
|
pMu sync.Mutex
|
|
// sMu is required because of txs are executed in parallel,
|
|
// this lock is used to protect StorageTrace's read and write mutual exclusion.
|
|
sMu sync.Mutex
|
|
*types.StorageTrace
|
|
TxStorageTraces []*types.StorageTrace
|
|
// zktrie tracer is used for zktrie storage to build additional deletion proof
|
|
ZkTrieTracer map[string]state.ZktrieProofTracer
|
|
ExecutionResults []*types.ExecutionResult
|
|
|
|
// StartL1QueueIndex is the next L1 message queue index that this block can process.
|
|
// Example: If the parent block included QueueIndex=9, then StartL1QueueIndex will
|
|
// be 10.
|
|
StartL1QueueIndex uint64
|
|
}
|
|
|
|
// Context is the same as Context in eth/tracers/tracers.go
|
|
type Context struct {
|
|
BlockHash common.Hash
|
|
TxIndex int
|
|
TxHash common.Hash
|
|
}
|
|
|
|
// txTraceTask is the same as txTraceTask in eth/tracers/api.go
|
|
type txTraceTask struct {
|
|
statedb *state.StateDB
|
|
index int
|
|
}
|
|
|
|
func CreateTraceEnvHelper(chainConfig *params.ChainConfig, logConfig *vm.LogConfig, blockCtx vm.BlockContext, startL1QueueIndex uint64, coinbase common.Address, statedb *state.StateDB, rootBefore common.Hash, block *types.Block, commitAfterApply bool) *TraceEnv {
|
|
return &TraceEnv{
|
|
logConfig: logConfig,
|
|
commitAfterApply: commitAfterApply,
|
|
chainConfig: chainConfig,
|
|
coinbase: coinbase,
|
|
signer: types.MakeSigner(chainConfig, block.Number()),
|
|
state: statedb,
|
|
blockCtx: blockCtx,
|
|
StorageTrace: &types.StorageTrace{
|
|
RootBefore: rootBefore,
|
|
RootAfter: block.Root(),
|
|
Proofs: make(map[string][]hexutil.Bytes),
|
|
StorageProofs: make(map[string]map[string][]hexutil.Bytes),
|
|
},
|
|
ZkTrieTracer: make(map[string]state.ZktrieProofTracer),
|
|
ExecutionResults: make([]*types.ExecutionResult, block.Transactions().Len()),
|
|
TxStorageTraces: make([]*types.StorageTrace, block.Transactions().Len()),
|
|
StartL1QueueIndex: startL1QueueIndex,
|
|
}
|
|
}
|
|
|
|
func CreateTraceEnv(chainConfig *params.ChainConfig, chainContext core.ChainContext, engine consensus.Engine, chaindb ethdb.Database, statedb *state.StateDB, parent *types.Block, block *types.Block, commitAfterApply bool) (*TraceEnv, error) {
|
|
var coinbase common.Address
|
|
|
|
var err error
|
|
if chainConfig.Scroll.FeeVaultEnabled() {
|
|
coinbase = *chainConfig.Scroll.FeeVaultAddress
|
|
} else {
|
|
coinbase, err = engine.Author(block.Header())
|
|
if err != nil {
|
|
log.Warn("recover coinbase in CreateTraceEnv fail. using zero-address", "err", err, "blockNumber", block.Header().Number, "headerHash", block.Header().Hash())
|
|
}
|
|
}
|
|
|
|
// Collect start queue index, we should always have this value for blocks
|
|
// that have been executed.
|
|
// FIXME: This value will be incorrect on the signer, since we reuse this
|
|
// DB entry to signal which index the worker should continue from.
|
|
// Example: Ledger A <-- B <-- C. Block `A` contains up to `QueueIndex=9`.
|
|
// For block `B`, the worker skips 10 messages and includes 0.
|
|
// `ReadFirstQueueIndexNotInL2Block(B)` will then return `20` on the
|
|
// signer to avoid re-processing the same 10 transactions again for
|
|
// block `C`.
|
|
// `ReadFirstQueueIndexNotInL1Block(B)` will return the correct value
|
|
// `10` on follower nodes.
|
|
startL1QueueIndex := rawdb.ReadFirstQueueIndexNotInL2Block(chaindb, parent.Hash())
|
|
if startL1QueueIndex == nil {
|
|
log.Error("missing FirstQueueIndexNotInL2Block for block during trace call", "number", parent.NumberU64(), "hash", parent.Hash())
|
|
return nil, fmt.Errorf("missing FirstQueueIndexNotInL2Block for block during trace call: hash=%v, parentHash=%vv", block.Hash(), parent.Hash())
|
|
}
|
|
env := CreateTraceEnvHelper(
|
|
chainConfig,
|
|
&vm.LogConfig{
|
|
DisableStorage: true,
|
|
DisableStack: true,
|
|
EnableMemory: false,
|
|
EnableReturnData: true,
|
|
},
|
|
core.NewEVMBlockContext(block.Header(), chainContext, chainConfig, nil),
|
|
*startL1QueueIndex,
|
|
coinbase,
|
|
statedb,
|
|
parent.Root(),
|
|
block,
|
|
commitAfterApply,
|
|
)
|
|
|
|
key := coinbase.String()
|
|
if _, exist := env.Proofs[key]; !exist {
|
|
proof, err := env.state.GetProof(coinbase)
|
|
if err != nil {
|
|
log.Error("Proof for coinbase not available", "coinbase", coinbase, "error", err)
|
|
// but we still mark the proofs map with nil array
|
|
}
|
|
env.Proofs[key] = types.WrapProof(proof)
|
|
}
|
|
|
|
return env, nil
|
|
}
|
|
|
|
func (env *TraceEnv) GetBlockTrace(block *types.Block) (*types.BlockTrace, error) {
|
|
// Execute all the transaction contained within the block concurrently
|
|
var (
|
|
txs = block.Transactions()
|
|
pend = new(sync.WaitGroup)
|
|
jobs = make(chan *txTraceTask, len(txs))
|
|
errCh = make(chan error, 1)
|
|
)
|
|
threads := runtime.NumCPU()
|
|
if threads > len(txs) {
|
|
threads = len(txs)
|
|
}
|
|
for th := 0; th < threads; th++ {
|
|
pend.Add(1)
|
|
go func() {
|
|
defer func(t time.Time) {
|
|
pend.Done()
|
|
getTxResultTimer.Update(time.Since(t))
|
|
}(time.Now())
|
|
|
|
// Fetch and execute the next transaction trace tasks
|
|
for task := range jobs {
|
|
if err := env.getTxResult(task.statedb, task.index, block); err != nil {
|
|
select {
|
|
case errCh <- err:
|
|
default:
|
|
}
|
|
log.Error(
|
|
"failed to trace tx",
|
|
"txHash", txs[task.index].Hash().String(),
|
|
"blockHash", block.Hash().String(),
|
|
"blockNumber", block.NumberU64(),
|
|
"err", err,
|
|
)
|
|
}
|
|
}
|
|
}()
|
|
}
|
|
|
|
// Feed the transactions into the tracers and return
|
|
var failed error
|
|
common.WithTimer(feedTxToTracerTimer, func() {
|
|
for i, tx := range txs {
|
|
// Send the trace task over for execution
|
|
jobs <- &txTraceTask{statedb: env.state.Copy(), index: i}
|
|
|
|
// Generate the next state snapshot fast without tracing
|
|
msg, _ := tx.AsMessage(env.signer, block.BaseFee())
|
|
env.state.SetTxContext(tx.Hash(), i)
|
|
vmenv := vm.NewEVM(env.blockCtx, core.NewEVMTxContext(msg), env.state, env.chainConfig, vm.Config{})
|
|
l1DataFee, err := fees.CalculateL1DataFee(tx, env.state, env.chainConfig, block.Number())
|
|
if err != nil {
|
|
failed = err
|
|
break
|
|
}
|
|
if _, err = core.ApplyMessage(vmenv, msg, new(core.GasPool).AddGas(msg.Gas()), l1DataFee); err != nil {
|
|
failed = err
|
|
break
|
|
}
|
|
if env.commitAfterApply {
|
|
env.state.Finalise(vmenv.ChainConfig().IsEIP158(block.Number()))
|
|
}
|
|
}
|
|
})
|
|
close(jobs)
|
|
pend.Wait()
|
|
|
|
// after all tx has been traced, collect "deletion proof" for zktrie
|
|
for _, tracer := range env.ZkTrieTracer {
|
|
delProofs, err := tracer.GetDeletionProofs()
|
|
if err != nil {
|
|
log.Error("deletion proof failure", "error", err)
|
|
} else {
|
|
for _, proof := range delProofs {
|
|
env.DeletionProofs = append(env.DeletionProofs, proof)
|
|
}
|
|
}
|
|
}
|
|
|
|
// build dummy per-tx deletion proof
|
|
for _, txStorageTrace := range env.TxStorageTraces {
|
|
if txStorageTrace != nil {
|
|
txStorageTrace.DeletionProofs = env.DeletionProofs
|
|
}
|
|
}
|
|
|
|
// If execution failed in between, abort
|
|
select {
|
|
case err := <-errCh:
|
|
return nil, err
|
|
default:
|
|
if failed != nil {
|
|
return nil, failed
|
|
}
|
|
}
|
|
|
|
return env.fillBlockTrace(block)
|
|
}
|
|
|
|
func (env *TraceEnv) getTxResult(state *state.StateDB, index int, block *types.Block) error {
|
|
tx := block.Transactions()[index]
|
|
msg, _ := tx.AsMessage(env.signer, block.BaseFee())
|
|
from, _ := types.Sender(env.signer, tx)
|
|
to := tx.To()
|
|
|
|
txctx := &Context{
|
|
BlockHash: block.TxHash(),
|
|
TxIndex: index,
|
|
TxHash: tx.Hash(),
|
|
}
|
|
|
|
sender := &types.AccountWrapper{
|
|
Address: from,
|
|
Nonce: state.GetNonce(from),
|
|
Balance: (*hexutil.Big)(state.GetBalance(from)),
|
|
KeccakCodeHash: state.GetKeccakCodeHash(from),
|
|
PoseidonCodeHash: state.GetPoseidonCodeHash(from),
|
|
CodeSize: state.GetCodeSize(from),
|
|
}
|
|
var receiver *types.AccountWrapper
|
|
if to != nil {
|
|
receiver = &types.AccountWrapper{
|
|
Address: *to,
|
|
Nonce: state.GetNonce(*to),
|
|
Balance: (*hexutil.Big)(state.GetBalance(*to)),
|
|
KeccakCodeHash: state.GetKeccakCodeHash(*to),
|
|
PoseidonCodeHash: state.GetPoseidonCodeHash(*to),
|
|
CodeSize: state.GetCodeSize(*to),
|
|
}
|
|
}
|
|
|
|
txContext := core.NewEVMTxContext(msg)
|
|
tracerContext := tracers.Context{
|
|
BlockHash: block.Hash(),
|
|
TxIndex: index,
|
|
TxHash: tx.Hash(),
|
|
}
|
|
callTracer, err := tracers.New("callTracer", &tracerContext)
|
|
if err != nil {
|
|
return fmt.Errorf("failed to create callTracer: %w", err)
|
|
}
|
|
prestateTracer, err := tracers.New("prestateTracer", &tracerContext)
|
|
if err != nil {
|
|
return fmt.Errorf("failed to create prestateTracer: %w", err)
|
|
}
|
|
|
|
applyMessageStart := time.Now()
|
|
structLogger := vm.NewStructLogger(env.logConfig)
|
|
tracer := NewMuxTracer(structLogger, callTracer, prestateTracer)
|
|
// Run the transaction with tracing enabled.
|
|
vmenv := vm.NewEVM(env.blockCtx, txContext, state, env.chainConfig, vm.Config{Debug: true, Tracer: tracer, NoBaseFee: true})
|
|
|
|
// Call Prepare to clear out the statedb access list
|
|
state.SetTxContext(txctx.TxHash, txctx.TxIndex)
|
|
|
|
// Computes the new state by applying the given message.
|
|
l1DataFee, err := fees.CalculateL1DataFee(tx, state, env.chainConfig, block.Number())
|
|
if err != nil {
|
|
return err
|
|
}
|
|
result, err := core.ApplyMessage(vmenv, msg, new(core.GasPool).AddGas(msg.Gas()), l1DataFee)
|
|
if err != nil {
|
|
getTxResultApplyMessageTimer.UpdateSince(applyMessageStart)
|
|
return err
|
|
}
|
|
getTxResultApplyMessageTimer.UpdateSince(applyMessageStart)
|
|
|
|
// If the result contains a revert reason, return it.
|
|
returnVal := result.Return()
|
|
if len(result.Revert()) > 0 {
|
|
returnVal = result.Revert()
|
|
}
|
|
|
|
createdAcc := structLogger.CreatedAccount()
|
|
var after []*types.AccountWrapper
|
|
if to == nil {
|
|
if createdAcc == nil {
|
|
return errors.New("unexpected tx: address for created contract unavailable")
|
|
}
|
|
to = &createdAcc.Address
|
|
}
|
|
// collect affected account after tx being applied
|
|
for _, acc := range []common.Address{from, *to, env.coinbase} {
|
|
after = append(after, &types.AccountWrapper{
|
|
Address: acc,
|
|
Nonce: state.GetNonce(acc),
|
|
Balance: (*hexutil.Big)(state.GetBalance(acc)),
|
|
KeccakCodeHash: state.GetKeccakCodeHash(acc),
|
|
PoseidonCodeHash: state.GetPoseidonCodeHash(acc),
|
|
CodeSize: state.GetCodeSize(acc),
|
|
})
|
|
}
|
|
|
|
txStorageTrace := &types.StorageTrace{
|
|
Proofs: make(map[string][]hexutil.Bytes),
|
|
StorageProofs: make(map[string]map[string][]hexutil.Bytes),
|
|
}
|
|
// still we have no state root for per tx, only set the head and tail
|
|
if index == 0 {
|
|
txStorageTrace.RootBefore = state.GetRootHash()
|
|
}
|
|
if index == len(block.Transactions())-1 {
|
|
txStorageTrace.RootAfter = block.Root()
|
|
}
|
|
|
|
// merge required proof data
|
|
proofAccounts := structLogger.UpdatedAccounts()
|
|
proofAccounts[vmenv.FeeRecipient()] = struct{}{}
|
|
for addr := range proofAccounts {
|
|
addrStr := addr.String()
|
|
|
|
env.pMu.Lock()
|
|
checkedProof, existed := env.Proofs[addrStr]
|
|
if existed {
|
|
txStorageTrace.Proofs[addrStr] = checkedProof
|
|
}
|
|
env.pMu.Unlock()
|
|
if existed {
|
|
continue
|
|
}
|
|
proof, err := state.GetProof(addr)
|
|
if err != nil {
|
|
log.Error("Proof not available", "address", addrStr, "error", err)
|
|
// but we still mark the proofs map with nil array
|
|
}
|
|
wrappedProof := types.WrapProof(proof)
|
|
env.pMu.Lock()
|
|
env.Proofs[addrStr] = wrappedProof
|
|
txStorageTrace.Proofs[addrStr] = wrappedProof
|
|
env.pMu.Unlock()
|
|
}
|
|
|
|
zkTrieBuildStart := time.Now()
|
|
proofStorages := structLogger.UpdatedStorages()
|
|
for addr, keys := range proofStorages {
|
|
if _, existed := txStorageTrace.StorageProofs[addr.String()]; !existed {
|
|
txStorageTrace.StorageProofs[addr.String()] = make(map[string][]hexutil.Bytes)
|
|
}
|
|
|
|
env.sMu.Lock()
|
|
trie, err := state.GetStorageTrieForProof(addr)
|
|
if err != nil {
|
|
// but we still continue to next address
|
|
log.Error("Storage trie not available", "error", err, "address", addr)
|
|
env.sMu.Unlock()
|
|
continue
|
|
}
|
|
zktrieTracer := state.NewProofTracer(trie)
|
|
env.sMu.Unlock()
|
|
|
|
for key, values := range keys {
|
|
addrStr := addr.String()
|
|
keyStr := key.String()
|
|
isDelete := bytes.Equal(values.Bytes(), common.Hash{}.Bytes())
|
|
|
|
txm := txStorageTrace.StorageProofs[addrStr]
|
|
env.sMu.Lock()
|
|
m, existed := env.StorageProofs[addrStr]
|
|
if !existed {
|
|
m = make(map[string][]hexutil.Bytes)
|
|
env.StorageProofs[addrStr] = m
|
|
}
|
|
if zktrieTracer.Available() && !env.ZkTrieTracer[addrStr].Available() {
|
|
env.ZkTrieTracer[addrStr] = state.NewProofTracer(trie)
|
|
}
|
|
|
|
if proof, existed := m[keyStr]; existed {
|
|
txm[keyStr] = proof
|
|
// still need to touch tracer for deletion
|
|
if isDelete && zktrieTracer.Available() {
|
|
env.ZkTrieTracer[addrStr].MarkDeletion(key)
|
|
}
|
|
env.sMu.Unlock()
|
|
continue
|
|
}
|
|
env.sMu.Unlock()
|
|
|
|
var proof [][]byte
|
|
var err error
|
|
if zktrieTracer.Available() {
|
|
proof, err = state.GetSecureTrieProof(zktrieTracer, key)
|
|
} else {
|
|
proof, err = state.GetSecureTrieProof(trie, key)
|
|
}
|
|
if err != nil {
|
|
log.Error("Storage proof not available", "error", err, "address", addrStr, "key", keyStr)
|
|
// but we still mark the proofs map with nil array
|
|
}
|
|
wrappedProof := types.WrapProof(proof)
|
|
env.sMu.Lock()
|
|
txm[keyStr] = wrappedProof
|
|
m[keyStr] = wrappedProof
|
|
if zktrieTracer.Available() {
|
|
if isDelete {
|
|
zktrieTracer.MarkDeletion(key)
|
|
}
|
|
env.ZkTrieTracer[addrStr].Merge(zktrieTracer)
|
|
}
|
|
env.sMu.Unlock()
|
|
}
|
|
}
|
|
getTxResultZkTrieBuildTimer.UpdateSince(zkTrieBuildStart)
|
|
|
|
tracerResultTimer := time.Now()
|
|
callTrace, err := callTracer.GetResult()
|
|
if err != nil {
|
|
return fmt.Errorf("failed to get callTracer result: %w", err)
|
|
}
|
|
prestate, err := prestateTracer.GetResult()
|
|
if err != nil {
|
|
return fmt.Errorf("failed to get prestateTracer result: %w", err)
|
|
}
|
|
getTxResultTracerResultTimer.UpdateSince(tracerResultTimer)
|
|
|
|
env.ExecutionResults[index] = &types.ExecutionResult{
|
|
From: sender,
|
|
To: receiver,
|
|
AccountCreated: createdAcc,
|
|
AccountsAfter: after,
|
|
L1DataFee: (*hexutil.Big)(result.L1DataFee),
|
|
Gas: result.UsedGas,
|
|
Failed: result.Failed(),
|
|
ReturnValue: fmt.Sprintf("%x", returnVal),
|
|
StructLogs: vm.FormatLogs(structLogger.StructLogs()),
|
|
CallTrace: callTrace,
|
|
Prestate: prestate,
|
|
}
|
|
env.TxStorageTraces[index] = txStorageTrace
|
|
|
|
return nil
|
|
}
|
|
|
|
// fillBlockTrace content after all the txs are finished running.
|
|
func (env *TraceEnv) fillBlockTrace(block *types.Block) (*types.BlockTrace, error) {
|
|
defer func(t time.Time) {
|
|
fillBlockTraceTimer.Update(time.Since(t))
|
|
}(time.Now())
|
|
|
|
statedb := env.state
|
|
|
|
txs := make([]*types.TransactionData, block.Transactions().Len())
|
|
for i, tx := range block.Transactions() {
|
|
txs[i] = types.NewTransactionData(tx, block.NumberU64(), env.chainConfig)
|
|
}
|
|
|
|
intrinsicStorageProofs := map[common.Address][]common.Hash{
|
|
rcfg.L2MessageQueueAddress: {rcfg.WithdrawTrieRootSlot},
|
|
rcfg.L1GasPriceOracleAddress: {
|
|
rcfg.L1BaseFeeSlot,
|
|
rcfg.OverheadSlot,
|
|
rcfg.ScalarSlot,
|
|
rcfg.L1BlobBaseFeeSlot,
|
|
rcfg.CommitScalarSlot,
|
|
rcfg.BlobScalarSlot,
|
|
rcfg.IsCurieSlot,
|
|
},
|
|
}
|
|
|
|
for addr, storages := range intrinsicStorageProofs {
|
|
if _, existed := env.Proofs[addr.String()]; !existed {
|
|
if proof, err := statedb.GetProof(addr); err != nil {
|
|
log.Error("Proof for intrinstic address not available", "error", err, "address", addr)
|
|
} else {
|
|
env.Proofs[addr.String()] = types.WrapProof(proof)
|
|
}
|
|
}
|
|
|
|
if _, existed := env.StorageProofs[addr.String()]; !existed {
|
|
env.StorageProofs[addr.String()] = make(map[string][]hexutil.Bytes)
|
|
}
|
|
|
|
for _, slot := range storages {
|
|
if _, existed := env.StorageProofs[addr.String()][slot.String()]; !existed {
|
|
if trie, err := statedb.GetStorageTrieForProof(addr); err != nil {
|
|
log.Error("Storage proof for intrinstic address not available", "error", err, "address", addr)
|
|
} else if proof, _ := statedb.GetSecureTrieProof(trie, slot); err != nil {
|
|
log.Error("Get storage proof for intrinstic address failed", "error", err, "address", addr, "slot", slot)
|
|
} else {
|
|
env.StorageProofs[addr.String()][slot.String()] = types.WrapProof(proof)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
var chainID uint64
|
|
if env.chainConfig.ChainID != nil {
|
|
chainID = env.chainConfig.ChainID.Uint64()
|
|
}
|
|
blockTrace := &types.BlockTrace{
|
|
ChainID: chainID,
|
|
Version: params.ArchiveVersion(params.CommitHash),
|
|
Coinbase: &types.AccountWrapper{
|
|
Address: env.coinbase,
|
|
Nonce: statedb.GetNonce(env.coinbase),
|
|
Balance: (*hexutil.Big)(statedb.GetBalance(env.coinbase)),
|
|
KeccakCodeHash: statedb.GetKeccakCodeHash(env.coinbase),
|
|
PoseidonCodeHash: statedb.GetPoseidonCodeHash(env.coinbase),
|
|
CodeSize: statedb.GetCodeSize(env.coinbase),
|
|
},
|
|
Header: block.Header(),
|
|
StorageTrace: env.StorageTrace,
|
|
ExecutionResults: env.ExecutionResults,
|
|
TxStorageTraces: env.TxStorageTraces,
|
|
Transactions: txs,
|
|
StartL1QueueIndex: env.StartL1QueueIndex,
|
|
}
|
|
|
|
for i, tx := range block.Transactions() {
|
|
evmTrace := env.ExecutionResults[i]
|
|
// Contract is created.
|
|
if tx.To() == nil {
|
|
evmTrace.ByteCode = hexutil.Encode(tx.Data())
|
|
} else { // contract call be included at this case, specially fallback call's data is empty.
|
|
evmTrace.ByteCode = hexutil.Encode(statedb.GetCode(*tx.To()))
|
|
// Get tx.to address's code hash.
|
|
codeHash := statedb.GetPoseidonCodeHash(*tx.To())
|
|
evmTrace.PoseidonCodeHash = &codeHash
|
|
}
|
|
}
|
|
|
|
blockTrace.WithdrawTrieRoot = withdrawtrie.ReadWTRSlot(rcfg.L2MessageQueueAddress, env.state)
|
|
|
|
return blockTrace, nil
|
|
}
|