Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

zktrie part1: change storage proof from per step to per block #102

Merged
merged 9 commits into from
Jun 8, 2022
Merged
Show file tree
Hide file tree
Changes from 8 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitattributes
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
# Auto detect text files and perform LF normalization
* text=auto
*.sol linguist-language=Solidity
*.go text eol=lf
49 changes: 7 additions & 42 deletions core/blockchain.go
Original file line number Diff line number Diff line change
Expand Up @@ -1193,7 +1193,7 @@ 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.EvmTxTraces, state *state.StateDB, emitHeadEvent bool) (status WriteStatus, err error) {
if !bc.chainmu.TryLock() {
return NonStatTy, errInsertionInterrupted
}
Expand All @@ -1203,7 +1203,7 @@ func (bc *BlockChain) WriteBlockWithState(block *types.Block, receipts []*types.

// 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.EvmTxTraces, state *state.StateDB, emitHeadEvent bool) (status WriteStatus, err error) {
if bc.insertStopped() {
return NonStatTy, errInsertionInterrupted
}
Expand Down Expand Up @@ -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.EvmTxTraces) *types.BlockResult {
blockResult := &types.BlockResult{
ExecutionResults: evmTraces,
ExecutionResults: evmTraces.TxResults,
StorageTrace: evmTraces.Storage,
}
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()))
Expand Down
35 changes: 35 additions & 0 deletions core/state/statedb.go
Original file line number Diff line number Diff line change
Expand Up @@ -325,6 +325,41 @@ func (s *StateDB) GetProofByHash(addrHash common.Hash) ([][]byte, error) {
return proof, err
}

func (s *StateDB) GetStateData(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
Expand Down
68 changes: 51 additions & 17 deletions core/types/l2trace.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,20 +23,50 @@ 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"`
}

// EvmTxTraces groups trace data from each executation of tx and left
// some field to be finished from blockResult
type EvmTxTraces struct {
TxResults []*ExecutionResult
Storage *StorageTrace
}

// 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 {
Gas uint64 `json:"gas"`
Failed bool `json:"failed"`
ReturnValue string `json:"returnValue,omitempty"`
// Sender's account proof.
From *AccountProofWrapper `json:"from,omitempty"`
// Sender's account proof (before Tx)..
From *AccountWrapper `json:"from,omitempty"`
// Receiver's account proof.
To *AccountProofWrapper `json:"to,omitempty"`
To *AccountWrapper `json:"to,omitempty"`
// AccountCreated record the account in case tx is create
// (for creating inside contracts we handle CREATE op)
AccountCreated *AccountWrapper `json:"accountCreated,omitempty"`

// Record all accounts' state which would be affected AFTER tx executed
// currently they are just sender and to account
AccountsAfter []*AccountWrapper `json:"accountAfter"`

// It's exist only when tx is a contract call.
CodeHash *common.Hash `json:"codeHash,omitempty"`
// If it is a contract call, the contract code is returned.
Expand Down Expand Up @@ -77,30 +107,34 @@ func NewStructLogResBasic(pc uint64, op string, gas, gasCost uint64, depth int,
}

type ExtraData struct {
// Indicate the call success 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"`
// CREATE | CREATE2: [created contract address’s accountProof (before constructed),
// created contract address's data (after constructed)]
// CALL | CALLCODE: [caller contract address’s accountProof, stack.nth_last(1) (i.e. called) address’s accountProof
// called contract address's data (value updated, before called)]
// STATICCALL: [stack.nth_last(1) (i.e. called) address’s accountProof
// called contract address's data (before called)]
ProofList []*AccountWrapper `json:"proofList,omitempty"`
}

type AccountProofWrapper 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
type AccountWrapper struct {
Address common.Address `json:"address"`
Nonce uint64 `json:"nonce"`
Balance *hexutil.Big `json:"balance"`
CodeHash common.Hash `json:"codeHash,omitempty"`
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 {
Key string `json:"key,omitempty"`
Value string `json:"value,omitempty"`
Proof []string `json:"proof,omitempty"`
type StorageWrapper struct {
Key string `json:"key,omitempty"`
Value string `json:"value,omitempty"`
}
18 changes: 9 additions & 9 deletions core/types/l2trace_block.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,14 +9,14 @@ import (
)

type BlockTrace struct {
Number *hexutil.Big `json:"number"`
Hash common.Hash `json:"hash"`
GasLimit uint64 `json:"gasLimit"`
Difficulty *hexutil.Big `json:"difficulty"`
BaseFee *hexutil.Big `json:"baseFee"`
Coinbase *AccountProofWrapper `json:"coinbase"`
Time uint64 `json:"time"`
Transactions []*TransactionTrace `json:"transactions"`
Number *hexutil.Big `json:"number"`
Hash common.Hash `json:"hash"`
GasLimit uint64 `json:"gasLimit"`
Difficulty *hexutil.Big `json:"difficulty"`
BaseFee *hexutil.Big `json:"baseFee"`
Coinbase *AccountWrapper `json:"coinbase"`
Time uint64 `json:"time"`
Transactions []*TransactionTrace `json:"transactions"`
}

type TransactionTrace struct {
Expand All @@ -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)
Expand Down
2 changes: 2 additions & 0 deletions core/vm/interface.go
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,8 @@ type StateDB interface {
GetState(common.Address, common.Hash) common.Hash
SetState(common.Address, common.Hash, common.Hash)

GetRootHash() common.Hash
GetStateData(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)
Expand Down
Loading