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
Original file line number Diff line number Diff line change
Expand Up @@ -28,8 +28,8 @@ public ApiController(final BlockChainService blockChainService) {

@RequestMapping(path = "/chain")
public List<Block> chain() {
BlockChain blockChain = blockChainService.getChain();
List<Block> mainChain = new LinkedList<>();
final BlockChain blockChain = blockChainService.getChain();
final List<Block> mainChain = new LinkedList<>();
TreeNode<Block> b = blockChain.getEndBlock();
do {
mainChain.add(b.getData());
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
package nl.johannisk.node.controller;

import com.netflix.discovery.EurekaClient;
import nl.johannisk.node.service.BlockChainService;
import nl.johannisk.node.service.model.Block;
import nl.johannisk.node.service.model.Message;
Expand Down
46 changes: 24 additions & 22 deletions node/src/main/java/nl/johannisk/node/hasher/JChainHasher.java
Original file line number Diff line number Diff line change
Expand Up @@ -9,23 +9,26 @@
import java.util.Set;

public class JChainHasher {

private JChainHasher() {

}

public static String hash(final String parentHash, final Set<Message> content, final String nonce) {
StringBuilder b = new StringBuilder();
b.append(parentHash);
b.append(content.toString());
b.append(nonce);
String blockData = b.toString();
MessageDigest messageDigest = null;
final MessageDigest messageDigest = getMessageDigest();
final String blockData = new StringBuilder()
.append(parentHash)
.append(content.toString())
.append(nonce)
.toString();
messageDigest.update(blockData.getBytes());
return new String(Base64.getEncoder().encode(messageDigest.digest()), StandardCharsets.UTF_8);
}

private static MessageDigest getMessageDigest() {
try {
messageDigest = MessageDigest.getInstance("SHA-256");
return MessageDigest.getInstance("SHA-256");
} catch (NoSuchAlgorithmException e) {
/*
* This excpetion may never be thrown.
* This exception may never be thrown.
*
* Every implementation of the Java platform is required to support the following standard MessageDigest algorithms:
* MD5
Expand All @@ -34,20 +37,19 @@ public static String hash(final String parentHash, final Set<Message> content, f
*/
throw new RuntimeException("Java platform does not support standard encryption", e);
}
messageDigest.update(blockData.getBytes());
return new String(Base64.getEncoder().encode(messageDigest.digest()), StandardCharsets.UTF_8);
}

public static boolean isValidHash(final String hash) {
String lowered = hash;
int j = lowered.indexOf('J');
int c = lowered.indexOf('C');
int o = lowered.indexOf('o');
int r = lowered.indexOf('r');
int e = lowered.indexOf('e');
return ((j != -1 && c != -1 && o != -1 && r != -1 && e != -1) && (j < c &&
c < o &&
o < r &&
r < e));
if (null == hash) {
return false;
}
final int jIndex = hash.indexOf('J');
final int cIndex = hash.indexOf('C');
final int oIndex = hash.indexOf('o');
final int rIndex = hash.indexOf('r');
final int eIndex = hash.indexOf('e');
final boolean didFindJcoreCharacters = jIndex != -1 && cIndex != -1 && oIndex != -1 && rIndex != -1 && eIndex != -1;
final boolean jcoreCharactersInCorrectOrder = jIndex < cIndex && cIndex < oIndex && oIndex < rIndex && rIndex < eIndex;
return didFindJcoreCharacters && jcoreCharactersInCorrectOrder;
}
}
46 changes: 24 additions & 22 deletions node/src/main/java/nl/johannisk/node/service/BlockChainService.java
Original file line number Diff line number Diff line change
Expand Up @@ -14,30 +14,33 @@
import org.springframework.stereotype.Service;
import org.springframework.web.client.RestTemplate;

import java.util.*;
import java.util.HashSet;
import java.util.List;
import java.util.Random;
import java.util.Set;
import java.util.stream.Collectors;

@Service
public class BlockChainService {

@Value("${eureka.instance.instanceId}")
String instanceId;
private String instanceId;

final Set<Message> unhandledMessages;
final Set<Message> handledMessages;
final BlockChain chain;
private BlockCreatorService blockCreatorService;
private final Set<Message> unhandledMessages;
private final Set<Message> handledMessages;
private final BlockChain chain;
private final BlockCreatorService blockCreatorService;
private final EurekaClient eurekaClient;
Random random;
private final Random random;

@Autowired
public BlockChainService(final BlockCreatorService blockCreatorService, final EurekaClient eurekaClient) {
this.blockCreatorService = blockCreatorService;
this.eurekaClient = eurekaClient;
unhandledMessages = new HashSet<>();
handledMessages = new HashSet<>();
chain = new BlockChain();
random = new Random();
this.unhandledMessages = new HashSet<>();
this.handledMessages = new HashSet<>();
this.chain = new BlockChain();
this.random = new Random();
}

public BlockChain getChain() {
Expand All @@ -52,37 +55,36 @@ public void addMessage(final Message m) {
if (!handledMessages.contains(m) && !unhandledMessages.contains(m)) {
unhandledMessages.add(m);
if (unhandledMessages.size() >= 5 && blockCreatorService.state == BlockCreatorService.State.READY) {
Set<Message> blockContent = pickMessagesForPotentialBlock();
final Set<Message> blockContent = pickMessagesForPotentialBlock();
blockCreatorService.createBlock(chain.getEndBlock().getData(), blockContent, this::addCreatedBlock);
}

}
}

public void addBlock(final Block block) {
String hash = JChainHasher.hash(block.getParentHash(), block.getContent(), block.getNonce());
final String hash = JChainHasher.hash(block.getParentHash(), block.getContent(), block.getNonce());
if (JChainHasher.isValidHash(hash) && block.getHash().equals(hash) && !chain.containsBlock(block)) {
String lastBlockHash = chain.getEndBlock().getData().getHash();
final String lastBlockHash = chain.getEndBlock().getData().getHash();
chain.addBlock(block);
if (!chain.getEndBlock().getData().getHash().equals(lastBlockHash)) {
if (blockCreatorService.state == BlockCreatorService.State.RUNNING) {
blockCreatorService.state = BlockCreatorService.State.CANCELLED;
}
resetMessagesAccordingToChain();
if (unhandledMessages.size() >= 5 && blockCreatorService.state == BlockCreatorService.State.READY) {
Set<Message> blockContent = pickMessagesForPotentialBlock();
final Set<Message> blockContent = pickMessagesForPotentialBlock();
blockCreatorService.createBlock(chain.getEndBlock().getData(), blockContent, this::addCreatedBlock);
}

}
}
}

public void addCreatedBlock(final Block block) {
private void addCreatedBlock(final Block block) {
if (chain.getEndBlock().getData().getHash().equals(block.getParentHash())) {
chain.addBlock(block);
Application application = eurekaClient.getApplication("jchain-node");
List<InstanceInfo> instanceInfo = application.getInstances();
final Application application = eurekaClient.getApplication("jchain-node");
final List<InstanceInfo> instanceInfo = application.getInstances();
for (InstanceInfo info : instanceInfo) {
if (info.getInstanceId().equals(instanceId)) {
continue;
Expand All @@ -105,7 +107,7 @@ private void resetMessagesAccordingToChain() {
}

private Set<Message> pickMessagesForPotentialBlock() {
Set<Message> messageForNextBlock = unhandledMessages.stream()
final Set<Message> messageForNextBlock = unhandledMessages.stream()
.limit(5)
.collect(Collectors.toSet());
unhandledMessages.removeAll(messageForNextBlock);
Expand All @@ -115,13 +117,13 @@ private Set<Message> pickMessagesForPotentialBlock() {

@Async
private void informNodeOfNewBlock(final String host, final Block block) {
int delay = random.nextInt(10000) + 3000;
final int delay = random.nextInt(10000) + 3000;
try {
Thread.sleep(delay);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
RestTemplate restTemplate = new RestTemplate();
final RestTemplate restTemplate = new RestTemplate();
restTemplate.postForObject("http://localhost:" + host + "/node/block", block, Block.class);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -19,16 +19,16 @@ public enum State {
CANCELLED
}

final Random random;
State state;
private final Random random;
private State state;

public BlockCreatorService() {
random = new Random();
state = State.READY;
this.random = new Random();
this.state = State.READY;
}

@Async
public void createBlock(Block parentBlock, Set<Message> messages, Consumer<Block> consumer) {
void createBlock(Block parentBlock, Set<Message> messages, Consumer<Block> consumer) {
state = State.RUNNING;
String hash;
String parentHash = parentBlock.getHash();
Expand All @@ -40,9 +40,9 @@ public void createBlock(Block parentBlock, Set<Message> messages, Consumer<Block
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
} while(!JChainHasher.isValidHash(hash) && state.equals(State.RUNNING));
} while (!JChainHasher.isValidHash(hash) && state.equals(State.RUNNING));

if(state.equals(State.RUNNING)) {
if (state.equals(State.RUNNING)) {
consumer.accept(new Block(hash, parentHash, messages, Long.toString(nonce)));
}
state = State.READY;
Expand Down
16 changes: 9 additions & 7 deletions node/src/main/java/nl/johannisk/node/service/model/Block.java
Original file line number Diff line number Diff line change
@@ -1,12 +1,14 @@
package nl.johannisk.node.service.model;

import com.fasterxml.jackson.annotation.JsonProperty;
import com.google.common.base.MoreObjects;

import java.util.LinkedHashSet;
import java.util.Set;

public class Block {
public final class Block {
public static final Block ZERO;

static {
ZERO = new Block("0", "0", new LinkedHashSet<>(), "0");
}
Expand Down Expand Up @@ -44,11 +46,11 @@ public String getNonce() {

@Override
public String toString() {
return "Block{" +
"hash='" + hash + '\'' +
", parentHash='" + parentHash + '\'' +
", contents=" + contents +
", nonce='" + nonce + '\'' +
'}';
return MoreObjects.toStringHelper(this)
.add("hash", hash)
.add("parentHash", parentHash)
.add("contents", contents)
.add("nonce", nonce)
.toString();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -21,18 +21,16 @@ public BlockChain() {
maxDepth = 0;
}

public boolean addBlock(final Block block) {
public void addBlock(final Block block) {
if(blocks.containsKey(block.getParentHash())) {
TreeNode<Block> parentBlock = blocks.get(block.getParentHash());
TreeNode<Block> newNode = parentBlock.addChild(block);
final TreeNode<Block> parentBlock = blocks.get(block.getParentHash());
final TreeNode<Block> newNode = parentBlock.addChild(block);
if (newNode.getDepth() > maxDepth) {
maxDepth = newNode.getDepth();
endBlock = newNode;
}
blocks.put(block.getHash(), newNode);
return true;
}
return false;
}

public boolean containsBlock(final Block b) {
Expand All @@ -44,7 +42,7 @@ public TreeNode<Block> getEndBlock() {
}

public List<Block> getOrphanedBlocks() {
List<Block> blocksInChain = new ArrayList<>();
final List<Block> blocksInChain = new ArrayList<>();
TreeNode<Block> b = endBlock;
do {
blocksInChain.add(b.getData());
Expand Down
31 changes: 18 additions & 13 deletions node/src/main/java/nl/johannisk/node/service/model/Message.java
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
package nl.johannisk.node.service.model;

import com.fasterxml.jackson.annotation.JsonProperty;
import com.google.common.base.MoreObjects;

public class Message implements Comparable{
final int index;
final String text;
public final class Message implements Comparable {
private final int index;
private final String text;

public Message(@JsonProperty("index") final int index, @JsonProperty("text") final String text) {
this.index = index;
Expand All @@ -21,10 +22,14 @@ public String getText() {

@Override
public boolean equals(final Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
if (this == o) {
return true;
}
if (o == null || !getClass().equals(o.getClass())) {
return false;
}

Message message = (Message) o;
final Message message = (Message) o;

return index == message.index;
}
Expand All @@ -36,16 +41,16 @@ public int hashCode() {

@Override
public String toString() {
return "Message{" +
"index=" + index +
", text='" + text + '\'' +
'}';
return MoreObjects.toStringHelper(this)
.add("index", index)
.add("text", text)
.toString();
}

@Override
public int compareTo(Object o) {
if(o instanceof Message) {
Message m = (Message)o;
public int compareTo(final Object o) {
if (o instanceof Message) {
Message m = (Message) o;
return this.getIndex() - m.getIndex();
}
return 0;
Expand Down
10 changes: 3 additions & 7 deletions node/src/main/java/nl/johannisk/node/service/model/TreeNode.java
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,8 @@

public class TreeNode<T> {
private final T data;
private final List<TreeNode> children = new ArrayList<>();
private final TreeNode parent;
private final List<TreeNode<T>> children = new ArrayList<>();
private final TreeNode<T> parent;
private final int depth;

public TreeNode(final TreeNode<T> parent, final T data, final int depth) {
Expand All @@ -21,11 +21,7 @@ public TreeNode<T> addChild(final T data) {
return newChild;
}

public List<TreeNode> getChildren() {
return children;
}

public TreeNode getParent() {
public TreeNode<T> getParent() {
return parent;
}

Expand Down
Loading