Machine Coding Problem

Basic Blockchain

maco60macoAllinfrastructurehashingchain-validation
Commonly Asked By:CoinbaseBinanceRipple

Functional Scope (In-Scope)

  • Immutability Chain Linking: Each block stores the preceding block's cryptographic hash, forming an append-only verifiable chain.
  • Proof-of-Work (PoW) Mining: Increments nonces repeatedly to resolve complex hashing constraints matching the target difficulty.
  • Digital Signatures & Transactions: Cryptographically signs transactions to support ownership validation.
  • Fork Resolution (Nakamoto Consensus): Resolves discrepancies by evaluating competitive chains and selecting the longest valid chain.

Explicit Boundaries (Out-of-Scope)

  • Peer-to-Peer Networking: Omits full gossiping network transport protocols (gRPC, TCP sockets).
  • Advanced Smart Contracts: Excludes executing EVM bytecode or transaction-based decentralized state machines.

Clean reference designs demonstrating basic proof-of-work blockchain in Java and Python:

// ─── JAVA BLUEPRINT ──────────────────────────────────────────────────────────
import java.nio.charset.StandardCharsets;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.*;
import java.util.concurrent.locks.ReentrantReadWriteLock;

class Transaction {
    private final String sender;
    private final String recipient;
    private final double amount;
    private final String signature;
    private final long timestamp;

    public Transaction(String sender, String recipient, double amount, String signature) {
        this.sender = sender;
        this.recipient = recipient;
        this.amount = amount;
        this.signature = signature;
        this.timestamp = System.currentTimeMillis();
    }

    public String getSender() { return sender; }
    public String getRecipient() { return recipient; }
    public double getAmount() { return amount; }
    public String getSignature() { return signature; }
    public long getTimestamp() { return timestamp; }

    public String hashValue() {
        try {
            String data = sender + ":" + recipient + ":" + amount + ":" + signature + ":" + timestamp;
            MessageDigest digest = MessageDigest.getInstance("SHA-256");
            byte[] hashBytes = digest.digest(data.getBytes(StandardCharsets.UTF_8));
            StringBuilder sb = new StringBuilder();
            for (byte b : hashBytes) {
                sb.append(String.format("%02x", b));
            }
            return sb.toString();
        } catch (NoSuchAlgorithmException e) {
            throw new RuntimeException(e);
        }
    }

    public boolean verifySignature() {
        // Simplified cryptographic verification simulation
        if (sender.equals("GENESIS")) return true;
        return signature != null && !signature.isEmpty() && signature.startsWith("sig_");
    }

    @Override
    public String toString() {
        return sender + "->" + recipient + ":" + amount + "[" + signature + "]";
    }
}

class Block {
    private final int index;
    private final long timestamp;
    private final List<Transaction> transactions;
    private final String previousHash;
    private String hash;
    private int nonce;

    public Block(int index, List<Transaction> transactions, String previousHash) {
        this.index = index;
        this.timestamp = System.currentTimeMillis();
        this.transactions = new ArrayList<>(transactions);
        this.previousHash = previousHash;
        this.nonce = 0;
        this.hash = calculateHash();
    }

    public Block(int index, long timestamp, List<Transaction> transactions, String previousHash, int nonce, String hash) {
        this.index = index;
        this.timestamp = timestamp;
        this.transactions = new ArrayList<>(transactions);
        this.previousHash = previousHash;
        this.nonce = nonce;
        this.hash = hash;
    }

    public String calculateHash() {
        try {
            StringBuilder txData = new StringBuilder();
            for (Transaction tx : transactions) {
                txData.append(tx.hashValue());
            }
            String data = index + ":" + timestamp + ":" + txData.toString() + ":" + previousHash + ":" + nonce;
            MessageDigest digest = MessageDigest.getInstance("SHA-256");
            byte[] hashBytes = digest.digest(data.getBytes(StandardCharsets.UTF_8));
            StringBuilder sb = new StringBuilder();
            for (byte b : hashBytes) {
                sb.append(String.format("%02x", b));
            }
            return sb.toString();
        } catch (NoSuchAlgorithmException e) {
            throw new RuntimeException("SHA-256 algorithm not found", e);
        }
    }

    public void mineBlock(int difficulty) {
        String target = new String(new char[difficulty]).replace('\0', '0');
        while (!hash.substring(0, difficulty).equals(target)) {
            nonce++;
            hash = calculateHash();
        }
    }

    public int getIndex() { return index; }
    public long getTimestamp() { return timestamp; }
    public List<Transaction> getTransactions() { return new ArrayList<>(transactions); }
    public String getPreviousHash() { return previousHash; }
    public String getHash() { return hash; }
    public int getNonce() { return nonce; }
    
    // For deliberate tampering testing
    public void tamperTransaction(int index, Transaction tamperedTx) {
        this.transactions.set(index, tamperedTx);
    }
}

class Blockchain {
    private List<Block> chain;
    private final int difficulty;
    private final ReentrantReadWriteLock rwLock = new ReentrantReadWriteLock();

    public Blockchain(int difficulty) {
        this.chain = new ArrayList<>();
        this.difficulty = difficulty;
        // Genesis block
        Block genesis = new Block(0, new ArrayList<>(), "0");
        genesis.mineBlock(difficulty);
        chain.add(genesis);
    }

    public Block getLatestBlock() {
        rwLock.readLock().lock();
        try {
            return chain.get(chain.size() - 1);
        } finally {
            rwLock.readLock().unlock();
        }
    }

    public void addBlock(Block newBlock) {
        rwLock.writeLock().lock();
        try {
            newBlock.mineBlock(difficulty);
            chain.add(newBlock);
        } finally {
            rwLock.writeLock().unlock();
        }
    }

