Skip to content

Instantly share code, notes, and snippets.

@anhldbk
Created December 31, 2016 14:35
Show Gist options
  • Save anhldbk/37140b3281940d1fed744d8e4fbcf1c3 to your computer and use it in GitHub Desktop.
Save anhldbk/37140b3281940d1fed744d8e4fbcf1c3 to your computer and use it in GitHub Desktop.
Scrooge Coin
// https://www.coursera.org/learn/cryptocurrency/programming/KOo3V/scrooge-coin
import java.security.PublicKey;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
public class TxHandler {
private UTXOPool pool;
private TxValidator validator = new TxValidator();
/**
* Creates a public ledger whose current UTXOPool (collection of unspent transaction outputs) is
* {@code utxoPool}. This should make a copy of utxoPool by using the UTXOPool(UTXOPool uPool)
* constructor.
*/
public TxHandler(UTXOPool utxoPool) {
this.pool = new UTXOPool(utxoPool);
}
private class TxValidator {
private double inSum = 0, outSum = 0;
public boolean validate(UTXOPool pool, Transaction tx) {
return notNull(tx) && inputValid(pool, tx) && outputValid(tx);
}
private boolean notNull(Transaction tx) {
return tx != null;
}
private boolean inputValid(UTXOPool pool, Transaction tx) {
inSum = 0;
Set<UTXO> usedTxs = new HashSet<>();
// check for rule #1
for (int i = 0; i < tx.numInputs(); i++) {
Transaction.Input input = tx.getInput(i);
if (input == null) {
return false;
}
UTXO utxo = new UTXO(input.prevTxHash, input.outputIndex);
if (!pool.contains(utxo) || usedTxs.contains(utxo)) {
return false;
}
Transaction.Output prevTxOut = pool.getTxOutput(utxo);
// verify rule #2
PublicKey pubKey = prevTxOut.address;
byte[] message = tx.getRawDataToSign(i);
byte[] signature = input.signature;
if (!Crypto.verifySignature(pubKey, message, signature)) {
return false;
}
// rule #3
// if the signatures are valid, then remove the associated output from the pool
// by removing the item, we ensure that no UTXO is claimed multiple times
//this.pool.removeUTXO(utxo);
usedTxs.add(utxo);
inSum += prevTxOut.value;
}
return true;
}
private boolean outputValid(Transaction tx) {
// check for rule #4
outSum = 0;
for (int i = 0; i < tx.numOutputs(); i++) {
Transaction.Output out = tx.getOutput(i);
if (out.value < 0) {
return false;
}
outSum += out.value;
}
// check for rule #5
return inSum >= outSum;
}
}
/**
* @return true if:
* (1) all outputs claimed by {@code tx} are in the current UTXO pool,
* (2) the signatures on each input of {@code tx} are valid,
* (3) no UTXO is claimed multiple times by {@code tx},
* (4) all of {@code tx}s output values are non-negative, and
* (5) the sum of {@code tx}s input values is greater than or equal to the sum of its output
* values; and false otherwise.
*/
public boolean isValidTx(Transaction tx) {
return validator.validate(this.pool, tx);
}
private ConcurrentHashMap<byte[], Transaction> getTxMap(Transaction[] possibleTxs) {
ConcurrentHashMap<byte[], Transaction> txs = new ConcurrentHashMap<>();
for (Transaction tx : possibleTxs) {
if (tx == null) {
continue;
}
// buffer the hashes so we don't have to re-calculate them later on
tx.finalize();
txs.put(tx.getHash(), tx);
}
return txs;
}
/**
* Handles each epoch by receiving an unordered array of proposed transactions, checking each
* transaction for correctness, returning a mutually valid array of accepted transactions, and
* updating the current UTXO pool as appropriate.
*/
public Transaction[] handleTxs(Transaction[] possibleTxs) {
if (possibleTxs == null) {
return new Transaction[0];
}
ConcurrentHashMap<byte[], Transaction> txs = getTxMap(possibleTxs);
ArrayList<Transaction> valid = new ArrayList<>();
int txCount;
do {
txCount = txs.size();
for (Transaction tx : txs.values()) {
if (!isValidTx(tx)) {
continue;
}
valid.add(tx);
this.applyTx(tx);
txs.remove(tx.getHash());
}
if (txCount == txs.size() || txCount == 0) { // still the same
break; // nothing to check
}
} while (true);
return valid.toArray(new Transaction[valid.size()]);
}
private void applyTx(Transaction tx) {
if (tx == null) {
return;
}
for (Transaction.Input input : tx.getInputs()) {
UTXO utxo = new UTXO(input.prevTxHash, input.outputIndex);
this.pool.removeUTXO(utxo);
}
byte[] txHash = tx.getHash();
int index = 0;
for (Transaction.Output output : tx.getOutputs()) {
UTXO utxo = new UTXO(txHash, index);
index += 1;
this.pool.addUTXO(utxo, output);
}
}
}
@ramneekhanda
Copy link

ramneekhanda commented Oct 28, 2017

@DmytroSokhach
Copy link

line 44: || usedTxs.contains(utxo)
seems to be redundant.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment