From fe8e8211a7fd5b6849c1de0a7219328eb55a44c5 Mon Sep 17 00:00:00 2001 From: oleksii Date: Mon, 19 Nov 2018 00:33:14 +0200 Subject: [PATCH 1/5] First phase implementation done --- src/blockchain/Block.java | 40 +++++++++++++++++++++++++++++++ src/blockchain/Blockchain.java | 44 ++++++++++++++++++++++++++++++++++ src/blockchain/Main.java | 18 ++++++++++++-- src/blockchain/StringUtil.java | 28 ++++++++++++++++++++++ 4 files changed, 128 insertions(+), 2 deletions(-) create mode 100644 src/blockchain/Block.java create mode 100644 src/blockchain/Blockchain.java create mode 100644 src/blockchain/StringUtil.java diff --git a/src/blockchain/Block.java b/src/blockchain/Block.java new file mode 100644 index 0000000..534b357 --- /dev/null +++ b/src/blockchain/Block.java @@ -0,0 +1,40 @@ +package blockchain; + +public class Block { + + private final Integer id; + private final String hashPreviousBlock; + private final String hash; + private final String data; + private final Long timestamp; + + + public Block(Integer id, String hashPreviousBlock, String hash, String data, + Long timestamp) { + this.id = id; + this.hashPreviousBlock = hashPreviousBlock; + this.hash = hash; + this.data = data; + this.timestamp = timestamp; + } + + public Integer getId() { + return id; + } + + public String getHashPreviousBlock() { + return hashPreviousBlock; + } + + public String getHash() { + return hash; + } + + public Long getTimestamp() { + return timestamp; + } + + public String getData() { + return data; + } +} diff --git a/src/blockchain/Blockchain.java b/src/blockchain/Blockchain.java new file mode 100644 index 0000000..39c97f6 --- /dev/null +++ b/src/blockchain/Blockchain.java @@ -0,0 +1,44 @@ +package blockchain; + +import java.util.Collections; +import java.util.Date; +import java.util.Iterator; +import java.util.LinkedList; + +public class Blockchain implements Iterable { + + private final LinkedList blocks; + + public Blockchain() { + blocks = new LinkedList<>(Collections.singleton( + new Block(1, + "0", + StringUtil.applySha256("0" + "pivot"), + "pivot", + new Date().getTime())) + ); + } + + public void generate() { + Long timestamp = new Date().getTime(); + Block tailBlock = blocks.getLast(); + String hash = StringUtil.applySha256(tailBlock.getHash() + "hello"); + blocks.add(new Block(tailBlock.getId() + 1, tailBlock.getHash(), hash, "hello", timestamp)); + } + + public boolean validate() { + for (int i = 1; i < blocks.size(); i++) { + Block prev = blocks.get(i - 1); + Block cur = blocks.get(i); + if (!cur.getHashPreviousBlock().equals(prev.getHash())) { + return false; + } + } + return true; + } + + @Override + public Iterator iterator() { + return blocks.iterator(); + } +} diff --git a/src/blockchain/Main.java b/src/blockchain/Main.java index adf1d02..c44806f 100644 --- a/src/blockchain/Main.java +++ b/src/blockchain/Main.java @@ -1,7 +1,21 @@ package blockchain; public class Main { - public static void main(String[] args) { - System.out.print("Hello world!"); + + public static void main(String[] args) { + Blockchain blockchain = new Blockchain(); + for (int i = 0; i < 10; i++) { + blockchain.generate(); + } + + for (Block block : blockchain) { + System.out.printf("Block:\n"); + System.out.printf("Id: %s\n", block.getId()); + System.out.printf("Timestamp: %s\n", block.getTimestamp()); + System.out.printf("Hash of the previous block:\n%s\n", block.getHashPreviousBlock()); + System.out.printf("Hash of the block: \n%s\n\n", block.getHash()); } + + System.out.println(blockchain.validate()); + } } \ No newline at end of file diff --git a/src/blockchain/StringUtil.java b/src/blockchain/StringUtil.java new file mode 100644 index 0000000..9a4c32d --- /dev/null +++ b/src/blockchain/StringUtil.java @@ -0,0 +1,28 @@ +package blockchain; + +import java.nio.charset.StandardCharsets; +import java.security.MessageDigest; + +public class StringUtil { + + /** + * Applies Sha256 to a string and returns a hash. + */ + public static String applySha256(String input) { + try { + MessageDigest digest = MessageDigest.getInstance("SHA-256"); + byte[] hash = digest.digest(input.getBytes(StandardCharsets.UTF_8)); + StringBuilder hexString = new StringBuilder(); + for (byte elem : hash) { + String hex = Integer.toHexString(0xff & elem); + if (hex.length() == 1) { + hexString.append('0'); + } + hexString.append(hex); + } + return hexString.toString(); + } catch (Exception e) { + throw new RuntimeException(e); + } + } +} From ddac0ff8d02695a247504226c30b9a4293f9054e Mon Sep 17 00:00:00 2001 From: oleksii Date: Tue, 27 Nov 2018 01:04:00 +0200 Subject: [PATCH 2/5] Second phase implementation done --- src/blockchain/Block.java | 12 +++++- src/blockchain/Blockchain.java | 69 +++++++++++++++++++++++++++------- src/blockchain/Main.java | 30 +++++++++------ src/blockchain/Persister.java | 43 +++++++++++++++++++++ 4 files changed, 127 insertions(+), 27 deletions(-) create mode 100644 src/blockchain/Persister.java diff --git a/src/blockchain/Block.java b/src/blockchain/Block.java index 534b357..6b92063 100644 --- a/src/blockchain/Block.java +++ b/src/blockchain/Block.java @@ -1,21 +1,25 @@ package blockchain; -public class Block { +import java.io.Serializable; + +public class Block implements Serializable { private final Integer id; private final String hashPreviousBlock; private final String hash; private final String data; private final Long timestamp; + private final Integer magicNumber; public Block(Integer id, String hashPreviousBlock, String hash, String data, - Long timestamp) { + Long timestamp, Integer magicNumber) { this.id = id; this.hashPreviousBlock = hashPreviousBlock; this.hash = hash; this.data = data; this.timestamp = timestamp; + this.magicNumber = magicNumber; } public Integer getId() { @@ -37,4 +41,8 @@ public Long getTimestamp() { public String getData() { return data; } + + public Integer getMagicNumber() { + return magicNumber; + } } diff --git a/src/blockchain/Blockchain.java b/src/blockchain/Blockchain.java index 39c97f6..c38e05d 100644 --- a/src/blockchain/Blockchain.java +++ b/src/blockchain/Blockchain.java @@ -4,29 +4,63 @@ import java.util.Date; import java.util.Iterator; import java.util.LinkedList; +import java.util.List; +import java.util.Random; +import java.util.stream.Stream; public class Blockchain implements Iterable { private final LinkedList blocks; + private final Random random; + private final String prefix; + private final Persister persister; - public Blockchain() { - blocks = new LinkedList<>(Collections.singleton( - new Block(1, - "0", - StringUtil.applySha256("0" + "pivot"), - "pivot", - new Date().getTime())) - ); + public Blockchain(int N) { + persister = new Persister("blockchain.ser"); + random = new Random(); + prefix = Stream.iterate("0", x -> "0").limit(N).reduce("", (x, y) -> x + y); + if (persister.existsChain()) { + LinkedList blocks = (LinkedList) persister.load(); + if (validate(blocks)) { + this.blocks = blocks; + } else { + Block firstBlock = initialBlockHead(); + this.blocks = new LinkedList<>(Collections.singleton(firstBlock)); + } + } else { + Block firstBlock = initialBlockHead(); + blocks = new LinkedList<>(Collections.singleton(firstBlock)); + } + persister.save(blocks); } + public void generate() { Long timestamp = new Date().getTime(); Block tailBlock = blocks.getLast(); - String hash = StringUtil.applySha256(tailBlock.getHash() + "hello"); - blocks.add(new Block(tailBlock.getId() + 1, tailBlock.getHash(), hash, "hello", timestamp)); + Integer magicNumber = random.nextInt(); + String hash = StringUtil.applySha256(tailBlock.getHash() + "hello" + magicNumber); + while (!hash.startsWith(prefix)) { + magicNumber = random.nextInt(); + hash = StringUtil.applySha256(tailBlock.getHash() + "hello" + magicNumber); + } + + Block newBlock = new Block(tailBlock.getId() + 1, tailBlock.getHash(), hash, "hello", timestamp, + magicNumber); + blocks.add(newBlock); + persister.save(blocks); } public boolean validate() { + return validate(blocks); + } + + @Override + public Iterator iterator() { + return blocks.iterator(); + } + + private boolean validate(List blocks) { for (int i = 1; i < blocks.size(); i++) { Block prev = blocks.get(i - 1); Block cur = blocks.get(i); @@ -37,8 +71,17 @@ public boolean validate() { return true; } - @Override - public Iterator iterator() { - return blocks.iterator(); + private Block initialBlockHead() { + Integer magicNumber = random.nextInt(); + String hash = StringUtil.applySha256("0" + magicNumber); + while (!hash.startsWith(prefix)) { + magicNumber = random.nextInt(); + hash = StringUtil.applySha256("0" + magicNumber); + } + return new Block(1, + "0", + hash, + "empty", + new Date().getTime(), magicNumber); } } diff --git a/src/blockchain/Main.java b/src/blockchain/Main.java index c44806f..d67a9ad 100644 --- a/src/blockchain/Main.java +++ b/src/blockchain/Main.java @@ -1,21 +1,27 @@ package blockchain; +import java.util.Scanner; + public class Main { public static void main(String[] args) { - Blockchain blockchain = new Blockchain(); - for (int i = 0; i < 10; i++) { - blockchain.generate(); - } + try (Scanner scanner = new Scanner(System.in)) { + System.out.printf("Enter how many zeros the hash must starts with: "); + Blockchain blockchain = new Blockchain(scanner.nextInt()); + for (int i = 0; i < 10; i++) { + blockchain.generate(); + } - for (Block block : blockchain) { - System.out.printf("Block:\n"); - System.out.printf("Id: %s\n", block.getId()); - System.out.printf("Timestamp: %s\n", block.getTimestamp()); - System.out.printf("Hash of the previous block:\n%s\n", block.getHashPreviousBlock()); - System.out.printf("Hash of the block: \n%s\n\n", block.getHash()); - } + for (Block block : blockchain) { + System.out.printf("Block:\n"); + System.out.printf("Id: %s\n", block.getId()); + System.out.printf("Timestamp: %s\n", block.getTimestamp()); + System.out.printf("Magic number: %s\n", block.getMagicNumber()); + System.out.printf("Hash of the previous block:\n%s\n", block.getHashPreviousBlock()); + System.out.printf("Hash of the block: \n%s\n\n", block.getHash()); + } - System.out.println(blockchain.validate()); + System.out.println(blockchain.validate()); + } } } \ No newline at end of file diff --git a/src/blockchain/Persister.java b/src/blockchain/Persister.java new file mode 100644 index 0000000..7b67c12 --- /dev/null +++ b/src/blockchain/Persister.java @@ -0,0 +1,43 @@ +package blockchain; + +import java.io.FileInputStream; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.ObjectInputStream; +import java.io.ObjectOutputStream; +import java.nio.file.Files; +import java.nio.file.Paths; +import java.util.List; + +public class Persister { + + + private String filename; + + public Persister(String filename) { + this.filename = filename; + } + + public void save(List blockchain) { + try (FileOutputStream fileOutputStream = new FileOutputStream(filename); + ObjectOutputStream objectOutputStream = new ObjectOutputStream(fileOutputStream)) { + objectOutputStream.writeObject(blockchain); + } catch (IOException e) { + e.printStackTrace(); + } + } + + public List load() { + try (FileInputStream fileInputStream = new FileInputStream(filename); + ObjectInputStream objectInputStream = new ObjectInputStream(fileInputStream)) { + return (List) objectInputStream.readObject(); + } catch (IOException | ClassNotFoundException e) { + e.printStackTrace(); + } + return null; + } + + public boolean existsChain() { + return Files.exists(Paths.get(filename)); + } +} From 7078dffbfbd4f7657a613f61779d9533524e038f Mon Sep 17 00:00:00 2001 From: oleksii Date: Mon, 3 Dec 2018 00:46:23 +0200 Subject: [PATCH 3/5] Second phase implementation done --- src/blockchain/Block.java | 8 +++- src/blockchain/Blockchain.java | 64 ++++++++++++------------------- src/blockchain/FilePersister.java | 47 +++++++++++++++++++++++ src/blockchain/Main.java | 13 +++++-- src/blockchain/Persister.java | 40 ++----------------- 5 files changed, 92 insertions(+), 80 deletions(-) create mode 100644 src/blockchain/FilePersister.java diff --git a/src/blockchain/Block.java b/src/blockchain/Block.java index 6b92063..ec7c110 100644 --- a/src/blockchain/Block.java +++ b/src/blockchain/Block.java @@ -10,16 +10,18 @@ public class Block implements Serializable { private final String data; private final Long timestamp; private final Integer magicNumber; + private final Long generationTime; public Block(Integer id, String hashPreviousBlock, String hash, String data, - Long timestamp, Integer magicNumber) { + Long timestamp, Integer magicNumber, Long generationTime) { this.id = id; this.hashPreviousBlock = hashPreviousBlock; this.hash = hash; this.data = data; this.timestamp = timestamp; this.magicNumber = magicNumber; + this.generationTime = generationTime; } public Integer getId() { @@ -45,4 +47,8 @@ public String getData() { public Integer getMagicNumber() { return magicNumber; } + + public Long getGenerationTime() { + return generationTime; + } } diff --git a/src/blockchain/Blockchain.java b/src/blockchain/Blockchain.java index c38e05d..83ff3f0 100644 --- a/src/blockchain/Blockchain.java +++ b/src/blockchain/Blockchain.java @@ -1,7 +1,6 @@ package blockchain; import java.util.Collections; -import java.util.Date; import java.util.Iterator; import java.util.LinkedList; import java.util.List; @@ -10,43 +9,32 @@ public class Blockchain implements Iterable { - private final LinkedList blocks; + private final List blocks; private final Random random; private final String prefix; private final Persister persister; - public Blockchain(int N) { - persister = new Persister("blockchain.ser"); - random = new Random(); - prefix = Stream.iterate("0", x -> "0").limit(N).reduce("", (x, y) -> x + y); - if (persister.existsChain()) { - LinkedList blocks = (LinkedList) persister.load(); - if (validate(blocks)) { - this.blocks = blocks; - } else { - Block firstBlock = initialBlockHead(); - this.blocks = new LinkedList<>(Collections.singleton(firstBlock)); - } + public Blockchain(int N, Persister persister) { + this.persister = persister; + this.random = new Random(); + this.prefix = Stream.iterate("0", x -> "0") + .limit(N) + .reduce("", (x, y) -> x + y); + + List blocks = persister.load(); + if (!blocks.isEmpty() && validate(blocks)) { + this.blocks = blocks; } else { - Block firstBlock = initialBlockHead(); - blocks = new LinkedList<>(Collections.singleton(firstBlock)); + this.blocks = new LinkedList<>(Collections.singleton( + generateNextBlock(1, "0", "empty"))); + persister.save(blocks); } - persister.save(blocks); } public void generate() { - Long timestamp = new Date().getTime(); - Block tailBlock = blocks.getLast(); - Integer magicNumber = random.nextInt(); - String hash = StringUtil.applySha256(tailBlock.getHash() + "hello" + magicNumber); - while (!hash.startsWith(prefix)) { - magicNumber = random.nextInt(); - hash = StringUtil.applySha256(tailBlock.getHash() + "hello" + magicNumber); - } - - Block newBlock = new Block(tailBlock.getId() + 1, tailBlock.getHash(), hash, "hello", timestamp, - magicNumber); + Block tailBlock = blocks.get(blocks.size() - 1); + Block newBlock = generateNextBlock(tailBlock.getId() + 1, tailBlock.getHash(), "hello"); blocks.add(newBlock); persister.save(blocks); } @@ -71,17 +59,15 @@ private boolean validate(List blocks) { return true; } - private Block initialBlockHead() { - Integer magicNumber = random.nextInt(); - String hash = StringUtil.applySha256("0" + magicNumber); - while (!hash.startsWith(prefix)) { + private Block generateNextBlock(int id, String hashPreviousBlock, String data) { + long timestamp = System.currentTimeMillis(); + Integer magicNumber; + String hash; + do { magicNumber = random.nextInt(); - hash = StringUtil.applySha256("0" + magicNumber); - } - return new Block(1, - "0", - hash, - "empty", - new Date().getTime(), magicNumber); + hash = StringUtil.applySha256(hashPreviousBlock + magicNumber); + } while (!hash.startsWith(prefix)); + return new Block(id, hashPreviousBlock, hash, data, timestamp, magicNumber, + System.currentTimeMillis() - timestamp); } } diff --git a/src/blockchain/FilePersister.java b/src/blockchain/FilePersister.java new file mode 100644 index 0000000..2b08d9e --- /dev/null +++ b/src/blockchain/FilePersister.java @@ -0,0 +1,47 @@ +package blockchain; + +import java.io.FileInputStream; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.ObjectInputStream; +import java.io.ObjectOutputStream; +import java.nio.file.Files; +import java.nio.file.Paths; +import java.util.List; + +class FilePersister implements Persister { + + + private final String filename; + + public FilePersister(String filename) { + this.filename = filename; + } + + @Override + public void save(List blockchain) { + try (FileOutputStream fileOutputStream = new FileOutputStream(filename); + ObjectOutputStream objectOutputStream = new ObjectOutputStream(fileOutputStream)) { + objectOutputStream.writeObject(blockchain); + } catch (IOException e) { + throw new RuntimeException(); + } + } + + @Override + public List load() { + if (fileExists()) { + try (FileInputStream fileInputStream = new FileInputStream(filename); + ObjectInputStream objectInputStream = new ObjectInputStream(fileInputStream)) { + return (List) objectInputStream.readObject(); + } catch (IOException | ClassNotFoundException e) { + return List.of(); + } + } + return List.of(); + } + + private boolean fileExists() { + return Files.exists(Paths.get(filename)); + } +} diff --git a/src/blockchain/Main.java b/src/blockchain/Main.java index d67a9ad..492d299 100644 --- a/src/blockchain/Main.java +++ b/src/blockchain/Main.java @@ -6,8 +6,11 @@ public class Main { public static void main(String[] args) { try (Scanner scanner = new Scanner(System.in)) { - System.out.printf("Enter how many zeros the hash must starts with: "); - Blockchain blockchain = new Blockchain(scanner.nextInt()); + System.out.print("Enter how many zeros the hash must starts with: "); + + Persister persister = new FilePersister("blockchain.ser"); + Blockchain blockchain = new Blockchain(scanner.nextInt(), persister); + for (int i = 0; i < 10; i++) { blockchain.generate(); } @@ -18,10 +21,12 @@ public static void main(String[] args) { System.out.printf("Timestamp: %s\n", block.getTimestamp()); System.out.printf("Magic number: %s\n", block.getMagicNumber()); System.out.printf("Hash of the previous block:\n%s\n", block.getHashPreviousBlock()); - System.out.printf("Hash of the block: \n%s\n\n", block.getHash()); + System.out.printf("Hash of the block: \n%s\n", block.getHash()); + System.out + .printf("Block was generating for: %s seconds\n\n", block.getGenerationTime() / 1000); } - System.out.println(blockchain.validate()); + System.out.printf("Valid: %s\n", blockchain.validate()); } } } \ No newline at end of file diff --git a/src/blockchain/Persister.java b/src/blockchain/Persister.java index 7b67c12..fe58027 100644 --- a/src/blockchain/Persister.java +++ b/src/blockchain/Persister.java @@ -1,43 +1,11 @@ package blockchain; -import java.io.FileInputStream; -import java.io.FileOutputStream; -import java.io.IOException; -import java.io.ObjectInputStream; -import java.io.ObjectOutputStream; -import java.nio.file.Files; -import java.nio.file.Paths; import java.util.List; -public class Persister { +public interface Persister { + void save(List blockchain); - private String filename; - - public Persister(String filename) { - this.filename = filename; - } - - public void save(List blockchain) { - try (FileOutputStream fileOutputStream = new FileOutputStream(filename); - ObjectOutputStream objectOutputStream = new ObjectOutputStream(fileOutputStream)) { - objectOutputStream.writeObject(blockchain); - } catch (IOException e) { - e.printStackTrace(); - } - } - - public List load() { - try (FileInputStream fileInputStream = new FileInputStream(filename); - ObjectInputStream objectInputStream = new ObjectInputStream(fileInputStream)) { - return (List) objectInputStream.readObject(); - } catch (IOException | ClassNotFoundException e) { - e.printStackTrace(); - } - return null; - } - - public boolean existsChain() { - return Files.exists(Paths.get(filename)); - } + List load(); } + From ee39a82633d5afcb257bbaa94f1cd30e490a9220 Mon Sep 17 00:00:00 2001 From: oleksii Date: Sat, 8 Dec 2018 16:04:54 +0200 Subject: [PATCH 4/5] Third phase implementation done --- src/blockchain/Block.java | 9 ++- src/blockchain/BlockGenerator.java | 25 ++++++++ src/blockchain/Blockchain.java | 98 +++++++++++++++++++----------- src/blockchain/Main.java | 37 +++++------ src/blockchain/miners/Miner.java | 27 ++++++++ 5 files changed, 140 insertions(+), 56 deletions(-) create mode 100644 src/blockchain/BlockGenerator.java create mode 100644 src/blockchain/miners/Miner.java diff --git a/src/blockchain/Block.java b/src/blockchain/Block.java index ec7c110..806bdcf 100644 --- a/src/blockchain/Block.java +++ b/src/blockchain/Block.java @@ -5,6 +5,7 @@ public class Block implements Serializable { private final Integer id; + private final Long minerId; private final String hashPreviousBlock; private final String hash; private final String data; @@ -13,9 +14,11 @@ public class Block implements Serializable { private final Long generationTime; - public Block(Integer id, String hashPreviousBlock, String hash, String data, + public Block(Integer id, Long minerId, String hashPreviousBlock, String hash, + String data, Long timestamp, Integer magicNumber, Long generationTime) { this.id = id; + this.minerId = minerId; this.hashPreviousBlock = hashPreviousBlock; this.hash = hash; this.data = data; @@ -51,4 +54,8 @@ public Integer getMagicNumber() { public Long getGenerationTime() { return generationTime; } + + public Long getMinerId() { + return minerId; + } } diff --git a/src/blockchain/BlockGenerator.java b/src/blockchain/BlockGenerator.java new file mode 100644 index 0000000..498a247 --- /dev/null +++ b/src/blockchain/BlockGenerator.java @@ -0,0 +1,25 @@ +package blockchain; + +import java.util.Random; + +public class BlockGenerator { + + private final Random random; + + public BlockGenerator() { + this.random = new Random(); + } + + public Block generateNextBlock(int id, long minerId, String prefix, String previousHash, + String data) { + long timestamp = System.currentTimeMillis(); + Integer magicNumber; + String hash; + do { + magicNumber = random.nextInt(); + hash = StringUtil.applySha256(previousHash + magicNumber); + } while (!hash.startsWith(prefix)); + return new Block(id, minerId, previousHash, hash, data, timestamp, magicNumber, + System.currentTimeMillis() - timestamp); + } +} diff --git a/src/blockchain/Blockchain.java b/src/blockchain/Blockchain.java index 83ff3f0..aff249b 100644 --- a/src/blockchain/Blockchain.java +++ b/src/blockchain/Blockchain.java @@ -1,54 +1,95 @@ package blockchain; import java.util.Collections; -import java.util.Iterator; import java.util.LinkedList; import java.util.List; -import java.util.Random; +import java.util.concurrent.TimeUnit; import java.util.stream.Stream; -public class Blockchain implements Iterable { +public class Blockchain { + + private static final Long ACCEPTABLE_TIME = TimeUnit.SECONDS.toMillis(6); private final List blocks; - private final Random random; - private final String prefix; + private int prefixLength; private final Persister persister; - public Blockchain(int N, Persister persister) { + public Blockchain(Persister persister, BlockGenerator blockGenerator) { this.persister = persister; - this.random = new Random(); - this.prefix = Stream.iterate("0", x -> "0") - .limit(N) - .reduce("", (x, y) -> x + y); - + this.prefixLength = 0; List blocks = persister.load(); - if (!blocks.isEmpty() && validate(blocks)) { + if (!blocks.isEmpty() && validateAllChain(blocks)) { this.blocks = blocks; } else { this.blocks = new LinkedList<>(Collections.singleton( - generateNextBlock(1, "0", "empty"))); + blockGenerator.generateNextBlock(1, 0, "", "0", ""))); persister.save(blocks); } } + public synchronized void accept(Block newBlock) { + if (isValid(newBlock)) { + outputAndAdjust(newBlock); + blocks.add(newBlock); + persister.save(blocks); + } + } + + public synchronized String getPrefix() { + return Stream.iterate("0", x -> "0") + .limit(prefixLength) + .reduce("", (x, y) -> x + y); + } + + public synchronized Block tail() { + return blocks.get(blocks.size() - 1); + } - public void generate() { + private boolean isValid(Block newBlock) { Block tailBlock = blocks.get(blocks.size() - 1); - Block newBlock = generateNextBlock(tailBlock.getId() + 1, tailBlock.getHash(), "hello"); - blocks.add(newBlock); - persister.save(blocks); + if (!newBlock.getHash().startsWith(getPrefix()) || !newBlock.getHashPreviousBlock() + .equals(tailBlock.getHash())) { + return false; + } + return true; + } + + private void outputAndAdjust(Block newBlock) { + outputStats(newBlock); + adjustComplexity(newBlock); + } + + private void outputStats(Block newBlock) { + System.out.printf("Block:\n"); + System.out.printf("Id: %s\n", newBlock.getId()); + System.out.printf("Created by miner # %s\n", newBlock.getMinerId()); + System.out.printf("Timestamp: %s\n", newBlock.getTimestamp()); + System.out.printf("Magic number: %s\n", newBlock.getMagicNumber()); + System.out.printf("Hash of the previous block:\n%s\n", newBlock.getHashPreviousBlock()); + System.out.printf("Hash of the block: \n%s\n", newBlock.getHash()); + System.out + .printf("Block was generating for: %s seconds\n", newBlock.getGenerationTime() / 1000); } - public boolean validate() { - return validate(blocks); + private void adjustComplexity(Block newBlock) { + if (!withinAcceptable(newBlock.getGenerationTime())) { + if (ACCEPTABLE_TIME < newBlock.getGenerationTime()) { + System.out.printf("N was decreased to %s\n\n", prefixLength); + prefixLength--; + } else { + System.out.printf("N was increased to %s\n\n", prefixLength); + prefixLength++; + } + } else { + System.out.printf("N stays the same\n\n"); + } } - @Override - public Iterator iterator() { - return blocks.iterator(); + private boolean withinAcceptable(Long generationTime) { + return Math.abs(generationTime - ACCEPTABLE_TIME) <= 1000; } - private boolean validate(List blocks) { + private boolean validateAllChain(List blocks) { for (int i = 1; i < blocks.size(); i++) { Block prev = blocks.get(i - 1); Block cur = blocks.get(i); @@ -59,15 +100,4 @@ private boolean validate(List blocks) { return true; } - private Block generateNextBlock(int id, String hashPreviousBlock, String data) { - long timestamp = System.currentTimeMillis(); - Integer magicNumber; - String hash; - do { - magicNumber = random.nextInt(); - hash = StringUtil.applySha256(hashPreviousBlock + magicNumber); - } while (!hash.startsWith(prefix)); - return new Block(id, hashPreviousBlock, hash, data, timestamp, magicNumber, - System.currentTimeMillis() - timestamp); - } } diff --git a/src/blockchain/Main.java b/src/blockchain/Main.java index 492d299..023ec28 100644 --- a/src/blockchain/Main.java +++ b/src/blockchain/Main.java @@ -1,32 +1,27 @@ package blockchain; -import java.util.Scanner; +import blockchain.miners.Miner; public class Main { - public static void main(String[] args) { - try (Scanner scanner = new Scanner(System.in)) { - System.out.print("Enter how many zeros the hash must starts with: "); + public static void main(String[] args) throws InterruptedException { - Persister persister = new FilePersister("blockchain.ser"); - Blockchain blockchain = new Blockchain(scanner.nextInt(), persister); + int NUMBER_OF_MINERS = 10; - for (int i = 0; i < 10; i++) { - blockchain.generate(); - } + Persister persister = new FilePersister("blockchain.ser"); + BlockGenerator blockGenerator = new BlockGenerator(); + Blockchain blockchain = new Blockchain(persister, blockGenerator); - for (Block block : blockchain) { - System.out.printf("Block:\n"); - System.out.printf("Id: %s\n", block.getId()); - System.out.printf("Timestamp: %s\n", block.getTimestamp()); - System.out.printf("Magic number: %s\n", block.getMagicNumber()); - System.out.printf("Hash of the previous block:\n%s\n", block.getHashPreviousBlock()); - System.out.printf("Hash of the block: \n%s\n", block.getHash()); - System.out - .printf("Block was generating for: %s seconds\n\n", block.getGenerationTime() / 1000); - } - - System.out.printf("Valid: %s\n", blockchain.validate()); + Thread[] miners = new Thread[NUMBER_OF_MINERS]; + for (int i = 0; i < NUMBER_OF_MINERS; i++) { + miners[i] = new Thread(new Miner(blockchain, blockGenerator, Long.valueOf(i))); + } + for (Thread miner : miners) { + miner.start(); + } + Thread.sleep(10000); + for (Thread miner : miners) { + miner.interrupt(); } } } \ No newline at end of file diff --git a/src/blockchain/miners/Miner.java b/src/blockchain/miners/Miner.java new file mode 100644 index 0000000..2c1dca2 --- /dev/null +++ b/src/blockchain/miners/Miner.java @@ -0,0 +1,27 @@ +package blockchain.miners; + +import blockchain.Block; +import blockchain.BlockGenerator; +import blockchain.Blockchain; + +public class Miner implements Runnable { + + private final Blockchain blockchain; + private BlockGenerator blockGenerator; + private final Long id; + + public Miner(Blockchain blockchain, BlockGenerator blockGenerator, Long id) { + this.blockchain = blockchain; + this.blockGenerator = blockGenerator; + this.id = id; + } + + @Override + public void run() { + while (!Thread.currentThread().isInterrupted()) { + Block prev = blockchain.tail(); + blockchain.accept(blockGenerator.generateNextBlock(prev.getId() + 1, id, + blockchain.getPrefix(), prev.getHash(), id.toString())); + } + } +} From df2dd3f9c5206b8a178f3b6be2e0685f03e743bc Mon Sep 17 00:00:00 2001 From: oleksii Date: Mon, 17 Dec 2018 00:36:12 +0200 Subject: [PATCH 5/5] third phase implementation done --- src/blockchain/Block.java | 15 +-------------- src/blockchain/BlockGenerator.java | 25 ------------------------- src/blockchain/Blockchain.java | 27 +++++++++++++++++---------- src/blockchain/Main.java | 5 ++--- src/blockchain/miners/Miner.java | 24 ++++++++++++++++++------ 5 files changed, 38 insertions(+), 58 deletions(-) delete mode 100644 src/blockchain/BlockGenerator.java diff --git a/src/blockchain/Block.java b/src/blockchain/Block.java index 806bdcf..45b2937 100644 --- a/src/blockchain/Block.java +++ b/src/blockchain/Block.java @@ -8,23 +8,18 @@ public class Block implements Serializable { private final Long minerId; private final String hashPreviousBlock; private final String hash; - private final String data; private final Long timestamp; private final Integer magicNumber; - private final Long generationTime; public Block(Integer id, Long minerId, String hashPreviousBlock, String hash, - String data, - Long timestamp, Integer magicNumber, Long generationTime) { + Long timestamp, Integer magicNumber) { this.id = id; this.minerId = minerId; this.hashPreviousBlock = hashPreviousBlock; this.hash = hash; - this.data = data; this.timestamp = timestamp; this.magicNumber = magicNumber; - this.generationTime = generationTime; } public Integer getId() { @@ -43,18 +38,10 @@ public Long getTimestamp() { return timestamp; } - public String getData() { - return data; - } - public Integer getMagicNumber() { return magicNumber; } - public Long getGenerationTime() { - return generationTime; - } - public Long getMinerId() { return minerId; } diff --git a/src/blockchain/BlockGenerator.java b/src/blockchain/BlockGenerator.java deleted file mode 100644 index 498a247..0000000 --- a/src/blockchain/BlockGenerator.java +++ /dev/null @@ -1,25 +0,0 @@ -package blockchain; - -import java.util.Random; - -public class BlockGenerator { - - private final Random random; - - public BlockGenerator() { - this.random = new Random(); - } - - public Block generateNextBlock(int id, long minerId, String prefix, String previousHash, - String data) { - long timestamp = System.currentTimeMillis(); - Integer magicNumber; - String hash; - do { - magicNumber = random.nextInt(); - hash = StringUtil.applySha256(previousHash + magicNumber); - } while (!hash.startsWith(prefix)); - return new Block(id, minerId, previousHash, hash, data, timestamp, magicNumber, - System.currentTimeMillis() - timestamp); - } -} diff --git a/src/blockchain/Blockchain.java b/src/blockchain/Blockchain.java index aff249b..9c3f69a 100644 --- a/src/blockchain/Blockchain.java +++ b/src/blockchain/Blockchain.java @@ -1,6 +1,7 @@ package blockchain; import java.util.Collections; +import java.util.Date; import java.util.LinkedList; import java.util.List; import java.util.concurrent.TimeUnit; @@ -8,13 +9,13 @@ public class Blockchain { - private static final Long ACCEPTABLE_TIME = TimeUnit.SECONDS.toMillis(6); + private static final Long ACCEPTABLE_TIME = TimeUnit.SECONDS.toMillis(5); private final List blocks; private int prefixLength; private final Persister persister; - public Blockchain(Persister persister, BlockGenerator blockGenerator) { + public Blockchain(Persister persister) { this.persister = persister; this.prefixLength = 0; List blocks = persister.load(); @@ -22,7 +23,7 @@ public Blockchain(Persister persister, BlockGenerator blockGenerator) { this.blocks = blocks; } else { this.blocks = new LinkedList<>(Collections.singleton( - blockGenerator.generateNextBlock(1, 0, "", "0", ""))); + new Block(1, 0L, "", "0", new Date().getTime(), 0))); persister.save(blocks); } } @@ -68,25 +69,31 @@ private void outputStats(Block newBlock) { System.out.printf("Hash of the previous block:\n%s\n", newBlock.getHashPreviousBlock()); System.out.printf("Hash of the block: \n%s\n", newBlock.getHash()); System.out - .printf("Block was generating for: %s seconds\n", newBlock.getGenerationTime() / 1000); + .printf("Block was generating for: %s seconds\n", getGenerationTime(newBlock) / 1000); } private void adjustComplexity(Block newBlock) { - if (!withinAcceptable(newBlock.getGenerationTime())) { - if (ACCEPTABLE_TIME < newBlock.getGenerationTime()) { + if (!withinAcceptable(newBlock)) { + if (ACCEPTABLE_TIME < getGenerationTime(newBlock)) { + if (prefixLength > 0) { + prefixLength--; + } System.out.printf("N was decreased to %s\n\n", prefixLength); - prefixLength--; } else { - System.out.printf("N was increased to %s\n\n", prefixLength); prefixLength++; + System.out.printf("N was increased to %s\n\n", prefixLength); } } else { System.out.printf("N stays the same\n\n"); } } - private boolean withinAcceptable(Long generationTime) { - return Math.abs(generationTime - ACCEPTABLE_TIME) <= 1000; + private Long getGenerationTime(Block newBlock) { + return tail() == null ? 0 : (newBlock.getTimestamp() - tail().getTimestamp()); + } + + private boolean withinAcceptable(Block newBlock) { + return Math.abs(getGenerationTime(newBlock) - ACCEPTABLE_TIME) <= 1000; } private boolean validateAllChain(List blocks) { diff --git a/src/blockchain/Main.java b/src/blockchain/Main.java index 023ec28..6c91797 100644 --- a/src/blockchain/Main.java +++ b/src/blockchain/Main.java @@ -9,12 +9,11 @@ public static void main(String[] args) throws InterruptedException { int NUMBER_OF_MINERS = 10; Persister persister = new FilePersister("blockchain.ser"); - BlockGenerator blockGenerator = new BlockGenerator(); - Blockchain blockchain = new Blockchain(persister, blockGenerator); + Blockchain blockchain = new Blockchain(persister); Thread[] miners = new Thread[NUMBER_OF_MINERS]; for (int i = 0; i < NUMBER_OF_MINERS; i++) { - miners[i] = new Thread(new Miner(blockchain, blockGenerator, Long.valueOf(i))); + miners[i] = new Thread(new Miner(blockchain, Long.valueOf(i))); } for (Thread miner : miners) { miner.start(); diff --git a/src/blockchain/miners/Miner.java b/src/blockchain/miners/Miner.java index 2c1dca2..f82ac6d 100644 --- a/src/blockchain/miners/Miner.java +++ b/src/blockchain/miners/Miner.java @@ -1,27 +1,39 @@ package blockchain.miners; import blockchain.Block; -import blockchain.BlockGenerator; import blockchain.Blockchain; +import blockchain.StringUtil; +import java.util.Random; public class Miner implements Runnable { private final Blockchain blockchain; - private BlockGenerator blockGenerator; + private final Random random; private final Long id; - public Miner(Blockchain blockchain, BlockGenerator blockGenerator, Long id) { + public Miner(Blockchain blockchain, Long id) { this.blockchain = blockchain; - this.blockGenerator = blockGenerator; this.id = id; + this.random = new Random(); } @Override public void run() { while (!Thread.currentThread().isInterrupted()) { Block prev = blockchain.tail(); - blockchain.accept(blockGenerator.generateNextBlock(prev.getId() + 1, id, - blockchain.getPrefix(), prev.getHash(), id.toString())); + blockchain.accept(generateNextBlock(prev.getId() + 1, id, prev.getHash())); } } + + private Block generateNextBlock(int id, long minerId, String previousHash) { + Integer magicNumber; + String hash; + String prefix; + do { + prefix = blockchain.getPrefix(); + magicNumber = random.nextInt(); + hash = StringUtil.applySha256(previousHash + magicNumber); + } while (!hash.startsWith(prefix)); + return new Block(id, minerId, previousHash, hash, System.currentTimeMillis(), magicNumber); + } }