mirror of
https://github.com/ethereum/go-ethereum.git
synced 2026-07-24 21:56:43 +00:00
zktrie part1: change storage proof from per step to per block (#102)
* change storage proof from step-wise to block-wise. Step-wise proof will be reconstructed in prover side * minor * minor * Update worker.go * purge hexInt * Update l2trace.go * Refactor l2witness/opt-storage-proof (#112) * rename GetStateData to GetLiveStateObject * revert EvmTxTraces type * rename GetLiveStateObject to GetLiveStateAccount * fix typo * some renamings * format codes * fix typo * fix typos * format codes some reverts some renamings some renamings format codes * update comments update comments * update comments update comments update comments * update comments update comments update comments * rename * rename * update * update comments Co-authored-by: maskpp <maskpp266@gmail.com> Co-authored-by: Ho Vei <noelwei@gmail.com> Co-authored-by: HAOYUatHZ <37070449+HAOYUatHZ@users.noreply.github.com>
This commit is contained in:
parent
35f6a91cd5
commit
d3bc8322dc
9 changed files with 334 additions and 217 deletions
1
.gitattributes
vendored
1
.gitattributes
vendored
|
|
@ -1,3 +1,4 @@
|
|||
# Auto detect text files and perform LF normalization
|
||||
* text=auto
|
||||
*.sol linguist-language=Solidity
|
||||
*.go text eol=lf
|
||||
|
|
|
|||
|
|
@ -1193,17 +1193,17 @@ func (bc *BlockChain) writeKnownBlock(block *types.Block) error {
|
|||
}
|
||||
|
||||
// WriteBlockWithState writes the block and all associated state to the database.
|
||||
func (bc *BlockChain) WriteBlockWithState(block *types.Block, receipts []*types.Receipt, logs []*types.Log, evmTraces []*types.ExecutionResult, state *state.StateDB, emitHeadEvent bool) (status WriteStatus, err error) {
|
||||
func (bc *BlockChain) WriteBlockWithState(block *types.Block, receipts []*types.Receipt, logs []*types.Log, evmTraces []*types.ExecutionResult, storageTrace *types.StorageTrace, state *state.StateDB, emitHeadEvent bool) (status WriteStatus, err error) {
|
||||
if !bc.chainmu.TryLock() {
|
||||
return NonStatTy, errInsertionInterrupted
|
||||
}
|
||||
defer bc.chainmu.Unlock()
|
||||
return bc.writeBlockWithState(block, receipts, logs, evmTraces, state, emitHeadEvent)
|
||||
return bc.writeBlockWithState(block, receipts, logs, evmTraces, storageTrace, state, emitHeadEvent)
|
||||
}
|
||||
|
||||
// writeBlockWithState writes the block and all associated state to the database,
|
||||
// but is expects the chain mutex to be held.
|
||||
func (bc *BlockChain) writeBlockWithState(block *types.Block, receipts []*types.Receipt, logs []*types.Log, evmTraces []*types.ExecutionResult, state *state.StateDB, emitHeadEvent bool) (status WriteStatus, err error) {
|
||||
func (bc *BlockChain) writeBlockWithState(block *types.Block, receipts []*types.Receipt, logs []*types.Log, evmTraces []*types.ExecutionResult, storageTrace *types.StorageTrace, state *state.StateDB, emitHeadEvent bool) (status WriteStatus, err error) {
|
||||
if bc.insertStopped() {
|
||||
return NonStatTy, errInsertionInterrupted
|
||||
}
|
||||
|
|
@ -1327,7 +1327,7 @@ func (bc *BlockChain) writeBlockWithState(block *types.Block, receipts []*types.
|
|||
// Fill blockResult content
|
||||
var blockResult *types.BlockResult
|
||||
if evmTraces != nil {
|
||||
blockResult = bc.writeBlockResult(state, block, evmTraces)
|
||||
blockResult = bc.writeBlockResult(state, block, evmTraces, storageTrace)
|
||||
bc.blockResultCache.Add(block.Hash(), blockResult)
|
||||
}
|
||||
|
||||
|
|
@ -1351,57 +1351,22 @@ func (bc *BlockChain) writeBlockWithState(block *types.Block, receipts []*types.
|
|||
}
|
||||
|
||||
// Fill blockResult content
|
||||
func (bc *BlockChain) writeBlockResult(state *state.StateDB, block *types.Block, evmTraces []*types.ExecutionResult) *types.BlockResult {
|
||||
func (bc *BlockChain) writeBlockResult(state *state.StateDB, block *types.Block, evmTraces []*types.ExecutionResult, storageTrace *types.StorageTrace) *types.BlockResult {
|
||||
blockResult := &types.BlockResult{
|
||||
ExecutionResults: evmTraces,
|
||||
StorageTrace: storageTrace,
|
||||
}
|
||||
coinbase := types.AccountProofWrapper{
|
||||
coinbase := types.AccountWrapper{
|
||||
Address: block.Coinbase(),
|
||||
Nonce: state.GetNonce(block.Coinbase()),
|
||||
Balance: (*hexutil.Big)(state.GetBalance(block.Coinbase())),
|
||||
CodeHash: state.GetCodeHash(block.Coinbase()),
|
||||
}
|
||||
// Get coinbase address's account proof.
|
||||
proof, err := state.GetProof(block.Coinbase())
|
||||
if err != nil {
|
||||
log.Error("Failed to get proof", "blockNumber", block.NumberU64(), "address", block.Coinbase().String(), "err", err)
|
||||
} else {
|
||||
coinbase.Proof = make([]string, len(proof))
|
||||
for i := range proof {
|
||||
coinbase.Proof[i] = hexutil.Encode(proof[i])
|
||||
}
|
||||
}
|
||||
|
||||
blockResult.BlockTrace = types.NewTraceBlock(bc.chainConfig, block, &coinbase)
|
||||
blockResult.StorageTrace.RootAfter = state.GetRootHash()
|
||||
for i, tx := range block.Transactions() {
|
||||
evmTrace := blockResult.ExecutionResults[i]
|
||||
|
||||
from := evmTrace.From.Address
|
||||
// Get proof
|
||||
proof, err := state.GetProof(from)
|
||||
if err != nil {
|
||||
log.Error("Failed to get proof", "blockNumber", block.NumberU64(), "address", from.String(), "err", err)
|
||||
} else {
|
||||
evmTrace.From.Proof = make([]string, len(proof))
|
||||
for i := range proof {
|
||||
evmTrace.From.Proof[i] = hexutil.Encode(proof[i])
|
||||
}
|
||||
}
|
||||
|
||||
if evmTrace.To != nil {
|
||||
to := evmTrace.To.Address
|
||||
// Get proof
|
||||
proof, err = state.GetProof(to)
|
||||
if err != nil {
|
||||
log.Error("Failed to get proof", "blockNumber", block.NumberU64(), "address", to.String(), "err", err)
|
||||
} else {
|
||||
evmTrace.To.Proof = make([]string, len(proof))
|
||||
for i := range proof {
|
||||
evmTrace.To.Proof[i] = hexutil.Encode(proof[i])
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Contract is called
|
||||
if len(tx.Data()) != 0 && tx.To() != nil {
|
||||
evmTrace.ByteCode = hexutil.Encode(state.GetCode(*tx.To()))
|
||||
|
|
@ -1727,8 +1692,8 @@ func (bc *BlockChain) insertChain(chain types.Blocks, verifySeals bool) (int, er
|
|||
|
||||
// Write the block to the chain and get the status.
|
||||
substart = time.Now()
|
||||
// EvmTraces is nil is safe because l2geth's p2p server is stoped and the code will not execute there.
|
||||
status, err := bc.writeBlockWithState(block, receipts, logs, nil, statedb, false)
|
||||
// EvmTraces & StorageTrace being nil is safe because l2geth's p2p server is stoped and the code will not execute there.
|
||||
status, err := bc.writeBlockWithState(block, receipts, logs, nil, nil, statedb, false)
|
||||
atomic.StoreUint32(&followupInterrupt, 1)
|
||||
if err != nil {
|
||||
return it.index, err
|
||||
|
|
|
|||
|
|
@ -325,6 +325,41 @@ func (s *StateDB) GetProofByHash(addrHash common.Hash) ([][]byte, error) {
|
|||
return proof, err
|
||||
}
|
||||
|
||||
func (s *StateDB) GetLiveStateAccount(addr common.Address) *types.StateAccount {
|
||||
obj, ok := s.stateObjects[addr]
|
||||
if !ok {
|
||||
return nil
|
||||
}
|
||||
return &obj.data
|
||||
}
|
||||
|
||||
func (s *StateDB) GetRootHash() common.Hash {
|
||||
return s.trie.Hash()
|
||||
}
|
||||
|
||||
// StorageTrieProof is not in Db interface and used explictily for reading proof in storage trie (not the dirty value)
|
||||
func (s *StateDB) GetStorageTrieProof(a common.Address, key common.Hash) ([][]byte, error) {
|
||||
// try the trie in stateObject first, else we would create one
|
||||
stateObject := s.getStateObject(a)
|
||||
if stateObject == nil {
|
||||
return nil, errors.New("storage trie for requested address does not exist")
|
||||
}
|
||||
|
||||
trie := stateObject.trie
|
||||
var err error
|
||||
if trie == nil {
|
||||
// use a new, temporary trie
|
||||
trie, err = s.db.OpenStorageTrie(stateObject.addrHash, stateObject.data.Root)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("can't create storage trie on root %s: %v ", stateObject.data.Root, err)
|
||||
}
|
||||
}
|
||||
|
||||
var proof proofList
|
||||
err = trie.Prove(crypto.Keccak256(key.Bytes()), 0, &proof)
|
||||
return proof, err
|
||||
}
|
||||
|
||||
// GetStorageProof returns the Merkle proof for given storage slot.
|
||||
func (s *StateDB) GetStorageProof(a common.Address, key common.Hash) ([][]byte, error) {
|
||||
var proof proofList
|
||||
|
|
|
|||
|
|
@ -23,9 +23,24 @@ var (
|
|||
// BlockResult contains block execution traces and results required for rollers.
|
||||
type BlockResult struct {
|
||||
BlockTrace *BlockTrace `json:"blockTrace"`
|
||||
StorageTrace *StorageTrace `json:"storageTrace"`
|
||||
ExecutionResults []*ExecutionResult `json:"executionResults"`
|
||||
}
|
||||
|
||||
// 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"`
|
||||
}
|
||||
|
||||
// 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
|
||||
|
|
@ -33,11 +48,19 @@ type ExecutionResult struct {
|
|||
Gas uint64 `json:"gas"`
|
||||
Failed bool `json:"failed"`
|
||||
ReturnValue string `json:"returnValue,omitempty"`
|
||||
// Sender's account proof.
|
||||
From *AccountProofWrapper `json:"from,omitempty"`
|
||||
// Receiver's account proof.
|
||||
To *AccountProofWrapper `json:"to,omitempty"`
|
||||
// It's exist only when tx is a contract call.
|
||||
// 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"`
|
||||
|
||||
// `CodeHash` only exists when tx is a contract call.
|
||||
CodeHash *common.Hash `json:"codeHash,omitempty"`
|
||||
// If it is a contract call, the contract code is returned.
|
||||
ByteCode string `json:"byteCode,omitempty"`
|
||||
|
|
@ -77,30 +100,35 @@ func NewStructLogResBasic(pc uint64, op string, gas, gasCost uint64, depth int,
|
|||
}
|
||||
|
||||
type ExtraData struct {
|
||||
// Indicate the call succeeds or not for CALL/CREATE op
|
||||
CallFailed bool `json:"callFailed,omitempty"`
|
||||
// CALL | CALLCODE | DELEGATECALL | STATICCALL: [tx.to address’s code, stack.nth_last(1) address’s code]
|
||||
CodeList [][]byte `json:"codeList,omitempty"`
|
||||
// SSTORE | SLOAD: [storageProof]
|
||||
// SELFDESTRUCT: [contract address’s accountProof, stack.nth_last(0) address’s accountProof]
|
||||
// SELFBALANCE: [contract address’s accountProof]
|
||||
// BALANCE | EXTCODEHASH: [stack.nth_last(0) address’s accountProof]
|
||||
// CREATE | CREATE2: [sender's accountProof, created contract address’s accountProof]
|
||||
// CALL | CALLCODE: [caller contract address’s accountProof, stack.nth_last(1) address’s accountProof]
|
||||
ProofList []*AccountProofWrapper `json:"proofList,omitempty"`
|
||||
// SELFDESTRUCT: [contract address’s account, stack.nth_last(0) address’s account]
|
||||
// SELFBALANCE: [contract address’s account]
|
||||
// BALANCE | EXTCODEHASH: [stack.nth_last(0) address’s account]
|
||||
// CREATE | CREATE2: [created contract address’s account (before constructed),
|
||||
// created contract address's account (after constructed)]
|
||||
// CALL | CALLCODE: [caller contract address’s account,
|
||||
// stack.nth_last(1) (i.e. callee) address’s account,
|
||||
// callee contract address's account (value updated, before called)]
|
||||
// STATICCALL: [stack.nth_last(1) (i.e. callee) address’s account,
|
||||
// callee contract address's account (before called)]
|
||||
StateList []*AccountWrapper `json:"proofList,omitempty"`
|
||||
}
|
||||
|
||||
type AccountProofWrapper struct {
|
||||
type AccountWrapper struct {
|
||||
Address common.Address `json:"address"`
|
||||
Nonce uint64 `json:"nonce"`
|
||||
Balance *hexutil.Big `json:"balance"`
|
||||
CodeHash common.Hash `json:"codeHash,omitempty"`
|
||||
Proof []string `json:"proof,omitempty"`
|
||||
Storage *StorageProofWrapper `json:"storage,omitempty"` // StorageProofWrapper can be empty if irrelated to storage operation
|
||||
Storage *StorageWrapper `json:"storage,omitempty"` // StorageWrapper can be empty if irrelated to storage operation
|
||||
}
|
||||
|
||||
// while key & value can also be retrieved from StructLogRes.Storage,
|
||||
// we still stored in here for roller's processing convenience.
|
||||
type StorageProofWrapper struct {
|
||||
type StorageWrapper struct {
|
||||
Key string `json:"key,omitempty"`
|
||||
Value string `json:"value,omitempty"`
|
||||
Proof []string `json:"proof,omitempty"`
|
||||
}
|
||||
|
|
|
|||
|
|
@ -14,7 +14,7 @@ type BlockTrace struct {
|
|||
GasLimit uint64 `json:"gasLimit"`
|
||||
Difficulty *hexutil.Big `json:"difficulty"`
|
||||
BaseFee *hexutil.Big `json:"baseFee"`
|
||||
Coinbase *AccountProofWrapper `json:"coinbase"`
|
||||
Coinbase *AccountWrapper `json:"coinbase"`
|
||||
Time uint64 `json:"time"`
|
||||
Transactions []*TransactionTrace `json:"transactions"`
|
||||
}
|
||||
|
|
@ -36,7 +36,7 @@ type TransactionTrace struct {
|
|||
}
|
||||
|
||||
// NewTraceBlock supports necessary fields for roller.
|
||||
func NewTraceBlock(config *params.ChainConfig, block *Block, coinbase *AccountProofWrapper) *BlockTrace {
|
||||
func NewTraceBlock(config *params.ChainConfig, block *Block, coinbase *AccountWrapper) *BlockTrace {
|
||||
txs := make([]*TransactionTrace, block.Transactions().Len())
|
||||
for i, tx := range block.Transactions() {
|
||||
txs[i] = newTraceTransaction(tx, block.NumberU64(), config)
|
||||
|
|
|
|||
|
|
@ -47,6 +47,8 @@ type StateDB interface {
|
|||
GetState(common.Address, common.Hash) common.Hash
|
||||
SetState(common.Address, common.Hash, common.Hash)
|
||||
|
||||
GetRootHash() common.Hash
|
||||
GetLiveStateAccount(addr common.Address) *types.StateAccount
|
||||
GetProof(addr common.Address) ([][]byte, error)
|
||||
GetProofByHash(addrHash common.Hash) ([][]byte, error)
|
||||
GetStorageProof(a common.Address, key common.Hash) ([][]byte, error)
|
||||
|
|
|
|||
|
|
@ -165,7 +165,11 @@ type StructLogger struct {
|
|||
cfg LogConfig
|
||||
env *EVM
|
||||
|
||||
statesAffected map[common.Address]struct{}
|
||||
storage map[common.Address]Storage
|
||||
createdAccount *types.AccountWrapper
|
||||
|
||||
callStackLogInd []int
|
||||
logs []StructLog
|
||||
output []byte
|
||||
err error
|
||||
|
|
@ -175,6 +179,7 @@ type StructLogger struct {
|
|||
func NewStructLogger(cfg *LogConfig) *StructLogger {
|
||||
logger := &StructLogger{
|
||||
storage: make(map[common.Address]Storage),
|
||||
statesAffected: make(map[common.Address]struct{}),
|
||||
}
|
||||
if cfg != nil {
|
||||
logger.cfg = *cfg
|
||||
|
|
@ -185,14 +190,30 @@ func NewStructLogger(cfg *LogConfig) *StructLogger {
|
|||
// Reset clears the data held by the logger.
|
||||
func (l *StructLogger) Reset() {
|
||||
l.storage = make(map[common.Address]Storage)
|
||||
l.statesAffected = make(map[common.Address]struct{})
|
||||
l.output = make([]byte, 0)
|
||||
l.logs = l.logs[:0]
|
||||
l.callStackLogInd = nil
|
||||
l.err = nil
|
||||
l.createdAccount = nil
|
||||
}
|
||||
|
||||
// CaptureStart implements the EVMLogger interface to initialize the tracing operation.
|
||||
func (l *StructLogger) CaptureStart(env *EVM, from common.Address, to common.Address, create bool, input []byte, gas uint64, value *big.Int) {
|
||||
func (l *StructLogger) CaptureStart(env *EVM, from common.Address, to common.Address, isCreate bool, input []byte, gas uint64, value *big.Int) {
|
||||
l.env = env
|
||||
|
||||
if isCreate {
|
||||
// notice codeHash is set AFTER CreateTx has exited, so here codeHash is still empty
|
||||
l.createdAccount = &types.AccountWrapper{
|
||||
Address: to,
|
||||
// nonce is 1 after EIP158, so we query it from stateDb
|
||||
Nonce: env.StateDB.GetNonce(to),
|
||||
Balance: (*hexutil.Big)(value),
|
||||
}
|
||||
}
|
||||
|
||||
l.statesAffected[from] = struct{}{}
|
||||
l.statesAffected[to] = struct{}{}
|
||||
}
|
||||
|
||||
// CaptureState logs a new structured log message and pushes it out to the environment
|
||||
|
|
@ -242,7 +263,7 @@ func (l *StructLogger) CaptureState(pc uint64, op OpCode, gas, cost uint64, scop
|
|||
l.storage[contractAddress][storageKey] = storageValue
|
||||
structlog.Storage = l.storage[contractAddress].Copy()
|
||||
|
||||
if err := traceStorageProof(l, scope, structlog.getOrInitExtraData()); err != nil {
|
||||
if err := traceStorage(l, scope, structlog.getOrInitExtraData()); err != nil {
|
||||
log.Error("Failed to trace data", "opcode", op.String(), "err", err)
|
||||
}
|
||||
}
|
||||
|
|
@ -264,36 +285,6 @@ func (l *StructLogger) CaptureState(pc uint64, op OpCode, gas, cost uint64, scop
|
|||
}
|
||||
|
||||
func (l *StructLogger) CaptureStateAfter(pc uint64, op OpCode, gas, cost uint64, scope *ScopeContext, rData []byte, depth int, err error) {
|
||||
if !l.cfg.DisableStorage && op == SSTORE {
|
||||
logLen := len(l.logs)
|
||||
if logLen <= 0 {
|
||||
log.Error("Failed to trace after_state for sstore", "err", "empty length log")
|
||||
return
|
||||
}
|
||||
|
||||
lastLog := l.logs[logLen-1]
|
||||
if lastLog.Op != SSTORE {
|
||||
log.Error("Failed to trace after_state for sstore", "err", "op mismatch")
|
||||
return
|
||||
}
|
||||
if lastLog.ExtraData == nil || len(lastLog.ExtraData.ProofList) == 0 {
|
||||
log.Error("Failed to trace after_state for sstore", "err", "empty before_state ExtraData")
|
||||
return
|
||||
}
|
||||
|
||||
contractAddress := scope.Contract.Address()
|
||||
if len(lastLog.Stack) <= 0 {
|
||||
log.Error("Failed to trace after_state for sstore", "err", "empty stack for last log")
|
||||
return
|
||||
}
|
||||
storageKey := common.Hash(lastLog.Stack[len(lastLog.Stack)-1].Bytes32())
|
||||
proof, err := getWrappedProofForStorage(l, contractAddress, storageKey)
|
||||
if err != nil {
|
||||
log.Error("Failed to trace after_state storage_proof for sstore", "err", err)
|
||||
}
|
||||
|
||||
l.logs[logLen-1].ExtraData.ProofList = append(lastLog.ExtraData.ProofList, proof)
|
||||
}
|
||||
}
|
||||
|
||||
// CaptureFault implements the EVMLogger interface to trace an execution fault
|
||||
|
|
@ -314,9 +305,68 @@ func (l *StructLogger) CaptureEnd(output []byte, gasUsed uint64, t time.Duration
|
|||
}
|
||||
|
||||
func (l *StructLogger) CaptureEnter(typ OpCode, from common.Address, to common.Address, input []byte, gas uint64, value *big.Int) {
|
||||
// the last logged op should be CALL/STATICCALL/CALLCODE/CREATE/CREATE2
|
||||
lastLogPos := len(l.logs) - 1
|
||||
log.Debug("mark call stack", "pos", lastLogPos, "op", l.logs[lastLogPos].Op)
|
||||
l.callStackLogInd = append(l.callStackLogInd, lastLogPos)
|
||||
// sanity check
|
||||
if len(l.callStackLogInd) != l.env.depth {
|
||||
panic("unexpected evm depth in capture enter")
|
||||
}
|
||||
l.statesAffected[to] = struct{}{}
|
||||
theLog := l.logs[lastLogPos]
|
||||
// handling additional updating for CALL/STATICCALL/CALLCODE/CREATE/CREATE2 only
|
||||
// append extraData part for the log, capture the account status (the nonce / balance has been updated in capture enter)
|
||||
wrappedStatus, _ := getWrappedAccountForAddr(l, to)
|
||||
theLog.ExtraData.StateList = append(theLog.ExtraData.StateList, wrappedStatus)
|
||||
}
|
||||
|
||||
func (l *StructLogger) CaptureExit(output []byte, gasUsed uint64, err error) {}
|
||||
// in CaptureExit phase, a CREATE has its target address's code being set and queryable
|
||||
func (l *StructLogger) CaptureExit(output []byte, gasUsed uint64, err error) {
|
||||
stackH := len(l.callStackLogInd)
|
||||
if stackH == 0 {
|
||||
panic("unexpected capture exit occur")
|
||||
}
|
||||
|
||||
theLogPos := l.callStackLogInd[stackH-1]
|
||||
l.callStackLogInd = l.callStackLogInd[:stackH-1]
|
||||
theLog := l.logs[theLogPos]
|
||||
// update "forecast" data
|
||||
if err != nil {
|
||||
theLog.ExtraData.CallFailed = true
|
||||
}
|
||||
|
||||
// handling updating for CREATE only
|
||||
switch theLog.Op {
|
||||
case CREATE, CREATE2:
|
||||
// append extraData part for the log whose op is CREATE(2), capture the account status (the codehash would be updated in capture exit)
|
||||
dataLen := len(theLog.ExtraData.StateList)
|
||||
if dataLen == 0 {
|
||||
panic("unexpected data capture for target op")
|
||||
}
|
||||
|
||||
lastAccData := theLog.ExtraData.StateList[dataLen-1]
|
||||
wrappedStatus, _ := getWrappedAccountForAddr(l, lastAccData.Address)
|
||||
theLog.ExtraData.StateList = append(theLog.ExtraData.StateList, wrappedStatus)
|
||||
default:
|
||||
//do nothing for other op code
|
||||
return
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// UpdatedAccounts is used to collect all "touched" accounts
|
||||
func (l *StructLogger) UpdatedAccounts() map[common.Address]struct{} {
|
||||
return l.statesAffected
|
||||
}
|
||||
|
||||
// UpdatedStorages is used to collect all "touched" storage slots
|
||||
func (l *StructLogger) UpdatedStorages() map[common.Address]Storage {
|
||||
return l.storage
|
||||
}
|
||||
|
||||
// CreatedAccount return the account data in case it is a create tx
|
||||
func (l *StructLogger) CreatedAccount() *types.AccountWrapper { return l.createdAccount }
|
||||
|
||||
// StructLogs returns the captured log entries.
|
||||
func (l *StructLogger) StructLogs() []StructLog { return l.logs }
|
||||
|
|
|
|||
|
|
@ -1,8 +1,6 @@
|
|||
package vm
|
||||
|
||||
import (
|
||||
"errors"
|
||||
|
||||
"github.com/scroll-tech/go-ethereum/common"
|
||||
"github.com/scroll-tech/go-ethereum/common/hexutil"
|
||||
"github.com/scroll-tech/go-ethereum/core/types"
|
||||
|
|
@ -13,18 +11,18 @@ type traceFunc func(l *StructLogger, scope *ScopeContext, extraData *types.Extra
|
|||
var (
|
||||
// OpcodeExecs the map to load opcodes' trace funcs.
|
||||
OpcodeExecs = map[OpCode][]traceFunc{
|
||||
CALL: {traceToAddressCode, traceLastNAddressCode(1), traceCallerProof, traceLastNAddressProof(1)},
|
||||
CALLCODE: {traceToAddressCode, traceLastNAddressCode(1), traceCallerProof, traceLastNAddressProof(1)},
|
||||
CALL: {traceToAddressCode, traceLastNAddressCode(1), traceCaller, traceLastNAddressAccount(1)},
|
||||
CALLCODE: {traceToAddressCode, traceLastNAddressCode(1), traceCaller, traceLastNAddressAccount(1)},
|
||||
DELEGATECALL: {traceToAddressCode, traceLastNAddressCode(1)},
|
||||
STATICCALL: {traceToAddressCode, traceLastNAddressCode(1)},
|
||||
CREATE: {traceCreatedContractProof}, // sender's wrapped_proof is already recorded in BlockChain.writeBlockResult
|
||||
CREATE2: {traceCreatedContractProof}, // sender's wrapped_proof is already recorded in BlockChain.writeBlockResult
|
||||
SLOAD: {}, // record storage_proof in `captureState` instead of here, to handle `l.cfg.DisableStorage` flag
|
||||
SSTORE: {}, // record storage_proof in `captureState` instead of here, to handle `l.cfg.DisableStorage` flag
|
||||
SELFDESTRUCT: {traceContractProof, traceLastNAddressProof(0)},
|
||||
SELFBALANCE: {traceContractProof},
|
||||
BALANCE: {traceLastNAddressProof(0)},
|
||||
EXTCODEHASH: {traceLastNAddressProof(0)},
|
||||
STATICCALL: {traceToAddressCode, traceLastNAddressCode(1), traceLastNAddressAccount(1)},
|
||||
CREATE: {}, // sender is already recorded in ExecutionResult, callee is recorded in CaptureEnter&CaptureExit
|
||||
CREATE2: {}, // sender is already recorded in ExecutionResult, callee is recorded in CaptureEnter&CaptureExit
|
||||
SLOAD: {}, // trace storage in `captureState` instead of here, to handle `l.cfg.DisableStorage` flag
|
||||
SSTORE: {}, // trace storage in `captureState` instead of here, to handle `l.cfg.DisableStorage` flag
|
||||
SELFDESTRUCT: {traceContractAccount, traceLastNAddressAccount(0)},
|
||||
SELFBALANCE: {traceContractAccount},
|
||||
BALANCE: {traceLastNAddressAccount(0)},
|
||||
EXTCODEHASH: {traceLastNAddressAccount(0)},
|
||||
}
|
||||
)
|
||||
|
||||
|
|
@ -52,49 +50,32 @@ func traceLastNAddressCode(n int) traceFunc {
|
|||
}
|
||||
}
|
||||
|
||||
// traceStorageProof get contract's storage proof at storage_address
|
||||
func traceStorageProof(l *StructLogger, scope *ScopeContext, extraData *types.ExtraData) error {
|
||||
// traceStorage get contract's storage at storage_address
|
||||
func traceStorage(l *StructLogger, scope *ScopeContext, extraData *types.ExtraData) error {
|
||||
if scope.Stack.len() == 0 {
|
||||
return nil
|
||||
}
|
||||
key := common.Hash(scope.Stack.peek().Bytes32())
|
||||
proof, err := getWrappedProofForStorage(l, scope.Contract.Address(), key)
|
||||
storage, err := getWrappedAccountForStorage(l, scope.Contract.Address(), key)
|
||||
if err == nil {
|
||||
extraData.ProofList = append(extraData.ProofList, proof)
|
||||
extraData.StateList = append(extraData.StateList, storage)
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
// traceContractProof gets the contract's account proof
|
||||
func traceContractProof(l *StructLogger, scope *ScopeContext, extraData *types.ExtraData) error {
|
||||
// Get account proof.
|
||||
proof, err := getWrappedProofForAddr(l, scope.Contract.Address())
|
||||
// traceContractAccount gets the contract's account
|
||||
func traceContractAccount(l *StructLogger, scope *ScopeContext, extraData *types.ExtraData) error {
|
||||
// Get account state.
|
||||
state, err := getWrappedAccountForAddr(l, scope.Contract.Address())
|
||||
if err == nil {
|
||||
extraData.ProofList = append(extraData.ProofList, proof)
|
||||
extraData.StateList = append(extraData.StateList, state)
|
||||
l.statesAffected[scope.Contract.Address()] = struct{}{}
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
/// traceCreatedContractProof get created contract address’s accountProof
|
||||
func traceCreatedContractProof(l *StructLogger, scope *ScopeContext, extraData *types.ExtraData) error {
|
||||
stack := scope.Stack
|
||||
if stack.len() < 1 {
|
||||
return nil
|
||||
}
|
||||
stackvalue := stack.peek()
|
||||
if stackvalue.IsZero() {
|
||||
return errors.New("can't get created contract address from stack")
|
||||
}
|
||||
address := common.BytesToAddress(stackvalue.Bytes())
|
||||
proof, err := getWrappedProofForAddr(l, address)
|
||||
if err == nil {
|
||||
extraData.ProofList = append(extraData.ProofList, proof)
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
// traceLastNAddressProof returns func about the last N's address proof.
|
||||
func traceLastNAddressProof(n int) traceFunc {
|
||||
// traceLastNAddressAccount returns func about the last N's address account.
|
||||
func traceLastNAddressAccount(n int) traceFunc {
|
||||
return func(l *StructLogger, scope *ScopeContext, extraData *types.ExtraData) error {
|
||||
stack := scope.Stack
|
||||
if stack.len() <= n {
|
||||
|
|
@ -102,72 +83,45 @@ func traceLastNAddressProof(n int) traceFunc {
|
|||
}
|
||||
|
||||
address := common.Address(stack.data[stack.len()-1-n].Bytes20())
|
||||
proof, err := getWrappedProofForAddr(l, address)
|
||||
state, err := getWrappedAccountForAddr(l, address)
|
||||
if err == nil {
|
||||
extraData.ProofList = append(extraData.ProofList, proof)
|
||||
extraData.StateList = append(extraData.StateList, state)
|
||||
l.statesAffected[scope.Contract.Address()] = struct{}{}
|
||||
}
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
// traceCallerProof gets caller address's proof.
|
||||
func traceCallerProof(l *StructLogger, scope *ScopeContext, extraData *types.ExtraData) error {
|
||||
// traceCaller gets caller address's account.
|
||||
func traceCaller(l *StructLogger, scope *ScopeContext, extraData *types.ExtraData) error {
|
||||
address := scope.Contract.CallerAddress
|
||||
proof, err := getWrappedProofForAddr(l, address)
|
||||
state, err := getWrappedAccountForAddr(l, address)
|
||||
if err == nil {
|
||||
extraData.ProofList = append(extraData.ProofList, proof)
|
||||
extraData.StateList = append(extraData.StateList, state)
|
||||
l.statesAffected[scope.Contract.Address()] = struct{}{}
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
// StorageProofWrapper will be empty
|
||||
func getWrappedProofForAddr(l *StructLogger, address common.Address) (*types.AccountProofWrapper, error) {
|
||||
proof, err := l.env.StateDB.GetProof(address)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &types.AccountProofWrapper{
|
||||
// StorageWrapper will be empty
|
||||
func getWrappedAccountForAddr(l *StructLogger, address common.Address) (*types.AccountWrapper, error) {
|
||||
return &types.AccountWrapper{
|
||||
Address: address,
|
||||
Nonce: l.env.StateDB.GetNonce(address),
|
||||
Balance: (*hexutil.Big)(l.env.StateDB.GetBalance(address)),
|
||||
CodeHash: l.env.StateDB.GetCodeHash(address),
|
||||
Proof: encodeProof(proof),
|
||||
}, nil
|
||||
}
|
||||
|
||||
func getWrappedProofForStorage(l *StructLogger, address common.Address, key common.Hash) (*types.AccountProofWrapper, error) {
|
||||
proof, err := l.env.StateDB.GetProof(address)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
storageProof, err := l.env.StateDB.GetStorageProof(address, key)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &types.AccountProofWrapper{
|
||||
func getWrappedAccountForStorage(l *StructLogger, address common.Address, key common.Hash) (*types.AccountWrapper, error) {
|
||||
return &types.AccountWrapper{
|
||||
Address: address,
|
||||
Nonce: l.env.StateDB.GetNonce(address),
|
||||
Balance: (*hexutil.Big)(l.env.StateDB.GetBalance(address)),
|
||||
CodeHash: l.env.StateDB.GetCodeHash(address),
|
||||
Proof: encodeProof(proof),
|
||||
Storage: &types.StorageProofWrapper{
|
||||
Storage: &types.StorageWrapper{
|
||||
Key: key.String(),
|
||||
Value: l.env.StateDB.GetState(address, key).String(),
|
||||
Proof: encodeProof(storageProof),
|
||||
},
|
||||
}, nil
|
||||
}
|
||||
|
||||
func encodeProof(proof [][]byte) []string {
|
||||
if len(proof) == 0 {
|
||||
return nil
|
||||
}
|
||||
res := make([]string, 0, len(proof))
|
||||
for _, node := range proof {
|
||||
res = append(res, hexutil.Encode(node))
|
||||
}
|
||||
return res
|
||||
}
|
||||
|
|
|
|||
|
|
@ -96,12 +96,15 @@ type environment struct {
|
|||
txs []*types.Transaction
|
||||
receipts []*types.Receipt
|
||||
executionResults []*types.ExecutionResult
|
||||
proofs map[string][]hexutil.Bytes
|
||||
storageProofs map[string]map[string][]hexutil.Bytes
|
||||
}
|
||||
|
||||
// task contains all information for consensus engine sealing and result submitting.
|
||||
type task struct {
|
||||
receipts []*types.Receipt
|
||||
executionResults []*types.ExecutionResult
|
||||
storageResults *types.StorageTrace
|
||||
state *state.StateDB
|
||||
block *types.Block
|
||||
createdAt time.Time
|
||||
|
|
@ -642,6 +645,7 @@ func (w *worker) resultLoop() {
|
|||
receipts = make([]*types.Receipt, len(task.receipts))
|
||||
evmTraces = make([]*types.ExecutionResult, len(task.executionResults))
|
||||
logs []*types.Log
|
||||
storageTrace = new(types.StorageTrace)
|
||||
)
|
||||
for i, taskReceipt := range task.receipts {
|
||||
receipt := new(types.Receipt)
|
||||
|
|
@ -651,6 +655,7 @@ func (w *worker) resultLoop() {
|
|||
evmTrace := new(types.ExecutionResult)
|
||||
evmTraces[i] = evmTrace
|
||||
*evmTrace = *task.executionResults[i]
|
||||
*storageTrace = *task.storageResults
|
||||
|
||||
// add block location fields
|
||||
receipt.BlockHash = hash
|
||||
|
|
@ -669,7 +674,7 @@ func (w *worker) resultLoop() {
|
|||
logs = append(logs, receipt.Logs...)
|
||||
}
|
||||
// Commit block and state to database.
|
||||
_, err := w.chain.WriteBlockWithState(block, receipts, logs, evmTraces, task.state, true)
|
||||
_, err := w.chain.WriteBlockWithState(block, receipts, logs, evmTraces, storageTrace, task.state, true)
|
||||
if err != nil {
|
||||
log.Error("Failed writing block to chain", "err", err)
|
||||
continue
|
||||
|
|
@ -706,6 +711,8 @@ func (w *worker) makeCurrent(parent *types.Block, header *types.Header) error {
|
|||
family: mapset.NewSet(),
|
||||
uncles: mapset.NewSet(),
|
||||
header: header,
|
||||
proofs: make(map[string][]hexutil.Bytes),
|
||||
storageProofs: make(map[string]map[string][]hexutil.Bytes),
|
||||
}
|
||||
// when 08 is processed ancestors contain 07 (quick block)
|
||||
for _, ancestor := range w.chain.GetBlocksFromHash(parent.Hash(), 7) {
|
||||
|
|
@ -788,17 +795,17 @@ func (w *worker) commitTransaction(tx *types.Transaction, coinbase common.Addres
|
|||
tracer.Reset()
|
||||
// Get sender's address.
|
||||
from, _ := types.Sender(w.current.signer, tx)
|
||||
sender := &types.AccountProofWrapper{
|
||||
sender := &types.AccountWrapper{
|
||||
Address: from,
|
||||
Nonce: w.current.state.GetNonce(from),
|
||||
Balance: (*hexutil.Big)(w.current.state.GetBalance(from)),
|
||||
CodeHash: w.current.state.GetCodeHash(from),
|
||||
}
|
||||
// Get receiver's address.
|
||||
var receiver *types.AccountProofWrapper
|
||||
var receiver *types.AccountWrapper
|
||||
if tx.To() != nil {
|
||||
to := *tx.To()
|
||||
receiver = &types.AccountProofWrapper{
|
||||
receiver = &types.AccountWrapper{
|
||||
Address: to,
|
||||
Nonce: w.current.state.GetNonce(to),
|
||||
Balance: (*hexutil.Big)(w.current.state.GetBalance(to)),
|
||||
|
|
@ -812,12 +819,80 @@ func (w *worker) commitTransaction(tx *types.Transaction, coinbase common.Addres
|
|||
return nil, err
|
||||
}
|
||||
|
||||
createdAcc := tracer.CreatedAccount()
|
||||
var after []*types.AccountWrapper
|
||||
to := tx.To()
|
||||
|
||||
if to == nil {
|
||||
if createdAcc == nil {
|
||||
panic("unexpected tx: address for created contract unavialable")
|
||||
}
|
||||
to = &createdAcc.Address
|
||||
}
|
||||
|
||||
// collect affected account after tx being applied
|
||||
for _, acc := range []*common.Address{&from, to} {
|
||||
after = append(after, &types.AccountWrapper{
|
||||
Address: *acc,
|
||||
Nonce: w.current.state.GetNonce(*acc),
|
||||
Balance: (*hexutil.Big)(w.current.state.GetBalance(*acc)),
|
||||
CodeHash: w.current.state.GetCodeHash(*acc),
|
||||
})
|
||||
}
|
||||
|
||||
// merge required proof data
|
||||
proofAccounts := tracer.UpdatedAccounts()
|
||||
for addr := range proofAccounts {
|
||||
addrStr := addr.String()
|
||||
if _, existed := w.current.proofs[addrStr]; !existed {
|
||||
proof, err := w.current.state.GetProof(addr)
|
||||
if err != nil {
|
||||
log.Error("Proof not available", "address", addrStr)
|
||||
// but we still mark the proofs map with nil array
|
||||
}
|
||||
wrappedProof := make([]hexutil.Bytes, len(proof))
|
||||
for _, bt := range proof {
|
||||
wrappedProof = append(wrappedProof, bt)
|
||||
}
|
||||
w.current.proofs[addrStr] = wrappedProof
|
||||
}
|
||||
}
|
||||
|
||||
proofStorages := tracer.UpdatedStorages()
|
||||
for addr, keys := range proofStorages {
|
||||
for key := range keys {
|
||||
addrStr := addr.String()
|
||||
m, existed := w.current.storageProofs[addrStr]
|
||||
if !existed {
|
||||
m = make(map[string][]hexutil.Bytes)
|
||||
w.current.storageProofs[addrStr] = m
|
||||
}
|
||||
|
||||
keyStr := key.String()
|
||||
if _, existed := m[keyStr]; !existed {
|
||||
proof, err := w.current.state.GetStorageTrieProof(addr, key)
|
||||
if err != nil {
|
||||
log.Error("Storage proof not available", "address", addrStr, "key", keyStr)
|
||||
// but we still mark the proofs map with nil array
|
||||
}
|
||||
wrappedProof := make([]hexutil.Bytes, len(proof))
|
||||
for _, bt := range proof {
|
||||
wrappedProof = append(wrappedProof, hexutil.Bytes(bt))
|
||||
}
|
||||
m[keyStr] = wrappedProof
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
w.current.txs = append(w.current.txs, tx)
|
||||
w.current.receipts = append(w.current.receipts, receipt)
|
||||
|
||||
w.current.executionResults = append(w.current.executionResults, &types.ExecutionResult{
|
||||
Gas: receipt.GasUsed,
|
||||
From: sender,
|
||||
To: receiver,
|
||||
AccountCreated: createdAcc,
|
||||
AccountsAfter: after,
|
||||
Failed: receipt.Status != types.ReceiptStatusSuccessful,
|
||||
ReturnValue: fmt.Sprintf("%x", receipt.ReturnValue),
|
||||
StructLogs: vm.FormatLogs(tracer.StructLogs()),
|
||||
|
|
@ -1077,6 +1152,13 @@ func (w *worker) commit(uncles []*types.Header, interval func(), update bool, st
|
|||
// Deep copy receipts here to avoid interaction between different tasks.
|
||||
receipts := copyReceipts(w.current.receipts)
|
||||
s := w.current.state.Copy()
|
||||
// complete storage before Finalize state (only RootAfter left unknown)
|
||||
storage := &types.StorageTrace{
|
||||
RootBefore: s.GetRootHash(),
|
||||
Proofs: w.current.proofs,
|
||||
StorageProofs: w.current.storageProofs,
|
||||
}
|
||||
|
||||
block, err := w.engine.FinalizeAndAssemble(w.chain, w.current.header, s, w.current.txs, uncles, receipts)
|
||||
if err != nil {
|
||||
return err
|
||||
|
|
@ -1086,7 +1168,7 @@ func (w *worker) commit(uncles []*types.Header, interval func(), update bool, st
|
|||
interval()
|
||||
}
|
||||
select {
|
||||
case w.taskCh <- &task{receipts: receipts, executionResults: w.current.executionResults, state: s, block: block, createdAt: time.Now()}:
|
||||
case w.taskCh <- &task{receipts: receipts, executionResults: w.current.executionResults, storageResults: storage, state: s, block: block, createdAt: time.Now()}:
|
||||
w.unconfirmed.Shift(block.NumberU64() - 1)
|
||||
log.Info("Commit new mining work", "number", block.Number(), "sealhash", w.engine.SealHash(block.Header()),
|
||||
"uncles", len(uncles), "txs", w.current.tcount,
|
||||
|
|
|
|||
Loading…
Reference in a new issue