Skip to content

Commit c913b3e

Browse files
noel20040xmountaintopUbuntu
authored
fix(zktrie): fix deletion proofs and collect them in commiting phase (#263)
* new deletion proof * complete tracer and test * extend proof for parallel tracing * integrating into blocktrace * lint * deduplication of deletion proofs * fix an issue on marking deletion * fixs since last review * Update version.go --------- Co-authored-by: HAOYUatHZ <[email protected]> Co-authored-by: Ubuntu <[email protected]>
1 parent 4919226 commit c913b3e

File tree

7 files changed

+371
-93
lines changed

7 files changed

+371
-93
lines changed

core/state/state_prove.go

Lines changed: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,88 @@
1+
package state
2+
3+
import (
4+
"fmt"
5+
6+
zkt "github.com/scroll-tech/zktrie/types"
7+
8+
zktrie "github.com/scroll-tech/go-ethereum/trie"
9+
10+
"github.com/scroll-tech/go-ethereum/common"
11+
"github.com/scroll-tech/go-ethereum/crypto"
12+
"github.com/scroll-tech/go-ethereum/ethdb"
13+
)
14+
15+
type TrieProve interface {
16+
Prove(key []byte, fromLevel uint, proofDb ethdb.KeyValueWriter) error
17+
}
18+
19+
type ZktrieProofTracer struct {
20+
*zktrie.ProofTracer
21+
}
22+
23+
// MarkDeletion overwrite the underlayer method with secure key
24+
func (t ZktrieProofTracer) MarkDeletion(key common.Hash) {
25+
key_s, _ := zkt.ToSecureKeyBytes(key.Bytes())
26+
t.ProofTracer.MarkDeletion(key_s.Bytes())
27+
}
28+
29+
// Merge overwrite underlayer method with proper argument
30+
func (t ZktrieProofTracer) Merge(another ZktrieProofTracer) {
31+
t.ProofTracer.Merge(another.ProofTracer)
32+
}
33+
34+
func (t ZktrieProofTracer) Available() bool {
35+
return t.ProofTracer != nil
36+
}
37+
38+
// NewProofTracer is not in Db interface and used explictily for reading proof in storage trie (not updated by the dirty value)
39+
func (s *StateDB) NewProofTracer(trieS Trie) ZktrieProofTracer {
40+
if s.IsZktrie() {
41+
zkTrie := trieS.(*zktrie.ZkTrie)
42+
if zkTrie == nil {
43+
panic("unexpected trie type for zktrie")
44+
}
45+
return ZktrieProofTracer{zkTrie.NewProofTracer()}
46+
}
47+
return ZktrieProofTracer{}
48+
}
49+
50+
// GetStorageTrieForProof is not in Db interface and used explictily for reading proof in storage trie (not updated by the dirty value)
51+
func (s *StateDB) GetStorageTrieForProof(addr common.Address) (Trie, error) {
52+
53+
// try the trie in stateObject first, else we would create one
54+
stateObject := s.getStateObject(addr)
55+
if stateObject == nil {
56+
// still return a empty trie
57+
addrHash := crypto.Keccak256Hash(addr[:])
58+
dummy_trie, _ := s.db.OpenStorageTrie(addrHash, common.Hash{})
59+
return dummy_trie, nil
60+
}
61+
62+
trie := stateObject.trie
63+
var err error
64+
if trie == nil {
65+
// use a new, temporary trie
66+
trie, err = s.db.OpenStorageTrie(stateObject.addrHash, stateObject.data.Root)
67+
if err != nil {
68+
return nil, fmt.Errorf("can't create storage trie on root %s: %v ", stateObject.data.Root, err)
69+
}
70+
}
71+
72+
return trie, nil
73+
}
74+
75+
// GetSecureTrieProof handle any interface with Prove (should be a Trie in most case) and
76+
// deliver the proof in bytes
77+
func (s *StateDB) GetSecureTrieProof(trieProve TrieProve, key common.Hash) ([][]byte, error) {
78+
79+
var proof proofList
80+
var err error
81+
if s.IsZktrie() {
82+
key_s, _ := zkt.ToSecureKeyBytes(key.Bytes())
83+
err = trieProve.Prove(key_s.Bytes(), 0, &proof)
84+
} else {
85+
err = trieProve.Prove(crypto.Keccak256(key.Bytes()), 0, &proof)
86+
}
87+
return proof, err
88+
}

core/state/statedb.go

Lines changed: 2 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -350,56 +350,13 @@ func (s *StateDB) GetRootHash() common.Hash {
350350
return s.trie.Hash()
351351
}
352352

353-
// StorageTrieProof is not in Db interface and used explictily for reading proof in storage trie (not the dirty value)
354-
// For zktrie it also provide required data for predict the deletion, else it just fallback to GetStorageProof
355-
func (s *StateDB) GetStorageTrieProof(a common.Address, key common.Hash) ([][]byte, []byte, error) {
356-
357-
// try the trie in stateObject first, else we would create one
358-
stateObject := s.getStateObject(a)
359-
if stateObject == nil {
360-
return nil, nil, errors.New("storage trie for requested address does not exist")
361-
}
362-
363-
trieS := stateObject.trie
364-
var err error
365-
if trieS == nil {
366-
// use a new, temporary trie
367-
trieS, err = s.db.OpenStorageTrie(stateObject.addrHash, stateObject.data.Root)
368-
if err != nil {
369-
return nil, nil, fmt.Errorf("can't create storage trie on root %s: %v ", stateObject.data.Root, err)
370-
}
371-
}
372-
373-
var proof proofList
374-
var sibling []byte
375-
if s.IsZktrie() {
376-
zkTrie := trieS.(*trie.ZkTrie)
377-
if zkTrie == nil {
378-
panic("unexpected trie type for zktrie")
379-
}
380-
key_s, _ := zkt.ToSecureKeyBytes(key.Bytes())
381-
sibling, err = zkTrie.ProveWithDeletion(key_s.Bytes(), 0, &proof)
382-
} else {
383-
err = trieS.Prove(crypto.Keccak256(key.Bytes()), 0, &proof)
384-
}
385-
return proof, sibling, err
386-
}
387-
388353
// GetStorageProof returns the Merkle proof for given storage slot.
389354
func (s *StateDB) GetStorageProof(a common.Address, key common.Hash) ([][]byte, error) {
390-
var proof proofList
391355
trie := s.StorageTrie(a)
392356
if trie == nil {
393-
return proof, errors.New("storage trie for requested address does not exist")
357+
return nil, errors.New("storage trie for requested address does not exist")
394358
}
395-
var err error
396-
if s.IsZktrie() {
397-
key_s, _ := zkt.ToSecureKeyBytes(key.Bytes())
398-
err = trie.Prove(key_s.Bytes(), 0, &proof)
399-
} else {
400-
err = trie.Prove(crypto.Keccak256(key.Bytes()), 0, &proof)
401-
}
402-
return proof, err
359+
return s.GetSecureTrieProof(trie, key)
403360
}
404361

405362
// GetCommittedState retrieves a value from the given account's committed storage trie.

eth/tracers/api_blocktrace.go

Lines changed: 48 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package tracers
22

33
import (
4+
"bytes"
45
"context"
56
"errors"
67
"fmt"
@@ -42,6 +43,8 @@ type traceEnv struct {
4243
// this lock is used to protect StorageTrace's read and write mutual exclusion.
4344
sMu sync.Mutex
4445
*types.StorageTrace
46+
// zktrie tracer is used for zktrie storage to build additional deletion proof
47+
zkTrieTracer map[string]state.ZktrieProofTracer
4548
executionResults []*types.ExecutionResult
4649
}
4750

@@ -119,6 +122,7 @@ func (api *API) createTraceEnv(ctx context.Context, config *TraceConfig, block *
119122
Proofs: make(map[string][]hexutil.Bytes),
120123
StorageProofs: make(map[string]map[string][]hexutil.Bytes),
121124
},
125+
zkTrieTracer: make(map[string]state.ZktrieProofTracer),
122126
executionResults: make([]*types.ExecutionResult, block.Transactions().Len()),
123127
}
124128

@@ -189,6 +193,18 @@ func (api *API) getBlockTrace(block *types.Block, env *traceEnv) (*types.BlockTr
189193
close(jobs)
190194
pend.Wait()
191195

196+
// after all tx has been traced, collect "deletion proof" for zktrie
197+
for _, tracer := range env.zkTrieTracer {
198+
delProofs, err := tracer.GetDeletionProofs()
199+
if err != nil {
200+
log.Error("deletion proof failure", "error", err)
201+
} else {
202+
for _, proof := range delProofs {
203+
env.DeletionProofs = append(env.DeletionProofs, proof)
204+
}
205+
}
206+
}
207+
192208
// If execution failed in between, abort
193209
select {
194210
case err := <-errCh:
@@ -299,22 +315,47 @@ func (api *API) getTxResult(env *traceEnv, state *state.StateDB, index int, bloc
299315

300316
proofStorages := tracer.UpdatedStorages()
301317
for addr, keys := range proofStorages {
302-
for key := range keys {
318+
env.sMu.Lock()
319+
trie, err := state.GetStorageTrieForProof(addr)
320+
if err != nil {
321+
// but we still continue to next address
322+
log.Error("Storage trie not available", "error", err, "address", addr)
323+
env.sMu.Unlock()
324+
continue
325+
}
326+
zktrieTracer := state.NewProofTracer(trie)
327+
env.sMu.Unlock()
328+
329+
for key, values := range keys {
303330
addrStr := addr.String()
304331
keyStr := key.String()
332+
isDelete := bytes.Equal(values.Bytes(), common.Hash{}.Bytes())
305333

306334
env.sMu.Lock()
307335
m, existed := env.StorageProofs[addrStr]
308336
if !existed {
309337
m = make(map[string][]hexutil.Bytes)
310338
env.StorageProofs[addrStr] = m
339+
if zktrieTracer.Available() {
340+
env.zkTrieTracer[addrStr] = zktrieTracer
341+
}
311342
} else if _, existed := m[keyStr]; existed {
343+
// still need to touch tracer for deletion
344+
if isDelete && zktrieTracer.Available() {
345+
env.zkTrieTracer[addrStr].MarkDeletion(key)
346+
}
312347
env.sMu.Unlock()
313348
continue
314349
}
315350
env.sMu.Unlock()
316351

317-
proof, sibling, err := state.GetStorageTrieProof(addr, key)
352+
var proof [][]byte
353+
var err error
354+
if zktrieTracer.Available() {
355+
proof, err = state.GetSecureTrieProof(zktrieTracer, key)
356+
} else {
357+
proof, err = state.GetSecureTrieProof(trie, key)
358+
}
318359
if err != nil {
319360
log.Error("Storage proof not available", "error", err, "address", addrStr, "key", keyStr)
320361
// but we still mark the proofs map with nil array
@@ -325,8 +366,11 @@ func (api *API) getTxResult(env *traceEnv, state *state.StateDB, index int, bloc
325366
}
326367
env.sMu.Lock()
327368
m[keyStr] = wrappedProof
328-
if sibling != nil {
329-
env.DeletionProofs = append(env.DeletionProofs, sibling)
369+
if zktrieTracer.Available() {
370+
if isDelete {
371+
zktrieTracer.MarkDeletion(key)
372+
}
373+
env.zkTrieTracer[addrStr].Merge(zktrieTracer)
330374
}
331375
env.sMu.Unlock()
332376
}

params/version.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ import (
2424
const (
2525
VersionMajor = 3 // Major version component of the current release
2626
VersionMinor = 1 // Minor version component of the current release
27-
VersionPatch = 10 // Patch version component of the current release
27+
VersionPatch = 11 // Patch version component of the current release
2828
VersionMeta = "alpha" // Version metadata to append to the version string
2929
)
3030

trie/zk_trie.go

Lines changed: 16 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -174,49 +174,29 @@ func (t *ZkTrie) NodeIterator(start []byte) NodeIterator {
174174
// nodes of the longest existing prefix of the key (at least the root node), ending
175175
// with the node that proves the absence of the key.
176176
func (t *ZkTrie) Prove(key []byte, fromLevel uint, proofDb ethdb.KeyValueWriter) error {
177-
// omit sibling, which is not required for proving only
178-
_, err := t.ProveWithDeletion(key, fromLevel, proofDb)
179-
return err
180-
}
181-
182-
// ProveWithDeletion is the implement of Prove, it also return possible sibling node
183-
// (if there is, i.e. the node of key exist and is not the only node in trie)
184-
// so witness generator can predict the final state root after deletion of this key
185-
// the returned sibling node has no key along with it for witness generator must decode
186-
// the node for its purpose
187-
func (t *ZkTrie) ProveWithDeletion(key []byte, fromLevel uint, proofDb ethdb.KeyValueWriter) (sibling []byte, err error) {
188-
err = t.ZkTrie.ProveWithDeletion(key, fromLevel,
189-
func(n *zktrie.Node) error {
190-
nodeHash, err := n.NodeHash()
191-
if err != nil {
192-
return err
193-
}
177+
err := t.ZkTrie.Prove(key, fromLevel, func(n *zktrie.Node) error {
178+
nodeHash, err := n.NodeHash()
179+
if err != nil {
180+
return err
181+
}
194182

195-
if n.Type == zktrie.NodeTypeLeaf {
196-
preImage := t.GetKey(n.NodeKey.Bytes())
197-
if len(preImage) > 0 {
198-
n.KeyPreimage = &zkt.Byte32{}
199-
copy(n.KeyPreimage[:], preImage)
200-
//return fmt.Errorf("key preimage not found for [%x] ref %x", n.NodeKey.Bytes(), k.Bytes())
201-
}
202-
}
203-
return proofDb.Put(nodeHash[:], n.Value())
204-
},
205-
func(_ *zktrie.Node, n *zktrie.Node) {
206-
// the sibling for each leaf should be unique except for EmptyNode
207-
if n != nil && n.Type != zktrie.NodeTypeEmpty {
208-
sibling = n.Value()
183+
if n.Type == zktrie.NodeTypeLeaf {
184+
preImage := t.GetKey(n.NodeKey.Bytes())
185+
if len(preImage) > 0 {
186+
n.KeyPreimage = &zkt.Byte32{}
187+
copy(n.KeyPreimage[:], preImage)
188+
//return fmt.Errorf("key preimage not found for [%x] ref %x", n.NodeKey.Bytes(), k.Bytes())
209189
}
210-
},
211-
)
190+
}
191+
return proofDb.Put(nodeHash[:], n.Value())
192+
})
212193
if err != nil {
213-
return
194+
return err
214195
}
215196

216197
// we put this special kv pair in db so we can distinguish the type and
217198
// make suitable Proof
218-
err = proofDb.Put(magicHash, zktrie.ProofMagicBytes())
219-
return
199+
return proofDb.Put(magicHash, zktrie.ProofMagicBytes())
220200
}
221201

222202
// VerifyProof checks merkle proofs. The given proof must contain the value for

0 commit comments

Comments
 (0)