.: stub on merkle nodes

This commit is contained in:
Vincent Batts 2015-03-17 16:45:01 -04:00
commit 205a82f081
3 changed files with 153 additions and 0 deletions

82
node.go Normal file
View File

@ -0,0 +1,82 @@
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
}

66
node_test.go Normal file
View File

@ -0,0 +1,66 @@
package merkle
import (
"log"
"strings"
"testing"
)
var words string = `Who were expelled from the academy for crazy & publishing obscene odes on the windows of the skull`
func TestNodeSums(t *testing.T) {
var (
nodes []*Node
h = DefaultHash.New()
)
for _, word := range strings.Split(words, " ") {
h.Reset()
if _, err := h.Write([]byte(word)); err != nil {
t.Errorf("on word %q, encountered %s", word, err)
}
sum := h.Sum(nil)
nodes = append(nodes, &Node{checksum: sum})
}
for {
nodes = levelUp(nodes)
if len(nodes) == 1 {
break
}
}
for i := range nodes {
c, err := nodes[i].Checksum()
if err != nil {
t.Error(err)
}
t.Logf("checksum %x", c)
}
if len(nodes) > 0 {
t.Errorf("%d nodes; %d characters", len(nodes), len(words))
}
}
func levelUp(nodes []*Node) []*Node {
var (
newNodes []*Node
last = len(nodes) - 1
)
for i := range nodes {
if i%2 == 0 {
if i == last {
// TODO rebalance the last parent
log.Println("WHOOP")
}
n := NewNode()
n.Left = nodes[i]
n.Left.Parent = n
newNodes = append(newNodes, n)
} else {
n := newNodes[len(newNodes)-1]
n.Right = nodes[i]
n.Right.Parent = n
}
}
return newNodes
}

5
tree.go Normal file
View File

@ -0,0 +1,5 @@
package merkle
type Tree struct {
Nodes []Node
}