Skip to content

Commit

Permalink
feat: assignment 3
Browse files Browse the repository at this point in the history
  • Loading branch information
DavidCai1111 committed Mar 12, 2020
1 parent 1cd6e91 commit daa1f8d
Show file tree
Hide file tree
Showing 11 changed files with 970 additions and 0 deletions.
Binary file added assignment3starterCode/Assignment3.pdf
Binary file not shown.
73 changes: 73 additions & 0 deletions assignment3starterCode/Block.java
@@ -0,0 +1,73 @@

import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.security.PublicKey;
import java.util.ArrayList;

public class Block {

public static final double COINBASE = 25;

private byte[] hash;
private byte[] prevBlockHash;
private Transaction coinbase;
private ArrayList<Transaction> txs;

/** {@code address} is the address to which the coinbase transaction would go */
public Block(byte[] prevHash, PublicKey address) {
prevBlockHash = prevHash;
coinbase = new Transaction(COINBASE, address);
txs = new ArrayList<Transaction>();
}

public Transaction getCoinbase() {
return coinbase;
}

public byte[] getHash() {
return hash;
}

public byte[] getPrevBlockHash() {
return prevBlockHash;
}

public ArrayList<Transaction> getTransactions() {
return txs;
}

public Transaction getTransaction(int index) {
return txs.get(index);
}

public void addTransaction(Transaction tx) {
txs.add(tx);
}

public byte[] getRawBlock() {
ArrayList<Byte> rawBlock = new ArrayList<Byte>();
if (prevBlockHash != null)
for (int i = 0; i < prevBlockHash.length; i++)
rawBlock.add(prevBlockHash[i]);
for (int i = 0; i < txs.size(); i++) {
byte[] rawTx = txs.get(i).getRawTx();
for (int j = 0; j < rawTx.length; j++) {
rawBlock.add(rawTx[j]);
}
}
byte[] raw = new byte[rawBlock.size()];
for (int i = 0; i < raw.length; i++)
raw[i] = rawBlock.get(i);
return raw;
}

public void finalize() {
try {
MessageDigest md = MessageDigest.getInstance("SHA-256");
md.update(getRawBlock());
hash = md.digest();
} catch (NoSuchAlgorithmException x) {
x.printStackTrace(System.err);
}
}
}
135 changes: 135 additions & 0 deletions assignment3starterCode/BlockChain.java
@@ -0,0 +1,135 @@
// Block Chain should maintain only limited block nodes to satisfy the functions
// You should not have all the blocks added to the block chain in memory
// as it would cause a memory overflow.

import java.util.ArrayList;
import java.util.HashMap;

public class BlockChain {

private class BlockNode {
public Block b;
public ArrayList<BlockNode> children;
public int height;
// utxo pool for making a new block on top of this block
private UTXOPool uPool;

public BlockNode(Block b, BlockNode parent, UTXOPool uPool) {
this.b = b;
this.parent = parent;
children = new ArrayList<>();
this.uPool = uPool;
if (parent != null) {
height = parent.height + 1;
parent.children.add(this);
} else {
height = 1;
}
}

public UTXOPool getUTXOPoolCopy() {
return new UTXOPool(uPool);
}
}

private HashMap<ByteArrayWrapper, BlockNode> blockChain;
private BlockNode maxHeightNode;
private TransactionPool txPool;

public static final int CUT_OFF_AGE = 10;

/**
* create an empty block chain with just a genesis block. Assume {@code genesisBlock} is a valid
* block
*/
public BlockChain(Block genesisBlock) {
blockChain = new HashMap<>();
UTXOPool utxoPool = new UTXOPool();
addCoinbaseToUTXOPool(genesisBlock, utxoPool);
BlockNode genesisNode = new BlockNode(genesisBlock, null, utxoPool);
blockChain.put(wrap(genesisBlock.getHash()), genesisNode);
txPool = new TransactionPool();
maxHeightNode = genesisNode;
}

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

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

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

/**
* Add {@code block} to the block chain if it is valid. For validity, all transactions should be
* valid and block should be at {@code height > (maxHeight - CUT_OFF_AGE)}.
* <p>
* <p>
* For example, you can try creating a new block over the genesis block (block height 2) if the
* block chain height is {@code <=
* CUT_OFF_AGE + 1}. As soon as {@code height > CUT_OFF_AGE + 1}, you cannot create a new block
* at height 2.
*
* @return true if block is successfully added
*/
public boolean addBlock(Block block) {
byte[] prevBlockHash = block.getPrevBlockHash();
if (prevBlockHash == null)
return false;
BlockNode parentBlockNode = blockChain.get(wrap(prevBlockHash));
if (parentBlockNode == null) {
return false;
}
TxHandler handler = new TxHandler(parentBlockNode.getUTXOPoolCopy());
Transaction[] txs = block.getTransactions().toArray(new Transaction[0]);
Transaction[] validTxs = handler.handleTxs(txs);
if (validTxs.length != txs.length) {
return false;
}
int proposedHeight = parentBlockNode.height + 1;
if (proposedHeight <= maxHeightNode.height - CUT_OFF_AGE) {
return false;
}
UTXOPool utxoPool = handler.getUTXOPool();
addCoinbaseToUTXOPool(block, utxoPool);
BlockNode node = new BlockNode(block, parentBlockNode, utxoPool);
blockChain.put(wrap(block.getHash()), node);
if (proposedHeight > maxHeightNode.height) {
maxHeightNode = node;
}
return true;
}

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

private void addCoinbaseToUTXOPool(Block block, UTXOPool utxoPool) {
Transaction coinbase = block.getCoinbase();
for (int i = 0; i < coinbase.numOutputs(); i++) {
Transaction.Output out = coinbase.getOutput(i);
UTXO utxo = new UTXO(coinbase.getHash(), i);
utxoPool.addUTXO(utxo, out);
}
}

private static ByteArrayWrapper wrap(byte[] arr) {
return new ByteArrayWrapper(arr);
}
}
47 changes: 47 additions & 0 deletions assignment3starterCode/BlockHandler.java
@@ -0,0 +1,47 @@

