package merkletree
|
|
|
|
import (
|
|
"bytes"
|
|
"encoding/hex"
|
|
"errors"
|
|
"fmt"
|
|
"io"
|
|
"math/big"
|
|
"strings"
|
|
"sync"
|
|
|
|
cryptoUtils "github.com/iden3/go-iden3-crypto/utils"
|
|
)
|
|
|
|
const (
|
|
// proofFlagsLen is the byte length of the flags in the proof header
|
|
// (first 32 bytes).
|
|
proofFlagsLen = 2
|
|
// ElemBytesLen is the length of the Hash byte array
|
|
ElemBytesLen = 32
|
|
|
|
numCharPrint = 8
|
|
)
|
|
|
|
var (
|
|
// ErrNodeKeyAlreadyExists is used when a node key already exists.
|
|
ErrNodeKeyAlreadyExists = errors.New("key already exists")
|
|
// ErrKeyNotFound is used when a key is not found in the MerkleTree.
|
|
ErrKeyNotFound = errors.New("Key not found in the MerkleTree")
|
|
// ErrNodeBytesBadSize is used when the data of a node has an incorrect
|
|
// size and can't be parsed.
|
|
ErrNodeBytesBadSize = errors.New("node data has incorrect size in the DB")
|
|
// ErrReachedMaxLevel is used when a traversal of the MT reaches the
|
|
// maximum level.
|
|
ErrReachedMaxLevel = errors.New("reached maximum level of the merkle tree")
|
|
// ErrInvalidNodeFound is used when an invalid node is found and can't
|
|
// be parsed.
|
|
ErrInvalidNodeFound = errors.New("found an invalid node in the DB")
|
|
// ErrInvalidProofBytes is used when a serialized proof is invalid.
|
|
ErrInvalidProofBytes = errors.New("the serialized proof is invalid")
|
|
// ErrInvalidDBValue is used when a value in the key value DB is
|
|
// invalid (for example, it doen't contain a byte header and a []byte
|
|
// body of at least len=1.
|
|
ErrInvalidDBValue = errors.New("the value in the DB is invalid")
|
|
// ErrEntryIndexAlreadyExists is used when the entry index already
|
|
// exists in the tree.
|
|
ErrEntryIndexAlreadyExists = errors.New("the entry index already exists in the tree")
|
|
// ErrNotWritable is used when the MerkleTree is not writable and a
|
|
// write function is called
|
|
ErrNotWritable = errors.New("Merkle Tree not writable")
|
|
|
|
dbKeyRootNode = []byte("currentroot")
|
|
// HashZero is used at Empty nodes
|
|
HashZero = Hash{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
|
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}
|
|
)
|
|
|
|
// Hash is the generic type stored in the MerkleTree
|
|
type Hash [32]byte
|
|
|
|
// MarshalText implements the marshaler for the Hash type
|
|
func (h Hash) MarshalText() ([]byte, error) {
|
|
return []byte(h.BigInt().String()), nil
|
|
}
|
|
|
|
// UnmarshalText implements the unmarshaler for the Hash type
|
|
func (h *Hash) UnmarshalText(b []byte) error {
|
|
ha, err := NewHashFromString(string(b))
|
|
copy(h[:], ha[:])
|
|
return err
|
|
}
|
|
|
|
// String returns decimal representation in string format of the Hash
|
|
func (h Hash) String() string {
|
|
s := h.BigInt().String()
|
|
if len(s) < numCharPrint {
|
|
return s
|
|
}
|
|
return s[0:numCharPrint] + "..."
|
|
}
|
|
|
|
// Hex returns the hexadecimal representation of the Hash
|
|
func (h Hash) Hex() string {
|
|
return hex.EncodeToString(h[:])
|
|
// alternatively equivalent, but with too extra steps:
|
|
// bRaw := h.BigInt().Bytes()
|
|
// b := [32]byte{}
|
|
// copy(b[:], SwapEndianness(bRaw[:]))
|
|
// return hex.EncodeToString(b[:])
|
|
}
|
|
|
|
// BigInt returns the *big.Int representation of the *Hash
|
|
func (h *Hash) BigInt() *big.Int {
|
|
if new(big.Int).SetBytes(SwapEndianness(h[:])) == nil {
|
|
return big.NewInt(0)
|
|
}
|
|
return new(big.Int).SetBytes(SwapEndianness(h[:]))
|
|
}
|
|
|
|
// Bytes returns the []byte representation of the *Hash, which always is 32
|
|
// bytes length.
|
|
func (h *Hash) Bytes() []byte {
|
|
bi := new(big.Int).SetBytes(h[:]).Bytes()
|
|
b := [32]byte{}
|
|
copy(b[:], SwapEndianness(bi[:]))
|
|
return b[:]
|
|
}
|
|
|
|
// NewBigIntFromHashBytes returns a *big.Int from a byte array, swapping the
|
|
// endianness in the process. This is the intended method to get a *big.Int
|
|
// from a byte array that previously has ben generated by the Hash.Bytes()
|
|
// method.
|
|
func NewBigIntFromHashBytes(b []byte) (*big.Int, error) {
|
|
if len(b) != ElemBytesLen {
|
|
return nil, fmt.Errorf("Expected 32 bytes, found %d bytes", len(b))
|
|
}
|
|
bi := new(big.Int).SetBytes(b[:ElemBytesLen])
|
|
if !cryptoUtils.CheckBigIntInField(bi) {
|
|
return nil, fmt.Errorf("NewBigIntFromHashBytes: Value not inside the Finite Field")
|
|
}
|
|
return bi, nil
|
|
}
|
|
|
|
// NewHashFromBigInt returns a *Hash representation of the given *big.Int
|
|
func NewHashFromBigInt(b *big.Int) *Hash {
|
|
r := &Hash{}
|
|
copy(r[:], SwapEndianness(b.Bytes()))
|
|
return r
|
|
}
|
|
|
|
// NewHashFromBytes returns a *Hash from a byte array, swapping the endianness
|
|
// in the process. This is the intended method to get a *Hash from a byte array
|
|
// that previously has ben generated by the Hash.Bytes() method.
|
|
func NewHashFromBytes(b []byte) (*Hash, error) {
|
|
if len(b) != ElemBytesLen {
|
|
return nil, fmt.Errorf("Expected 32 bytes, found %d bytes", len(b))
|
|
}
|
|
var h Hash
|
|
copy(h[:], SwapEndianness(b))
|
|
return &h, nil
|
|
}
|
|
|
|
// NewHashFromHex returns a *Hash representation of the given hex string
|
|
func NewHashFromHex(h string) (*Hash, error) {
|
|
h = strings.TrimPrefix(h, "0x")
|
|
b, err := hex.DecodeString(h)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return NewHashFromBytes(SwapEndianness(b[:]))
|
|
}
|
|
|
|
// NewHashFromString returns a *Hash representation of the given decimal string
|
|
func NewHashFromString(s string) (*Hash, error) {
|
|
bi, ok := new(big.Int).SetString(s, 10)
|
|
if !ok {
|
|
return nil, fmt.Errorf("Can not parse string to Hash")
|
|
}
|
|
return NewHashFromBigInt(bi), nil
|
|
}
|
|
|
|
// MerkleTree is the struct with the main elements of the MerkleTree
|
|
type MerkleTree struct {
|
|
sync.RWMutex
|
|
db Storage
|
|
rootKey *Hash
|
|
writable bool
|
|
maxLevels int
|
|
}
|
|
|
|
// NewMerkleTree loads a new MerkleTree. If in the storage already exists one
|
|
// will open that one, if not, will create a new one.
|
|
func NewMerkleTree(storage Storage, maxLevels int) (*MerkleTree, error) {
|
|
mt := MerkleTree{db: storage, maxLevels: maxLevels, writable: true}
|
|
|
|
root, err := mt.dbGetRoot()
|
|
if err == ErrNotFound {
|
|
tx, err := mt.db.NewTx()
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
mt.rootKey = &HashZero
|
|
err = tx.SetRoot(mt.rootKey)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
err = tx.Commit()
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return &mt, nil
|
|
} else if err != nil {
|
|
return nil, err
|
|
}
|
|
mt.rootKey = root
|
|
return &mt, nil
|
|
}
|
|
|
|
func (mt *MerkleTree) dbGetRoot() (*Hash, error) {
|
|
hash, err := mt.db.GetRoot()
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return hash, nil
|
|
}
|
|
|
|
// DB returns the MerkleTree.DB()
|
|
func (mt *MerkleTree) DB() Storage {
|
|
return mt.db
|
|
}
|
|
|
|
// Root returns the MerkleRoot
|
|
func (mt *MerkleTree) Root() *Hash {
|
|
return mt.rootKey
|
|
}
|
|
|
|
// MaxLevels returns the MT maximum level
|
|
func (mt *MerkleTree) MaxLevels() int {
|
|
return mt.maxLevels
|
|
}
|
|
|
|
// Snapshot returns a read-only copy of the MerkleTree
|
|
func (mt *MerkleTree) Snapshot(rootKey *Hash) (*MerkleTree, error) {
|
|
mt.RLock()
|
|
defer mt.RUnlock()
|
|
_, err := mt.GetNode(rootKey)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return &MerkleTree{db: mt.db, maxLevels: mt.maxLevels, rootKey: rootKey, writable: false}, nil
|
|
}
|
|
|
|
// Add adds a Key & Value into the MerkleTree. Where the `k` determines the
|
|
// path from the Root to the Leaf.
|
|
func (mt *MerkleTree) Add(k, v *big.Int) error {
|
|
// verify that the MerkleTree is writable
|
|
if !mt.writable {
|
|
return ErrNotWritable
|
|
}
|
|
|
|
// verify that k & v are valid and fit inside the Finite Field.
|
|
if !cryptoUtils.CheckBigIntInField(k) {
|
|
return errors.New("Key not inside the Finite Field")
|
|
}
|
|
if !cryptoUtils.CheckBigIntInField(v) {
|
|
return errors.New("Value not inside the Finite Field")
|
|
}
|
|
|
|
tx, err := mt.db.NewTx()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
mt.Lock()
|
|
defer mt.Unlock()
|
|
|
|
kHash := NewHashFromBigInt(k)
|
|
vHash := NewHashFromBigInt(v)
|
|
newNodeLeaf := NewNodeLeaf(kHash, vHash)
|
|
path := getPath(mt.maxLevels, kHash[:])
|
|
|
|
newRootKey, err := mt.addLeaf(tx, newNodeLeaf, mt.rootKey, 0, path)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
mt.rootKey = newRootKey
|
|
err = mt.setCurrentRoot(tx, mt.rootKey)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
if err := tx.Commit(); err != nil {
|
|
return err
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
// AddAndGetCircomProof does an Add, and returns a CircomProcessorProof
|
|
func (mt *MerkleTree) AddAndGetCircomProof(k,
|
|
v *big.Int) (*CircomProcessorProof, error) {
|
|
var cp CircomProcessorProof
|
|
cp.Fnc = 2
|
|
cp.OldRoot = mt.rootKey
|
|
gotK, gotV, _, err := mt.Get(k)
|
|
if err != nil && err != ErrKeyNotFound {
|
|
return nil, err
|
|
}
|
|
cp.OldKey = NewHashFromBigInt(gotK)
|
|
cp.OldValue = NewHashFromBigInt(gotV)
|
|
if bytes.Equal(cp.OldKey[:], HashZero[:]) {
|
|
cp.IsOld0 = true
|
|
}
|
|
_, _, siblings, err := mt.Get(k)
|
|
if err != nil && err != ErrKeyNotFound {
|
|
return nil, err
|
|
}
|
|
cp.Siblings = CircomSiblingsFromSiblings(siblings, mt.maxLevels)
|
|
|
|
err = mt.Add(k, v)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
cp.NewKey = NewHashFromBigInt(k)
|
|
cp.NewValue = NewHashFromBigInt(v)
|
|
cp.NewRoot = mt.rootKey
|
|
|
|
return &cp, nil
|
|
}
|
|
|
|
// pushLeaf recursively pushes an existing oldLeaf down until its path diverges
|
|
// from newLeaf, at which point both leafs are stored, all while updating the
|
|
// path.
|
|
func (mt *MerkleTree) pushLeaf(tx Tx, newLeaf *Node, oldLeaf *Node, lvl int,
|
|
pathNewLeaf []bool, pathOldLeaf []bool) (*Hash, error) {
|
|
if lvl > mt.maxLevels-2 {
|
|
return nil, ErrReachedMaxLevel
|
|
}
|
|
var newNodeMiddle *Node
|
|
if pathNewLeaf[lvl] == pathOldLeaf[lvl] { // We need to go deeper!
|
|
nextKey, err := mt.pushLeaf(tx, newLeaf, oldLeaf, lvl+1, pathNewLeaf, pathOldLeaf)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
if pathNewLeaf[lvl] { // go right
|
|
newNodeMiddle = NewNodeMiddle(&HashZero, nextKey)
|
|
} else { // go left
|
|
newNodeMiddle = NewNodeMiddle(nextKey, &HashZero)
|
|
}
|
|
return mt.addNode(tx, newNodeMiddle)
|
|
}
|
|
oldLeafKey, err := oldLeaf.Key()
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
newLeafKey, err := newLeaf.Key()
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
if pathNewLeaf[lvl] {
|
|
newNodeMiddle = NewNodeMiddle(oldLeafKey, newLeafKey)
|
|
} else {
|
|
newNodeMiddle = NewNodeMiddle(newLeafKey, oldLeafKey)
|
|
}
|
|
// We can add newLeaf now. We don't need to add oldLeaf because it's
|
|
// already in the tree.
|
|
_, err = mt.addNode(tx, newLeaf)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return mt.addNode(tx, newNodeMiddle)
|
|
}
|
|
|
|
// addLeaf recursively adds a newLeaf in the MT while updating the path.
|
|
func (mt *MerkleTree) addLeaf(tx Tx, newLeaf *Node, key *Hash,
|
|
lvl int, path []bool) (*Hash, error) {
|
|
var err error
|
|
var nextKey *Hash
|
|
if lvl > mt.maxLevels-1 {
|
|
return nil, ErrReachedMaxLevel
|
|
}
|
|
n, err := mt.GetNode(key)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
switch n.Type {
|
|
case NodeTypeEmpty:
|
|
// We can add newLeaf now
|
|
return mt.addNode(tx, newLeaf)
|
|
case NodeTypeLeaf:
|
|
nKey := n.Entry[0]
|
|
// Check if leaf node found contains the leaf node we are
|
|
// trying to add
|
|
newLeafKey := newLeaf.Entry[0]
|
|
if bytes.Equal(nKey[:], newLeafKey[:]) {
|
|
return nil, ErrEntryIndexAlreadyExists
|
|
}
|
|
pathOldLeaf := getPath(mt.maxLevels, nKey[:])
|
|
// We need to push newLeaf down until its path diverges from
|
|
// n's path
|
|
return mt.pushLeaf(tx, newLeaf, n, lvl, path, pathOldLeaf)
|
|
case NodeTypeMiddle:
|
|
// We need to go deeper, continue traversing the tree, left or
|
|
// right depending on path
|
|
var newNodeMiddle *Node
|
|
if path[lvl] { // go right
|
|
nextKey, err = mt.addLeaf(tx, newLeaf, n.ChildR, lvl+1, path)
|
|
newNodeMiddle = NewNodeMiddle(n.ChildL, nextKey)
|
|
} else { // go left
|
|
nextKey, err = mt.addLeaf(tx, newLeaf, n.ChildL, lvl+1, path)
|
|
newNodeMiddle = NewNodeMiddle(nextKey, n.ChildR)
|
|
}
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
// Update the node to reflect the modified child
|
|
return mt.addNode(tx, newNodeMiddle)
|
|
default:
|
|
return nil, ErrInvalidNodeFound
|
|
}
|
|
}
|
|
|
|
// addNode adds a node into the MT. Empty nodes are not stored in the tree;
|
|
// they are all the same and assumed to always exist.
|
|
func (mt *MerkleTree) addNode(tx Tx, n *Node) (*Hash, error) {
|
|
// verify that the MerkleTree is writable
|
|
if !mt.writable {
|
|
return nil, ErrNotWritable
|
|
}
|
|
if n.Type == NodeTypeEmpty {
|
|
return n.Key()
|
|
}
|
|
k, err := n.Key()
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
//v := n.Value()
|
|
// Check that the node key doesn't already exist
|
|
if _, err := tx.Get(k[:]); err == nil {
|
|
return nil, ErrNodeKeyAlreadyExists
|
|
}
|
|
err = tx.Put(k[:], n)
|
|
return k, err
|
|
}
|
|
|
|
// updateNode updates an existing node in the MT. Empty nodes are not stored
|
|
// in the tree; they are all the same and assumed to always exist.
|
|
func (mt *MerkleTree) updateNode(tx Tx, n *Node) (*Hash, error) {
|
|
// verify that the MerkleTree is writable
|
|
if !mt.writable {
|
|
return nil, ErrNotWritable
|
|
}
|
|
if n.Type == NodeTypeEmpty {
|
|
return n.Key()
|
|
}
|
|
k, err := n.Key()
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
//v := n.Value()
|
|
err = tx.Put(k[:], n)
|
|
return k, err
|
|
}
|
|
|
|
// Get returns the value of the leaf for the given key
|
|
func (mt *MerkleTree) Get(k *big.Int) (*big.Int, *big.Int, []*Hash, error) {
|
|
// verify that k is valid and fits inside the Finite Field.
|
|
if !cryptoUtils.CheckBigIntInField(k) {
|
|
return nil, nil, nil, errors.New("Key not inside the Finite Field")
|
|
}
|
|
|
|
kHash := NewHashFromBigInt(k)
|
|
path := getPath(mt.maxLevels, kHash[:])
|
|
|
|
nextKey := mt.rootKey
|
|
siblings := []*Hash{}
|
|
for i := 0; i < mt.maxLevels; i++ {
|
|
n, err := mt.GetNode(nextKey)
|
|
if err != nil {
|
|
return nil, nil, nil, err
|
|
}
|
|
switch n.Type {
|
|
case NodeTypeEmpty:
|
|
return big.NewInt(0), big.NewInt(0), siblings, ErrKeyNotFound
|
|
case NodeTypeLeaf:
|
|
if bytes.Equal(kHash[:], n.Entry[0][:]) {
|
|
return n.Entry[0].BigInt(), n.Entry[1].BigInt(), siblings, nil
|
|
}
|
|
return n.Entry[0].BigInt(), n.Entry[1].BigInt(), siblings, ErrKeyNotFound
|
|
case NodeTypeMiddle:
|
|
if path[i] {
|
|
nextKey = n.ChildR
|
|
siblings = append(siblings, n.ChildL)
|
|
} else {
|
|
nextKey = n.ChildL
|
|
siblings = append(siblings, n.ChildR)
|
|
}
|
|
default:
|
|
return nil, nil, nil, ErrInvalidNodeFound
|
|
}
|
|
}
|
|
|
|
return nil, nil, nil, ErrReachedMaxLevel
|
|
}
|
|
|
|
// Update updates the value of a specified key in the MerkleTree, and updates
|
|
// the path from the leaf to the Root with the new values. Returns the
|
|
// CircomProcessorProof.
|
|
func (mt *MerkleTree) Update(k, v *big.Int) (*CircomProcessorProof, error) {
|
|
// verify that the MerkleTree is writable
|
|
if !mt.writable {
|
|
return nil, ErrNotWritable
|
|
}
|
|
|
|
// verify that k & v are valid and fit inside the Finite Field.
|
|
if !cryptoUtils.CheckBigIntInField(k) {
|
|
return nil, errors.New("Key not inside the Finite Field")
|
|
}
|
|
if !cryptoUtils.CheckBigIntInField(v) {
|
|
return nil, errors.New("Key not inside the Finite Field")
|
|
}
|
|
tx, err := mt.db.NewTx()
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
mt.Lock()
|
|
defer mt.Unlock()
|
|
|
|
kHash := NewHashFromBigInt(k)
|
|
vHash := NewHashFromBigInt(v)
|
|
path := getPath(mt.maxLevels, kHash[:])
|
|
|
|
var cp CircomProcessorProof
|
|
cp.Fnc = 1
|
|
cp.OldRoot = mt.rootKey
|
|
cp.OldKey = kHash
|
|
cp.NewKey = kHash
|
|
cp.NewValue = vHash
|
|
|
|
nextKey := mt.rootKey
|
|
siblings := []*Hash{}
|
|
for i := 0; i < mt.maxLevels; i++ {
|
|
n, err := mt.GetNode(nextKey)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
switch n.Type {
|
|
case NodeTypeEmpty:
|
|
return nil, ErrKeyNotFound
|
|
case NodeTypeLeaf:
|
|
if bytes.Equal(kHash[:], n.Entry[0][:]) {
|
|
cp.OldValue = n.Entry[1]
|
|
cp.Siblings = CircomSiblingsFromSiblings(siblings, mt.maxLevels)
|
|
// update leaf and upload to the root
|
|
newNodeLeaf := NewNodeLeaf(kHash, vHash)
|
|
_, err := mt.updateNode(tx, newNodeLeaf)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
newRootKey, err :=
|
|
mt.recalculatePathUntilRoot(tx, path, newNodeLeaf, siblings)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
mt.rootKey = newRootKey
|
|
err = mt.setCurrentRoot(tx, mt.rootKey)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
cp.NewRoot = newRootKey
|
|
if err := tx.Commit(); err != nil {
|
|
return nil, err
|
|
}
|
|
return &cp, nil
|
|
}
|
|
return nil, ErrKeyNotFound
|
|
case NodeTypeMiddle:
|
|
if path[i] {
|
|
nextKey = n.ChildR
|
|
siblings = append(siblings, n.ChildL)
|
|
} else {
|
|
nextKey = n.ChildL
|
|
siblings = append(siblings, n.ChildR)
|
|
}
|
|
default:
|
|
return nil, ErrInvalidNodeFound
|
|
}
|
|
}
|
|
|
|
return nil, ErrKeyNotFound
|
|
}
|
|
|
|
// Delete removes the specified Key from the MerkleTree and updates the path
|
|
// from the deleted key to the Root with the new values. This method removes
|
|
// the key from the MerkleTree, but does not remove the old nodes from the
|
|
// key-value database; this means that if the tree is accessed by an old Root
|
|
// where the key was not deleted yet, the key will still exist. If is desired
|
|
// to remove the key-values from the database that are not under the current
|
|
// Root, an option could be to dump all the leaves (using mt.DumpLeafs) and
|
|
// import them in a new MerkleTree in a new database (using
|
|
// mt.ImportDumpedLeafs), but this will loose all the Root history of the
|
|
// MerkleTree
|
|
func (mt *MerkleTree) Delete(k *big.Int) error {
|
|
// verify that the MerkleTree is writable
|
|
if !mt.writable {
|
|
return ErrNotWritable
|
|
}
|
|
|
|
// verify that k is valid and fits inside the Finite Field.
|
|
if !cryptoUtils.CheckBigIntInField(k) {
|
|
return errors.New("Key not inside the Finite Field")
|
|
}
|
|
tx, err := mt.db.NewTx()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
mt.Lock()
|
|
defer mt.Unlock()
|
|
|
|
kHash := NewHashFromBigInt(k)
|
|
path := getPath(mt.maxLevels, kHash[:])
|
|
|
|
nextKey := mt.rootKey
|
|
siblings := []*Hash{}
|
|
for i := 0; i < mt.maxLevels; i++ {
|
|
n, err := mt.GetNode(nextKey)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
switch n.Type {
|
|
case NodeTypeEmpty:
|
|
return ErrKeyNotFound
|
|
case NodeTypeLeaf:
|
|
if bytes.Equal(kHash[:], n.Entry[0][:]) {
|
|
// remove and go up with the sibling
|
|
err = mt.rmAndUpload(tx, path, kHash, siblings)
|
|
return err
|
|
}
|
|
return ErrKeyNotFound
|
|
case NodeTypeMiddle:
|
|
if path[i] {
|
|
nextKey = n.ChildR
|
|
siblings = append(siblings, n.ChildL)
|
|
} else {
|
|
nextKey = n.ChildL
|
|
siblings = append(siblings, n.ChildR)
|
|
}
|
|
default:
|
|
return ErrInvalidNodeFound
|
|
}
|
|
}
|
|
|
|
return ErrKeyNotFound
|
|
}
|
|
|
|
// rmAndUpload removes the key, and goes up until the root updating all the
|
|
// nodes with the new values.
|
|
func (mt *MerkleTree) rmAndUpload(tx Tx, path []bool, kHash *Hash, siblings []*Hash) error {
|
|
if len(siblings) == 0 {
|
|
mt.rootKey = &HashZero
|
|
err := mt.setCurrentRoot(tx, mt.rootKey)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
return tx.Commit()
|
|
}
|
|
|
|
toUpload := siblings[len(siblings)-1]
|
|
if len(siblings) < 2 { //nolint:gomnd
|
|
mt.rootKey = siblings[0]
|
|
err := mt.setCurrentRoot(tx, mt.rootKey)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
return tx.Commit()
|
|
}
|
|
for i := len(siblings) - 2; i >= 0; i-- { //nolint:gomnd
|
|
if !bytes.Equal(siblings[i][:], HashZero[:]) {
|
|
var newNode *Node
|
|
if path[i] {
|
|
newNode = NewNodeMiddle(siblings[i], toUpload)
|
|
} else {
|
|
newNode = NewNodeMiddle(toUpload, siblings[i])
|
|
}
|
|
_, err := mt.addNode(tx, newNode)
|
|
if err != ErrNodeKeyAlreadyExists && err != nil {
|
|
return err
|
|
}
|
|
// go up until the root
|
|
newRootKey, err := mt.recalculatePathUntilRoot(tx, path, newNode,
|
|
siblings[:i])
|
|
if err != nil {
|
|
return err
|
|
}
|
|
mt.rootKey = newRootKey
|
|
err = mt.setCurrentRoot(tx, mt.rootKey)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
break
|
|
}
|
|
// if i==0 (root position), stop and store the sibling of the
|
|
// deleted leaf as root
|
|
if i == 0 {
|
|
mt.rootKey = toUpload
|
|
err := mt.setCurrentRoot(tx, mt.rootKey)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
break
|
|
}
|
|
}
|
|
if err := tx.Commit(); err != nil {
|
|
return err
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
// recalculatePathUntilRoot recalculates the nodes until the Root
|
|
func (mt *MerkleTree) recalculatePathUntilRoot(tx Tx, path []bool, node *Node,
|
|
siblings []*Hash) (*Hash, error) {
|
|
for i := len(siblings) - 1; i >= 0; i-- {
|
|
nodeKey, err := node.Key()
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
if path[i] {
|
|
node = NewNodeMiddle(siblings[i], nodeKey)
|
|
} else {
|
|
node = NewNodeMiddle(nodeKey, siblings[i])
|
|
}
|
|
_, err = mt.addNode(tx, node)
|
|
if err != ErrNodeKeyAlreadyExists && err != nil {
|
|
return nil, err
|
|
}
|
|
}
|
|
|
|
// return last node added, which is the root
|
|
nodeKey, err := node.Key()
|
|
return nodeKey, err
|
|
}
|
|
|
|
// setCurrentRoot is a helper function to update current root in an open db
|
|
// transaction.
|
|
func (mt *MerkleTree) setCurrentRoot(tx Tx, hash *Hash) error {
|
|
return tx.SetRoot(hash)
|
|
}
|
|
|
|
// GetNode gets a node by key from the MT. Empty nodes are not stored in the
|
|
// tree; they are all the same and assumed to always exist.
|
|
func (mt *MerkleTree) GetNode(key *Hash) (*Node, error) {
|
|
if bytes.Equal(key[:], HashZero[:]) {
|
|
return NewNodeEmpty(), nil
|
|
}
|
|
n, err := mt.db.Get(key[:])
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return n, nil
|
|
}
|
|
|
|
// getPath returns the binary path, from the root to the leaf.
|
|
func getPath(numLevels int, k []byte) []bool {
|
|
path := make([]bool, numLevels)
|
|
for n := 0; n < numLevels; n++ {
|
|
path[n] = TestBit(k[:], uint(n))
|
|
}
|
|
return path
|
|
}
|
|
|
|
// NodeAux contains the auxiliary node used in a non-existence proof.
|
|
type NodeAux struct {
|
|
Key *Hash
|
|
Value *Hash
|
|
}
|
|
|
|
// Proof defines the required elements for a MT proof of existence or
|
|
// non-existence.
|
|
type Proof struct {
|
|
// existence indicates wether this is a proof of existence or
|
|
// non-existence.
|
|
Existence bool
|
|
// depth indicates how deep in the tree the proof goes.
|
|
depth uint
|
|
// notempties is a bitmap of non-empty Siblings found in Siblings.
|
|
notempties [ElemBytesLen - proofFlagsLen]byte
|
|
// Siblings is a list of non-empty sibling keys.
|
|
Siblings []*Hash
|
|
NodeAux *NodeAux
|
|
}
|
|
|
|
// NewProofFromBytes parses a byte array into a Proof.
|
|
func NewProofFromBytes(bs []byte) (*Proof, error) {
|
|
if len(bs) < ElemBytesLen {
|
|
return nil, ErrInvalidProofBytes
|
|
}
|
|
p := &Proof{}
|
|
if (bs[0] & 0x01) == 0 {
|
|
p.Existence = true
|
|
}
|
|
p.depth = uint(bs[1])
|
|
copy(p.notempties[:], bs[proofFlagsLen:ElemBytesLen])
|
|
siblingBytes := bs[ElemBytesLen:]
|
|
sibIdx := 0
|
|
for i := uint(0); i < p.depth; i++ {
|
|
if TestBitBigEndian(p.notempties[:], i) {
|
|
if len(siblingBytes) < (sibIdx+1)*ElemBytesLen {
|
|
return nil, ErrInvalidProofBytes
|
|
}
|
|
var sib Hash
|
|
copy(sib[:], siblingBytes[sibIdx*ElemBytesLen:(sibIdx+1)*ElemBytesLen])
|
|
p.Siblings = append(p.Siblings, &sib)
|
|
sibIdx++
|
|
}
|
|
}
|
|
|
|
if !p.Existence && ((bs[0] & 0x02) != 0) {
|
|
p.NodeAux = &NodeAux{Key: &Hash{}, Value: &Hash{}}
|
|
nodeAuxBytes := siblingBytes[len(p.Siblings)*ElemBytesLen:]
|
|
if len(nodeAuxBytes) != 2*ElemBytesLen {
|
|
return nil, ErrInvalidProofBytes
|
|
}
|
|
copy(p.NodeAux.Key[:], nodeAuxBytes[:ElemBytesLen])
|
|
copy(p.NodeAux.Value[:], nodeAuxBytes[ElemBytesLen:2*ElemBytesLen])
|
|
}
|
|
return p, nil
|
|
}
|
|
|
|
// Bytes serializes a Proof into a byte array.
|
|
func (p *Proof) Bytes() []byte {
|
|
bsLen := proofFlagsLen + len(p.notempties) + ElemBytesLen*len(p.Siblings)
|
|
if p.NodeAux != nil {
|
|
bsLen += 2 * ElemBytesLen //nolint:gomnd
|
|
}
|
|
bs := make([]byte, bsLen)
|
|
|
|
if !p.Existence {
|
|
bs[0] |= 0x01
|
|
}
|
|
bs[1] = byte(p.depth)
|
|
copy(bs[proofFlagsLen:len(p.notempties)+proofFlagsLen], p.notempties[:])
|
|
siblingsBytes := bs[len(p.notempties)+proofFlagsLen:]
|
|
for i, k := range p.Siblings {
|
|
copy(siblingsBytes[i*ElemBytesLen:(i+1)*ElemBytesLen], k[:])
|
|
}
|
|
if p.NodeAux != nil {
|
|
bs[0] |= 0x02
|
|
copy(bs[len(bs)-2*ElemBytesLen:], p.NodeAux.Key[:])
|
|
copy(bs[len(bs)-1*ElemBytesLen:], p.NodeAux.Value[:])
|
|
}
|
|
return bs
|
|
}
|
|
|
|
// SiblingsFromProof returns all the siblings of the proof.
|
|
func SiblingsFromProof(proof *Proof) []*Hash {
|
|
sibIdx := 0
|
|
siblings := []*Hash{}
|
|
for lvl := 0; lvl < int(proof.depth); lvl++ {
|
|
if TestBitBigEndian(proof.notempties[:], uint(lvl)) {
|
|
siblings = append(siblings, proof.Siblings[sibIdx])
|
|
sibIdx++
|
|
} else {
|
|
siblings = append(siblings, &HashZero)
|
|
}
|
|
}
|
|
return siblings
|
|
}
|
|
|
|
// AllSiblings returns all the siblings of the proof.
|
|
func (p *Proof) AllSiblings() []*Hash {
|
|
return SiblingsFromProof(p)
|
|
}
|
|
|
|
// CircomSiblingsFromSiblings returns the full siblings compatible with circom
|
|
func CircomSiblingsFromSiblings(siblings []*Hash, levels int) []*Hash {
|
|
// Add the rest of empty levels to the siblings
|
|
for i := len(siblings); i < levels+1; i++ {
|
|
siblings = append(siblings, &HashZero)
|
|
}
|
|
return siblings
|
|
}
|
|
|
|
// CircomProcessorProof defines the ProcessorProof compatible with circom. Is
|
|
// the data of the proof between the transition from one state to another.
|
|
type CircomProcessorProof struct {
|
|
OldRoot *Hash `json:"oldRoot"`
|
|
NewRoot *Hash `json:"newRoot"`
|
|
Siblings []*Hash `json:"siblings"`
|
|
OldKey *Hash `json:"oldKey"`
|
|
OldValue *Hash `json:"oldValue"`
|
|
NewKey *Hash `json:"newKey"`
|
|
NewValue *Hash `json:"newValue"`
|
|
IsOld0 bool `json:"isOld0"`
|
|
// 0: NOP, 1: Update, 2: Insert, 3: Delete
|
|
Fnc int `json:"fnc"`
|
|
}
|
|
|
|
// String returns a human readable string representation of the
|
|
// CircomProcessorProof
|
|
func (p CircomProcessorProof) String() string {
|
|
buf := bytes.NewBufferString("{")
|
|
fmt.Fprintf(buf, " OldRoot: %v,\n", p.OldRoot)
|
|
fmt.Fprintf(buf, " NewRoot: %v,\n", p.NewRoot)
|
|
fmt.Fprintf(buf, " Siblings: [\n ")
|
|
for _, s := range p.Siblings {
|
|
fmt.Fprintf(buf, "%v, ", s)
|
|
}
|
|
fmt.Fprintf(buf, "\n ],\n")
|
|
fmt.Fprintf(buf, " OldKey: %v,\n", p.OldKey)
|
|
fmt.Fprintf(buf, " OldValue: %v,\n", p.OldValue)
|
|
fmt.Fprintf(buf, " NewKey: %v,\n", p.NewKey)
|
|
fmt.Fprintf(buf, " NewValue: %v,\n", p.NewValue)
|
|
fmt.Fprintf(buf, " IsOld0: %v,\n", p.IsOld0)
|
|
fmt.Fprintf(buf, "}\n")
|
|
|
|
return buf.String()
|
|
}
|
|
|
|
// CircomVerifierProof defines the VerifierProof compatible with circom. Is the
|
|
// data of the proof that a certain leaf exists in the MerkleTree.
|
|
type CircomVerifierProof struct {
|
|
Root *Hash `json:"root"`
|
|
Siblings []*Hash `json:"siblings"`
|
|
OldKey *Hash `json:"oldKey"`
|
|
OldValue *Hash `json:"oldValue"`
|
|
IsOld0 bool `json:"isOld0"`
|
|
Key *Hash `json:"key"`
|
|
Value *Hash `json:"value"`
|
|
Fnc int `json:"fnc"` // 0: inclusion, 1: non inclusion
|
|
}
|
|
|
|
// GenerateCircomVerifierProof returns the CircomVerifierProof for a certain
|
|
// key in the MerkleTree. If the rootKey is nil, the current merkletree root
|
|
// is used.
|
|
func (mt *MerkleTree) GenerateCircomVerifierProof(k *big.Int,
|
|
rootKey *Hash) (*CircomVerifierProof, error) {
|
|
cp, err := mt.GenerateSCVerifierProof(k, rootKey)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
cp.Siblings = CircomSiblingsFromSiblings(cp.Siblings, mt.maxLevels)
|
|
return cp, nil
|
|
}
|
|
|
|
// GenerateSCVerifierProof returns the CircomVerifierProof for a certain key in
|
|
// the MerkleTree with the Siblings without the extra 0 needed at the circom
|
|
// circuits, which makes it straight forward to verifiy inside a Smart
|
|
// Contract. If the rootKey is nil, the current merkletree root is used.
|
|
func (mt *MerkleTree) GenerateSCVerifierProof(k *big.Int,
|
|
rootKey *Hash) (*CircomVerifierProof, error) {
|
|
if rootKey == nil {
|
|
rootKey = mt.Root()
|
|
}
|
|
p, v, err := mt.GenerateProof(k, rootKey)
|
|
if err != nil && err != ErrKeyNotFound {
|
|
return nil, err
|
|
}
|
|
var cp CircomVerifierProof
|
|
cp.Root = rootKey
|
|
cp.Siblings = p.AllSiblings()
|
|
if p.NodeAux != nil {
|
|
cp.OldKey = p.NodeAux.Key
|
|
cp.OldValue = p.NodeAux.Value
|
|
} else {
|
|
cp.OldKey = &HashZero
|
|
cp.OldValue = &HashZero
|
|
}
|
|
cp.Key = NewHashFromBigInt(k)
|
|
cp.Value = NewHashFromBigInt(v)
|
|
if p.Existence {
|
|
cp.Fnc = 0 // inclusion
|
|
} else {
|
|
cp.Fnc = 1 // non inclusion
|
|
}
|
|
|
|
return &cp, nil
|
|
}
|
|
|
|
// GenerateProof generates the proof of existence (or non-existence) of an
|
|
// Entry's hash Index for a Merkle Tree given the root.
|
|
// If the rootKey is nil, the current merkletree root is used
|
|
func (mt *MerkleTree) GenerateProof(k *big.Int, rootKey *Hash) (*Proof,
|
|
*big.Int, error) {
|
|
p := &Proof{}
|
|
var siblingKey *Hash
|
|
|
|
kHash := NewHashFromBigInt(k)
|
|
path := getPath(mt.maxLevels, kHash[:])
|
|
if rootKey == nil {
|
|
rootKey = mt.Root()
|
|
}
|
|
nextKey := rootKey
|
|
for p.depth = 0; p.depth < uint(mt.maxLevels); p.depth++ {
|
|
n, err := mt.GetNode(nextKey)
|
|
if err != nil {
|
|
return nil, nil, err
|
|
}
|
|
switch n.Type {
|
|
case NodeTypeEmpty:
|
|
return p, big.NewInt(0), nil
|
|
case NodeTypeLeaf:
|
|
if bytes.Equal(kHash[:], n.Entry[0][:]) {
|
|
p.Existence = true
|
|
return p, n.Entry[1].BigInt(), nil
|
|
}
|
|
// We found a leaf whose entry didn't match hIndex
|
|
p.NodeAux = &NodeAux{Key: n.Entry[0], Value: n.Entry[1]}
|
|
return p, n.Entry[1].BigInt(), nil
|
|
case NodeTypeMiddle:
|
|
if path[p.depth] {
|
|
nextKey = n.ChildR
|
|
siblingKey = n.ChildL
|
|
} else {
|
|
nextKey = n.ChildL
|
|
siblingKey = n.ChildR
|
|
}
|
|
default:
|
|
return nil, nil, ErrInvalidNodeFound
|
|
}
|
|
if !bytes.Equal(siblingKey[:], HashZero[:]) {
|
|
SetBitBigEndian(p.notempties[:], uint(p.depth))
|
|
p.Siblings = append(p.Siblings, siblingKey)
|
|
}
|
|
}
|
|
return nil, nil, ErrKeyNotFound
|
|
}
|
|
|
|
// VerifyProof verifies the Merkle Proof for the entry and root.
|
|
func VerifyProof(rootKey *Hash, proof *Proof, k, v *big.Int) bool {
|
|
rootFromProof, err := RootFromProof(proof, k, v)
|
|
if err != nil {
|
|
return false
|
|
}
|
|
return bytes.Equal(rootKey[:], rootFromProof[:])
|
|
}
|
|
|
|
// RootFromProof calculates the root that would correspond to a tree whose
|
|
// siblings are the ones in the proof with the leaf hashing to hIndex and
|
|
// hValue.
|
|
func RootFromProof(proof *Proof, k, v *big.Int) (*Hash, error) {
|
|
kHash := NewHashFromBigInt(k)
|
|
vHash := NewHashFromBigInt(v)
|
|
sibIdx := len(proof.Siblings) - 1
|
|
var err error
|
|
var midKey *Hash
|
|
if proof.Existence {
|
|
midKey, err = LeafKey(kHash, vHash)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
} else {
|
|
if proof.NodeAux == nil {
|
|
midKey = &HashZero
|
|
} else {
|
|
if bytes.Equal(kHash[:], proof.NodeAux.Key[:]) {
|
|
return nil,
|
|
fmt.Errorf("Non-existence proof being checked against hIndex equal to nodeAux")
|
|
}
|
|
midKey, err = LeafKey(proof.NodeAux.Key, proof.NodeAux.Value)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
}
|
|
}
|
|
path := getPath(int(proof.depth), kHash[:])
|
|
var siblingKey *Hash
|
|
for lvl := int(proof.depth) - 1; lvl >= 0; lvl-- {
|
|
if TestBitBigEndian(proof.notempties[:], uint(lvl)) {
|
|
siblingKey = proof.Siblings[sibIdx]
|
|
sibIdx--
|
|
} else {
|
|
siblingKey = &HashZero
|
|
}
|
|
if path[lvl] {
|
|
midKey, err = NewNodeMiddle(siblingKey, midKey).Key()
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
} else {
|
|
midKey, err = NewNodeMiddle(midKey, siblingKey).Key()
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
}
|
|
}
|
|
return midKey, nil
|
|
}
|
|
|
|
// walk is a helper recursive function to iterate over all tree branches
|
|
func (mt *MerkleTree) walk(key *Hash, f func(*Node)) error {
|
|
n, err := mt.GetNode(key)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
switch n.Type {
|
|
case NodeTypeEmpty:
|
|
f(n)
|
|
case NodeTypeLeaf:
|
|
f(n)
|
|
case NodeTypeMiddle:
|
|
f(n)
|
|
if err := mt.walk(n.ChildL, f); err != nil {
|
|
return err
|
|
}
|
|
if err := mt.walk(n.ChildR, f); err != nil {
|
|
return err
|
|
}
|
|
default:
|
|
return ErrInvalidNodeFound
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// Walk iterates over all the branches of a MerkleTree with the given rootKey
|
|
// if rootKey is nil, it will get the current RootKey of the current state of
|
|
// the MerkleTree. For each node, it calls the f function given in the
|
|
// parameters. See some examples of the Walk function usage in the
|
|
// merkletree.go and merkletree_test.go
|
|
func (mt *MerkleTree) Walk(rootKey *Hash, f func(*Node)) error {
|
|
if rootKey == nil {
|
|
rootKey = mt.Root()
|
|
}
|
|
err := mt.walk(rootKey, f)
|
|
return err
|
|
}
|
|
|
|
// GraphViz uses Walk function to generate a string GraphViz representation of
|
|
// the tree and writes it to w
|
|
func (mt *MerkleTree) GraphViz(w io.Writer, rootKey *Hash) error {
|
|
fmt.Fprintf(w, `digraph hierarchy {
|
|
node [fontname=Monospace,fontsize=10,shape=box]
|
|
`)
|
|
cnt := 0
|
|
var errIn error
|
|
err := mt.Walk(rootKey, func(n *Node) {
|
|
k, err := n.Key()
|
|
if err != nil {
|
|
errIn = err
|
|
}
|
|
switch n.Type {
|
|
case NodeTypeEmpty:
|
|
case NodeTypeLeaf:
|
|
fmt.Fprintf(w, "\"%v\" [style=filled];\n", k.String())
|
|
case NodeTypeMiddle:
|
|
lr := [2]string{n.ChildL.String(), n.ChildR.String()}
|
|
emptyNodes := ""
|
|
for i := range lr {
|
|
if lr[i] == "0" {
|
|
lr[i] = fmt.Sprintf("empty%v", cnt)
|
|
emptyNodes += fmt.Sprintf("\"%v\" [style=dashed,label=0];\n", lr[i])
|
|
cnt++
|
|
}
|
|
}
|
|
fmt.Fprintf(w, "\"%v\" -> {\"%v\" \"%v\"}\n", k.String(), lr[0], lr[1])
|
|
fmt.Fprint(w, emptyNodes)
|
|
default:
|
|
}
|
|
})
|
|
fmt.Fprintf(w, "}\n")
|
|
if errIn != nil {
|
|
return errIn
|
|
}
|
|
return err
|
|
}
|
|
|
|
// PrintGraphViz prints directly the GraphViz() output
|
|
func (mt *MerkleTree) PrintGraphViz(rootKey *Hash) error {
|
|
if rootKey == nil {
|
|
rootKey = mt.Root()
|
|
}
|
|
w := bytes.NewBufferString("")
|
|
fmt.Fprintf(w,
|
|
"--------\nGraphViz of the MerkleTree with RootKey "+rootKey.BigInt().String()+"\n")
|
|
err := mt.GraphViz(w, nil)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
fmt.Fprintf(w,
|
|
"End of GraphViz of the MerkleTree with RootKey "+rootKey.BigInt().String()+"\n--------\n")
|
|
|
|
fmt.Println(w)
|
|
return nil
|
|
}
|
|
|
|
// DumpLeafs returns all the Leafs that exist under the given Root. If no Root
|
|
// is given (nil), it uses the current Root of the MerkleTree.
|
|
func (mt *MerkleTree) DumpLeafs(rootKey *Hash) ([]byte, error) {
|
|
var b []byte
|
|
err := mt.Walk(rootKey, func(n *Node) {
|
|
if n.Type == NodeTypeLeaf {
|
|
l := n.Entry[0].Bytes()
|
|
r := n.Entry[1].Bytes()
|
|
b = append(b, append(l[:], r[:]...)...)
|
|
}
|
|
})
|
|
return b, err
|
|
}
|
|
|
|
// ImportDumpedLeafs parses and adds to the MerkleTree the dumped list of leafs
|
|
// from the DumpLeafs function.
|
|
func (mt *MerkleTree) ImportDumpedLeafs(b []byte) error {
|
|
for i := 0; i < len(b); i += 64 {
|
|
lr := b[i : i+64]
|
|
lB, err := NewBigIntFromHashBytes(lr[:32])
|
|
if err != nil {
|
|
return err
|
|
}
|
|
rB, err := NewBigIntFromHashBytes(lr[32:])
|
|
if err != nil {
|
|
return err
|
|
}
|
|
err = mt.Add(lB, rB)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
}
|
|
return nil
|
|
}
|