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  }