import java.security.PublicKey;

public class BlockHandler {
private BlockChain blockChain;

/** assume blockChain has the genesis block */
public BlockHandler(BlockChain blockChain) {
this.blockChain = blockChain;
}

/**
* add {@code block} to the block chain if it is valid.
*
* @return true if the block is valid and has been added, false otherwise
*/
public boolean processBlock(Block block) {
if (block == null)
return false;
return blockChain.addBlock(block);
}

/** create a new {@code block} over the max height {@code block} */
public Block createBlock(PublicKey myAddress) {
Block parent = blockChain.getMaxHeightBlock();
byte[] parentHash = parent.getHash();
Block current = new Block(parentHash, myAddress);
UTXOPool uPool = blockChain.getMaxHeightUTXOPool();
TransactionPool txPool = blockChain.getTransactionPool();
TxHandler handler = new TxHandler(uPool);
Transaction[] txs = txPool.getTransactions().toArray(new Transaction[0]);
Transaction[] rTxs = handler.handleTxs(txs);
for (int i = 0; i < rTxs.length; i++)
current.addTransaction(rTxs[i]);

current.finalize();
if (blockChain.addBlock(current))
return current;
else
return null;
}

/** process a {@code Transaction} */
public void processTx(Transaction tx) {
blockChain.addTransaction(tx);
}
}
47 changes: 47 additions & 0 deletions assignment3starterCode/ByteArrayWrapper.java
@@ -0,0 +1,47 @@

import java.util.Arrays;

/** a wrapper for byte array with hashCode and equals function implemented */
public class ByteArrayWrapper {

private byte[] contents;

public ByteArrayWrapper(byte[] b) {
contents = new byte[b.length];
for (int i = 0; i < contents.length; i++)
contents[i] = b[i];
}

public boolean equals(Object other) {
if (other == null) {
return false;
}
if (getClass() != other.getClass()) {
return false;
}

ByteArrayWrapper otherB = (ByteArrayWrapper) other;
byte[] b = otherB.contents;
if (contents == null) {
if (b == null)
return true;
else
return false;
} else {
if (b == null)
return false;
else {
if (contents.length != b.length)
return false;
for (int i = 0; i < b.length; i++)
if (contents[i] != b[i])
return false;
return true;
}
}
}

public int hashCode() {
return Arrays.hashCode(contents);
}
}
36 changes: 36 additions & 0 deletions assignment3starterCode/Crypto.java
@@ -0,0 +1,36 @@
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.security.PublicKey;
import java.security.Signature;
import java.security.SignatureException;

public class Crypto {

/**
* @return true is {@code signature} is a valid digital signature of {@code message} under the
* key {@code pubKey}. Internally, this uses RSA signature, but the student does not
* have to deal with any of the implementation details of the specific signature
* algorithm
*/
public static boolean verifySignature(PublicKey pubKey, byte[] message, byte[] signature) {
Signature sig = null;
try {
sig = Signature.getInstance("SHA256withRSA");
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
}
try {
sig.initVerify(pubKey);
} catch (InvalidKeyException e) {
e.printStackTrace();
}
try {
sig.update(message);
return sig.verify(signature);
} catch (SignatureException e) {
e.printStackTrace();
}
return false;

}
}

0 comments on commit daa1f8d

Please sign in to comment.