- 获取UTXOs
- 生成交易原始数据包
- 签名交易原始数据包
- 广播签名的交易原始数据包
UTXO模型
比特币作为一种数字加密货币, 没有采用传统的基于账户(account based)的模型,而是采用了称之为UTXO的账户模型。传统的基于账户的模型用户的资产往往通过关系型数据库以数字的形式记录在表里,而在比特币系统中没有账户, 那么资产是怎么记录的呢?
比特币的区块中记录的都是每笔交易。每笔交易都有若干交易输入,也就是资金来源,也都有若干笔交易输出,也就是资金去向。一般来说,每一笔交易都要花费(spend)一笔输入,产生一笔输出,而其所产生的输出,就是“未花费过的交易输出”,也就是 UTXO。因此有种说法, “其实并没有什么比特币,只有 UTXO”。
然而, 最原始的资金来源来自那里呢?在比特币中是挖矿,也就是除了coinbase交易之外,所有的资金来源都必须来自前面某一个或者几个交易的 UTXO。
举个例子,假设张三有100元, 李四有200元, 现在张三要转帐50元给李四, 用传统数据库实现:
- 检查张三, 李四账户的合法性
- 检查张三账户余额是否足够转账
- 开启事务, 在张三的账户-50, 同时在李四的账户+50
转账成功结束后, 张三账户应该有50元, 李四账户应该有250元。
现在,假设张三有100比特币, 李四有200比特币, 张三转账10比特币给李四。由于比特币系统没有账户的概念, 张三拥有100个比特币, 只是表示有100的未花费输出指向张三的地址, 或者说, 有若干个UTXO的收款人地址是张三, 而所有UTXO的总数恰好是100.
MIN_DUST_AMOUNT=10000; //最小有效交易金额,单位satoshi,即0.00000001 BTC
MIN_TRANSACTION_FEE=10000; //矿工费用的最小金额,单位satoshi
//获取未使用的交易(UTXO)用于构建新交易的输入数据块
client.listunspent(6,9999999,[TEST_ADDRESS],function(err, array_unspent) {
if (err) return console.log('ERROR[listunspent]:',err);
console.log('Unspent:', array_unspent);
var array_transaction_in=[];
var sum_amount=0;
for(var uu=0;uu<array_unspent.length;uu++){
var unspent_record=array_unspent[uu];
console.log(unspent_record);
if(unspent_record.amount>0){
sum_amount+=unspent_record.amount*100000000; //注意:因为JS语言缺省不支持64位整数,此处示例程序简单采用32位整数,只能处理交易涉及金额数值不大于0xFFFFFFF即4294967295 satoshi = 42.94967295 BTC。 实际应用程序需留意完善能处理64位整数
array_transaction_in[array_transaction_in.length]={&#34;txid&#34;:unspent_record.txid,&#34;vout&#34;:unspent_record.vout};
if( sum_amount > (MIN_DUST_AMOUNT+MIN_TRANSACTION_FEE) )
break;
}
}
//确保新交易的输入金额满足最小交易条件
if (sum_amount<MIN_DUST_AMOUNT+MIN_TRANSACTION_FEE) return console.log(&#39;Invalid unspent amount&#39;);
console.log(&#39;Transaction_in:&#39;, array_transaction_in);
//生成测试新交易的输出数据块,此处示例是给指定目标测试钱包地址转账一小笔测试比特币
//注意:输入总金额与给目标转账加找零金额间的差额即MIN_TRANSACTION_FEE,就是支付给比特币矿工的交易成本费用
var obj_transaction_out={
&#34;mieC38pnPwMqbMAN6sGWwHRQ3msp7nRnNz&#34;:MIN_DUST_AMOUNT/100000000, //目标转账地址和金额
&#34;mkiytxYA6kxUC8iTnzLPgMfCphnz91zRfZ&#34;:(sum_amount-MIN_DUST_AMOUNT-MIN_TRANSACTION_FEE)/100000000 //找零地址和金额,默认用发送者地址
};
console.log(&#39;Transaction_out:&#39;, obj_transaction_out);
//生成交易原始数据包
client.createrawtransaction(array_transaction_in,obj_transaction_out,function(err2, rawtransaction) {
if (err2) return console.log(&#39;ERROR[createrawtransaction]:&#39;,err2);
console.log(&#39;Rawtransaction:&#39;, rawtransaction);
//签名交易原始数据包
client.signrawtransaction(rawtransaction,function(err3, signedtransaction) {
if (err3) return console.log(&#39;ERROR[signrawtransaction]:&#39;,err3);
console.log(&#39;Signedtransaction:&#39;, signedtransaction);
var signedtransaction_hex_str=signedtransaction.hex;
console.log(&#39;signedtransaction_hex_str:&#39;, signedtransaction_hex_str);
//广播已签名的交易数据包
client.sendrawtransaction(signedtransaction_hex_str,false,function(err4, sended) { //注意第二个参数缺省为false,如果设为true则指Allow high fees to force it to spend,会在in与out金额差额大于正常交易成本费用时强制发送作为矿工费用(谨慎!)
if (err4) return console.log(&#39;ERROR[sendrawtransaction]:&#39;,err4);
console.log(&#39;Sended TX:&#39;, sended);
client.listaccounts(function(err, account_list) {
if (err) return console.log(err);
console.log(&#34;Accounts list:\n&#34;, account_list); //发送新交易成功后,可以核对下账户余额变动情况
});
});
});
}); |
|