Sei sulla pagina 1di 5

How to find UTXO of inputs given a bitcoin transaction?

I am implementing a method public boolean isValidTx(Transaction tx) in Java


to validate a bitcoin transaction by comparing the UTXO of inputs and sum of the outputs
(sum(input) >= sum(outputs)).
public boolean isValidTx(Transaction tx) {
}
For ith output, the value of the outputs value can be found as:
tx.getOutput(i).value
The problem I have is how to find UTXO of the ith input. The ith input does have the
hash of previous transaction in tx.getInput(i).prevTxHash whose output is
the input for transaction tx here. But I have no clue about how to find the UTXO of the
input given a transaction tx. Points appreciated (not necessary in java code).
Here is the definition of Transaction class (Transaction.java)

To retrieve a UTXO, you should query the database for UTXO with the transaction's
txid, which is the hash value.

The database ultimately is the blockchain itself. In a vanilla design, you can iterate
through the blockchain data structure for the txid. But there are a couple of other
things you may want to consider:

1. Besides the blockchain that consists of all the "verified" transactions, you may have some
floating transactions that are not verified, like the one you are currently handling. They
may spend some UTXOs of the blockchain. And your current transaction's input may be
the output of one of the floating transactions. Hence you should also iterate through these
floating transactions in order to validate or discard your current transaction, assuming
you have validated those floating transactions.
2. To iterate through the whole blockchain may be unnecessary since most outputs of the
transactions in it are already spent. To save your time, a cache for all the actual UTXOs
can be built as a summary, so that you only need to query this cache database for the txid
in order to validate your transaction. (Of course, you should then maintain this cache
database to ensure its consistency with the blockchain.)
In Bitcoin, the "verified" floating transactions are arranged in Mempool pending to be
mined (i.e., included in a block), and all the UTXOs are arranged as UTXO set in
LevelDB.

import java.nio.ByteBuffer;
import java.util.ArrayList;
import java.util.Arrays;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.security.PublicKey;

public class Transaction {

public class Input {


/** hash of the Transaction whose output is being used */
public byte[] prevTxHash;
/** used output's index in the previous transaction */
public int outputIndex;
/** the signature produced to check validity */
public byte[] signature;

public Input(byte[] prevHash, int index) {


if (prevHash == null)
prevTxHash = null;
else
prevTxHash = Arrays.copyOf(prevHash, prevHash.length);
outputIndex = index;
}

public void addSignature(byte[] sig) {


if (sig == null)
signature = null;
else
signature = Arrays.copyOf(sig, sig.length);
}
}

public class Output {


/** value in bitcoins of the output */
public double value;
/** the address or public key of the recipient */
public PublicKey address;

public Output(double v, PublicKey addr) {


value = v;
address = addr;
}
}

/** hash of the transaction, its unique id */


private byte[] hash;
private ArrayList<Input> inputs;
private ArrayList<Output> outputs;

public Transaction() {
inputs = new ArrayList<Input>();
outputs = new ArrayList<Output>();
}

public Transaction(Transaction tx) {


hash = tx.hash.clone();
inputs = new ArrayList<Input>(tx.inputs);
outputs = new ArrayList<Output>(tx.outputs);
}

public void addInput(byte[] prevTxHash, int outputIndex) {


Input in = new Input(prevTxHash, outputIndex);
inputs.add(in);
}

public void addOutput(double value, PublicKey address) {


Output op = new Output(value, address);
outputs.add(op);
}

public void removeInput(int index) {


inputs.remove(index);
}

public void removeInput(UTXO ut) {


for (int i = 0; i < inputs.size(); i++) {
Input in = inputs.get(i);
UTXO u = new UTXO(in.prevTxHash, in.outputIndex);
if (u.equals(ut)) {
inputs.remove(i);
return;
}
}
}

public byte[] getRawDataToSign(int index) {


// ith input and all outputs
ArrayList<Byte> sigData = new ArrayList<Byte>();
if (index > inputs.size())
return null;
Input in = inputs.get(index);
byte[] prevTxHash = in.prevTxHash;
ByteBuffer b = ByteBuffer.allocate(Integer.SIZE / 8);
b.putInt(in.outputIndex);
byte[] outputIndex = b.array();
if (prevTxHash != null)
for (int i = 0; i < prevTxHash.length; i++)
sigData.add(prevTxHash[i]);
for (int i = 0; i < outputIndex.length; i++)
sigData.add(outputIndex[i]);
for (Output op : outputs) {
ByteBuffer bo = ByteBuffer.allocate(Double.SIZE / 8);
bo.putDouble(op.value);
byte[] value = bo.array();
byte[] addressBytes = op.address.getEncoded();
for (int i = 0; i < value.length; i++)
sigData.add(value[i]);

for (int i = 0; i < addressBytes.length; i++)


sigData.add(addressBytes[i]);
}
byte[] sigD = new byte[sigData.size()];
int i = 0;
for (Byte sb : sigData)
sigD[i++] = sb;
return sigD;
}

public void addSignature(byte[] signature, int index) {


inputs.get(index).addSignature(signature);
}

public byte[] getRawTx() {


ArrayList<Byte> rawTx = new ArrayList<Byte>();
for (Input in : inputs) {
byte[] prevTxHash = in.prevTxHash;
ByteBuffer b = ByteBuffer.allocate(Integer.SIZE / 8);
b.putInt(in.outputIndex);
byte[] outputIndex = b.array();
byte[] signature = in.signature;
if (prevTxHash != null)
for (int i = 0; i < prevTxHash.length; i++)
rawTx.add(prevTxHash[i]);
for (int i = 0; i < outputIndex.length; i++)
rawTx.add(outputIndex[i]);
if (signature != null)
for (int i = 0; i < signature.length; i++)
rawTx.add(signature[i]);
}
for (Output op : outputs) {
ByteBuffer b = ByteBuffer.allocate(Double.SIZE / 8);
b.putDouble(op.value);
byte[] value = b.array();
byte[] addressBytes = op.address.getEncoded();
for (int i = 0; i < value.length; i++) {
rawTx.add(value[i]);
}
for (int i = 0; i < addressBytes.length; i++) {
rawTx.add(addressBytes[i]);
}

}
byte[] tx = new byte[rawTx.size()];
int i = 0;
for (Byte b : rawTx)
tx[i++] = b;
return tx;
}

public void finalize() {


try {
MessageDigest md = MessageDigest.getInstance("SHA-256");
md.update(getRawTx());
hash = md.digest();
} catch (NoSuchAlgorithmException x) {
x.printStackTrace(System.err);
}
}

public void setHash(byte[] h) {


hash = h;
}

public byte[] getHash() {


return hash;
}

public ArrayList<Input> getInputs() {


return inputs;
}

public ArrayList<Output> getOutputs() {


return outputs;
}

public Input getInput(int index) {


if (index < inputs.size()) {
return inputs.get(index);
}
return null;
}
public Output getOutput(int index) {
if (index < outputs.size()) {
return outputs.get(index);
}
return null;
}

public int numInputs() {


return inputs.size();
}

public int numOutputs() {


return outputs.size();
}
}

Potrebbero piacerti anche