交易流程图
image交易说明
交易的创建
-
遍历自己地址的可用UTXO是否大于交易的数额amount。
-
计算交易费用fee
-
如果如果可用UTXO >= fee + amount 则开始组织交易。
-
生成输出 一个一个的 TxOut,并添加到数组中 (包含转账地址和找零地址)。
-
根据UTXO生成 TxIn,并添加到数组中。
-
对 TxIn 数组中的 TxIn 进行逐一签名。
-
发送交易信息。
-
交易的输入
每一笔交易都会由一个或一组输入和一个或一组输出组成,除了一种特殊的交易 coinbase,coinbase是bitcoin-core定义的给区块挖出者的奖励,其实也是比特币的产出过程。只有coinbase交易会产生比特币,其他的交易都是比特币的转移。
一个交易(MsgTx)是由多个Input和多个Output组成的,而在Input中是由指向UTXO的OutPoint,解锁脚本SignatureScript和序列Sequence组成,一笔交易分为三个步骤:
-
构建原始交易RawTransaction,该交易包含了输入指向的OutPoint,也包含了完整的Output,但是没有签名,也就是没有设置SignatureScript的内容。
-
用私钥对签名构建的RawTransaction进行签名,并将签名构建成完整的解锁脚本,填入对应的Input的SignatureScript字段中。
-
将签名后的Transaction发送到P2P网络中。
对交易进行签名,有几个Input,就要要签名几次,每个签名的原理是一样的,在比特币中,对一笔交易的签名流程是这样的:
-
查找该笔交易对应的UTXO
-
获得该UTXO对应的锁定脚本
-
复制该交易对象,并在复制副本中将该Input的解锁脚本字段的值设置为对应的锁定脚本
未花费的消费列表
│ {
│ "output_no": 0,
│ "scriptPubKey": "76a914b73187dbcab54a93d861f1e53b39843f8a8d8ce988ac",
│ "address": "1HhdzZNfAdpdtbGnRNkat9YSc5BN6TKenG",
│ "txid": "ac0472c6a1095ffd2372412ba1c88c291c25b2315fedcdb45d02189fc2436c06",
│ "value": "0.00003499",
│ "satoshis": "3499"
│ }
签名的信息
01000000024a2989e933ed9875f83776ddb4ee105a3ee7363e60e0f53db8a682f82aa52303010000006b483045022071e572d11357a0801ce69741ffde20de49099cd4ddeac19d4ad6697ca766c5fc022100835d7dabdc674e78ca523f09811b7178b3c31d08735909a28975721c6a2d2e34012103be47f412d8e23a3baf591bbe42c5859062b5b0b168567ecd8746ddc1948103fdffffffff0bcdf46d36a33c8d768e25be6b185bc9efad8aca7fad55b5180c9475afe6718f000000006c493046022100ae57aeb9711a6879bc7ddd363feb62aa5aa42b0dca6aa81e1eafc533e4cce0c4022100db46f11ac362e0fe494c65d24fb702c92ebc554690a7447365d38e7aa3613a740121030b7979840ce44d7318f5883aed9d8f5a3c0778326beae0c2117ea8712c7de5aaffffffff0108880200000000001976a9148c2cbf83f9b3034007456580a9072a9304684a4988ac00000000
签名信息解析
{
"addresses": [
"1DnBD1YD8HhDdneKUUh36TK48pTWVKkWvC"
],
"block_height": -1,
"block_index": -1,
"confirmations": 0,
"double_spend": false,
"fees": 0,
"hash": "a0b956a450134ede9839c4d9fcf4c31446a24a135c2f8c11a8288eac0ba1a34f",
"inputs": [
{
"age": 0,
"output_index": 1,
"prev_hash": "0323a52af882a6b83df5e0603e36e73e5a10eeb4dd7637f87598ed33e989294a",
"script": "483045022071e572d11357a0801ce69741ffde20de49099cd4ddeac19d4ad6697ca766c5fc022100835d7dabdc674e78ca523f09811b7178b3c31d08735909a28975721c6a2d2e34012103be47f412d8e23a3baf591bbe42c5859062b5b0b168567ecd8746ddc1948103fd",
"script_type": "empty",
"sequence": 4294967295
},
{
"age": 0,
"output_index": 0,
"prev_hash": "8f71e6af75940c18b555ad7fca8aadefc95b186bbe258e768d3ca3366df4cd0b",
"script": "493046022100ae57aeb9711a6879bc7ddd363feb62aa5aa42b0dca6aa81e1eafc533e4cce0c4022100db46f11ac362e0fe494c65d24fb702c92ebc554690a7447365d38e7aa3613a740121030b7979840ce44d7318f5883aed9d8f5a3c0778326beae0c2117ea8712c7de5aa",
"script_type": "empty",
"sequence": 4294967295
}
],
"outputs": [
{
"addresses": [
"1DnBD1YD8HhDdneKUUh36TK48pTWVKkWvC"
],
"script": "76a9148c2cbf83f9b3034007456580a9072a9304684a4988ac",
"script_type": "pay-to-pubkey-hash",
"value": 165896
}
],
"preference": "low",
"received": "2020-06-15T08:47:43.844912895Z",
"relayed_by": "3.92.56.73",
"size": 341,
"total": 165896,
"ver": 1,
"vin_sz": 2,
"vout_sz": 1
}
签名交易流程
private BtcTxSignResult signTransaction(String password, Wallet wallet, String to, boolean isFork) {
// 发送到的地址
this.to = to;
// NetworkParameters上下文
this.network = NetParamsService.getNetworkParameters(wallet.chainType);
// 获取私钥信息
byte[] bytes = WalletManager.getBtcPriKey(wallet.keystore, password);
String xprv = new String(bytes, Charset.forName("UTF-8"));
DeterministicKey xprvKey = DeterministicKey.deserializeB58(xprv, network);
BigInteger privateKey = xprvKey.getPrivKey();
ECKey ecKey = ECKey.fromPrivate(privateKey);
// 创建交易信息实体
Transaction tran = new Transaction(network);
long changeAmount = totalMoney - amount - fee;
LogUtils.eTag("UTXOTransaction", changeAmount + "totalMoney:" + totalMoney + "amount:" + amount + "fee:" + fee);
// 输出地址
tran.addOutput(Coin.valueOf(amount), Address.fromString(network, this.to));
// 找零地址
if (changeAmount > DUST_THRESHOLD) {
tran.addOutput(Coin.valueOf(changeAmount), Address.fromString(network, wallet.getAddress()));
}
// 输入信息
for (UTXO output : getOutputs()) {
TransactionOutPoint transactionOutPoint = new TransactionOutPoint(network, output.getVout(), Sha256Hash.wrap(output.getTxHash()));
//这个添加签名输入的最后一个参数就是添加了SIGHASH_FORKID(0x40)
tran.addSignedInput(transactionOutPoint, Coin.valueOf(new BigDecimal(output.getAmount()).longValue())
, new Script(NumericUtil.hexToBytes(output.getScriptPubKey())), ecKey, Transaction.SigHash.ALL, true, isFork);
}
// 生产交易hash
String signedHex = NumericUtil.bytesToHex(tran.bitcoinSerialize());
String txHash = NumericUtil.beBigEndianHex(Hash.sha256(Hash.sha256(signedHex)));
return new BtcTxSignResult(signedHex, txHash);
}
获取NetworkParametersNetworkParameters说明
不同的币种NetworkParameters的里面的两个参数不同
int addressHeader = 0;
case LTC:
return 0x30;
case BCH:
case BTC:
case EOS:
return 0x00;
case DASH:
return 0x4C;
case DOGE:
return 0x1E;
int bip32HeaderP2PKHpriv
case LTC:
return 0x019D9CFE;
case BCH:
case BTC:
case EOS:
return 0x0488ADE4;
case DASH:
return 0x02FE52CC;
case DOGE:
return 0x0488E1F4;
UTXO数据UTXO的定义
新的交易模块设计:
/**
* @FileName : TransactionModule
* @date : 2020/6/15 10:46
* @author : Owen
* @description : 交易相关接口类
*/
object TransactionModule {
open class ValidationError : Exception() {
class EmptyValue() : ValidationError()
class InsufficientBalance() : ValidationError()
class NotEnoughForMinimumRequiredBalance() : ValidationError()
class TooFewAmount() : ValidationError()
class MaxAmountLimit() : ValidationError()
}
/**
* 转账金额处理
*/
interface IAmountDelegate {
val availableBalance: BigDecimal
var inputAmount: BigDecimal
fun setInputAmount(amount: String)
@Throws
fun validAmount(): Boolean
}
/**
* 发送到的地址处理
*/
interface IAddressDelegate {
fun setToAddress(toAddress: String)
@Throws
fun validAddress(): Boolean
}
/**
* 转账费用处理
*/
interface IFeeDelegate {
fun loadData()
@Throws
fun validFee(): Boolean
fun onClear()
}
/**
* 广播交易处理
*/
interface ISendDelegate {
fun signTransaction()
fun sendTransaction()
}
/**
* 发送交易流程代理
*/
interface ITransactionDelegate {
fun loadData()
fun setInputAmount(amount: String)
fun setToAddress(toAddress: String)
fun calculateFee()
fun setMemo(memo: String)
fun validTransactionData()
fun sendTransaction()
}
class Factory(private val wallet: Wallet) : ViewModelProvider.Factory {
override fun <T : ViewModel?> create(modelClass: Class<T>): T {
val presenter = TransactionPresenter()
when(wallet.chainType){
CoinEnum.BTC.symbol,
CoinEnum.BTC.symbol,
CoinEnum.BTC.symbol,
CoinEnum.BTC.symbol -> {
}
CoinEnum.ETH.symbol -> {
}
CoinEnum.EOS.symbol -> {
}
return presenter as T
}
}
}
/**
* @FileName : AmountDelegate
* @date : 2020/6/15 11:25
* @author : Owen
* @description : Amount交易金额代理类
*/
class AmountDelegate(balance: String) : TransactionModule.IAmountDelegate {
override val availableBalance: BigDecimal
get() = TODO("not implemented") //To change initializer of created properties use File | Settings | File Templates.
override var inputAmount: BigDecimal = BigDecimal.ZERO
override fun setInputAmount(amount: String) {
}
override fun validAmount(): Boolean {
return true
}
}
/**
* @FileName : TransactionPresenter
* @date : 2020/6/15 10:56
* @author : Owen
* @description : 交易代理实现
*/
class TransactionPresenter : ViewModel(), TransactionModule.ITransactionDelegate {
private var memo: String = ""
private val errorMessage = MutableLiveData<String>()
var amountDelegate: TransactionModule.IAmountDelegate? = null
var addressDelegate: TransactionModule.IAddressDelegate? = null
var feeDelegate: TransactionModule.IFeeDelegate? = null
var sendDelegate: TransactionModule.ISendDelegate? = null
override fun loadData() {
feeDelegate?.loadData()
}
override fun setInputAmount(amount: String) {
amountDelegate?.setInputAmount(amount)
}
override fun setToAddress(toAddress: String) {
addressDelegate?.setToAddress(toAddress)
}
override fun calculateFee() {
}
override fun setMemo(memo: String) {
this.memo = memo
}
override fun validTransactionData() {
try {
amountDelegate?.validAmount()
addressDelegate?.validAddress()
feeDelegate?.validFee()
sendTransaction()
} catch (e: TransactionModule.ValidationError) {
when (e) {
is TransactionModule.ValidationError.EmptyValue -> {
errorMessage.value = ""
}
else -> {
errorMessage.value = ""
}
}
}
}
override fun sendTransaction() {
sendDelegate?.signTransaction()
}
override fun onCleared() {
}
}
网友评论