mirror of
https://github.com/vbatts/merkle.git
synced 2025-03-16 21:56:19 +00:00
82 lines
1.6 KiB
Go
82 lines
1.6 KiB
Go
package merkle
|
|
|
|
import (
|
|
"crypto"
|
|
_ "crypto/sha1"
|
|
"fmt"
|
|
)
|
|
|
|
var (
|
|
DefaultHash = crypto.SHA1
|
|
)
|
|
|
|
func NewNode() *Node {
|
|
return &Node{hash: DefaultHash}
|
|
}
|
|
|
|
// Node is a fundamental part of the tree.
|
|
type Node struct {
|
|
hash crypto.Hash
|
|
checksum []byte
|
|
Parent, Left, Right *Node
|
|
}
|
|
|
|
// 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.New()
|
|
|
|
// 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}
|
|
}
|
|
|
|
type ErrNoChecksumAvailable struct {
|
|
node *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
|
|
}
|