diff --git a/node.go b/node.go new file mode 100644 index 0000000..d794c12 --- /dev/null +++ b/node.go @@ -0,0 +1,143 @@ +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" + } +}