add l2trace.go (#554)

* feat: add l2trace.go

* minor

minor

* clean up
This commit is contained in:
HAOYUatHZ 2023-11-08 16:29:19 +08:00 committed by GitHub
parent 76756cea1c
commit 261983dbdb
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23

203
core/types/l2trace.go Normal file
View file

@ -0,0 +1,203 @@
package types
import (
"encoding/json"
"math/big"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/common/hexutil"
"github.com/ethereum/go-ethereum/params"
)
// BlockTrace contains block execution traces and results required for rollers.
type BlockTrace struct {
ChainID uint64 `json:"chainID"`
Version string `json:"version"`
Coinbase *AccountWrapper `json:"coinbase"`
Header *Header `json:"header"`
Transactions []*TransactionData `json:"transactions"`
StorageTrace *StorageTrace `json:"storageTrace"`
TxStorageTraces []*StorageTrace `json:"txStorageTraces,omitempty"`
ExecutionResults []*ExecutionResult `json:"executionResults"`
MPTWitness *json.RawMessage `json:"mptwitness,omitempty"`
WithdrawTrieRoot common.Hash `json:"withdraw_trie_root,omitempty"`
StartL1QueueIndex uint64 `json:"startL1QueueIndex"`
}
// StorageTrace stores proofs of storage needed by storage circuit
type StorageTrace struct {
// Root hash before block execution:
RootBefore common.Hash `json:"rootBefore,omitempty"`
// Root hash after block execution, is nil if execution has failed
RootAfter common.Hash `json:"rootAfter,omitempty"`
// All proofs BEFORE execution, for accounts which would be used in tracing
Proofs map[string][]hexutil.Bytes `json:"proofs"`
// All storage proofs BEFORE execution
StorageProofs map[string]map[string][]hexutil.Bytes `json:"storageProofs,omitempty"`
// Node entries for deletion, no need to distinguish what it is from, just read them
// into the partial db
DeletionProofs []hexutil.Bytes `json:"deletionProofs,omitempty"`
}
// ExecutionResult groups all structured logs emitted by the EVM
// while replaying a transaction in debug mode as well as transaction
// execution status, the amount of gas used and the return value
type ExecutionResult struct {
L1DataFee *hexutil.Big `json:"l1DataFee,omitempty"`
Gas uint64 `json:"gas"`
Failed bool `json:"failed"`
ReturnValue string `json:"returnValue"`
// Sender's account state (before Tx)
From *AccountWrapper `json:"from,omitempty"`
// Receiver's account state (before Tx)
To *AccountWrapper `json:"to,omitempty"`
// AccountCreated record the account if the tx is "create"
// (for creating inside a contract, we just handle CREATE op)
AccountCreated *AccountWrapper `json:"accountCreated,omitempty"`
// Record all accounts' state which would be affected AFTER tx executed
// currently they are just `from` and `to` account
AccountsAfter []*AccountWrapper `json:"accountAfter"`
// `PoseidonCodeHash` only exists when tx is a contract call.
PoseidonCodeHash *common.Hash `json:"poseidonCodeHash,omitempty"`
// If it is a contract call, the contract code is returned.
ByteCode string `json:"byteCode,omitempty"`
StructLogs []*StructLogRes `json:"structLogs"`
}
// StructLogRes stores a structured log emitted by the EVM while replaying a
// transaction in debug mode
type StructLogRes struct {
Pc uint64 `json:"pc"`
Op string `json:"op"`
Gas uint64 `json:"gas"`
GasCost uint64 `json:"gasCost"`
Depth int `json:"depth"`
Error string `json:"error,omitempty"`
Stack []string `json:"stack,omitempty"`
Memory []string `json:"memory,omitempty"`
Storage map[string]string `json:"storage,omitempty"`
RefundCounter uint64 `json:"refund,omitempty"`
ExtraData *ExtraData `json:"extraData,omitempty"`
}
// NewStructLogResBasic Basic StructLogRes skeleton, Stack&Memory&Storage&ExtraData are separated from it for GC optimization;
// still need to fill in with Stack&Memory&Storage&ExtraData
func NewStructLogResBasic(pc uint64, op string, gas, gasCost uint64, depth int, refundCounter uint64, err error) *StructLogRes {
logRes := &StructLogRes{
Pc: pc,
Op: op,
Gas: gas,
GasCost: gasCost,
Depth: depth,
RefundCounter: refundCounter,
}
if err != nil {
logRes.Error = err.Error()
}
return logRes
}
type ExtraData struct {
// Indicate the call succeeds or not for CALL/CREATE op
CallFailed bool `json:"callFailed,omitempty"`
// CALL | CALLCODE | DELEGATECALL | STATICCALL: [tx.to addresss code, stack.nth_last(1) addresss code]
// CREATE | CREATE2: [created contracts code]
// CODESIZE | CODECOPY: [contracts code]
// EXTCODESIZE | EXTCODECOPY: [stack.nth_last(0) addresss code]
CodeList []string `json:"codeList,omitempty"`
// SSTORE | SLOAD: [storageProof]
// SELFDESTRUCT: [contract addresss account, stack.nth_last(0) addresss account]
// SELFBALANCE: [contract addresss account]
// BALANCE | EXTCODEHASH: [stack.nth_last(0) addresss account]
// CREATE | CREATE2: [created contract addresss account (before constructed),
// created contract address's account (after constructed)]
// CALL | CALLCODE: [caller contract addresss account,
// stack.nth_last(1) (i.e. callee) addresss account,
// callee contract address's account (value updated, before called)]
// STATICCALL: [stack.nth_last(1) (i.e. callee) addresss account,
// callee contract address's account (before called)]
StateList []*AccountWrapper `json:"proofList,omitempty"`
// The status of caller, it would be captured twice:
// 1. before execution and 2. updated in CaptureEnter (for CALL/CALLCODE it duplicated with StateList[0])
Caller []*AccountWrapper `json:"caller,omitempty"`
}
type AccountWrapper struct {
Address common.Address `json:"address"`
Nonce uint64 `json:"nonce"`
Balance *hexutil.Big `json:"balance"`
KeccakCodeHash common.Hash `json:"keccakCodeHash,omitempty"`
PoseidonCodeHash common.Hash `json:"poseidonCodeHash,omitempty"`
CodeSize uint64 `json:"codeSize"`
Storage *StorageWrapper `json:"storage,omitempty"` // StorageWrapper can be empty if irrelated to storage operation
}
// StorageWrapper while key & value can also be retrieved from StructLogRes.Storage,
// we still stored in here for roller's processing convenience.
type StorageWrapper struct {
Key string `json:"key,omitempty"`
Value string `json:"value,omitempty"`
}
type TransactionData struct {
Type uint8 `json:"type"`
Nonce uint64 `json:"nonce"`
TxHash string `json:"txHash"`
Gas uint64 `json:"gas"`
GasPrice *hexutil.Big `json:"gasPrice"`
From common.Address `json:"from"`
To *common.Address `json:"to"`
ChainId *hexutil.Big `json:"chainId"`
Value *hexutil.Big `json:"value"`
Data string `json:"data"`
IsCreate bool `json:"isCreate"`
V *hexutil.Big `json:"v"`
R *hexutil.Big `json:"r"`
S *hexutil.Big `json:"s"`
}
// NewTransactionData returns a transaction that will serialize to the trace
// representation, with the given location metadata set (if available).
func NewTransactionData(tx *Transaction, blockNumber uint64, blockTime uint64, config *params.ChainConfig) *TransactionData {
signer := MakeSigner(config, big.NewInt(0).SetUint64(blockNumber), blockTime)
from, _ := Sender(signer, tx)
v, r, s := tx.RawSignatureValues()
nonce := tx.Nonce()
if tx.IsL1MessageTx() {
nonce = tx.L1MessageQueueIndex()
}
result := &TransactionData{
Type: tx.Type(),
TxHash: tx.Hash().String(),
Nonce: nonce,
ChainId: (*hexutil.Big)(tx.ChainId()),
From: from,
Gas: tx.Gas(),
GasPrice: (*hexutil.Big)(tx.GasPrice()),
To: tx.To(),
Value: (*hexutil.Big)(tx.Value()),
Data: hexutil.Encode(tx.Data()),
IsCreate: tx.To() == nil,
V: (*hexutil.Big)(v),
R: (*hexutil.Big)(r),
S: (*hexutil.Big)(s),
}
return result
}
// WrapProof turn the bytes array into proof type (array of hexutil.Bytes)
func WrapProof(proofBytes [][]byte) (wrappedProof []hexutil.Bytes) {
wrappedProof = make([]hexutil.Bytes, len(proofBytes))
for i, bt := range proofBytes {
wrappedProof[i] = bt
}
return
}