Blockchain in Java
This class implements a basic blockchain in Java, incorporating fundamental blockchain concepts such as block validation, transactions, and wallet management.

Simple implementation blockchain implemented in Java on Linux.
Clone catalog GitHub repo
git clone git@github.com:karol-preiskorn/java-examples.git src/main/java/blockchain/
Setup environment
We need Gradle to test and run. Simple method to install. Try use https://sdkman.io/usage SDKMAN! (my favorite)
curl -s "https://get.sdkman.io" | bash source "$HOME/.sdkman/bin/sdkman-init.sh"
Install Java & Gradle
# list all version sdk list java * # choice one sdk install java 13.0.1.j9-adpt # list all version gradle sdk list gradle * # choice 6.* because we will test by jupiter sdk install gradle 6.0.1
Minimum theory
We create simple list.
Every next element is linked by hash key with body block
Source @github
package blockchain; import java.util.ArrayList; import java.util.Date; public class Block { public String hash; public String previousHash; public String merkleRoot; public ArrayList<Transaction> transactions = new ArrayList<Transaction>(); //our data will be a simple message. public long timeStamp; //as number of milliseconds since 1/1/1970. public int nonce; //Block Constructor. public Block(String previousHash) { this.previousHash = previousHash; this.timeStamp = new Date().getTime(); this.hash = calculateHash(); //Making sure we do this after we set the other values. } //Calculate new hash based on blocks contents public String calculateHash() { return StringUtil.applySha256( previousHash + Long.toString(timeStamp) + Integer.toString(nonce) + merkleRoot ); } //Increases nonce value until hash target is reached. public void mineBlock(int difficulty) { merkleRoot = StringUtil.getMerkleRoot(transactions); String target = StringUtil.getDificultyString(difficulty); //Create a string with difficulty * "0" while (!hash.substring(0, difficulty).equals(target)) { nonce++; hash = calculateHash(); } System.out.println("Block Mined!!! : " + hash); } //Add transactions to this block public boolean addTransaction(Transaction transaction) { //process transaction and check if valid, unless block is genesis block then ignore. if (transaction == null) return false; if ((!"0".equals(previousHash))) { 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; } }
String Utils
package blockchain; import java.security.*; import java.util.ArrayList; import java.util.Base64; import com.google.gson.GsonBuilder; import java.util.List; 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); } } //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("ECDSA", "BC"); 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("ECDSA", "BC"); ecdsaVerify.initVerify(publicKey); ecdsaVerify.update(data.getBytes()); return ecdsaVerify.verify(signature); } catch (Exception e) { throw new RuntimeException(e); } } //Short hand helper to turn Object into a json string public static String getJson(Object o) { return new GsonBuilder().setPrettyPrinting().create().toJson(o); } //Returns difficulty string target, to compare to hash. eg difficulty of 5 will return "00000" public static String getDificultyString(int difficulty) { return new String(new char[difficulty]).replace('\0', '0'); } public static String getStringFromKey(Key key) { return Base64.getEncoder().encodeToString(key.getEncoded()); } public static String getMerkleRoot(ArrayList<Transaction> transactions) { int count = transactions.size(); List<String> previousTreeLayer = new ArrayList<String>(); for (Transaction transaction : transactions) { previousTreeLayer.add(transaction.transactionId); } List<String> treeLayer = previousTreeLayer; while (count > 1) { treeLayer = new ArrayList<String>(); for (int i = 1; i < previousTreeLayer.size(); i += 2) { 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; } }
Wallet
package blockchain; import java.security.*; import java.security.spec.ECGenParameterSpec; import java.util.ArrayList; import java.util.HashMap; import java.util.Map; public class Wallet { public PrivateKey privateKey; public PublicKey publicKey; public HashMap<String, TransactionOutput> UTXOs = new HashMap<String, TransactionOutput>(); public Wallet() { generateKeyPair(); } public void generateKeyPair() { try { KeyPairGenerator keyGen = KeyPairGenerator.getInstance("ECDSA", "BC"); SecureRandom random = SecureRandom.getInstance("SHA1PRNG"); ECGenParameterSpec ecSpec = new ECGenParameterSpec("prime192v1"); // Initialize the key generator and generate a KeyPair keyGen.initialize(ecSpec, random); //256 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); } } public float getBalance() { float total = 0; for (Map.Entry<String, TransactionOutput> item : NoobChain.UTXOs.entrySet()) { TransactionOutput UTXO = item.getValue(); if (UTXO.isMine(publicKey)) { //if output belongs to me ( if coins belong to me ) UTXOs.put(UTXO.id, UTXO); //add it to our list of unspent transactions. total += UTXO.value; } } return total; } public Transaction sendFunds(PublicKey _recipient, float value) { if (getBalance() < value) { System.out.println("#Not Enough funds to send transaction. Transaction Discarded."); return null; } ArrayList<TransactionInput> inputs = new ArrayList<TransactionInput>(); float total = 0; for (Map.Entry<String, TransactionOutput> item : UTXOs.entrySet()) { TransactionOutput UTXO = item.getValue(); total += UTXO.value; inputs.add(new TransactionInput(UTXO.id)); if (total > value) break; } Transaction newTransaction = new Transaction(publicKey, _recipient, value, inputs); newTransaction.generateSignature(privateKey); for (TransactionInput input : inputs) { UTXOs.remove(input.transactionOutputId); } return newTransaction; } }
package blockchain; 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; } }
package blockchain; import java.security.PublicKey; 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 = StringUtil .applySha256(StringUtil.getStringFromKey(reciepient) + value + parentTransactionId); } // Check if coin belongs to you public boolean isMine(PublicKey publicKey) { return (publicKey == reciepient); } }
Noby chain v2
package blockchain; import java.security.Security; import java.util.ArrayList; public class NoobChainV2 { public static ArrayList<Block> blockchain = new ArrayList<Block>(); public static int difficulty = 5; public static Wallet walletA; public static Wallet walletB; /** * @return Boolean */ public static Boolean isChainValid() { Block currentBlock; Block previousBlock; String hashTarget = new String(new char[difficulty]).replace('\0', '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; } } return true; } /** * Noob blockchain. * * @param args no arguments */ public static void main(String[] args) { // Setup Bouncey castle as a Security Provider Security.addProvider(new org.bouncycastle.jce.provider.BouncyCastleProvider()); // Create the new wallets walletA = new Wallet(); walletB = new Wallet(); // Test public and private keys System.out.println("Private key: " + StringUtil.getStringFromKey(walletA.privateKey)); System.out.println("Public key: " + StringUtil.getStringFromKey(walletA.publicKey)); // Create a test transaction from WalletA to walletB Transaction transaction = new Transaction(walletA.publicKey, walletB.publicKey, 5, null); transaction.generateSignature(walletA.privateKey); // Verify the signature works and verify it from the public key System.out.println("Is signature verified: " + transaction.verifiySignature()); } }
Here’s an explanation of the NoobChainV2
class in the provided code. This class implements a basic blockchain in Java, incorporating fundamental blockchain concepts such as block validation, transactions, and wallet management.
Class Overview
Imports
java.security.Security
: Manages security providers.java.util.ArrayList
: Used for storing the blockchain as a list of blocks.
Class Variables
public static ArrayList<Block> blockchain
: This is a list that stores all the blocks in the blockchain.public static int difficulty
: The difficulty level of the mining process, which determines the number of leading zeros required in the block’s hash.public static Wallet walletA
: Represents a wallet for storing and managing cryptocurrency.public static Wallet walletB
: Represents another wallet.
Key Methods
isChainValid()
This method checks if the blockchain is valid:
- Hash Target: Creates a string of zeros (
hashTarget
) based on the difficulty level. This is used to validate the mining process. - Loop Through Blockchain: Iterates through the blockchain starting from the second block (index 1):
- Current and Previous Blocks: Retrieves the current and previous blocks.
- Hash Comparison: Compares the hash of the current block with its calculated hash to ensure no tampering has occurred.
- Previous Hash Comparison: Ensures the current block’s
previousHash
matches the hash of the previous block. - Mining Check: Verifies if the block’s hash meets the required difficulty level by checking the leading zeros.
- Return: If all checks pass, returns
true
, indicating the blockchain is valid. Otherwise, prints an error message and returnsfalse
.
main(String[] args)
The entry point for the application:
- Security Provider Setup: Adds the BouncyCastle security provider, which provides cryptographic operations.
- Create Wallets: Initializes
walletA
andwalletB
, which are used for transactions. - Display Keys: Prints the private and public keys of
walletA
. - Create and Test Transaction:
- Transaction Creation: Creates a new
Transaction
fromwalletA
towalletB
, with an amount of 5. - Generate Signature: Signs the transaction using
walletA
‘s private key. - Verify Signature: Checks if the signature is valid and prints the result.
- Transaction Creation: Creates a new
Explanation of Blockchain Elements
- Block: Represents a single block in the blockchain. It includes attributes like
hash
,previousHash
, and methods to calculate its hash. - Wallet: Manages cryptographic keys used for signing transactions. Each wallet has a public key (for receiving funds) and a private key (for signing transactions).
- Transaction: Represents a transfer of value between wallets. It includes methods to generate and verify cryptographic signatures.
Summary
The NoobChainV2
class demonstrates fundamental blockchain concepts and operations:
- Blockchain Integrity: Ensures the blockchain remains tamper-proof through hash and chain validation.
- Cryptographic Wallets: Manages keys for secure transactions.
- Transactions: Handles and verifies cryptocurrency transactions.
This class serves as a simple, illustrative example of a blockchain, showing how blocks are validated and how basic cryptographic operations are performed in a blockchain system.