├── README.md ├── LICENSE ├── assignment-2-consensus-from-trust └── CompliantNode.java ├── assignment-1-scroogecoin ├── TxHandler.java ├── MaxFeeTxHandler.java └── MaxFeeTxHandlerRecurse.java └── assignment-3-blockchain └── BlockChain.java /README.md: -------------------------------------------------------------------------------- 1 | # coursera-cryptocurrency 2 | Assignments from the Coursera course "Bitcoin and Cryptocurrency Technologies" 3 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2016 Michael Silbermann 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /assignment-2-consensus-from-trust/CompliantNode.java: -------------------------------------------------------------------------------- 1 | import java.util.HashSet; 2 | import java.util.Set; 3 | 4 | import static java.util.stream.Collectors.toSet; 5 | 6 | /* CompliantNode refers to a node that follows the rules (not malicious)*/ 7 | public class CompliantNode implements Node { 8 | 9 | private double p_graph; 10 | private double p_malicious; 11 | private double p_tXDistribution; 12 | private int numRounds; 13 | 14 | private boolean[] followees; 15 | 16 | private Set pendingTransactions; 17 | 18 | private boolean[] blackListed; 19 | 20 | public CompliantNode(double p_graph, double p_malicious, double p_txDistribution, int numRounds) { 21 | this.p_graph = p_graph; 22 | this.p_malicious = p_malicious; 23 | this.p_tXDistribution = p_txDistribution; 24 | this.numRounds = numRounds; 25 | } 26 | 27 | public void setFollowees(boolean[] followees) { 28 | this.followees = followees; 29 | this.blackListed = new boolean[followees.length]; 30 | } 31 | 32 | public void setPendingTransaction(Set pendingTransactions) { 33 | this.pendingTransactions = pendingTransactions; 34 | } 35 | 36 | public Set sendToFollowers() { 37 | Set toSend = new HashSet<>(pendingTransactions); 38 | pendingTransactions.clear(); 39 | return toSend; 40 | } 41 | 42 | public void receiveFromFollowees(Set candidates) { 43 | Set senders = candidates.stream().map(c -> c.sender).collect(toSet()); 44 | for (int i = 0; i < followees.length; i++) { 45 | if (followees[i] && !senders.contains(i)) 46 | blackListed[i] = true; 47 | } 48 | for (Candidate c : candidates) { 49 | if (!blackListed[c.sender]) { 50 | pendingTransactions.add(c.tx); 51 | } 52 | } 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /assignment-1-scroogecoin/TxHandler.java: -------------------------------------------------------------------------------- 1 | import java.util.Arrays; 2 | import java.util.HashSet; 3 | import java.util.Set; 4 | 5 | public class TxHandler { 6 | 7 | private UTXOPool utxoPool; 8 | 9 | /** 10 | * Creates a public ledger whose current UTXOPool (collection of unspent transaction outputs) is 11 | * {@code utxoPool}. This should make a copy of utxoPool by using the UTXOPool(UTXOPool uPool) 12 | * constructor. 13 | */ 14 | public TxHandler(UTXOPool utxoPool) { 15 | this.utxoPool = new UTXOPool(utxoPool); 16 | } 17 | 18 | /** 19 | * @return true if: 20 | * (1) all outputs claimed by {@code tx} are in the current UTXO pool, 21 | * (2) the signatures on each input of {@code tx} are valid, 22 | * (3) no UTXO is claimed multiple times by {@code tx}, 23 | * (4) all of {@code tx}s output values are non-negative, and 24 | * (5) the sum of {@code tx}s input values is greater than or equal to the sum of its output 25 | * values; and false otherwise. 26 | */ 27 | public boolean isValidTx(Transaction tx) { 28 | UTXOPool uniqueUtxos = new UTXOPool(); 29 | double previousTxOutSum = 0; 30 | double currentTxOutSum = 0; 31 | for (int i = 0; i < tx.numInputs(); i++) { 32 | Transaction.Input in = tx.getInput(i); 33 | UTXO utxo = new UTXO(in.prevTxHash, in.outputIndex); 34 | Transaction.Output output = utxoPool.getTxOutput(utxo); 35 | if (!utxoPool.contains(utxo)) return false; 36 | if (!Crypto.verifySignature(output.address, tx.getRawDataToSign(i), in.signature)) 37 | return false; 38 | if (uniqueUtxos.contains(utxo)) return false; 39 | uniqueUtxos.addUTXO(utxo, output); 40 | previousTxOutSum += output.value; 41 | } 42 | for (Transaction.Output out : tx.getOutputs()) { 43 | if (out.value < 0) return false; 44 | currentTxOutSum += out.value; 45 | } 46 | return previousTxOutSum >= currentTxOutSum; 47 | } 48 | 49 | /** 50 | * Handles each epoch by receiving an unordered array of proposed transactions, checking each 51 | * transaction for correctness, returning a mutually valid array of accepted transactions, and 52 | * updating the current UTXO pool as appropriate. 53 | */ 54 | public Transaction[] handleTxs(Transaction[] possibleTxs) { 55 | 56 | Set validTxs = new HashSet<>(); 57 | 58 | for (Transaction tx : possibleTxs) { 59 | if (isValidTx(tx)) { 60 | validTxs.add(tx); 61 | for (Transaction.Input in : tx.getInputs()) { 62 | UTXO utxo = new UTXO(in.prevTxHash, in.outputIndex); 63 | utxoPool.removeUTXO(utxo); 64 | } 65 | for (int i = 0; i < tx.numOutputs(); i++) { 66 | Transaction.Output out = tx.getOutput(i); 67 | UTXO utxo = new UTXO(tx.getHash(), i); 68 | utxoPool.addUTXO(utxo, out); 69 | } 70 | } 71 | } 72 | 73 | Transaction[] validTxArray = new Transaction[validTxs.size()]; 74 | return validTxs.toArray(validTxArray); 75 | } 76 | } 77 | -------------------------------------------------------------------------------- /assignment-1-scroogecoin/MaxFeeTxHandler.java: -------------------------------------------------------------------------------- 1 | import java.util.Collections; 2 | import java.util.HashSet; 3 | import java.util.Set; 4 | import java.util.TreeSet; 5 | 6 | public class MaxFeeTxHandler { 7 | 8 | private UTXOPool utxoPool; 9 | 10 | /** 11 | * Creates a public ledger whose current UTXOPool (collection of unspent transaction outputs) is 12 | * {@code utxoPool}. This should make a copy of utxoPool by using the UTXOPool(UTXOPool uPool) 13 | * constructor. 14 | */ 15 | public MaxFeeTxHandler(UTXOPool utxoPool) { 16 | this.utxoPool = new UTXOPool(utxoPool); 17 | } 18 | 19 | /** 20 | * @return true if: 21 | * (1) all outputs claimed by {@code tx} are in the current UTXO pool, 22 | * (2) the signatures on each input of {@code tx} are valid, 23 | * (3) no UTXO is claimed multiple times by {@code tx}, 24 | * (4) all of {@code tx}s output values are non-negative, and 25 | * (5) the sum of {@code tx}s input values is greater than or equal to the sum of its output 26 | * values; and false otherwise. 27 | */ 28 | public boolean isValidTx(Transaction tx) { 29 | UTXOPool uniqueUtxos = new UTXOPool(); 30 | double previousTxOutSum = 0; 31 | double currentTxOutSum = 0; 32 | for (int i = 0; i < tx.numInputs(); i++) { 33 | Transaction.Input in = tx.getInput(i); 34 | UTXO utxo = new UTXO(in.prevTxHash, in.outputIndex); 35 | Transaction.Output output = utxoPool.getTxOutput(utxo); 36 | if (!utxoPool.contains(utxo)) return false; 37 | if (!Crypto.verifySignature(output.address, tx.getRawDataToSign(i), in.signature)) 38 | return false; 39 | if (uniqueUtxos.contains(utxo)) return false; 40 | uniqueUtxos.addUTXO(utxo, output); 41 | previousTxOutSum += output.value; 42 | } 43 | for (Transaction.Output out : tx.getOutputs()) { 44 | if (out.value < 0) return false; 45 | currentTxOutSum += out.value; 46 | } 47 | return previousTxOutSum >= currentTxOutSum; 48 | } 49 | 50 | private double calcTxFees(Transaction tx) { 51 | double sumInputs = 0; 52 | double sumOutputs = 0; 53 | for (Transaction.Input in : tx.getInputs()) { 54 | UTXO utxo = new UTXO(in.prevTxHash, in.outputIndex); 55 | if (!utxoPool.contains(utxo) || !isValidTx(tx)) continue; 56 | Transaction.Output txOutput = utxoPool.getTxOutput(utxo); 57 | sumInputs += txOutput.value; 58 | } 59 | for (Transaction.Output out : tx.getOutputs()) { 60 | sumOutputs += out.value; 61 | } 62 | return sumInputs - sumOutputs; 63 | } 64 | 65 | /** 66 | * Handles each epoch by receiving an unordered array of proposed transactions, checking each 67 | * transaction for correctness, returning a mutually valid array of accepted transactions, and 68 | * updating the current UTXO pool as appropriate. 69 | */ 70 | public Transaction[] handleTxs(Transaction[] possibleTxs) { 71 | 72 | Set txsSortedByFees = new TreeSet<>((tx1, tx2) -> { 73 | double tx1Fees = calcTxFees(tx1); 74 | double tx2Fees = calcTxFees(tx2); 75 | return Double.valueOf(tx2Fees).compareTo(tx1Fees); 76 | }); 77 | 78 | Collections.addAll(txsSortedByFees, possibleTxs); 79 | 80 | Set acceptedTxs = new HashSet<>(); 81 | for (Transaction tx : txsSortedByFees) { 82 | if (isValidTx(tx)) { 83 | acceptedTxs.add(tx); 84 | for (Transaction.Input in : tx.getInputs()) { 85 | UTXO utxo = new UTXO(in.prevTxHash, in.outputIndex); 86 | utxoPool.removeUTXO(utxo); 87 | } 88 | for (int i = 0; i < tx.numOutputs(); i++) { 89 | Transaction.Output out = tx.getOutput(i); 90 | UTXO utxo = new UTXO(tx.getHash(), i); 91 | utxoPool.addUTXO(utxo, out); 92 | } 93 | } 94 | } 95 | 96 | Transaction[] validTxArray = new Transaction[acceptedTxs.size()]; 97 | return acceptedTxs.toArray(validTxArray); 98 | } 99 | } 100 | -------------------------------------------------------------------------------- /assignment-3-blockchain/BlockChain.java: -------------------------------------------------------------------------------- 1 | // Block Chain should maintain only limited block nodes to satisfy the functions 2 | // You should not have all the blocks added to the block chain in memory 3 | // as it would cause a memory overflow. 4 | 5 | import java.util.ArrayList; 6 | import java.util.HashMap; 7 | 8 | public class BlockChain { 9 | 10 | private class BlockNode { 11 | public Block b; 12 | public BlockNode parent; 13 | public ArrayList children; 14 | public int height; 15 | // utxo pool for making a new block on top of this block 16 | private UTXOPool uPool; 17 | 18 | public BlockNode(Block b, BlockNode parent, UTXOPool uPool) { 19 | this.b = b; 20 | this.parent = parent; 21 | children = new ArrayList<>(); 22 | this.uPool = uPool; 23 | if (parent != null) { 24 | height = parent.height + 1; 25 | parent.children.add(this); 26 | } else { 27 | height = 1; 28 | } 29 | } 30 | 31 | public UTXOPool getUTXOPoolCopy() { 32 | return new UTXOPool(uPool); 33 | } 34 | } 35 | 36 | private HashMap blockChain; 37 | private BlockNode maxHeightNode; 38 | private TransactionPool txPool; 39 | 40 | public static final int CUT_OFF_AGE = 10; 41 | 42 | 43 | /** 44 | * create an empty block chain with just a genesis block. Assume {@code genesisBlock} is a valid 45 | * block 46 | */ 47 | public BlockChain(Block genesisBlock) { 48 | blockChain = new HashMap<>(); 49 | UTXOPool utxoPool = new UTXOPool(); 50 | addCoinbaseToUTXOPool(genesisBlock, utxoPool); 51 | BlockNode genesisNode = new BlockNode(genesisBlock, null, utxoPool); 52 | blockChain.put(wrap(genesisBlock.getHash()), genesisNode); 53 | txPool = new TransactionPool(); 54 | maxHeightNode = genesisNode; 55 | } 56 | 57 | /** 58 | * Get the maximum height block 59 | */ 60 | public Block getMaxHeightBlock() { 61 | return maxHeightNode.b; 62 | } 63 | 64 | /** 65 | * Get the UTXOPool for mining a new block on top of max height block 66 | */ 67 | public UTXOPool getMaxHeightUTXOPool() { 68 | return maxHeightNode.getUTXOPoolCopy(); 69 | } 70 | 71 | /** 72 | * Get the transaction pool to mine a new block 73 | */ 74 | public TransactionPool getTransactionPool() { 75 | return txPool; 76 | } 77 | 78 | /** 79 | * Add {@code block} to the block chain if it is valid. For validity, all transactions should be 80 | * valid and block should be at {@code height > (maxHeight - CUT_OFF_AGE)}. 81 | *

