Added README and solved ScheduledExecutorService halting

This commit is contained in:
2021-08-15 17:20:44 +02:00
parent ca2235f636
commit 147e0244e5
11 changed files with 69 additions and 116 deletions

63
.gitignore vendored
View File

@@ -1,61 +1,10 @@
/.idea/.gitignore /.idea/*
/build/reports/tests/test/css/base-style.css /.gradle/*
/build/classes/java/main/model/Block.class /build/*
/out/production/blockchain_gitea/model/Block.class /out/*
/build/classes/java/main/model/BlockChain.class /gradle/*
/out/production/blockchain_gitea/model/BlockChain.class
/blockChain.txt
/build/classes/java/test/BlockChainTest.class
/build/reports/tests/test/classes/BlockChainTest.html
/.gradle/buildOutputCleanup/buildOutputCleanup.lock
/.gradle/buildOutputCleanup/cache.properties
/.gradle/checksums/checksums.lock
/.idea/compiler.xml
/build/reports/tests/test/packages/default-package.html
/.gradle/7.1/dependencies-accessors/dependencies-accessors.lock
/.gradle/7.1/executionHistory/executionHistory.lock
/.gradle/7.1/fileHashes/fileHashes.lock
/build/classes/java/main/util/FileManagement.class
/out/production/blockchain_gitea/util/FileManagement.class
/.gradle/7.1/dependencies-accessors/gc.properties
/.gradle/7.1/gc.properties
/.gradle/vcs-1/gc.properties
/.idea/gradle.xml
/gradle/wrapper/gradle-wrapper.jar
/gradle/wrapper/gradle-wrapper.properties
/gradlew /gradlew
/gradlew.bat /gradlew.bat
/build/classes/java/main/util/HashFunction.class /blockchain.txt
/out/production/blockchain_gitea/util/HashFunction.class
/build/reports/tests/test/index.html
/.idea/sonarlint/issuestore/index.pb
/.idea/jarRepositories.xml
/.gradle/7.1/fileChanges/last-build.bin
/build/classes/java/main/Main.class
/out/production/blockchain_gitea/Main.class
/build/classes/java/main/model/Miner.class
/out/production/blockchain_gitea/model/Miner.class
/.idea/misc.xml
/build/classes/java/main/exceptions/NoSelfTransactionException.class
/build/classes/java/main/exceptions/NotEnoughCoinsException.class
/out/production/blockchain_gitea/exceptions/NotEnoughCoinsException.class
/build/test-results/test/binary/output.bin
/build/test-results/test/binary/output.bin.idx
/build/tmp/compileJava/previous-compilation-data.bin
/build/tmp/compileTestJava/previous-compilation-data.bin
/KeyPair/privateKey /KeyPair/privateKey
/.idea/inspectionProfiles/Project_Default.xml
/KeyPair/publicKey /KeyPair/publicKey
/build/reports/tests/test/js/report.js
/build/test-results/test/binary/results.bin
/.idea/runConfigurations.xml
/build/classes/java/main/util/Security.class
/out/production/blockchain_gitea/util/Security.class
/build/reports/tests/test/css/style.css
/build/test-results/test/TEST-BlockChainTest.xml
/build/classes/java/main/model/Transaction.class
/out/production/blockchain_gitea/model/Transaction.class
/build/classes/java/main/util/TransactionsGenerator.class
/out/production/blockchain_gitea/util/TransactionsGenerator.class
/.idea/uiDesigner.xml
/.idea/vcs.xml

13
README.md Normal file
View File

@@ -0,0 +1,13 @@
<h1>Blockchain</h1>
Blockchain implementation with following characteristics:
- Ability to store transactions between users.
- Mining reward for each block added to the blockchain.
- Proof of work with magic number self-balancing hashing.
- SHA-256 algorithm based hashing.
Main method allows to test it by creating fake miners, and it is also included a transaction generator to mock a series of users interacting each other.
Execution example:
![](src/main/resources/img/blockchain.png)

View File

@@ -3,7 +3,7 @@ import java.io.IOException;
import java.util.concurrent.Executors; import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeUnit;
import java.util.stream.IntStream; import java.util.stream.IntStream;
import model.BlockChain; import model.Blockchain;
import util.TransactionsGenerator; import util.TransactionsGenerator;
import model.Miner; import model.Miner;
import util.FileManagement; import util.FileManagement;
@@ -14,7 +14,7 @@ public final class Main {
public static void main(final String[] args) throws InterruptedException, IOException { public static void main(final String[] args) throws InterruptedException, IOException {
final var nThreads = Runtime.getRuntime().availableProcessors(); final var nThreads = Runtime.getRuntime().availableProcessors();
final var blockChain = BlockChain.getInstance(); final var blockchain = Blockchain.getInstance();
// Cryptographic keys management // Cryptographic keys management
final File publicKey = new File(Security.PUBLIC_KEY); final File publicKey = new File(Security.PUBLIC_KEY);
@@ -27,13 +27,13 @@ public final class Main {
final var transactionsExecutor = Executors.newScheduledThreadPool(nThreads); final var transactionsExecutor = Executors.newScheduledThreadPool(nThreads);
// Mocks a generator to send transactions into the blockchain // Mocks a generator to send transactions into the blockchain
transactionsExecutor.scheduleAtFixedRate(new TransactionsGenerator(blockChain), 0, 200, TimeUnit.MILLISECONDS); transactionsExecutor.scheduleAtFixedRate(new TransactionsGenerator(blockchain), 0, 200, TimeUnit.MILLISECONDS);
final var minerExecutor = Executors.newFixedThreadPool(nThreads); final var minerExecutor = Executors.newFixedThreadPool(nThreads);
// Creation of 15 miners // Creation of 15 miners
IntStream.range(0, 15) IntStream.range(0, 15)
.mapToObj(minerId -> new Miner(minerId, blockChain)) .mapToObj(minerId -> new Miner(minerId, blockchain))
.forEach(minerExecutor::submit); .forEach(minerExecutor::submit);
minerExecutor.shutdown(); minerExecutor.shutdown();
@@ -48,7 +48,7 @@ public final class Main {
transactionsExecutor.shutdownNow(); transactionsExecutor.shutdownNow();
} }
FileManagement.saveBlockChain(blockChain); FileManagement.saveBlockchain(blockchain);
} }

View File

@@ -48,7 +48,7 @@ public class Block implements Serializable {
public String toString() { public String toString() {
return "Block: " + "\n" return "Block: " + "\n"
+ "Created by miner #" + this.minerId + "\n" + "Created by miner #" + this.minerId + "\n"
+ "miner #" + this.minerId + " gets 100 VC\n" + "miner #" + this.minerId + " gets 100 KarmaCoins\n"
+ "Id: " + this.id + "\n" + "Id: " + this.id + "\n"
+ "Timestamp: " + this.timeStamp + "\n" + "Timestamp: " + this.timeStamp + "\n"
+ "Magic number: " + this.magicNumber + "\n" + "Magic number: " + this.magicNumber + "\n"
@@ -61,7 +61,7 @@ public class Block implements Serializable {
public String messagesToString() { public String messagesToString() {
return transactions.isEmpty() ? "No transactions\n" : transactions.stream() return transactions.isEmpty() ? "No transactions\n" : transactions.stream()
.map(m -> "ID: ".concat(String.valueOf(m.getId()).concat(" - ").concat(m.getText()).concat("\n"))) .map(m -> "Id: ".concat(String.valueOf(m.getId()).concat(" - ").concat(m.getText()).concat("\n")))
.collect(Collectors.joining()); .collect(Collectors.joining());
} }

View File

@@ -18,7 +18,7 @@ import java.util.stream.Collectors;
import static java.lang.String.valueOf; import static java.lang.String.valueOf;
public class BlockChain implements Serializable { public class Blockchain implements Serializable {
private static final long serialVersionUID = 1L; private static final long serialVersionUID = 1L;
private static final float LOWER_LIMIT_SECS = 0.1F; private static final float LOWER_LIMIT_SECS = 0.1F;
@@ -35,17 +35,17 @@ public class BlockChain implements Serializable {
private int magicNumber; private int magicNumber;
private float generationSecs = 0; private float generationSecs = 0;
public static BlockChain getInstance() { public static Blockchain getInstance() {
try { try {
final BlockChain blockChain = (BlockChain) FileManagement.loadBlockChain(); final Blockchain blockchain = (Blockchain) FileManagement.loadBlockchain();
if (!blockChain.validateBlockchain()) { if (!blockchain.validateBlockchain()) {
System.out.println("Blockchain not valid! Creating new one"); System.out.println("Blockchain not valid! Creating new one");
return new BlockChain(); return new Blockchain();
} else { } else {
return blockChain; return blockchain;
} }
} catch (final ClassNotFoundException | IOException e) { } catch (final ClassNotFoundException | IOException e) {
return new BlockChain(); return new Blockchain();
} }
} }

View File

@@ -3,16 +3,16 @@ package model;
public class Miner implements Runnable { public class Miner implements Runnable {
private final int minerId; private final int minerId;
private final BlockChain blockChain; private final Blockchain blockchain;
public Miner(final int minerId, final BlockChain blockChain) { public Miner(final int minerId, final Blockchain blockchain) {
this.minerId = minerId; this.minerId = minerId;
this.blockChain = blockChain; this.blockchain = blockchain;
} }
@Override @Override
public void run() { public void run() {
blockChain.addBlock(minerId); blockchain.addBlock(minerId);
} }
} }

View File

@@ -23,7 +23,7 @@ public class Transaction implements Serializable {
final PublicKey publicKey final PublicKey publicKey
) throws NoSuchAlgorithmException, InvalidKeyException, SignatureException { ) throws NoSuchAlgorithmException, InvalidKeyException, SignatureException {
this.id = id; this.id = id;
this.text = sender + " sent " + amount + " VC to " + receiver; this.text = sender + " sent " + amount + " KarmaCoins to " + receiver;
this.signature = Security.sign(text, privateKey); this.signature = Security.sign(text, privateKey);
this.publicKey = publicKey; this.publicKey = publicKey;
} }

View File

@@ -11,13 +11,13 @@ import java.io.ObjectOutputStream;
public final class FileManagement { public final class FileManagement {
private static final String BLOCKCHAIN = "blockChain.txt"; private static final String BLOCKCHAIN = "blockchain.txt";
private FileManagement() { private FileManagement() {
throw new IllegalStateException("FileManagement class"); throw new IllegalStateException("FileManagement class");
} }
public static void saveBlockChain(final Object obj) throws IOException { public static void saveBlockchain(final Object obj) throws IOException {
final FileOutputStream fos = new FileOutputStream(BLOCKCHAIN); final FileOutputStream fos = new FileOutputStream(BLOCKCHAIN);
final BufferedOutputStream bos = new BufferedOutputStream(fos); final BufferedOutputStream bos = new BufferedOutputStream(fos);
final ObjectOutputStream oos = new ObjectOutputStream(bos); final ObjectOutputStream oos = new ObjectOutputStream(bos);
@@ -25,7 +25,7 @@ public final class FileManagement {
oos.close(); oos.close();
} }
public static Object loadBlockChain() throws IOException, ClassNotFoundException { public static Object loadBlockchain() throws IOException, ClassNotFoundException {
final FileInputStream fis = new FileInputStream(BLOCKCHAIN); final FileInputStream fis = new FileInputStream(BLOCKCHAIN);
final BufferedInputStream bis = new BufferedInputStream(fis); final BufferedInputStream bis = new BufferedInputStream(fis);
final ObjectInputStream ois = new ObjectInputStream(bis); final ObjectInputStream ois = new ObjectInputStream(bis);

View File

@@ -2,14 +2,7 @@ package util;
import java.util.Arrays; import java.util.Arrays;
import java.util.stream.Stream; import java.util.stream.Stream;
import model.BlockChain; import model.Blockchain;
import exceptions.NotEnoughCoinsException;
import java.io.IOException;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.security.SignatureException;
import java.security.spec.InvalidKeySpecException;
import java.util.Random; import java.util.Random;
public class TransactionsGenerator implements Runnable { public class TransactionsGenerator implements Runnable {
@@ -20,11 +13,11 @@ public class TransactionsGenerator implements Runnable {
"Thom", "Jonny", "Ed", "Colin", "Phil" "Thom", "Jonny", "Ed", "Colin", "Phil"
).toArray(String[]::new); ).toArray(String[]::new);
private final BlockChain blockChain; private final Blockchain blockchain;
public TransactionsGenerator(final BlockChain blockChain) { public TransactionsGenerator(final Blockchain blockchain) {
this.blockChain = blockChain; this.blockchain = blockchain;
if (blockChain.getUsersCoins().isEmpty()) { if (blockchain.getUsersCoins().isEmpty()) {
setInitialUserCoins(); setInitialUserCoins();
} }
} }
@@ -32,20 +25,18 @@ public class TransactionsGenerator implements Runnable {
@Override @Override
public void run() { public void run() {
try { try {
blockChain.acceptTransaction( blockchain.acceptTransaction(
blockChain.getNextTransactionId(), blockchain.getNextTransactionId(),
getRandomUserName(), getRandomUserName(),
getRandomAmount(), getRandomAmount(),
getRandomUserName() getRandomUserName()
); );
} catch (final NoSuchAlgorithmException | /*
SignatureException | Catching throwable instead of exception to avoid ScheduledExecutorService from stop working
InvalidKeyException | because any thrown exception or error reaching the executor causes the executor to halt.
IOException | */
InvalidKeySpecException | } catch (final Throwable t) {
NotEnoughCoinsException e) t.printStackTrace();
{
e.printStackTrace();
} }
} }
@@ -55,13 +46,13 @@ public class TransactionsGenerator implements Runnable {
private int getRandomAmount(){ private int getRandomAmount(){
final int MIN_AMOUNT = 1; final int MIN_AMOUNT = 1;
final int MAX_AMOUNT = 100; final int MAX_AMOUNT = 50;
return random.nextInt((MAX_AMOUNT - MIN_AMOUNT) + 1) + MIN_AMOUNT; return random.nextInt((MAX_AMOUNT - MIN_AMOUNT) + 1) + MIN_AMOUNT;
} }
private void setInitialUserCoins() { private void setInitialUserCoins() {
Arrays.stream(userNames).forEach(userName -> blockChain.acceptUser(userName, INITIAL_COINS)); Arrays.stream(userNames).forEach(userName -> blockchain.acceptUser(userName, INITIAL_COINS));
} }
} }

