区块链基础:交易简单实现

1、交易输入

package com.blockchain.model;

/** * 交易输入,UTXO=<txId,value> * 一个交易,可能有多个输入 */
public class TransactionInput {

    /** * 前一次交易id */
    private String txId;
    /** * 交易金额 */
    private int value;
    /** * 交易签名 */
    private String signature;
    /** * 交易发送方的钱包公钥 */
    private String publicKey;

    public TransactionInput() {
        super();
    }

    public TransactionInput(String txId, int value, String signature, String publicKey) {
        super();
        this.txId = txId;
        this.value = value;
        this.signature = signature;
        this.publicKey = publicKey;
    }

    //省略getter和setter
}

2、交易输出

package com.blockchain.model;

/** * 交易输出,一个交易可能有多个输出 */
public class TransactionOutput {

    /** * 收纳金额 */
    private int value;
    /** * 交易接收方的钱包公钥的hash值 */
    private String publicKeyHash;

    public TransactionOutput() {
        super();
    }

    public TransactionOutput(int value, String publicKeyHash) {
        this.value = value;
        this.publicKeyHash = publicKeyHash;
    }
    //省略getter和setter
}

3、交易参数

package com.blockchain.model;

/** * 交易接口参数 * */
public class TransactionParam {

    /** * 发送方钱包地址 */
    private String sender;
    /** * 接收方钱包地址 */
    private String recipient;
    /** * 交易金额 */
    private int amount;

    //省略getter和setter

}

3、交易

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
}
阅读更多

更多精彩内容