Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
48 changes: 48 additions & 0 deletions src/blockchain/Block.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
package blockchain;

import java.io.Serializable;

public class Block implements Serializable {

private final Integer id;
private final String hashPreviousBlock;
private final String hash;
private final Long timestamp;
private final Integer magicNumber;
private final Long generationTime;


public Block(Integer id, String hashPreviousBlock, String hash,
Long timestamp, Integer magicNumber, Long generationTime) {
this.id = id;
this.hashPreviousBlock = hashPreviousBlock;
this.hash = hash;
this.timestamp = timestamp;
this.magicNumber = magicNumber;
this.generationTime = generationTime;
}

public Integer getId() {
return id;
}

public String getHashPreviousBlock() {
return hashPreviousBlock;
}

public String getHash() {
return hash;
}

public Long getTimestamp() {
return timestamp;
}

public Integer getMagicNumber() {
return magicNumber;
}

public Long getGenerationTime() {
return generationTime;
}
}
73 changes: 73 additions & 0 deletions src/blockchain/Blockchain.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
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.stream.Stream;

public class Blockchain implements Iterable<Block> {

private final List<Block> blocks;
private final Random random;
private final String prefix;
private final Persister persister;

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<Block> blocks = persister.load();
if (!blocks.isEmpty() && validate(blocks)) {
this.blocks = blocks;
} else {
this.blocks = new LinkedList<>(Collections.singleton(
generateNextBlock(1, "0")));
persister.save(blocks);
}
}


public void generate() {
Block tailBlock = blocks.get(blocks.size() - 1);
Block newBlock = generateNextBlock(tailBlock.getId() + 1, tailBlock.getHash());
blocks.add(newBlock);
persister.save(blocks);
}

public boolean validate() {
return validate(blocks);
}

@Override
public Iterator<Block> iterator() {
return blocks.iterator();
}

private boolean validate(List<Block> blocks) {
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;
}

private Block generateNextBlock(int id, String hashPreviousBlock) {
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, timestamp, magicNumber,
System.currentTimeMillis() - timestamp);
}
}
47 changes: 47 additions & 0 deletions src/blockchain/FilePersister.java
Original file line number Diff line number Diff line change
@@ -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<Block> blockchain) {
try (FileOutputStream fileOutputStream = new FileOutputStream(filename);
ObjectOutputStream objectOutputStream = new ObjectOutputStream(fileOutputStream)) {
objectOutputStream.writeObject(blockchain);
} catch (IOException e) {
throw new RuntimeException();
}
}

@Override
public List<Block> load() {
if (fileExists()) {
try (FileInputStream fileInputStream = new FileInputStream(filename);
ObjectInputStream objectInputStream = new ObjectInputStream(fileInputStream)) {
return (List<Block>) objectInputStream.readObject();
} catch (IOException | ClassNotFoundException e) {
return List.of();
}
}
return List.of();
}

private boolean fileExists() {
return Files.exists(Paths.get(filename));
}
}
27 changes: 25 additions & 2 deletions src/blockchain/Main.java
Original file line number Diff line number Diff line change
@@ -1,7 +1,30 @@
package blockchain;

import java.util.Scanner;

public class Main {
public static void main(String[] args) {
System.out.print("Hello world!");

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: ");

Persister persister = new FilePersister("blockchain.ser");
Blockchain blockchain = new Blockchain(scanner.nextInt(), persister);

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("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);
}
}
}
}
11 changes: 11 additions & 0 deletions src/blockchain/Persister.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
package blockchain;

import java.util.List;

public interface Persister {

void save(List<Block> blockchain);

List<Block> load();
}

28 changes: 28 additions & 0 deletions src/blockchain/StringUtil.java
Original file line number Diff line number Diff line change
@@ -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);
}
}
}