Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
1cd6e91
commit daa1f8d
Showing
11 changed files
with
970 additions
and
0 deletions.
There are no files selected for viewing
Binary file not shown.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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); | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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; | ||
|
||
} | ||
} |
Oops, something went wrong.