github.com/cryptotooltop/go-ethereum@v0.0.0-20231103184714-151d1922f3e5/trie/zktrie_deletionproof.go (about) 1 package trie 2 3 import ( 4 "bytes" 5 "fmt" 6 7 zktrie "github.com/scroll-tech/zktrie/trie" 8 zkt "github.com/scroll-tech/zktrie/types" 9 10 "github.com/scroll-tech/go-ethereum/ethdb" 11 ) 12 13 // Pick Node from its hash directly from database, notice it has different 14 // interface with the function of same name in `trie` 15 func (t *ZkTrie) TryGetNode(nodeHash *zkt.Hash) (*zktrie.Node, error) { 16 if bytes.Equal(nodeHash[:], zkt.HashZero[:]) { 17 return zktrie.NewEmptyNode(), nil 18 } 19 nBytes, err := t.db.Get(nodeHash[:]) 20 if err == zktrie.ErrKeyNotFound { 21 return nil, zktrie.ErrKeyNotFound 22 } else if err != nil { 23 return nil, err 24 } 25 return zktrie.NewNodeFromBytes(nBytes) 26 } 27 28 type ProofTracer struct { 29 *ZkTrie 30 deletionTracer map[zkt.Hash]struct{} 31 rawPaths map[string][]*zktrie.Node 32 emptyTermPaths map[string][]*zktrie.Node 33 } 34 35 // NewProofTracer create a proof tracer object 36 func (t *ZkTrie) NewProofTracer() *ProofTracer { 37 return &ProofTracer{ 38 ZkTrie: t, 39 // always consider 0 is "deleted" 40 deletionTracer: map[zkt.Hash]struct{}{zkt.HashZero: {}}, 41 rawPaths: make(map[string][]*zktrie.Node), 42 emptyTermPaths: make(map[string][]*zktrie.Node), 43 } 44 } 45 46 // Merge merge the input tracer into current and return current tracer 47 func (t *ProofTracer) Merge(another *ProofTracer) *ProofTracer { 48 49 // sanity checking 50 if !bytes.Equal(t.Hash().Bytes(), another.Hash().Bytes()) { 51 panic("can not merge two proof tracer base on different trie") 52 } 53 54 for k := range another.deletionTracer { 55 t.deletionTracer[k] = struct{}{} 56 } 57 58 for k, v := range another.rawPaths { 59 t.rawPaths[k] = v 60 } 61 62 for k, v := range another.emptyTermPaths { 63 t.emptyTermPaths[k] = v 64 } 65 66 return t 67 } 68 69 // GetDeletionProofs generate current deletionTracer and collect deletion proofs 70 // which is possible to be used from all rawPaths, which enabling witness generator 71 // to predict the final state root after executing any deletion 72 // along any of the rawpath, no matter of the deletion occurs in any position of the mpt ops 73 // Note the collected sibling node has no key along with it since witness generator would 74 // always decode the node for its purpose 75 func (t *ProofTracer) GetDeletionProofs() ([][]byte, error) { 76 77 retMap := map[zkt.Hash][]byte{} 78 79 // check each path: reversively, skip the final leaf node 80 for _, path := range t.rawPaths { 81 82 checkPath := path[:len(path)-1] 83 for i := len(checkPath); i > 0; i-- { 84 n := checkPath[i-1] 85 _, deletedL := t.deletionTracer[*n.ChildL] 86 _, deletedR := t.deletionTracer[*n.ChildR] 87 if deletedL && deletedR { 88 nodeHash, _ := n.NodeHash() 89 t.deletionTracer[*nodeHash] = struct{}{} 90 } else { 91 var siblingHash *zkt.Hash 92 if deletedL { 93 siblingHash = n.ChildR 94 } else if deletedR { 95 siblingHash = n.ChildL 96 } 97 if siblingHash != nil { 98 sibling, err := t.TryGetNode(siblingHash) 99 if err != nil { 100 return nil, err 101 } 102 if sibling.Type != zktrie.NodeTypeEmpty_New { 103 retMap[*siblingHash] = sibling.Value() 104 } 105 } 106 break 107 } 108 } 109 } 110 111 var ret [][]byte 112 for _, bt := range retMap { 113 ret = append(ret, bt) 114 } 115 116 return ret, nil 117 118 } 119 120 // MarkDeletion mark a key has been involved into deletion 121 func (t *ProofTracer) MarkDeletion(key []byte) { 122 if path, existed := t.emptyTermPaths[string(key)]; existed { 123 // copy empty node terminated path for final scanning 124 t.rawPaths[string(key)] = path 125 } else if path, existed = t.rawPaths[string(key)]; existed { 126 // sanity check 127 leafNode := path[len(path)-1] 128 129 if leafNode.Type != zktrie.NodeTypeLeaf_New { 130 panic("all path recorded in proofTrace should be ended with leafNode") 131 } 132 133 nodeHash, _ := leafNode.NodeHash() 134 t.deletionTracer[*nodeHash] = struct{}{} 135 } 136 } 137 138 // Prove act the same as zktrie.Prove, while also collect the raw path 139 // for collecting deletion proofs in a post-work 140 func (t *ProofTracer) Prove(key []byte, fromLevel uint, proofDb ethdb.KeyValueWriter) error { 141 var mptPath []*zktrie.Node 142 err := t.ZkTrie.ProveWithDeletion(key, fromLevel, 143 func(n *zktrie.Node) error { 144 nodeHash, err := n.NodeHash() 145 if err != nil { 146 return err 147 } 148 149 switch n.Type { 150 case zktrie.NodeTypeLeaf_New: 151 preImage := t.GetKey(n.NodeKey.Bytes()) 152 if len(preImage) > 0 { 153 n.KeyPreimage = &zkt.Byte32{} 154 copy(n.KeyPreimage[:], preImage) 155 } 156 case zktrie.NodeTypeBranch_0, zktrie.NodeTypeBranch_1, 157 zktrie.NodeTypeBranch_2, zktrie.NodeTypeBranch_3: 158 mptPath = append(mptPath, n) 159 case zktrie.NodeTypeEmpty_New: 160 // empty node is considered as "unhit" but it should be also being added 161 // into a temporary slot for possibly being marked as deletion later 162 mptPath = append(mptPath, n) 163 t.emptyTermPaths[string(key)] = mptPath 164 default: 165 panic(fmt.Errorf("unexpected node type %d", n.Type)) 166 } 167 168 return proofDb.Put(nodeHash[:], n.Value()) 169 }, 170 func(n *zktrie.Node, _ *zktrie.Node) { 171 // only "hit" path (i.e. the leaf node corresponding the input key can be found) 172 // would be add into tracer 173 mptPath = append(mptPath, n) 174 t.rawPaths[string(key)] = mptPath 175 }, 176 ) 177 if err != nil { 178 return err 179 } 180 // we put this special kv pair in db so we can distinguish the type and 181 // make suitable Proof 182 return proofDb.Put(magicHash, zktrie.ProofMagicBytes()) 183 }