mirror of
https://github.com/vbatts/merkle.git
synced 2025-04-23 15:24:37 +00:00
.: stub on merkle nodes
This commit is contained in:
commit
205a82f081
3 changed files with 153 additions and 0 deletions
82
node.go
Normal file
82
node.go
Normal 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
66
node_test.go
Normal 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
5
tree.go
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
package merkle
|
||||||
|
|
||||||
|
type Tree struct {
|
||||||
|
Nodes []Node
|
||||||
|
}
|
Loading…
Add table
Reference in a new issue