-
Notifications
You must be signed in to change notification settings - Fork 41
/
MerkleNode.cs
175 lines (148 loc) · 5.63 KB
/
MerkleNode.cs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
/*
* Copyright (c) Marc Clifton
* The Code Project Open License (CPOL) 1.02
* http://www.codeproject.com/info/cpol10.aspx
*/
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using Clifton.Core.ExtensionMethods;
namespace Clifton.Blockchain
{
public class MerkleNode : IEnumerable<MerkleNode>
{
public MerkleHash Hash { get; protected set; }
public MerkleNode LeftNode { get; protected set; }
public MerkleNode RightNode { get; protected set; }
public MerkleNode Parent { get; protected set; }
public bool IsLeaf { get { return LeftNode == null && RightNode == null; } }
public MerkleNode()
{
}
/// <summary>
/// Constructor for a base node (leaf), representing the lowest level of the tree.
/// </summary>
public MerkleNode(MerkleHash hash)
{
Hash = hash;
}
/// <summary>
/// Constructor for a parent node.
/// </summary>
public MerkleNode(MerkleNode left, MerkleNode right = null)
{
LeftNode = left;
RightNode = right;
LeftNode.Parent = this;
RightNode.IfNotNull(r => r.Parent = this);
ComputeHash();
}
public override string ToString()
{
return Hash.ToString();
}
IEnumerator IEnumerable.GetEnumerator()
{
return GetEnumerator();
}
public IEnumerator<MerkleNode> GetEnumerator()
{
foreach (var n in Iterate(this)) yield return n;
}
/// <summary>
/// Bottom-up/left-right iteration of the tree.
/// </summary>
/// <param name="node"></param>
/// <returns></returns>
protected IEnumerable<MerkleNode> Iterate(MerkleNode node)
{
if (node.LeftNode != null)
{
foreach (var n in Iterate(node.LeftNode)) yield return n;
}
if (node.RightNode != null)
{
foreach (var n in Iterate(node.RightNode)) yield return n;
}
yield return node;
}
public MerkleHash ComputeHash(byte[] buffer)
{
Hash = MerkleHash.Create(buffer);
return Hash;
}
/// <summary>
/// Return the leaves (not all children, just leaves) under this node
/// </summary>
public IEnumerable<MerkleNode> Leaves()
{
return this.Where(n => n.LeftNode == null && n.RightNode == null);
}
public void SetLeftNode(MerkleNode node)
{
MerkleTree.Contract(() => node.Hash != null, "Node hash must be initialized.");
LeftNode = node;
LeftNode.Parent = this;
ComputeHash();
}
public void SetRightNode(MerkleNode node)
{
MerkleTree.Contract(() => node.Hash != null, "Node hash must be initialized.");
RightNode = node;
RightNode.Parent = this;
// Can't compute hash if the left node isn't set yet.
if (LeftNode != null)
{
ComputeHash();
}
}
/// <summary>
/// True if we have enough data to verify our hash, particularly if we have child nodes.
/// </summary>
/// <returns>True if this node is a leaf or a branch with at least a left node.</returns>
public bool CanVerifyHash()
{
return (LeftNode != null && RightNode != null) || (LeftNode != null);
}
/// <summary>
/// Verifies the hash for this node against the computed hash for our child nodes.
/// If we don't have any children, the return is always true because we have nothing to verify against.
/// </summary>
public bool VerifyHash()
{
if (LeftNode == null && RightNode == null)
{
return true;
}
if (RightNode == null)
{
return Hash.Equals(LeftNode.Hash);
}
MerkleTree.Contract(() => LeftNode != null, "Left branch must be a node if right branch is a node.");
MerkleHash leftRightHash = MerkleHash.Create(LeftNode.Hash, RightNode.Hash);
return Hash.Equals(leftRightHash);
}
/// <summary>
/// If the hashes are equal, we know the entire node tree is equal.
/// </summary>
public bool Equals(MerkleNode node)
{
return Hash.Equals(node.Hash);
}
protected void ComputeHash()
{
// Repeat the left node if the right node doesn't exist.
// This process breaks the case of doing a consistency check on 3 leaves when there are only 3 leaves in the tree.
//MerkleHash rightHash = RightNode == null ? LeftNode.Hash : RightNode.Hash;
//Hash = MerkleHash.Create(LeftNode.Hash.Value.Concat(rightHash.Value).ToArray());
// Alternativately, do not repeat the left node, but carry the left node's hash up.
// This process does not break the edge case described above.
// We're implementing this version because the consistency check unit tests pass when we don't simulate
// a right-hand node.
Hash = RightNode == null ?
LeftNode.Hash : //MerkleHash.Create(LeftNode.Hash.Value.Concat(LeftNode.Hash.Value).ToArray()) :
MerkleHash.Create(LeftNode.Hash.Value.Concat(RightNode.Hash.Value).ToArray());
Parent?.ComputeHash(); // Recurse, because out hash has changed.
}
}
}