package merkletree
|
|
|
|
import (
|
|
"fmt"
|
|
"math/big"
|
|
)
|
|
|
|
// NodeType defines the type of node in the MT.
|
|
type NodeType byte
|
|
|
|
const (
|
|
// NodeTypeMiddle indicates the type of middle Node that has children.
|
|
NodeTypeMiddle NodeType = 0
|
|
// NodeTypeLeaf indicates the type of a leaf Node that contains a claim.
|
|
NodeTypeLeaf NodeType = 1
|
|
// NodeTypeEmpty indicates the type of an empty Node.
|
|
NodeTypeEmpty NodeType = 2
|
|
|
|
// DBEntryTypeRoot indicates the type of a DB entry that indicates the current Root of a MerkleTree
|
|
DBEntryTypeRoot NodeType = 3
|
|
)
|
|
|
|
// Node is the struct that represents a node in the MT. The node should not be
|
|
// modified after creation because the cached key won't be updated.
|
|
type Node struct {
|
|
// Type is the type of node in the tree.
|
|
Type NodeType
|
|
// ChildL is the left child of a middle node.
|
|
ChildL *Hash
|
|
// ChildR is the right child of a middle node.
|
|
ChildR *Hash
|
|
// Entry is the data stored in a leaf node.
|
|
Entry [2]*Hash
|
|
// key is a cache used to avoid recalculating key
|
|
key *Hash
|
|
}
|
|
|
|
// NewNodeLeaf creates a new leaf node.
|
|
func NewNodeLeaf(k, v *Hash) *Node {
|
|
return &Node{Type: NodeTypeLeaf, Entry: [2]*Hash{k, v}}
|
|
}
|
|
|
|
// NewNodeMiddle creates a new middle node.
|
|
func NewNodeMiddle(childL *Hash, childR *Hash) *Node {
|
|
return &Node{Type: NodeTypeMiddle, ChildL: childL, ChildR: childR}
|
|
}
|
|
|
|
// NewNodeEmpty creates a new empty node.
|
|
func NewNodeEmpty() *Node {
|
|
return &Node{Type: NodeTypeEmpty}
|
|
}
|
|
|
|
// NewNodeFromBytes creates a new node by parsing the input []byte.
|
|
func NewNodeFromBytes(b []byte) (*Node, error) {
|
|
if len(b) < 1 {
|
|
return nil, ErrNodeDataBadSize
|
|
}
|
|
n := Node{Type: NodeType(b[0])}
|
|
b = b[1:]
|
|
switch n.Type {
|
|
case NodeTypeMiddle:
|
|
if len(b) != 2*ElemBytesLen {
|
|
return nil, ErrNodeDataBadSize
|
|
}
|
|
n.ChildL, n.ChildR = &Hash{}, &Hash{}
|
|
copy(n.ChildL[:], b[:ElemBytesLen])
|
|
copy(n.ChildR[:], b[ElemBytesLen:ElemBytesLen*2])
|
|
case NodeTypeLeaf:
|
|
if len(b) != 2*ElemBytesLen {
|
|
return nil, ErrNodeDataBadSize
|
|
}
|
|
n.Entry = [2]*Hash{&Hash{}, &Hash{}}
|
|
copy(n.Entry[0][:], b[0:32])
|
|
copy(n.Entry[1][:], b[32:64])
|
|
case NodeTypeEmpty:
|
|
break
|
|
default:
|
|
return nil, ErrInvalidNodeFound
|
|
}
|
|
return &n, nil
|
|
}
|
|
|
|
// LeafKey computes the key of a leaf node given the hIndex and hValue of the
|
|
// entry of the leaf.
|
|
func LeafKey(k, v *Hash) (*Hash, error) {
|
|
return HashElemsKey(big.NewInt(1), k.BigInt(), v.BigInt())
|
|
}
|
|
|
|
// Key computes the key of the node by hashing the content in a specific way
|
|
// for each type of node. This key is used as the hash of the merklee tree for
|
|
// each node.
|
|
func (n *Node) Key() (*Hash, error) {
|
|
if n.key == nil { // Cache the key to avoid repeated hash computations.
|
|
// NOTE: We are not using the type to calculate the hash!
|
|
switch n.Type {
|
|
case NodeTypeMiddle: // H(ChildL || ChildR)
|
|
var err error
|
|
n.key, err = HashElems(n.ChildL.BigInt(), n.ChildR.BigInt())
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
case NodeTypeLeaf:
|
|
var err error
|
|
n.key, err = LeafKey(n.Entry[0], n.Entry[1])
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
case NodeTypeEmpty: // Zero
|
|
n.key = &HashZero
|
|
default:
|
|
n.key = &HashZero
|
|
}
|
|
}
|
|
return n.key, nil
|
|
}
|
|
|
|
// Value returns the value of the node. This is the content that is stored in the backend database.
|
|
func (n *Node) Value() []byte {
|
|
switch n.Type {
|
|
case NodeTypeMiddle: // {Type || ChildL || ChildR}
|
|
return append([]byte{byte(n.Type)}, append(n.ChildL[:], n.ChildR[:]...)...)
|
|
case NodeTypeLeaf: // {Type || Data...}
|
|
return append([]byte{byte(n.Type)}, append(n.Entry[0][:], n.Entry[1][:]...)...)
|
|
case NodeTypeEmpty: // {}
|
|
return []byte{}
|
|
default:
|
|
return []byte{}
|
|
}
|
|
}
|
|
|
|
// String outputs a string representation of a node (different for each type).
|
|
func (n *Node) String() string {
|
|
switch n.Type {
|
|
case NodeTypeMiddle: // {Type || ChildL || ChildR}
|
|
return fmt.Sprintf("Middle L:%s R:%s", n.ChildL, n.ChildR)
|
|
case NodeTypeLeaf: // {Type || Data...}
|
|
return fmt.Sprintf("Leaf I:%v D:%v", n.Entry[0], n.Entry[1])
|
|
case NodeTypeEmpty: // {}
|
|
return "Empty"
|
|
default:
|
|
return "Invalid Node"
|
|
}
|
|
}
|