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
/build/reports/tests/test/css/base-style.css
/build/classes/java/main/model/Block.class
/out/production/blockchain_gitea/model/Block.class
/build/classes/java/main/model/BlockChain.class
/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
/.idea/*
/.gradle/*
/build/*
/out/*
/gradle/*
/gradlew
/gradlew.bat
/build/classes/java/main/util/HashFunction.class
/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
/blockchain.txt
/KeyPair/privateKey
/.idea/inspectionProfiles/Project_Default.xml
/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.TimeUnit;
import java.util.stream.IntStream;
import model.BlockChain;
import model.Blockchain;
import util.TransactionsGenerator;
import model.Miner;
import util.FileManagement;
@@ -14,7 +14,7 @@ public final class Main {
public static void main(final String[] args) throws InterruptedException, IOException {
final var nThreads = Runtime.getRuntime().availableProcessors();
final var blockChain = BlockChain.getInstance();
final var blockchain = Blockchain.getInstance();
// Cryptographic keys management
final File publicKey = new File(Security.PUBLIC_KEY);
@@ -27,13 +27,13 @@ public final class Main {
final var transactionsExecutor = Executors.newScheduledThreadPool(nThreads);
// 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);
// Creation of 15 miners
IntStream.range(0, 15)
.mapToObj(minerId -> new Miner(minerId, blockChain))
.mapToObj(minerId -> new Miner(minerId, blockchain))
.forEach(minerExecutor::submit);
minerExecutor.shutdown();
@@ -48,7 +48,7 @@ public final class Main {
transactionsExecutor.shutdownNow();
}
FileManagement.saveBlockChain(blockChain);
FileManagement.saveBlockchain(blockchain);
}

View File

@@ -48,7 +48,7 @@ public class Block implements Serializable {
public String toString() {
return "Block: " + "\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"
+ "Timestamp: " + this.timeStamp + "\n"
+ "Magic number: " + this.magicNumber + "\n"
@@ -61,7 +61,7 @@ public class Block implements Serializable {
public String messagesToString() {
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());
}

View File

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

View File

@@ -3,16 +3,16 @@ package model;
public class Miner implements Runnable {
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.blockChain = blockChain;
this.blockchain = blockchain;
}
@Override
public void run() {
blockChain.addBlock(minerId);
blockchain.addBlock(minerId);
}
}

View File

@@ -23,7 +23,7 @@ public class Transaction implements Serializable {
final PublicKey publicKey
) throws NoSuchAlgorithmException, InvalidKeyException, SignatureException {
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.publicKey = publicKey;
}

View File

@@ -11,13 +11,13 @@ import java.io.ObjectOutputStream;
public final class FileManagement {
private static final String BLOCKCHAIN = "blockChain.txt";
private static final String BLOCKCHAIN = "blockchain.txt";
private FileManagement() {
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 BufferedOutputStream bos = new BufferedOutputStream(fos);
final ObjectOutputStream oos = new ObjectOutputStream(bos);
@@ -25,7 +25,7 @@ public final class FileManagement {
oos.close();
}
public static Object loadBlockChain() throws IOException, ClassNotFoundException {
public static Object loadBlockchain() throws IOException, ClassNotFoundException {
final FileInputStream fis = new FileInputStream(BLOCKCHAIN);
final BufferedInputStream bis = new BufferedInputStream(fis);
final ObjectInputStream ois = new ObjectInputStream(bis);

View File

@@ -2,14 +2,7 @@ package util;
import java.util.Arrays;
import java.util.stream.Stream;
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 model.Blockchain;
import java.util.Random;
public class TransactionsGenerator implements Runnable {
@@ -20,11 +13,11 @@ public class TransactionsGenerator implements Runnable {
"Thom", "Jonny", "Ed", "Colin", "Phil"
).toArray(String[]::new);
private final BlockChain blockChain;
private final Blockchain blockchain;
public TransactionsGenerator(final BlockChain blockChain) {
this.blockChain = blockChain;
if (blockChain.getUsersCoins().isEmpty()) {
public TransactionsGenerator(final Blockchain blockchain) {
this.blockchain = blockchain;
if (blockchain.getUsersCoins().isEmpty()) {
setInitialUserCoins();
}
}
@@ -32,20 +25,18 @@ public class TransactionsGenerator implements Runnable {
@Override
public void run() {
try {
blockChain.acceptTransaction(
blockChain.getNextTransactionId(),
blockchain.acceptTransaction(
blockchain.getNextTransactionId(),
getRandomUserName(),
getRandomAmount(),
getRandomUserName()
);
} catch (final NoSuchAlgorithmException |
SignatureException |
InvalidKeyException |
IOException |
InvalidKeySpecException |
NotEnoughCoinsException e)
{
e.printStackTrace();
/*
Catching throwable instead of exception to avoid ScheduledExecutorService from stop working
because any thrown exception or error reaching the executor causes the executor to halt.
*/
} catch (final Throwable t) {
t.printStackTrace();
}
}
@@ -55,13 +46,13 @@ public class TransactionsGenerator implements Runnable {
private int getRandomAmount(){
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;
}
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.stream.Collectors;
import java.util.stream.IntStream;
import model.BlockChain;
import model.Blockchain;
import model.Miner;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
class BlockChainTest {
class BlockchainTest {
private final int nThreads = Runtime.getRuntime().availableProcessors();
private List<Miner> miners;
private BlockChain blockChain;
private Blockchain blockchain;
@BeforeEach
public void setUp() {
blockChain = new BlockChain();
blockChain.acceptUser("tester1", 100);
blockChain.acceptUser("tester2", 100);
blockchain = new Blockchain();
blockchain.acceptUser("tester1", 100);
blockchain.acceptUser("tester2", 100);
miners = IntStream.range(0, 5)
.mapToObj(minerId -> new Miner(minerId, blockChain))
.mapToObj(minerId -> new Miner(minerId, blockchain))
.collect(Collectors.toList());
}
@@ -41,8 +41,8 @@ class BlockChainTest {
final var minerExecutor = Executors.newFixedThreadPool(nThreads);
for (final Miner miner: miners){
blockChain.acceptTransaction(blockChain.getNextTransactionId(), "tester1", 10, "tester2");
blockChain.acceptTransaction(blockChain.getNextTransactionId(), "tester2", 10, "tester1");
blockchain.acceptTransaction(blockchain.getNextTransactionId(), "tester1", 10, "tester2");
blockchain.acceptTransaction(blockchain.getNextTransactionId(), "tester2", 10, "tester1");
minerExecutor.execute(miner);
}
@@ -52,14 +52,14 @@ class BlockChainTest {
minerExecutor.shutdownNow();
}
assertTrue(blockChain.validateBlockchain());
assertTrue(blockchain.validateBlockchain());
}
@Test
void block_chain_not_enough_coins_test() {
final NotEnoughCoinsException thrown = assertThrows(
NotEnoughCoinsException.class,
() -> blockChain.acceptTransaction(
() -> blockchain.acceptTransaction(
1,
"tester1",
110,
@@ -72,7 +72,7 @@ class BlockChainTest {
void block_chain_not_self_transaction_test() {
final NoSelfTransactionException thrown = assertThrows(
NoSelfTransactionException.class,
() -> blockChain.acceptTransaction(
() -> blockchain.acceptTransaction(
1,
"tester1",
10,