Implemented chat client simulation
This commit is contained in:
@@ -2,25 +2,40 @@ import java.util.concurrent.Executors;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.stream.IntStream;
|
||||
import model.BlockChain;
|
||||
import model.ChatClient;
|
||||
import model.Miner;
|
||||
|
||||
public final class Main {
|
||||
|
||||
public static void main(final String[] args) throws InterruptedException {
|
||||
final var nThreads = Runtime.getRuntime().availableProcessors();
|
||||
final var blockChain = new BlockChain();
|
||||
blockChain.load();
|
||||
|
||||
final var nThreads = Runtime.getRuntime().availableProcessors();
|
||||
final var executor = Executors.newFixedThreadPool(nThreads);
|
||||
final var chatExecutor = Executors.newScheduledThreadPool(nThreads);
|
||||
|
||||
// Mocks 3 chat clients who send messages to the blockchain
|
||||
IntStream.range(0, 3)
|
||||
.mapToObj(chatClientId -> new ChatClient(chatClientId, blockChain))
|
||||
.forEach(e -> chatExecutor.scheduleAtFixedRate(e, 0, 100, TimeUnit.MILLISECONDS));
|
||||
|
||||
final var minerExecutor = Executors.newFixedThreadPool(nThreads);
|
||||
|
||||
// Creation of 5 miners
|
||||
IntStream.range(0, 5)
|
||||
.mapToObj(minerId -> new Miner(minerId, blockChain))
|
||||
.forEach(executor::submit);
|
||||
.mapToObj(minerId -> new Miner(minerId, blockChain))
|
||||
.forEach(minerExecutor::submit);
|
||||
|
||||
executor.shutdown();
|
||||
minerExecutor.shutdown();
|
||||
|
||||
if (!executor.awaitTermination(60, TimeUnit.SECONDS)) {
|
||||
executor.shutdownNow();
|
||||
if (!minerExecutor.awaitTermination(60, TimeUnit.SECONDS)) {
|
||||
minerExecutor.shutdownNow();
|
||||
}
|
||||
|
||||
chatExecutor.shutdown();
|
||||
|
||||
if (!chatExecutor.awaitTermination(60, TimeUnit.SECONDS)) {
|
||||
chatExecutor.shutdownNow();
|
||||
}
|
||||
|
||||
blockChain.save();
|
||||
|
||||
@@ -1,32 +1,39 @@
|
||||
package model;
|
||||
|
||||
import static java.lang.String.valueOf;
|
||||
|
||||
import util.HashFunction;
|
||||
import java.io.Serializable;
|
||||
import java.util.Date;
|
||||
import java.util.Random;
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
public class Block implements Serializable {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
private final String previousBlockHash;
|
||||
private final String blockHash;
|
||||
private final int minerId;
|
||||
private final int id;
|
||||
private final int hashZeroes;
|
||||
private final int magicNumber;
|
||||
private final float generationSecs;
|
||||
private final List<String> chatMessages;
|
||||
private final long timeStamp;
|
||||
private final String blockHash;
|
||||
private final transient Random random = new Random();
|
||||
private int magicNumber;
|
||||
private float generationSecs = 0;
|
||||
|
||||
public Block(final String previousBlockHash,final int minerId, final int id, final int hashZeroes) {
|
||||
public Block(
|
||||
final String previousBlockHash,
|
||||
final String blockHash,
|
||||
final int minerId,
|
||||
final int id,
|
||||
final int magicNumber,
|
||||
final float generationSecs,
|
||||
final List<String> chatMessages
|
||||
) {
|
||||
this.previousBlockHash = previousBlockHash;
|
||||
this.blockHash = blockHash;
|
||||
this.minerId = minerId;
|
||||
this.id = id;
|
||||
this.hashZeroes = hashZeroes;
|
||||
this.magicNumber = magicNumber;
|
||||
this.generationSecs = generationSecs;
|
||||
this.chatMessages = chatMessages;
|
||||
this.timeStamp = new Date().getTime();
|
||||
this.blockHash = calculateBlockHash();
|
||||
}
|
||||
|
||||
public String getBlockHash() {
|
||||
@@ -37,35 +44,23 @@ public class Block implements Serializable {
|
||||
return previousBlockHash;
|
||||
}
|
||||
|
||||
public float getGenerationSecs() {
|
||||
return generationSecs;
|
||||
}
|
||||
|
||||
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"
|
||||
+ "Created by miner #" + this.minerId + "\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";
|
||||
+ "Created by miner #" + this.minerId + "\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 data: \n"
|
||||
+ getMessages()
|
||||
+ "Block was generating for " + this.generationSecs + " seconds";
|
||||
}
|
||||
|
||||
public String getMessages() {
|
||||
return chatMessages.stream()
|
||||
.map(m -> m.concat("\n"))
|
||||
.collect(Collectors.joining());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,46 +1,80 @@
|
||||
package model;
|
||||
|
||||
import util.FileManagement;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import util.HashFunction;
|
||||
|
||||
import java.util.*;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import static java.lang.String.valueOf;
|
||||
|
||||
public class BlockChain {
|
||||
|
||||
private final Random random = new Random();
|
||||
private final List<Block> blockList = new LinkedList<>();
|
||||
private final List<String> chatMessages = new ArrayList<>();
|
||||
private int hashZeroes;
|
||||
private static final List<Block> blockList = new LinkedList<>();
|
||||
private int magicNumber;
|
||||
private float generationSecs = 0;
|
||||
|
||||
public synchronized void addBlock(final int minerId) {
|
||||
final int nextId = blockList.size();
|
||||
final String previousBlockHash = (nextId > 0) ? blockList.get(nextId - 1).getBlockHash() : "0";
|
||||
final String blockHash = calculateBlockHash(minerId);
|
||||
|
||||
final var block = new Block(previousBlockHash, minerId, nextId, hashZeroes);
|
||||
final var block = new Block(
|
||||
previousBlockHash,
|
||||
blockHash,
|
||||
minerId,
|
||||
nextId,
|
||||
magicNumber,
|
||||
generationSecs,
|
||||
chatMessages
|
||||
);
|
||||
blockList.add(block);
|
||||
System.out.println(block);
|
||||
this.chatMessages.clear();
|
||||
|
||||
final float generationTime = blockList.get(nextId).getGenerationSecs();
|
||||
final String CASE;
|
||||
|
||||
if (generationTime < 10) {
|
||||
if (generationSecs < 1) {
|
||||
hashZeroes += 1;
|
||||
CASE = "N was increased to " + hashZeroes + "\n";
|
||||
} else if (generationTime > 60) {
|
||||
System.out.println("N was increased to " + hashZeroes +"\n");
|
||||
} else if (generationSecs > 10) {
|
||||
hashZeroes -= 1;
|
||||
CASE = "N was decreased by 1" + "\n";
|
||||
System.out.println("N was decreased by 1\n");
|
||||
} else {
|
||||
CASE = "N stays the same";
|
||||
System.out.println("N stays the same\n");
|
||||
}
|
||||
|
||||
System.out.println(block);
|
||||
System.out.println(CASE);
|
||||
}
|
||||
|
||||
public boolean validateBlockchain() {
|
||||
public void acceptMessage(String message) {
|
||||
if (!blockList.isEmpty()) {
|
||||
chatMessages.add(message);
|
||||
}
|
||||
}
|
||||
|
||||
public void load() {
|
||||
FileManagement.loadBlockchain(blockList);
|
||||
}
|
||||
|
||||
public void save() {
|
||||
FileManagement.saveBlockchain(blockList);
|
||||
}
|
||||
|
||||
public String toString() {
|
||||
if (!validateBlockchain()) {
|
||||
System.out.println("Blockchain invalid!");
|
||||
}
|
||||
return blockList.stream().map(Block::toString).collect(Collectors.joining("\n"));
|
||||
}
|
||||
|
||||
private boolean validateBlockchain() {
|
||||
if (blockList.isEmpty()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
final List<String> previousHashes = blockList.stream().map(Block::getPreviousBlockHash).collect(
|
||||
Collectors.toList());
|
||||
Collectors.toList());
|
||||
final List<String> hashes = blockList.stream().map(Block::getBlockHash).collect(Collectors.toList());
|
||||
|
||||
for (var index = 1; index < hashes.size(); index++) {
|
||||
@@ -52,19 +86,23 @@ public class BlockChain {
|
||||
return true;
|
||||
}
|
||||
|
||||
public String toString() {
|
||||
if (!validateBlockchain()) {
|
||||
System.out.println("Blockchain invalid!");
|
||||
}
|
||||
return blockList.stream().map(Block::toString).collect(Collectors.joining("\n"));
|
||||
private String calculateBlockHash(final int minerId) {
|
||||
final long start = System.currentTimeMillis();
|
||||
var hash = "";
|
||||
do {
|
||||
hash = HashFunction.applySha256(minerId
|
||||
+ valueOf(new Date().getTime())
|
||||
+ calculateMagicNumber()
|
||||
);
|
||||
} while (!hash.matches("(?s)0{" + hashZeroes + "}([^0].*)?"));
|
||||
final long end = System.currentTimeMillis();
|
||||
generationSecs = (end - start) / 1000F;
|
||||
return hash;
|
||||
}
|
||||
|
||||
public void load() {
|
||||
FileManagement.loadBlockchain(blockList);
|
||||
}
|
||||
|
||||
public void save() {
|
||||
FileManagement.saveBlockchain(blockList);
|
||||
private int calculateMagicNumber() {
|
||||
magicNumber = random.nextInt();
|
||||
return magicNumber;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
31
src/model/ChatClient.java
Normal file
31
src/model/ChatClient.java
Normal file
@@ -0,0 +1,31 @@
|
||||
package model;
|
||||
|
||||
import java.util.Random;
|
||||
|
||||
public class ChatClient implements Runnable {
|
||||
|
||||
private final int clientId;
|
||||
private final BlockChain blockChain;
|
||||
private static final Random random = new Random();
|
||||
|
||||
public ChatClient(int chatClientId, BlockChain blockChain) {
|
||||
this.clientId = chatClientId;
|
||||
this.blockChain = blockChain;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
blockChain.acceptMessage(generateRandomAlphabeticString());
|
||||
}
|
||||
|
||||
private String generateRandomAlphabeticString() {
|
||||
int leftLimit = 97; // letter 'a'
|
||||
int rightLimit = 122; // letter 'z'
|
||||
int targetStringLength = 10;
|
||||
|
||||
return "Client " + clientId + " says: " + random.ints(leftLimit, rightLimit + 1)
|
||||
.limit(targetStringLength)
|
||||
.collect(StringBuilder::new, StringBuilder::appendCodePoint, StringBuilder::append)
|
||||
.toString();
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user