diff --git a/.classpath b/.classpath
index 1114cb5..3138ed5 100644
--- a/.classpath
+++ b/.classpath
@@ -29,5 +29,6 @@
+
diff --git a/README.md b/README.md
index 1724b08..ddd5df4 100644
--- a/README.md
+++ b/README.md
@@ -1,8 +1,11 @@
# DiversTestsJava
Quelques tests de programmation en Java, des notes diverses,
- * JDBC local (avec un peu de MySQL)
- * OAuth
- * Challenge CybserSec
- * Java Swing : Full Screen, SpalshScreen (à revoir / préciser)
- * Java reflect / réflexivité / introspection
+ * JDBC local (avec un peu de MySQL)
+ * OAuth
+ * Socket implementation
+ * HTTP use
+ * Challenge CybserSec (quelques notes)
+ * Java Swing : Full Screen, SpalshScreen (à revoir / préciser)
+ * Java reflect / réflexivité / introspection
+ * "blockchain"
diff --git a/pom.xml b/pom.xml
index ab7560a..9d6655d 100644
--- a/pom.xml
+++ b/pom.xml
@@ -48,5 +48,21 @@
utilities
0.0.2-SNAPSHOT
+
+
+
+
+ com.google.code.gson
+ gson
+ 2.8.8
+
+
+
+ org.bouncycastle
+ bcprov-jdk15to18
+ 1.69
+
+
+
\ No newline at end of file
diff --git a/src/main/java/NoobChainTests.java b/src/main/java/NoobChainTests.java
new file mode 100644
index 0000000..3139ee4
--- /dev/null
+++ b/src/main/java/NoobChainTests.java
@@ -0,0 +1,127 @@
+import static org.junit.jupiter.api.Assertions.*;
+
+import java.security.Security;
+import java.util.ArrayList;
+import java.util.List;
+
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.Test;
+
+import com.google.gson.GsonBuilder;
+
+import gabywald.crypto.blockchain.Block;
+import gabywald.crypto.blockchain.BlockChain;
+import gabywald.crypto.blockchain.StringUtils;
+import gabywald.crypto.blockchain.Transaction;
+import gabywald.crypto.blockchain.Wallet;
+
+/**
+ * Tests about BlockChain / NoobChain.
+ * @author Gabriel Chandesris (2021)
+ */
+class NoobChainTests {
+
+ /**
+ * Test : block.
+ */
+ @Test
+ void testPart01() {
+ Block genesisBlock = new Block("Hi im the first block", "0");
+ Assertions.assertNotNull( genesisBlock );
+ System.out.println("Hash for block 1 : " + genesisBlock.getHash() );
+ Assertions.assertNotNull( genesisBlock.getHash() );
+
+ Block secondBlock = new Block("Yo im the second block", genesisBlock.getHash() );
+ Assertions.assertNotNull( secondBlock );
+ System.out.println("Hash for block 2 : " + secondBlock.getHash() );
+ Assertions.assertNotNull( secondBlock.getHash() );
+
+ Block thirdBlock = new Block("Hey im the third block", secondBlock.getHash() );
+ Assertions.assertNotNull( thirdBlock );
+ System.out.println("Hash for block 3 : " + thirdBlock.getHash() );
+ Assertions.assertNotNull( thirdBlock.getHash() );
+ }
+
+ /**
+ * Test : blockchain
+ */
+ @Test
+ void testPart02() {
+ List blockchain = new ArrayList();
+ // Add our blocks to the blockchain List:
+ blockchain.add(new Block("Hi im the first block", "0"));
+ blockchain.add(new Block("Yo im the second block", blockchain.get(blockchain.size()-1).getHash() ));
+ blockchain.add(new Block("Hey im the third block", blockchain.get(blockchain.size()-1).getHash() ));
+ Assertions.assertEquals(3, blockchain.size());
+ String blockchainJson = new GsonBuilder().setPrettyPrinting().create().toJson(blockchain);
+ Assertions.assertNotNull( blockchainJson );
+ System.out.println(blockchainJson);
+ }
+
+ @Test
+ void testPart03() {
+ List blockchain = new ArrayList();
+ int difficulty = 3;
+ // Add our blocks to the blockchain List:
+ blockchain.add(new Block("Hi im the first block", "0"));
+ System.out.println("Trying to Mine block 1... ");
+ blockchain.get(0).mineBlock(difficulty);
+
+ blockchain.add(new Block("Yo im the second block", blockchain.get(blockchain.size()-1).getHash() ));
+ System.out.println("Trying to Mine block 2... ");
+ blockchain.get(1).mineBlock(difficulty);
+
+ blockchain.add(new Block("Hey im the third block", blockchain.get(blockchain.size()-1).getHash() ));
+ System.out.println("Trying to Mine block 3... ");
+ blockchain.get(2).mineBlock(difficulty);
+
+ Assertions.assertEquals(3, blockchain.size());
+
+ System.out.println("\nBlockchain is Valid: " + BlockChain.isChainValidV1( blockchain ));
+
+ Assertions.assertTrue( BlockChain.isChainValidV1( blockchain ) );
+
+ String blockchainJson = new GsonBuilder().setPrettyPrinting().create().toJson(blockchain);
+
+ Assertions.assertNotNull( blockchainJson );
+
+ System.out.println("\nThe block chain: ");
+ System.out.println(blockchainJson);
+ }
+
+ @Test
+ void testPart04() {
+ // Setup Bouncey castle as a Security Provider
+ Security.addProvider(new org.bouncycastle.jce.provider.BouncyCastleProvider());
+ // Security.addProvider(new sun.security.provider.Sun());
+ // XXX NOTE see https://docs.oracle.com/javase/8/docs/technotes/guides/security/SunProviders.html
+ // Create the new wallets
+ Wallet walletA = new Wallet();
+ Wallet walletB = new Wallet();
+ Assertions.assertNotNull( walletA );
+ Assertions.assertNotNull( walletB );
+ // Test public and private keys
+ System.out.println("Private and public keys:");
+ System.out.println(StringUtils.getStringFromKey(walletA.privateKey));
+ System.out.println(StringUtils.getStringFromKey(walletA.publicKey));
+ Assertions.assertNotNull( walletA.privateKey );
+ Assertions.assertNotNull( walletA.publicKey );
+ Assertions.assertNotNull( walletB.privateKey );
+ Assertions.assertNotNull( walletB.publicKey );
+ //Create a test transaction from WalletA to walletB
+ Transaction transaction = new Transaction(walletA.publicKey, walletB.publicKey, 5, null);
+ Assertions.assertNotNull( transaction );
+ transaction.generateSignature(walletA.privateKey);
+ Assertions.assertNotNull( walletA.privateKey );
+ //Verify the signature works and verify it from the public key
+ System.out.println("Is signature verified");
+ System.out.println(transaction.verifySignature());
+ Assertions.assertTrue( transaction.verifySignature() );
+ }
+
+ @Test
+ void testPart05() {
+ fail("Not yet implemented");
+ }
+
+}
diff --git a/src/main/java/gabywald/crypto/blockchain/Block.java b/src/main/java/gabywald/crypto/blockchain/Block.java
new file mode 100644
index 0000000..71dee6e
--- /dev/null
+++ b/src/main/java/gabywald/crypto/blockchain/Block.java
@@ -0,0 +1,123 @@
+package gabywald.crypto.blockchain;
+
+import java.util.ArrayList;
+import java.util.Date;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * Block of BlockChain.
+ *
https://medium.com/programmers-blockchain/create-simple-blockchain-java-tutorial-from-scratch-6eeed3cb03fa
+ *
https://github.com/CryptoKass/NoobChain-Tutorial-Part-1
+ * @author Gabriel Chandesris (2021)
+ */
+public class Block {
+
+ private String hash;
+ private String previousHash;
+ /** Our data will be a simple message. */
+ // private String data;
+ /** As number of milliseconds since 1/1/1970. */
+ private long timeStamp;
+ private int nonce;
+
+ private String merkleRoot;
+
+ private List transactions = new ArrayList();
+
+ /**
+ * Block Constructor.
+ * @param data
+ * @param previousHash
+ */
+ public Block(String data, String previousHash ) {
+ // this.data = data;
+ this.previousHash = previousHash;
+ this.timeStamp = new Date().getTime();
+ // Making sure we do this after we set the other values.
+ this.hash = this.calculateHash();
+ }
+
+ /**
+ * Block Constructor.
+ * @param previousHash
+ */
+ public Block(String previousHash ) {
+ this(null, previousHash);
+ }
+
+ /**
+ * Calculate new hash based on blocks contents
+ * @return (null if exception apply internally).
+ * @see StringUtils#applySha256(String)
+ */
+ public String calculateHash() {
+ String calculatedhash = null;
+ try {
+ calculatedhash = StringUtils.applySha256(
+ previousHash +
+ Long.toString(timeStamp) +
+ Integer.toString(nonce) +
+ merkleRoot
+ );
+ } catch (BlockchainException e) {
+ // e.printStackTrace();
+ System.out.println( e.getMessage() );
+ calculatedhash = null;
+ }
+ return calculatedhash;
+ }
+
+ /**
+ * Mining block
+ * @param difficulty
+ */
+ public void mineBlock(int difficulty) {
+ this.merkleRoot = StringUtils.getMerkleRoot(this.transactions);
+ // Create a string with difficulty * "0"
+ String target = StringUtils.getDifficultyString(difficulty);
+ while ( (this.hash == null) || ( ! this.hash.substring( 0, difficulty ).equals(target)) ) {
+ this.nonce++;
+ this.hash = this.calculateHash();
+ // XXX NOTE if exception inside calculateHash : hash will be null !
+ }
+ System.out.println("Block Mined!!! : " + this.hash);
+ }
+
+ /**
+ * Add transactions to this block.
+ * @param transaction
+ * @param UTXOs
+ * @param minimumTransaction
+ * @return (boolean)
+ */
+ public boolean addTransaction( final Transaction transaction,
+ final Map UTXOs,
+ final float minimumTransaction) {
+ // Process transaction and check if valid, unless block is genesis block then ignore.
+ if (transaction == null) { return false; }
+ if (this.previousHash != "0") {
+ if (transaction.processTransaction(UTXOs, minimumTransaction) != true) {
+ System.out.println("Transaction failed to process. Discarded.");
+ return false;
+ }
+ }
+ this.transactions.add(transaction);
+ System.out.println("Transaction Successfully added to Block");
+ return true;
+ }
+
+ public String getHash()
+ { return this.hash; }
+
+ public String getPreviousHash()
+ { return this.previousHash; }
+
+ public String getMerkleRoot()
+ { return this.merkleRoot; }
+
+ public List getTransactions()
+ { return this.transactions; }
+
+
+}
diff --git a/src/main/java/gabywald/crypto/blockchain/BlockChain.java b/src/main/java/gabywald/crypto/blockchain/BlockChain.java
new file mode 100644
index 0000000..0166905
--- /dev/null
+++ b/src/main/java/gabywald/crypto/blockchain/BlockChain.java
@@ -0,0 +1,144 @@
+package gabywald.crypto.blockchain;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+
+/**
+ *
+ * @author Gabriel Chandesris (2021)
+ */
+public abstract class BlockChain {
+
+ /**
+ * Build a list of Blocks.
+ * @return
+ */
+ public static List build() {
+ return new ArrayList();
+ }
+
+ /**
+ *
+ * @param blockchain
+ * @return (boolean)
+ */
+ public static boolean isChainValidV1(List blockchain) {
+ Block currentBlock = null;
+ Block previousBlock = null;
+
+ // Loop through blockchain to check hashes:
+ for (int i = 1 ; i < blockchain.size() ; i++) {
+ currentBlock = blockchain.get(i);
+ previousBlock = blockchain.get(i-1);
+ // Compare registered hash and calculated hash:
+ if ( ! currentBlock.getHash().equals(currentBlock.calculateHash()) ) {
+ System.out.println("Current Hashes not equal");
+ return false;
+ }
+ // Compare previous hash and registered previous hash
+ if ( ! previousBlock.getHash().equals(currentBlock.getPreviousHash() ) ) {
+ System.out.println("Previous Hashes not equal");
+ return false;
+ }
+ }
+ return true;
+ }
+
+ /**
+ *
+ * @param blockchain
+ * @param genesisTransaction
+ * @param difficulty
+ * @return (boolean)
+ */
+ public static boolean isChainValidV2(List blockchain, Transaction genesisTransaction, int difficulty) {
+ Block currentBlock = null;
+ Block previousBlock = null;
+ String hashTarget = StringUtils.getDifficultyString(difficulty);
+
+ // A temporary working list of unspent transactions at a given block state.
+ HashMap tempUTXOs = new HashMap();
+ tempUTXOs.put(genesisTransaction.getOutputs().get(0).getId(), genesisTransaction.getOutputs().get(0));
+
+ // Loop through blockchain to check hashes:
+ for (int i = 1 ; i < blockchain.size() ; i++) {
+
+ currentBlock = blockchain.get(i);
+ previousBlock = blockchain.get(i-1);
+
+ // Compare registered hash and calculated hash:
+ if ( ! currentBlock.getHash().equals(currentBlock.calculateHash()) ) {
+ System.out.println("#Current Hashes not equal");
+ return false;
+ }
+
+ // Compare previous hash and registered previous hash
+ if ( ! previousBlock.getHash().equals(currentBlock.getPreviousHash() ) ) {
+ System.out.println("#Previous Hashes not equal");
+ return false;
+ }
+
+ // Check if hash is solved
+ if ( ! currentBlock.getHash().substring( 0, difficulty).equals(hashTarget)) {
+ System.out.println("#This block hasn't been mined");
+ return false;
+ }
+
+ // Loop thru blockchains transactions:
+ TransactionOutput tempOutput;
+ for (int t = 0 ; t < currentBlock.getTransactions().size() ; t++) {
+ Transaction currentTransaction = currentBlock.getTransactions().get(t);
+
+ if ( ! currentTransaction.verifySignature()) {
+ System.out.println("#Signature on Transaction(" + t + ") is Invalid");
+ return false;
+ }
+
+ if (currentTransaction.getInputsValue() != currentTransaction.getOutputsValue()) {
+ System.out.println("#Inputs are note equal to outputs on Transaction(" + t + ")");
+ return false;
+ }
+
+ for (TransactionInput input: currentTransaction.getInputs()) {
+ tempOutput = tempUTXOs.get(input.getTransactionOutputId() );
+
+ if (tempOutput == null) {
+ System.out.println("#Referenced input on Transaction(" + t + ") is Missing");
+ return false;
+ }
+
+ if (input.getTransactionOutput().getValue() != tempOutput.getValue()) {
+ System.out.println("#Referenced input Transaction(" + t + ") value is Invalid");
+ return false;
+ }
+
+ tempUTXOs.remove(input.getTransactionOutputId() );
+ }
+
+ for (TransactionOutput output: currentTransaction.getOutputs()) {
+ tempUTXOs.put(output.getId(), output);
+ }
+
+ if (currentTransaction.getOutputs().get(0).getRecipient() != currentTransaction.getRecipient()) {
+ System.out.println("#Transaction(" + t + ") output reciepient is not who it should be");
+ return false;
+ }
+
+ if (currentTransaction.getOutputs().get(1).getRecipient() != currentTransaction.getSender()) {
+ System.out.println("#Transaction(" + t + ") output 'change' is not sender.");
+ return false;
+ }
+
+ }
+
+ }
+ System.out.println("Blockchain is valid");
+ return true;
+ }
+
+ public static boolean addBlock(final List blockchain, final Block newBlock, final int difficulty) {
+ newBlock.mineBlock(difficulty);
+ return blockchain.add(newBlock);
+ }
+}
diff --git a/src/main/java/gabywald/crypto/blockchain/BlockchainException.java b/src/main/java/gabywald/crypto/blockchain/BlockchainException.java
new file mode 100644
index 0000000..52efef1
--- /dev/null
+++ b/src/main/java/gabywald/crypto/blockchain/BlockchainException.java
@@ -0,0 +1,14 @@
+package gabywald.crypto.blockchain;
+
+/**
+ *
+ * @author Gabriel Chandesris (2021).
+ */
+@SuppressWarnings("serial")
+public class BlockchainException extends Exception {
+
+ public BlockchainException(String msg) {
+ super(msg);
+ }
+
+}
diff --git a/src/main/java/gabywald/crypto/blockchain/NoobChainFinale.java b/src/main/java/gabywald/crypto/blockchain/NoobChainFinale.java
new file mode 100644
index 0000000..2502fd3
--- /dev/null
+++ b/src/main/java/gabywald/crypto/blockchain/NoobChainFinale.java
@@ -0,0 +1,114 @@
+package gabywald.crypto.blockchain;
+
+import java.security.Security;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * NoobChain Test and Main Class of BlockChain.
+ *
https://medium.com/programmers-blockchain/creating-your-first-blockchain-with-java-part-2-transactions-2cdac335e0ce
+ *
https://github.com/CryptoKass/NoobChain-Tutorial-Part-2
+ * @author Gabriel Chandesris (2021)
+ */
+public class NoobChainFinale {
+
+ private static List blockchain = new ArrayList();
+ private static Map mapUTXOs = new HashMap();
+
+ private static int difficulty = 3;
+ private static float minimumTransaction = 0.1f;
+ private static Wallet walletA;
+ private static Wallet walletB;
+ private static Transaction genesisTransaction;
+
+ public static void main(String[] args) {
+ // Add our blocks to the blockchain List:
+
+ // Setup Bouncey castle as a Security Provider
+ Security.addProvider(new org.bouncycastle.jce.provider.BouncyCastleProvider());
+
+ // Create wallets:
+ NoobChainFinale.walletA = new Wallet();
+ NoobChainFinale.walletB = new Wallet();
+ Wallet coinbase = new Wallet();
+
+ // Create genesis transaction, which sends 100 NoobCoin to walletA:
+ NoobChainFinale.genesisTransaction = new Transaction(coinbase.publicKey, NoobChainFinale.walletA.publicKey, 100f, null);
+ // Manually sign the genesis transaction
+ NoobChainFinale.genesisTransaction.generateSignature(coinbase.privateKey);
+ // Manually set the transaction id
+ NoobChainFinale.genesisTransaction.setTransactionId( "0" );
+ // Manually add the Transactions Output
+ NoobChainFinale.genesisTransaction.getOutputs().add(new TransactionOutput( NoobChainFinale.genesisTransaction.getRecipient(),
+ NoobChainFinale.genesisTransaction.getValue(),
+ NoobChainFinale.genesisTransaction.getTransactionId()));
+
+ // Its important to store our first transaction in the UTXOs list.
+ NoobChainFinale.mapUTXOs.put( NoobChainFinale.genesisTransaction.getOutputs().get(0).getId(),
+ NoobChainFinale.genesisTransaction.getOutputs().get(0));
+
+ System.out.println("Creating and Mining Genesis block... ");
+ Block genesis = new Block("0");
+ genesis.addTransaction(NoobChainFinale.genesisTransaction, NoobChainFinale.mapUTXOs, NoobChainFinale.minimumTransaction);
+ BlockChain.addBlock(NoobChainFinale.blockchain, genesis, NoobChainFinale.difficulty);
+
+ // Testing
+ Block block1 = new Block(genesis.getHash() );
+ System.out.println("\nWalletA's balance is: " + NoobChainFinale.walletA.getBalance( NoobChainFinale.mapUTXOs ));
+ System.out.println("\nWalletA is Attempting to send funds (40) to WalletB...");
+ block1.addTransaction( NoobChainFinale.walletA.sendFunds(NoobChainFinale.walletB.publicKey, 40f, NoobChainFinale.mapUTXOs),
+ NoobChainFinale.mapUTXOs, NoobChainFinale.minimumTransaction);
+ BlockChain.addBlock(NoobChainFinale.blockchain, block1, NoobChainFinale.difficulty);
+ System.out.println("\nWalletA's balance is: " + NoobChainFinale.walletA.getBalance( NoobChainFinale.mapUTXOs ));
+ System.out.println("WalletB's balance is: " + NoobChainFinale.walletB.getBalance( NoobChainFinale.mapUTXOs ));
+
+ Block block2 = new Block(block1.getHash() );
+ System.out.println("\nWalletA Attempting to send more funds (1000) than it has...");
+ block2.addTransaction( NoobChainFinale.walletA.sendFunds(NoobChainFinale.walletB.publicKey, 1000f, NoobChainFinale.mapUTXOs),
+ NoobChainFinale.mapUTXOs, NoobChainFinale.minimumTransaction);
+ BlockChain.addBlock(NoobChainFinale.blockchain, block2, NoobChainFinale.difficulty);
+ System.out.println("\nWalletA's balance is: " + NoobChainFinale.walletA.getBalance( NoobChainFinale.mapUTXOs ));
+ System.out.println("WalletB's balance is: " + NoobChainFinale.walletB.getBalance( NoobChainFinale.mapUTXOs ));
+
+ Block block3 = new Block(block2.getHash() );
+ System.out.println("\nWalletB is Attempting to send funds (20) to WalletA...");
+ block3.addTransaction( NoobChainFinale.walletB.sendFunds( NoobChainFinale.walletA.publicKey, 20, NoobChainFinale.mapUTXOs),
+ NoobChainFinale.mapUTXOs, NoobChainFinale.minimumTransaction);
+ System.out.println("\nWalletA's balance is: " + NoobChainFinale.walletA.getBalance( NoobChainFinale.mapUTXOs ));
+ System.out.println("WalletB's balance is: " + NoobChainFinale.walletB.getBalance( NoobChainFinale.mapUTXOs ));
+
+ BlockChain.isChainValidV2( NoobChainFinale.blockchain, NoobChainFinale.genesisTransaction, NoobChainFinale.difficulty );
+ }
+
+ /*
+Creating and Mining Genesis block...
+Transaction Successfully added to Block
+Block Mined!!! : 0000141c6c378745ae6ae56f9defa305d6bb2783e0dbba55c74764a0a8c78baa
+
+WalletA's balance is: 100.0
+
+WalletA is Attempting to send funds (40) to WalletB...
+Transaction Successfully added to Block
+Block Mined!!! : 000483240ccb2fc341fb704805ab0fe1b26b3e01d2eb2cc530ada22688dc4336
+
+WalletA's balance is: 60.0
+WalletB's balance is: 40.0
+
+WalletA Attempting to send more funds (1000) than it has...
+#Not Enough funds to send transaction. Transaction Discarded.
+Block Mined!!! : 000099416026dec23bf3f9c9d3fc368c518b3bd09e02387f71c2a654885de07b
+
+WalletA's balance is: 60.0
+WalletB's balance is: 40.0
+
+WalletB is Attempting to send funds (20) to WalletA...
+Transaction Successfully added to Block
+
+WalletA's balance is: 20.0
+WalletB's balance is: 80.0
+Blockchain is valid
+ */
+
+}
diff --git a/src/main/java/gabywald/crypto/blockchain/NoobChainIntegration.java b/src/main/java/gabywald/crypto/blockchain/NoobChainIntegration.java
new file mode 100644
index 0000000..8a7709f
--- /dev/null
+++ b/src/main/java/gabywald/crypto/blockchain/NoobChainIntegration.java
@@ -0,0 +1,152 @@
+package gabywald.crypto.blockchain;
+
+import java.security.Security;
+import java.util.List;
+import java.util.ArrayList;
+
+// TODO replace GsonBuilder !! (build json from objet !)
+import com.google.gson.GsonBuilder;
+
+/**
+ * NoobChain Test and Main Class of BlockChain.
+ *
https://medium.com/programmers-blockchain/create-simple-blockchain-java-tutorial-from-scratch-6eeed3cb03fa
+ *
https://github.com/CryptoKass/NoobChain-Tutorial-Part-1
+ *
https://medium.com/programmers-blockchain/creating-your-first-blockchain-with-java-part-2-transactions-2cdac335e0ce
+ *
https://github.com/CryptoKass/NoobChain-Tutorial-Part-2
+ * @author Gabriel Chandesris (2021)
+ */
+public class NoobChainIntegration {
+
+ private static List blockchain = new ArrayList();
+ private static List blockchain2 = new ArrayList();
+
+ private static int difficulty = 3;
+
+ private static Wallet walletA;
+ private static Wallet walletB;
+
+ public static void main(String[] args) {
+
+ // ***** Part I
+ Block genesisBlock = new Block("Hi im the first block", "0");
+ System.out.println("Hash for block 1 : " + genesisBlock.getHash());
+ Block secondBlock = new Block("Yo im the second block", genesisBlock.getHash());
+ System.out.println("Hash for block 2 : " + secondBlock.getHash());
+ Block thirdBlock = new Block("Hey im the third block", secondBlock.getHash());
+ System.out.println("Hash for block 3 : " + thirdBlock.getHash());
+
+
+ // ***** Part II
+ // Add our blocks to the blockchain List:
+ NoobChainIntegration.blockchain.add(new Block("Hi im the first block", "0"));
+ NoobChainIntegration.blockchain.add(new Block("Yo im the second block", NoobChainIntegration.blockchain.get(NoobChainIntegration.blockchain.size()-1).getHash() ));
+ NoobChainIntegration.blockchain.add(new Block("Hey im the third block", NoobChainIntegration.blockchain.get(NoobChainIntegration.blockchain.size()-1).getHash() ));
+ String blockchainJson = new GsonBuilder().setPrettyPrinting().create().toJson(NoobChainIntegration.blockchain);
+ System.out.println(blockchainJson);
+
+ // ***** Part III
+ // Add our blocks to the blockchain List:
+ NoobChainIntegration.blockchain2.add(new Block("Hi im the first block", "0"));
+ System.out.println("Trying to Mine block 1... ");
+ NoobChainIntegration.blockchain2.get(0).mineBlock(NoobChainIntegration.difficulty);
+
+ NoobChainIntegration.blockchain2.add(new Block("Yo im the second block", NoobChainIntegration.blockchain2.get(NoobChainIntegration.blockchain2.size()-1).getHash() ));
+ System.out.println("Trying to Mine block 2... ");
+ NoobChainIntegration.blockchain2.get(1).mineBlock(NoobChainIntegration.difficulty);
+
+ NoobChainIntegration.blockchain2.add(new Block("Hey im the third block", NoobChainIntegration.blockchain2.get(NoobChainIntegration.blockchain2.size()-1).getHash() ));
+ System.out.println("Trying to Mine block 3... ");
+ NoobChainIntegration.blockchain2.get(2).mineBlock(NoobChainIntegration.difficulty);
+
+ System.out.println("\nBlockchain is Valid: " + BlockChain.isChainValidV1( NoobChainIntegration.blockchain2 ));
+
+ String blockchain2Json = new GsonBuilder().setPrettyPrinting().create().toJson(NoobChainIntegration.blockchain2);
+ System.out.println("\nThe block chain: ");
+ System.out.println(blockchain2Json);
+
+ // ***** Part IV
+ // Setup Bouncey castle as a Security Provider
+ Security.addProvider(new org.bouncycastle.jce.provider.BouncyCastleProvider());
+ // Security.addProvider(new sun.security.provider.Sun());
+ // XXX NOTE see https://docs.oracle.com/javase/8/docs/technotes/guides/security/SunProviders.html
+ // Create the new wallets
+ NoobChainIntegration.walletA = new Wallet();
+ NoobChainIntegration.walletB = new Wallet();
+ // Test public and private keys
+ System.out.println("Private and public keys:");
+ System.out.println(StringUtils.getStringFromKey(NoobChainIntegration.walletA.privateKey));
+ System.out.println(StringUtils.getStringFromKey(NoobChainIntegration.walletA.publicKey));
+ // Create a test transaction from WalletA to walletB
+ Transaction transaction = new Transaction(NoobChainIntegration.walletA.publicKey, NoobChainIntegration.walletB.publicKey, 5, null);
+ transaction.generateSignature(NoobChainIntegration.walletA.privateKey);
+ // Verify the signature works and verify it from the public key
+ System.out.println("Is signature verified");
+ System.out.println(transaction.verifySignature());
+
+ // ***** Part V
+ // XXX NOTE see NoobChainFinale and NoobChainTests !!
+ }
+
+ /*
+Hash for block 1 : 9f98b14417f65bf7f55053d6ee948de264beaf4f4effdf1be1b80adb939a56bb
+Hash for block 2 : e7571024da930e1fee1dad719e0b8eeeefdf80c70a3a3739d83d9de1e23eced8
+Hash for block 3 : 7f18457511c26228726b46cb99248113973cb695df6918d78b1402c77435d003
+[
+ {
+ "hash": "b9e3632117413cb710d39c3a449747b6beeeb2b524f26c05882c3bd6af9d74e5",
+ "previousHash": "0",
+ "data": "Hi im the first block",
+ "timeStamp": 1633963207796
+ },
+ {
+ "hash": "7e27f719f013fec598427375fe7b880a5630ceaa901ee03348c17987cb4d392b",
+ "previousHash": "b9e3632117413cb710d39c3a449747b6beeeb2b524f26c05882c3bd6af9d74e5",
+ "data": "Yo im the second block",
+ "timeStamp": 1633963207796
+ },
+ {
+ "hash": "e12c29de9bbb838e5851d5d8ce59cac2e6225192c8e6b3afde054803e5483ee4",
+ "previousHash": "7e27f719f013fec598427375fe7b880a5630ceaa901ee03348c17987cb4d392b",
+ "data": "Hey im the third block",
+ "timeStamp": 1633963207796
+ }
+]
+ */
+
+ /*
+Trying to Mine block 1...
+Block Mined!!! : 00000206cc43b6e9b1e4d81cc5e2959296c0e1b90db2b0e509ef7fc3ec04095d
+Trying to Mine block 2...
+Block Mined!!! : 0000091a52ce1847ea50156a6cb4a5fd479fa0575f39a95011968c0b682cb5d9
+Trying to Mine block 3...
+Block Mined!!! : 0000043ba1e8397d791066bd0b9122339e03135abb451ccbf8b0efc37dc3a109
+
+Blockchain is Valid: true
+
+The block chain:
+[
+ {
+ "hash": "00000206cc43b6e9b1e4d81cc5e2959296c0e1b90db2b0e509ef7fc3ec04095d",
+ "previousHash": "0",
+ "data": "Hi im the first block",
+ "timeStamp": 1633963490480,
+ "nonce": 285549
+ },
+ {
+ "hash": "0000091a52ce1847ea50156a6cb4a5fd479fa0575f39a95011968c0b682cb5d9",
+ "previousHash": "00000206cc43b6e9b1e4d81cc5e2959296c0e1b90db2b0e509ef7fc3ec04095d",
+ "data": "Yo im the second block",
+ "timeStamp": 1633963491272,
+ "nonce": 3329949
+ },
+ {
+ "hash": "0000043ba1e8397d791066bd0b9122339e03135abb451ccbf8b0efc37dc3a109",
+ "previousHash": "0000091a52ce1847ea50156a6cb4a5fd479fa0575f39a95011968c0b682cb5d9",
+ "data": "Hey im the third block",
+ "timeStamp": 1633963497804,
+ "nonce": 3752258
+ }
+]
+ */
+}
+
diff --git a/src/main/java/gabywald/crypto/blockchain/NoobChainTests.java b/src/main/java/gabywald/crypto/blockchain/NoobChainTests.java
new file mode 100644
index 0000000..2281eb9
--- /dev/null
+++ b/src/main/java/gabywald/crypto/blockchain/NoobChainTests.java
@@ -0,0 +1,204 @@
+package gabywald.crypto.blockchain;
+
+import java.security.Security;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.Test;
+
+import com.google.gson.GsonBuilder;
+
+/**
+ * Tests about BlockChain / NoobChain.
+ * @author Gabriel Chandesris (2021)
+ */
+class NoobChainTests {
+
+ /**
+ * Test : block.
+ */
+ @Test
+ void testPart01() {
+ Block genesisBlock = new Block("Hi im the first block", "0");
+ Assertions.assertNotNull( genesisBlock );
+ System.out.println("Hash for block 1 : " + genesisBlock.getHash() );
+ Assertions.assertNotNull( genesisBlock.getHash() );
+
+ Block secondBlock = new Block("Yo im the second block", genesisBlock.getHash() );
+ Assertions.assertNotNull( secondBlock );
+ System.out.println("Hash for block 2 : " + secondBlock.getHash() );
+ Assertions.assertNotNull( secondBlock.getHash() );
+
+ Block thirdBlock = new Block("Hey im the third block", secondBlock.getHash() );
+ Assertions.assertNotNull( thirdBlock );
+ System.out.println("Hash for block 3 : " + thirdBlock.getHash() );
+ Assertions.assertNotNull( thirdBlock.getHash() );
+ }
+
+ /**
+ * Test : blockchain
+ */
+ @Test
+ void testPart02() {
+ List blockchain = BlockChain.build();
+ // Add our blocks to the blockchain List:
+ blockchain.add(new Block("Hi im the first block", "0"));
+ blockchain.add(new Block("Yo im the second block", blockchain.get(blockchain.size()-1).getHash() ));
+ blockchain.add(new Block("Hey im the third block", blockchain.get(blockchain.size()-1).getHash() ));
+ Assertions.assertEquals(3, blockchain.size());
+ String blockchainJson = new GsonBuilder().setPrettyPrinting().create().toJson(blockchain);
+ Assertions.assertNotNull( blockchainJson );
+ System.out.println(blockchainJson);
+ }
+
+ @Test
+ void testPart03() {
+ List blockchain = BlockChain.build();
+ int difficulty = 3;
+ // Add our blocks to the blockchain List:
+ blockchain.add(new Block("Hi im the first block", "0"));
+ System.out.println("Trying to Mine block 1... ");
+ blockchain.get(0).mineBlock(difficulty);
+
+ blockchain.add(new Block("Yo im the second block", blockchain.get(blockchain.size()-1).getHash() ));
+ System.out.println("Trying to Mine block 2... ");
+ blockchain.get(1).mineBlock(difficulty);
+
+ blockchain.add(new Block("Hey im the third block", blockchain.get(blockchain.size()-1).getHash() ));
+ System.out.println("Trying to Mine block 3... ");
+ blockchain.get(2).mineBlock(difficulty);
+
+ Assertions.assertEquals(3, blockchain.size());
+
+ System.out.println("\nBlockchain is Valid: " + BlockChain.isChainValidV1( blockchain ));
+
+ Assertions.assertTrue( BlockChain.isChainValidV1( blockchain ) );
+
+ String blockchainJson = new GsonBuilder().setPrettyPrinting().create().toJson(blockchain);
+
+ Assertions.assertNotNull( blockchainJson );
+
+ System.out.println("\nThe block chain: ");
+ System.out.println(blockchainJson);
+ }
+
+ @Test
+ void testPart04() {
+ // Setup Bouncey castle as a Security Provider
+ Security.addProvider(new org.bouncycastle.jce.provider.BouncyCastleProvider());
+ // Security.addProvider(new sun.security.provider.Sun());
+ // XXX NOTE see https://docs.oracle.com/javase/8/docs/technotes/guides/security/SunProviders.html
+ // Create the new wallets
+ Wallet walletA = new Wallet();
+ Wallet walletB = new Wallet();
+ Assertions.assertNotNull( walletA );
+ Assertions.assertNotNull( walletB );
+ // Test public and private keys
+ System.out.println("Private and public keys:");
+ System.out.println(StringUtils.getStringFromKey(walletA.privateKey));
+ System.out.println(StringUtils.getStringFromKey(walletA.publicKey));
+ Assertions.assertNotNull( walletA.privateKey );
+ Assertions.assertNotNull( walletA.publicKey );
+ Assertions.assertNotNull( walletB.privateKey );
+ Assertions.assertNotNull( walletB.publicKey );
+ //Create a test transaction from WalletA to walletB
+ Transaction transaction = new Transaction(walletA.publicKey, walletB.publicKey, 5, null);
+ Assertions.assertNotNull( transaction );
+ transaction.generateSignature(walletA.privateKey);
+ Assertions.assertNotNull( walletA.privateKey );
+ //Verify the signature works and verify it from the public key
+ System.out.println("Is signature verified");
+ System.out.println(transaction.verifySignature());
+ Assertions.assertTrue( transaction.verifySignature() );
+ }
+
+ @Test
+ void testPart05() {
+
+ List blockchain = BlockChain.build();
+
+ Map mapUTXOs = new HashMap();
+
+ int difficulty = 3;
+ float minimumTransaction = 0.1f;
+
+ // Setup Bouncey castle as a Security Provider
+ Security.addProvider(new org.bouncycastle.jce.provider.BouncyCastleProvider());
+
+ // Create wallets:
+ Wallet walletA = new Wallet();
+ Wallet walletB = new Wallet();
+ Wallet coinbase = new Wallet();
+
+ // Create genesis transaction, which sends 100 NoobCoin to walletA:
+ Transaction genesisTransaction = new Transaction(coinbase.publicKey, walletA.publicKey, 100f, null);
+ Assertions.assertNotNull( genesisTransaction );
+ // Manually sign the genesis transaction
+ genesisTransaction.generateSignature(coinbase.privateKey);
+ // Manually set the transaction id
+ genesisTransaction.setTransactionId( "0" );
+ // Manually add the Transactions Output
+ genesisTransaction.getOutputs().add(new TransactionOutput( genesisTransaction.getRecipient(),
+ genesisTransaction.getValue(),
+ genesisTransaction.getTransactionId() ));
+
+ // Its important to store our first transaction in the UTXOs list.
+ mapUTXOs.put(genesisTransaction.getOutputs().get(0).getId(), genesisTransaction.getOutputs().get(0));
+ System.out.println("Creating and Mining Genesis block... ");
+ Block genesis = new Block("0");
+ genesis.addTransaction(genesisTransaction, mapUTXOs, minimumTransaction);
+ BlockChain.addBlock(blockchain, genesis, difficulty);
+
+ // Testing
+ Block block1 = new Block(genesis.getHash() );
+ System.out.println("\nWalletA's balance is: " + walletA.getBalance( mapUTXOs ));
+
+ Assertions.assertEquals(100, walletA.getBalance( mapUTXOs ));
+
+ System.out.println("\nWalletA is Attempting to send funds (40) to WalletB...");
+ boolean bBlock1AddTransactionResult = block1.addTransaction( walletA.sendFunds(walletB.publicKey, 40f, mapUTXOs),
+ mapUTXOs, minimumTransaction) ;
+ Assertions.assertTrue( bBlock1AddTransactionResult );
+ Assertions.assertTrue( BlockChain.addBlock(blockchain, block1, difficulty) );
+ System.out.println("\nWalletA's balance is: " + walletA.getBalance( mapUTXOs ));
+ System.out.println("WalletB's balance is: " + walletB.getBalance( mapUTXOs ));
+
+ Assertions.assertEquals(60, walletA.getBalance( mapUTXOs ));
+ Assertions.assertEquals(40, walletB.getBalance( mapUTXOs ));
+
+ boolean isChainValid = BlockChain.isChainValidV2( blockchain, genesisTransaction, difficulty );
+ Assertions.assertTrue( isChainValid );
+
+ Block block2 = new Block(block1.getHash() );
+ System.out.println("\nWalletA Attempting to send more funds (1000) than it has...");
+ boolean bBlock2AddTransactionResult = block2.addTransaction( walletA.sendFunds(walletB.publicKey, 1000f, mapUTXOs),
+ mapUTXOs, minimumTransaction);
+ Assertions.assertFalse( bBlock2AddTransactionResult);
+ Assertions.assertTrue( BlockChain.addBlock(blockchain, block2, difficulty) );
+ System.out.println("\nWalletA's balance is: " + walletA.getBalance( mapUTXOs ));
+ System.out.println("WalletB's balance is: " + walletB.getBalance( mapUTXOs ));
+
+ Assertions.assertEquals(60, walletA.getBalance( mapUTXOs ));
+ Assertions.assertEquals(40, walletB.getBalance( mapUTXOs ));
+
+ isChainValid = BlockChain.isChainValidV2( blockchain, genesisTransaction, difficulty );
+ Assertions.assertTrue( isChainValid );
+
+ Block block3 = new Block(block2.getHash() );
+ System.out.println("\nWalletB is Attempting to send funds (20) to WalletA...");
+ boolean bBlock3AddTransactionResult = block3.addTransaction( walletB.sendFunds( walletA.publicKey, 20, mapUTXOs),
+ mapUTXOs, minimumTransaction);
+ Assertions.assertTrue( bBlock3AddTransactionResult );
+ System.out.println("\nWalletA's balance is: " + walletA.getBalance( mapUTXOs ));
+ System.out.println("WalletB's balance is: " + walletB.getBalance( mapUTXOs ));
+
+ Assertions.assertEquals(80, walletA.getBalance( mapUTXOs ));
+ Assertions.assertEquals(20, walletB.getBalance( mapUTXOs ));
+
+ isChainValid = BlockChain.isChainValidV2( blockchain, genesisTransaction, difficulty );
+ Assertions.assertTrue( isChainValid );
+ }
+
+}
diff --git a/src/main/java/gabywald/crypto/blockchain/StringUtils.java b/src/main/java/gabywald/crypto/blockchain/StringUtils.java
new file mode 100644
index 0000000..5505109
--- /dev/null
+++ b/src/main/java/gabywald/crypto/blockchain/StringUtils.java
@@ -0,0 +1,157 @@
+package gabywald.crypto.blockchain;
+
+import java.io.UnsupportedEncodingException;
+import java.security.InvalidKeyException;
+import java.security.Key;
+import java.security.MessageDigest;
+import java.security.NoSuchAlgorithmException;
+import java.security.NoSuchProviderException;
+import java.security.PrivateKey;
+import java.security.PublicKey;
+import java.security.Signature;
+import java.security.SignatureException;
+import java.util.List;
+import java.util.ArrayList;
+import java.util.Base64;
+
+/**
+ * StringUtil of BlockChain.
+ *
https://medium.com/programmers-blockchain/create-simple-blockchain-java-tutorial-from-scratch-6eeed3cb03fa
+ *
https://github.com/CryptoKass/NoobChain-Tutorial-Part-1
+ *
https://medium.com/programmers-blockchain/creating-your-first-blockchain-with-java-part-2-transactions-2cdac335e0ce
+ *
https://github.com/CryptoKass/NoobChain-Tutorial-Part-2
+ * @author Gabriel Chandesris (2021)
+ */
+public abstract class StringUtils {
+
+ /**
+ * Applies Sha256 to a string and returns the result.
+ * @param input
+ * @return (String)
+ * @throws BlockchainException
+ * TODO upgrade / improve this method (applySha256) to return null value if any exception inside !
+ */
+ public static String applySha256(String input)
+ throws BlockchainException {
+ try {
+ MessageDigest digest = MessageDigest.getInstance("SHA-256");
+ // Applies sha256 to our input,
+ byte[] hash = digest.digest(input.getBytes("UTF-8"));
+ // This will contain hash as hexidecimal
+ StringBuffer hexString = new StringBuffer();
+ for (int i = 0 ; i < hash.length ; i++) {
+ String hex = Integer.toHexString(0xff & hash[i]);
+ if (hex.length() == 1) {
+ hexString.append('0');
+ }
+ hexString.append(hex);
+ }
+ return hexString.toString();
+ } catch (NoSuchAlgorithmException | UnsupportedEncodingException e) {
+ System.out.println( "StringUtils: " + e.getClass().getName() + ": " + e.getMessage() );
+ throw new BlockchainException("StringUtils: " + e.getClass().getName() +": " + e.getMessage());
+ }
+ }
+
+ public static final String ECDSA = "ECDSA"; // CipherAlgorithm
+ public static final String BC = "BC"; // CipherProvider
+
+ // public static final String CipherAlgorithm = "DiffieHellman"; // "SHA1withDSA";
+ // public static final String CipherProvider = "SunJCE"; // "SUN";
+
+ // XXX NOTE see https://docs.oracle.com/javase/8/docs/technotes/guides/security/SunProviders.html
+ // TODO learning how security works here !
+
+ /**
+ * Applies ECDSA Signature and returns the result ( as bytes ).
+ * @param privateKey
+ * @param input
+ * @return (byte[])
+ * @throws NoSuchProviderException
+ * @throws NoSuchAlgorithmException
+ * @throws InvalidKeyException
+ * @throws SignatureException
+ */
+ public static byte[] applyECDSASig(PrivateKey privateKey, String input)
+ throws BlockchainException {
+ Signature dsa;
+ byte[] output = new byte[0];
+ try {
+ dsa = Signature.getInstance(StringUtils.ECDSA, StringUtils.BC);
+ // dsa = Signature.getInstance(StringUtils.CipherAlgorithm, StringUtils.CipherProvider);
+ dsa.initSign(privateKey);
+ byte[] strByte = input.getBytes();
+ dsa.update(strByte);
+ byte[] realSig = dsa.sign();
+ output = realSig;
+ } catch (NoSuchAlgorithmException | NoSuchProviderException | InvalidKeyException | SignatureException e) {
+ System.out.println( "StringUtils: " + e.getClass().getName() + ": " + e.getMessage() );
+ throw new BlockchainException("StringUtils: " + e.getClass().getName() + ": " + e.getMessage());
+ }
+ return output;
+ }
+
+ /**
+ * Verifies a String signature.
+ * @param publicKey
+ * @param data
+ * @param signature
+ * @return (boolean)
+ */
+ public static boolean verifyECDSASig(PublicKey publicKey, String data, byte[] signature) {
+ try {
+ Signature ecdsaVerify = Signature.getInstance(StringUtils.ECDSA, StringUtils.BC);
+ // Signature ecdsaVerify = Signature.getInstance(StringUtils.CipherAlgorithm, StringUtils.CipherProvider);
+ ecdsaVerify.initVerify(publicKey);
+ ecdsaVerify.update(data.getBytes());
+ return ecdsaVerify.verify(signature);
+ } catch (NoSuchAlgorithmException | NoSuchProviderException | InvalidKeyException | SignatureException e) {
+ // throw new BlockchainException("StringUtils: " + e.getClass().getName() + ": " + e.getMessage());
+ System.out.println( "StringUtils: " + e.getClass().getName() + ": " + e.getMessage() );
+ }
+ return false; // if any Exception is throwed !
+ }
+
+ public static String getStringFromKey(Key key) {
+ return Base64.getEncoder().encodeToString(key.getEncoded());
+ }
+
+ /**
+ * Returns difficulty string target, to compare to hash. eg difficulty of 5 will return "00000"
+ * @param difficulty
+ * @return (String)
+ */
+ public static String getDifficultyString(int difficulty) {
+ return new String(new char[difficulty]).replace('\0', '0');
+ }
+
+ /**
+ * Tacks in array of transactions and returns a merkle root.
+ * @param transactions
+ * @return (String)
+ */
+ public static String getMerkleRoot(List transactions) {
+ int count = transactions.size();
+ List previousTreeLayer = new ArrayList();
+ for (Transaction transaction : transactions) {
+ previousTreeLayer.add(transaction.getTransactionId());
+ }
+ List treeLayer = previousTreeLayer;
+ while (count > 1) {
+ treeLayer = new ArrayList();
+ for (int i = 1 ; i < previousTreeLayer.size() ; i++) {
+ try {
+ treeLayer.add(StringUtils.applySha256(previousTreeLayer.get(i-1) + previousTreeLayer.get(i)));
+ } catch (BlockchainException e) {
+ System.out.println( e.getMessage() );
+ }
+ }
+ count = treeLayer.size();
+ previousTreeLayer = treeLayer;
+ }
+ String merkleRoot = (treeLayer.size() == 1) ? treeLayer.get(0) : "";
+ return merkleRoot;
+ }
+
+}
+
diff --git a/src/main/java/gabywald/crypto/blockchain/Transaction.java b/src/main/java/gabywald/crypto/blockchain/Transaction.java
new file mode 100644
index 0000000..86f7096
--- /dev/null
+++ b/src/main/java/gabywald/crypto/blockchain/Transaction.java
@@ -0,0 +1,238 @@
+package gabywald.crypto.blockchain;
+
+import java.security.Key;
+import java.security.PrivateKey;
+import java.security.PublicKey;
+import java.security.Signature;
+
+import java.util.List;
+import java.util.Map;
+import java.util.ArrayList;
+import java.util.Base64;
+
+/**
+ * Transaction of BlockChain.
+ *
https://medium.com/programmers-blockchain/creating-your-first-blockchain-with-java-part-2-transactions-2cdac335e0ce
+ *
https://github.com/CryptoKass/NoobChain-Tutorial-Part-2
+ * @author Gabriel Chandesris (2021)
+ */
+public class Transaction {
+ /** This is also the hash of the transaction. */
+ private String transactionId;
+ /** senders address/public key. */
+ private PublicKey sender;
+ /** Recipients address/public key. */
+ private PublicKey recipient;
+ private float value;
+ /** This is to prevent anybody else from spending funds in our wallet. */
+ private byte[] signature;
+
+ private List inputs = new ArrayList();
+ private List outputs = new ArrayList();
+
+ /** A rough count of how many transactions have been generated. */
+ private static int sequence = 0;
+
+ // Constructor:
+ public Transaction(PublicKey from, PublicKey to, float value, List inputs) {
+ this.sender = from;
+ this.recipient = to;
+ this.value = value;
+ this.inputs = inputs;
+ }
+
+ /**
+ * This Calculates the transaction hash (which will be used as its Id)
+ * @return
+ */
+ private String calculateHash() {
+ // Increase the sequence to avoid 2 identical transactions having the same hash
+ Transaction.sequence++;
+ try {
+ return StringUtils.applySha256(
+ StringUtils.getStringFromKey(this.sender) +
+ StringUtils.getStringFromKey(this.recipient) +
+ Float.toString(value) + Transaction.sequence
+ );
+ } catch (BlockchainException e) {
+ // e.printStackTrace();
+ System.out.println( e.getMessage() );
+ return null;
+ }
+ }
+
+ /**
+ * Applies ECDSA Signature and returns the result ( as bytes ).
+ * @param privateKey
+ * @param input
+ * @return
+ */
+ public static byte[] applyECDSASig(PrivateKey privateKey, String input) {
+ Signature dsa;
+ byte[] output = new byte[0];
+ try {
+ dsa = Signature.getInstance(StringUtils.ECDSA, StringUtils.BC);
+ // Signature.getInstance(StringUtils.CipherAlgorithm, StringUtils.CipherProvider);
+ dsa.initSign(privateKey);
+ byte[] strByte = input.getBytes();
+ dsa.update(strByte);
+ byte[] realSig = dsa.sign();
+ output = realSig;
+ } catch (Exception e) {
+ throw new RuntimeException(e);
+ }
+ return output;
+ }
+
+ /**
+ * Verifies a String signature.
+ * @param publicKey
+ * @param data
+ * @param signature
+ * @return
+ */
+ public static boolean verifyECDSASig(PublicKey publicKey, String data, byte[] signature) {
+ try {
+ Signature ecdsaVerify = Signature.getInstance(StringUtils.ECDSA, StringUtils.BC);
+ // Signature.getInstance(StringUtils.CipherAlgorithm, StringUtils.CipherProvider);
+ ecdsaVerify.initVerify(publicKey);
+ ecdsaVerify.update(data.getBytes());
+ return ecdsaVerify.verify(signature);
+ } catch(Exception e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ public static String getStringFromKey(Key key) {
+ return Base64.getEncoder().encodeToString(key.getEncoded());
+ }
+
+ /**
+ * Signs all the data we don't wish to be tampered with.
+ * @param privateKey
+ */
+ public void generateSignature(PrivateKey privateKey) {
+ String data = StringUtils.getStringFromKey(this.sender)
+ + StringUtils.getStringFromKey(this.recipient)
+ + Float.toString(this.value);
+ try {
+ this.signature = StringUtils.applyECDSASig(privateKey,data);
+ } catch (BlockchainException e) {
+ // e.printStackTrace();
+ System.out.println( e.getMessage() );
+ this.signature = null;
+ }
+ }
+
+ /**
+ * Verifies the data we signed hasn't been tampered with.
+ * @return
+ */
+ public boolean verifySignature() {
+ String data = StringUtils.getStringFromKey(this.sender)
+ + StringUtils.getStringFromKey(this.recipient)
+ + Float.toString(this.value);
+ return StringUtils.verifyECDSASig(this.sender, data, this.signature);
+ }
+
+ /**
+ * Returns true if new transaction could be created.
+ * @param mapUTXOs
+ * @param minimumTransaction
+ * @return
+ */
+ public boolean processTransaction( final Map mapUTXOs,
+ final float minimumTransaction) {
+
+ if (this.verifySignature() == false) {
+ System.out.println("#Transaction Signature failed to verify");
+ return false;
+ }
+
+ // Gather transaction inputs (Make sure they are unspent):
+ // this.inputs.stream().forEach(ti -> ti.UTXO = UTXOs.get(ti.transactionOutputId) );
+ for (TransactionInput it : inputs) {
+ it.setTransactionOutput( mapUTXOs.get(it.getTransactionOutputId() ) );
+ }
+
+ // Check if transaction is valid:
+ if (this.getInputsValue() < minimumTransaction) {
+ System.out.println("#Transaction Inputs to small: " + this.getInputsValue());
+ return false;
+ }
+
+ // Generate transaction outputs:
+ float leftOver = this.getInputsValue() - value; //
+ // - Get value of inputs then the left over change:
+ this.transactionId = this.calculateHash();
+ // - Send value to recipient
+ this.outputs.add(new TransactionOutput( this.recipient, this.value, this.transactionId));
+ // - Send the left over 'change' back to sender
+ this.outputs.add(new TransactionOutput( this.sender, leftOver, this.transactionId));
+
+ // Add outputs to Unspent list
+ // this.outputs.stream().forEach(to -> mapUTXOs.put(to.id , to) );
+ for (TransactionOutput to : outputs) {
+ mapUTXOs.put(to.getId() , to);
+ }
+
+ // Remove transaction inputs from UTXO lists as spent:
+ this.inputs.stream().forEach(ti -> {
+ // If Transaction can't be found skip it
+ if ( ti.getTransactionOutput() != null)
+ { mapUTXOs.remove(ti.getTransactionOutput().getId()); }
+ } );
+
+ return true;
+ }
+
+ /**
+ * Returns sum of inputs(UTXOs) values.
+ * @return
+ */
+ public float getInputsValue() {
+ float total = 0;
+ for (TransactionInput ti : inputs) {
+ // If Transaction can't be found skip it
+ if ( ti.getTransactionOutput() == null) { continue; }
+ total += ti.getTransactionOutput().getValue();
+ }
+ return total;
+ }
+
+ /**
+ * Returns sum of outputs:
+ * @return
+ */
+ public float getOutputsValue() {
+ float total = 0;
+ for (TransactionOutput to : outputs) {
+ total += to.getValue();
+ }
+ return total;
+ }
+
+
+
+ public void setTransactionId(String transactionId)
+ { this.transactionId = transactionId; }
+
+ public String getTransactionId()
+ { return this.transactionId; }
+
+ public PublicKey getSender()
+ { return this.sender; }
+
+ public PublicKey getRecipient()
+ { return this.recipient; }
+
+ public float getValue()
+ { return this.value; }
+
+ public List getOutputs()
+ { return this.outputs; }
+
+ public List getInputs()
+ { return this.inputs; }
+
+}
diff --git a/src/main/java/gabywald/crypto/blockchain/TransactionInput.java b/src/main/java/gabywald/crypto/blockchain/TransactionInput.java
new file mode 100644
index 0000000..d95d24e
--- /dev/null
+++ b/src/main/java/gabywald/crypto/blockchain/TransactionInput.java
@@ -0,0 +1,27 @@
+package gabywald.crypto.blockchain;
+
+/**
+ * TransactionInput of BlockChain.
+ *
https://medium.com/programmers-blockchain/creating-your-first-blockchain-with-java-part-2-transactions-2cdac335e0ce
+ *
https://github.com/CryptoKass/NoobChain-Tutorial-Part-2
+ * @author Gabriel Chandesris (2021)
+ */
+public class TransactionInput {
+ /** Reference to TransactionOutputs -> transactionId */
+ private String transactionOutputId = null;
+ /** Contains the Unspent transaction output */
+ private TransactionOutput to = null;
+
+ public TransactionInput(String transactionOutputId) {
+ this.transactionOutputId = transactionOutputId;
+ }
+
+ public String getTransactionOutputId()
+ { return this.transactionOutputId; }
+
+ public TransactionOutput getTransactionOutput()
+ { return this.to; }
+
+ void setTransactionOutput(TransactionOutput to)
+ { this.to = to; }
+}
diff --git a/src/main/java/gabywald/crypto/blockchain/TransactionOutput.java b/src/main/java/gabywald/crypto/blockchain/TransactionOutput.java
new file mode 100644
index 0000000..d345b03
--- /dev/null
+++ b/src/main/java/gabywald/crypto/blockchain/TransactionOutput.java
@@ -0,0 +1,65 @@
+package gabywald.crypto.blockchain;
+
+import java.security.PublicKey;
+
+/**
+ * TransactionOutput of BlockChain.
+ *
https://medium.com/programmers-blockchain/creating-your-first-blockchain-with-java-part-2-transactions-2cdac335e0ce
+ *
https://github.com/CryptoKass/NoobChain-Tutorial-Part-2
+ * @author Gabriel Chandesris (2021)
+ */
+public class TransactionOutput {
+ private String id;
+ /** Also known as the new owner of these coins. */
+ private PublicKey recipient;
+ /** The amount of coins they own */
+ private float value;
+ /** The id of the transaction this output was created in */
+ private String parentTransactionId;
+
+ /**
+ * Constructor
+ * @param reciepient
+ * @param value
+ * @param parentTransactionId
+ */
+ public TransactionOutput(PublicKey reciepient, float value, String parentTransactionId) {
+ this.recipient = reciepient;
+ this.value = value;
+ this.parentTransactionId = parentTransactionId;
+ try {
+ this.id = StringUtils.applySha256(StringUtils.getStringFromKey(reciepient)
+ + Float.toString(value) + parentTransactionId);
+ // TODO check alternatives for here when exception occurs !! (builder ?) : see StringUtils.applySha256 comment !
+ } catch (BlockchainException e) {
+ // e.printStackTrace();
+ System.out.println(e.getMessage());
+ this.id = null;
+ }
+ }
+
+ /**
+ * Check if coin belongs to you.
+ * @param publicKey
+ * @return
+ */
+ public boolean isMine(PublicKey publicKey) {
+ return this.recipient.equals(publicKey);
+ // return (publicKey == this.reciepient);
+ }
+
+ public String getId()
+ { return this.id; }
+
+ public PublicKey getRecipient()
+ { return this.recipient; }
+
+ public float getValue()
+ { return this.value; }
+
+ public String getParentTransactionId()
+ { return this.parentTransactionId; }
+
+
+
+}
diff --git a/src/main/java/gabywald/crypto/blockchain/Wallet.java b/src/main/java/gabywald/crypto/blockchain/Wallet.java
new file mode 100644
index 0000000..4c0f6e3
--- /dev/null
+++ b/src/main/java/gabywald/crypto/blockchain/Wallet.java
@@ -0,0 +1,111 @@
+package gabywald.crypto.blockchain;
+
+import java.security.InvalidAlgorithmParameterException;
+import java.security.KeyPair;
+import java.security.KeyPairGenerator;
+import java.security.NoSuchAlgorithmException;
+import java.security.NoSuchProviderException;
+import java.security.PrivateKey;
+import java.security.PublicKey;
+import java.security.SecureRandom;
+import java.security.spec.ECGenParameterSpec;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * Wallet of BlockChain.
+ *
https://medium.com/programmers-blockchain/creating-your-first-blockchain-with-java-part-2-transactions-2cdac335e0ce
+ *
https://github.com/CryptoKass/NoobChain-Tutorial-Part-2
+ * @author Gabriel Chandesris (2021)
+ */
+public class Wallet {
+
+ public PrivateKey privateKey = null;
+ public PublicKey publicKey = null;
+
+ public Wallet() {
+ this.generateKeyPair();
+ }
+
+ /**
+ * Generate some KeyPair (Public and Private).
+ */
+ public void generateKeyPair() {
+ try {
+ // TODO learning how security works here !
+ KeyPairGenerator keyGen = KeyPairGenerator.getInstance(StringUtils.ECDSA, StringUtils.BC);
+ // KeyPairGenerator keyGen = KeyPairGenerator.getInstance(StringUtils.CipherAlgorithm, StringUtils.CipherProvider);
+ SecureRandom random = SecureRandom.getInstance("SHA1PRNG");
+ ECGenParameterSpec ecSpec = new ECGenParameterSpec("prime192v1");
+ // Initialize the key generator and generate a KeyPair : 256 bytes provides an acceptable security level
+ keyGen.initialize(ecSpec, random);
+ KeyPair keyPair = keyGen.generateKeyPair();
+ // Set the public and private keys from the keyPair
+ this.privateKey = keyPair.getPrivate();
+ this.publicKey = keyPair.getPublic();
+ } catch (NoSuchAlgorithmException nsae) {
+ // throw new BlockchainException("StringUtils: NoSuchAlgorithmException: " + nsae.getMessage());
+ System.out.println( "StringUtils: NoSuchAlgorithmException: " + nsae.getMessage() );
+ } catch (NoSuchProviderException nspe) {
+ // throw new BlockchainException("StringUtils: NoSuchProviderException: " + nspe.getMessage());
+ System.out.println( "StringUtils: NoSuchProviderException: " + nspe.getMessage() );
+ } catch (InvalidAlgorithmParameterException iape) {
+ // throw new BlockchainException("StringUtils: InvalidAlgorithmParameterException: " + iape.getMessage());
+ System.out.println( "StringUtils: InvalidAlgorithmParameterException: " + iape.getMessage() );
+ }
+ }
+
+ /**
+ * Returns balance and stores the UTXO's owned by this wallet in this.UTXOs
+ * @return
+ */
+ public float getBalance(final Map mapUTXOs) {
+ float total = 0;
+ for (Map.Entry item : mapUTXOs.entrySet()) {
+ TransactionOutput to = item.getValue();
+ if (to.isMine(this.publicKey)) {
+ // If output belongs to me ( if coins belong to me )
+ // Add it to our list of unspent transactions.
+ mapUTXOs.put(to.getId(), to);
+ total += to.getValue() ;
+ }
+ }
+ return total;
+ }
+
+ /**
+ * Generates and returns a new transaction from this wallet.
+ * @param recipient
+ * @param value
+ * @param mapUTXOs
+ * @return (Transaction)
+ */
+ public Transaction sendFunds(PublicKey recipient, float value, final Map mapUTXOs) {
+ // Gather balance and check funds.
+ if (this.getBalance( mapUTXOs ) < value) {
+ System.out.println("#Not Enough funds to send transaction. Transaction Discarded.");
+ return null;
+ }
+ // Create array list of inputs
+ List inputs = new ArrayList();
+
+ float total = 0;
+ for (Map.Entry item : mapUTXOs.entrySet()) {
+ TransactionOutput to = item.getValue();
+ total += to.getValue();
+ inputs.add(new TransactionInput(to.getId()));
+ if (total > value) { break; }
+ }
+
+ Transaction newTransaction = new Transaction(this.publicKey, recipient , value, inputs);
+ newTransaction.generateSignature(this.privateKey);
+
+ // for (TransactionInput input : inputs)
+ // { mapUTXOs.remove(input.getTransactionOutputId()); }
+
+ return newTransaction;
+ }
+
+}
+
diff --git a/src/main/resources/BioJavaNotes.txt b/src/main/resources/BioJavaNotes.txt
deleted file mode 100644
index e192846..0000000
--- a/src/main/resources/BioJavaNotes.txt
+++ /dev/null
@@ -1,33 +0,0 @@
-https://biojava.org/wiki/BioJava%3AGetStarted --
-https://github.com/biojava/biojava --
-https://github.com/biojava/biojava-tutorial --
-https://mvnrepository.com/artifact/org.biojava --
-https://biojava.org/wiki/BioJava:CookBook4.0/ --
-https://biojava.org/docs/api/ --
-
-https://www.ncbi.nlm.nih.gov/pmc/articles/PMC2530884/ BioJava: an open-source framework for bioinformatics
-
-https://www.developpez.net/forums/d827942/autres-langages/perl/bioinformatique/langage-bioinformatique/ --
-
-https://linuxfr.org/news/bioinformatique-libre --
-https://linuxfr.org/news/biologie-libre --
-http://www.bioinformatics.org/ --
-
-https://www.developpez.net/forums/d787364/autres-langages/perl/bioinformatique/creation-d-application-bioinformatique/ --
-https://www.developpez.net/forums/d253638-2/autres-langages/perl/bioinformatique/sondage-faq-bioinfo-tutoriaux/ --
-
-
-https://en.wikipedia.org/wiki/Bioinformatics --
- => Software and tools
- => Open-source bioinformatics software ;
- => Web services in bioinformatics ;
- => Bioinformatics workflow management systems ;
- => BioCompute and BioCompute Objects ;
- ...
-https://en.wikipedia.org/wiki/Bioinformatics_workflow_management_system -- Bioinformatics workflow management system
- => KNIME, Galaxy, (Pipeline Pilot)...
-https://en.wikipedia.org/wiki/List_of_bioinformatics_software -- List of bioinformatics software
-https://en.wikipedia.org/wiki/List_of_proprietary_bioinformatics_software -- List of proprietary bioinformatics software
-https://en.wikipedia.org/wiki/List_of_open-source_bioinformatics_software -- List of open-source bioinformatics software
-
-
diff --git a/src/main/resources/toInvestigate.txt b/src/main/resources/toInvestigate.txt
new file mode 100644
index 0000000..26671e0
--- /dev/null
+++ b/src/main/resources/toInvestigate.txt
@@ -0,0 +1,14 @@
+
+20211011
+
+Some tutorials and more ideas to test in java :
+ - security
+ - code project building
+ - blockchain
+
+#github #VeeralPatel "How to Secure Anything" #Security #CyberSecurity #Engineering ... https://github.com/veeral-patel/how-to-secure-anything
+#github #danistefanovic "Build Your Own {X}" Build #Code #Understanding #code #programming #development #Java #Python #JavaScript #Go #CPlusPlus #Git #Shell #Bot #BitTorrent #Game ... https://github.com/danistefanovic/build-your-own-x
+
+#medium #blockchain "Creating Your First Blockchain with Java. Part 1." #programmers #java #ProofOfWork ... https://medium.com/programmers-blockchain/create-simple-blockchain-java-tutorial-from-scratch-6eeed3cb03fa
+#medium #blockchain "Creating Your First Blockchain with Java. Part 2." #programmers #java #transactions ... https://medium.com/programmers-blockchain/creating-your-first-blockchain-with-java-part-2-transactions-2cdac335e0ce
+#medium #blockchain "Blockchain Development — Zero to Hero [Guide]" #programmers #development #guide ... https://medium.com/programmers-blockchain/blockchain-development-mega-guide-5a316e6d10df