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