Binary file not shown.

After

Width:  |  Height:  |  Size: 99 KiB

View File

@@ -13,24 +13,24 @@ import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors; import java.util.stream.Collectors;
import java.util.stream.IntStream; import java.util.stream.IntStream;
import model.BlockChain; import model.Blockchain;
import model.Miner; import model.Miner;
import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Test;
class BlockChainTest { class BlockchainTest {
private final int nThreads = Runtime.getRuntime().availableProcessors(); private final int nThreads = Runtime.getRuntime().availableProcessors();
private List<Miner> miners; private List<Miner> miners;
private BlockChain blockChain; private Blockchain blockchain;
@BeforeEach @BeforeEach
public void setUp() { public void setUp() {
blockChain = new BlockChain(); blockchain = new Blockchain();
blockChain.acceptUser("tester1", 100); blockchain.acceptUser("tester1", 100);
blockChain.acceptUser("tester2", 100); blockchain.acceptUser("tester2", 100);
miners = IntStream.range(0, 5) miners = IntStream.range(0, 5)
.mapToObj(minerId -> new Miner(minerId, blockChain)) .mapToObj(minerId -> new Miner(minerId, blockchain))
.collect(Collectors.toList()); .collect(Collectors.toList());
} }
@@ -41,8 +41,8 @@ class BlockChainTest {
final var minerExecutor = Executors.newFixedThreadPool(nThreads); final var minerExecutor = Executors.newFixedThreadPool(nThreads);
for (final Miner miner: miners){ for (final Miner miner: miners){
blockChain.acceptTransaction(blockChain.getNextTransactionId(), "tester1", 10, "tester2"); blockchain.acceptTransaction(blockchain.getNextTransactionId(), "tester1", 10, "tester2");
blockChain.acceptTransaction(blockChain.getNextTransactionId(), "tester2", 10, "tester1"); blockchain.acceptTransaction(blockchain.getNextTransactionId(), "tester2", 10, "tester1");
minerExecutor.execute(miner); minerExecutor.execute(miner);
} }
@@ -52,14 +52,14 @@ class BlockChainTest {
minerExecutor.shutdownNow(); minerExecutor.shutdownNow();
} }
assertTrue(blockChain.validateBlockchain()); assertTrue(blockchain.validateBlockchain());
} }
@Test @Test
void block_chain_not_enough_coins_test() { void block_chain_not_enough_coins_test() {
final NotEnoughCoinsException thrown = assertThrows( final NotEnoughCoinsException thrown = assertThrows(
NotEnoughCoinsException.class, NotEnoughCoinsException.class,
() -> blockChain.acceptTransaction( () -> blockchain.acceptTransaction(
1, 1,
"tester1", "tester1",
110, 110,
@@ -72,7 +72,7 @@ class BlockChainTest {
void block_chain_not_self_transaction_test() { void block_chain_not_self_transaction_test() {
final NoSelfTransactionException thrown = assertThrows( final NoSelfTransactionException thrown = assertThrows(
NoSelfTransactionException.class, NoSelfTransactionException.class,
() -> blockChain.acceptTransaction( () -> blockchain.acceptTransaction(
1, 1,
"tester1", "tester1",
10, 10,