-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathallcode.java
642 lines (507 loc) · 23.3 KB
/
allcode.java
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
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
import java.io.BufferedReader;
import java.io.FileReader;
import java.io.FileWriter;
import java.math.BigInteger;
import java.nio.charset.StandardCharsets;
import java.security.MessageDigest;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Map;
import java.util.Scanner;
import java.io.FileWriter;
import java.util.ArrayList;
import java.util.Map;
import java.util.Scanner;
import java.io.BufferedReader;
import java.io.FileReader;
import java.math.BigInteger;
import java.nio.charset.StandardCharsets;
import java.security.MessageDigest;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Map;
//Referenced https://www.geeksforgeeks.org/sha-256-hash-in-java/
class Node {
String address;
String balance;
String hash;
// Sets address balance and hash
public Node(String address, String balance) {
this.address = address;
this.balance = balance;
// concat address and balance into one string
String concat = address.concat(balance);
hash = hash(concat);
}
public Node(String hash) {
this.hash = hash;
}
// Referenced https://www.geeksforgeeks.org/sha-256-hash-in-java/
// takes a string and uses SHA-256 to hash
public static String hash(String s) {
MessageDigest md;
try {
md = MessageDigest.getInstance("SHA-256");
byte[] bytes = md.digest(s.getBytes(StandardCharsets.UTF_8));
// convert the bytes to signum representation
BigInteger number = new BigInteger(1, bytes);
// Convert message digest into hex value
StringBuilder hexString = new StringBuilder(number.toString(16));
// Pad with leading zeros
while (hexString.length() < 64) {
hexString.insert(0, '0');
}
// return hash
return hexString.toString();
} catch (Exception e) {
System.out.println("error making instance from messgae digest: " + e);
return s;
}
}
}
class MerkleRoot {
// take a file called filename and returns a map of the address and balance on
// each line
public static Map<String, String> InputToMap(String filename) throws Exception {
// instance of map that will put the address and integer into hashmap for later
// use of making leaf
Map<String, String> map = new HashMap<String, String>();
try (BufferedReader read = new BufferedReader(new FileReader(filename))) { // try to read the file
String line = read.readLine(); // start reading first line
while (line != null) { // while there is another line
String[] split = line.split(" "); // split the line by a space
String address = split[0]; // get the address
String balance = split[1]; // get the balance
map.put(address, balance); // put the address and balance into the map
line = read.readLine(); // read the next line
}
} catch (Exception e) {
System.out.println("There was an error while reading file: " + e);
}
return map;
}
// takes a map and returns an arraylist of nodes
public static ArrayList<Node> LeafNodes(Map<String, String> map) {
// instance of arraylist that will hold the nodes
ArrayList<Node> leafNodes = new ArrayList<Node>();
// for each key in the map
for (String key : map.keySet()) {
// get the address and balance
String address = key;
String balance = map.get(key);
// make a new node with the address and balance
Node node = new Node(address, balance); // hashes it aswell
// add the node to the arraylist
leafNodes.add(node);
}
return leafNodes;
}
// takes an arraylist of nodes and returns the root by going through and making
// new arraylists of parent nodes until there is only one node left
public static Node getMerkleRoot(ArrayList<Node> leafNodes) {
// instance of arraylist that will hold the parent nodes
ArrayList<Node> parentNodes = new ArrayList<Node>();
while (leafNodes.size() > 1) { // while there is more than one node in the leaf nodes
// for each node in the leaf nodes
// System.out.println(leafNodes.size());
for (int i = 0; i < leafNodes.size(); i += 2) {
// if there is another node after the current node
if (i + 1 < leafNodes.size()) {
// get the current node and the next node
Node node1 = leafNodes.get(i);
Node node2 = leafNodes.get(i + 1);
// concat the hashes of the two nodes
String concat = node1.hash.concat(node2.hash);
// hash the concat
String hashedParent = Node.hash(concat);
// make a new node with the hash
Node parentNode = new Node("", "");
parentNode.hash = hashedParent;
// add the node to the parent nodes
parentNodes.add(parentNode);
}
// if there is not another node after the current node
else {
// get the current node
Node node1 = leafNodes.get(i);
// concat the hash of the current node with itself
String concat2 = node1.hash.concat(node1.hash);
// hash the concat
String hashParent2 = Node.hash(concat2);
// make a new node with the hash
Node parentNode = new Node("", "");
parentNode.hash = hashParent2;
// add the node to the parent nodes
parentNodes.add(parentNode);
}
}
leafNodes = parentNodes; // set the leaf nodes to the parent nodes
parentNodes = new ArrayList<Node>(); // make a new arraylist for the parent nodes
}
return leafNodes.get(0); // return the root
}
public static ArrayList<String> generateProofOfMembership(ArrayList<Node> leafNodes, String address) {
ArrayList<String> proof = new ArrayList<>();
// Find the index of the node with the given address
int index = -1;
for (int i = 0; i < leafNodes.size(); i++) {
if (leafNodes.get(i).address.equals(address)) {
index = i;
break;
}
}
if (index == -1)
return null; // Address not found
ArrayList<Node> currentLevelNodes = new ArrayList<>(leafNodes);
while (currentLevelNodes.size() > 1) {
ArrayList<Node> parentLevelNodes = new ArrayList<>();
for (int i = 0; i < currentLevelNodes.size(); i += 2) {
Node left = currentLevelNodes.get(i);
Node right = (i + 1 < currentLevelNodes.size()) ? currentLevelNodes.get(i + 1) : left; // Handle odd
// number of
// nodes
if (i == index || i + 1 == index) {
// One of these nodes is our target; we add the other one to the proof
proof.add((i == index) ? right.hash : left.hash);
}
// Create the parent hash, similar to your getMerkleRoot method
String combinedHash = left.hash.concat(right.hash);
String parentHash = Node.hash(combinedHash);
Node parentNode = new Node("", "");
parentNode.hash = parentHash;
parentLevelNodes.add(parentNode);
}
// Update index for the next level
index = index / 2;
// Move up the tree
currentLevelNodes = parentLevelNodes;
}
return proof;
}
}
public class run {
public static void main(String[] args) throws Exception {
Scanner scan = new Scanner(System.in);
System.out.println("Enter the names of the input files separated by spaces: ");
String inputFilesStr = scan.nextLine();
String[] inputFiles = inputFilesStr.split(" ");
ArrayList<Block> blockChain = new ArrayList<>(); // Moved the declaration here
for (String inputFile : inputFiles) {
String prevHeader = null;
if (!blockChain.isEmpty()) { // Only set previous header if there's already a block in the chain
prevHeader = blockChain.get(blockChain.size() - 1).getHeader();
}
// Get the root for the current input file
String root = Block.getMerkleRoot(inputFile);
// Create a new block
Block block = new Block(prevHeader, root, 8, inputFile);
// add to blockchain
blockChain.add(block);
// Print the block
block.printBlock(true);
// check the address
System.out.println(block.balance("0x2a0f89d65a30bE98cDb42174674222A7bDeBfDfF", blockChain));
// Create the output file name
try {
// Create the output file using the input file name and replacing .txt with
// .block.out
String outputFileName = inputFile.substring(0, inputFile.length() - 4) + ".block.out";
System.out.println("Output file name: " + outputFileName);
// Write to the file
FileWriter writer = new FileWriter(outputFileName);
String blockInfo = block.getBlockInfo(false); // Get the block info as a string
writer.write(blockInfo);
writer.close(); // Close the writer
} catch (Exception e) {
System.out.println("There was an error while writing to the file: " + e);
}
}
scan.close();
}
}
public class Block {
static ArrayList<Block> blockChain = new ArrayList<>();
// Information for the current header
String prevHeader;
String root;
Integer timeStamp;
// a difficulty target NEEDS to be passed in as a value of "8"
// will result in a 50 percent sucess rate
Integer difficultyTarget;
// nonce will start at 0 and will be calculated in the block build below
Integer nonce = 0;
// Information for this block
// Map including a string for address then
Map<String, String> map;
// Header
static String header;
public Block(String prevHeader, String root, Integer difficultyTarget, String inputFileName) {
this.prevHeader = prevHeader;
this.root = root;
timeStamp = (int) (System.currentTimeMillis() / 1000);
// MAKE SURE TO ONLY PASS IN 2 FOR DIFFICULTY TARGET
this.difficultyTarget = difficultyTarget;
nonce = findNonce(root, nonce, difficultyTarget);
// Create header. In the print block we need to take this apart
header = prevHeader + root + Integer.toString(timeStamp) + Integer.toString(difficultyTarget)
+ Integer.toString(nonce);
// Hash the header
header = hash(header);
// load in the account information.
// Not sure if this is supposed to be hashed or not
try {
map = InputToMap(inputFileName);
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
public static String getHeader() {
return header;
}
public static String hash(String s) {
MessageDigest md;
try {
md = MessageDigest.getInstance("SHA-256");
byte[] bytes = md.digest(s.getBytes(StandardCharsets.UTF_8));
// convert the bytes to signum representation
BigInteger number = new BigInteger(1, bytes);
// Convert message digest into hex value
StringBuilder hexString = new StringBuilder(number.toString(16));
// Pad with leading zeros
while (hexString.length() < 64) {
hexString.insert(0, '0');
}
// return hash
return hexString.toString();
} catch (Exception e) {
System.out.println("error making instance from messgae digest: " + e);
return s;
}
}
public Integer findNonce(String hash, Integer nonce, Integer target) {
while (true) {
// concatonate nonce to hash
hash = hash + Integer.toString(nonce);
// hash the current hash
hash = hash(hash);
// When target is set to 8, this loop will check the first 8 characters of the
// hash to see if any
// of them are equal to '0' and since there are 16 possible values of hex, there
// is a 50 percent chance
// that there will be a '0' found each time.
for (int i = 0; i < target; i++) {
if (hash.charAt(i) == '0') {
return nonce;
}
}
nonce++;
}
}
// take a file called filename and returns a map of the address and balance on
// each line
public static Map<String, String> InputToMap(String filename) throws Exception {
// instance of map that will put the address and integer into hashmap for later
// use of making leaf
Map<String, String> map = new HashMap<String, String>();
try (BufferedReader read = new BufferedReader(new FileReader(filename))) { // try to read the file
String line = read.readLine(); // start reading first line
while (line != null) { // while there is another line
String[] split = line.split(" "); // split the line by a space
String address = split[0]; // get the address
String balance = split[1]; // get the balance
map.put(address, balance); // put the address and balance into the map
line = read.readLine(); // read the next line
}
} catch (Exception e) {
System.out.println("There was an error while reading file: " + e);
}
return map;
}
// Print the block
// Your tree structure should include a print function, and that function must
// take a parameter to print or not print the full account ledger (this may be
// useful for your testing now and later on).
public void printBlock(boolean printLedger) {
System.out.println("BEGIN BLOCK");
System.out.println("BEGIN HEADER:");
System.out.println("Previous Header: " + prevHeader);
System.out.println("Merkle Root: " + root);
System.out.println("Timestamp: " + timeStamp);
System.out.println("Difficulty Target: " + difficultyTarget);
System.out.println("Nonce: " + nonce);
System.out.println("END HEADER");
if (printLedger) {
System.out.println("Ledger: " + map);
}
System.out.println("END BLOCK");
System.out.println("");
}
// helper fucntuon to get the merkle root of the input file
public static String getMerkleRoot(String filename) throws Exception {
// read input inot a map
Map<String, String> map = MerkleRoot.InputToMap(filename);
// leafnode arrayList
ArrayList<Node> leafNode = MerkleRoot.LeafNodes(map);
// get merkle tree and find the merkle root
Node merkleRoot = MerkleRoot.getMerkleRoot(leafNode);
// check output
System.out.println("Merkle Root Hash: " + merkleRoot.hash);
return merkleRoot.hash;
}
// I think we were suppoed to update the main in run.java instead of make a new
// main in this class
public static void main(String[] args) throws Exception {
Scanner scan = new Scanner(System.in);
System.out.println("Enter the names of the input files separated by spaces: ");
String inputFilesStr = scan.nextLine();
String[] inputFiles = inputFilesStr.split(" ");
for (String inputFile : inputFiles) {
String prevHeader = Block.getHeader();
// Get the root for the current input file
String root = Block.getMerkleRoot(inputFile);
// Create a new block
Block block = new Block(prevHeader, root, 8, inputFile);
ArrayList<Block> blockChain = new ArrayList<>();
// add to blockchain
blockChain.add(block);
// Print the block
block.printBlock(true);
// check the address
System.out.println(block.balance("address", blockChain));
// Create the output file name
try {
// Create the output file using the input file name and replacing .txt with
// .block.out
String outputFileName = inputFile.substring(0, inputFile.length() - 4) + ".block.out";
System.out.println("Output file name: " + outputFileName);
// Write to the file
FileWriter writer = new FileWriter(outputFileName);
String blockInfo = block.getBlockInfo(false); // Get the block info as a string
writer.write(blockInfo);
writer.close(); // Close the writer
} catch (Exception e) {
System.out.println("There was an error while writing to the file: " + e);
}
}
scan.close();
}
public String getBlockInfo(boolean printLedger) {
StringBuilder blockInfo = new StringBuilder();
blockInfo.append("BEGIN BLOCK\n");
blockInfo.append("BEGIN HEADER:\n");
blockInfo.append("Previous Header: ").append(prevHeader).append("\n");
blockInfo.append("Merkle Root: ").append(root).append("\n");
blockInfo.append("Timestamp: ").append(timeStamp).append("\n");
blockInfo.append("Difficulty Target: ").append(difficultyTarget).append("\n");
blockInfo.append("Nonce: ").append(nonce).append("\n");
blockInfo.append("END HEADER\n");
if (printLedger) {
blockInfo.append("Ledger: ").append(map).append("\n");
}
blockInfo.append("END BLOCK\n\n");
return blockInfo.toString();
}
// run "varibleBlock.blockValidation()" to see if your block is right
public boolean blockValidation() {
ArrayList<Node> calulatedLeafNode = MerkleRoot.LeafNodes(map);
// get merkle tree and find the merkle root
Node merkleRoot = MerkleRoot.getMerkleRoot(calulatedLeafNode);
String calculatedMerkleRoot = merkleRoot.hash;
// if the calculated merkel root matches the blocks merkel root return true
// If the block was created properly, this will always pass
// we will have to purposely make false blocks to make this fail
if (calculatedMerkleRoot.equals(root)) {
return true;
} else {
return false;
}
}
public boolean checkBlockChain(ArrayList<Block> blockChain) {
int size = blockChain.size();
// if the blockchain is only one block, no need to check prev blocks
if (size == 1) {
return blockChain.get(0).blockValidation();
}
for (int i = 1; i <= size - 1; i++) {
// if the previous header doesnt equal the last blocks header
if (blockChain.get(i).prevHeader != blockChain.get(i - 1).header) {
return false;
}
// if blocks hash doesnt match properly/ ist valid return false
if (!blockChain.get(i).blockValidation()) {
return false;
}
}
// if whole chain is valid return true
return true;
}
// fucntion that runs through the blockchain and finds the balance associated
// with an address
public String balance(String address, ArrayList<Block> blockChain) {
int blockSize = blockChain.size() - 1;
System.out.println(blockSize);
// start with newest block in the chain
for (int i = blockSize; i >= 0; i--) {
Block b = blockChain.get(i);
// call the findAddress method to check
if (b.findAddress(address, b)) {
String blockBalance = b.map.get(address);
ArrayList<String> proofOfMembership = b.getProofOfMembership(address);
// Now, you can utilize the proofOfMembership list however you deem necessary
return "Address: " + address + ", Balance: " + blockBalance + ", Proof: "
+ proofOfMembership.toString();
}
}
return "Address " + address + " is not in this blockchain";
}
// boolean function to confirm if address is in the specific block or not
public boolean findAddress(String address, Block b) {
if (b.map.containsKey(address)) {
return true;
}
return false;
}
public static ArrayList<String> searchBlockchainForAddress(String address, ArrayList<Block> blockChain) {
for (int i = blockChain.size() - 1; i >= 0; i--) {
Block currentBlock = blockChain.get(i);
if (currentBlock.map.containsKey(address)) {
return currentBlock.getProofOfMembership(address);
}
}
return null;
}
public ArrayList<String> getProofOfMembership(String address) {
ArrayList<String> proof = new ArrayList<>();
if (!map.containsKey(address)) {
return proof; // Return empty list if address not present
}
String leafHash = hash(address + map.get(address)); // Simulating the hashing of a leaf node for this address
proof.add(leafHash);
ArrayList<Node> currentLevel = MerkleRoot.LeafNodes(map); // Assuming LeafNodes gives back the list of leaf
// nodes
Node currentNode = new Node(address, map.get(address)); // Create a node for the address
while (currentLevel.size() > 1) { // Until we reach the root
ArrayList<Node> nextLevel = new ArrayList<>();
for (int i = 0; i < currentLevel.size(); i += 2) {
Node left = currentLevel.get(i);
Node right = (i + 1 < currentLevel.size()) ? currentLevel.get(i + 1) : left; // Duplicate the last node
// if odd number
// Check if the current node is on the left or right
if (left.hash.equals(currentNode.hash) || right.hash.equals(currentNode.hash)) {
if (left.hash.equals(currentNode.hash)) {
proof.add(right.hash);
} else {
proof.add(left.hash);
}
currentNode = new Node(left.hash + right.hash); // Combine and move up
}
Node parent = new Node(left.hash + right.hash); // This is a simplistic combination, might differ in
// actual implementation
nextLevel.add(parent);
}
currentLevel = nextLevel; // Move up the tree
}
return proof;
}
}