1
0
Fork 0
mirror of https://github.com/vbatts/merkle.git synced 2024-12-02 19:15:39 +00:00
merkle/node.go

115 lines
2.8 KiB
Go

package merkle
import (
"crypto/sha1"
"fmt"
"hash"
)
var (
// DefaultHashMaker is for checksum of blocks and nodes
DefaultHashMaker = func() hash.Hash { return sha1.New() }
)
// HashMaker produces a new has for use in making checksums
type HashMaker func() hash.Hash
// NewNode returns a new Node with the DefaultHashMaker for checksums
func NewNode() *Node {
return NewNodeHash(DefaultHashMaker)
}
// NewNodeHash returns a new Node using the provided crypto.Hash for checksums
func NewNodeHash(h HashMaker) *Node {
return &Node{hash: h}
}
// NewNodeHashBlock returns a new Node using the provided crypto.Hash, and calculates the block's checksum
func NewNodeHashBlock(h HashMaker, b []byte) (*Node, error) {
n := &Node{hash: h}
h1 := n.hash()
if _, err := h1.Write(b); err != nil {
return nil, err
}
n.checksum = h1.Sum(nil)
return n, nil
}
// Node is a fundamental part of the tree.
type Node struct {
hash HashMaker
checksum []byte
Parent, Left, Right *Node
//pos int // XXX maybe keep their order when it is a direct block's hash
}
// IsLeaf indicates this node is for specific block (and has no children)
func (n Node) IsLeaf() bool {
return len(n.checksum) != 0 && (n.Left == nil && n.Right == nil)
}
// Checksum returns the checksum of the block, or the checksum of this nodes
// children (left.checksum + right.checksum)
// If it is a leaf (no children) Node, then the Checksum is of the block of a
// payload. Otherwise, the Checksum is of it's two children's Checksum.
func (n Node) Checksum() ([]byte, error) {
if n.checksum != nil {
return n.checksum, nil
}
if n.Left != nil && n.Right != nil {
// we'll ask our children for their sum and wait til they return
var (
lSumChan = make(chan childSumResponse)
rSumChan = make(chan childSumResponse)
)
go func() {
c, err := n.Left.Checksum()
lSumChan <- childSumResponse{checksum: c, err: err}
}()
go func() {
c, err := n.Right.Checksum()
rSumChan <- childSumResponse{checksum: c, err: err}
}()
h := n.hash()
// First left
lSum := <-lSumChan
if lSum.err != nil {
return nil, lSum.err
}
if _, err := h.Write(lSum.checksum); err != nil {
return nil, err
}
// then right
rSum := <-rSumChan
if rSum.err != nil {
return nil, rSum.err
}
if _, err := h.Write(rSum.checksum); err != nil {
return nil, err
}
return h.Sum(nil), nil
}
return nil, ErrNoChecksumAvailable{node: &n}
}
// ErrNoChecksumAvailable is for nodes that do not have the means to provide
// their checksum
type ErrNoChecksumAvailable struct {
node *Node
}
// Error shows the message with information on the node
func (err ErrNoChecksumAvailable) Error() string {
return fmt.Sprintf("no block or children available to derive checksum from: %#v", *err.node)
}
type childSumResponse struct {
checksum []byte
err error
}