比特币源码研读--交易细节

0x00 读码即挖矿

前两天看到群里还有人在讨论ETH和EOS的DAPP开发,看来区块链的落地还是一线希望,大家可以继续给自己的信仰充值。充值方式众多,比如加仓,Fomo,或是写DAPP,读代码。那我继续前两次的操作,继续阅读BTC的代码,版本0.8.2。上次粗读了一番交易的流程,大概的来龙去脉也略知一二,这次就按照关键的执行函数来扣一下具体的细节。

0x01 函数CreateTransaction

函数原型如下:

我们先看一下该函数的具体引用,出现在rpcwallet.cpp中,具体的用途就是RPC调用,进行转账交易。

现在来看CreateTransaction函数的返回值和参数:

返回值:如果创建成功返回true,否则false
参数:
1,const vector<pair<CScript, int64> >& vecSend:目标地址和金额,数组,支持批量转账
2,CWalletTx& wtxNew:生成的钱包关联交易详情
3,int64& nFeeRet:gas费用
4,std::string& strFailReason:失败原因

整个代码的模块是一个loop,通过找到符合条件的utxo再break出来,看一看代码的内部实现:

1,判断发送目标地址和金额是否合法,没啥好说的

2,构造交易输出,并丢弃粉尘交易,防止DDOS攻击,这块下文会详细描述

3,构造交易出入,也就是根据规则找到发送方最优的utxo集合,SelectCoins下文详细描述

4,先计算优先级,再根据优先级推导出交易费用,下文详细描述

5,计算找零,并创建新的找零钱包公钥,把找零加入到vOut中

6,签名,并计算交易费用,如果费用不满足条件,再重新进入循环,重复上述流程

7,填充好WalletTX的vIn和vOut后,用vIn的prevout来填充WalletTX的vtxPrev

0x02 粉尘交易Dust

前文我们已经讲过,比特币的交易并不是传统意义上的数字加减运算,它实质是utxo的转移。所以A向B转移1个BTC这个动作,产生的结果是未知的,比如就可以是1个完整的BTC转移,也有可能是1000个0.001个BTC的转移,也有可能是1.05个BTC转移(0.05个找零),当然用户不需要自己去手工配置,代码已经帮我们完成(下文的SelectCoin),但有时候还是无法避免有dust交易的产生。dust的存在会造成区块链网络上DDOS攻击,攻击者通过发送许多这样小金额的交易,来堵塞整个网络。在比特币的网络中,如果交易费(Fee)用高于1/3的交易价值(Value),则被视为粉尘交易。 常规的来说,一个P2PKH交易,由于其最小体积时为一个输入,一个输出,总共 '148 + 34 = 182' 字节,而交易手续费是由每个节点配置的minRelayTxFee(比特币中默认为'0.00001BTC/KB')决定的,故而手续费就是 '182 / 1000 minRelayTxFee',那么其的3倍即 '546 / 1000 minRelayTxFee',也即默认为 0.00000546 BTC,在BCH网络上也是一样的。

钱包在准备支付金额的时候有一个既定的规则,就是在众多输入(inputs)中筹备支付金额的时候尽量避免产生小于0.01BTC的金额变动(比如你要支付5.005BTC,钱包尽可能的选择3+2.005或者1+1+3.005,而不是5+0.005)。

Dust代码描述如下:

0x03 交易费用计算Fee

数额越大、币龄(age)越高优先级越高

如果你发送金额太小或者是你的比特币刚开采出来不久,那么你的转账就不再免费之列。每一个交易都会分配一个优先级,这个优先级通过币的新旧程度、交易的字节数和交易的数量。具体来说,对于每一个输入(inputs)来讲,客户端会先将比特币的数量乘以这些币在块中存在的时间(币龄,age),然后将所有的乘积加起来除以此次交易的大小(以字节为单位),计算公式:priority = sum(input_value_in_base_units * input_age)/size_in_bytes,计算结果如果小于0.576,那么该交易就必须支付手续费。如果你确实大量的小额输入,又想免费转出,这时候你可以加一个数额大的、币龄大的比特币金额(多余的找零返回),就会将平均优先级提高,从而可以免费转出比特币。
代码如下:

在转账的最后客户端会检测本次转账的大小(以字节为单位),大小一般取决于输入和输出的数额大小。
如果该次转账的大小超过10000字节但是优先级符合免费的标准,那么仍然可以享受免费转账,否则需要支付手续费。没1000字节的费用默认是0.0001BTC,但是你也可以在客户端里进行追加,依次打开选项卡“设置>选项>主要”进行手续费的调整。
相关代码如下:

0x04 寻找最佳utxo集合策略SelectCoins

这个函数比较有意思,我们先看下函数实现

先说一说函数的返回值和参数

返回值:成功找到TxIn的集合返回true,否则返回false
nTargetValue:目标金额
setCoinsRet:要返回的TxIn集合
nValueRet:实际金额(有可能包含找零)

AvailabeCoins通过在当前钱包视图中预过滤筛选出COutput的集合,筛选策略主要是判断当前的引用输入是否经过确认等。
下一步再来看看SelectCoinsMinConf这个函数,看到(1,6),(1,1),(0,1)这样的硬编码顿时就有一种紧张感,还好参数的命名比较直观。这三次相同函数不同参数的函数调用,其实反映了一种选择策略,就是按照交易被区块链确认次数递减来扩大选择范围,转账给自己优先要用通过1次确认的,转给别人要优先用通过6次确认的。这就是为什么我们现在在交易所进行法币场外交易时,交易所作为第三方担保,一般会在交易经过6次甚至更多次确认后才会把法币打给转出方,这样实施双花恶意攻击就会难上加难。

再来说说SelectCoinsMinConf的策略:

1,如果存在某UTXO值正好等于发送金额nValue(已包含手续费nFee),则将该UTXO加入目标交易集并返回成功
2,找出账户中UTXO值小于发送金额nValue的UTXO集vValue,并将vValue中所有UTXO值求和为nTotalLower,并找出所有UTXO值大于nValue的最小值nLowestLarger,再分两种情况
2.1:nTotalLower小于nValue,如果nLowestLarger存在,则将该值对应的pcoinLowestLarger交易加入目标交易集并返回成功,如果nLowestLarger不存在,则说明“余额”不足,返回失败
2.2:nTotalLower大于nValue,则使用随进逼近法(最多1000次)找出UTXO值的和nBest最接近nValue的集合vfBest,看nBest和nLowestLarger(如果存在)谁更接近nValue,则选择谁为相应的目标UTXO集,并返回成功

代码如下:

      

0x05 总结

从上面分析来看,交易的流程主要就是创建交易输出vOut,以及寻找到交易输入vIn,然后配置上gas,广播到网络节点中去。比特币的交易模块是整个区块链技术体系中比较重要的部分,也是比较晦涩难懂的部分,值得花上一些时间细细品读。由于本人水平有限,文中难免有理解不透的地方,还请大家多多指教!

网友Black在我学习的过程中给出了大量的指点和帮助,特此感谢!

参考:
1,http://www.btc38.com/btc/btc_learning/220.html
2,https://zhuanlan.zhihu.com/p/43203734

发布了929 篇原创文章 · 获赞 968 · 访问量 153万+
展开阅读全文

没有更多推荐了,返回首页

©️2019 CSDN 皮肤主题: 技术黑板 设计师: CSDN官方博客

分享到微信朋友圈

×

扫一扫,手机浏览