-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathMerkleTree.java
More file actions
149 lines (136 loc) · 4.82 KB
/
MerkleTree.java
File metadata and controls
149 lines (136 loc) · 4.82 KB
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
package modules.ads.merkletree;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.locks.ReentrantReadWriteLock;
import crypto.Sha3256Hasher;
import model.Entity;
import model.crypto.Sha3256Hash;
import model.lightchain.Identifier;
import modules.ads.AuthenticatedDataStructure;
/**
* Implementation of an in-memory Authenticated Skip List
* that is capable of storing and retrieval of LightChain entities.
*/
public class MerkleTree implements AuthenticatedDataStructure {
private static final Sha3256Hasher hasher = new Sha3256Hasher();
private final ReentrantReadWriteLock lock;
private final ArrayList<MerkleNode> leafNodes;
private final Map<Sha3256Hash, Integer> leafNodesHashTable;
private final Map<Identifier, Entity> entityHashTable;
private int size;
private MerkleNode root;
/**
* Default constructor for a Merkle Tree.
*/
public MerkleTree() {
this.size = 0;
this.root = new MerkleNode();
this.leafNodes = new ArrayList<>();
this.lock = new ReentrantReadWriteLock();
this.leafNodesHashTable = new HashMap<>();
this.entityHashTable = new HashMap<>();
}
@Override
public modules.ads.AuthenticatedEntity put(Entity e) throws IllegalArgumentException {
try {
lock.writeLock().lock();
if (e == null) {
throw new IllegalArgumentException("entity cannot be null");
}
Sha3256Hash hash = new Sha3256Hash(e.id().getBytes());
Integer idx = leafNodesHashTable.get(hash);
if (idx == null) {
leafNodes.add(new MerkleNode(e, false));
leafNodesHashTable.put(hash, size);
entityHashTable.put(e.id(), e);
size++;
buildMerkleTree();
MerkleProof proof = getProof(e.id());
return new MerkleTreeAuthenticatedEntity(proof, e.type(), e);
} else {
MerkleProof proof = getProof(e.id());
return new MerkleTreeAuthenticatedEntity(proof, e.type(), e);
}
} finally {
lock.writeLock().unlock();
}
}
@Override
public modules.ads.AuthenticatedEntity get(Identifier id) throws IllegalArgumentException {
MerkleProof proof;
if (id == null) {
throw new IllegalArgumentException("identifier cannot be null");
}
try {
lock.readLock().lock();
proof = getProof(id);
Entity e = entityHashTable.get(id);
return new MerkleTreeAuthenticatedEntity(proof, e.type(), e);
} finally {
lock.readLock().unlock();
}
}
private MerkleProof getProof(Identifier id) throws IllegalArgumentException {
ArrayList<Boolean> isLeftNode = new ArrayList<>();
Sha3256Hash hash = new Sha3256Hash(id.getBytes());
Integer idx = leafNodesHashTable.get(hash);
if (idx == null) {
throw new IllegalArgumentException("identifier not found");
}
ArrayList<Sha3256Hash> path = new ArrayList<>();
MerkleNode currentNode = leafNodes.get(idx);
while (currentNode != root) {
path.add(currentNode.getSibling().getHash());
isLeftNode.add(currentNode.isLeft());
currentNode = currentNode.getParent();
}
return new MerkleProof(path, root.getHash(), isLeftNode);
}
private void buildMerkleTree() {
// keeps nodes of the current level of the merkle tree
// will be updated bottom up
// initialized with leaves
ArrayList<MerkleNode> currentLevelNodes = new ArrayList<>(leafNodes);
// keeps nodes of the next level of merkle tree
// used as an intermediary data structure.
ArrayList<MerkleNode> nextLevelNodes = new ArrayList<>();
while (currentLevelNodes.size() > 1) { // more than one current node, means we have not yet reached root.
for (int i = 0; i < currentLevelNodes.size(); i += 2) {
// pairs up current level nodes as siblings for next level.
MerkleNode left = currentLevelNodes.get(i);
left.setLeft(true);
MerkleNode right;
if (i + 1 < currentLevelNodes.size()) {
right = currentLevelNodes.get(i + 1); // we have a right node
} else {
// TODO: edge case need to get fixed.
right = new MerkleNode(left.getHash());
}
Sha3256Hash hash = hasher.computeHash(left.getHash().getBytes(), right.getHash().getBytes());
MerkleNode parent = new MerkleNode(hash, left, right);
left.setParent(parent);
right.setParent(parent);
nextLevelNodes.add(parent);
}
currentLevelNodes = nextLevelNodes;
nextLevelNodes = new ArrayList<>();
}
root = currentLevelNodes.get(0);
}
public int size() {
return this.size;
}
/**
* returns all entities in the merkle tree.
*
* @return all entities in the merkle tree.
*/
public ArrayList<Entity> all() {
ArrayList<Entity> entities = new ArrayList<>();
for (Entity e : entityHashTable.values()) {
entities.add(e);
}
return entities;
}
}