Skip to content

Commit b70272b

Browse files
committed
Merkle Tree implementation
0 parents  commit b70272b

File tree

8 files changed

+476
-0
lines changed

8 files changed

+476
-0
lines changed

.gitignore

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
# Binaries for programs and plugins
2+
*.exe
3+
*.exe~
4+
*.dll
5+
*.so
6+
*.dylib
7+
8+
# Test binary, built with `go test -c`
9+
*.test
10+
11+
# Output of the go coverage tool, specifically when used with LiteIDE
12+
*.out
13+
14+
# compiled npm output
15+
dist/
16+
tmp/
17+
out-tsc/
18+
# Only exists if Bazel was run
19+
bazel-out/
20+
21+
# profiling files
22+
chrome-profiler-events*.json
23+
speed-measure-plugin*.json
24+
25+
# IDEs and editors
26+
.idea/
27+
.project
28+
.classpath
29+
.c9/
30+
*.launch
31+
.settings/
32+
*.sublime-workspace
33+
34+
# IDE - VSCode
35+
.vscode/*
36+
!.vscode/settings.json
37+
!.vscode/tasks.json
38+
!.vscode/launch.json
39+
!.vscode/extensions.json
40+
.history/*
41+
42+
# misc
43+
/.sass-cache
44+
/connect.lock
45+
/coverage
46+
/libpeerconnection.log
47+
npm-debug.log
48+
yarn-error.log
49+
testem.log
50+
/typings
51+
52+
# System Files
53+
.DS_Store
54+
Thumbs.db

LICENSE

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
MIT License
2+
3+
Copyright (c) 2022 Dmitrii Limonov
4+
5+
Permission is hereby granted, free of charge, to any person obtaining a copy
6+
of this software and associated documentation files (the "Software"), to deal
7+
in the Software without restriction, including without limitation the rights
8+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9+
copies of the Software, and to permit persons to whom the Software is
10+
furnished to do so, subject to the following conditions:
11+
12+
The above copyright notice and this permission notice shall be included in all
13+
copies or substantial portions of the Software.
14+
15+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21+
SOFTWARE.

README.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
# Merkle Tree
2+
3+
An implementation of a Merkle Tree written in Go.
4+

go.mod

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
module github.com/scDisorder/merkle
2+
3+
go 1.17

merkle_tree.go

Lines changed: 189 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,189 @@
1+
package merkletree
2+
3+
import (
4+
"bytes"
5+
"crypto/sha256"
6+
"fmt"
7+
"hash"
8+
)
9+
10+
type MerkleTree struct {
11+
Root *Node
12+
merkleRoot []byte
13+
Leafs []*Node
14+
hashStrategy func() hash.Hash
15+
}
16+
17+
// MerkleRoot returns hash of the root node
18+
func (m *MerkleTree) MerkleRoot() []byte {
19+
return m.merkleRoot
20+
}
21+
22+
// MerklePath returns merkle path and indexes (left or right leaf)
23+
func (m *MerkleTree) MerklePath(content Content) ([][]byte, []int64, error) {
24+
for _, l := range m.Leafs {
25+
ok, err := l.C.Equals(content)
26+
if err != nil {
27+
return nil, nil, err
28+
}
29+
30+
if ok {
31+
leafParent := l.Parent
32+
33+
var merklePath [][]byte
34+
var indices []int64
35+
36+
for leafParent != nil {
37+
if bytes.Equal(leafParent.Left.Hash, leafParent.Hash) {
38+
merklePath = append(merklePath, leafParent.Right.Hash)
39+
indices = append(indices, 1)
40+
} else {
41+
merklePath = append(merklePath, leafParent.Left.Hash)
42+
indices = append(indices, 0)
43+
}
44+
45+
l = leafParent
46+
leafParent = leafParent.Parent
47+
}
48+
49+
return merklePath, indices, nil
50+
}
51+
}
52+
53+
return nil, nil, nil
54+
}
55+
56+
// Rebuilt is a helper func which rebuilds the tree with existing contents
57+
func (m *MerkleTree) Rebuilt() error {
58+
var contents []Content
59+
for _, c := range m.Leafs {
60+
contents = append(contents, c.C)
61+
}
62+
63+
root, leafs, err := buildWithContent(contents, m)
64+
if err != nil {
65+
return err
66+
}
67+
68+
m.Root = root
69+
m.Leafs = leafs
70+
m.merkleRoot = root.Hash
71+
72+
return nil
73+
}
74+
75+
// RebuiltWith is a helper func which rebuilds the tree by replacing content passed as argument
76+
func (m *MerkleTree) RebuiltWith(contents []Content) error {
77+
root, leafs, err := buildWithContent(contents, m)
78+
if err != nil {
79+
return err
80+
}
81+
82+
m.Root = root
83+
m.Leafs = leafs
84+
m.merkleRoot = root.Hash
85+
86+
return nil
87+
}
88+
89+
// VerifyTree validates hash on each level
90+
func (m *MerkleTree) VerifyTree() (bool, error) {
91+
merkleRoot, err := m.Root.verify()
92+
if err != nil {
93+
return false, err
94+
}
95+
96+
if bytes.Compare(merkleRoot, m.MerkleRoot()) == 0 {
97+
return true, nil
98+
}
99+
100+
return false, nil
101+
}
102+
103+
// VerifyContent indicates if given content is a part of the tree
104+
func (m *MerkleTree) VerifyContent(c Content) (bool, error) {
105+
for _, l := range m.Leafs {
106+
ok, err := l.C.Equals(c)
107+
if err != nil {
108+
return false, err
109+
}
110+
111+
if ok {
112+
leafParent := l.Parent
113+
114+
for leafParent != nil {
115+
h := m.hashStrategy()
116+
117+
right, err := leafParent.Right.verify()
118+
if err != nil {
119+
return false, err
120+
}
121+
122+
left, err := leafParent.Left.verify()
123+
if err != nil {
124+
return false, err
125+
}
126+
127+
if _, err := h.Write(append(left, right...)); err != nil {
128+
return false, err
129+
}
130+
131+
if bytes.Compare(h.Sum(nil), leafParent.Hash) != 0 {
132+
return false, nil
133+
}
134+
135+
leafParent = leafParent.Parent
136+
}
137+
138+
return true, nil
139+
}
140+
}
141+
142+
return false, nil
143+
}
144+
145+
// returns merkle tree string representation
146+
func (m *MerkleTree) String() string {
147+
s := ""
148+
for _, l := range m.Leafs {
149+
s += fmt.Sprint(l)
150+
s += "\n"
151+
}
152+
return s
153+
}
154+
155+
func NewTree(contents []Content) (*MerkleTree, error) {
156+
defaultHashStrategy := sha256.New
157+
158+
tree := &MerkleTree{
159+
hashStrategy: defaultHashStrategy,
160+
}
161+
162+
root, leafs, err := buildWithContent(contents, tree)
163+
if err != nil {
164+
return nil, err
165+
}
166+
167+
tree.Root = root
168+
tree.Leafs = leafs
169+
tree.merkleRoot = root.Hash
170+
171+
return tree, nil
172+
}
173+
174+
func NewTreeWithHashStrategy(contents []Content, hashStrategy func() hash.Hash) (*MerkleTree, error) {
175+
tree := &MerkleTree{
176+
hashStrategy: hashStrategy,
177+
}
178+
179+
root, leafs, err := buildWithContent(contents, tree)
180+
if err != nil {
181+
return nil, err
182+
}
183+
184+
tree.Root = root
185+
tree.Leafs = leafs
186+
tree.merkleRoot = root.Hash
187+
188+
return tree, nil
189+
}

merkle_tree_test.go

Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
package merkletree
2+
3+
import (
4+
"crypto/sha256"
5+
"log"
6+
"testing"
7+
)
8+
9+
type TestContent struct {
10+
x string
11+
}
12+
13+
// CalculateHash hashes the values of a TestContent
14+
func (t TestContent) CalculateHash() ([]byte, error) {
15+
h := sha256.New()
16+
if _, err := h.Write([]byte(t.x)); err != nil {
17+
return nil, err
18+
}
19+
20+
return h.Sum(nil), nil
21+
}
22+
23+
// Equals tests for equality of two Contents
24+
func (t TestContent) Equals(other Content) (bool, error) {
25+
return t.x == other.(TestContent).x, nil
26+
}
27+
28+
func Test(t *testing.T) {
29+
//Build list of Content to build tree
30+
var list []Content
31+
list = append(list, TestContent{x: "So"})
32+
list = append(list, TestContent{x: "Much"})
33+
list = append(list, TestContent{x: "Content"})
34+
list = append(list, TestContent{x: "Wow"})
35+
36+
//Create a new Merkle Tree from the list of Content
37+
tree, err := NewTree(list)
38+
if err != nil {
39+
log.Fatal(err)
40+
}
41+
42+
//Get the Merkle Root of the tree
43+
mr := tree.MerkleRoot()
44+
log.Println(mr)
45+
46+
// Verify the entire tree (hashes for each node) is valid
47+
vt, err := tree.VerifyTree()
48+
if err != nil {
49+
log.Fatal(err)
50+
}
51+
log.Println("Verify Tree: ", vt)
52+
53+
// Verify a specific content in the tree
54+
vc, err := tree.VerifyContent(list[0])
55+
if err != nil {
56+
log.Fatal(err)
57+
}
58+
59+
log.Println("Verify Content: ", vc)
60+
61+
//String representation
62+
log.Println(t)
63+
}

0 commit comments

Comments
 (0)