├── .travis.yml ├── doc.go ├── block_test.go ├── block.go ├── README.md ├── LICENSE ├── tree.go ├── node_test.go ├── node.go ├── stream_test.go └── stream.go /.travis.yml: -------------------------------------------------------------------------------- 1 | language: go 2 | go: 3 | - 1.4.1 4 | - 1.3.3 5 | 6 | # let us have pretty, fast Docker-based Travis workers! 7 | sudo: false 8 | 9 | # we don't need "go get" here <3 10 | install: true 11 | 12 | script: 13 | - go test -v ./... 14 | -------------------------------------------------------------------------------- /doc.go: -------------------------------------------------------------------------------- 1 | /* 2 | What do you expect from a merkle tree API? 3 | * streaming support 4 | - building a tree from an io.Reader 5 | - validating a tree from an io.Reader 6 | * concurrency safe 7 | - any buffer or hash.Hash reuse 8 | */ 9 | 10 | package merkle 11 | -------------------------------------------------------------------------------- /block_test.go: -------------------------------------------------------------------------------- 1 | package merkle 2 | 3 | import "testing" 4 | 5 | func TestBlockSize(t *testing.T) { 6 | var testSet = [][2]int{ 7 | {1024 * 1024, 16384}, 8 | {1023 * 1023, 0}, // Not a evenly divisible 9 | {1023, 1023}, // less than the max 10 | } 11 | for _, item := range testSet { 12 | got := DetermineBlockSize(item[0]) 13 | if got != item[1] { 14 | t.Errorf("expected %d, got %d", item[1], got) 15 | } 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /block.go: -------------------------------------------------------------------------------- 1 | package merkle 2 | 3 | const ( 4 | // MaxBlockSize reasonable max byte size for blocks that are checksummed for 5 | // a Node 6 | MaxBlockSize = 1024 * 16 7 | ) 8 | 9 | // DetermineBlockSize returns a reasonable block size to use, based on the 10 | // provided size 11 | func DetermineBlockSize(blockSize int) int { 12 | var b = blockSize 13 | for b > MaxBlockSize { 14 | b /= 2 15 | } 16 | if b == 0 || (blockSize%b != 0) { 17 | return 0 18 | } 19 | return b 20 | } 21 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | merkle 2 | ====== 3 | 4 | [![Build Status](https://travis-ci.org/vbatts/merkle.svg?branch=master)](https://travis-ci.org/vbatts/merkle) 5 | 6 | Golang Merkle Tree Implementation, with `hash.Hash` interface for streaming support. 7 | 8 | 9 | Docs 10 | ---- 11 | 12 | * https://godoc.org/github.com/vbatts/merkle 13 | 14 | 15 | What's Next? 16 | ------------ 17 | 18 | * More tests and review 19 | * Streaming `HashTreeer` type, that can validate provided Nodes, for streaming validation 20 | 21 | 22 | License 23 | ------- 24 | 25 | See LICENSE 26 | 27 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2015 Vincent Batts, Raleigh, NC, USA 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy 4 | of this software and associated documentation files (the "Software"), to deal 5 | in the Software without restriction, including without limitation the rights 6 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | copies of the Software, and to permit persons to whom the Software is 8 | furnished to do so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in 11 | all copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | THE SOFTWARE. 20 | -------------------------------------------------------------------------------- /tree.go: -------------------------------------------------------------------------------- 1 | package merkle 2 | 3 | // Tree is the information on the structure of a set of nodes 4 | // 5 | // TODO more docs here 6 | type Tree struct { 7 | Nodes []*Node `json:"pieces"` 8 | BlockLength int `json:"piece length"` 9 | } 10 | 11 | // Pieces returns the concatenation of hash values of all blocks 12 | // 13 | // TODO integrate with hash size 14 | func (t *Tree) Pieces() []byte { 15 | if len(t.Nodes) == 0 { 16 | return nil 17 | } 18 | pieces := []byte{} 19 | for _, n := range t.Nodes { 20 | if n.checksum == nil || len(n.checksum) == 0 { 21 | continue 22 | } 23 | pieces = append(pieces, n.checksum...) 24 | } 25 | return pieces 26 | } 27 | 28 | // Root generates a hash tree bash on the current nodes, and returns the root 29 | // of the tree 30 | func (t *Tree) Root() *Node { 31 | newNodes := t.Nodes 32 | for { 33 | newNodes = levelUp(newNodes) 34 | if len(newNodes) == 1 { 35 | break 36 | } 37 | } 38 | return newNodes[0] 39 | } 40 | 41 | func levelUp(nodes []*Node) []*Node { 42 | var ( 43 | newNodes []*Node 44 | last = len(nodes) - 1 45 | ) 46 | 47 | for i := range nodes { 48 | if i%2 == 0 { 49 | if i == last { 50 | // last nodes on uneven node counts get pushed up, to be in the next 51 | // level up 52 | newNodes = append(newNodes, nodes[i]) 53 | continue 54 | } 55 | //n := NewNodeHash(nodes[i].hash) // use the node's hash type 56 | n := NewNode() 57 | n.Left = nodes[i] 58 | n.Left.Parent = n 59 | newNodes = append(newNodes, n) 60 | } else { 61 | n := newNodes[len(newNodes)-1] 62 | n.Right = nodes[i] 63 | n.Right.Parent = n 64 | } 65 | } 66 | return newNodes 67 | } 68 | -------------------------------------------------------------------------------- /node_test.go: -------------------------------------------------------------------------------- 1 | package merkle 2 | 3 | import ( 4 | "fmt" 5 | "strings" 6 | "testing" 7 | ) 8 | 9 | func TestNodeSums(t *testing.T) { 10 | var ( 11 | nodes []*Node 12 | h = DefaultHashMaker() 13 | words = `Who were expelled from the academy for crazy & publishing obscene odes on the windows of the skull` 14 | expectedChecksum = "819fe8fed7a46900bd0613344c5ba2be336c74db" 15 | ) 16 | for _, word := range strings.Split(words, " ") { 17 | h.Reset() 18 | if _, err := h.Write([]byte(word)); err != nil { 19 | t.Errorf("on word %q, encountered %s", word, err) 20 | } 21 | sum := h.Sum(nil) 22 | nodes = append(nodes, &Node{checksum: sum, hash: DefaultHashMaker}) 23 | } 24 | 25 | newNodes := nodes 26 | for { 27 | newNodes = levelUp(newNodes) 28 | if len(newNodes) == 1 { 29 | break 30 | } 31 | } 32 | if len(newNodes) != 1 { 33 | t.Errorf("%d nodes", len(newNodes)) 34 | } 35 | c, err := newNodes[0].Checksum() 36 | if err != nil { 37 | t.Error(err) 38 | } 39 | gotChecksum := fmt.Sprintf("%x", c) 40 | if gotChecksum != expectedChecksum { 41 | t.Errorf("expected checksum %q, got %q", expectedChecksum, gotChecksum) 42 | } 43 | 44 | tree := Tree{Nodes: nodes} 45 | c, err = tree.Root().Checksum() 46 | if err != nil { 47 | t.Error(err) 48 | } 49 | rootChecksum := fmt.Sprintf("%x", c) 50 | if rootChecksum != gotChecksum { 51 | t.Errorf("expected checksum %q, got %q", gotChecksum, rootChecksum) 52 | } 53 | 54 | expectedPieces := `7d531617dd394cef59d3cf58fc32b3bc458f6744a315dee0bd22f45265f67268f091869cca3cbf4ac267872aa7424b933c7e2b4de64e7c91b710686b0b1e95cfd9775191a7224d0a218ae79187e80c1dbbccdf2efb33b52e6c9d0a14dd70b2d415fbea6ecb2766cf39b9ee567af0081faffc4bb74c2b1fba43eef9a62abb8b1e1654f8a890aae054abffa82b33b501a5f87749b22562d3a7d38f8db6ccb80fe97c4d33785daa5c2370201ffa236b427aa37c99963fea93d27d200a96fc9e41ada467fda07ed68560efc7daae2005c903a8cb459ff1d51aee2988a3b3b04666d10863651a70ac9859cbeb83e919460bd3db3d405b10675998c030223177d42e71b4e7a312bbccdf2efb33b52e6c9d0a14dd70b2d415fbea6eab378b80a8a4aafabac7db7ae169f25796e65994de04fa0e29f9b35e24905d2e512bedc9bb6e09e4bbccdf2efb33b52e6c9d0a14dd70b2d415fbea6e15e9abb2e818480bc62afceb1b7f438663f7f08f` 55 | gotPieces := fmt.Sprintf("%x", tree.Pieces()) 56 | if gotPieces != expectedPieces { 57 | t.Errorf("expected pieces %q, got %q", expectedPieces, gotPieces) 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /node.go: -------------------------------------------------------------------------------- 1 | package merkle 2 | 3 | import ( 4 | "crypto/sha1" 5 | "fmt" 6 | "hash" 7 | ) 8 | 9 | var ( 10 | // DefaultHashMaker is for checksum of blocks and nodes 11 | DefaultHashMaker = func() hash.Hash { return sha1.New() } 12 | ) 13 | 14 | // HashMaker produces a new has for use in making checksums 15 | type HashMaker func() hash.Hash 16 | 17 | // NewNode returns a new Node with the DefaultHashMaker for checksums 18 | func NewNode() *Node { 19 | return NewNodeHash(DefaultHashMaker) 20 | } 21 | 22 | // NewNodeHash returns a new Node using the provided crypto.Hash for checksums 23 | func NewNodeHash(h HashMaker) *Node { 24 | return &Node{hash: h} 25 | } 26 | 27 | // NewNodeHashBlock returns a new Node using the provided crypto.Hash, and calculates the block's checksum 28 | func NewNodeHashBlock(h HashMaker, b []byte) (*Node, error) { 29 | n := &Node{hash: h} 30 | h1 := n.hash() 31 | if _, err := h1.Write(b); err != nil { 32 | return nil, err 33 | } 34 | n.checksum = h1.Sum(nil) 35 | return n, nil 36 | } 37 | 38 | // Node is a fundamental part of the tree. 39 | type Node struct { 40 | hash HashMaker 41 | checksum []byte 42 | Parent, Left, Right *Node 43 | 44 | //pos int // XXX maybe keep their order when it is a direct block's hash 45 | } 46 | 47 | // IsLeaf indicates this node is for specific block (and has no children) 48 | func (n Node) IsLeaf() bool { 49 | return len(n.checksum) != 0 && (n.Left == nil && n.Right == nil) 50 | } 51 | 52 | // Checksum returns the checksum of the block, or the checksum of this nodes 53 | // children (left.checksum + right.checksum) 54 | // If it is a leaf (no children) Node, then the Checksum is of the block of a 55 | // payload. Otherwise, the Checksum is of it's two children's Checksum. 56 | func (n Node) Checksum() ([]byte, error) { 57 | if n.checksum != nil { 58 | return n.checksum, nil 59 | } 60 | if n.Left != nil && n.Right != nil { 61 | 62 | // we'll ask our children for their sum and wait til they return 63 | var ( 64 | lSumChan = make(chan childSumResponse) 65 | rSumChan = make(chan childSumResponse) 66 | ) 67 | go func() { 68 | c, err := n.Left.Checksum() 69 | lSumChan <- childSumResponse{checksum: c, err: err} 70 | }() 71 | go func() { 72 | c, err := n.Right.Checksum() 73 | rSumChan <- childSumResponse{checksum: c, err: err} 74 | }() 75 | 76 | h := n.hash() 77 | 78 | // First left 79 | lSum := <-lSumChan 80 | if lSum.err != nil { 81 | return nil, lSum.err 82 | } 83 | if _, err := h.Write(lSum.checksum); err != nil { 84 | return nil, err 85 | } 86 | 87 | // then right 88 | rSum := <-rSumChan 89 | if rSum.err != nil { 90 | return nil, rSum.err 91 | } 92 | if _, err := h.Write(rSum.checksum); err != nil { 93 | return nil, err 94 | } 95 | 96 | return h.Sum(nil), nil 97 | } 98 | return nil, ErrNoChecksumAvailable{node: &n} 99 | } 100 | 101 | // ErrNoChecksumAvailable is for nodes that do not have the means to provide 102 | // their checksum 103 | type ErrNoChecksumAvailable struct { 104 | node *Node 105 | } 106 | 107 | // Error shows the message with information on the node 108 | func (err ErrNoChecksumAvailable) Error() string { 109 | return fmt.Sprintf("no block or children available to derive checksum from: %#v", *err.node) 110 | } 111 | 112 | type childSumResponse struct { 113 | checksum []byte 114 | err error 115 | } 116 | -------------------------------------------------------------------------------- /stream_test.go: -------------------------------------------------------------------------------- 1 | package merkle 2 | 3 | import ( 4 | "crypto/sha256" 5 | "crypto/sha512" 6 | "fmt" 7 | "hash" 8 | "io" 9 | "io/ioutil" 10 | "os" 11 | "testing" 12 | ) 13 | 14 | func TestMerkleHashWriterLargeChunk(t *testing.T) { 15 | // make a large enough test file of increments, corresponding to our blockSize 16 | bs := 512 * 1024 17 | fh, err := ioutil.TempFile("", "merkleChunks.") 18 | if err != nil { 19 | t.Fatal(err) 20 | } 21 | defer fh.Close() 22 | defer os.Remove(fh.Name()) 23 | 24 | for i := 0; i < 5; i++ { 25 | b := []byte{byte(i)} 26 | var b2 []byte 27 | for j := 0; j < bs; j++ { 28 | b2 = append(b2, b...) 29 | } 30 | fh.Write(b2) 31 | } 32 | if err := fh.Sync(); err != nil { 33 | t.Fatal(err) 34 | } 35 | if _, err := fh.Seek(0, 0); err != nil { 36 | t.Fatal(err) 37 | } 38 | 39 | expectedSums := []string{ 40 | "6a521e1d2a632c26e53b83d2cc4b0edecfc1e68c", // 0's 41 | "316c136d75ffdeb6ac5f1262c45dd8c6ec50fd85", // 1's 42 | "a56e9c245b9c50d61a91c6c4299813b5e6313722", // 2's 43 | "58bed752c036310cc48d9dd0d25c4ee9ad0d7ff1", // 3's 44 | "bf382d8394213b897424803c27f3e2ec2223e5fd", // 4's 45 | } 46 | 47 | h := NewHash(DefaultHashMaker, bs) 48 | if _, err = io.Copy(h, fh); err != nil { 49 | t.Fatal(err) 50 | } 51 | h.Sum(nil) 52 | for i, node := range h.Nodes() { 53 | c, err := node.Checksum() 54 | if err != nil { 55 | t.Fatal(err) 56 | } 57 | if cs := fmt.Sprintf("%x", c); cs != expectedSums[i] { 58 | t.Errorf("expected sum %q; got %q", expectedSums[i], cs) 59 | } 60 | } 61 | } 62 | 63 | func TestMerkleHashWriter(t *testing.T) { 64 | msg := []byte("the quick brown fox jumps over the lazy dog") 65 | expectedSum := "48940c1c72636648ad40aa59c162f2208e835b38" 66 | 67 | h := NewHash(DefaultHashMaker, 10) 68 | i, err := h.Write(msg) 69 | if err != nil { 70 | t.Fatal(err) 71 | } 72 | if i != len(msg) { 73 | t.Fatalf("expected to write %d, only wrote %d", len(msg), i) 74 | } 75 | 76 | // We're left with a partial lastBlock 77 | expectedNum := 4 78 | if len(h.Nodes()) != expectedNum { 79 | t.Errorf("expected %d nodes, got %d", expectedNum, len(h.Nodes())) 80 | } 81 | 82 | // Next test Sum() 83 | gotSum := fmt.Sprintf("%x", h.Sum(nil)) 84 | if expectedSum != gotSum { 85 | t.Errorf("expected initial checksum %q; got %q", expectedSum, gotSum) 86 | } 87 | 88 | // count blocks again, we should get 5 nodes now 89 | expectedNum = 5 90 | if len(h.Nodes()) != expectedNum { 91 | t.Errorf("expected %d nodes, got %d", expectedNum, len(h.Nodes())) 92 | } 93 | 94 | // Test Sum() again, ensure same sum 95 | gotSum = fmt.Sprintf("%x", h.Sum(nil)) 96 | if expectedSum != gotSum { 97 | t.Errorf("expected checksum %q; got %q", expectedSum, gotSum) 98 | } 99 | 100 | // test that Reset() nulls us out 101 | h.Reset() 102 | gotSum = fmt.Sprintf("%x", h.Sum(nil)) 103 | if expectedSum == gotSum { 104 | t.Errorf("expected reset checksum to not equal %q; got %q", expectedSum, gotSum) 105 | } 106 | 107 | // write our msg again and get the same sum 108 | i, err = h.Write(msg) 109 | if err != nil { 110 | t.Fatal(err) 111 | } 112 | if i != len(msg) { 113 | t.Fatalf("expected to write %d, only wrote %d", len(msg), i) 114 | } 115 | // Test Sum(), ensure same sum 116 | gotSum = fmt.Sprintf("%x", h.Sum(nil)) 117 | if expectedSum != gotSum { 118 | t.Errorf("expected checksum %q; got %q", expectedSum, gotSum) 119 | } 120 | 121 | // Write more. This should pop the last node, and use the lastBlock. 122 | i, err = h.Write(msg) 123 | if err != nil { 124 | t.Fatal(err) 125 | } 126 | if i != len(msg) { 127 | t.Fatalf("expected to write %d, only wrote %d", len(msg), i) 128 | } 129 | expectedNum = 9 130 | if len(h.Nodes()) != expectedNum { 131 | t.Errorf("expected %d nodes, got %d", expectedNum, len(h.Nodes())) 132 | } 133 | gotSum = fmt.Sprintf("%x", h.Sum(nil)) 134 | if expectedSum == gotSum { 135 | t.Errorf("expected reset checksum to not equal %q; got %q", expectedSum, gotSum) 136 | } 137 | if len(h.Nodes()) != expectedNum { 138 | t.Errorf("expected %d nodes, got %d", expectedNum, len(h.Nodes())) 139 | } 140 | 141 | } 142 | 143 | var benchDefault = NewHash(DefaultHashMaker, 8192) 144 | var benchSha256 = NewHash(func() hash.Hash { return sha256.New() }, 8192) 145 | var benchSha512 = NewHash(func() hash.Hash { return sha512.New() }, 8192) 146 | var buf = make([]byte, 8192) 147 | 148 | func benchmarkSize(bench hash.Hash, b *testing.B, size int) { 149 | b.SetBytes(int64(size)) 150 | sum := make([]byte, bench.Size()) 151 | for i := 0; i < b.N; i++ { 152 | bench.Reset() 153 | bench.Write(buf[:size]) 154 | bench.Sum(sum[:0]) 155 | } 156 | } 157 | 158 | func BenchmarkHash8Bytes(b *testing.B) { 159 | benchmarkSize(benchDefault, b, 8) 160 | } 161 | 162 | func BenchmarkHash1K(b *testing.B) { 163 | benchmarkSize(benchDefault, b, 1024) 164 | } 165 | 166 | func BenchmarkHash8K(b *testing.B) { 167 | benchmarkSize(benchDefault, b, 8192) 168 | } 169 | 170 | func BenchmarkSha256Hash8Bytes(b *testing.B) { 171 | benchmarkSize(benchSha256, b, 8) 172 | } 173 | 174 | func BenchmarkSha256Hash1K(b *testing.B) { 175 | benchmarkSize(benchSha256, b, 1024) 176 | } 177 | 178 | func BenchmarkSha256Hash8K(b *testing.B) { 179 | benchmarkSize(benchSha256, b, 8192) 180 | } 181 | 182 | func BenchmarkSha512Hash8Bytes(b *testing.B) { 183 | benchmarkSize(benchSha512, b, 8) 184 | } 185 | 186 | func BenchmarkSha512Hash1K(b *testing.B) { 187 | benchmarkSize(benchSha512, b, 1024) 188 | } 189 | 190 | func BenchmarkSha512Hash8K(b *testing.B) { 191 | benchmarkSize(benchSha512, b, 8192) 192 | } 193 | -------------------------------------------------------------------------------- /stream.go: -------------------------------------------------------------------------------- 1 | package merkle 2 | 3 | import ( 4 | "fmt" 5 | "hash" 6 | "os" 7 | "runtime" 8 | ) 9 | 10 | // NewHash provides a hash.Hash to generate a merkle.Tree checksum, given a 11 | // HashMaker for the checksums of the blocks written and the blockSize of each 12 | // block per node in the tree. 13 | func NewHash(hm HashMaker, merkleBlockLength int) HashTreeer { 14 | return newMerkleHash(hm, merkleBlockLength) 15 | } 16 | 17 | func newMerkleHash(hm HashMaker, merkleBlockLength int) *merkleHash { 18 | mh := new(merkleHash) 19 | mh.blockSize = merkleBlockLength 20 | mh.hm = hm 21 | mh.tree = &Tree{Nodes: []*Node{}, BlockLength: merkleBlockLength} 22 | mh.lastBlock = make([]byte, merkleBlockLength) 23 | return mh 24 | } 25 | 26 | // Treeer (Tree-er) provides access to the Merkle tree internals 27 | type Treeer interface { 28 | Nodes() []*Node 29 | Root() *Node 30 | } 31 | 32 | // HashTreeer can be used as a hash.Hash but also provide access to the Merkle 33 | // tree internals 34 | type HashTreeer interface { 35 | hash.Hash 36 | Treeer 37 | } 38 | 39 | // TODO make a similar hash.Hash, that accepts an argument of a merkle.Tree, 40 | // that will validate nodes as the new bytes are written. If a new written 41 | // block fails checksum, then return an error on the io.Writer 42 | 43 | type merkleHash struct { 44 | blockSize int 45 | tree *Tree 46 | hm HashMaker 47 | lastBlock []byte // as needed, for Sum() 48 | lastBlockLen int 49 | partialLastNode bool // true when Sum() has appended a Node for a partial block 50 | } 51 | 52 | func (mh *merkleHash) Reset() { 53 | mh.tree = &Tree{Nodes: []*Node{}, BlockLength: mh.blockSize} 54 | mh.lastBlockLen = 0 55 | mh.partialLastNode = false 56 | } 57 | 58 | func (mh merkleHash) Nodes() []*Node { 59 | return mh.tree.Nodes 60 | } 61 | 62 | func (mh merkleHash) Root() *Node { 63 | return mh.tree.Root() 64 | } 65 | 66 | // XXX this will be tricky, as the last block can be less than the BlockSize. 67 | // if they get the sum, it will be mh.tree.Root().Checksum() at that point. 68 | // 69 | // But if they continue writing, it would mean a continuation of the bytes in 70 | // the last block. So popping the last node, and having a buffer for the bytes 71 | // in that last partial block. 72 | // 73 | // if that last block was complete, then no worries. start the next node. 74 | func (mh *merkleHash) Sum(b []byte) []byte { 75 | var ( 76 | curBlock = []byte{} 77 | offset int 78 | ) 79 | if mh.partialLastNode { 80 | // if this is true, then we need to pop the last node 81 | mh.tree.Nodes = mh.tree.Nodes[:len(mh.tree.Nodes)-1] 82 | mh.partialLastNode = false 83 | } 84 | 85 | if mh.lastBlockLen > 0 { 86 | curBlock = append(curBlock[:], mh.lastBlock[:mh.lastBlockLen]...) 87 | mh.lastBlockLen = 0 88 | } 89 | 90 | if b != nil && len(b) > 0 { 91 | curBlock = append(curBlock, b...) 92 | } 93 | 94 | // incase we're at a new or reset state 95 | if len(mh.tree.Nodes) == 0 && len(curBlock) == 0 { 96 | return nil 97 | } 98 | 99 | for i := 0; i < len(curBlock)/mh.blockSize; i++ { 100 | n, err := NewNodeHashBlock(mh.hm, curBlock[offset:(offset+mh.blockSize)]) 101 | if err != nil { 102 | // XXX i hate to swallow an error here, but the `Sum() []byte` signature 103 | // :-\ 104 | sBuf := make([]byte, 1024) 105 | runtime.Stack(sBuf, false) 106 | fmt.Fprintf(os.Stderr, "[ERROR]: %s %q", err, string(sBuf)) 107 | return nil 108 | } 109 | mh.tree.Nodes = append(mh.tree.Nodes, n) 110 | offset = offset + mh.blockSize 111 | } 112 | 113 | // If there is remainder, we'll need to make a partial node and stash it 114 | if m := (len(curBlock) % mh.blockSize); m != 0 { 115 | mh.lastBlockLen = copy(mh.lastBlock, curBlock[offset:]) 116 | 117 | n, err := NewNodeHashBlock(mh.hm, curBlock[offset:]) 118 | if err != nil { 119 | sBuf := make([]byte, 1024) 120 | runtime.Stack(sBuf, false) 121 | fmt.Fprintf(os.Stderr, "[ERROR]: %s %q", err, string(sBuf)) 122 | return nil 123 | } 124 | mh.tree.Nodes = append(mh.tree.Nodes, n) 125 | mh.partialLastNode = true 126 | } 127 | 128 | sum, err := mh.tree.Root().Checksum() 129 | if err != nil { 130 | // XXX i hate to swallow an error here, but the `Sum() []byte` signature 131 | // :-\ 132 | sBuf := make([]byte, 1024) 133 | runtime.Stack(sBuf, false) 134 | fmt.Fprintf(os.Stderr, "[ERROR]: %s %q", err, string(sBuf)) 135 | return nil 136 | } 137 | return sum 138 | } 139 | 140 | func (mh *merkleHash) Write(b []byte) (int, error) { 141 | // basically we need to: 142 | // * include prior partial lastBlock, if any 143 | // * chunk these writes into blockSize 144 | // * create Node of the sum 145 | // * add the Node to the tree 146 | // * stash remainder in the mh.lastBlock 147 | 148 | var ( 149 | curBlock = make([]byte, mh.blockSize) 150 | numBytes int 151 | numWritten int 152 | offset int 153 | ) 154 | if mh.lastBlock != nil && mh.lastBlockLen > 0 { 155 | if (mh.lastBlockLen + len(b)) < mh.blockSize { 156 | mh.lastBlockLen += copy(mh.lastBlock[mh.lastBlockLen:], b[:]) 157 | return len(b), nil 158 | } 159 | 160 | // XXX off by one? 161 | numBytes = copy(curBlock[:], mh.lastBlock[:mh.lastBlockLen]) 162 | // not adding to numWritten, since these blocks were accounted for in a 163 | // prior Write() 164 | 165 | // then we'll chunk the front of the incoming bytes 166 | end := mh.blockSize - numBytes 167 | if end > len(b) { 168 | end = len(b) 169 | } 170 | offset = copy(curBlock[numBytes:], b[:end]) 171 | n, err := NewNodeHashBlock(mh.hm, curBlock) 172 | if err != nil { 173 | // XXX might need to stash again the prior lastBlock and first little chunk 174 | return numWritten, err 175 | } 176 | mh.tree.Nodes = append(mh.tree.Nodes, n) 177 | numWritten += offset 178 | } 179 | 180 | numBytes = (len(b) - offset) 181 | for i := 0; i < numBytes/mh.blockSize; i++ { 182 | //fmt.Printf("%s", b[offset:offset+mh.blockSize]) 183 | numWritten += copy(curBlock, b[offset:offset+mh.blockSize]) 184 | n, err := NewNodeHashBlock(mh.hm, curBlock) 185 | if err != nil { 186 | // XXX might need to stash again the prior lastBlock and first little chunk 187 | return numWritten, err 188 | } 189 | mh.tree.Nodes = append(mh.tree.Nodes, n) 190 | offset = offset + mh.blockSize 191 | } 192 | 193 | mh.lastBlockLen = numBytes % mh.blockSize 194 | // XXX off by one? 195 | numWritten += copy(mh.lastBlock[:], b[(len(b)-mh.lastBlockLen):]) 196 | 197 | return numWritten, nil 198 | } 199 | 200 | // likely not the best to pass this through and not use our own node block 201 | // size, but let's revisit this. 202 | func (mh *merkleHash) BlockSize() int { return mh.hm().BlockSize() } 203 | func (mh *merkleHash) Size() int { return mh.hm().Size() } 204 | --------------------------------------------------------------------------------