Blockchain in Java

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

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:

  1. Hash Target: Creates a string of zeros (hashTarget) based on the difficulty level. This is used to validate the mining process.
  2. 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.
  3. Return: If all checks pass, returns true, indicating the blockchain is valid. Otherwise, prints an error message and returns false.

main(String[] args)

The entry point for the application:

  1. Security Provider Setup: Adds the BouncyCastle security provider, which provides cryptographic operations.
  2. Create Wallets: Initializes walletA and walletB, which are used for transactions.
  3. Display Keys: Prints the private and public keys of walletA.
  4. Create and Test Transaction:
    • Transaction Creation: Creates a new Transaction from walletA to walletB, 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.

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.

References

  1. https://medium.com/programmers-blockchain/creating-your-first-blockchain-with-java-part-2-transactions-2cdac335e0ce
Subscribe
Notify of
guest
0 Comments
Newest
Oldest Most Voted
Inline Feedbacks
View all comments