go-ethereum/trie/zk_trie.go
colin e5b4f3ee9d
docs(zktrie): remove an outdated comment (#765)
fix(zktrie): remove an outdated comment

Co-authored-by: Péter Garamvölgyi <peter@scroll.io>
2024-05-23 15:59:35 +02:00

235 lines
7.3 KiB
Go

// Copyright 2015 The go-ethereum Authors
// This file is part of the go-ethereum library.
//
// The go-ethereum library is free software: you can redistribute it and/or modify
// it under the terms of the GNU Lesser General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// The go-ethereum library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public License
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
package trie
import (
"fmt"
zktrie "github.com/scroll-tech/zktrie/trie"
zkt "github.com/scroll-tech/zktrie/types"
"github.com/scroll-tech/go-ethereum/common"
"github.com/scroll-tech/go-ethereum/core/types"
"github.com/scroll-tech/go-ethereum/crypto/poseidon"
"github.com/scroll-tech/go-ethereum/ethdb"
"github.com/scroll-tech/go-ethereum/log"
)
var magicHash []byte = []byte("THIS IS THE MAGIC INDEX FOR ZKTRIE")
// wrap zktrie for trie interface
type ZkTrie struct {
*zktrie.ZkTrie
db *ZktrieDatabase
}
func init() {
zkt.InitHashScheme(poseidon.HashFixedWithDomain)
}
func sanityCheckByte32Key(b []byte) {
if len(b) != 32 && len(b) != 20 {
panic(fmt.Errorf("do not support length except for 120bit and 256bit now. data: %v len: %v", b, len(b)))
}
}
// NewZkTrie creates a trie
// NewZkTrie bypasses all the buffer mechanism in *Database, it directly uses the
// underlying diskdb
func NewZkTrie(root common.Hash, db *ZktrieDatabase) (*ZkTrie, error) {
tr, err := zktrie.NewZkTrie(*zkt.NewByte32FromBytes(root.Bytes()), db)
if err != nil {
return nil, err
}
return &ZkTrie{tr, db}, nil
}
// Get returns the value for key stored in the trie.
// The value bytes must not be modified by the caller.
func (t *ZkTrie) Get(key []byte) []byte {
sanityCheckByte32Key(key)
res, err := t.TryGet(key)
if err != nil {
log.Error(fmt.Sprintf("Unhandled trie error: %v", err))
}
return res
}
// TryUpdateAccount will abstract the write of an account to the
// secure trie.
func (t *ZkTrie) TryUpdateAccount(key []byte, acc *types.StateAccount) error {
sanityCheckByte32Key(key)
value, flag := acc.MarshalFields()
return t.ZkTrie.TryUpdate(key, flag, value)
}
// Update associates key with value in the trie. Subsequent calls to
// Get will return value. If value has length zero, any existing value
// is deleted from the trie and calls to Get will return nil.
//
// The value bytes must not be modified by the caller while they are
// stored in the trie.
func (t *ZkTrie) Update(key, value []byte) {
if err := t.TryUpdate(key, value); err != nil {
log.Error(fmt.Sprintf("Unhandled trie error: %v", err))
}
}
// NOTE: value is restricted to length of bytes32.
// we override the underlying zktrie's TryUpdate method
func (t *ZkTrie) TryUpdate(key, value []byte) error {
sanityCheckByte32Key(key)
return t.ZkTrie.TryUpdate(key, 1, []zkt.Byte32{*zkt.NewByte32FromBytes(value)})
}
// Delete removes any existing value for key from the trie.
func (t *ZkTrie) Delete(key []byte) {
sanityCheckByte32Key(key)
if err := t.TryDelete(key); err != nil {
log.Error(fmt.Sprintf("Unhandled trie error: %v", err))
}
}
// GetKey returns the preimage of a hashed key that was
// previously used to store a value.
func (t *ZkTrie) GetKey(kHashBytes []byte) []byte {
// TODO: use a kv cache in memory
k, err := zkt.NewBigIntFromHashBytes(kHashBytes)
if err != nil {
log.Error(fmt.Sprintf("Unhandled trie error: %v", err))
}
if t.db.db.preimages != nil {
return t.db.db.preimages.preimage(common.BytesToHash(k.Bytes()))
}
return nil
}
// Commit writes all nodes and the secure hash pre-images to the trie's database.
// Nodes are stored with their sha3 hash as the key.
//
// Committing flushes nodes from memory. Subsequent Get calls will load nodes
// from the database.
func (t *ZkTrie) Commit(LeafCallback) (common.Hash, int, error) {
if err := t.ZkTrie.Commit(); err != nil {
return common.Hash{}, 0, err
}
return t.Hash(), 0, nil
}
// Hash returns the root hash of SecureBinaryTrie. It does not write to the
// database and can be used even if the trie doesn't have one.
func (t *ZkTrie) Hash() common.Hash {
var hash common.Hash
hash.SetBytes(t.ZkTrie.Hash())
return hash
}
// Copy returns a copy of SecureBinaryTrie.
func (t *ZkTrie) Copy() *ZkTrie {
return &ZkTrie{t.ZkTrie.Copy(), t.db}
}
// NodeIterator returns an iterator that returns nodes of the underlying trie. Iteration
// starts at the key after the given start key.
func (t *ZkTrie) NodeIterator(start []byte) NodeIterator {
/// FIXME
panic("not implemented")
}
// hashKey returns the hash of key as an ephemeral buffer.
// The caller must not hold onto the return value because it will become
// invalid on the next call to hashKey or secKey.
/*func (t *ZkTrie) hashKey(key []byte) []byte {
if len(key) != 32 {
panic("non byte32 input to hashKey")
}
low16 := new(big.Int).SetBytes(key[:16])
high16 := new(big.Int).SetBytes(key[16:])
hash, err := poseidon.Hash([]*big.Int{low16, high16})
if err != nil {
panic(err)
}
return hash.Bytes()
}
*/
// Prove constructs a merkle proof for key. The result contains all encoded nodes
// on the path to the value at key. The value itself is also included in the last
// node and can be retrieved by verifying the proof.
//
// If the trie does not contain a value for key, the returned proof contains all
// nodes of the longest existing prefix of the key (at least the root node), ending
// with the node that proves the absence of the key.
func (t *ZkTrie) Prove(key []byte, fromLevel uint, proofDb ethdb.KeyValueWriter) error {
err := t.ZkTrie.Prove(key, fromLevel, func(n *zktrie.Node) error {
nodeHash, err := n.NodeHash()
if err != nil {
return err
}
if n.Type == zktrie.NodeTypeLeaf_New {
preImage := t.GetKey(n.NodeKey.Bytes())
if len(preImage) > 0 {
n.KeyPreimage = &zkt.Byte32{}
copy(n.KeyPreimage[:], preImage)
//return fmt.Errorf("key preimage not found for [%x] ref %x", n.NodeKey.Bytes(), k.Bytes())
}
}
return proofDb.Put(nodeHash[:], n.Value())
})
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())
}
// VerifyProof checks merkle proofs. The given proof must contain the value for
// key in a trie with the given root hash. VerifyProof returns an error if the
// proof contains invalid trie nodes or the wrong value.
func VerifyProofSMT(rootHash common.Hash, key []byte, proofDb ethdb.KeyValueReader) (value []byte, err error) {
h := zkt.NewHashFromBytes(rootHash.Bytes())
k, err := zkt.ToSecureKey(key)
if err != nil {
return nil, err
}
proof, n, err := zktrie.BuildZkTrieProof(h, k, len(key)*8, func(key *zkt.Hash) (*zktrie.Node, error) {
buf, _ := proofDb.Get(key[:])
if buf == nil {
return nil, zktrie.ErrKeyNotFound
}
n, err := zktrie.NewNodeFromBytes(buf)
return n, err
})
if err != nil {
// do not contain the key
return nil, err
} else if !proof.Existence {
return nil, nil
}
if zktrie.VerifyProofZkTrie(h, proof, n) {
return n.Data(), nil
} else {
return nil, fmt.Errorf("bad proof node %v", proof)
}
}