    public boolean isChainValid() {
        rwLock.readLock().lock();
        try {
            return isValidChain(this.chain, this.difficulty);
        } finally {
            rwLock.readLock().unlock();
        }
    }

    public static boolean isValidChain(List<Block> chainToValidate, int difficulty) {
        String target = new String(new char[difficulty]).replace('\0', '0');

        for (int i = 1; i < chainToValidate.size(); i++) {
            Block currentBlock = chainToValidate.get(i);
            Block previousBlock = chainToValidate.get(i - 1);

            // 1. Verify self-hash integrity
            if (!currentBlock.getHash().equals(currentBlock.calculateHash())) {
                System.out.println("[VALIDATION FAILURE] Block " + currentBlock.getIndex() + " hash has been modified.");
                return false;
            }

            // 2. Verify chain linkage
            if (!currentBlock.getPreviousHash().equals(previousBlock.getHash())) {
                System.out.println("[VALIDATION FAILURE] Block " + currentBlock.getIndex() + " previous hash mismatch.");
                return false;
            }

            // 3. Verify Proof-of-Work difficulty prefix
            if (!currentBlock.getHash().substring(0, difficulty).equals(target)) {
                System.out.println("[VALIDATION FAILURE] Block " + currentBlock.getIndex() + " does not meet Proof of Work target.");
                return false;
            }

            // 4. Verify transactions inside the block
            for (Transaction tx : currentBlock.getTransactions()) {
                if (!tx.verifySignature()) {
                    System.out.println("[VALIDATION FAILURE] Invalid transaction signature detected in Block " + currentBlock.getIndex());
                    return false;
                }
            }
        }
        return true;
    }

    public boolean resolveFork(List<Block> newChain) {
        rwLock.writeLock().lock();
        try {
            if (newChain.size() > this.chain.size() && isValidChain(newChain, this.difficulty)) {
                System.out.println("[CONSENSUS] Fork resolved! Accepted longer chain of length " + newChain.size());
                this.chain = new ArrayList<>(newChain);
                return true;
            }
            System.out.println("[CONSENSUS] Rejected incoming fork. Local chain is equal/longer or invalid.");
            return false;
        } finally {
            rwLock.writeLock().unlock();
        }
    }

    public List<Block> getChain() {
        rwLock.readLock().lock();
        try {
            return new ArrayList<>(chain);
        } finally {
            rwLock.readLock().unlock();
        }
    }
}

public class Main {
    public static void main(String[] args) {
        System.out.println("=== INITIALIZING BLOCKCHAIN SIMULATOR (DIFFICULTY = 2) ===");
        Blockchain mainChain = new Blockchain(2);

        System.out.println("\n=== 1. MINING INITIAL TRANSACTIONS ===");
        List<Transaction> block1Tx = Arrays.asList(
            new Transaction("Alice", "Bob", 50.0, "sig_alice_123"),
            new Transaction("Bob", "Charlie", 25.0, "sig_bob_456")
        );
        Block block1 = new Block(1, block1Tx, mainChain.getLatestBlock().getHash());
        mainChain.addBlock(block1);
        System.out.println("Mined Block 1: " + block1.getHash());

        List<Transaction> block2Tx = Arrays.asList(
            new Transaction("Charlie", "David", 10.0, "sig_charlie_789")
        );
        Block block2 = new Block(2, block2Tx, mainChain.getLatestBlock().getHash());
        mainChain.addBlock(block2);
        System.out.println("Mined Block 2: " + block2.getHash());
        System.out.println("Blockchain length: " + mainChain.getChain().size() + " | Is Valid: " + mainChain.isChainValid());

        System.out.println("\n=== 2. SIMULATING A LONGER VALID FORK (Nakamoto Consensus) ===");
        // Duplicate the chain state up to block 1 to simulate a fork starting from block 1
        List<Block> forkChain = new ArrayList<>();
        forkChain.add(mainChain.getChain().get(0));
        forkChain.add(mainChain.getChain().get(1));

        // Create alternative Block 2 and 3 on the fork chain
        List<Transaction> forkBlock2Tx = Arrays.asList(
            new Transaction("Bob", "Eve", 40.0, "sig_bob_eve_fork")
        );
        Block forkBlock2 = new Block(2, forkBlock2Tx, forkChain.get(forkChain.size() - 1).getHash());
        forkBlock2.mineBlock(2);
        forkChain.add(forkBlock2);

        List<Transaction> forkBlock3Tx = Arrays.asList(
            new Transaction("Eve", "Alice", 15.0, "sig_eve_alice_fork")
        );
        Block forkBlock3 = new Block(3, forkBlock3Tx, forkChain.get(forkChain.size() - 1).getHash());
        forkBlock3.mineBlock(2);
        forkChain.add(forkBlock3);

        System.out.println("Attempting fork resolution with fork chain length " + forkChain.size());
        boolean resolved = mainChain.resolveFork(forkChain);
        System.out.println("Fork resolution status: " + resolved);
        System.out.println("New main blockchain length: " + mainChain.getChain().size());

        System.out.println("\n=== 3. TAMPER DETECTION SYSTEM ===");
        System.out.println("Deliberately tampering block 1 with illegal transaction...");
        Block hackedBlock = mainChain.getChain().get(1);
        Transaction hackedTx = new Transaction("Alice", "Hacker", 9999.0, "sig_alice_stolen");
        hackedBlock.tamperTransaction(0, hackedTx);

        System.out.println("Checking blockchain validity after tampering...");
        boolean isValid = mainChain.isChainValid();
        System.out.println("Validity Status: " + isValid + " (Expected: false)");
    }
}