mirror of
https://github.com/ethereum/go-ethereum.git
synced 2026-07-25 06:06:44 +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
|
# Auto detect text files and perform LF normalization
|
||||||
* text=auto
|
* text=auto
|
||||||
*.sol linguist-language=Solidity
|
*.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.
|
// 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() {
|
if !bc.chainmu.TryLock() {
|
||||||
return NonStatTy, errInsertionInterrupted
|
return NonStatTy, errInsertionInterrupted
|
||||||
}
|
}
|
||||||
defer bc.chainmu.Unlock()
|
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,
|
// writeBlockWithState writes the block and all associated state to the database,
|
||||||
// but is expects the chain mutex to be held.
|
// 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() {
|
if bc.insertStopped() {
|
||||||
return NonStatTy, errInsertionInterrupted
|
return NonStatTy, errInsertionInterrupted
|
||||||
}
|
}
|
||||||
|
|
@ -1327,7 +1327,7 @@ func (bc *BlockChain) writeBlockWithState(block *types.Block, receipts []*types.
|
||||||
// Fill blockResult content
|
// Fill blockResult content
|
||||||
var blockResult *types.BlockResult
|
var blockResult *types.BlockResult
|
||||||
if evmTraces != nil {
|
if evmTraces != nil {
|
||||||
blockResult = bc.writeBlockResult(state, block, evmTraces)
|
blockResult = bc.writeBlockResult(state, block, evmTraces, storageTrace)
|
||||||
bc.blockResultCache.Add(block.Hash(), blockResult)
|
bc.blockResultCache.Add(block.Hash(), blockResult)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -1351,57 +1351,22 @@ func (bc *BlockChain) writeBlockWithState(block *types.Block, receipts []*types.
|
||||||
}
|
}
|
||||||
|
|
||||||
// Fill blockResult content
|
// 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{
|
blockResult := &types.BlockResult{
|
||||||
ExecutionResults: evmTraces,
|
ExecutionResults: evmTraces,
|
||||||
|
StorageTrace: storageTrace,
|
||||||
}
|
}
|
||||||
coinbase := types.AccountProofWrapper{
|
coinbase := types.AccountWrapper{
|
||||||
Address: block.Coinbase(),
|
Address: block.Coinbase(),
|
||||||
Nonce: state.GetNonce(block.Coinbase()),
|
Nonce: state.GetNonce(block.Coinbase()),
|
||||||
Balance: (*hexutil.Big)(state.GetBalance(block.Coinbase())),
|
Balance: (*hexutil.Big)(state.GetBalance(block.Coinbase())),
|
||||||
CodeHash: state.GetCodeHash(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.BlockTrace = types.NewTraceBlock(bc.chainConfig, block, &coinbase)
|
||||||
|
blockResult.StorageTrace.RootAfter = state.GetRootHash()
|
||||||
for i, tx := range block.Transactions() {
|
for i, tx := range block.Transactions() {
|
||||||
evmTrace := blockResult.ExecutionResults[i]
|
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
|
// Contract is called
|
||||||
if len(tx.Data()) != 0 && tx.To() != nil {
|
if len(tx.Data()) != 0 && tx.To() != nil {
|
||||||
evmTrace.ByteCode = hexutil.Encode(state.GetCode(*tx.To()))
|
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.
|
// Write the block to the chain and get the status.
|
||||||
substart = time.Now()
|
substart = time.Now()
|
||||||
// EvmTraces is nil is safe because l2geth's p2p server is stoped and the code will not execute there.
|
// 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, statedb, false)
|
status, err := bc.writeBlockWithState(block, receipts, logs, nil, nil, statedb, false)
|
||||||
atomic.StoreUint32(&followupInterrupt, 1)
|
atomic.StoreUint32(&followupInterrupt, 1)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return it.index, err
|
return it.index, err
|
||||||
|
|
|
||||||
|
|
@ -325,6 +325,41 @@ func (s *StateDB) GetProofByHash(addrHash common.Hash) ([][]byte, error) {
|
||||||
return proof, err
|
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.
|
// GetStorageProof returns the Merkle proof for given storage slot.
|
||||||
func (s *StateDB) GetStorageProof(a common.Address, key common.Hash) ([][]byte, error) {
|
func (s *StateDB) GetStorageProof(a common.Address, key common.Hash) ([][]byte, error) {
|
||||||
var proof proofList
|
var proof proofList
|
||||||
|
|
|
||||||
|
|
@ -23,9 +23,24 @@ var (
|
||||||
// BlockResult contains block execution traces and results required for rollers.
|
// BlockResult contains block execution traces and results required for rollers.
|
||||||
type BlockResult struct {
|
type BlockResult struct {
|
||||||
BlockTrace *BlockTrace `json:"blockTrace"`
|
BlockTrace *BlockTrace `json:"blockTrace"`
|
||||||
|
StorageTrace *StorageTrace `json:"storageTrace"`
|
||||||
ExecutionResults []*ExecutionResult `json:"executionResults"`
|
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
|
// ExecutionResult groups all structured logs emitted by the EVM
|
||||||
// while replaying a transaction in debug mode as well as transaction
|
// while replaying a transaction in debug mode as well as transaction
|
||||||
// execution status, the amount of gas used and the return value
|
// execution status, the amount of gas used and the return value
|
||||||
|
|
@ -33,11 +48,19 @@ type ExecutionResult struct {
|
||||||
Gas uint64 `json:"gas"`
|
Gas uint64 `json:"gas"`
|
||||||
Failed bool `json:"failed"`
|
Failed bool `json:"failed"`
|
||||||
ReturnValue string `json:"returnValue,omitempty"`
|
ReturnValue string `json:"returnValue,omitempty"`
|
||||||
// Sender's account proof.
|
// Sender's account state (before Tx)
|
||||||
From *AccountProofWrapper `json:"from,omitempty"`
|
From *AccountWrapper `json:"from,omitempty"`
|
||||||
// Receiver's account proof.
|
// Receiver's account state (before Tx)
|
||||||
To *AccountProofWrapper `json:"to,omitempty"`
|
To *AccountWrapper `json:"to,omitempty"`
|
||||||
// It's exist only when tx is a contract call.
|
// 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"`
|
CodeHash *common.Hash `json:"codeHash,omitempty"`
|
||||||
// If it is a contract call, the contract code is returned.
|
// If it is a contract call, the contract code is returned.
|
||||||
ByteCode string `json:"byteCode,omitempty"`
|
ByteCode string `json:"byteCode,omitempty"`
|
||||||
|
|
@ -77,30 +100,35 @@ func NewStructLogResBasic(pc uint64, op string, gas, gasCost uint64, depth int,
|
||||||
}
|
}
|
||||||
|
|
||||||
type ExtraData struct {
|
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]
|
// CALL | CALLCODE | DELEGATECALL | STATICCALL: [tx.to address’s code, stack.nth_last(1) address’s code]
|
||||||
CodeList [][]byte `json:"codeList,omitempty"`
|
CodeList [][]byte `json:"codeList,omitempty"`
|
||||||
// SSTORE | SLOAD: [storageProof]
|
// SSTORE | SLOAD: [storageProof]
|
||||||
// SELFDESTRUCT: [contract address’s accountProof, stack.nth_last(0) address’s accountProof]
|
// SELFDESTRUCT: [contract address’s account, stack.nth_last(0) address’s account]
|
||||||
// SELFBALANCE: [contract address’s accountProof]
|
// SELFBALANCE: [contract address’s account]
|
||||||
// BALANCE | EXTCODEHASH: [stack.nth_last(0) address’s accountProof]
|
// BALANCE | EXTCODEHASH: [stack.nth_last(0) address’s account]
|
||||||
// CREATE | CREATE2: [sender's accountProof, created contract address’s accountProof]
|
// CREATE | CREATE2: [created contract address’s account (before constructed),
|
||||||
// CALL | CALLCODE: [caller contract address’s accountProof, stack.nth_last(1) address’s accountProof]
|
// created contract address's account (after constructed)]
|
||||||
ProofList []*AccountProofWrapper `json:"proofList,omitempty"`
|
// 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"`
|
Address common.Address `json:"address"`
|
||||||
Nonce uint64 `json:"nonce"`
|
Nonce uint64 `json:"nonce"`
|
||||||
Balance *hexutil.Big `json:"balance"`
|
Balance *hexutil.Big `json:"balance"`
|
||||||
CodeHash common.Hash `json:"codeHash,omitempty"`
|
CodeHash common.Hash `json:"codeHash,omitempty"`
|
||||||
Proof []string `json:"proof,omitempty"`
|
Storage *StorageWrapper `json:"storage,omitempty"` // StorageWrapper can be empty if irrelated to storage operation
|
||||||
Storage *StorageProofWrapper `json:"storage,omitempty"` // StorageProofWrapper can be empty if irrelated to storage operation
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// while key & value can also be retrieved from StructLogRes.Storage,
|
// while key & value can also be retrieved from StructLogRes.Storage,
|
||||||
// we still stored in here for roller's processing convenience.
|
// we still stored in here for roller's processing convenience.
|
||||||
type StorageProofWrapper struct {
|
type StorageWrapper struct {
|
||||||
Key string `json:"key,omitempty"`
|
Key string `json:"key,omitempty"`
|
||||||
Value string `json:"value,omitempty"`
|
Value string `json:"value,omitempty"`
|
||||||
Proof []string `json:"proof,omitempty"`
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -9,14 +9,14 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
type BlockTrace struct {
|
type BlockTrace struct {
|
||||||
Number *hexutil.Big `json:"number"`
|
Number *hexutil.Big `json:"number"`
|
||||||
Hash common.Hash `json:"hash"`
|
Hash common.Hash `json:"hash"`
|
||||||
GasLimit uint64 `json:"gasLimit"`
|
GasLimit uint64 `json:"gasLimit"`
|
||||||
Difficulty *hexutil.Big `json:"difficulty"`
|
Difficulty *hexutil.Big `json:"difficulty"`
|
||||||
BaseFee *hexutil.Big `json:"baseFee"`
|
BaseFee *hexutil.Big `json:"baseFee"`
|
||||||
Coinbase *AccountProofWrapper `json:"coinbase"`
|
Coinbase *AccountWrapper `json:"coinbase"`
|
||||||
Time uint64 `json:"time"`
|
Time uint64 `json:"time"`
|
||||||
Transactions []*TransactionTrace `json:"transactions"`
|
Transactions []*TransactionTrace `json:"transactions"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type TransactionTrace struct {
|
type TransactionTrace struct {
|
||||||
|
|
@ -36,7 +36,7 @@ type TransactionTrace struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewTraceBlock supports necessary fields for roller.
|
// 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())
|
txs := make([]*TransactionTrace, block.Transactions().Len())
|
||||||
for i, tx := range block.Transactions() {
|
for i, tx := range block.Transactions() {
|
||||||
txs[i] = newTraceTransaction(tx, block.NumberU64(), config)
|
txs[i] = newTraceTransaction(tx, block.NumberU64(), config)
|
||||||
|
|
|
||||||
|
|
@ -47,6 +47,8 @@ type StateDB interface {
|
||||||
GetState(common.Address, common.Hash) common.Hash
|
GetState(common.Address, common.Hash) common.Hash
|
||||||
SetState(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)
|
GetProof(addr common.Address) ([][]byte, error)
|
||||||
GetProofByHash(addrHash common.Hash) ([][]byte, error)
|
GetProofByHash(addrHash common.Hash) ([][]byte, error)
|
||||||
GetStorageProof(a common.Address, key common.Hash) ([][]byte, error)
|
GetStorageProof(a common.Address, key common.Hash) ([][]byte, error)
|
||||||
|
|
|
||||||
|
|
@ -165,16 +165,21 @@ type StructLogger struct {
|
||||||
cfg LogConfig
|
cfg LogConfig
|
||||||
env *EVM
|
env *EVM
|
||||||
|
|
||||||
storage map[common.Address]Storage
|
statesAffected map[common.Address]struct{}
|
||||||
logs []StructLog
|
storage map[common.Address]Storage
|
||||||
output []byte
|
createdAccount *types.AccountWrapper
|
||||||
err error
|
|
||||||
|
callStackLogInd []int
|
||||||
|
logs []StructLog
|
||||||
|
output []byte
|
||||||
|
err error
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewStructLogger returns a new logger
|
// NewStructLogger returns a new logger
|
||||||
func NewStructLogger(cfg *LogConfig) *StructLogger {
|
func NewStructLogger(cfg *LogConfig) *StructLogger {
|
||||||
logger := &StructLogger{
|
logger := &StructLogger{
|
||||||
storage: make(map[common.Address]Storage),
|
storage: make(map[common.Address]Storage),
|
||||||
|
statesAffected: make(map[common.Address]struct{}),
|
||||||
}
|
}
|
||||||
if cfg != nil {
|
if cfg != nil {
|
||||||
logger.cfg = *cfg
|
logger.cfg = *cfg
|
||||||
|
|
@ -185,14 +190,30 @@ func NewStructLogger(cfg *LogConfig) *StructLogger {
|
||||||
// Reset clears the data held by the logger.
|
// Reset clears the data held by the logger.
|
||||||
func (l *StructLogger) Reset() {
|
func (l *StructLogger) Reset() {
|
||||||
l.storage = make(map[common.Address]Storage)
|
l.storage = make(map[common.Address]Storage)
|
||||||
|
l.statesAffected = make(map[common.Address]struct{})
|
||||||
l.output = make([]byte, 0)
|
l.output = make([]byte, 0)
|
||||||
l.logs = l.logs[:0]
|
l.logs = l.logs[:0]
|
||||||
|
l.callStackLogInd = nil
|
||||||
l.err = nil
|
l.err = nil
|
||||||
|
l.createdAccount = nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// CaptureStart implements the EVMLogger interface to initialize the tracing operation.
|
// 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
|
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
|
// 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
|
l.storage[contractAddress][storageKey] = storageValue
|
||||||
structlog.Storage = l.storage[contractAddress].Copy()
|
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)
|
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) {
|
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
|
// 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) {
|
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.
|
// StructLogs returns the captured log entries.
|
||||||
func (l *StructLogger) StructLogs() []StructLog { return l.logs }
|
func (l *StructLogger) StructLogs() []StructLog { return l.logs }
|
||||||
|
|
|
||||||
|
|
@ -1,8 +1,6 @@
|
||||||
package vm
|
package vm
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"errors"
|
|
||||||
|
|
||||||
"github.com/scroll-tech/go-ethereum/common"
|
"github.com/scroll-tech/go-ethereum/common"
|
||||||
"github.com/scroll-tech/go-ethereum/common/hexutil"
|
"github.com/scroll-tech/go-ethereum/common/hexutil"
|
||||||
"github.com/scroll-tech/go-ethereum/core/types"
|
"github.com/scroll-tech/go-ethereum/core/types"
|
||||||
|
|
@ -13,18 +11,18 @@ type traceFunc func(l *StructLogger, scope *ScopeContext, extraData *types.Extra
|
||||||
var (
|
var (
|
||||||
// OpcodeExecs the map to load opcodes' trace funcs.
|
// OpcodeExecs the map to load opcodes' trace funcs.
|
||||||
OpcodeExecs = map[OpCode][]traceFunc{
|
OpcodeExecs = map[OpCode][]traceFunc{
|
||||||
CALL: {traceToAddressCode, traceLastNAddressCode(1), traceCallerProof, traceLastNAddressProof(1)},
|
CALL: {traceToAddressCode, traceLastNAddressCode(1), traceCaller, traceLastNAddressAccount(1)},
|
||||||
CALLCODE: {traceToAddressCode, traceLastNAddressCode(1), traceCallerProof, traceLastNAddressProof(1)},
|
CALLCODE: {traceToAddressCode, traceLastNAddressCode(1), traceCaller, traceLastNAddressAccount(1)},
|
||||||
DELEGATECALL: {traceToAddressCode, traceLastNAddressCode(1)},
|
DELEGATECALL: {traceToAddressCode, traceLastNAddressCode(1)},
|
||||||
STATICCALL: {traceToAddressCode, traceLastNAddressCode(1)},
|
STATICCALL: {traceToAddressCode, traceLastNAddressCode(1), traceLastNAddressAccount(1)},
|
||||||
CREATE: {traceCreatedContractProof}, // sender's wrapped_proof is already recorded in BlockChain.writeBlockResult
|
CREATE: {}, // sender is already recorded in ExecutionResult, callee is recorded in CaptureEnter&CaptureExit
|
||||||
CREATE2: {traceCreatedContractProof}, // sender's wrapped_proof is already recorded in BlockChain.writeBlockResult
|
CREATE2: {}, // sender is already recorded in ExecutionResult, callee is recorded in CaptureEnter&CaptureExit
|
||||||
SLOAD: {}, // record storage_proof in `captureState` instead of here, to handle `l.cfg.DisableStorage` flag
|
SLOAD: {}, // trace storage 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
|
SSTORE: {}, // trace storage in `captureState` instead of here, to handle `l.cfg.DisableStorage` flag
|
||||||
SELFDESTRUCT: {traceContractProof, traceLastNAddressProof(0)},
|
SELFDESTRUCT: {traceContractAccount, traceLastNAddressAccount(0)},
|
||||||
SELFBALANCE: {traceContractProof},
|
SELFBALANCE: {traceContractAccount},
|
||||||
BALANCE: {traceLastNAddressProof(0)},
|
BALANCE: {traceLastNAddressAccount(0)},
|
||||||
EXTCODEHASH: {traceLastNAddressProof(0)},
|
EXTCODEHASH: {traceLastNAddressAccount(0)},
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
@ -52,49 +50,32 @@ func traceLastNAddressCode(n int) traceFunc {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// traceStorageProof get contract's storage proof at storage_address
|
// traceStorage get contract's storage at storage_address
|
||||||
func traceStorageProof(l *StructLogger, scope *ScopeContext, extraData *types.ExtraData) error {
|
func traceStorage(l *StructLogger, scope *ScopeContext, extraData *types.ExtraData) error {
|
||||||
if scope.Stack.len() == 0 {
|
if scope.Stack.len() == 0 {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
key := common.Hash(scope.Stack.peek().Bytes32())
|
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 {
|
if err == nil {
|
||||||
extraData.ProofList = append(extraData.ProofList, proof)
|
extraData.StateList = append(extraData.StateList, storage)
|
||||||
}
|
}
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
// traceContractProof gets the contract's account proof
|
// traceContractAccount gets the contract's account
|
||||||
func traceContractProof(l *StructLogger, scope *ScopeContext, extraData *types.ExtraData) error {
|
func traceContractAccount(l *StructLogger, scope *ScopeContext, extraData *types.ExtraData) error {
|
||||||
// Get account proof.
|
// Get account state.
|
||||||
proof, err := getWrappedProofForAddr(l, scope.Contract.Address())
|
state, err := getWrappedAccountForAddr(l, scope.Contract.Address())
|
||||||
if err == nil {
|
if err == nil {
|
||||||
extraData.ProofList = append(extraData.ProofList, proof)
|
extraData.StateList = append(extraData.StateList, state)
|
||||||
|
l.statesAffected[scope.Contract.Address()] = struct{}{}
|
||||||
}
|
}
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
/// traceCreatedContractProof get created contract address’s accountProof
|
// traceLastNAddressAccount returns func about the last N's address account.
|
||||||
func traceCreatedContractProof(l *StructLogger, scope *ScopeContext, extraData *types.ExtraData) error {
|
func traceLastNAddressAccount(n int) traceFunc {
|
||||||
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 {
|
|
||||||
return func(l *StructLogger, scope *ScopeContext, extraData *types.ExtraData) error {
|
return func(l *StructLogger, scope *ScopeContext, extraData *types.ExtraData) error {
|
||||||
stack := scope.Stack
|
stack := scope.Stack
|
||||||
if stack.len() <= n {
|
if stack.len() <= n {
|
||||||
|
|
@ -102,72 +83,45 @@ func traceLastNAddressProof(n int) traceFunc {
|
||||||
}
|
}
|
||||||
|
|
||||||
address := common.Address(stack.data[stack.len()-1-n].Bytes20())
|
address := common.Address(stack.data[stack.len()-1-n].Bytes20())
|
||||||
proof, err := getWrappedProofForAddr(l, address)
|
state, err := getWrappedAccountForAddr(l, address)
|
||||||
if err == nil {
|
if err == nil {
|
||||||
extraData.ProofList = append(extraData.ProofList, proof)
|
extraData.StateList = append(extraData.StateList, state)
|
||||||
|
l.statesAffected[scope.Contract.Address()] = struct{}{}
|
||||||
}
|
}
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// traceCallerProof gets caller address's proof.
|
// traceCaller gets caller address's account.
|
||||||
func traceCallerProof(l *StructLogger, scope *ScopeContext, extraData *types.ExtraData) error {
|
func traceCaller(l *StructLogger, scope *ScopeContext, extraData *types.ExtraData) error {
|
||||||
address := scope.Contract.CallerAddress
|
address := scope.Contract.CallerAddress
|
||||||
proof, err := getWrappedProofForAddr(l, address)
|
state, err := getWrappedAccountForAddr(l, address)
|
||||||
if err == nil {
|
if err == nil {
|
||||||
extraData.ProofList = append(extraData.ProofList, proof)
|
extraData.StateList = append(extraData.StateList, state)
|
||||||
|
l.statesAffected[scope.Contract.Address()] = struct{}{}
|
||||||
}
|
}
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
// StorageProofWrapper will be empty
|
// StorageWrapper will be empty
|
||||||
func getWrappedProofForAddr(l *StructLogger, address common.Address) (*types.AccountProofWrapper, error) {
|
func getWrappedAccountForAddr(l *StructLogger, address common.Address) (*types.AccountWrapper, error) {
|
||||||
proof, err := l.env.StateDB.GetProof(address)
|
return &types.AccountWrapper{
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
return &types.AccountProofWrapper{
|
|
||||||
Address: address,
|
Address: address,
|
||||||
Nonce: l.env.StateDB.GetNonce(address),
|
Nonce: l.env.StateDB.GetNonce(address),
|
||||||
Balance: (*hexutil.Big)(l.env.StateDB.GetBalance(address)),
|
Balance: (*hexutil.Big)(l.env.StateDB.GetBalance(address)),
|
||||||
CodeHash: l.env.StateDB.GetCodeHash(address),
|
CodeHash: l.env.StateDB.GetCodeHash(address),
|
||||||
Proof: encodeProof(proof),
|
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func getWrappedProofForStorage(l *StructLogger, address common.Address, key common.Hash) (*types.AccountProofWrapper, error) {
|
func getWrappedAccountForStorage(l *StructLogger, address common.Address, key common.Hash) (*types.AccountWrapper, error) {
|
||||||
proof, err := l.env.StateDB.GetProof(address)
|
return &types.AccountWrapper{
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
storageProof, err := l.env.StateDB.GetStorageProof(address, key)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
return &types.AccountProofWrapper{
|
|
||||||
Address: address,
|
Address: address,
|
||||||
Nonce: l.env.StateDB.GetNonce(address),
|
Nonce: l.env.StateDB.GetNonce(address),
|
||||||
Balance: (*hexutil.Big)(l.env.StateDB.GetBalance(address)),
|
Balance: (*hexutil.Big)(l.env.StateDB.GetBalance(address)),
|
||||||
CodeHash: l.env.StateDB.GetCodeHash(address),
|
CodeHash: l.env.StateDB.GetCodeHash(address),
|
||||||
Proof: encodeProof(proof),
|
Storage: &types.StorageWrapper{
|
||||||
Storage: &types.StorageProofWrapper{
|
|
||||||
Key: key.String(),
|
Key: key.String(),
|
||||||
Value: l.env.StateDB.GetState(address, key).String(),
|
Value: l.env.StateDB.GetState(address, key).String(),
|
||||||
Proof: encodeProof(storageProof),
|
|
||||||
},
|
},
|
||||||
}, nil
|
}, 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
|
|
||||||
}
|
|
||||||
|
|
|
||||||
122
miner/worker.go
122
miner/worker.go
|
|
@ -96,12 +96,15 @@ type environment struct {
|
||||||
txs []*types.Transaction
|
txs []*types.Transaction
|
||||||
receipts []*types.Receipt
|
receipts []*types.Receipt
|
||||||
executionResults []*types.ExecutionResult
|
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.
|
// task contains all information for consensus engine sealing and result submitting.
|
||||||
type task struct {
|
type task struct {
|
||||||
receipts []*types.Receipt
|
receipts []*types.Receipt
|
||||||
executionResults []*types.ExecutionResult
|
executionResults []*types.ExecutionResult
|
||||||
|
storageResults *types.StorageTrace
|
||||||
state *state.StateDB
|
state *state.StateDB
|
||||||
block *types.Block
|
block *types.Block
|
||||||
createdAt time.Time
|
createdAt time.Time
|
||||||
|
|
@ -639,9 +642,10 @@ func (w *worker) resultLoop() {
|
||||||
}
|
}
|
||||||
// Different block could share same sealhash, deep copy here to prevent write-write conflict.
|
// Different block could share same sealhash, deep copy here to prevent write-write conflict.
|
||||||
var (
|
var (
|
||||||
receipts = make([]*types.Receipt, len(task.receipts))
|
receipts = make([]*types.Receipt, len(task.receipts))
|
||||||
evmTraces = make([]*types.ExecutionResult, len(task.executionResults))
|
evmTraces = make([]*types.ExecutionResult, len(task.executionResults))
|
||||||
logs []*types.Log
|
logs []*types.Log
|
||||||
|
storageTrace = new(types.StorageTrace)
|
||||||
)
|
)
|
||||||
for i, taskReceipt := range task.receipts {
|
for i, taskReceipt := range task.receipts {
|
||||||
receipt := new(types.Receipt)
|
receipt := new(types.Receipt)
|
||||||
|
|
@ -651,6 +655,7 @@ func (w *worker) resultLoop() {
|
||||||
evmTrace := new(types.ExecutionResult)
|
evmTrace := new(types.ExecutionResult)
|
||||||
evmTraces[i] = evmTrace
|
evmTraces[i] = evmTrace
|
||||||
*evmTrace = *task.executionResults[i]
|
*evmTrace = *task.executionResults[i]
|
||||||
|
*storageTrace = *task.storageResults
|
||||||
|
|
||||||
// add block location fields
|
// add block location fields
|
||||||
receipt.BlockHash = hash
|
receipt.BlockHash = hash
|
||||||
|
|
@ -669,7 +674,7 @@ func (w *worker) resultLoop() {
|
||||||
logs = append(logs, receipt.Logs...)
|
logs = append(logs, receipt.Logs...)
|
||||||
}
|
}
|
||||||
// Commit block and state to database.
|
// 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 {
|
if err != nil {
|
||||||
log.Error("Failed writing block to chain", "err", err)
|
log.Error("Failed writing block to chain", "err", err)
|
||||||
continue
|
continue
|
||||||
|
|
@ -700,12 +705,14 @@ func (w *worker) makeCurrent(parent *types.Block, header *types.Header) error {
|
||||||
state.StartPrefetcher("miner")
|
state.StartPrefetcher("miner")
|
||||||
|
|
||||||
env := &environment{
|
env := &environment{
|
||||||
signer: types.MakeSigner(w.chainConfig, header.Number),
|
signer: types.MakeSigner(w.chainConfig, header.Number),
|
||||||
state: state,
|
state: state,
|
||||||
ancestors: mapset.NewSet(),
|
ancestors: mapset.NewSet(),
|
||||||
family: mapset.NewSet(),
|
family: mapset.NewSet(),
|
||||||
uncles: mapset.NewSet(),
|
uncles: mapset.NewSet(),
|
||||||
header: header,
|
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)
|
// when 08 is processed ancestors contain 07 (quick block)
|
||||||
for _, ancestor := range w.chain.GetBlocksFromHash(parent.Hash(), 7) {
|
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()
|
tracer.Reset()
|
||||||
// Get sender's address.
|
// Get sender's address.
|
||||||
from, _ := types.Sender(w.current.signer, tx)
|
from, _ := types.Sender(w.current.signer, tx)
|
||||||
sender := &types.AccountProofWrapper{
|
sender := &types.AccountWrapper{
|
||||||
Address: from,
|
Address: from,
|
||||||
Nonce: w.current.state.GetNonce(from),
|
Nonce: w.current.state.GetNonce(from),
|
||||||
Balance: (*hexutil.Big)(w.current.state.GetBalance(from)),
|
Balance: (*hexutil.Big)(w.current.state.GetBalance(from)),
|
||||||
CodeHash: w.current.state.GetCodeHash(from),
|
CodeHash: w.current.state.GetCodeHash(from),
|
||||||
}
|
}
|
||||||
// Get receiver's address.
|
// Get receiver's address.
|
||||||
var receiver *types.AccountProofWrapper
|
var receiver *types.AccountWrapper
|
||||||
if tx.To() != nil {
|
if tx.To() != nil {
|
||||||
to := *tx.To()
|
to := *tx.To()
|
||||||
receiver = &types.AccountProofWrapper{
|
receiver = &types.AccountWrapper{
|
||||||
Address: to,
|
Address: to,
|
||||||
Nonce: w.current.state.GetNonce(to),
|
Nonce: w.current.state.GetNonce(to),
|
||||||
Balance: (*hexutil.Big)(w.current.state.GetBalance(to)),
|
Balance: (*hexutil.Big)(w.current.state.GetBalance(to)),
|
||||||
|
|
@ -812,15 +819,83 @@ func (w *worker) commitTransaction(tx *types.Transaction, coinbase common.Addres
|
||||||
return nil, err
|
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.txs = append(w.current.txs, tx)
|
||||||
w.current.receipts = append(w.current.receipts, receipt)
|
w.current.receipts = append(w.current.receipts, receipt)
|
||||||
|
|
||||||
w.current.executionResults = append(w.current.executionResults, &types.ExecutionResult{
|
w.current.executionResults = append(w.current.executionResults, &types.ExecutionResult{
|
||||||
Gas: receipt.GasUsed,
|
Gas: receipt.GasUsed,
|
||||||
From: sender,
|
From: sender,
|
||||||
To: receiver,
|
To: receiver,
|
||||||
Failed: receipt.Status != types.ReceiptStatusSuccessful,
|
AccountCreated: createdAcc,
|
||||||
ReturnValue: fmt.Sprintf("%x", receipt.ReturnValue),
|
AccountsAfter: after,
|
||||||
StructLogs: vm.FormatLogs(tracer.StructLogs()),
|
Failed: receipt.Status != types.ReceiptStatusSuccessful,
|
||||||
|
ReturnValue: fmt.Sprintf("%x", receipt.ReturnValue),
|
||||||
|
StructLogs: vm.FormatLogs(tracer.StructLogs()),
|
||||||
})
|
})
|
||||||
|
|
||||||
return receipt.Logs, nil
|
return receipt.Logs, nil
|
||||||
|
|
@ -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.
|
// Deep copy receipts here to avoid interaction between different tasks.
|
||||||
receipts := copyReceipts(w.current.receipts)
|
receipts := copyReceipts(w.current.receipts)
|
||||||
s := w.current.state.Copy()
|
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)
|
block, err := w.engine.FinalizeAndAssemble(w.chain, w.current.header, s, w.current.txs, uncles, receipts)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
|
|
@ -1086,7 +1168,7 @@ func (w *worker) commit(uncles []*types.Header, interval func(), update bool, st
|
||||||
interval()
|
interval()
|
||||||
}
|
}
|
||||||
select {
|
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)
|
w.unconfirmed.Shift(block.NumberU64() - 1)
|
||||||
log.Info("Commit new mining work", "number", block.Number(), "sealhash", w.engine.SealHash(block.Header()),
|
log.Info("Commit new mining work", "number", block.Number(), "sealhash", w.engine.SealHash(block.Header()),
|
||||||
"uncles", len(uncles), "txs", w.current.tcount,
|
"uncles", len(uncles), "txs", w.current.tcount,
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue