From ad57e674148439cd7dbb56f108bb8b0e40f26083 Mon Sep 17 00:00:00 2001 From: Gaby Wald <30340425+gabywald@users.noreply.github.com> Date: Mon, 11 Oct 2021 11:42:26 +0200 Subject: [PATCH 01/12] [java][ideas][blockchain]review and notes for exploration --- README.md | 12 +++++----- src/main/resources/BioJavaNotes.txt | 33 ---------------------------- src/main/resources/toInvestigate.txt | 14 ++++++++++++ 3 files changed, 21 insertions(+), 38 deletions(-) delete mode 100644 src/main/resources/BioJavaNotes.txt create mode 100644 src/main/resources/toInvestigate.txt diff --git a/README.md b/README.md index 1724b08..0e5b8a2 100644 --- a/README.md +++ b/README.md @@ -1,8 +1,10 @@ # 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 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 From fd3bf6067632d46526d317fea81d57929acf24d6 Mon Sep 17 00:00:00 2001 From: Gaby Wald <30340425+gabywald@users.noreply.github.com> Date: Mon, 11 Oct 2021 16:46:50 +0200 Subject: [PATCH 02/12] [blockchain]First Tutorial --- pom.xml | 9 ++ .../gabywald/crypto/blockchain/Block.java | 48 ++++++ .../gabywald/crypto/blockchain/NoobChain.java | 144 ++++++++++++++++++ .../crypto/blockchain/StringUtil.java | 30 ++++ 4 files changed, 231 insertions(+) create mode 100644 src/main/java/gabywald/crypto/blockchain/Block.java create mode 100644 src/main/java/gabywald/crypto/blockchain/NoobChain.java create mode 100644 src/main/java/gabywald/crypto/blockchain/StringUtil.java diff --git a/pom.xml b/pom.xml index ab7560a..9089389 100644 --- a/pom.xml +++ b/pom.xml @@ -48,5 +48,14 @@ utilities 0.0.2-SNAPSHOT + + + + com.google.code.gson + gson + 2.8.8 + + + \ No newline at end of file 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..8cc624a --- /dev/null +++ b/src/main/java/gabywald/crypto/blockchain/Block.java @@ -0,0 +1,48 @@ +package gabywald.crypto.blockchain; + +import java.util.Date; + +/** + * Block of BlockChain. + *
https://medium.com/programmers-blockchain/create-simple-blockchain-java-tutorial-from-scratch-6eeed3cb03fa + * @author Gabriel Chandesris (2021) + */ +public class Block { + + public String hash; + public String previousHash; + private String data; //our data will be a simple message. + private long timeStamp; //as number of milliseconds since 1/1/1970. + private int nonce; + + // Block Constructor. + 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 = calculateHash(); + } + + // Calculate new hash based on blocks contents + public String calculateHash() { + String calculatedhash = StringUtil.applySha256( + previousHash + + Long.toString(timeStamp) + + Integer.toString(nonce) + + data + ); + return calculatedhash; + } + + public void mineBlock(int difficulty) { + // Create a string with difficulty * "0" + String target = new String(new char[difficulty]).replace('\0', '0'); + while(!hash.substring( 0, difficulty).equals(target)) { + nonce ++; + hash = calculateHash(); + } + System.out.println("Block Mined!!! : " + hash); + } +} diff --git a/src/main/java/gabywald/crypto/blockchain/NoobChain.java b/src/main/java/gabywald/crypto/blockchain/NoobChain.java new file mode 100644 index 0000000..d1e2d81 --- /dev/null +++ b/src/main/java/gabywald/crypto/blockchain/NoobChain.java @@ -0,0 +1,144 @@ +package gabywald.crypto.blockchain; + +import java.util.ArrayList; + +import com.google.gson.GsonBuilder; + +/** + * StringUtil of BlockChain. + *
https://medium.com/programmers-blockchain/create-simple-blockchain-java-tutorial-from-scratch-6eeed3cb03fa + * @author Gabriel Chandesris (2021) + */ +public class NoobChain { + + public static ArrayList blockchain = new ArrayList(); + public static ArrayList blockchain2 = new ArrayList(); + + public static int difficulty = 5; + + public static void main(String[] args) { + + Block genesisBlock = new Block("Hi im the first block", "0"); + System.out.println("Hash for block 1 : " + genesisBlock.hash); + + Block secondBlock = new Block("Yo im the second block",genesisBlock.hash); + System.out.println("Hash for block 2 : " + secondBlock.hash); + + Block thirdBlock = new Block("Hey im the third block",secondBlock.hash); + System.out.println("Hash for block 3 : " + thirdBlock.hash); + + + // Add our blocks to the blockchain ArrayList: + NoobChain.blockchain.add(new Block("Hi im the first block", "0")); + NoobChain.blockchain.add(new Block("Yo im the second block", NoobChain.blockchain.get(NoobChain.blockchain.size()-1).hash)); + NoobChain.blockchain.add(new Block("Hey im the third block", NoobChain.blockchain.get(NoobChain.blockchain.size()-1).hash)); + String blockchainJson = new GsonBuilder().setPrettyPrinting().create().toJson(NoobChain.blockchain); + System.out.println(blockchainJson); + + + // Add our blocks to the blockchain ArrayList: + NoobChain.blockchain2.add(new Block("Hi im the first block", "0")); + System.out.println("Trying to Mine block 1... "); + NoobChain.blockchain2.get(0).mineBlock(NoobChain.difficulty); + + NoobChain.blockchain2.add(new Block("Yo im the second block", NoobChain.blockchain2.get(NoobChain.blockchain2.size()-1).hash)); + System.out.println("Trying to Mine block 2... "); + NoobChain.blockchain2.get(1).mineBlock(NoobChain.difficulty); + + NoobChain.blockchain2.add(new Block("Hey im the third block", NoobChain.blockchain2.get(NoobChain.blockchain2.size()-1).hash)); + System.out.println("Trying to Mine block 3... "); + NoobChain.blockchain2.get(2).mineBlock(NoobChain.difficulty); + + System.out.println("\nBlockchain is Valid: " + isChainValid()); + + String blockchain2Json = new GsonBuilder().setPrettyPrinting().create().toJson(NoobChain.blockchain2); + System.out.println("\nThe block chain: "); + System.out.println(blockchain2Json); + + } + + public static Boolean isChainValid() { + Block currentBlock; + Block previousBlock; + + // Loop through blockchain to check hashes: + for (int i = 1 ; i < NoobChain.blockchain.size() ; i++) { + currentBlock = NoobChain.blockchain.get(i); + previousBlock = NoobChain.blockchain.get(i-1); + // Compare registered hash and calculated hash: + if ( ! currentBlock.hash.equals(currentBlock.calculateHash()) ) { + System.out.println("Current Hashes not equal"); + return false; + } + // Compare previous hash and registered previous hash + if ( ! previousBlock.hash.equals(currentBlock.previousHash) ) { + System.out.println("Previous Hashes not equal"); + return false; + } + } + return true; + } + + /* +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/StringUtil.java b/src/main/java/gabywald/crypto/blockchain/StringUtil.java new file mode 100644 index 0000000..5ad1cdc --- /dev/null +++ b/src/main/java/gabywald/crypto/blockchain/StringUtil.java @@ -0,0 +1,30 @@ +package gabywald.crypto.blockchain; + +import java.security.MessageDigest; + +/** + * StringUtil of BlockChain. + *
https://medium.com/programmers-blockchain/create-simple-blockchain-java-tutorial-from-scratch-6eeed3cb03fa + * @author Gabriel Chandesris (2021) + */ +public class StringUtil { + //Applies Sha256 to a string and returns the result. + public static String applySha256(String input){ + try { + MessageDigest digest = MessageDigest.getInstance("SHA-256"); + // Applies sha256 to our input, + byte[] hash = digest.digest(input.getBytes("UTF-8")); + StringBuffer hexString = new StringBuffer(); // This will contain hash as hexidecimal + 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(Exception e) { + throw new RuntimeException(e); + } + } +} + From 1ead7a9133a15b121ae4b0b955994867cc6d9935 Mon Sep 17 00:00:00 2001 From: Gaby Wald <30340425+gabywald@users.noreply.github.com> Date: Mon, 11 Oct 2021 19:15:05 +0200 Subject: [PATCH 03/12] [blockchain]Second Tutorial intermediate notes --- .../gabywald/crypto/blockchain/Block.java | 19 ++- .../gabywald/crypto/blockchain/NoobChain.java | 118 ++++++++----- .../crypto/blockchain/StringUtil.java | 30 ---- .../crypto/blockchain/StringUtils.java | 104 +++++++++++ .../crypto/blockchain/Transaction.java | 161 ++++++++++++++++++ .../crypto/blockchain/TransactionInput.java | 16 ++ .../crypto/blockchain/TransactionOutput.java | 30 ++++ .../gabywald/crypto/blockchain/Wallet.java | 43 +++++ 8 files changed, 440 insertions(+), 81 deletions(-) delete mode 100644 src/main/java/gabywald/crypto/blockchain/StringUtil.java create mode 100644 src/main/java/gabywald/crypto/blockchain/StringUtils.java create mode 100644 src/main/java/gabywald/crypto/blockchain/Transaction.java create mode 100644 src/main/java/gabywald/crypto/blockchain/TransactionInput.java create mode 100644 src/main/java/gabywald/crypto/blockchain/TransactionOutput.java create mode 100644 src/main/java/gabywald/crypto/blockchain/Wallet.java diff --git a/src/main/java/gabywald/crypto/blockchain/Block.java b/src/main/java/gabywald/crypto/blockchain/Block.java index 8cc624a..0e1d0fa 100644 --- a/src/main/java/gabywald/crypto/blockchain/Block.java +++ b/src/main/java/gabywald/crypto/blockchain/Block.java @@ -5,6 +5,7 @@ /** * 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 { @@ -27,11 +28,11 @@ public Block(String data,String previousHash ) { // Calculate new hash based on blocks contents public String calculateHash() { - String calculatedhash = StringUtil.applySha256( - previousHash + - Long.toString(timeStamp) + - Integer.toString(nonce) + - data + String calculatedhash = StringUtils.applySha256( + this.previousHash + + Long.toString(this.timeStamp) + + Integer.toString(this.nonce) + + this.data ); return calculatedhash; } @@ -39,10 +40,10 @@ public String calculateHash() { public void mineBlock(int difficulty) { // Create a string with difficulty * "0" String target = new String(new char[difficulty]).replace('\0', '0'); - while(!hash.substring( 0, difficulty).equals(target)) { - nonce ++; - hash = calculateHash(); + while ( ! this.hash.substring( 0, difficulty).equals(target)) { + this.nonce++; + this.hash = this.calculateHash(); } - System.out.println("Block Mined!!! : " + hash); + System.out.println("Block Mined!!! : " + this.hash); } } diff --git a/src/main/java/gabywald/crypto/blockchain/NoobChain.java b/src/main/java/gabywald/crypto/blockchain/NoobChain.java index d1e2d81..db5c8ae 100644 --- a/src/main/java/gabywald/crypto/blockchain/NoobChain.java +++ b/src/main/java/gabywald/crypto/blockchain/NoobChain.java @@ -1,62 +1,96 @@ package gabywald.crypto.blockchain; +import java.security.Security; import java.util.ArrayList; +import java.util.HashMap; +// TODO replace GsonBuilder !! (build json from objet !) import com.google.gson.GsonBuilder; /** - * StringUtil of BlockChain. + * 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 + * * @author Gabriel Chandesris (2021) */ public class NoobChain { public static ArrayList blockchain = new ArrayList(); - public static ArrayList blockchain2 = new ArrayList(); + public static ArrayList blockchain2 = new ArrayList(); + public static ArrayList blockchain3 = new ArrayList(); + + public static int difficulty = 3; + + public static Wallet walletA; + public static Wallet walletB; + + // List of all unspent transactions. + public static HashMap UTXOs = new HashMap(); - public static int difficulty = 5; + public static float minimumTransaction = 0.1f; + public static Transaction genesisTransaction; public static void main(String[] args) { - Block genesisBlock = new Block("Hi im the first block", "0"); - System.out.println("Hash for block 1 : " + genesisBlock.hash); - - Block secondBlock = new Block("Yo im the second block",genesisBlock.hash); - System.out.println("Hash for block 2 : " + secondBlock.hash); - - Block thirdBlock = new Block("Hey im the third block",secondBlock.hash); - System.out.println("Hash for block 3 : " + thirdBlock.hash); - - - // Add our blocks to the blockchain ArrayList: - NoobChain.blockchain.add(new Block("Hi im the first block", "0")); - NoobChain.blockchain.add(new Block("Yo im the second block", NoobChain.blockchain.get(NoobChain.blockchain.size()-1).hash)); - NoobChain.blockchain.add(new Block("Hey im the third block", NoobChain.blockchain.get(NoobChain.blockchain.size()-1).hash)); - String blockchainJson = new GsonBuilder().setPrettyPrinting().create().toJson(NoobChain.blockchain); - System.out.println(blockchainJson); - - - // Add our blocks to the blockchain ArrayList: - NoobChain.blockchain2.add(new Block("Hi im the first block", "0")); - System.out.println("Trying to Mine block 1... "); - NoobChain.blockchain2.get(0).mineBlock(NoobChain.difficulty); - - NoobChain.blockchain2.add(new Block("Yo im the second block", NoobChain.blockchain2.get(NoobChain.blockchain2.size()-1).hash)); - System.out.println("Trying to Mine block 2... "); - NoobChain.blockchain2.get(1).mineBlock(NoobChain.difficulty); - - NoobChain.blockchain2.add(new Block("Hey im the third block", NoobChain.blockchain2.get(NoobChain.blockchain2.size()-1).hash)); - System.out.println("Trying to Mine block 3... "); - NoobChain.blockchain2.get(2).mineBlock(NoobChain.difficulty); - - System.out.println("\nBlockchain is Valid: " + isChainValid()); - - String blockchain2Json = new GsonBuilder().setPrettyPrinting().create().toJson(NoobChain.blockchain2); - System.out.println("\nThe block chain: "); - System.out.println(blockchain2Json); +// Block genesisBlock = new Block("Hi im the first block", "0"); +// System.out.println("Hash for block 1 : " + genesisBlock.hash); +// +// Block secondBlock = new Block("Yo im the second block",genesisBlock.hash); +// System.out.println("Hash for block 2 : " + secondBlock.hash); +// +// Block thirdBlock = new Block("Hey im the third block",secondBlock.hash); +// System.out.println("Hash for block 3 : " + thirdBlock.hash); +// +// +// // Add our blocks to the blockchain ArrayList: +// NoobChain.blockchain.add(new Block("Hi im the first block", "0")); +// NoobChain.blockchain.add(new Block("Yo im the second block", NoobChain.blockchain.get(NoobChain.blockchain.size()-1).hash)); +// NoobChain.blockchain.add(new Block("Hey im the third block", NoobChain.blockchain.get(NoobChain.blockchain.size()-1).hash)); +// String blockchainJson = new GsonBuilder().setPrettyPrinting().create().toJson(NoobChain.blockchain); +// System.out.println(blockchainJson); +// +// +// // Add our blocks to the blockchain ArrayList: +// NoobChain.blockchain2.add(new Block("Hi im the first block", "0")); +// System.out.println("Trying to Mine block 1... "); +// NoobChain.blockchain2.get(0).mineBlock(NoobChain.difficulty); +// +// NoobChain.blockchain2.add(new Block("Yo im the second block", NoobChain.blockchain2.get(NoobChain.blockchain2.size()-1).hash)); +// System.out.println("Trying to Mine block 2... "); +// NoobChain.blockchain2.get(1).mineBlock(NoobChain.difficulty); +// +// NoobChain.blockchain2.add(new Block("Hey im the third block", NoobChain.blockchain2.get(NoobChain.blockchain2.size()-1).hash)); +// System.out.println("Trying to Mine block 3... "); +// NoobChain.blockchain2.get(2).mineBlock(NoobChain.difficulty); +// +// System.out.println("\nBlockchain is Valid: " + isChainValid()); +// +// String blockchain2Json = new GsonBuilder().setPrettyPrinting().create().toJson(NoobChain.blockchain2); +// System.out.println("\nThe block chain: "); +// System.out.println(blockchain2Json); + + + // 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 + walletA = new Wallet(); + walletB = new Wallet(); + //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)); + //Create a test transaction from WalletA to walletB + Transaction transaction = new Transaction(NoobChain.walletA.publicKey, NoobChain.walletB.publicKey, 5, null); + transaction.generateSignature(NoobChain.walletA.privateKey); + //Verify the signature works and verify it from the public key + System.out.println("Is signature verified"); + System.out.println(transaction.verifiySignature()); } - + public static Boolean isChainValid() { Block currentBlock; Block previousBlock; @@ -78,7 +112,7 @@ public static Boolean isChainValid() { } return true; } - + /* Hash for block 1 : 9f98b14417f65bf7f55053d6ee948de264beaf4f4effdf1be1b80adb939a56bb Hash for block 2 : e7571024da930e1fee1dad719e0b8eeeefdf80c70a3a3739d83d9de1e23eced8 @@ -104,7 +138,7 @@ public static Boolean isChainValid() { } ] */ - + /* Trying to Mine block 1... Block Mined!!! : 00000206cc43b6e9b1e4d81cc5e2959296c0e1b90db2b0e509ef7fc3ec04095d diff --git a/src/main/java/gabywald/crypto/blockchain/StringUtil.java b/src/main/java/gabywald/crypto/blockchain/StringUtil.java deleted file mode 100644 index 5ad1cdc..0000000 --- a/src/main/java/gabywald/crypto/blockchain/StringUtil.java +++ /dev/null @@ -1,30 +0,0 @@ -package gabywald.crypto.blockchain; - -import java.security.MessageDigest; - -/** - * StringUtil of BlockChain. - *
https://medium.com/programmers-blockchain/create-simple-blockchain-java-tutorial-from-scratch-6eeed3cb03fa - * @author Gabriel Chandesris (2021) - */ -public class StringUtil { - //Applies Sha256 to a string and returns the result. - public static String applySha256(String input){ - try { - MessageDigest digest = MessageDigest.getInstance("SHA-256"); - // Applies sha256 to our input, - byte[] hash = digest.digest(input.getBytes("UTF-8")); - StringBuffer hexString = new StringBuffer(); // This will contain hash as hexidecimal - 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(Exception e) { - throw new RuntimeException(e); - } - } -} - 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..811ab22 --- /dev/null +++ b/src/main/java/gabywald/crypto/blockchain/StringUtils.java @@ -0,0 +1,104 @@ +package gabywald.crypto.blockchain; + +import java.security.Key; +import java.security.MessageDigest; +import java.security.PrivateKey; +import java.security.PublicKey; +import java.security.Signature; +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/tree/master/src/noobchain + * @author Gabriel Chandesris (2021) + */ +public class StringUtils { + + //Applies Sha256 to a string and returns the result. + public static String applySha256(String input){ + try { + MessageDigest digest = MessageDigest.getInstance("SHA-256"); + // Applies sha256 to our input, + byte[] hash = digest.digest(input.getBytes("UTF-8")); + StringBuffer hexString = new StringBuffer(); // This will contain hash as hexidecimal + 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 (Exception e) { + throw new RuntimeException(e); + } + } + + public static final String ECDSA = "ECDSA"; + public static final String BC = "BC"; + + 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 + + // Applies ECDSA Signature and returns the result ( as bytes ). + public static byte[] applyECDSASig(PrivateKey privateKey, String input) { + Signature dsa; + byte[] output = new byte[0]; + try { + dsa = // Signature.getInstance(StringUtil.ECDSA, StringUtil.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 + public static boolean verifyECDSASig(PublicKey publicKey, String data, byte[] signature) { + try { + Signature ecdsaVerify = // Signature.getInstance(StringUtil.ECDSA, StringUtil.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()); + } + + // Tacks in array of transactions and returns a merkle root. + public static String getMerkleRoot(ArrayList transactions) { + int count = transactions.size(); + ArrayList previousTreeLayer = new ArrayList(); + for(Transaction transaction : transactions) { + previousTreeLayer.add(transaction.transactionId); + } + ArrayList treeLayer = previousTreeLayer; + while(count > 1) { + treeLayer = new ArrayList(); + for(int i=1; i < previousTreeLayer.size(); i++) { + treeLayer.add(applySha256(previousTreeLayer.get(i-1) + previousTreeLayer.get(i))); + } + 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..6bf3be4 --- /dev/null +++ b/src/main/java/gabywald/crypto/blockchain/Transaction.java @@ -0,0 +1,161 @@ +package gabywald.crypto.blockchain; + +import java.security.Key; +import java.security.PrivateKey; +import java.security.PublicKey; +import java.security.Signature; + +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/tree/master/src/noobchain + * @author Gabriel Chandesris (2021) + */ +public class Transaction { + /** This is also the hash of the transaction. */ + public String transactionId; + /** senders address/public key. */ + public PublicKey sender; + /** Recipients address/public key. */ + public PublicKey reciepient; + public float value; + /** This is to prevent anybody else from spending funds in our wallet. */ + public byte[] signature; + + public ArrayList inputs = new ArrayList(); + public ArrayList outputs = new ArrayList(); + + private static int sequence = 0; // a rough count of how many transactions have been generated. + + // Constructor: + public Transaction(PublicKey from, PublicKey to, float value, ArrayList inputs) { + this.sender = from; + this.reciepient = to; + this.value = value; + this.inputs = inputs; + } + + // This Calculates the transaction hash (which will be used as its Id) + private String calulateHash() { + // Increase the sequence to avoid 2 identical transactions having the same hash + Transaction.sequence++; + return StringUtils.applySha256( + StringUtils.getStringFromKey(this.sender) + + StringUtils.getStringFromKey(this.reciepient) + + Float.toString(value) + Transaction.sequence + ); + } + + // Applies ECDSA Signature and returns the result ( as bytes ). + public static byte[] applyECDSASig(PrivateKey privateKey, String input) { + Signature dsa; + byte[] output = new byte[0]; + try { + dsa = // Signature.getInstance(StringUtil.ECDSA, StringUtil.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 + public static boolean verifyECDSASig(PublicKey publicKey, String data, byte[] signature) { + try { + Signature ecdsaVerify = // Signature.getInstance(StringUtil.ECDSA, StringUtil.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. + public void generateSignature(PrivateKey privateKey) { + String data = StringUtils.getStringFromKey(this.sender) + + StringUtils.getStringFromKey(this.reciepient) + + Float.toString(this.value); + this.signature = StringUtils.applyECDSASig(privateKey,data); + } + + // Verifies the data we signed hasn't been tampered with + public boolean verifiySignature() { + String data = StringUtils.getStringFromKey(this.sender) + + StringUtils.getStringFromKey(this.reciepient) + + Float.toString(this.value); + return StringUtils.verifyECDSASig(this.sender, data, this.signature); + } + + // Returns true if new transaction could be created. + public boolean processTransaction() { + + if(verifiySignature() == false) { + System.out.println("#Transaction Signature failed to verify"); + return false; + } + + // gather transaction inputs (Make sure they are unspent): + for(TransactionInput i : inputs) { + i.UTXO = NoobChain.UTXOs.get(i.transactionOutputId); + } + + // check if transaction is valid: + if (getInputsValue() < NoobChain.minimumTransaction) { + System.out.println("#Transaction Inputs to small: " + getInputsValue()); + return false; + } + + // generate transaction outputs: + float leftOver = getInputsValue() - value; // get value of inputs then the left over change: + transactionId = calulateHash(); + outputs.add(new TransactionOutput( this.reciepient, value, transactionId)); // send value to recipient + outputs.add(new TransactionOutput( this.sender, leftOver ,transactionId)); // send the left over 'change' back to sender + + // add outputs to Unspent list + for(TransactionOutput o : outputs) { + NoobChain.UTXOs.put(o.id , o); + } + + //remove transaction inputs from UTXO lists as spent: + for(TransactionInput i : inputs) { + if(i.UTXO == null) continue; //if Transaction can't be found skip it + NoobChain.UTXOs.remove(i.UTXO.id); + } + + return true; + } + + //returns sum of inputs(UTXOs) values + public float getInputsValue() { + float total = 0; + for(TransactionInput i : inputs) { + if(i.UTXO == null) continue; //if Transaction can't be found skip it + total += i.UTXO.value; + } + return total; + } + + //returns sum of outputs: + public float getOutputsValue() { + float total = 0; + for(TransactionOutput o : outputs) { + total += o.value; + } + return total; + } + +} 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..831955b --- /dev/null +++ b/src/main/java/gabywald/crypto/blockchain/TransactionInput.java @@ -0,0 +1,16 @@ +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/tree/master/src/noobchain + * @author Gabriel Chandesris (2021) + */ +public class TransactionInput { + public String transactionOutputId; //Reference to TransactionOutputs -> transactionId + public TransactionOutput UTXO; //Contains the Unspent transaction output + + public TransactionInput(String transactionOutputId) { + this.transactionOutputId = transactionOutputId; + } +} 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..07ac913 --- /dev/null +++ b/src/main/java/gabywald/crypto/blockchain/TransactionOutput.java @@ -0,0 +1,30 @@ +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/tree/master/src/noobchain + * @author Gabriel Chandesris (2021) + */ +public class TransactionOutput { + public String id; + public PublicKey reciepient; // Also known as the new owner of these coins. + public float value; // The amount of coins they own + public String parentTransactionId; // The id of the transaction this output was created in + + //Constructor + public TransactionOutput(PublicKey reciepient, float value, String parentTransactionId) { + this.reciepient = reciepient; + this.value = value; + this.parentTransactionId = parentTransactionId; + this.id = StringUtils.applySha256(StringUtils.getStringFromKey(reciepient)+Float.toString(value)+parentTransactionId); + } + + //Check if coin belongs to you + public boolean isMine(PublicKey publicKey) { + return (publicKey == reciepient); + } + +} 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..b5f4810 --- /dev/null +++ b/src/main/java/gabywald/crypto/blockchain/Wallet.java @@ -0,0 +1,43 @@ +package gabywald.crypto.blockchain; + +import java.security.KeyPair; +import java.security.KeyPairGenerator; +import java.security.PrivateKey; +import java.security.PublicKey; +import java.security.SecureRandom; +import java.security.spec.ECGenParameterSpec; + +/** + * 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/tree/master/src/noobchain + * @author Gabriel Chandesris (2021) + */ +public class Wallet { + + public PrivateKey privateKey; + public PublicKey publicKey; + + public Wallet() { + generateKeyPair(); + } + + public void generateKeyPair() { + try { + KeyPairGenerator keyGen = // KeyPairGenerator.getInstance(StringUtil.ECDSA, StringUtil.BC); + 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 + privateKey = keyPair.getPrivate(); + publicKey = keyPair.getPublic(); + } catch(Exception e) { + throw new RuntimeException(e); + } + } + +} + From dca95f9afcb5430ea4651f71078805302d32fcac Mon Sep 17 00:00:00 2001 From: Gaby Wald <30340425+gabywald@users.noreply.github.com> Date: Mon, 11 Oct 2021 19:20:24 +0200 Subject: [PATCH 04/12] [blockchain]Second Tutorial intermediate notes TODO --- src/main/java/gabywald/crypto/blockchain/NoobChain.java | 3 ++- src/main/java/gabywald/crypto/blockchain/StringUtils.java | 1 + src/main/java/gabywald/crypto/blockchain/Wallet.java | 1 + 3 files changed, 4 insertions(+), 1 deletion(-) diff --git a/src/main/java/gabywald/crypto/blockchain/NoobChain.java b/src/main/java/gabywald/crypto/blockchain/NoobChain.java index db5c8ae..d6c5057 100644 --- a/src/main/java/gabywald/crypto/blockchain/NoobChain.java +++ b/src/main/java/gabywald/crypto/blockchain/NoobChain.java @@ -11,7 +11,8 @@ * 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/tree/master/src/noobchain * @author Gabriel Chandesris (2021) */ public class NoobChain { diff --git a/src/main/java/gabywald/crypto/blockchain/StringUtils.java b/src/main/java/gabywald/crypto/blockchain/StringUtils.java index 811ab22..0b03ffb 100644 --- a/src/main/java/gabywald/crypto/blockchain/StringUtils.java +++ b/src/main/java/gabywald/crypto/blockchain/StringUtils.java @@ -44,6 +44,7 @@ public static String applySha256(String input){ 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 ). public static byte[] applyECDSASig(PrivateKey privateKey, String input) { diff --git a/src/main/java/gabywald/crypto/blockchain/Wallet.java b/src/main/java/gabywald/crypto/blockchain/Wallet.java index b5f4810..7e3fb5e 100644 --- a/src/main/java/gabywald/crypto/blockchain/Wallet.java +++ b/src/main/java/gabywald/crypto/blockchain/Wallet.java @@ -24,6 +24,7 @@ public Wallet() { public void generateKeyPair() { try { + // TODO learning how security works here ! KeyPairGenerator keyGen = // KeyPairGenerator.getInstance(StringUtil.ECDSA, StringUtil.BC); KeyPairGenerator.getInstance(StringUtils.CipherAlgorithm, StringUtils.CipherProvider); SecureRandom random = SecureRandom.getInstance("SHA1PRNG"); From 0140831c22b3e4bfa23fb0024c99e5170f6871a5 Mon Sep 17 00:00:00 2001 From: Gaby Wald <30340425+gabywald@users.noreply.github.com> Date: Mon, 11 Oct 2021 19:28:23 +0200 Subject: [PATCH 05/12] [blockchain]Second Tutorial intermediate notes Review code / comments --- .../gabywald/crypto/blockchain/Block.java | 6 +- .../crypto/blockchain/StringUtils.java | 37 +++++++-- .../crypto/blockchain/Transaction.java | 80 +++++++++++++------ .../crypto/blockchain/TransactionInput.java | 6 +- .../crypto/blockchain/TransactionOutput.java | 25 ++++-- .../gabywald/crypto/blockchain/Wallet.java | 2 +- 6 files changed, 115 insertions(+), 41 deletions(-) diff --git a/src/main/java/gabywald/crypto/blockchain/Block.java b/src/main/java/gabywald/crypto/blockchain/Block.java index 0e1d0fa..32a756c 100644 --- a/src/main/java/gabywald/crypto/blockchain/Block.java +++ b/src/main/java/gabywald/crypto/blockchain/Block.java @@ -16,7 +16,11 @@ public class Block { private long timeStamp; //as number of milliseconds since 1/1/1970. private int nonce; - // Block Constructor. + /** + * Block Constructor. + * @param data + * @param previousHash + */ public Block(String data,String previousHash ) { this.data = data; this.previousHash = previousHash; diff --git a/src/main/java/gabywald/crypto/blockchain/StringUtils.java b/src/main/java/gabywald/crypto/blockchain/StringUtils.java index 0b03ffb..3b64ba8 100644 --- a/src/main/java/gabywald/crypto/blockchain/StringUtils.java +++ b/src/main/java/gabywald/crypto/blockchain/StringUtils.java @@ -18,16 +18,22 @@ */ public class StringUtils { - //Applies Sha256 to a string and returns the result. + /** + * Applies Sha256 to a string and returns the result. + * @param input + * @return + */ public static String applySha256(String input){ try { MessageDigest digest = MessageDigest.getInstance("SHA-256"); // Applies sha256 to our input, byte[] hash = digest.digest(input.getBytes("UTF-8")); StringBuffer hexString = new StringBuffer(); // This will contain hash as hexidecimal - for (int i = 0; i < hash.length; i++) { + for (int i = 0 ; i < hash.length ; i++) { String hex = Integer.toHexString(0xff & hash[i]); - if(hex.length() == 1) hexString.append('0'); + if (hex.length() == 1) { + hexString.append('0'); + } hexString.append(hex); } return hexString.toString(); @@ -46,7 +52,12 @@ public static String applySha256(String input){ // 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 ). + /** + * 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]; @@ -64,7 +75,13 @@ public static byte[] applyECDSASig(PrivateKey privateKey, String input) { return output; } - // Verifies a String signature + /** + * 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(StringUtil.ECDSA, StringUtil.BC); @@ -81,7 +98,11 @@ public static String getStringFromKey(Key key) { return Base64.getEncoder().encodeToString(key.getEncoded()); } - // Tacks in array of transactions and returns a merkle root. + /** + * Tacks in array of transactions and returns a merkle root. + * @param transactions + * @return + */ public static String getMerkleRoot(ArrayList transactions) { int count = transactions.size(); ArrayList previousTreeLayer = new ArrayList(); @@ -89,9 +110,9 @@ public static String getMerkleRoot(ArrayList transactions) { previousTreeLayer.add(transaction.transactionId); } ArrayList treeLayer = previousTreeLayer; - while(count > 1) { + while (count > 1) { treeLayer = new ArrayList(); - for(int i=1; i < previousTreeLayer.size(); i++) { + for (int i = 1 ; i < previousTreeLayer.size() ; i++) { treeLayer.add(applySha256(previousTreeLayer.get(i-1) + previousTreeLayer.get(i))); } count = treeLayer.size(); diff --git a/src/main/java/gabywald/crypto/blockchain/Transaction.java b/src/main/java/gabywald/crypto/blockchain/Transaction.java index 6bf3be4..b1eae65 100644 --- a/src/main/java/gabywald/crypto/blockchain/Transaction.java +++ b/src/main/java/gabywald/crypto/blockchain/Transaction.java @@ -38,7 +38,10 @@ public Transaction(PublicKey from, PublicKey to, float value, ArrayList transactionId - public TransactionOutput UTXO; //Contains the Unspent transaction output + /** Reference to TransactionOutputs -> transactionId */ + public String transactionOutputId; + /** Contains the Unspent transaction output */ + public TransactionOutput UTXO; public TransactionInput(String transactionOutputId) { this.transactionOutputId = transactionOutputId; diff --git a/src/main/java/gabywald/crypto/blockchain/TransactionOutput.java b/src/main/java/gabywald/crypto/blockchain/TransactionOutput.java index 07ac913..95ab32f 100644 --- a/src/main/java/gabywald/crypto/blockchain/TransactionOutput.java +++ b/src/main/java/gabywald/crypto/blockchain/TransactionOutput.java @@ -10,19 +10,32 @@ */ public class TransactionOutput { public String id; - public PublicKey reciepient; // Also known as the new owner of these coins. - public float value; // The amount of coins they own - public String parentTransactionId; // The id of the transaction this output was created in + /** Also known as the new owner of these coins. */ + public PublicKey reciepient; + /** The amount of coins they own */ + public float value; + /** The id of the transaction this output was created in */ + public String parentTransactionId; - //Constructor + /** + * Constructor + * @param reciepient + * @param value + * @param parentTransactionId + */ public TransactionOutput(PublicKey reciepient, float value, String parentTransactionId) { this.reciepient = reciepient; this.value = value; this.parentTransactionId = parentTransactionId; - this.id = StringUtils.applySha256(StringUtils.getStringFromKey(reciepient)+Float.toString(value)+parentTransactionId); + this.id = StringUtils.applySha256(StringUtils.getStringFromKey(reciepient) + + Float.toString(value)+parentTransactionId); } - //Check if coin belongs to you + /** + * Check if coin belongs to you. + * @param publicKey + * @return + */ public boolean isMine(PublicKey publicKey) { return (publicKey == reciepient); } diff --git a/src/main/java/gabywald/crypto/blockchain/Wallet.java b/src/main/java/gabywald/crypto/blockchain/Wallet.java index 7e3fb5e..1b1dead 100644 --- a/src/main/java/gabywald/crypto/blockchain/Wallet.java +++ b/src/main/java/gabywald/crypto/blockchain/Wallet.java @@ -35,7 +35,7 @@ public void generateKeyPair() { // Set the public and private keys from the keyPair privateKey = keyPair.getPrivate(); publicKey = keyPair.getPublic(); - } catch(Exception e) { + } catch (Exception e) { throw new RuntimeException(e); } } From b0c7b355082cbfc9b06769152b6abd059bc86d48 Mon Sep 17 00:00:00 2001 From: Gaby Wald <30340425+gabywald@users.noreply.github.com> Date: Tue, 12 Oct 2021 18:22:51 +0200 Subject: [PATCH 06/12] [blockchain]cryptography using Bouncy Castle Provider --- pom.xml | 7 ++ .../gabywald/crypto/blockchain/NoobChain.java | 85 ++++++++++--------- .../crypto/blockchain/StringUtils.java | 21 ++--- .../crypto/blockchain/Transaction.java | 15 ++-- .../gabywald/crypto/blockchain/Wallet.java | 4 +- 5 files changed, 72 insertions(+), 60 deletions(-) diff --git a/pom.xml b/pom.xml index 9089389..9d6655d 100644 --- a/pom.xml +++ b/pom.xml @@ -49,12 +49,19 @@ 0.0.2-SNAPSHOT + com.google.code.gson gson 2.8.8 + + + org.bouncycastle + bcprov-jdk15to18 + 1.69 + diff --git a/src/main/java/gabywald/crypto/blockchain/NoobChain.java b/src/main/java/gabywald/crypto/blockchain/NoobChain.java index d6c5057..3fd18c1 100644 --- a/src/main/java/gabywald/crypto/blockchain/NoobChain.java +++ b/src/main/java/gabywald/crypto/blockchain/NoobChain.java @@ -1,6 +1,7 @@ package gabywald.crypto.blockchain; import java.security.Security; +import java.util.List; import java.util.ArrayList; import java.util.HashMap; @@ -11,15 +12,15 @@ * 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://medium.com/programmers-blockchain/creating-your-first-blockchain-with-java-part-2-transactions-2cdac335e0ce *
https://github.com/CryptoKass/NoobChain-Tutorial-Part-2/tree/master/src/noobchain * @author Gabriel Chandesris (2021) */ public class NoobChain { - public static ArrayList blockchain = new ArrayList(); - public static ArrayList blockchain2 = new ArrayList(); - public static ArrayList blockchain3 = new ArrayList(); + public static List blockchain = new ArrayList(); + public static List blockchain2 = new ArrayList(); + public static List blockchain3 = new ArrayList(); public static int difficulty = 3; @@ -34,47 +35,46 @@ public class NoobChain { public static void main(String[] args) { -// Block genesisBlock = new Block("Hi im the first block", "0"); -// System.out.println("Hash for block 1 : " + genesisBlock.hash); -// -// Block secondBlock = new Block("Yo im the second block",genesisBlock.hash); -// System.out.println("Hash for block 2 : " + secondBlock.hash); -// -// Block thirdBlock = new Block("Hey im the third block",secondBlock.hash); -// System.out.println("Hash for block 3 : " + thirdBlock.hash); -// -// -// // Add our blocks to the blockchain ArrayList: -// NoobChain.blockchain.add(new Block("Hi im the first block", "0")); -// NoobChain.blockchain.add(new Block("Yo im the second block", NoobChain.blockchain.get(NoobChain.blockchain.size()-1).hash)); -// NoobChain.blockchain.add(new Block("Hey im the third block", NoobChain.blockchain.get(NoobChain.blockchain.size()-1).hash)); -// String blockchainJson = new GsonBuilder().setPrettyPrinting().create().toJson(NoobChain.blockchain); -// System.out.println(blockchainJson); -// -// -// // Add our blocks to the blockchain ArrayList: -// NoobChain.blockchain2.add(new Block("Hi im the first block", "0")); -// System.out.println("Trying to Mine block 1... "); -// NoobChain.blockchain2.get(0).mineBlock(NoobChain.difficulty); -// -// NoobChain.blockchain2.add(new Block("Yo im the second block", NoobChain.blockchain2.get(NoobChain.blockchain2.size()-1).hash)); -// System.out.println("Trying to Mine block 2... "); -// NoobChain.blockchain2.get(1).mineBlock(NoobChain.difficulty); -// -// NoobChain.blockchain2.add(new Block("Hey im the third block", NoobChain.blockchain2.get(NoobChain.blockchain2.size()-1).hash)); -// System.out.println("Trying to Mine block 3... "); -// NoobChain.blockchain2.get(2).mineBlock(NoobChain.difficulty); -// -// System.out.println("\nBlockchain is Valid: " + isChainValid()); -// -// String blockchain2Json = new GsonBuilder().setPrettyPrinting().create().toJson(NoobChain.blockchain2); -// System.out.println("\nThe block chain: "); -// System.out.println(blockchain2Json); + // ***** Part I + Block genesisBlock = new Block("Hi im the first block", "0"); + System.out.println("Hash for block 1 : " + genesisBlock.hash); + Block secondBlock = new Block("Yo im the second block",genesisBlock.hash); + System.out.println("Hash for block 2 : " + secondBlock.hash); + Block thirdBlock = new Block("Hey im the third block",secondBlock.hash); + System.out.println("Hash for block 3 : " + thirdBlock.hash); + // ***** Part II + // Add our blocks to the blockchain List: + NoobChain.blockchain.add(new Block("Hi im the first block", "0")); + NoobChain.blockchain.add(new Block("Yo im the second block", NoobChain.blockchain.get(NoobChain.blockchain.size()-1).hash)); + NoobChain.blockchain.add(new Block("Hey im the third block", NoobChain.blockchain.get(NoobChain.blockchain.size()-1).hash)); + String blockchainJson = new GsonBuilder().setPrettyPrinting().create().toJson(NoobChain.blockchain); + System.out.println(blockchainJson); + // ***** Part III + // Add our blocks to the blockchain List: + NoobChain.blockchain2.add(new Block("Hi im the first block", "0")); + System.out.println("Trying to Mine block 1... "); + NoobChain.blockchain2.get(0).mineBlock(NoobChain.difficulty); + + NoobChain.blockchain2.add(new Block("Yo im the second block", NoobChain.blockchain2.get(NoobChain.blockchain2.size()-1).hash)); + System.out.println("Trying to Mine block 2... "); + NoobChain.blockchain2.get(1).mineBlock(NoobChain.difficulty); + + NoobChain.blockchain2.add(new Block("Hey im the third block", NoobChain.blockchain2.get(NoobChain.blockchain2.size()-1).hash)); + System.out.println("Trying to Mine block 3... "); + NoobChain.blockchain2.get(2).mineBlock(NoobChain.difficulty); + + System.out.println("\nBlockchain is Valid: " + isChainValid()); + + String blockchain2Json = new GsonBuilder().setPrettyPrinting().create().toJson(NoobChain.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 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 @@ -90,6 +90,9 @@ public static void main(String[] args) { //Verify the signature works and verify it from the public key System.out.println("Is signature verified"); System.out.println(transaction.verifiySignature()); + + // ***** Part V + // TODO next step of tutorials !! } public static Boolean isChainValid() { diff --git a/src/main/java/gabywald/crypto/blockchain/StringUtils.java b/src/main/java/gabywald/crypto/blockchain/StringUtils.java index 3b64ba8..e6581eb 100644 --- a/src/main/java/gabywald/crypto/blockchain/StringUtils.java +++ b/src/main/java/gabywald/crypto/blockchain/StringUtils.java @@ -5,6 +5,7 @@ import java.security.PrivateKey; import java.security.PublicKey; import java.security.Signature; +import java.util.List; import java.util.ArrayList; import java.util.Base64; @@ -43,11 +44,11 @@ public static String applySha256(String input){ } } - public static final String ECDSA = "ECDSA"; - public static final String BC = "BC"; + 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"; +// 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 ! @@ -62,8 +63,8 @@ public static byte[] applyECDSASig(PrivateKey privateKey, String input) { Signature dsa; byte[] output = new byte[0]; try { - dsa = // Signature.getInstance(StringUtil.ECDSA, StringUtil.BC); - Signature.getInstance(StringUtils.CipherAlgorithm, StringUtils.CipherProvider); + dsa = Signature.getInstance(StringUtils.ECDSA, StringUtils.BC); + // Signature.getInstance(StringUtils.CipherAlgorithm, StringUtils.CipherProvider); dsa.initSign(privateKey); byte[] strByte = input.getBytes(); dsa.update(strByte); @@ -85,7 +86,7 @@ public static byte[] applyECDSASig(PrivateKey privateKey, String input) { public static boolean verifyECDSASig(PublicKey publicKey, String data, byte[] signature) { try { Signature ecdsaVerify = // Signature.getInstance(StringUtil.ECDSA, StringUtil.BC); - Signature.getInstance(StringUtils.CipherAlgorithm, StringUtils.CipherProvider); + Signature.getInstance(StringUtils.ECDSA, StringUtils.BC); ecdsaVerify.initVerify(publicKey); ecdsaVerify.update(data.getBytes()); return ecdsaVerify.verify(signature); @@ -103,13 +104,13 @@ public static String getStringFromKey(Key key) { * @param transactions * @return */ - public static String getMerkleRoot(ArrayList transactions) { + public static String getMerkleRoot(List transactions) { int count = transactions.size(); - ArrayList previousTreeLayer = new ArrayList(); + List previousTreeLayer = new ArrayList(); for(Transaction transaction : transactions) { previousTreeLayer.add(transaction.transactionId); } - ArrayList treeLayer = previousTreeLayer; + List treeLayer = previousTreeLayer; while (count > 1) { treeLayer = new ArrayList(); for (int i = 1 ; i < previousTreeLayer.size() ; i++) { diff --git a/src/main/java/gabywald/crypto/blockchain/Transaction.java b/src/main/java/gabywald/crypto/blockchain/Transaction.java index b1eae65..3be892f 100644 --- a/src/main/java/gabywald/crypto/blockchain/Transaction.java +++ b/src/main/java/gabywald/crypto/blockchain/Transaction.java @@ -5,6 +5,7 @@ import java.security.PublicKey; import java.security.Signature; +import java.util.List; import java.util.ArrayList; import java.util.Base64; @@ -25,13 +26,13 @@ public class Transaction { /** This is to prevent anybody else from spending funds in our wallet. */ public byte[] signature; - public ArrayList inputs = new ArrayList(); - public ArrayList outputs = new ArrayList(); + public List inputs = new ArrayList(); + public List outputs = new ArrayList(); private static int sequence = 0; // a rough count of how many transactions have been generated. // Constructor: - public Transaction(PublicKey from, PublicKey to, float value, ArrayList inputs) { + public Transaction(PublicKey from, PublicKey to, float value, List inputs) { this.sender = from; this.reciepient = to; this.value = value; @@ -62,8 +63,8 @@ public static byte[] applyECDSASig(PrivateKey privateKey, String input) { Signature dsa; byte[] output = new byte[0]; try { - dsa = // Signature.getInstance(StringUtil.ECDSA, StringUtil.BC); - Signature.getInstance(StringUtils.CipherAlgorithm, StringUtils.CipherProvider); + dsa = Signature.getInstance(StringUtils.ECDSA, StringUtils.BC); + // Signature.getInstance(StringUtils.CipherAlgorithm, StringUtils.CipherProvider); dsa.initSign(privateKey); byte[] strByte = input.getBytes(); dsa.update(strByte); @@ -84,8 +85,8 @@ public static byte[] applyECDSASig(PrivateKey privateKey, String input) { */ public static boolean verifyECDSASig(PublicKey publicKey, String data, byte[] signature) { try { - Signature ecdsaVerify = // Signature.getInstance(StringUtil.ECDSA, StringUtil.BC); - Signature.getInstance(StringUtils.CipherAlgorithm, StringUtils.CipherProvider); + 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); diff --git a/src/main/java/gabywald/crypto/blockchain/Wallet.java b/src/main/java/gabywald/crypto/blockchain/Wallet.java index 1b1dead..afba656 100644 --- a/src/main/java/gabywald/crypto/blockchain/Wallet.java +++ b/src/main/java/gabywald/crypto/blockchain/Wallet.java @@ -25,8 +25,8 @@ public Wallet() { public void generateKeyPair() { try { // TODO learning how security works here ! - KeyPairGenerator keyGen = // KeyPairGenerator.getInstance(StringUtil.ECDSA, StringUtil.BC); - KeyPairGenerator.getInstance(StringUtils.CipherAlgorithm, StringUtils.CipherProvider); + KeyPairGenerator keyGen = KeyPairGenerator.getInstance(StringUtils.ECDSA, StringUtils.BC); + // 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 From f1427c0257ae7493d156681d5e789516d4e72588 Mon Sep 17 00:00:00 2001 From: Gaby Wald <30340425+gabywald@users.noreply.github.com> Date: Wed, 13 Oct 2021 16:10:34 +0200 Subject: [PATCH 07/12] [blockchain]Some review and rework (exceptions) --- README.md | 1 + .../gabywald/crypto/blockchain/Block.java | 37 +++++++---- .../blockchain/BlockchainException.java | 14 ++++ .../gabywald/crypto/blockchain/NoobChain.java | 10 +-- .../crypto/blockchain/StringUtils.java | 54 ++++++++++++---- .../crypto/blockchain/Transaction.java | 64 +++++++++++-------- .../crypto/blockchain/TransactionOutput.java | 14 +++- .../gabywald/crypto/blockchain/Wallet.java | 4 +- 8 files changed, 134 insertions(+), 64 deletions(-) create mode 100644 src/main/java/gabywald/crypto/blockchain/BlockchainException.java diff --git a/README.md b/README.md index 0e5b8a2..ddd5df4 100644 --- a/README.md +++ b/README.md @@ -8,3 +8,4 @@ Quelques tests de programmation en Java, des notes diverses, * Challenge CybserSec (quelques notes) * Java Swing : Full Screen, SpalshScreen (à revoir / préciser) * Java reflect / réflexivité / introspection + * "blockchain" diff --git a/src/main/java/gabywald/crypto/blockchain/Block.java b/src/main/java/gabywald/crypto/blockchain/Block.java index 32a756c..93858ef 100644 --- a/src/main/java/gabywald/crypto/blockchain/Block.java +++ b/src/main/java/gabywald/crypto/blockchain/Block.java @@ -12,8 +12,10 @@ public class Block { public String hash; public String previousHash; - private String data; //our data will be a simple message. - private long timeStamp; //as number of milliseconds since 1/1/1970. + /** our data will be a simple message. */ + private String data; + /** as number of milliseconds since 1/1/1970. */ + private long timeStamp; private int nonce; /** @@ -25,28 +27,39 @@ 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 = calculateHash(); + this.hash = this.calculateHash(); } - // Calculate new hash based on blocks contents + /** + * Calculate new hash based on blocks contents + * @return (null if exception apply internally). + * @see StringUtils#applySha256(String) + */ public String calculateHash() { - String calculatedhash = StringUtils.applySha256( - this.previousHash + - Long.toString(this.timeStamp) + - Integer.toString(this.nonce) + - this.data - ); + String calculatedhash = null; + try { + calculatedhash = StringUtils.applySha256( + this.previousHash + + Long.toString(this.timeStamp) + + Integer.toString(this.nonce) + + this.data + ); + } catch (BlockchainException e) { + // e.printStackTrace(); + System.out.println( e.getMessage() ); + calculatedhash = null; + } return calculatedhash; } public void mineBlock(int difficulty) { // Create a string with difficulty * "0" String target = new String(new char[difficulty]).replace('\0', '0'); - while ( ! this.hash.substring( 0, difficulty).equals(target)) { + while ( (this.hash == null) || ( ! this.hash.substring( 0, difficulty ).equals(target)) ) { this.nonce++; this.hash = this.calculateHash(); + // XXX NOTE if excpetion inside calculateHash : hash will be null ! } System.out.println("Block Mined!!! : " + this.hash); } 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/NoobChain.java b/src/main/java/gabywald/crypto/blockchain/NoobChain.java index 3fd18c1..229113b 100644 --- a/src/main/java/gabywald/crypto/blockchain/NoobChain.java +++ b/src/main/java/gabywald/crypto/blockchain/NoobChain.java @@ -64,9 +64,9 @@ public static void main(String[] args) { NoobChain.blockchain2.add(new Block("Hey im the third block", NoobChain.blockchain2.get(NoobChain.blockchain2.size()-1).hash)); System.out.println("Trying to Mine block 3... "); - NoobChain.blockchain2.get(2).mineBlock(NoobChain.difficulty); + NoobChain.blockchain2.get(2).mineBlock(NoobChain.difficulty); - System.out.println("\nBlockchain is Valid: " + isChainValid()); + System.out.println("\nBlockchain is Valid: " + NoobChain.isChainValid()); String blockchain2Json = new GsonBuilder().setPrettyPrinting().create().toJson(NoobChain.blockchain2); System.out.println("\nThe block chain: "); @@ -100,9 +100,9 @@ public static Boolean isChainValid() { Block previousBlock; // Loop through blockchain to check hashes: - for (int i = 1 ; i < NoobChain.blockchain.size() ; i++) { - currentBlock = NoobChain.blockchain.get(i); - previousBlock = NoobChain.blockchain.get(i-1); + for (int i = 1 ; i < NoobChain.blockchain2.size() ; i++) { + currentBlock = NoobChain.blockchain2.get(i); + previousBlock = NoobChain.blockchain2.get(i-1); // Compare registered hash and calculated hash: if ( ! currentBlock.hash.equals(currentBlock.calculateHash()) ) { System.out.println("Current Hashes not equal"); diff --git a/src/main/java/gabywald/crypto/blockchain/StringUtils.java b/src/main/java/gabywald/crypto/blockchain/StringUtils.java index e6581eb..0842a05 100644 --- a/src/main/java/gabywald/crypto/blockchain/StringUtils.java +++ b/src/main/java/gabywald/crypto/blockchain/StringUtils.java @@ -1,10 +1,15 @@ 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; @@ -23,13 +28,17 @@ public class StringUtils { * Applies Sha256 to a string and returns the result. * @param input * @return + * @throws BlockchainException + * TODO upgrade / improve this method (applySha256) to return null value if any exception inside ! */ - public static String applySha256(String input){ + 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")); - StringBuffer hexString = new StringBuffer(); // This will contain hash as hexidecimal + 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) { @@ -38,9 +47,10 @@ public static String applySha256(String input){ hexString.append(hex); } return hexString.toString(); - } - catch (Exception e) { - throw new RuntimeException(e); + } catch (NoSuchAlgorithmException nsae) { + throw new BlockchainException("StringUtils: NoSuchAlgorithmException: " + nsae.getMessage()); + } catch (UnsupportedEncodingException uee) { + throw new BlockchainException("StringUtils: UnsupportedEncodingException: " + uee.getMessage()); } } @@ -58,20 +68,31 @@ public static String applySha256(String input){ * @param privateKey * @param input * @return + * @throws NoSuchProviderException + * @throws NoSuchAlgorithmException + * @throws InvalidKeyException + * @throws SignatureException */ - public static byte[] applyECDSASig(PrivateKey privateKey, String input) { + 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); - // Signature.getInstance(StringUtils.CipherAlgorithm, StringUtils.CipherProvider); + // dsa = 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); + } catch (NoSuchAlgorithmException nsae) { + throw new BlockchainException("StringUtils: NoSuchAlgorithmException: " + nsae.getMessage()); + } catch (NoSuchProviderException nspe) { + throw new BlockchainException("StringUtils: NoSuchProviderException: " + nspe.getMessage()); + } catch (InvalidKeyException ike) { + throw new BlockchainException("StringUtils: InvalidKeyException: " + ike.getMessage()); + } catch (SignatureException se) { + throw new BlockchainException("StringUtils: SignatureException: " + se.getMessage()); } return output; } @@ -85,8 +106,8 @@ public static byte[] applyECDSASig(PrivateKey privateKey, String input) { */ public static boolean verifyECDSASig(PublicKey publicKey, String data, byte[] signature) { try { - Signature ecdsaVerify = // Signature.getInstance(StringUtil.ECDSA, StringUtil.BC); - Signature.getInstance(StringUtils.ECDSA, StringUtils.BC); + 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); @@ -107,14 +128,19 @@ public static String getStringFromKey(Key key) { public static String getMerkleRoot(List transactions) { int count = transactions.size(); List previousTreeLayer = new ArrayList(); - for(Transaction transaction : transactions) { + for (Transaction transaction : transactions) { previousTreeLayer.add(transaction.transactionId); } List treeLayer = previousTreeLayer; while (count > 1) { treeLayer = new ArrayList(); for (int i = 1 ; i < previousTreeLayer.size() ; i++) { - treeLayer.add(applySha256(previousTreeLayer.get(i-1) + previousTreeLayer.get(i))); + try { + treeLayer.add(StringUtils.applySha256(previousTreeLayer.get(i-1) + previousTreeLayer.get(i))); + } catch (BlockchainException e) { + // e.printStackTrace(); + System.out.println( e.getMessage() ); + } } count = treeLayer.size(); previousTreeLayer = treeLayer; diff --git a/src/main/java/gabywald/crypto/blockchain/Transaction.java b/src/main/java/gabywald/crypto/blockchain/Transaction.java index 3be892f..713ee42 100644 --- a/src/main/java/gabywald/crypto/blockchain/Transaction.java +++ b/src/main/java/gabywald/crypto/blockchain/Transaction.java @@ -40,17 +40,23 @@ public Transaction(PublicKey from, PublicKey to, float value, List ti.UTXO = NoobChain.UTXOs.get(ti.transactionOutputId) ); // Check if transaction is valid: - if (getInputsValue() < NoobChain.minimumTransaction) { + if (this.getInputsValue() < NoobChain.minimumTransaction) { System.out.println("#Transaction Inputs to small: " + getInputsValue()); return false; } // Generate transaction outputs: - float leftOver = getInputsValue() - value; // + float leftOver = this.getInputsValue() - value; // // - Get value of inputs then the left over change: - transactionId = calulateHash(); + this.transactionId = this.calculateHash(); // - Send value to recipient - outputs.add(new TransactionOutput( this.reciepient, value, transactionId)); + this.outputs.add(new TransactionOutput( this.reciepient, this.value, this.transactionId)); // - Send the left over 'change' back to sender - outputs.add(new TransactionOutput( this.sender, leftOver ,transactionId)); + this.outputs.add(new TransactionOutput( this.sender, leftOver, this.transactionId)); // Add outputs to Unspent list - for (TransactionOutput o : outputs) { - NoobChain.UTXOs.put(o.id , o); - } + this.outputs.stream().forEach(to -> NoobChain.UTXOs.put(to.id , to) ); // Remove transaction inputs from UTXO lists as spent: - for(TransactionInput i : inputs) { + this.inputs.stream().forEach(ti -> { // If Transaction can't be found skip it - if ( i.UTXO == null) { continue; } - NoobChain.UTXOs.remove(i.UTXO.id); - } + if ( ti.UTXO != null) + { NoobChain.UTXOs.remove(ti.UTXO.id); } + } ); return true; } @@ -173,10 +181,10 @@ public boolean processTransaction() { */ public float getInputsValue() { float total = 0; - for (TransactionInput i : inputs) { + for (TransactionInput ti : inputs) { // If Transaction can't be found skip it - if ( i.UTXO == null) { continue; } - total += i.UTXO.value; + if ( ti.UTXO == null) { continue; } + total += ti.UTXO.value; } return total; } @@ -187,8 +195,8 @@ public float getInputsValue() { */ public float getOutputsValue() { float total = 0; - for (TransactionOutput o : outputs) { - total += o.value; + for (TransactionOutput to : outputs) { + total += to.value; } return total; } diff --git a/src/main/java/gabywald/crypto/blockchain/TransactionOutput.java b/src/main/java/gabywald/crypto/blockchain/TransactionOutput.java index 95ab32f..ed10eed 100644 --- a/src/main/java/gabywald/crypto/blockchain/TransactionOutput.java +++ b/src/main/java/gabywald/crypto/blockchain/TransactionOutput.java @@ -27,8 +27,15 @@ public TransactionOutput(PublicKey reciepient, float value, String parentTransac this.reciepient = reciepient; this.value = value; this.parentTransactionId = parentTransactionId; - this.id = StringUtils.applySha256(StringUtils.getStringFromKey(reciepient) - + Float.toString(value)+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; + } } /** @@ -37,7 +44,8 @@ public TransactionOutput(PublicKey reciepient, float value, String parentTransac * @return */ public boolean isMine(PublicKey publicKey) { - return (publicKey == reciepient); + return this.reciepient.equals(publicKey); + // return (publicKey == this.reciepient); } } diff --git a/src/main/java/gabywald/crypto/blockchain/Wallet.java b/src/main/java/gabywald/crypto/blockchain/Wallet.java index afba656..9695fbb 100644 --- a/src/main/java/gabywald/crypto/blockchain/Wallet.java +++ b/src/main/java/gabywald/crypto/blockchain/Wallet.java @@ -19,14 +19,14 @@ public class Wallet { public PublicKey publicKey; public Wallet() { - generateKeyPair(); + this.generateKeyPair(); } public void generateKeyPair() { try { // TODO learning how security works here ! KeyPairGenerator keyGen = KeyPairGenerator.getInstance(StringUtils.ECDSA, StringUtils.BC); - // KeyPairGenerator.getInstance(StringUtils.CipherAlgorithm, StringUtils.CipherProvider); + // 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 From da394d69264b60f4590a348a02c20c25cada385a Mon Sep 17 00:00:00 2001 From: Gaby Wald <30340425+gabywald@users.noreply.github.com> Date: Wed, 13 Oct 2021 19:25:11 +0200 Subject: [PATCH 08/12] [blockchain]documentation link correction --- src/main/java/gabywald/crypto/blockchain/NoobChain.java | 2 +- src/main/java/gabywald/crypto/blockchain/StringUtils.java | 2 +- src/main/java/gabywald/crypto/blockchain/Transaction.java | 2 +- src/main/java/gabywald/crypto/blockchain/TransactionInput.java | 2 +- src/main/java/gabywald/crypto/blockchain/TransactionOutput.java | 2 +- src/main/java/gabywald/crypto/blockchain/Wallet.java | 2 +- 6 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/main/java/gabywald/crypto/blockchain/NoobChain.java b/src/main/java/gabywald/crypto/blockchain/NoobChain.java index 229113b..e112a0e 100644 --- a/src/main/java/gabywald/crypto/blockchain/NoobChain.java +++ b/src/main/java/gabywald/crypto/blockchain/NoobChain.java @@ -13,7 +13,7 @@ *
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/tree/master/src/noobchain + *
https://github.com/CryptoKass/NoobChain-Tutorial-Part-2 * @author Gabriel Chandesris (2021) */ public class NoobChain { diff --git a/src/main/java/gabywald/crypto/blockchain/StringUtils.java b/src/main/java/gabywald/crypto/blockchain/StringUtils.java index 0842a05..eb53a02 100644 --- a/src/main/java/gabywald/crypto/blockchain/StringUtils.java +++ b/src/main/java/gabywald/crypto/blockchain/StringUtils.java @@ -19,7 +19,7 @@ *
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/tree/master/src/noobchain + *
https://github.com/CryptoKass/NoobChain-Tutorial-Part-2 * @author Gabriel Chandesris (2021) */ public class StringUtils { diff --git a/src/main/java/gabywald/crypto/blockchain/Transaction.java b/src/main/java/gabywald/crypto/blockchain/Transaction.java index 713ee42..f472fc8 100644 --- a/src/main/java/gabywald/crypto/blockchain/Transaction.java +++ b/src/main/java/gabywald/crypto/blockchain/Transaction.java @@ -12,7 +12,7 @@ /** * 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/tree/master/src/noobchain + *
https://github.com/CryptoKass/NoobChain-Tutorial-Part-2 * @author Gabriel Chandesris (2021) */ public class Transaction { diff --git a/src/main/java/gabywald/crypto/blockchain/TransactionInput.java b/src/main/java/gabywald/crypto/blockchain/TransactionInput.java index 16f7695..269c0be 100644 --- a/src/main/java/gabywald/crypto/blockchain/TransactionInput.java +++ b/src/main/java/gabywald/crypto/blockchain/TransactionInput.java @@ -3,7 +3,7 @@ /** * 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/tree/master/src/noobchain + *
https://github.com/CryptoKass/NoobChain-Tutorial-Part-2 * @author Gabriel Chandesris (2021) */ public class TransactionInput { diff --git a/src/main/java/gabywald/crypto/blockchain/TransactionOutput.java b/src/main/java/gabywald/crypto/blockchain/TransactionOutput.java index ed10eed..02271b6 100644 --- a/src/main/java/gabywald/crypto/blockchain/TransactionOutput.java +++ b/src/main/java/gabywald/crypto/blockchain/TransactionOutput.java @@ -5,7 +5,7 @@ /** * 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/tree/master/src/noobchain + *
https://github.com/CryptoKass/NoobChain-Tutorial-Part-2 * @author Gabriel Chandesris (2021) */ public class TransactionOutput { diff --git a/src/main/java/gabywald/crypto/blockchain/Wallet.java b/src/main/java/gabywald/crypto/blockchain/Wallet.java index 9695fbb..705d2cd 100644 --- a/src/main/java/gabywald/crypto/blockchain/Wallet.java +++ b/src/main/java/gabywald/crypto/blockchain/Wallet.java @@ -10,7 +10,7 @@ /** * 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/tree/master/src/noobchain + *
https://github.com/CryptoKass/NoobChain-Tutorial-Part-2 * @author Gabriel Chandesris (2021) */ public class Wallet { From c8480e6281dec92d0553650be70f778aca845745 Mon Sep 17 00:00:00 2001 From: Gaby Wald <30340425+gabywald@users.noreply.github.com> Date: Wed, 13 Oct 2021 20:05:46 +0200 Subject: [PATCH 09/12] [blockchain]Finale adds (to check and tests) --- .../gabywald/crypto/blockchain/Block.java | 46 ++++- .../crypto/blockchain/NoobChainFinale.java | 169 ++++++++++++++++++ .../crypto/blockchain/StringUtils.java | 40 +++-- .../gabywald/crypto/blockchain/Wallet.java | 78 +++++++- 4 files changed, 303 insertions(+), 30 deletions(-) create mode 100644 src/main/java/gabywald/crypto/blockchain/NoobChainFinale.java diff --git a/src/main/java/gabywald/crypto/blockchain/Block.java b/src/main/java/gabywald/crypto/blockchain/Block.java index 93858ef..59b4824 100644 --- a/src/main/java/gabywald/crypto/blockchain/Block.java +++ b/src/main/java/gabywald/crypto/blockchain/Block.java @@ -1,6 +1,8 @@ package gabywald.crypto.blockchain; +import java.util.ArrayList; import java.util.Date; +import java.util.List; /** * Block of BlockChain. @@ -18,18 +20,30 @@ public class Block { private long timeStamp; private int nonce; + public String merkleRoot; + /** Our data will be a simple message. */ + public List transactions = new ArrayList(); + /** * Block Constructor. * @param data * @param previousHash */ - public Block(String data,String 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 @@ -40,10 +54,10 @@ public String calculateHash() { String calculatedhash = null; try { calculatedhash = StringUtils.applySha256( - this.previousHash + - Long.toString(this.timeStamp) + - Integer.toString(this.nonce) + - this.data + previousHash + + Long.toString(timeStamp) + + Integer.toString(nonce) + + merkleRoot ); } catch (BlockchainException e) { // e.printStackTrace(); @@ -54,8 +68,9 @@ public String calculateHash() { } public void mineBlock(int difficulty) { + this.merkleRoot = StringUtils.getMerkleRoot(this.transactions); // Create a string with difficulty * "0" - String target = new String(new char[difficulty]).replace('\0', '0'); + String target = StringUtils.getDificultyString(difficulty); while ( (this.hash == null) || ( ! this.hash.substring( 0, difficulty ).equals(target)) ) { this.nonce++; this.hash = this.calculateHash(); @@ -63,4 +78,23 @@ public void mineBlock(int difficulty) { } System.out.println("Block Mined!!! : " + this.hash); } + + /** + * Add transactions to this block. + * @param transaction + * @return + */ + public boolean addTransaction(Transaction transaction) { + // Process transaction and check if valid, unless block is genesis block then ignore. + if (transaction == null) { return false; } + if (previousHash != "0") { + if (transaction.processTransaction() != true) { + System.out.println("Transaction failed to process. Discarded."); + return false; + } + } + transactions.add(transaction); + System.out.println("Transaction Successfully added to Block"); + return true; + } } 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..1b65dde --- /dev/null +++ b/src/main/java/gabywald/crypto/blockchain/NoobChainFinale.java @@ -0,0 +1,169 @@ +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 { + + public static List blockchain = new ArrayList(); + public static Map UTXOs = new HashMap(); + + public static int difficulty = 3; + public static float minimumTransaction = 0.1f; + public static Wallet walletA; + public static Wallet walletB; + public 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.transactionId = "0"; + // Manually add the Transactions Output + NoobChainFinale.genesisTransaction.outputs.add(new TransactionOutput( NoobChainFinale.genesisTransaction.reciepient, + NoobChainFinale.genesisTransaction.value, + NoobChainFinale.genesisTransaction.transactionId)); + + // Its important to store our first transaction in the UTXOs list. + UTXOs.put(NoobChainFinale.genesisTransaction.outputs.get(0).id, NoobChainFinale.genesisTransaction.outputs.get(0)); + + System.out.println("Creating and Mining Genesis block... "); + Block genesis = new Block("0"); + genesis.addTransaction(NoobChainFinale.genesisTransaction); + NoobChainFinale.addBlock(genesis); + + // Testing + Block block1 = new Block(genesis.hash); + System.out.println("\nWalletA's balance is: " + walletA.getBalance()); + System.out.println("\nWalletA is Attempting to send funds (40) to WalletB..."); + block1.addTransaction(walletA.sendFunds(walletB.publicKey, 40f)); + NoobChainFinale.addBlock(block1); + System.out.println("\nWalletA's balance is: " + walletA.getBalance()); + System.out.println("WalletB's balance is: " + walletB.getBalance()); + + Block block2 = new Block(block1.hash); + System.out.println("\nWalletA Attempting to send more funds (1000) than it has..."); + block2.addTransaction(walletA.sendFunds(walletB.publicKey, 1000f)); + NoobChainFinale.addBlock(block2); + System.out.println("\nWalletA's balance is: " + walletA.getBalance()); + System.out.println("WalletB's balance is: " + walletB.getBalance()); + + Block block3 = new Block(block2.hash); + System.out.println("\nWalletB is Attempting to send funds (20) to WalletA..."); + block3.addTransaction(walletB.sendFunds( walletA.publicKey, 20)); + System.out.println("\nWalletA's balance is: " + walletA.getBalance()); + System.out.println("WalletB's balance is: " + walletB.getBalance()); + + NoobChainFinale.isChainValid(); + } + + public static Boolean isChainValid() { + Block currentBlock; + Block previousBlock; + String hashTarget = new String(new char[difficulty]).replace('\0', '0'); + + // A temporary working list of unspent transactions at a given block state. + HashMap tempUTXOs = new HashMap(); + tempUTXOs.put(genesisTransaction.outputs.get(0).id, genesisTransaction.outputs.get(0)); + + // Loop through blockchain to check hashes: + for (int i = 1 ; i < NoobChainFinale.blockchain.size() ; i++) { + + currentBlock = NoobChainFinale.blockchain.get(i); + previousBlock = NoobChainFinale.blockchain.get(i-1); + + // Compare registered hash and calculated hash: + if ( ! currentBlock.hash.equals(currentBlock.calculateHash()) ) { + System.out.println("#Current Hashes not equal"); + return false; + } + + // Compare previous hash and registered previous hash + if ( ! previousBlock.hash.equals(currentBlock.previousHash) ) { + System.out.println("#Previous Hashes not equal"); + return false; + } + + // Check if hash is solved + if ( ! currentBlock.hash.substring( 0, NoobChainFinale.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.transactions.size() ; t++) { + Transaction currentTransaction = currentBlock.transactions.get(t); + + if ( ! currentTransaction.verifiySignature()) { + 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.inputs) { + tempOutput = tempUTXOs.get(input.transactionOutputId); + + if (tempOutput == null) { + System.out.println("#Referenced input on Transaction(" + t + ") is Missing"); + return false; + } + + if (input.UTXO.value != tempOutput.value) { + System.out.println("#Referenced input Transaction(" + t + ") value is Invalid"); + return false; + } + + tempUTXOs.remove(input.transactionOutputId); + } + + for (TransactionOutput output: currentTransaction.outputs) { + tempUTXOs.put(output.id, output); + } + + if (currentTransaction.outputs.get(0).reciepient != currentTransaction.reciepient) { + System.out.println("#Transaction(" + t + ") output reciepient is not who it should be"); + return false; + } + if (currentTransaction.outputs.get(1).reciepient != currentTransaction.sender) { + System.out.println("#Transaction(" + t + ") output 'change' is not sender."); + return false; + } + + } + + } + System.out.println("Blockchain is valid"); + return true; + } + + public static void addBlock(Block newBlock) { + newBlock.mineBlock(difficulty); + NoobChainFinale.blockchain.add(newBlock); + } +} diff --git a/src/main/java/gabywald/crypto/blockchain/StringUtils.java b/src/main/java/gabywald/crypto/blockchain/StringUtils.java index eb53a02..27ec9f9 100644 --- a/src/main/java/gabywald/crypto/blockchain/StringUtils.java +++ b/src/main/java/gabywald/crypto/blockchain/StringUtils.java @@ -37,7 +37,7 @@ public static String applySha256(String input) 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 + // 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]); @@ -47,18 +47,17 @@ public static String applySha256(String input) hexString.append(hex); } return hexString.toString(); - } catch (NoSuchAlgorithmException nsae) { - throw new BlockchainException("StringUtils: NoSuchAlgorithmException: " + nsae.getMessage()); - } catch (UnsupportedEncodingException uee) { - throw new BlockchainException("StringUtils: UnsupportedEncodingException: " + uee.getMessage()); + } 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"; + // 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 ! @@ -85,14 +84,9 @@ public static byte[] applyECDSASig(PrivateKey privateKey, String input) dsa.update(strByte); byte[] realSig = dsa.sign(); output = realSig; - } catch (NoSuchAlgorithmException nsae) { - throw new BlockchainException("StringUtils: NoSuchAlgorithmException: " + nsae.getMessage()); - } catch (NoSuchProviderException nspe) { - throw new BlockchainException("StringUtils: NoSuchProviderException: " + nspe.getMessage()); - } catch (InvalidKeyException ike) { - throw new BlockchainException("StringUtils: InvalidKeyException: " + ike.getMessage()); - } catch (SignatureException se) { - throw new BlockchainException("StringUtils: SignatureException: " + se.getMessage()); + } 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; } @@ -111,14 +105,25 @@ public static boolean verifyECDSASig(PublicKey publicKey, String data, byte[] si ecdsaVerify.initVerify(publicKey); ecdsaVerify.update(data.getBytes()); return ecdsaVerify.verify(signature); - } catch(Exception e) { - throw new RuntimeException(e); + } 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 + */ + public static String getDificultyString(int difficulty) { + return new String(new char[difficulty]).replace('\0', '0'); + } /** * Tacks in array of transactions and returns a merkle root. @@ -138,7 +143,6 @@ public static String getMerkleRoot(List transactions) { try { treeLayer.add(StringUtils.applySha256(previousTreeLayer.get(i-1) + previousTreeLayer.get(i))); } catch (BlockchainException e) { - // e.printStackTrace(); System.out.println( e.getMessage() ); } } diff --git a/src/main/java/gabywald/crypto/blockchain/Wallet.java b/src/main/java/gabywald/crypto/blockchain/Wallet.java index 705d2cd..43a57d4 100644 --- a/src/main/java/gabywald/crypto/blockchain/Wallet.java +++ b/src/main/java/gabywald/crypto/blockchain/Wallet.java @@ -1,11 +1,17 @@ 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. @@ -15,13 +21,16 @@ */ public class Wallet { - public PrivateKey privateKey; - public PublicKey publicKey; + 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 ! @@ -33,11 +42,68 @@ public void generateKeyPair() { keyGen.initialize(ecSpec, random); KeyPair keyPair = keyGen.generateKeyPair(); // Set the public and private keys from the keyPair - privateKey = keyPair.getPrivate(); - publicKey = keyPair.getPublic(); - } catch (Exception e) { - throw new RuntimeException(e); + 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() { + float total = 0; + for (Map.Entry item: NoobChainFinale.UTXOs.entrySet()){ + TransactionOutput UTXO = item.getValue(); + if (UTXO.isMine(publicKey)) { + // If output belongs to me ( if coins belong to me ) + // Add it to our list of unspent transactions. + NoobChainFinale.UTXOs.put(UTXO.id,UTXO); + total += UTXO.value ; + } + } + return total; + } + + /** + * Generates and returns a new transaction from this wallet. + * @param recipient + * @param value + * @return + */ + public Transaction sendFunds(PublicKey recipient, float value) { + // Gather balance and check funds. + if (this.getBalance() < 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: NoobChainFinale.UTXOs.entrySet()){ + TransactionOutput UTXO = item.getValue(); + total += UTXO.value; + inputs.add(new TransactionInput(UTXO.id)); + if (total > value) { break; } + } + + Transaction newTransaction = new Transaction(this.publicKey, recipient , value, inputs); + newTransaction.generateSignature(this.privateKey); + + for (TransactionInput input: inputs) { + NoobChainFinale.UTXOs.remove(input.transactionOutputId); } + return newTransaction; } } From 67e8ebf977ea637206fa2ac76f53ab19b7d6927d Mon Sep 17 00:00:00 2001 From: Gaby Wald <30340425+gabywald@users.noreply.github.com> Date: Wed, 13 Oct 2021 23:27:38 +0200 Subject: [PATCH 10/12] [blockchain]Review, corrections, Unit Tests --- .classpath | 1 + src/main/java/NoobChainTests.java | 127 ++++++++++++ .../gabywald/crypto/blockchain/Block.java | 23 ++- .../crypto/blockchain/BlockChain.java | 127 ++++++++++++ .../gabywald/crypto/blockchain/NoobChain.java | 64 ++---- .../crypto/blockchain/NoobChainFinale.java | 147 +++----------- .../crypto/blockchain/NoobChainTests.java | 192 ++++++++++++++++++ .../crypto/blockchain/StringUtils.java | 2 +- .../crypto/blockchain/Transaction.java | 72 +++++-- .../gabywald/crypto/blockchain/Wallet.java | 22 +- 10 files changed, 572 insertions(+), 205 deletions(-) create mode 100644 src/main/java/NoobChainTests.java create mode 100644 src/main/java/gabywald/crypto/blockchain/BlockChain.java create mode 100644 src/main/java/gabywald/crypto/blockchain/NoobChainTests.java diff --git a/.classpath b/.classpath index 1114cb5..3138ed5 100644 --- a/.classpath +++ b/.classpath @@ -29,5 +29,6 @@ + diff --git a/src/main/java/NoobChainTests.java b/src/main/java/NoobChainTests.java new file mode 100644 index 0000000..c396855 --- /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.hash); + Assertions.assertNotNull( genesisBlock.hash ); + + Block secondBlock = new Block("Yo im the second block", genesisBlock.hash); + Assertions.assertNotNull( secondBlock ); + System.out.println("Hash for block 2 : " + secondBlock.hash); + Assertions.assertNotNull( secondBlock.hash ); + + Block thirdBlock = new Block("Hey im the third block", secondBlock.hash); + Assertions.assertNotNull( thirdBlock ); + System.out.println("Hash for block 3 : " + thirdBlock.hash); + Assertions.assertNotNull( thirdBlock.hash ); + } + + /** + * 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).hash)); + blockchain.add(new Block("Hey im the third block", blockchain.get(blockchain.size()-1).hash)); + 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).hash)); + 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).hash)); + 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 index 59b4824..746a8ca 100644 --- a/src/main/java/gabywald/crypto/blockchain/Block.java +++ b/src/main/java/gabywald/crypto/blockchain/Block.java @@ -3,6 +3,7 @@ import java.util.ArrayList; import java.util.Date; import java.util.List; +import java.util.Map; /** * Block of BlockChain. @@ -14,9 +15,9 @@ public class Block { public String hash; public String previousHash; - /** our data will be a simple message. */ - private String data; - /** as number of milliseconds since 1/1/1970. */ + /** Our data will be a simple message. */ + // private String data; + /** As number of milliseconds since 1/1/1970. */ private long timeStamp; private int nonce; @@ -30,7 +31,7 @@ public class Block { * @param previousHash */ public Block(String data, String previousHash ) { - this.data = data; + // this.data = data; this.previousHash = previousHash; this.timeStamp = new Date().getTime(); // Making sure we do this after we set the other values. @@ -74,7 +75,7 @@ public void mineBlock(int difficulty) { while ( (this.hash == null) || ( ! this.hash.substring( 0, difficulty ).equals(target)) ) { this.nonce++; this.hash = this.calculateHash(); - // XXX NOTE if excpetion inside calculateHash : hash will be null ! + // XXX NOTE if exception inside calculateHash : hash will be null ! } System.out.println("Block Mined!!! : " + this.hash); } @@ -82,18 +83,22 @@ public void mineBlock(int difficulty) { /** * Add transactions to this block. * @param transaction + * @param UTXOs + * @param minimumTransaction * @return */ - public boolean addTransaction(Transaction transaction) { + 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 (previousHash != "0") { - if (transaction.processTransaction() != true) { + if (this.previousHash != "0") { + if (transaction.processTransaction(UTXOs, minimumTransaction) != true) { System.out.println("Transaction failed to process. Discarded."); return false; } } - transactions.add(transaction); + this.transactions.add(transaction); System.out.println("Transaction Successfully added to Block"); return true; } 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..0ff2869 --- /dev/null +++ b/src/main/java/gabywald/crypto/blockchain/BlockChain.java @@ -0,0 +1,127 @@ +package gabywald.crypto.blockchain; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; + +/** + * + * @author Gabriel Chandesris (2021) + */ +public abstract class BlockChain { + + public static List build() { + return new ArrayList(); + } + + public static Boolean isChainValidV1(List blockchain) { + Block currentBlock; + Block previousBlock; + + // 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.hash.equals(currentBlock.calculateHash()) ) { + System.out.println("Current Hashes not equal"); + return false; + } + // Compare previous hash and registered previous hash + if ( ! previousBlock.hash.equals(currentBlock.previousHash) ) { + System.out.println("Previous Hashes not equal"); + return false; + } + } + return true; + } + + public static boolean isChainValidV2(List blockchain, Transaction genesisTransaction, int difficulty) { + Block currentBlock = null; + Block previousBlock = null; + String hashTarget = new String(new char[difficulty]).replace('\0', '0'); + + // A temporary working list of unspent transactions at a given block state. + HashMap tempUTXOs = new HashMap(); + tempUTXOs.put(genesisTransaction.getOutputs().get(0).id, 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.hash.equals(currentBlock.calculateHash()) ) { + System.out.println("#Current Hashes not equal"); + return false; + } + + // Compare previous hash and registered previous hash + if ( ! previousBlock.hash.equals(currentBlock.previousHash) ) { + System.out.println("#Previous Hashes not equal"); + return false; + } + + // Check if hash is solved + if ( ! currentBlock.hash.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.transactions.size() ; t++) { + Transaction currentTransaction = currentBlock.transactions.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.transactionOutputId); + + if (tempOutput == null) { + System.out.println("#Referenced input on Transaction(" + t + ") is Missing"); + return false; + } + + if (input.UTXO.value != tempOutput.value) { + System.out.println("#Referenced input Transaction(" + t + ") value is Invalid"); + return false; + } + + tempUTXOs.remove(input.transactionOutputId); + } + + for (TransactionOutput output: currentTransaction.getOutputs()) { + tempUTXOs.put(output.id, output); + } + + if (currentTransaction.getOutputs().get(0).reciepient != currentTransaction.getRecipient()) { + System.out.println("#Transaction(" + t + ") output reciepient is not who it should be"); + return false; + } + if (currentTransaction.getOutputs().get(1).reciepient != 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/NoobChain.java b/src/main/java/gabywald/crypto/blockchain/NoobChain.java index e112a0e..cb7e90c 100644 --- a/src/main/java/gabywald/crypto/blockchain/NoobChain.java +++ b/src/main/java/gabywald/crypto/blockchain/NoobChain.java @@ -3,7 +3,6 @@ import java.security.Security; import java.util.List; import java.util.ArrayList; -import java.util.HashMap; // TODO replace GsonBuilder !! (build json from objet !) import com.google.gson.GsonBuilder; @@ -18,29 +17,22 @@ */ public class NoobChain { - public static List blockchain = new ArrayList(); - public static List blockchain2 = new ArrayList(); - public static List blockchain3 = new ArrayList(); + private static List blockchain = new ArrayList(); + private static List blockchain2 = new ArrayList(); - public static int difficulty = 3; + private static int difficulty = 3; - public static Wallet walletA; - public static Wallet walletB; + private static Wallet walletA; + private static Wallet walletB; - // List of all unspent transactions. - public static HashMap UTXOs = new HashMap(); - - public static float minimumTransaction = 0.1f; - public static Transaction genesisTransaction; - 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.hash); - Block secondBlock = new Block("Yo im the second block",genesisBlock.hash); + Block secondBlock = new Block("Yo im the second block", genesisBlock.hash); System.out.println("Hash for block 2 : " + secondBlock.hash); - Block thirdBlock = new Block("Hey im the third block",secondBlock.hash); + Block thirdBlock = new Block("Hey im the third block", secondBlock.hash); System.out.println("Hash for block 3 : " + thirdBlock.hash); @@ -66,7 +58,7 @@ public static void main(String[] args) { System.out.println("Trying to Mine block 3... "); NoobChain.blockchain2.get(2).mineBlock(NoobChain.difficulty); - System.out.println("\nBlockchain is Valid: " + NoobChain.isChainValid()); + System.out.println("\nBlockchain is Valid: " + BlockChain.isChainValidV1( NoobChain.blockchain2 )); String blockchain2Json = new GsonBuilder().setPrettyPrinting().create().toJson(NoobChain.blockchain2); System.out.println("\nThe block chain: "); @@ -77,46 +69,24 @@ public static void main(String[] args) { 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 - walletA = new Wallet(); - walletB = new Wallet(); - //Test public and private keys + // Create the new wallets + NoobChain.walletA = new Wallet(); + NoobChain.walletB = new Wallet(); + // 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)); - //Create a test transaction from WalletA to walletB + System.out.println(StringUtils.getStringFromKey(NoobChain.walletA.privateKey)); + System.out.println(StringUtils.getStringFromKey(NoobChain.walletA.publicKey)); + // Create a test transaction from WalletA to walletB Transaction transaction = new Transaction(NoobChain.walletA.publicKey, NoobChain.walletB.publicKey, 5, null); transaction.generateSignature(NoobChain.walletA.privateKey); - //Verify the signature works and verify it from the public key + // Verify the signature works and verify it from the public key System.out.println("Is signature verified"); - System.out.println(transaction.verifiySignature()); + System.out.println(transaction.verifySignature()); // ***** Part V // TODO next step of tutorials !! } - public static Boolean isChainValid() { - Block currentBlock; - Block previousBlock; - - // Loop through blockchain to check hashes: - for (int i = 1 ; i < NoobChain.blockchain2.size() ; i++) { - currentBlock = NoobChain.blockchain2.get(i); - previousBlock = NoobChain.blockchain2.get(i-1); - // Compare registered hash and calculated hash: - if ( ! currentBlock.hash.equals(currentBlock.calculateHash()) ) { - System.out.println("Current Hashes not equal"); - return false; - } - // Compare previous hash and registered previous hash - if ( ! previousBlock.hash.equals(currentBlock.previousHash) ) { - System.out.println("Previous Hashes not equal"); - return false; - } - } - return true; - } - /* Hash for block 1 : 9f98b14417f65bf7f55053d6ee948de264beaf4f4effdf1be1b80adb939a56bb Hash for block 2 : e7571024da930e1fee1dad719e0b8eeeefdf80c70a3a3739d83d9de1e23eced8 diff --git a/src/main/java/gabywald/crypto/blockchain/NoobChainFinale.java b/src/main/java/gabywald/crypto/blockchain/NoobChainFinale.java index 1b65dde..cbf8ef2 100644 --- a/src/main/java/gabywald/crypto/blockchain/NoobChainFinale.java +++ b/src/main/java/gabywald/crypto/blockchain/NoobChainFinale.java @@ -14,19 +14,19 @@ */ public class NoobChainFinale { - public static List blockchain = new ArrayList(); - public static Map UTXOs = new HashMap(); + private static List blockchain = new ArrayList(); + private static Map mapUTXOs = new HashMap(); - public static int difficulty = 3; - public static float minimumTransaction = 0.1f; - public static Wallet walletA; - public static Wallet walletB; - public static Transaction genesisTransaction; + 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 + // Setup Bouncey castle as a Security Provider Security.addProvider(new org.bouncycastle.jce.provider.BouncyCastleProvider()); // Create wallets: @@ -39,131 +39,46 @@ public static void main(String[] args) { // Manually sign the genesis transaction NoobChainFinale.genesisTransaction.generateSignature(coinbase.privateKey); // Manually set the transaction id - NoobChainFinale.genesisTransaction.transactionId = "0"; + NoobChainFinale.genesisTransaction.setTransactionId( "0" ); // Manually add the Transactions Output - NoobChainFinale.genesisTransaction.outputs.add(new TransactionOutput( NoobChainFinale.genesisTransaction.reciepient, - NoobChainFinale.genesisTransaction.value, - NoobChainFinale.genesisTransaction.transactionId)); + 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. - UTXOs.put(NoobChainFinale.genesisTransaction.outputs.get(0).id, NoobChainFinale.genesisTransaction.outputs.get(0)); + NoobChainFinale.mapUTXOs.put(NoobChainFinale.genesisTransaction.getOutputs().get(0).id, NoobChainFinale.genesisTransaction.getOutputs().get(0)); System.out.println("Creating and Mining Genesis block... "); Block genesis = new Block("0"); - genesis.addTransaction(NoobChainFinale.genesisTransaction); - NoobChainFinale.addBlock(genesis); + genesis.addTransaction(NoobChainFinale.genesisTransaction, NoobChainFinale.mapUTXOs, NoobChainFinale.minimumTransaction); + BlockChain.addBlock(NoobChainFinale.blockchain, genesis, NoobChainFinale.difficulty); // Testing Block block1 = new Block(genesis.hash); - System.out.println("\nWalletA's balance is: " + walletA.getBalance()); + 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(walletA.sendFunds(walletB.publicKey, 40f)); - NoobChainFinale.addBlock(block1); - System.out.println("\nWalletA's balance is: " + walletA.getBalance()); - System.out.println("WalletB's balance is: " + walletB.getBalance()); + 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.hash); System.out.println("\nWalletA Attempting to send more funds (1000) than it has..."); - block2.addTransaction(walletA.sendFunds(walletB.publicKey, 1000f)); - NoobChainFinale.addBlock(block2); - System.out.println("\nWalletA's balance is: " + walletA.getBalance()); - System.out.println("WalletB's balance is: " + walletB.getBalance()); + 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.hash); System.out.println("\nWalletB is Attempting to send funds (20) to WalletA..."); - block3.addTransaction(walletB.sendFunds( walletA.publicKey, 20)); - System.out.println("\nWalletA's balance is: " + walletA.getBalance()); - System.out.println("WalletB's balance is: " + walletB.getBalance()); + 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 )); - NoobChainFinale.isChainValid(); + BlockChain.isChainValidV2( NoobChainFinale.blockchain, NoobChainFinale.genesisTransaction, NoobChainFinale.difficulty ); } - public static Boolean isChainValid() { - Block currentBlock; - Block previousBlock; - String hashTarget = new String(new char[difficulty]).replace('\0', '0'); - - // A temporary working list of unspent transactions at a given block state. - HashMap tempUTXOs = new HashMap(); - tempUTXOs.put(genesisTransaction.outputs.get(0).id, genesisTransaction.outputs.get(0)); - - // Loop through blockchain to check hashes: - for (int i = 1 ; i < NoobChainFinale.blockchain.size() ; i++) { - - currentBlock = NoobChainFinale.blockchain.get(i); - previousBlock = NoobChainFinale.blockchain.get(i-1); - - // Compare registered hash and calculated hash: - if ( ! currentBlock.hash.equals(currentBlock.calculateHash()) ) { - System.out.println("#Current Hashes not equal"); - return false; - } - - // Compare previous hash and registered previous hash - if ( ! previousBlock.hash.equals(currentBlock.previousHash) ) { - System.out.println("#Previous Hashes not equal"); - return false; - } - - // Check if hash is solved - if ( ! currentBlock.hash.substring( 0, NoobChainFinale.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.transactions.size() ; t++) { - Transaction currentTransaction = currentBlock.transactions.get(t); - - if ( ! currentTransaction.verifiySignature()) { - 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.inputs) { - tempOutput = tempUTXOs.get(input.transactionOutputId); - - if (tempOutput == null) { - System.out.println("#Referenced input on Transaction(" + t + ") is Missing"); - return false; - } - - if (input.UTXO.value != tempOutput.value) { - System.out.println("#Referenced input Transaction(" + t + ") value is Invalid"); - return false; - } - - tempUTXOs.remove(input.transactionOutputId); - } - - for (TransactionOutput output: currentTransaction.outputs) { - tempUTXOs.put(output.id, output); - } - - if (currentTransaction.outputs.get(0).reciepient != currentTransaction.reciepient) { - System.out.println("#Transaction(" + t + ") output reciepient is not who it should be"); - return false; - } - if (currentTransaction.outputs.get(1).reciepient != currentTransaction.sender) { - System.out.println("#Transaction(" + t + ") output 'change' is not sender."); - return false; - } - - } - - } - System.out.println("Blockchain is valid"); - return true; - } - - public static void addBlock(Block newBlock) { - newBlock.mineBlock(difficulty); - NoobChainFinale.blockchain.add(newBlock); - } } 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..4fefa0d --- /dev/null +++ b/src/main/java/gabywald/crypto/blockchain/NoobChainTests.java @@ -0,0 +1,192 @@ +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.hash); + Assertions.assertNotNull( genesisBlock.hash ); + + Block secondBlock = new Block("Yo im the second block", genesisBlock.hash); + Assertions.assertNotNull( secondBlock ); + System.out.println("Hash for block 2 : " + secondBlock.hash); + Assertions.assertNotNull( secondBlock.hash ); + + Block thirdBlock = new Block("Hey im the third block", secondBlock.hash); + Assertions.assertNotNull( thirdBlock ); + System.out.println("Hash for block 3 : " + thirdBlock.hash); + Assertions.assertNotNull( thirdBlock.hash ); + } + + /** + * 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).hash)); + blockchain.add(new Block("Hey im the third block", blockchain.get(blockchain.size()-1).hash)); + 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).hash)); + 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).hash)); + 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); + // 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).id, 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.hash); + 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..."); + Assertions.assertTrue( + block1.addTransaction( walletA.sendFunds(walletB.publicKey, 40f, mapUTXOs), + mapUTXOs, minimumTransaction) ); + 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 )); + + Block block2 = new Block(block1.hash); + System.out.println("\nWalletA Attempting to send more funds (1000) than it has..."); + Assertions.assertTrue( + block2.addTransaction( walletA.sendFunds(walletB.publicKey, 1000f, mapUTXOs), + mapUTXOs, minimumTransaction) ); + 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 )); + + Block block3 = new Block(block2.hash); + System.out.println("\nWalletB is Attempting to send funds (20) to WalletA..."); + Assertions.assertTrue( + block3.addTransaction( walletB.sendFunds( walletA.publicKey, 20, mapUTXOs), + mapUTXOs, minimumTransaction) ); + System.out.println("\nWalletA's balance is: " + walletA.getBalance( mapUTXOs )); + System.out.println("WalletB's balance is: " + walletB.getBalance( mapUTXOs )); + + BlockChain.isChainValidV2( blockchain, genesisTransaction, difficulty ); + Assertions.assertTrue( BlockChain.isChainValidV2( blockchain, genesisTransaction, difficulty ) ); + } + +} diff --git a/src/main/java/gabywald/crypto/blockchain/StringUtils.java b/src/main/java/gabywald/crypto/blockchain/StringUtils.java index 27ec9f9..12b0e4f 100644 --- a/src/main/java/gabywald/crypto/blockchain/StringUtils.java +++ b/src/main/java/gabywald/crypto/blockchain/StringUtils.java @@ -134,7 +134,7 @@ public static String getMerkleRoot(List transactions) { int count = transactions.size(); List previousTreeLayer = new ArrayList(); for (Transaction transaction : transactions) { - previousTreeLayer.add(transaction.transactionId); + previousTreeLayer.add(transaction.getTransactionId()); } List treeLayer = previousTreeLayer; while (count > 1) { diff --git a/src/main/java/gabywald/crypto/blockchain/Transaction.java b/src/main/java/gabywald/crypto/blockchain/Transaction.java index f472fc8..fdd25bd 100644 --- a/src/main/java/gabywald/crypto/blockchain/Transaction.java +++ b/src/main/java/gabywald/crypto/blockchain/Transaction.java @@ -6,6 +6,7 @@ import java.security.Signature; import java.util.List; +import java.util.Map; import java.util.ArrayList; import java.util.Base64; @@ -17,24 +18,25 @@ */ public class Transaction { /** This is also the hash of the transaction. */ - public String transactionId; + private String transactionId; /** senders address/public key. */ - public PublicKey sender; + private PublicKey sender; /** Recipients address/public key. */ - public PublicKey reciepient; - public float value; + private PublicKey recipient; + private float value; /** This is to prevent anybody else from spending funds in our wallet. */ - public byte[] signature; + private byte[] signature; - public List inputs = new ArrayList(); - public List outputs = new ArrayList(); + private List inputs = new ArrayList(); + private List outputs = new ArrayList(); - private static int sequence = 0; // a rough count of how many transactions have been generated. + /** 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.reciepient = to; + this.recipient = to; this.value = value; this.inputs = inputs; } @@ -49,7 +51,7 @@ private String calculateHash() { try { return StringUtils.applySha256( StringUtils.getStringFromKey(this.sender) + - StringUtils.getStringFromKey(this.reciepient) + + StringUtils.getStringFromKey(this.recipient) + Float.toString(value) + Transaction.sequence ); } catch (BlockchainException e) { @@ -111,7 +113,7 @@ public static String getStringFromKey(Key key) { */ public void generateSignature(PrivateKey privateKey) { String data = StringUtils.getStringFromKey(this.sender) - + StringUtils.getStringFromKey(this.reciepient) + + StringUtils.getStringFromKey(this.recipient) + Float.toString(this.value); try { this.signature = StringUtils.applyECDSASig(privateKey,data); @@ -126,30 +128,33 @@ public void generateSignature(PrivateKey privateKey) { * Verifies the data we signed hasn't been tampered with. * @return */ - public boolean verifiySignature() { + public boolean verifySignature() { String data = StringUtils.getStringFromKey(this.sender) - + StringUtils.getStringFromKey(this.reciepient) + + StringUtils.getStringFromKey(this.recipient) + Float.toString(this.value); return StringUtils.verifyECDSASig(this.sender, data, this.signature); } /** - * Returns true if new transaction could be created. + * Returns true if new transaction could be created. + * @param UTXOs + * @param minimumTransaction * @return */ - public boolean processTransaction() { + public boolean processTransaction( final Map UTXOs, + final float minimumTransaction) { - if (verifiySignature() == false) { + if (this.verifySignature() == false) { System.out.println("#Transaction Signature failed to verify"); return false; } // Gather transaction inputs (Make sure they are unspent): - inputs.stream().forEach(ti -> ti.UTXO = NoobChain.UTXOs.get(ti.transactionOutputId) ); + this.inputs.stream().forEach(ti -> ti.UTXO = UTXOs.get(ti.transactionOutputId) ); // Check if transaction is valid: - if (this.getInputsValue() < NoobChain.minimumTransaction) { - System.out.println("#Transaction Inputs to small: " + getInputsValue()); + if (this.getInputsValue() < minimumTransaction) { + System.out.println("#Transaction Inputs to small: " + this.getInputsValue()); return false; } @@ -158,18 +163,18 @@ public boolean processTransaction() { // - Get value of inputs then the left over change: this.transactionId = this.calculateHash(); // - Send value to recipient - this.outputs.add(new TransactionOutput( this.reciepient, this.value, this.transactionId)); + 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 -> NoobChain.UTXOs.put(to.id , to) ); + this.outputs.stream().forEach(to -> UTXOs.put(to.id , to) ); // Remove transaction inputs from UTXO lists as spent: this.inputs.stream().forEach(ti -> { // If Transaction can't be found skip it if ( ti.UTXO != null) - { NoobChain.UTXOs.remove(ti.UTXO.id); } + { UTXOs.remove(ti.UTXO.id); } } ); return true; @@ -200,5 +205,28 @@ public float getOutputsValue() { } 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/Wallet.java b/src/main/java/gabywald/crypto/blockchain/Wallet.java index 43a57d4..1fa94b9 100644 --- a/src/main/java/gabywald/crypto/blockchain/Wallet.java +++ b/src/main/java/gabywald/crypto/blockchain/Wallet.java @@ -60,14 +60,14 @@ public void generateKeyPair() { * Returns balance and stores the UTXO's owned by this wallet in this.UTXOs * @return */ - public float getBalance() { + public float getBalance(final Map mapUTXOs) { float total = 0; - for (Map.Entry item: NoobChainFinale.UTXOs.entrySet()){ + for (Map.Entry item : mapUTXOs.entrySet()) { TransactionOutput UTXO = item.getValue(); - if (UTXO.isMine(publicKey)) { + if (UTXO.isMine(this.publicKey)) { // If output belongs to me ( if coins belong to me ) // Add it to our list of unspent transactions. - NoobChainFinale.UTXOs.put(UTXO.id,UTXO); + mapUTXOs.put(UTXO.id, UTXO); total += UTXO.value ; } } @@ -75,14 +75,15 @@ public float getBalance() { } /** - * Generates and returns a new transaction from this wallet. + * Generates and returns a new transaction from this wallet. * @param recipient * @param value + * @param mapUTXOs * @return */ - public Transaction sendFunds(PublicKey recipient, float value) { + public Transaction sendFunds(PublicKey recipient, float value, final Map mapUTXOs) { // Gather balance and check funds. - if (this.getBalance() < value) { + if (this.getBalance( mapUTXOs ) < value) { System.out.println("#Not Enough funds to send transaction. Transaction Discarded."); return null; } @@ -90,19 +91,20 @@ public Transaction sendFunds(PublicKey recipient, float value) { List inputs = new ArrayList(); float total = 0; - for (Map.Entry item: NoobChainFinale.UTXOs.entrySet()){ + for (Map.Entry item : mapUTXOs.entrySet()) { TransactionOutput UTXO = item.getValue(); total += UTXO.value; inputs.add(new TransactionInput(UTXO.id)); - if (total > value) { break; } + if (total > value) { break; } } Transaction newTransaction = new Transaction(this.publicKey, recipient , value, inputs); newTransaction.generateSignature(this.privateKey); for (TransactionInput input: inputs) { - NoobChainFinale.UTXOs.remove(input.transactionOutputId); + mapUTXOs.remove(input.transactionOutputId); } + return newTransaction; } From a04e139557319f2050c525015928396d26d030d8 Mon Sep 17 00:00:00 2001 From: Gaby Wald <30340425+gabywald@users.noreply.github.com> Date: Thu, 14 Oct 2021 18:23:40 +0200 Subject: [PATCH 11/12] [blockchain]Some Debug, finalizing Unit Tests, correction --- .../gabywald/crypto/blockchain/Block.java | 8 +++- .../crypto/blockchain/BlockChain.java | 25 ++++++++++-- .../crypto/blockchain/NoobChainFinale.java | 29 ++++++++++++++ .../crypto/blockchain/NoobChainTests.java | 32 +++++++++++----- .../crypto/blockchain/StringUtils.java | 14 +++---- .../crypto/blockchain/Transaction.java | 38 +++++++++++-------- .../gabywald/crypto/blockchain/Wallet.java | 7 ++-- 7 files changed, 110 insertions(+), 43 deletions(-) diff --git a/src/main/java/gabywald/crypto/blockchain/Block.java b/src/main/java/gabywald/crypto/blockchain/Block.java index 746a8ca..3e9a960 100644 --- a/src/main/java/gabywald/crypto/blockchain/Block.java +++ b/src/main/java/gabywald/crypto/blockchain/Block.java @@ -68,10 +68,14 @@ public String calculateHash() { 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.getDificultyString(difficulty); + String target = StringUtils.getDifficultyString(difficulty); while ( (this.hash == null) || ( ! this.hash.substring( 0, difficulty ).equals(target)) ) { this.nonce++; this.hash = this.calculateHash(); @@ -85,7 +89,7 @@ public void mineBlock(int difficulty) { * @param transaction * @param UTXOs * @param minimumTransaction - * @return + * @return (boolean) */ public boolean addTransaction( final Transaction transaction, final Map UTXOs, diff --git a/src/main/java/gabywald/crypto/blockchain/BlockChain.java b/src/main/java/gabywald/crypto/blockchain/BlockChain.java index 0ff2869..fcc00f1 100644 --- a/src/main/java/gabywald/crypto/blockchain/BlockChain.java +++ b/src/main/java/gabywald/crypto/blockchain/BlockChain.java @@ -10,13 +10,22 @@ */ public abstract class BlockChain { + /** + * Build a list of Blocks. + * @return + */ public static List build() { return new ArrayList(); } - public static Boolean isChainValidV1(List blockchain) { - Block currentBlock; - Block previousBlock; + /** + * + * @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++) { @@ -36,10 +45,17 @@ public static Boolean isChainValidV1(List blockchain) { 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 = new String(new char[difficulty]).replace('\0', '0'); + String hashTarget = StringUtils.getDifficultyString(difficulty); // A temporary working list of unspent transactions at a given block state. HashMap tempUTXOs = new HashMap(); @@ -108,6 +124,7 @@ public static boolean isChainValidV2(List blockchain, Transaction genesis System.out.println("#Transaction(" + t + ") output reciepient is not who it should be"); return false; } + if (currentTransaction.getOutputs().get(1).reciepient != currentTransaction.getSender()) { System.out.println("#Transaction(" + t + ") output 'change' is not sender."); return false; diff --git a/src/main/java/gabywald/crypto/blockchain/NoobChainFinale.java b/src/main/java/gabywald/crypto/blockchain/NoobChainFinale.java index cbf8ef2..237eaf9 100644 --- a/src/main/java/gabywald/crypto/blockchain/NoobChainFinale.java +++ b/src/main/java/gabywald/crypto/blockchain/NoobChainFinale.java @@ -80,5 +80,34 @@ public static void main(String[] args) { 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/NoobChainTests.java b/src/main/java/gabywald/crypto/blockchain/NoobChainTests.java index 4fefa0d..1ae4884 100644 --- a/src/main/java/gabywald/crypto/blockchain/NoobChainTests.java +++ b/src/main/java/gabywald/crypto/blockchain/NoobChainTests.java @@ -134,6 +134,7 @@ void testPart05() { // 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 @@ -141,11 +142,10 @@ void testPart05() { // Manually add the Transactions Output genesisTransaction.getOutputs().add(new TransactionOutput( genesisTransaction.getRecipient(), genesisTransaction.getValue(), - genesisTransaction.getTransactionId())); + genesisTransaction.getTransactionId() )); // Its important to store our first transaction in the UTXOs list. mapUTXOs.put(genesisTransaction.getOutputs().get(0).id, genesisTransaction.getOutputs().get(0)); - System.out.println("Creating and Mining Genesis block... "); Block genesis = new Block("0"); genesis.addTransaction(genesisTransaction, mapUTXOs, minimumTransaction); @@ -158,24 +158,33 @@ void testPart05() { Assertions.assertEquals(100, walletA.getBalance( mapUTXOs )); System.out.println("\nWalletA is Attempting to send funds (40) to WalletB..."); - Assertions.assertTrue( - block1.addTransaction( walletA.sendFunds(walletB.publicKey, 40f, mapUTXOs), - mapUTXOs, minimumTransaction) ); + 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.hash); System.out.println("\nWalletA Attempting to send more funds (1000) than it has..."); - Assertions.assertTrue( - block2.addTransaction( walletA.sendFunds(walletB.publicKey, 1000f, mapUTXOs), - mapUTXOs, minimumTransaction) ); + 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.hash); System.out.println("\nWalletB is Attempting to send funds (20) to WalletA..."); @@ -184,9 +193,12 @@ void testPart05() { mapUTXOs, minimumTransaction) ); 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 )); - BlockChain.isChainValidV2( blockchain, genesisTransaction, difficulty ); - Assertions.assertTrue( BlockChain.isChainValidV2( blockchain, genesisTransaction, difficulty ) ); + 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 index 12b0e4f..5505109 100644 --- a/src/main/java/gabywald/crypto/blockchain/StringUtils.java +++ b/src/main/java/gabywald/crypto/blockchain/StringUtils.java @@ -22,12 +22,12 @@ *
https://github.com/CryptoKass/NoobChain-Tutorial-Part-2 * @author Gabriel Chandesris (2021) */ -public class StringUtils { +public abstract class StringUtils { /** * Applies Sha256 to a string and returns the result. * @param input - * @return + * @return (String) * @throws BlockchainException * TODO upgrade / improve this method (applySha256) to return null value if any exception inside ! */ @@ -66,7 +66,7 @@ public static String applySha256(String input) * Applies ECDSA Signature and returns the result ( as bytes ). * @param privateKey * @param input - * @return + * @return (byte[]) * @throws NoSuchProviderException * @throws NoSuchAlgorithmException * @throws InvalidKeyException @@ -96,7 +96,7 @@ public static byte[] applyECDSASig(PrivateKey privateKey, String input) * @param publicKey * @param data * @param signature - * @return + * @return (boolean) */ public static boolean verifyECDSASig(PublicKey publicKey, String data, byte[] signature) { try { @@ -119,16 +119,16 @@ public static String getStringFromKey(Key key) { /** * Returns difficulty string target, to compare to hash. eg difficulty of 5 will return "00000" * @param difficulty - * @return + * @return (String) */ - public static String getDificultyString(int difficulty) { + 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 + * @return (String) */ public static String getMerkleRoot(List transactions) { int count = transactions.size(); diff --git a/src/main/java/gabywald/crypto/blockchain/Transaction.java b/src/main/java/gabywald/crypto/blockchain/Transaction.java index fdd25bd..7594fa6 100644 --- a/src/main/java/gabywald/crypto/blockchain/Transaction.java +++ b/src/main/java/gabywald/crypto/blockchain/Transaction.java @@ -137,12 +137,12 @@ public boolean verifySignature() { /** * Returns true if new transaction could be created. - * @param UTXOs + * @param mapUTXOs * @param minimumTransaction * @return */ - public boolean processTransaction( final Map UTXOs, - final float minimumTransaction) { + public boolean processTransaction( final Map mapUTXOs, + final float minimumTransaction) { if (this.verifySignature() == false) { System.out.println("#Transaction Signature failed to verify"); @@ -150,7 +150,10 @@ public boolean processTransaction( final Map UTXOs, } // Gather transaction inputs (Make sure they are unspent): - this.inputs.stream().forEach(ti -> ti.UTXO = UTXOs.get(ti.transactionOutputId) ); + // this.inputs.stream().forEach(ti -> ti.UTXO = UTXOs.get(ti.transactionOutputId) ); + for (TransactionInput it : inputs) { + it.UTXO = mapUTXOs.get(it.transactionOutputId); + } // Check if transaction is valid: if (this.getInputsValue() < minimumTransaction) { @@ -168,13 +171,16 @@ public boolean processTransaction( final Map UTXOs, this.outputs.add(new TransactionOutput( this.sender, leftOver, this.transactionId)); // Add outputs to Unspent list - this.outputs.stream().forEach(to -> UTXOs.put(to.id , to) ); + // this.outputs.stream().forEach(to -> mapUTXOs.put(to.id , to) ); + for (TransactionOutput o : outputs) { + mapUTXOs.put(o.id , o); + } // Remove transaction inputs from UTXO lists as spent: this.inputs.stream().forEach(ti -> { // If Transaction can't be found skip it if ( ti.UTXO != null) - { UTXOs.remove(ti.UTXO.id); } + { mapUTXOs.remove(ti.UTXO.id); } } ); return true; @@ -205,28 +211,28 @@ public float getOutputsValue() { } return total; } - - + + public void setTransactionId(String transactionId) - { this.transactionId = transactionId; } + { this.transactionId = transactionId; } public String getTransactionId() - { return this.transactionId; } + { return this.transactionId; } public PublicKey getSender() - { return this.sender; } + { return this.sender; } public PublicKey getRecipient() - { return this.recipient; } + { return this.recipient; } public float getValue() - { return this.value; } + { return this.value; } public List getOutputs() - { return this.outputs; } + { return this.outputs; } public List getInputs() - { return this.inputs; } - + { return this.inputs; } + } diff --git a/src/main/java/gabywald/crypto/blockchain/Wallet.java b/src/main/java/gabywald/crypto/blockchain/Wallet.java index 1fa94b9..610adaf 100644 --- a/src/main/java/gabywald/crypto/blockchain/Wallet.java +++ b/src/main/java/gabywald/crypto/blockchain/Wallet.java @@ -79,7 +79,7 @@ public float getBalance(final Map mapUTXOs) { * @param recipient * @param value * @param mapUTXOs - * @return + * @return (Transaction) */ public Transaction sendFunds(PublicKey recipient, float value, final Map mapUTXOs) { // Gather balance and check funds. @@ -101,9 +101,8 @@ public Transaction sendFunds(PublicKey recipient, float value, final Map Date: Thu, 14 Oct 2021 19:02:37 +0200 Subject: [PATCH 12/12] [blockchain]Some More review (data access) --- src/main/java/NoobChainTests.java | 24 ++++----- .../gabywald/crypto/blockchain/Block.java | 24 +++++++-- .../crypto/blockchain/BlockChain.java | 28 +++++------ .../crypto/blockchain/NoobChainFinale.java | 9 ++-- ...obChain.java => NoobChainIntegration.java} | 50 +++++++++---------- .../crypto/blockchain/NoobChainTests.java | 38 +++++++------- .../crypto/blockchain/Transaction.java | 32 ++++++------ .../crypto/blockchain/TransactionInput.java | 13 ++++- .../crypto/blockchain/TransactionOutput.java | 26 +++++++--- .../gabywald/crypto/blockchain/Wallet.java | 16 +++--- 10 files changed, 149 insertions(+), 111 deletions(-) rename src/main/java/gabywald/crypto/blockchain/{NoobChain.java => NoobChainIntegration.java} (70%) diff --git a/src/main/java/NoobChainTests.java b/src/main/java/NoobChainTests.java index c396855..3139ee4 100644 --- a/src/main/java/NoobChainTests.java +++ b/src/main/java/NoobChainTests.java @@ -28,18 +28,18 @@ class NoobChainTests { void testPart01() { Block genesisBlock = new Block("Hi im the first block", "0"); Assertions.assertNotNull( genesisBlock ); - System.out.println("Hash for block 1 : " + genesisBlock.hash); - Assertions.assertNotNull( genesisBlock.hash ); + System.out.println("Hash for block 1 : " + genesisBlock.getHash() ); + Assertions.assertNotNull( genesisBlock.getHash() ); - Block secondBlock = new Block("Yo im the second block", genesisBlock.hash); + Block secondBlock = new Block("Yo im the second block", genesisBlock.getHash() ); Assertions.assertNotNull( secondBlock ); - System.out.println("Hash for block 2 : " + secondBlock.hash); - Assertions.assertNotNull( secondBlock.hash ); + System.out.println("Hash for block 2 : " + secondBlock.getHash() ); + Assertions.assertNotNull( secondBlock.getHash() ); - Block thirdBlock = new Block("Hey im the third block", secondBlock.hash); + Block thirdBlock = new Block("Hey im the third block", secondBlock.getHash() ); Assertions.assertNotNull( thirdBlock ); - System.out.println("Hash for block 3 : " + thirdBlock.hash); - Assertions.assertNotNull( thirdBlock.hash ); + System.out.println("Hash for block 3 : " + thirdBlock.getHash() ); + Assertions.assertNotNull( thirdBlock.getHash() ); } /** @@ -50,8 +50,8 @@ 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).hash)); - blockchain.add(new Block("Hey im the third block", blockchain.get(blockchain.size()-1).hash)); + 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 ); @@ -67,11 +67,11 @@ void testPart03() { 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).hash)); + 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).hash)); + 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); diff --git a/src/main/java/gabywald/crypto/blockchain/Block.java b/src/main/java/gabywald/crypto/blockchain/Block.java index 3e9a960..71dee6e 100644 --- a/src/main/java/gabywald/crypto/blockchain/Block.java +++ b/src/main/java/gabywald/crypto/blockchain/Block.java @@ -13,17 +13,17 @@ */ public class Block { - public String hash; - public String previousHash; + 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; - public String merkleRoot; - /** Our data will be a simple message. */ - public List transactions = new ArrayList(); + private String merkleRoot; + + private List transactions = new ArrayList(); /** * Block Constructor. @@ -106,4 +106,18 @@ public boolean addTransaction( final Transaction 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 index fcc00f1..0166905 100644 --- a/src/main/java/gabywald/crypto/blockchain/BlockChain.java +++ b/src/main/java/gabywald/crypto/blockchain/BlockChain.java @@ -32,12 +32,12 @@ public static boolean isChainValidV1(List blockchain) { currentBlock = blockchain.get(i); previousBlock = blockchain.get(i-1); // Compare registered hash and calculated hash: - if ( ! currentBlock.hash.equals(currentBlock.calculateHash()) ) { + if ( ! currentBlock.getHash().equals(currentBlock.calculateHash()) ) { System.out.println("Current Hashes not equal"); return false; } // Compare previous hash and registered previous hash - if ( ! previousBlock.hash.equals(currentBlock.previousHash) ) { + if ( ! previousBlock.getHash().equals(currentBlock.getPreviousHash() ) ) { System.out.println("Previous Hashes not equal"); return false; } @@ -59,7 +59,7 @@ public static boolean isChainValidV2(List blockchain, Transaction genesis // A temporary working list of unspent transactions at a given block state. HashMap tempUTXOs = new HashMap(); - tempUTXOs.put(genesisTransaction.getOutputs().get(0).id, genesisTransaction.getOutputs().get(0)); + 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++) { @@ -68,27 +68,27 @@ public static boolean isChainValidV2(List blockchain, Transaction genesis previousBlock = blockchain.get(i-1); // Compare registered hash and calculated hash: - if ( ! currentBlock.hash.equals(currentBlock.calculateHash()) ) { + if ( ! currentBlock.getHash().equals(currentBlock.calculateHash()) ) { System.out.println("#Current Hashes not equal"); return false; } // Compare previous hash and registered previous hash - if ( ! previousBlock.hash.equals(currentBlock.previousHash) ) { + if ( ! previousBlock.getHash().equals(currentBlock.getPreviousHash() ) ) { System.out.println("#Previous Hashes not equal"); return false; } // Check if hash is solved - if ( ! currentBlock.hash.substring( 0, difficulty).equals(hashTarget)) { + 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.transactions.size() ; t++) { - Transaction currentTransaction = currentBlock.transactions.get(t); + 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"); @@ -101,31 +101,31 @@ public static boolean isChainValidV2(List blockchain, Transaction genesis } for (TransactionInput input: currentTransaction.getInputs()) { - tempOutput = tempUTXOs.get(input.transactionOutputId); + tempOutput = tempUTXOs.get(input.getTransactionOutputId() ); if (tempOutput == null) { System.out.println("#Referenced input on Transaction(" + t + ") is Missing"); return false; } - if (input.UTXO.value != tempOutput.value) { + if (input.getTransactionOutput().getValue() != tempOutput.getValue()) { System.out.println("#Referenced input Transaction(" + t + ") value is Invalid"); return false; } - tempUTXOs.remove(input.transactionOutputId); + tempUTXOs.remove(input.getTransactionOutputId() ); } for (TransactionOutput output: currentTransaction.getOutputs()) { - tempUTXOs.put(output.id, output); + tempUTXOs.put(output.getId(), output); } - if (currentTransaction.getOutputs().get(0).reciepient != currentTransaction.getRecipient()) { + 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).reciepient != currentTransaction.getSender()) { + if (currentTransaction.getOutputs().get(1).getRecipient() != currentTransaction.getSender()) { System.out.println("#Transaction(" + t + ") output 'change' is not sender."); return false; } diff --git a/src/main/java/gabywald/crypto/blockchain/NoobChainFinale.java b/src/main/java/gabywald/crypto/blockchain/NoobChainFinale.java index 237eaf9..2502fd3 100644 --- a/src/main/java/gabywald/crypto/blockchain/NoobChainFinale.java +++ b/src/main/java/gabywald/crypto/blockchain/NoobChainFinale.java @@ -46,7 +46,8 @@ public static void main(String[] args) { NoobChainFinale.genesisTransaction.getTransactionId())); // Its important to store our first transaction in the UTXOs list. - NoobChainFinale.mapUTXOs.put(NoobChainFinale.genesisTransaction.getOutputs().get(0).id, NoobChainFinale.genesisTransaction.getOutputs().get(0)); + 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"); @@ -54,7 +55,7 @@ public static void main(String[] args) { BlockChain.addBlock(NoobChainFinale.blockchain, genesis, NoobChainFinale.difficulty); // Testing - Block block1 = new Block(genesis.hash); + 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), @@ -63,7 +64,7 @@ public static void main(String[] args) { 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.hash); + 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); @@ -71,7 +72,7 @@ public static void main(String[] args) { 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.hash); + 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); diff --git a/src/main/java/gabywald/crypto/blockchain/NoobChain.java b/src/main/java/gabywald/crypto/blockchain/NoobChainIntegration.java similarity index 70% rename from src/main/java/gabywald/crypto/blockchain/NoobChain.java rename to src/main/java/gabywald/crypto/blockchain/NoobChainIntegration.java index cb7e90c..8a7709f 100644 --- a/src/main/java/gabywald/crypto/blockchain/NoobChain.java +++ b/src/main/java/gabywald/crypto/blockchain/NoobChainIntegration.java @@ -15,7 +15,7 @@ *
https://github.com/CryptoKass/NoobChain-Tutorial-Part-2 * @author Gabriel Chandesris (2021) */ -public class NoobChain { +public class NoobChainIntegration { private static List blockchain = new ArrayList(); private static List blockchain2 = new ArrayList(); @@ -29,38 +29,38 @@ 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.hash); - Block secondBlock = new Block("Yo im the second block", genesisBlock.hash); - System.out.println("Hash for block 2 : " + secondBlock.hash); - Block thirdBlock = new Block("Hey im the third block", secondBlock.hash); - System.out.println("Hash for block 3 : " + thirdBlock.hash); + 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: - NoobChain.blockchain.add(new Block("Hi im the first block", "0")); - NoobChain.blockchain.add(new Block("Yo im the second block", NoobChain.blockchain.get(NoobChain.blockchain.size()-1).hash)); - NoobChain.blockchain.add(new Block("Hey im the third block", NoobChain.blockchain.get(NoobChain.blockchain.size()-1).hash)); - String blockchainJson = new GsonBuilder().setPrettyPrinting().create().toJson(NoobChain.blockchain); + 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: - NoobChain.blockchain2.add(new Block("Hi im the first block", "0")); + NoobChainIntegration.blockchain2.add(new Block("Hi im the first block", "0")); System.out.println("Trying to Mine block 1... "); - NoobChain.blockchain2.get(0).mineBlock(NoobChain.difficulty); + NoobChainIntegration.blockchain2.get(0).mineBlock(NoobChainIntegration.difficulty); - NoobChain.blockchain2.add(new Block("Yo im the second block", NoobChain.blockchain2.get(NoobChain.blockchain2.size()-1).hash)); + 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... "); - NoobChain.blockchain2.get(1).mineBlock(NoobChain.difficulty); + NoobChainIntegration.blockchain2.get(1).mineBlock(NoobChainIntegration.difficulty); - NoobChain.blockchain2.add(new Block("Hey im the third block", NoobChain.blockchain2.get(NoobChain.blockchain2.size()-1).hash)); + 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... "); - NoobChain.blockchain2.get(2).mineBlock(NoobChain.difficulty); + NoobChainIntegration.blockchain2.get(2).mineBlock(NoobChainIntegration.difficulty); - System.out.println("\nBlockchain is Valid: " + BlockChain.isChainValidV1( NoobChain.blockchain2 )); + System.out.println("\nBlockchain is Valid: " + BlockChain.isChainValidV1( NoobChainIntegration.blockchain2 )); - String blockchain2Json = new GsonBuilder().setPrettyPrinting().create().toJson(NoobChain.blockchain2); + String blockchain2Json = new GsonBuilder().setPrettyPrinting().create().toJson(NoobChainIntegration.blockchain2); System.out.println("\nThe block chain: "); System.out.println(blockchain2Json); @@ -70,21 +70,21 @@ public static void main(String[] args) { // 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 - NoobChain.walletA = new Wallet(); - NoobChain.walletB = new Wallet(); + 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(NoobChain.walletA.privateKey)); - System.out.println(StringUtils.getStringFromKey(NoobChain.walletA.publicKey)); + 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(NoobChain.walletA.publicKey, NoobChain.walletB.publicKey, 5, null); - transaction.generateSignature(NoobChain.walletA.privateKey); + 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 - // TODO next step of tutorials !! + // XXX NOTE see NoobChainFinale and NoobChainTests !! } /* diff --git a/src/main/java/gabywald/crypto/blockchain/NoobChainTests.java b/src/main/java/gabywald/crypto/blockchain/NoobChainTests.java index 1ae4884..2281eb9 100644 --- a/src/main/java/gabywald/crypto/blockchain/NoobChainTests.java +++ b/src/main/java/gabywald/crypto/blockchain/NoobChainTests.java @@ -23,18 +23,18 @@ class NoobChainTests { void testPart01() { Block genesisBlock = new Block("Hi im the first block", "0"); Assertions.assertNotNull( genesisBlock ); - System.out.println("Hash for block 1 : " + genesisBlock.hash); - Assertions.assertNotNull( genesisBlock.hash ); + System.out.println("Hash for block 1 : " + genesisBlock.getHash() ); + Assertions.assertNotNull( genesisBlock.getHash() ); - Block secondBlock = new Block("Yo im the second block", genesisBlock.hash); + Block secondBlock = new Block("Yo im the second block", genesisBlock.getHash() ); Assertions.assertNotNull( secondBlock ); - System.out.println("Hash for block 2 : " + secondBlock.hash); - Assertions.assertNotNull( secondBlock.hash ); + System.out.println("Hash for block 2 : " + secondBlock.getHash() ); + Assertions.assertNotNull( secondBlock.getHash() ); - Block thirdBlock = new Block("Hey im the third block", secondBlock.hash); + Block thirdBlock = new Block("Hey im the third block", secondBlock.getHash() ); Assertions.assertNotNull( thirdBlock ); - System.out.println("Hash for block 3 : " + thirdBlock.hash); - Assertions.assertNotNull( thirdBlock.hash ); + System.out.println("Hash for block 3 : " + thirdBlock.getHash() ); + Assertions.assertNotNull( thirdBlock.getHash() ); } /** @@ -45,8 +45,8 @@ 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).hash)); - blockchain.add(new Block("Hey im the third block", blockchain.get(blockchain.size()-1).hash)); + 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 ); @@ -62,11 +62,11 @@ void testPart03() { 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).hash)); + 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).hash)); + 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); @@ -145,14 +145,14 @@ void testPart05() { genesisTransaction.getTransactionId() )); // Its important to store our first transaction in the UTXOs list. - mapUTXOs.put(genesisTransaction.getOutputs().get(0).id, genesisTransaction.getOutputs().get(0)); + 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.hash); + Block block1 = new Block(genesis.getHash() ); System.out.println("\nWalletA's balance is: " + walletA.getBalance( mapUTXOs )); Assertions.assertEquals(100, walletA.getBalance( mapUTXOs )); @@ -171,7 +171,7 @@ void testPart05() { boolean isChainValid = BlockChain.isChainValidV2( blockchain, genesisTransaction, difficulty ); Assertions.assertTrue( isChainValid ); - Block block2 = new Block(block1.hash); + 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); @@ -186,11 +186,11 @@ void testPart05() { isChainValid = BlockChain.isChainValidV2( blockchain, genesisTransaction, difficulty ); Assertions.assertTrue( isChainValid ); - Block block3 = new Block(block2.hash); + Block block3 = new Block(block2.getHash() ); System.out.println("\nWalletB is Attempting to send funds (20) to WalletA..."); - Assertions.assertTrue( - block3.addTransaction( walletB.sendFunds( walletA.publicKey, 20, mapUTXOs), - mapUTXOs, minimumTransaction) ); + 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 )); diff --git a/src/main/java/gabywald/crypto/blockchain/Transaction.java b/src/main/java/gabywald/crypto/blockchain/Transaction.java index 7594fa6..86f7096 100644 --- a/src/main/java/gabywald/crypto/blockchain/Transaction.java +++ b/src/main/java/gabywald/crypto/blockchain/Transaction.java @@ -142,7 +142,7 @@ public boolean verifySignature() { * @return */ public boolean processTransaction( final Map mapUTXOs, - final float minimumTransaction) { + final float minimumTransaction) { if (this.verifySignature() == false) { System.out.println("#Transaction Signature failed to verify"); @@ -152,7 +152,7 @@ public boolean processTransaction( final Map mapUTXOs // Gather transaction inputs (Make sure they are unspent): // this.inputs.stream().forEach(ti -> ti.UTXO = UTXOs.get(ti.transactionOutputId) ); for (TransactionInput it : inputs) { - it.UTXO = mapUTXOs.get(it.transactionOutputId); + it.setTransactionOutput( mapUTXOs.get(it.getTransactionOutputId() ) ); } // Check if transaction is valid: @@ -172,15 +172,15 @@ public boolean processTransaction( final Map mapUTXOs // Add outputs to Unspent list // this.outputs.stream().forEach(to -> mapUTXOs.put(to.id , to) ); - for (TransactionOutput o : outputs) { - mapUTXOs.put(o.id , o); + 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.UTXO != null) - { mapUTXOs.remove(ti.UTXO.id); } + if ( ti.getTransactionOutput() != null) + { mapUTXOs.remove(ti.getTransactionOutput().getId()); } } ); return true; @@ -194,8 +194,8 @@ public float getInputsValue() { float total = 0; for (TransactionInput ti : inputs) { // If Transaction can't be found skip it - if ( ti.UTXO == null) { continue; } - total += ti.UTXO.value; + if ( ti.getTransactionOutput() == null) { continue; } + total += ti.getTransactionOutput().getValue(); } return total; } @@ -207,7 +207,7 @@ public float getInputsValue() { public float getOutputsValue() { float total = 0; for (TransactionOutput to : outputs) { - total += to.value; + total += to.getValue(); } return total; } @@ -215,24 +215,24 @@ public float getOutputsValue() { public void setTransactionId(String transactionId) - { this.transactionId = transactionId; } + { this.transactionId = transactionId; } public String getTransactionId() - { return this.transactionId; } + { return this.transactionId; } public PublicKey getSender() - { return this.sender; } + { return this.sender; } public PublicKey getRecipient() - { return this.recipient; } + { return this.recipient; } public float getValue() - { return this.value; } + { return this.value; } public List getOutputs() - { return this.outputs; } + { return this.outputs; } public List getInputs() - { return this.inputs; } + { return this.inputs; } } diff --git a/src/main/java/gabywald/crypto/blockchain/TransactionInput.java b/src/main/java/gabywald/crypto/blockchain/TransactionInput.java index 269c0be..d95d24e 100644 --- a/src/main/java/gabywald/crypto/blockchain/TransactionInput.java +++ b/src/main/java/gabywald/crypto/blockchain/TransactionInput.java @@ -8,11 +8,20 @@ */ public class TransactionInput { /** Reference to TransactionOutputs -> transactionId */ - public String transactionOutputId; + private String transactionOutputId = null; /** Contains the Unspent transaction output */ - public TransactionOutput UTXO; + 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 index 02271b6..d345b03 100644 --- a/src/main/java/gabywald/crypto/blockchain/TransactionOutput.java +++ b/src/main/java/gabywald/crypto/blockchain/TransactionOutput.java @@ -9,13 +9,13 @@ * @author Gabriel Chandesris (2021) */ public class TransactionOutput { - public String id; + private String id; /** Also known as the new owner of these coins. */ - public PublicKey reciepient; + private PublicKey recipient; /** The amount of coins they own */ - public float value; + private float value; /** The id of the transaction this output was created in */ - public String parentTransactionId; + private String parentTransactionId; /** * Constructor @@ -24,7 +24,7 @@ public class TransactionOutput { * @param parentTransactionId */ public TransactionOutput(PublicKey reciepient, float value, String parentTransactionId) { - this.reciepient = reciepient; + this.recipient = reciepient; this.value = value; this.parentTransactionId = parentTransactionId; try { @@ -44,8 +44,22 @@ public TransactionOutput(PublicKey reciepient, float value, String parentTransac * @return */ public boolean isMine(PublicKey publicKey) { - return this.reciepient.equals(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 index 610adaf..4c0f6e3 100644 --- a/src/main/java/gabywald/crypto/blockchain/Wallet.java +++ b/src/main/java/gabywald/crypto/blockchain/Wallet.java @@ -63,12 +63,12 @@ public void generateKeyPair() { public float getBalance(final Map mapUTXOs) { float total = 0; for (Map.Entry item : mapUTXOs.entrySet()) { - TransactionOutput UTXO = item.getValue(); - if (UTXO.isMine(this.publicKey)) { + 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(UTXO.id, UTXO); - total += UTXO.value ; + mapUTXOs.put(to.getId(), to); + total += to.getValue() ; } } return total; @@ -92,9 +92,9 @@ public Transaction sendFunds(PublicKey recipient, float value, final Map item : mapUTXOs.entrySet()) { - TransactionOutput UTXO = item.getValue(); - total += UTXO.value; - inputs.add(new TransactionInput(UTXO.id)); + TransactionOutput to = item.getValue(); + total += to.getValue(); + inputs.add(new TransactionInput(to.getId())); if (total > value) { break; } } @@ -102,7 +102,7 @@ public Transaction sendFunds(PublicKey recipient, float value, final Map