82 | *

83 | * For example, you can try creating a new block over the genesis block (block height 2) if the 84 | * block chain height is {@code <= 85 | * CUT_OFF_AGE + 1}. As soon as {@code height > CUT_OFF_AGE + 1}, you cannot create a new block 86 | * at height 2. 87 | * 88 | * @return true if block is successfully added 89 | */ 90 | public boolean addBlock(Block block) { 91 | byte[] prevBlockHash = block.getPrevBlockHash(); 92 | if (prevBlockHash == null) 93 | return false; 94 | BlockNode parentBlockNode = blockChain.get(wrap(prevBlockHash)); 95 | if (parentBlockNode == null) { 96 | return false; 97 | } 98 | TxHandler handler = new TxHandler(parentBlockNode.getUTXOPoolCopy()); 99 | Transaction[] txs = block.getTransactions().toArray(new Transaction[0]); 100 | Transaction[] validTxs = handler.handleTxs(txs); 101 | if (validTxs.length != txs.length) { 102 | return false; 103 | } 104 | int proposedHeight = parentBlockNode.height + 1; 105 | if (proposedHeight <= maxHeightNode.height - CUT_OFF_AGE) { 106 | return false; 107 | } 108 | UTXOPool utxoPool = handler.getUTXOPool(); 109 | addCoinbaseToUTXOPool(block, utxoPool); 110 | BlockNode node = new BlockNode(block, parentBlockNode, utxoPool); 111 | blockChain.put(wrap(block.getHash()), node); 112 | if (proposedHeight > maxHeightNode.height) { 113 | maxHeightNode = node; 114 | } 115 | return true; 116 | } 117 | 118 | /** 119 | * Add a transaction to the transaction pool 120 | */ 121 | public void addTransaction(Transaction tx) { 122 | txPool.addTransaction(tx); 123 | } 124 | 125 | private void addCoinbaseToUTXOPool(Block block, UTXOPool utxoPool) { 126 | Transaction coinbase = block.getCoinbase(); 127 | for (int i = 0; i < coinbase.numOutputs(); i++) { 128 | Transaction.Output out = coinbase.getOutput(i); 129 | UTXO utxo = new UTXO(coinbase.getHash(), i); 130 | utxoPool.addUTXO(utxo, out); 131 | } 132 | } 133 | 134 | private static ByteArrayWrapper wrap(byte[] arr) { 135 | return new ByteArrayWrapper(arr); 136 | } 137 | } 138 | -------------------------------------------------------------------------------- /assignment-1-scroogecoin/MaxFeeTxHandlerRecurse.java: -------------------------------------------------------------------------------- 1 | import java.util.ArrayList; 2 | import java.util.Arrays; 3 | import java.util.HashSet; 4 | import java.util.List; 5 | import java.util.Set; 6 | 7 | /** 8 | * Recursive solution 9 | */ 10 | public class MaxFeeTxHandlerRecurse { 11 | 12 | private UTXOPool ledger; 13 | 14 | /** 15 | * Creates a public ledger whose current UTXOPool (collection of unspent transaction outputs) is 16 | * {@code utxoPool}. This should make a copy of utxoPool by using the UTXOPool(UTXOPool uPool) 17 | * constructor. 18 | */ 19 | public MaxFeeTxHandlerRecurse(UTXOPool utxoPool) { 20 | ledger = new UTXOPool(utxoPool); 21 | } 22 | 23 | private boolean isValidTx(Transaction tx, UTXOPool currentLedger) { 24 | Set claimedAlready = new HashSet<>(); 25 | double inputSum=0, outputSum=0; 26 | // (1) all outputs claimed by {@code tx} are in the current UTXO pool 27 | // Input --> (hash, index) --> new UTXO --> contained in pool 28 | // (2) the signatures on each input of {@code tx} are valid 29 | // Input --> sig 30 | // tx --> message 31 | // Input --> (hash, index) --> new UTXO --> output --> pk 32 | int inputIndex = 0; 33 | for (Transaction.Input input : tx.getInputs()) { 34 | UTXO toFind = new UTXO(input.prevTxHash, input.outputIndex); 35 | // Check (1) 36 | if (!currentLedger.contains(toFind)) { 37 | return false; // (1) fails 38 | } 39 | // Check (2) 40 | Transaction.Output spentOutput = currentLedger.getTxOutput(toFind); 41 | inputSum += spentOutput.value; 42 | if (!Crypto.verifySignature(spentOutput.address, tx.getRawDataToSign(inputIndex++), input.signature)) { 43 | return false; // (2) fails 44 | } 45 | // (3) no UTXO is claimed multiple times by {@code tx} 46 | if (claimedAlready.contains(toFind)) { 47 | return false; // (3) fails 48 | } 49 | claimedAlready.add(toFind); 50 | } 51 | // (4) all of {@code tx}s output values are non-negative 52 | for (Transaction.Output output : tx.getOutputs()) { 53 | outputSum += output.value; 54 | if (output.value<0) { 55 | return false; // (4) fails 56 | } 57 | } 58 | // (5) the sum of {@code tx}s input values is greater than or equal to the sum of its output 59 | return inputSum >= outputSum; 60 | } 61 | 62 | /** 63 | * Handles each epoch by receiving an unordered array of proposed transactions, checking each 64 | * transaction for correctness, returning a mutually valid array of accepted transactions, and 65 | * updating the current UTXO pool as appropriate. 66 | * Finds a set of transactions with maximum total transaction fees -- i.e. maximize the sum over 67 | * all transactions in the set of (sum of input values - sum of output values)). 68 | */ 69 | public Transaction[] handleTxs(Transaction[] possibleTxs) { 70 | List available = new ArrayList(Arrays.asList(possibleTxs)); 71 | List chain = new ArrayList<>(); 72 | UTXOPool currentLedger = new UTXOPool(ledger); 73 | handleTxsRecurse(available, chain, currentLedger); 74 | return chain.toArray(new Transaction[chain.size()]); 75 | } 76 | 77 | private double handleTxsRecurse(List available, List chain, UTXOPool currentLedger) { 78 | List maxChain = new ArrayList<>(chain); 79 | double maxFees=0; 80 | for (Transaction transaction : available) { 81 | if (isValidTx(transaction, currentLedger)) { 82 | UTXOPool newLedger = new UTXOPool(currentLedger); 83 | chain.add(transaction); 84 | double transactionFee = addAndCalcFee(transaction, newLedger); 85 | List newAvailable = new ArrayList(available); 86 | newAvailable.remove(transaction); 87 | List newChain = new ArrayList(chain); 88 | double chainFees = transactionFee + handleTxsRecurse(newAvailable, newChain, newLedger); 89 | if (chainFees>maxFees) { 90 | maxFees = chainFees; 91 | maxChain = newChain; 92 | } 93 | chain.remove(transaction); 94 | } 95 | } 96 | chain.clear(); 97 | chain.addAll(maxChain); 98 | return maxFees; 99 | } 100 | 101 | private double addAndCalcFee(Transaction tx, UTXOPool currentLedger) { 102 | double inputSum=0, outputSum=0; 103 | // Remove consumed 104 | for (Transaction.Input input : tx.getInputs()) { 105 | UTXO toConsume = new UTXO(input.prevTxHash, input.outputIndex); 106 | Transaction.Output spentOutput = currentLedger.getTxOutput(toConsume); 107 | inputSum += spentOutput.value; 108 | currentLedger.removeUTXO(toConsume); 109 | } 110 | // Add unspent 111 | int index=0; 112 | for (Transaction.Output output : tx.getOutputs()) { 113 | outputSum += output.value; 114 | UTXO toAdd = new UTXO(tx.getHash(), index++); 115 | currentLedger.addUTXO(toAdd, output); 116 | } 117 | return inputSum-outputSum; 118 | } 119 | 120 | } 121 | --------------------------------------------------------------------------------