主页 > imtoken安装下载地址 > 比特币交易和内存池,比特币交易和内存池

比特币交易和内存池,比特币交易和内存池

imtoken安装下载地址 2023-07-24 05:14:40

我们先来看一条旧闻:

图 3-1 关于比特币内存池的旧闻

这里的比特币内存池包含尚未并入区块链的交易,因此也称为交易池。当用户使用比特币发起一笔交易时,它会通过 P2P 网络广播到各个节点,然后各个节点将这笔交易放入交易池中。然后矿工挖矿,也就是出块的时候,然后从交易池中选择一部分交易进行打包。区块生成后,相关交易从交易池中移除。因此,可以想象,当交易特别火爆时,交易数量急剧增加,但出块速度依然稳定,可能会导致交易积压,长时间无法处理。情况。

那么问题来了,既然需要选择交易的一部分进行打包,那么该选谁,不选谁呢?基于什么?很简单,比特币规定在每一笔交易中,所有输入的总和必须大于或等于所有输入的总和。注意这里大于等于!也就是说,你可以输入 101 个比特币,只能输出 100 个比特币,好吗?当然!额外的 1 比特币去哪儿了?答案是作为交易费用bitcoin交易,奖励给挖矿的矿工。回到上一个问题,矿工选择什么交易,不选择什么交易?很明显,看看哪个交易的手续费更高!

因此,朋友们,如果您希望您的交易更优惠地打包确认,请记得适当增加交易费用。目前各大交易平台都会对手续费给出一个参考值,基本上都是取平均值。那么,这笔费用是如何计算的呢?交易金额越大,手续费越高吗?胖兔告诉你,不,手续费只和交易本身的大小有关。交易规模是多少?一般来说,就是把所有的输入输出相加,总共占用多少字节。如果一次转账1000 BTC,但只使用一个UTXO,交易规模会非常小;可能你只转了1个BTC,却使用了10个UTXO,那么对不起,交易量反而会增加,极端情况下,你转的金额可能没有你要支付的手续费那么高!

图3-2 每日交易手续费走势图

上图是区块链给出的每日交易手续费走势图,截至发稿,每日总手续费为16.57个比特币。最高点出现在2017年12月22日,当天的手续费达到1495比特币!不知道比特币的价值?再看下图:

图3-3 单笔交易费用图表

这是每笔交易的平均费用,单位是美元。目前的平均费用为每笔交易 7 美元6.54 美元,高峰时为 160 美元!这个费用不便宜!

bitcoin交易

图 3-4 交易池中的交易总数

上图是比特币交易池的交易总数趋势图。现在矿池中的交易数量保持在 2700 到 2800 左右,峰值峰值出现在 2017 年 12 月,当时比特币最有价值,总交易量突破 18 万!这么多交易,交易池一定是巨大的!看下图:

图 3-5 交易池规模图

可以看到,交易池最大的时候已经达到了139M,但是和现在的电脑内存容量相比,这根本不算什么,一部手机就能轻松搞定。

好,我们进入源码时间。

交易池的代码在txmempool.h/cpp中定义,主要涉及CTxMemPoolEntry和CTxMemPool两个类。

CTxMemPoolEntry

CTxMemPoolEntry 是指交易池中的单个条目,包含交易和相关数据。数据声明部分如下:

图 3-6 CTxMemPoolEntry 数据成员

bitcoin交易

请注意,这里的所有数据成员都是私有的。所有数据都在构造函数中赋值或计算,然后只能通过类成员函数修改。

tx为交易指针,指向当前入口对应的交易。

nFee 是交易费用。

nTxWeight 是交易权重,是在 2017 年比特币引入 Segregated Witness 时添加的,未来会研究 Segregated Witness。

nUsageSize 是事务的内存使用量。这三个数据被缓存起来,方便统计计算。

nTime 是交易加入交易池的时间。

entryHeight是加入交易池时区块链的高度。

spendsCoinbase 是指交易是否花费一定的 Coinbase。所谓Coinbase就是矿工在挖矿过程中的奖励。比特币规定,所有成功挖出某个区块的矿工,除了交易手续费外,还有额外的奖励。 2009年比特币刚出现的时候,挖一个区块可以奖励50比特币,然后每4年减半,现在只有12.5比特币。

sigOpCost 为签名操作成本,即本次交易的签名需要多少条指令。关于签名脚本,我们以后会详细研究。

feeDelta为费用增量,增加的费用为nFee + feeDelta。其目的是在挖矿时调整交易优先级,所以初始化时这个值必须为0。

lockPoints 是锁定点。所谓锁点,可以是区块高度,也可以是时间,也就是说,交易只有达到一定的区块高度或者一定的时间点,才能被打包进区块。

bitcoin交易

接下来的两组数据分别对应后代事务和祖先事务。所谓后代事务,就是依赖于当前事务的后续事务。比如交易A是张三给李四10个BTC,交易B是李四拿了这10个BTC给王五8个BTC,那么交易B就是交易A的后代,交易A就是交易B的祖先。如果没有选择交易A进行打包,交易B自然要在后面等待。

nCountWithDescendants、nSizeWithDescendants 和 nModFeesWithDescendants 分别是指当前交易和所有后代交易,它们的总数、总大小和总手续费。

nCountWithAncestors、nSizeWithAncestors、nModFeesWithAncestors、nSigOpCostWithAncestors分别是指当前交易和所有后代交易,它们的总数、总大小、总手续费和总签名消耗。

