Simple Blockchain Implementation


Introduction

We have implemented a node that is part of a blockchain-based decentralized consensus algorithm. The program is designed to receive transactions and blocks as input and maintain an updated blockchain. This is one of the programming assignments from Princeton University’s course, Bitcoin and Cryptocurrency Technologies.”

Blockchain Structure

Since multiple forks may exist in a blockchain, blocks form a tree rather than a list.
Additionally, we must maintain a UTXO pool corresponding to every block that can be extended by a newly created block.

Implementation Assumptions

The following are the assumptions made for this implementation:

  • Since the entire blockchain can grow to an enormous size, only the latest blocks will be maintained. The number of stored blocks can be freely determined as long as all API functions are implemented.
  • A new Genesis block will not be mined. If a block claiming to be a Genesis block (i.e., with a NULL hash parent) is received, the addBlock(Block b) method of the BlockChain class should return false.
  • If there are multiple blocks at the same height, the getMaxHeightBlock() method should return the oldest block.
  • For simplicity, we assume that a coinbase transaction from a block can be used in the next block mined on top of it. This differs from the actual Bitcoin protocol, where a maturity period of 100 confirmations is required before usage.
  • The blockchain maintains only a single global transaction pool. When a transaction is received, it is added to this pool. When a new block is received or created, transactions from this pool are removed accordingly. Blockchain reorganizations (i.e., when a forked chain becomes the new longest chain) may cause some transactions to be discarded. Specifically, transactions that were in the original main branch (and removed from the transaction pool) but do not exist in the new forked branch may be lost.
  • The coinbase reward is fixed at 25 coins¹.
  • When a node receives a new block, it only needs to verify whether the transactions form a valid set. The set does not have to be the maximum valid set of transactions. Additionally, Proof of Work (PoW) verification is not required.

1 The coinbase transaction refers to the mining reward. In Bitcoin, the reward started at 25 BTC, halves approximately every 4 years, and as of Feb 2025, it is 3.125 BTC. The final Bitcoin block is expected to be mined around the year 2140.

Class Specifications

The following table summarizes the classes used in the implementation.

FileDescription
Block.javaThe Block class stores block data structures. It includes fields for the block hash, previous block hash, and transaction data.
BlockChain.javaThe BlockChain class manages the blockchain. It includes fields for the blockchain, the highest block node, and the transaction pool. The internal BlockNode class contains a block, parent node, child nodes, block height, and a UTXO pool for mining new blocks.
BlockHandler.javaThe BlockHandler class processes newly received blocks, creates new blocks, and processes received transactions using the BlockChain class.
ByteArrayWrapper.javaThe ByteArrayWrapper class is a wrapper for byte arrays to be used as keys in a HashMap. Refers to the TransactionPool class.
Crypto.javaThe Crypto class includes the verifySignature() method to check the validity of digital signatures.
Transaction.javaSimilar to the Transaction class in the “Implementation of Scrooge Coin Transactions,” but with added functionality to create coinbase transactions. The Block class constructor implements coinbase transaction creation.
TransactionPool.javaThe TransactionPool class implements the transaction pool required for creating new blocks.
TxHandler.javaThe TxHandler class from “Implementation of Scrooge Coin Transactions” with an added getUTXOPool() method.
UTXO.javaIdentical to the UTXO class from “Implementation of Scrooge Coin Transactions.”
UTXOPool.javaIdentical to the UTXOPool class from “Implementation of Scrooge Coin Transactions.”
Table 1: Class Specifications (Overview)

Implementation of the BlockChain Class

The BlockChain class is implemented as follows. For clarity, we have used pseudocode to describe the flow of processing.

public class BlockChain {

    private class BlockNode {
        
        Declares fields b, parent, children, height, uPool;
        
        public BlockNode(Block b, BlockNode parent, UTXOPool uPool) {
            Assign b to the b field of own instance;
            Assign parent to the parent field of own instance;
            Create a children instance;
            Assign uPool to the uPool field of own instance;
            
            if (!parent is null) {
                Assign parent.height + 1 to the height;
                Add own instance to the end of parent.children;
            } else {
                Assign 1 to the height;
            }

        }

        public UTXOPool getUTXOPoolCopy() {
            return new instance with the argument value uPool;
        }
    }

    Declares fields blockChain, maxHeightNode, txPool;
    
    Initializes the class constant CUT_OFF_AGE is 10;

    /** create an empty block chain with just a genesis block. Assume {@code genesisBlock} is a valid block */
    public BlockChain(Block genesisBlock) {    
        Create a blockChain instance;
        Create a utxoPool instance;
        Add a coinbase transaction to the UTXO pool;
        Initializes the field genesisNode with the argument values genesisBlock, null utxoPool;
        Store the pair of genesisBlock hash and genesisNode in the blockchain;
        Create a txPool instance;        
        Assign genesisNode to the maxHeightNode;
    }

    /** Get the maximum height block */
    public Block getMaxHeightBlock() {
        return the block of maxHeightNode;
    }

    /** Get the UTXOPool for mining a new block on top of max height block */
    public UTXOPool getMaxHeightUTXOPool() {
        return the UTXO pool of maxHeightNode;
    }

    /** Get the transaction pool to mine a new block */
    public TransactionPool getTransactionPool() {
        return the transaction pool;
    }

    /** Add {@code block} to the block chain if it is valid */
    /** Return true if block is successfully added */
    public boolean addBlock(Block block) {
        Initializes the prevBlockHash is getPrevBlockHash() of block;

        if (prevBlockHash is null)
            return false;

        Initializes the field parentBlockNode is BlockNode corresponding to the prevBlockHash of blockChain;

        if (parentBlockNode is null) {
            return false;
        }

        /** Transaction validity check */
        Initializes the field handler with the argument value UTXO pool of parentBlockNode;
        Initializes the field txs is the transaction of block;
        Initializes the field validTxs is the valid transaction set in txs;

        if (!validTxs.length is txs.length) {
            return false;
        }

        /** Block validity check*/
        Initializes the field proposedHeight is parentBlockNode.height + 1;         

        if (proposedHeight <= maxHeightNode.height - CUT_OFF_AGE) {
            return false;
        }

        Initializes the field utxoPool is the UTXO pool of handler;
        Add a coinbase transaction to the UTXO pool;
        Initializes the field node with the argument values block, parentBlockNode, utxoPool;
        Store the pair of block hash and node in the blockchain;

        if (proposedHeight > maxHeightNode.height) {
            Assign node to the maxHeightNode;
        }

        return true;
    }

    /** Add a transaction to the transaction pool */
    public void addTransaction(Transaction tx) {
        Add a tx to the transaction pool;
    }

}


Leave a Reply

Your email address will not be published. Required fields are marked *