1
0
Fork 0
mirror of https://github.com/vbatts/merkle.git synced 2024-12-11 06:36:35 +00:00
merkle/node.go

116 lines
2.8 KiB
Go
Raw Normal View History

2015-03-17 20:45:01 +00:00
package merkle
import (
2015-03-25 21:28:17 +00:00
"crypto/sha1"
2015-03-17 20:45:01 +00:00
"fmt"
2015-03-18 18:31:16 +00:00
"hash"
2015-03-17 20:45:01 +00:00
)
var (
2015-03-25 21:28:17 +00:00
// DefaultHashMaker is for checksum of blocks and nodes
DefaultHashMaker = func() hash.Hash { return sha1.New() }
2015-03-17 20:45:01 +00:00
)
2015-03-18 18:31:16 +00:00
// HashMaker produces a new has for use in making checksums
type HashMaker func() hash.Hash
2015-03-25 21:28:17 +00:00
// NewNode returns a new Node with the DefaultHashMaker for checksums
2015-03-17 20:45:01 +00:00
func NewNode() *Node {
2015-03-25 21:28:17 +00:00
return NewNodeHash(DefaultHashMaker)
2015-03-18 18:31:16 +00:00
}
// NewNodeHash returns a new Node using the provided crypto.Hash for checksums
func NewNodeHash(h HashMaker) *Node {
return &Node{hash: h}
2015-03-17 20:45:01 +00:00
}
2015-03-25 21:28:17 +00:00
// NewNodeHashBlock returns a new Node using the provided crypto.Hash, and calculates the block's checksum
2015-03-26 20:46:02 +00:00
func NewNodeHashBlock(h HashMaker, b []byte) (*Node, error) {
2015-03-25 21:28:17 +00:00
n := &Node{hash: h}
h1 := n.hash()
2015-03-26 20:46:02 +00:00
if _, err := h1.Write(b); err != nil {
return nil, err
}
2015-03-25 21:28:17 +00:00
n.checksum = h1.Sum(nil)
2015-03-26 20:46:02 +00:00
return n, nil
2015-03-25 21:28:17 +00:00
}
2015-03-17 20:45:01 +00:00
// Node is a fundamental part of the tree.
type Node struct {
2015-03-18 18:31:16 +00:00
hash HashMaker
2015-03-17 20:45:01 +00:00
checksum []byte
Parent, Left, Right *Node
2015-03-18 18:31:16 +00:00
2015-03-25 21:28:17 +00:00
//pos int // XXX maybe keep their order when it is a direct block's hash
2015-03-18 18:31:16 +00:00
}
// 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)
2015-03-17 20:45:01 +00:00
}
2015-03-18 18:31:16 +00:00
// Checksum returns the checksum of the block, or the checksum of this nodes
// children (left.checksum + right.checksum)
2015-03-17 20:45:01 +00:00
// 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}
}()
2015-03-18 18:31:16 +00:00
h := n.hash()
2015-03-17 20:45:01 +00:00
// 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}
}
2015-03-18 18:31:16 +00:00
// ErrNoChecksumAvailable is for nodes that do not have the means to provide
// their checksum
2015-03-17 20:45:01 +00:00
type ErrNoChecksumAvailable struct {
node *Node
}
2015-03-18 18:31:16 +00:00
// Error shows the message with information on the node
2015-03-17 20:45:01 +00:00
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
}