CTxMemPool

交易池的实现是比特币源码中比较复杂的部分。班级宣言前有70行评论,而且都是大句子,让翻滚爬起来才英语四级的胖兔子显得很郁闷,想吐血。

因为CTxMemPool里面的数据和成员函数太多了,一一介绍太累了,就挑重点吧(瘦兔:别听他的,其实很多连胖兔自己看不懂~)。源码就不贴了,小伙伴们可以自行阅读理解。

地图发送。这是第一个重要的数据成员,也是 CTxMemPoolEntry 的集合。其数据类型为indexed_transaction_set,定义如下:

图3 -7 索引事务集合的类型声明

这使用了 boost 库的 multi_index_container,它是一个多索引容器,允许为一系列数据添加索引。容器中的元素类型为CTxMemPoolEntry,一共建了4个索引:第一,交易ID其实就是交易的hash;然后是手续费比率,也就是手续费除以交易规模,注意这里的手续费比率包括后代交易。 CompareTxMemPoolEntryByDesendantScore 是一个自定义算子,比较交易费率;三是进入交易池的时间;最后一个是包含祖先交易的交易费用比率。从上图我们可以知道,交易的最大数量可以达到18万,但是有了这四个索引bitcoin交易,在大多数情况下,搜索交易的时候,都能很快找到。

vTxHash。用于存储所有隔离见证哈希。它的类型是std::vector >,是一个向量,每个元素都是一对。 pair的第一个元素是witness hash,第二个元素类型txiter的定义是indexed_transaction_set::nth_index::type::const_iterator其实就是mapTx的第一个index的迭代器,通过它可以遍历所有的CTxMemPoolEntry,也就是遍历事务。

bitcoin交易

地图链接。用于存储与每个事务相关的父节点和子节点。它的类型是std::map,其中TxLinks 是一个包含两个数据成员,父母和孩子的结构。这两个成员的类型称为setEntries,是txiter(std::set)的集合。注意这个数据是私有的,只在类内部使用,主要是为了加快上下节点的访问速度。

mapNextTx。它的类型是indirectmap,是比特币中为了方便比较指针所指向的数据而自定义的数据类型。我们知道每笔交易的输入实际上来自于之前的输出,mapNextTx 存储了从每一个 COutPoint 到后续交易的链接。它与mapLinks的区别在于mapLinks中指向的父子交易还在交易池中,但是mapNextTx中的COutPoint可能已经写入了区块。

mapDeltas。存储的是每次交易所对应的交易手续费增量。

其他几个重要的私有数据成员,主要是:

nCheckFrequency,表示在2^32秒内,一共要检查几次交易池。

nTransactionsUpdated,当前更新了多少交易以触发挖矿操作。

minerPolicyEstimator 是一个 CBlockPolicyEstimator 类变量,用于挖矿策略评估。

totalTxSize,所有交易的大小,不包括见证数据,与交易存储时占用的大小不同。

cachedInnerUsage,所有map成员的累积元素占用的动态内存。包括mapTx、mapNextTx、mapDeltas等。

lastRollingFeeUpdate,上次调整费率的时间。

blockSinceLastRollingFeeBump,自上次调整速率后是否已生成块。

bitcoin交易

rollingMinimumFeeRate,最后调整的最低费率。

最后介绍比较重要的成员函数:

check 和 setSanityCheck,setSanityCheck 用于设置对事务池进行完整性检查的频率。 check是检查整个交易池,包括:每笔交易的输入是现有的UTXO还是交易池中其他交易的输出;每个交易输入都在 mapNextTx 中;祖先交易状态正常;可以通过 mapNextTx 找到后代交易;还要检查事务大小和内存使用情况。注意这里的check可以防止双花交易,我们知道每个输出只能作为输入一次,用完就会销毁,但是如果一个输出被使用了两次,就叫做双花-spend or double-spend,这是数字货币必须解决的问题。

addUnchecked,removeUnchecked是添加或删除新事务。

HasNoInputsOf 用于检查交易的所有输入是否不在交易池中。也就是说,所有祖先交易都被打包进了区块。

PrioritiseTransaction、ApplyDelta、ClearPrioritisation 用于调整交易优先级,一般用于挖矿。

RemoveStaged 用于从事务池中删除部分事务。这里删除的原因可能是过期、大小限制、重组、打包成块、冲突、被取代等。删除交易后,其相关祖先和后代交易中的数据将一起更新。

UpdateTransactionsFromBlock,受挖矿机制影响,有时矿工会提前挖出后面的区块,但由于没有链接到主链,这个区块中包含的交易会被放回交易池,然后他们的后代交易可能已经在交易池中,UpdateTransactionsFromBlock 会找到这些后代交易并更新相关数据。

CalculateMemPoolAncestors,CalculateDescendants,当一个事务被放入内存池时,CalculateMemPoolAncestors用于查找其所有祖先事务,CalculateDescendants查找所有后代事务。

GetMinFee 用于计算加入交易池所需的最低费用。

TrimToSize 用于调整交易池的大小,此时可能会移除部分交易(原因设置为大小限制)。

Expire 用于将所有早于指定时间的事务设置为过期。

交易池是比特币非常重要的一部分,下面的很多内容如果不啃这部分就不容易理解。嗯,这次就到这里了。下一章已经准备好开始考虑挖矿了。

本文原作者胖兔(魏兆华)首发于简书,欢迎转载,转载请注明出处。