go-ethereum/trie/zktrie_deletionproof.go
Ho 11b35cd014
feat: upgrade zktrie to v0.6.0 (#403)
* add domain-spec poseidon hash

* add warning message for deprecated method

* update goimports

* update zktrie to v0.6

* fixes for upgrade

* resume deletion proof

* update alpha genesis since new hash sch eme

* dump zktrie to 0.6

* induce genesis block for scroll sepolia network

* Update version.go

---------

Co-authored-by: HAOYUatHZ <37070449+HAOYUatHZ@users.noreply.github.com>
Co-authored-by: Péter Garamvölgyi <peter@scroll.io>
2023-08-02 10:54:41 +08:00

183 lines
5.1 KiB
Go

package trie
import (
"bytes"
"fmt"
zktrie "github.com/scroll-tech/zktrie/trie"
zkt "github.com/scroll-tech/zktrie/types"
"github.com/scroll-tech/go-ethereum/ethdb"
)
// Pick Node from its hash directly from database, notice it has different
// interface with the function of same name in `trie`
func (t *ZkTrie) TryGetNode(nodeHash *zkt.Hash) (*zktrie.Node, error) {
if bytes.Equal(nodeHash[:], zkt.HashZero[:]) {
return zktrie.NewEmptyNode(), nil
}
nBytes, err := t.db.Get(nodeHash[:])
if err == zktrie.ErrKeyNotFound {
return nil, zktrie.ErrKeyNotFound
} else if err != nil {
return nil, err
}
return zktrie.NewNodeFromBytes(nBytes)
}
type ProofTracer struct {
*ZkTrie
deletionTracer map[zkt.Hash]struct{}
rawPaths map[string][]*zktrie.Node
emptyTermPaths map[string][]*zktrie.Node
}
// NewProofTracer create a proof tracer object
func (t *ZkTrie) NewProofTracer() *ProofTracer {
return &ProofTracer{
ZkTrie: t,
// always consider 0 is "deleted"
deletionTracer: map[zkt.Hash]struct{}{zkt.HashZero: {}},
rawPaths: make(map[string][]*zktrie.Node),
emptyTermPaths: make(map[string][]*zktrie.Node),
}
}
// Merge merge the input tracer into current and return current tracer
func (t *ProofTracer) Merge(another *ProofTracer) *ProofTracer {
// sanity checking
if !bytes.Equal(t.Hash().Bytes(), another.Hash().Bytes()) {
panic("can not merge two proof tracer base on different trie")
}
for k := range another.deletionTracer {
t.deletionTracer[k] = struct{}{}
}
for k, v := range another.rawPaths {
t.rawPaths[k] = v
}
for k, v := range another.emptyTermPaths {
t.emptyTermPaths[k] = v
}
return t
}
// GetDeletionProofs generate current deletionTracer and collect deletion proofs
// which is possible to be used from all rawPaths, which enabling witness generator
// to predict the final state root after executing any deletion
// along any of the rawpath, no matter of the deletion occurs in any position of the mpt ops
// Note the collected sibling node has no key along with it since witness generator would
// always decode the node for its purpose
func (t *ProofTracer) GetDeletionProofs() ([][]byte, error) {
retMap := map[zkt.Hash][]byte{}
// check each path: reversively, skip the final leaf node
for _, path := range t.rawPaths {
checkPath := path[:len(path)-1]
for i := len(checkPath); i > 0; i-- {
n := checkPath[i-1]
_, deletedL := t.deletionTracer[*n.ChildL]
_, deletedR := t.deletionTracer[*n.ChildR]
if deletedL && deletedR {
nodeHash, _ := n.NodeHash()
t.deletionTracer[*nodeHash] = struct{}{}
} else {
var siblingHash *zkt.Hash
if deletedL {
siblingHash = n.ChildR
} else if deletedR {
siblingHash = n.ChildL
}
if siblingHash != nil {
sibling, err := t.TryGetNode(siblingHash)
if err != nil {
return nil, err
}
if sibling.Type != zktrie.NodeTypeEmpty_New {
retMap[*siblingHash] = sibling.Value()
}
}
break
}
}
}
var ret [][]byte
for _, bt := range retMap {
ret = append(ret, bt)
}
return ret, nil
}
// MarkDeletion mark a key has been involved into deletion
func (t *ProofTracer) MarkDeletion(key []byte) {
if path, existed := t.emptyTermPaths[string(key)]; existed {
// copy empty node terminated path for final scanning
t.rawPaths[string(key)] = path
} else if path, existed = t.rawPaths[string(key)]; existed {
// sanity check
leafNode := path[len(path)-1]
if leafNode.Type != zktrie.NodeTypeLeaf_New {
panic("all path recorded in proofTrace should be ended with leafNode")
}
nodeHash, _ := leafNode.NodeHash()
t.deletionTracer[*nodeHash] = struct{}{}
}
}
// Prove act the same as zktrie.Prove, while also collect the raw path
// for collecting deletion proofs in a post-work
func (t *ProofTracer) Prove(key []byte, fromLevel uint, proofDb ethdb.KeyValueWriter) error {
var mptPath []*zktrie.Node
err := t.ZkTrie.ProveWithDeletion(key, fromLevel,
func(n *zktrie.Node) error {
nodeHash, err := n.NodeHash()
if err != nil {
return err
}
switch n.Type {
case zktrie.NodeTypeLeaf_New:
preImage := t.GetKey(n.NodeKey.Bytes())
if len(preImage) > 0 {
n.KeyPreimage = &zkt.Byte32{}
copy(n.KeyPreimage[:], preImage)
}
case zktrie.NodeTypeBranch_0, zktrie.NodeTypeBranch_1,
zktrie.NodeTypeBranch_2, zktrie.NodeTypeBranch_3:
mptPath = append(mptPath, n)
case zktrie.NodeTypeEmpty_New:
// empty node is considered as "unhit" but it should be also being added
// into a temporary slot for possibly being marked as deletion later
mptPath = append(mptPath, n)
t.emptyTermPaths[string(key)] = mptPath
default:
panic(fmt.Errorf("unexpected node type %d", n.Type))
}
return proofDb.Put(nodeHash[:], n.Value())
},
func(n *zktrie.Node, _ *zktrie.Node) {
// only "hit" path (i.e. the leaf node corresponding the input key can be found)
// would be add into tracer
mptPath = append(mptPath, n)
t.rawPaths[string(key)] = mptPath
},
)
if err != nil {
return err
}
// we put this special kv pair in db so we can distinguish the type and
// make suitable Proof
return proofDb.Put(magicHash, zktrie.ProofMagicBytes())
}