Initial commit
This commit is contained in:
3
.gitignore
vendored
Normal file
3
.gitignore
vendored
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
/out/production/blockchain/
|
||||||
|
/.idea/
|
||||||
|
/blockchain.iml
|
||||||
16
src/Main.java
Normal file
16
src/Main.java
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
import model.BlockChain;
|
||||||
|
import java.util.Scanner;
|
||||||
|
import java.util.stream.IntStream;
|
||||||
|
|
||||||
|
public final class Main {
|
||||||
|
public static void main(final String[] args) {
|
||||||
|
final var sc = new Scanner(System.in);
|
||||||
|
System.out.print("Enter how many zeros the hash must start with: ");
|
||||||
|
final var hashZeroes = sc.nextInt();
|
||||||
|
final var blockChain = new BlockChain(hashZeroes);
|
||||||
|
|
||||||
|
IntStream.range(0, 5).forEach(blockChain::addBlock);
|
||||||
|
|
||||||
|
System.out.println(blockChain);
|
||||||
|
}
|
||||||
|
}
|
||||||
61
src/model/Block.java
Normal file
61
src/model/Block.java
Normal file
@@ -0,0 +1,61 @@
|
|||||||
|
package model;
|
||||||
|
|
||||||
|
import static java.lang.String.valueOf;
|
||||||
|
|
||||||
|
import java.util.Date;
|
||||||
|
import java.util.Random;
|
||||||
|
import util.HashFunction;
|
||||||
|
|
||||||
|
public class Block {
|
||||||
|
|
||||||
|
private final String previousBlockHash;
|
||||||
|
private final int id;
|
||||||
|
private final int hashZeroes;
|
||||||
|
private final long timeStamp;
|
||||||
|
private final String blockHash;
|
||||||
|
private final Random random = new Random();
|
||||||
|
private int magicNumber;
|
||||||
|
private float generationSecs = 0;
|
||||||
|
|
||||||
|
public Block(final String previousBlockHash, final int id, final int hashZeroes) {
|
||||||
|
this.previousBlockHash = previousBlockHash;
|
||||||
|
this.id = id;
|
||||||
|
this.hashZeroes = hashZeroes;
|
||||||
|
this.timeStamp = new Date().getTime();
|
||||||
|
this.blockHash = calculateBlockHash();
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getBlockHash() {
|
||||||
|
return blockHash;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getPreviousBlockHash() {
|
||||||
|
return previousBlockHash;
|
||||||
|
}
|
||||||
|
|
||||||
|
private String calculateBlockHash() {
|
||||||
|
final long start = System.currentTimeMillis();
|
||||||
|
var hash = "";
|
||||||
|
do {
|
||||||
|
hash = HashFunction.applySha256(this.id + valueOf(this.timeStamp) + calculateMagicNumber());
|
||||||
|
} while (!hash.matches("(?s)0{"+hashZeroes+"}([^0].*)?"));
|
||||||
|
final long end = System.currentTimeMillis();
|
||||||
|
generationSecs = (end - start) / 1000F;
|
||||||
|
return hash;
|
||||||
|
}
|
||||||
|
|
||||||
|
private int calculateMagicNumber() {
|
||||||
|
magicNumber = random.nextInt();
|
||||||
|
return magicNumber;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String toString() {
|
||||||
|
return "Block: " + "\n"
|
||||||
|
+"Id: " + this.id + "\n"
|
||||||
|
+"Timestamp: " + this.timeStamp + "\n"
|
||||||
|
+"Magic number: " + this.magicNumber + "\n"
|
||||||
|
+"Hash of the previous block: " + "\n" + this.previousBlockHash + "\n"
|
||||||
|
+"Hash of the block: \n" + this.blockHash + "\n"
|
||||||
|
+"Block was generating for: " + this.generationSecs + " seconds" + "\n";
|
||||||
|
}
|
||||||
|
}
|
||||||
44
src/model/BlockChain.java
Normal file
44
src/model/BlockChain.java
Normal file
@@ -0,0 +1,44 @@
|
|||||||
|
package model;
|
||||||
|
|
||||||
|
import java.util.LinkedList;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
|
public class BlockChain {
|
||||||
|
private final int hashZeroes;
|
||||||
|
private static final List<Block> blockList = new LinkedList<>();
|
||||||
|
|
||||||
|
public BlockChain(final int hashZeroes) {
|
||||||
|
this.hashZeroes = hashZeroes;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void addBlock(final int id) {
|
||||||
|
final String previousBlockHash = (id > 0) ? blockList.get(id - 1).getBlockHash() : "0";
|
||||||
|
blockList.add(new Block(previousBlockHash, id, hashZeroes));
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean validateBlockchain() {
|
||||||
|
if (blockList.isEmpty()) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
final List<String> previousHashes = blockList.stream().map(Block::getPreviousBlockHash).collect(
|
||||||
|
Collectors.toList());
|
||||||
|
final List<String> hashes = blockList.stream().map(Block::getBlockHash).collect(Collectors.toList());
|
||||||
|
|
||||||
|
for (var index = 1; index < hashes.size(); index++) {
|
||||||
|
if (!previousHashes.get(index).equals(hashes.get(index - 1))) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String toString() {
|
||||||
|
if (!validateBlockchain()) {
|
||||||
|
System.out.println("Blockchain invalid!");
|
||||||
|
}
|
||||||
|
return blockList.stream().map(Block::toString).collect(Collectors.joining("\n"));
|
||||||
|
}
|
||||||
|
}
|
||||||
25
src/util/HashFunction.java
Normal file
25
src/util/HashFunction.java
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
package util;
|
||||||
|
|
||||||
|
import java.nio.charset.StandardCharsets;
|
||||||
|
import java.security.MessageDigest;
|
||||||
|
|
||||||
|
public final class HashFunction {
|
||||||
|
/* Applies Sha256 to a string and returns a hash. */
|
||||||
|
public static String applySha256(final String input) throws RuntimeException {
|
||||||
|
try {
|
||||||
|
final var digest = MessageDigest.getInstance("SHA-256");
|
||||||
|
/* Applies sha256 to our input */
|
||||||
|
final byte[] hash = digest.digest(input.getBytes(StandardCharsets.UTF_8));
|
||||||
|
final var hexString = new StringBuilder();
|
||||||
|
for (final byte elem: hash) {
|
||||||
|
final var hex = Integer.toHexString(0xff & elem);
|
||||||
|
if (hex.length() == 1) hexString.append('0');
|
||||||
|
hexString.append(hex);
|
||||||
|
}
|
||||||
|
return hexString.toString();
|
||||||
|
}
|
||||||
|
catch(final Exception e) {
|
||||||
|
throw new RuntimeException(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user