package com.blockchain.model;
import java.util.List;
import com.alibaba.fastjson.JSON;
import com.blockchain.security.CryptoUtil;
import com.blockchain.security.RSACoder;
/**
* 交易类
* 易过程中,转账方需要通过签名脚本来证明自己是 UTXO 的合法使用者易过程中,转账方需要通过签名脚本来证明自己是 UTXO 的合法使用者
*/
public class Transaction {
/**
* 交易唯一标识
*/
private String id;
/**
* 简单交易输入(只有一个输入)
*/
private TransactionInput txIn;
/**
* 简单交易输出(只有一个输出)
*/
private TransactionOutput txOut;
/**
* 一般交易输入
*/
private List<TransactionInput> txInList;
/**
* 简单交易输出(只有一个输出)
*/
private List<TransactionOutput> txOutList;
public Transaction() {
super();
}
/**
* 简单交易:只有一个输入和一个输出
*/
public Transaction(String id, TransactionInput txIn, TransactionOutput txOut) {
super();
this.id = id;
this.txIn = txIn;
this.txOut = txOut;
}
/**
* 一般交易:多个输入和多个输出
*/
public Transaction(String id, List<TransactionInput> txInList, List<TransactionOutput> txOutList) {
super();
this.id = id;
this.txInList = txInList;
this.txOutList = txOutList;
}
/**
* 是否系统生成区块的奖励交易
* coinbase交易的txIn是空值(前一次交易:id为0,value为-1)
*/
public boolean coinbaseTx() {
return txIn.getTxId().equals("0") && txIn.getValue() == -1;
}
/**
* 付款人对交易进行签名确认,用交易发起者的私钥对交易的Hash数据进行签名:
* (一般是发送者将信息用哈希算法处理得出一个哈希值,然后用私钥对该哈希值进行加密,得出一个签名。
* 然后发送者再将信息和签名一起发送给接收者。
* 接收者使用发送者的公钥对签名进行解密,还原出哈希值,再通过哈希算法来验证信息的哈希值和解密签名还原出来的哈希值是否一致,
* 从而可以鉴定信息是否来自发送者或验证信息是否被篡改。)
*
* 签名结果存入交易的输入中(txIn)
*
* @param privateKey 交易发起者的私钥
* @param prevTx
*/
public void sign(String privateKey, Transaction prevTx) {
if (coinbaseTx()) {//跳过挖矿系统奖励的交易
return;
}
//交易输入引用的前一笔交易与传入的前一笔交易不匹配
if (!prevTx.getId().equals(txIn.getTxId())) {
System.err.println("交易签名失败:当前交易输入引用的前一笔交易与传入的前一笔交易不匹配");
}
Transaction txClone = cloneTx();
//设置交易副本的txIn的公钥
txClone.getTxIn().setPublicKey(prevTx.getTxOut().getPublicKeyHash());
String sign = "";
try {
//用交易发起者的私钥对交易的Hash数据进行签名
sign = RSACoder.sign(txClone.hash().getBytes(), privateKey);
} catch (Exception e) {
System.err.println("数字签名出错!");
e.printStackTrace();
}
System.out.println("数字签名长度:"+sign.length());
txIn.setSignature(sign);
}
/**
* 生成用于交易签名的交易记录副本
*/
public Transaction cloneTx() {
TransactionInput transactionInput = new TransactionInput(txIn.getTxId(), txIn.getValue(), null, null);
TransactionOutput transactionOutput = new TransactionOutput(txOut.getValue(), txOut.getPublicKeyHash());
return new Transaction(id, transactionInput, transactionOutput);
}
/**
* 收款人对付款人的交易签名进行校验
* @param prevTx
*/
public boolean verify(Transaction prevTx) {
if (coinbaseTx()) {//跳过挖矿系统奖励的交易
return true;
}
//交易输入引用的前一笔交易与传入的前一笔交易不匹配
if (!prevTx.getId().equals(txIn.getTxId())) {
System.err.println("验证交易签名失败:当前交易输入引用的前一笔交易与传入的前一笔交易不匹配");
}
Transaction txClone = cloneTx();
//上个交易的输出指定了接受者的公钥,也就是当前交易发起者的公钥
txClone.getTxIn().setPublicKey(prevTx.getTxOut().getPublicKeyHash());
boolean result = false;
try {
//通过发送者的公钥进行签名校验
result = RSACoder.verify(txClone.hash().getBytes(), txIn.getPublicKey(), txIn.getSignature());
} catch (Exception e) {
e.printStackTrace();
}
return result;
}
/**
* 生成交易的hash
*/
public String hash() {
return CryptoUtil.sha256(JSON.toJSONString(this));
}
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + ((id == null) ? 0 : id.hashCode());
return result;
}
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
Transaction other = (Transaction) obj;
if (id == null) {
if (other.id != null)
return false;
} else if (!id.equals(other.id))
return false;
return true;
}
//省略getter和setter
}