我在Java中实现一个方法public boolean isValidTx(Transaction tx)
,通过比较输入的UTXO和输出的总和(sum(input)> = sum(outputs))来验证比特币交易。
public boolean isValidTx(Transaction tx) {
}
对于ith
output
,outputs
值的值可以是:
tx.getOutput(i).value
我遇到的问题是如何找到ith
input
的UTXO。 ith
输入确实具有tx.getInput(i).prevTxHash
中先前交易的哈希值,其output
是此处的input
tx
。但我不知道如何在给定事务tx
的情况下找到输入的UTXO。赞赏点(在java代码中不是必需的)。
以下是Transaction
类(Transaction.java)
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();
}
}
答案 0 :(得分:0)
要检索UTXO,您应该使用事务的txid(即哈希值)在数据库中查询UTXO。
数据库最终是区块链本身。在vanilla设计中,您可以遍历txid的区块链数据结构。但是您可能还需要考虑其他一些事项:
除了包含所有“已验证”交易的区块链之外,您可能还有一些未经过验证的浮动交易,例如您当前正在处理的交易。他们可能会花一些区块链的UTXO。而您当前的交易输入可能是其中一个浮动交易的输出。因此,假设您已经验证了这些浮动事务,那么您还应该遍历这些浮动事务以验证或放弃当前事务。
迭代整个区块链可能是不必要的,因为其中的大部分交易输出已经花掉了。为了节省您的时间,可以将所有实际UTXO的缓存构建为摘要,这样您只需要查询此缓存数据库以获取txid以验证您的事务。 (当然,您应该维护此缓存数据库以确保其与区块链的一致性。)
在比特币中,“已验证”的浮动交易在Mempool中排列,等待开采(即包含在一个块中),并且所有UTXO都被安排为在LevelDB中设置的UTXO。