Added README and solved ScheduledExecutorService halting
This commit is contained in:
63
.gitignore
vendored
63
.gitignore
vendored
@@ -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
13
README.md
Normal 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:
|
||||
|
||||

|
||||
@@ -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);
|
||||
|
||||
}
|
||||
|
||||
|
||||
@@ -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());
|
||||
}
|
||||
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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));
|
||||
}
|
||||
|
||||
}
|
||||
BIN
src/main/resources/img/blockchain.png
Normal file
BIN
src/main/resources/img/blockchain.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 99 KiB |
@@ -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,
|
||||
Reference in New Issue
Block a user