github.com/cryptotooltop/go-ethereum@v0.0.0-20231103184714-151d1922f3e5/core/trace.go (about)

     1  package core
     2  
     3  import (
     4  	"bytes"
     5  	"errors"
     6  	"fmt"
     7  	"runtime"
     8  	"sync"
     9  
    10  	"github.com/scroll-tech/go-ethereum/common"
    11  	"github.com/scroll-tech/go-ethereum/common/hexutil"
    12  	"github.com/scroll-tech/go-ethereum/consensus"
    13  	"github.com/scroll-tech/go-ethereum/core/rawdb"
    14  	"github.com/scroll-tech/go-ethereum/core/state"
    15  	"github.com/scroll-tech/go-ethereum/core/types"
    16  	"github.com/scroll-tech/go-ethereum/core/vm"
    17  	"github.com/scroll-tech/go-ethereum/ethdb"
    18  	"github.com/scroll-tech/go-ethereum/log"
    19  	"github.com/scroll-tech/go-ethereum/params"
    20  	"github.com/scroll-tech/go-ethereum/rollup/fees"
    21  	"github.com/scroll-tech/go-ethereum/rollup/rcfg"
    22  	"github.com/scroll-tech/go-ethereum/rollup/withdrawtrie"
    23  	"github.com/scroll-tech/go-ethereum/trie/zkproof"
    24  )
    25  
    26  type TraceEnv struct {
    27  	logConfig        *vm.LogConfig
    28  	commitAfterApply bool
    29  	chainConfig      *params.ChainConfig
    30  
    31  	coinbase common.Address
    32  
    33  	// rMu lock is used to protect txs executed in parallel.
    34  	signer   types.Signer
    35  	state    *state.StateDB
    36  	blockCtx vm.BlockContext
    37  
    38  	// pMu lock is used to protect Proofs' read and write mutual exclusion,
    39  	// since txs are executed in parallel, so this lock is required.
    40  	pMu sync.Mutex
    41  	// sMu is required because of txs are executed in parallel,
    42  	// this lock is used to protect StorageTrace's read and write mutual exclusion.
    43  	sMu sync.Mutex
    44  	*types.StorageTrace
    45  	TxStorageTraces []*types.StorageTrace
    46  	// zktrie tracer is used for zktrie storage to build additional deletion proof
    47  	ZkTrieTracer     map[string]state.ZktrieProofTracer
    48  	ExecutionResults []*types.ExecutionResult
    49  
    50  	// StartL1QueueIndex is the next L1 message queue index that this block can process.
    51  	// Example: If the parent block included QueueIndex=9, then StartL1QueueIndex will
    52  	// be 10.
    53  	StartL1QueueIndex uint64
    54  }
    55  
    56  // Context is the same as Context in eth/tracers/tracers.go
    57  type Context struct {
    58  	BlockHash common.Hash
    59  	TxIndex   int
    60  	TxHash    common.Hash
    61  }
    62  
    63  // txTraceTask is the same as txTraceTask in eth/tracers/api.go
    64  type txTraceTask struct {
    65  	statedb *state.StateDB
    66  	index   int
    67  }
    68  
    69  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 {
    70  	return &TraceEnv{
    71  		logConfig:        logConfig,
    72  		commitAfterApply: commitAfterApply,
    73  		chainConfig:      chainConfig,
    74  		coinbase:         coinbase,
    75  		signer:           types.MakeSigner(chainConfig, block.Number()),
    76  		state:            statedb,
    77  		blockCtx:         blockCtx,
    78  		StorageTrace: &types.StorageTrace{
    79  			RootBefore:    rootBefore,
    80  			RootAfter:     block.Root(),
    81  			Proofs:        make(map[string][]hexutil.Bytes),
    82  			StorageProofs: make(map[string]map[string][]hexutil.Bytes),
    83  		},
    84  		ZkTrieTracer:      make(map[string]state.ZktrieProofTracer),
    85  		ExecutionResults:  make([]*types.ExecutionResult, block.Transactions().Len()),
    86  		TxStorageTraces:   make([]*types.StorageTrace, block.Transactions().Len()),
    87  		StartL1QueueIndex: startL1QueueIndex,
    88  	}
    89  }
    90  
    91  func CreateTraceEnv(chainConfig *params.ChainConfig, chainContext ChainContext, engine consensus.Engine, chaindb ethdb.Database, statedb *state.StateDB, parent *types.Block, block *types.Block, commitAfterApply bool) (*TraceEnv, error) {
    92  	var coinbase common.Address
    93  	var err error
    94  	if chainConfig.Scroll.FeeVaultEnabled() {
    95  		coinbase = *chainConfig.Scroll.FeeVaultAddress
    96  	} else {
    97  		coinbase, err = engine.Author(block.Header())
    98  		if err != nil {
    99  			log.Warn("recover coinbase in CreateTraceEnv fail. using zero-address", "err", err, "blockNumber", block.Header().Number, "headerHash", block.Header().Hash())
   100  		}
   101  	}
   102  
   103  	// Collect start queue index, we should always have this value for blocks
   104  	// that have been executed.
   105  	// FIXME: This value will be incorrect on the signer, since we reuse this
   106  	// DB entry to signal which index the worker should continue from.
   107  	// Example: Ledger A <-- B <-- C. Block `A` contains up to `QueueIndex=9`.
   108  	// For block `B`, the worker skips 10 messages and includes 0.
   109  	// `ReadFirstQueueIndexNotInL2Block(B)` will then return `20` on the
   110  	// signer to avoid re-processing the same 10 transactions again for
   111  	// block `C`.
   112  	// `ReadFirstQueueIndexNotInL1Block(B)` will return the correct value
   113  	// `10` on follower nodes.
   114  	startL1QueueIndex := rawdb.ReadFirstQueueIndexNotInL2Block(chaindb, parent.Hash())
   115  	if startL1QueueIndex == nil {
   116  		log.Error("missing FirstQueueIndexNotInL2Block for block during trace call", "number", parent.NumberU64(), "hash", parent.Hash())
   117  		return nil, fmt.Errorf("missing FirstQueueIndexNotInL2Block for block during trace call: hash=%v, parentHash=%vv", block.Hash(), parent.Hash())
   118  	}
   119  	env := CreateTraceEnvHelper(
   120  		chainConfig,
   121  		&vm.LogConfig{
   122  			EnableMemory:     false,
   123  			EnableReturnData: true,
   124  		},
   125  		NewEVMBlockContext(block.Header(), chainContext, chainConfig, nil),
   126  		*startL1QueueIndex,
   127  		coinbase,
   128  		statedb,
   129  		parent.Root(),
   130  		block,
   131  		commitAfterApply,
   132  	)
   133  
   134  	key := coinbase.String()
   135  	if _, exist := env.Proofs[key]; !exist {
   136  		proof, err := env.state.GetProof(coinbase)
   137  		if err != nil {
   138  			log.Error("Proof for coinbase not available", "coinbase", coinbase, "error", err)
   139  			// but we still mark the proofs map with nil array
   140  		}
   141  		env.Proofs[key] = types.WrapProof(proof)
   142  	}
   143  
   144  	return env, nil
   145  }
   146  
   147  func (env *TraceEnv) GetBlockTrace(block *types.Block) (*types.BlockTrace, error) {
   148  	// Execute all the transaction contained within the block concurrently
   149  	var (
   150  		txs   = block.Transactions()
   151  		pend  = new(sync.WaitGroup)
   152  		jobs  = make(chan *txTraceTask, len(txs))
   153  		errCh = make(chan error, 1)
   154  	)
   155  	threads := runtime.NumCPU()
   156  	if threads > len(txs) {
   157  		threads = len(txs)
   158  	}
   159  	for th := 0; th < threads; th++ {
   160  		pend.Add(1)
   161  		go func() {
   162  			defer pend.Done()
   163  			// Fetch and execute the next transaction trace tasks
   164  			for task := range jobs {
   165  				if err := env.getTxResult(task.statedb, task.index, block); err != nil {
   166  					select {
   167  					case errCh <- err:
   168  					default:
   169  					}
   170  					log.Error(
   171  						"failed to trace tx",
   172  						"txHash", txs[task.index].Hash().String(),
   173  						"blockHash", block.Hash().String(),
   174  						"blockNumber", block.NumberU64(),
   175  						"err", err,
   176  					)
   177  				}
   178  			}
   179  		}()
   180  	}
   181  
   182  	// Feed the transactions into the tracers and return
   183  	var failed error
   184  	for i, tx := range txs {
   185  		// Send the trace task over for execution
   186  		jobs <- &txTraceTask{statedb: env.state.Copy(), index: i}
   187  
   188  		// Generate the next state snapshot fast without tracing
   189  		msg, _ := tx.AsMessage(env.signer, block.BaseFee())
   190  		env.state.Prepare(tx.Hash(), i)
   191  		vmenv := vm.NewEVM(env.blockCtx, NewEVMTxContext(msg), env.state, env.chainConfig, vm.Config{})
   192  		l1DataFee, err := fees.CalculateL1DataFee(tx, env.state)
   193  		if err != nil {
   194  			failed = err
   195  			break
   196  		}
   197  		if _, err = ApplyMessage(vmenv, msg, new(GasPool).AddGas(msg.Gas()), l1DataFee); err != nil {
   198  			failed = err
   199  			break
   200  		}
   201  		if env.commitAfterApply {
   202  			env.state.Finalise(vmenv.ChainConfig().IsEIP158(block.Number()))
   203  		}
   204  	}
   205  	close(jobs)
   206  	pend.Wait()
   207  
   208  	// after all tx has been traced, collect "deletion proof" for zktrie
   209  	for _, tracer := range env.ZkTrieTracer {
   210  		delProofs, err := tracer.GetDeletionProofs()
   211  		if err != nil {
   212  			log.Error("deletion proof failure", "error", err)
   213  		} else {
   214  			for _, proof := range delProofs {
   215  				env.DeletionProofs = append(env.DeletionProofs, proof)
   216  			}
   217  		}
   218  	}
   219  
   220  	// build dummy per-tx deletion proof
   221  	for _, txStorageTrace := range env.TxStorageTraces {
   222  		if txStorageTrace != nil {
   223  			txStorageTrace.DeletionProofs = env.DeletionProofs
   224  		}
   225  	}
   226  
   227  	// If execution failed in between, abort
   228  	select {
   229  	case err := <-errCh:
   230  		return nil, err
   231  	default:
   232  		if failed != nil {
   233  			return nil, failed
   234  		}
   235  	}
   236  
   237  	return env.fillBlockTrace(block)
   238  }
   239  
   240  func (env *TraceEnv) getTxResult(state *state.StateDB, index int, block *types.Block) error {
   241  	tx := block.Transactions()[index]
   242  	msg, _ := tx.AsMessage(env.signer, block.BaseFee())
   243  	from, _ := types.Sender(env.signer, tx)
   244  	to := tx.To()
   245  
   246  	txctx := &Context{
   247  		BlockHash: block.TxHash(),
   248  		TxIndex:   index,
   249  		TxHash:    tx.Hash(),
   250  	}
   251  
   252  	sender := &types.AccountWrapper{
   253  		Address:          from,
   254  		Nonce:            state.GetNonce(from),
   255  		Balance:          (*hexutil.Big)(state.GetBalance(from)),
   256  		KeccakCodeHash:   state.GetKeccakCodeHash(from),
   257  		PoseidonCodeHash: state.GetPoseidonCodeHash(from),
   258  		CodeSize:         state.GetCodeSize(from),
   259  	}
   260  	var receiver *types.AccountWrapper
   261  	if to != nil {
   262  		receiver = &types.AccountWrapper{
   263  			Address:          *to,
   264  			Nonce:            state.GetNonce(*to),
   265  			Balance:          (*hexutil.Big)(state.GetBalance(*to)),
   266  			KeccakCodeHash:   state.GetKeccakCodeHash(*to),
   267  			PoseidonCodeHash: state.GetPoseidonCodeHash(*to),
   268  			CodeSize:         state.GetCodeSize(*to),
   269  		}
   270  	}
   271  
   272  	tracer := vm.NewStructLogger(env.logConfig)
   273  	// Run the transaction with tracing enabled.
   274  	vmenv := vm.NewEVM(env.blockCtx, NewEVMTxContext(msg), state, env.chainConfig, vm.Config{Debug: true, Tracer: tracer, NoBaseFee: true})
   275  
   276  	// Call Prepare to clear out the statedb access list
   277  	state.Prepare(txctx.TxHash, txctx.TxIndex)
   278  
   279  	// Computes the new state by applying the given message.
   280  	l1DataFee, err := fees.CalculateL1DataFee(tx, state)
   281  	if err != nil {
   282  		return fmt.Errorf("tracing failed: %w", err)
   283  	}
   284  	result, err := ApplyMessage(vmenv, msg, new(GasPool).AddGas(msg.Gas()), l1DataFee)
   285  	if err != nil {
   286  		return fmt.Errorf("tracing failed: %w", err)
   287  	}
   288  	// If the result contains a revert reason, return it.
   289  	returnVal := result.Return()
   290  	if len(result.Revert()) > 0 {
   291  		returnVal = result.Revert()
   292  	}
   293  
   294  	createdAcc := tracer.CreatedAccount()
   295  	var after []*types.AccountWrapper
   296  	if to == nil {
   297  		if createdAcc == nil {
   298  			return errors.New("unexpected tx: address for created contract unavailable")
   299  		}
   300  		to = &createdAcc.Address
   301  	}
   302  	// collect affected account after tx being applied
   303  	for _, acc := range []common.Address{from, *to, env.coinbase} {
   304  		after = append(after, &types.AccountWrapper{
   305  			Address:          acc,
   306  			Nonce:            state.GetNonce(acc),
   307  			Balance:          (*hexutil.Big)(state.GetBalance(acc)),
   308  			KeccakCodeHash:   state.GetKeccakCodeHash(acc),
   309  			PoseidonCodeHash: state.GetPoseidonCodeHash(acc),
   310  			CodeSize:         state.GetCodeSize(acc),
   311  		})
   312  	}
   313  
   314  	txStorageTrace := &types.StorageTrace{
   315  		Proofs:        make(map[string][]hexutil.Bytes),
   316  		StorageProofs: make(map[string]map[string][]hexutil.Bytes),
   317  	}
   318  	// still we have no state root for per tx, only set the head and tail
   319  	if index == 0 {
   320  		txStorageTrace.RootBefore = state.GetRootHash()
   321  	}
   322  	if index == len(block.Transactions())-1 {
   323  		txStorageTrace.RootAfter = block.Root()
   324  	}
   325  
   326  	// merge required proof data
   327  	proofAccounts := tracer.UpdatedAccounts()
   328  	proofAccounts[vmenv.FeeRecipient()] = struct{}{}
   329  	for addr := range proofAccounts {
   330  		addrStr := addr.String()
   331  
   332  		env.pMu.Lock()
   333  		checkedProof, existed := env.Proofs[addrStr]
   334  		if existed {
   335  			txStorageTrace.Proofs[addrStr] = checkedProof
   336  		}
   337  		env.pMu.Unlock()
   338  		if existed {
   339  			continue
   340  		}
   341  		proof, err := state.GetProof(addr)
   342  		if err != nil {
   343  			log.Error("Proof not available", "address", addrStr, "error", err)
   344  			// but we still mark the proofs map with nil array
   345  		}
   346  		wrappedProof := types.WrapProof(proof)
   347  		env.pMu.Lock()
   348  		env.Proofs[addrStr] = wrappedProof
   349  		txStorageTrace.Proofs[addrStr] = wrappedProof
   350  		env.pMu.Unlock()
   351  	}
   352  
   353  	proofStorages := tracer.UpdatedStorages()
   354  	for addr, keys := range proofStorages {
   355  		if _, existed := txStorageTrace.StorageProofs[addr.String()]; !existed {
   356  			txStorageTrace.StorageProofs[addr.String()] = make(map[string][]hexutil.Bytes)
   357  		}
   358  
   359  		env.sMu.Lock()
   360  		trie, err := state.GetStorageTrieForProof(addr)
   361  		if err != nil {
   362  			// but we still continue to next address
   363  			log.Error("Storage trie not available", "error", err, "address", addr)
   364  			env.sMu.Unlock()
   365  			continue
   366  		}
   367  		zktrieTracer := state.NewProofTracer(trie)
   368  		env.sMu.Unlock()
   369  
   370  		for key, values := range keys {
   371  			addrStr := addr.String()
   372  			keyStr := key.String()
   373  			isDelete := bytes.Equal(values.Bytes(), common.Hash{}.Bytes())
   374  
   375  			txm := txStorageTrace.StorageProofs[addrStr]
   376  			env.sMu.Lock()
   377  			m, existed := env.StorageProofs[addrStr]
   378  			if !existed {
   379  				m = make(map[string][]hexutil.Bytes)
   380  				env.StorageProofs[addrStr] = m
   381  			}
   382  			if zktrieTracer.Available() && !env.ZkTrieTracer[addrStr].Available() {
   383  				env.ZkTrieTracer[addrStr] = state.NewProofTracer(trie)
   384  			}
   385  
   386  			if proof, existed := m[keyStr]; existed {
   387  				txm[keyStr] = proof
   388  				// still need to touch tracer for deletion
   389  				if isDelete && zktrieTracer.Available() {
   390  					env.ZkTrieTracer[addrStr].MarkDeletion(key)
   391  				}
   392  				env.sMu.Unlock()
   393  				continue
   394  			}
   395  			env.sMu.Unlock()
   396  
   397  			var proof [][]byte
   398  			var err error
   399  			if zktrieTracer.Available() {
   400  				proof, err = state.GetSecureTrieProof(zktrieTracer, key)
   401  			} else {
   402  				proof, err = state.GetSecureTrieProof(trie, key)
   403  			}
   404  			if err != nil {
   405  				log.Error("Storage proof not available", "error", err, "address", addrStr, "key", keyStr)
   406  				// but we still mark the proofs map with nil array
   407  			}
   408  			wrappedProof := types.WrapProof(proof)
   409  			env.sMu.Lock()
   410  			txm[keyStr] = wrappedProof
   411  			m[keyStr] = wrappedProof
   412  			if zktrieTracer.Available() {
   413  				if isDelete {
   414  					zktrieTracer.MarkDeletion(key)
   415  				}
   416  				env.ZkTrieTracer[addrStr].Merge(zktrieTracer)
   417  			}
   418  			env.sMu.Unlock()
   419  		}
   420  	}
   421  
   422  	env.ExecutionResults[index] = &types.ExecutionResult{
   423  		From:           sender,
   424  		To:             receiver,
   425  		AccountCreated: createdAcc,
   426  		AccountsAfter:  after,
   427  		L1DataFee:      (*hexutil.Big)(result.L1DataFee),
   428  		Gas:            result.UsedGas,
   429  		Failed:         result.Failed(),
   430  		ReturnValue:    fmt.Sprintf("%x", returnVal),
   431  		StructLogs:     vm.FormatLogs(tracer.StructLogs()),
   432  	}
   433  	env.TxStorageTraces[index] = txStorageTrace
   434  
   435  	return nil
   436  }
   437  
   438  // fillBlockTrace content after all the txs are finished running.
   439  func (env *TraceEnv) fillBlockTrace(block *types.Block) (*types.BlockTrace, error) {
   440  	statedb := env.state
   441  
   442  	txs := make([]*types.TransactionData, block.Transactions().Len())
   443  	for i, tx := range block.Transactions() {
   444  		txs[i] = types.NewTransactionData(tx, block.NumberU64(), env.chainConfig)
   445  	}
   446  
   447  	intrinsicStorageProofs := map[common.Address][]common.Hash{
   448  		rcfg.L2MessageQueueAddress: {rcfg.WithdrawTrieRootSlot},
   449  		rcfg.L1GasPriceOracleAddress: {
   450  			rcfg.L1BaseFeeSlot,
   451  			rcfg.OverheadSlot,
   452  			rcfg.ScalarSlot,
   453  		},
   454  	}
   455  
   456  	for addr, storages := range intrinsicStorageProofs {
   457  		if _, existed := env.Proofs[addr.String()]; !existed {
   458  			if proof, err := statedb.GetProof(addr); err != nil {
   459  				log.Error("Proof for intrinstic address not available", "error", err, "address", addr)
   460  			} else {
   461  				env.Proofs[addr.String()] = types.WrapProof(proof)
   462  			}
   463  		}
   464  
   465  		if _, existed := env.StorageProofs[addr.String()]; !existed {
   466  			env.StorageProofs[addr.String()] = make(map[string][]hexutil.Bytes)
   467  		}
   468  
   469  		for _, slot := range storages {
   470  			if _, existed := env.StorageProofs[addr.String()][slot.String()]; !existed {
   471  				if trie, err := statedb.GetStorageTrieForProof(addr); err != nil {
   472  					log.Error("Storage proof for intrinstic address not available", "error", err, "address", addr)
   473  				} else if proof, _ := statedb.GetSecureTrieProof(trie, slot); err != nil {
   474  					log.Error("Get storage proof for intrinstic address failed", "error", err, "address", addr, "slot", slot)
   475  				} else {
   476  					env.StorageProofs[addr.String()][slot.String()] = types.WrapProof(proof)
   477  				}
   478  			}
   479  		}
   480  	}
   481  
   482  	var chainID uint64
   483  	if env.chainConfig.ChainID != nil {
   484  		chainID = env.chainConfig.ChainID.Uint64()
   485  	}
   486  	blockTrace := &types.BlockTrace{
   487  		ChainID: chainID,
   488  		Version: params.ArchiveVersion(params.CommitHash),
   489  		Coinbase: &types.AccountWrapper{
   490  			Address:          env.coinbase,
   491  			Nonce:            statedb.GetNonce(env.coinbase),
   492  			Balance:          (*hexutil.Big)(statedb.GetBalance(env.coinbase)),
   493  			KeccakCodeHash:   statedb.GetKeccakCodeHash(env.coinbase),
   494  			PoseidonCodeHash: statedb.GetPoseidonCodeHash(env.coinbase),
   495  			CodeSize:         statedb.GetCodeSize(env.coinbase),
   496  		},
   497  		Header:            block.Header(),
   498  		StorageTrace:      env.StorageTrace,
   499  		ExecutionResults:  env.ExecutionResults,
   500  		TxStorageTraces:   env.TxStorageTraces,
   501  		Transactions:      txs,
   502  		StartL1QueueIndex: env.StartL1QueueIndex,
   503  	}
   504  
   505  	for i, tx := range block.Transactions() {
   506  		evmTrace := env.ExecutionResults[i]
   507  		// Contract is created.
   508  		if tx.To() == nil {
   509  			evmTrace.ByteCode = hexutil.Encode(tx.Data())
   510  		} else { // contract call be included at this case, specially fallback call's data is empty.
   511  			evmTrace.ByteCode = hexutil.Encode(statedb.GetCode(*tx.To()))
   512  			// Get tx.to address's code hash.
   513  			codeHash := statedb.GetPoseidonCodeHash(*tx.To())
   514  			evmTrace.PoseidonCodeHash = &codeHash
   515  		}
   516  	}
   517  
   518  	// only zktrie model has the ability to get `mptwitness`.
   519  	if env.chainConfig.Scroll.ZktrieEnabled() {
   520  		// we use MPTWitnessNothing by default and do not allow switch among MPTWitnessType atm.
   521  		// MPTWitness will be removed from traces in the future.
   522  		if err := zkproof.FillBlockTraceForMPTWitness(zkproof.MPTWitnessNothing, blockTrace); err != nil {
   523  			log.Error("fill mpt witness fail", "error", err)
   524  		}
   525  	}
   526  
   527  	blockTrace.WithdrawTrieRoot = withdrawtrie.ReadWTRSlot(rcfg.L2MessageQueueAddress, env.state)
   528  
   529  	return blockTrace, nil
   530  }