比特币探究之三:交易池

先来看一则旧闻:

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

这里说的比特币内存池,里面都是还没有被纳入区块链中的交易,因此也叫交易池。当用户使用比特币发起交易之后,会通过P2P网络广播发送到各个节点,每个节点再把这个交易放入交易池中。随后矿工挖矿、也就是生成区块的时候,再从交易池中选择一部分交易进行打包。当区块生成以后,相关的交易就从交易池中移除。因此,可以想象,当交易特别火爆的时候,交易数量猛增,但是区块生成的速度仍然是稳定的,就可能会导致大量交易积压,长期得不到处理,这就会出现上图中的情况了。

那么问题来了,既然要从交易中选择一部分进行打包,那到底选谁、不选谁?依据什么来定?很简单,比特币规定,每个交易当中,所有的输入总和必须大于等于所有的输入总和,注意这里是大于等于!也就是说,你可以输入101个比特币,输出的只有100个比特币,可以吗?当然可以!那么多出的1个比特币哪里去了?答案是作为交易手续费,奖给了挖矿的矿工。再回到刚才的问题,矿工选哪个交易、不选哪个交易主要看什么?很显然,看哪个交易给的手续费更高呗!

因此,小伙伴们,如果你希望你的交易能够更优先地被打包确认,那就要记得适当提高交易手续费。目前主要的交易平台都会给出一个手续费参考值,基本上按平均值给可以了。那么,这个费用怎么计算?是不是交易数额越大,手续费就越大呢?胖兔告诉你,不是的,手续费只跟交易本身的大小有关系。什么叫交易的大小?大体上就是把所有的输入和输出加起来,一共占多少个字节。如果你一次转1000个BTC,但只用了一个UTXO,交易大小就会很小;而说不定你才转1个BTC,但用了10个UTXO,那对不起,交易大小反而上去了,极端情况下,你转账的金额可能还没有你要付的手续费高!

图 3-2 每日交易费用走势图

上图是blockchain给出的每日交易手续费走势图,截止本文写作时,每天的手续费总额是16.57个比特币。最高点发生在2017年12月22号,当天的手续费达到了1495个比特币!对比特币的价值没有概念?再来看下图:

图3-3 单个交易费用走势图

这是每个交易的平均手续费,单位是美元。当前每个交易平均要付手续费76.54美元,最高峰的时候要付160美元!这个手续费用可不便宜哦!

图3-4 交易池里交易总数

上图是比特币交易池里交易总数量变化走势图。现在交易池中的数量保持在2700到2800个左右,峰值最高的时候在2017年12月,差不多正式比特币最值钱的时候,交易总数走过18万个!这么多交易,那交易池一定很大喽!看看下面的图:

图3-5 交易池大小走图

可以看到交易池最大的时候曾经达到了139M,不过相对于当前的电脑内存容量来说,这其实根本不算什么,一部手机都可以轻松搞定。

好了,下面进入源码时间。


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

CTxMemPoolEntry

CTxMemPoolEntry指的是交易池中的单个条目,包含一条交易以及相关的数据。其数据声明部分是这样的:

图3-6 CTxMemPoolEntry数据成员

注意这里的数据成员全部是私有的。所有数据在构造函数里就被赋值或计算出来,之后只能通过类成员函数去修改。

tx就是交易指针,指向当前条目所对应的交易。

nFee就是交易手续费。

nTxWeight是交易重量,这是2017年比特币引入隔离见证时增加的,隔离见证到将来再研究。

nUsageSize是交易的内存使用量。这3个数据都是缓存用来便于统计计算的。

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

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

spendsCoinbase指的是这个交易是否花费了某个币基(Coinbase),所谓Coinbase就是挖矿时对矿工的奖励,比特币规定凡是成功挖到某个区块的矿工,除了交易费之外,还有额外奖励。2009年比特币刚出来的时候,挖到一个块能奖励50个比特币,之后每4年减半,现在只有12.5个比特币了。

sigOpCost是签名操作消耗,也就是这个交易的签名需要多少条指令。关于签名脚本,将来再详细研究。

feeDelta是手续费增量,增加后的手续费就是nFee + feeDelta,它的目的是为了在挖矿时调整交易优先级,因此这个值在初始化的时候肯定是0。

lockPoints是锁定点。所谓锁定点,可以是区块高度,也可以是时间,表示只有当达到一定的区块高度,或是到达某个时间点以后,这个交易才能被打包进区块。

接下来的两组数据,分别对应子孙交易和祖先交易。所谓子孙交易,就是依赖当前交易的后续交易,比如交易A是张三给李四10个BTC,交易B是李四再拿这这10个BTC给了王五8个BTC,那么交易B就是交易A的子孙,交易A就是交易B的祖先。如果交易A没有被选中打包,交易B自然就只能跟在后面等。

nCountWithDescendants、nSizeWithDescendants、nModFeesWithDescendants分别指的是当前交易以及所有子孙交易,它们的总数量、总大小和总手续费。

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


CTxMemPool

交易池的实现在比特币源码里算是比较复杂的部分了,光是类声明之前的注释就有70行,而且全部是巨长的句子,让当年连滚带爬才过英语四级的胖兔看得郁闷无比、几欲吐血。

由于CTxMemPool里的数据和成员函数实在太多,逐个介绍实在太累,还是只挑关键地来说吧(瘦兔:别听他忽悠,其实很多连胖兔自己都还没搞懂~)。源码不多贴了,小伙伴们可以自己对照着阅读理解。

mapTx。这是第一个重要的数据成员,也是CTxMemPoolEntry的集合,它的数据类型是indexed_transaction_set,这个类型是这样定义的:

图3-7 索引化交易集合的类型声明

这里使用了boost库的multi_index_container,它是个多索引容器,允许对一系列数据添加之个索引。容器里的元素类型就是CTxMemPoolEntry,并给它一共建了4个索引:首先是交易ID其实就是交易的hash;接着是手续费比例,也就是手续费除以交易大小,注意这里的手续费比是包含子孙交易的,CompareTxMemPoolEntryByDesendantScore是一个自定义的比较手续费比例的运算子;第三个是进入交易池的时间;最后一个是包含祖先交易的手续费比例。从前面的图中我们可以知道,交易数量最多的时候可达18万个,但有这4个索引,绝大多数情况下搜索交易时,都可以非常迅速地找到。

vTxHashes。用于存放所有的隔离见证hash。它的类型是std::vector<std::pair<uint256, txiter> >,这是一个vector,每个元素又是一个pair,pair的第一个元素就是见证hash,第二个元素类型txiter的定义是indexed_transaction_set::nth_index<0>::type::const_iterator,其实就是mapTx第一个索引的迭代子,通过它可以遍历所有的CTxMemPoolEntry,也就是遍历交易。

mapLinks。用于存放与每个交易相关的父、子节点。它的类型是std::map<txiter, TxLinks, CompareIteratorByHash>,其中的TxLinks是一个结构,包含parents、children两个数据成员,这两个成员的类型叫setEntries,是一个txiter的集合(std::set)。注意这个数据是私有的,它只在类内部使用,主要是加快访问上、下级节点的速度。

mapNextTx。它的类型是indirectmap<COutPoint, const CTransaction*>,indirectmap是在比特币里自定义的数据类型,是为了方便比较指针所指向的数据。我们知道,每个交易的输入其实都来自于之前的某个输出,mapNextTx存放是从每个COutPoint到后续交易的链接。它与mapLinks的区别,mapLinks里所指向的父子交易都还在交易池里,但mapNextTx里的COutPoint有可能已经被写入区块。

mapDeltas。存放的是每个交易所对应的交易费增量。

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

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

nTransactionsUpdated,当前已更新多少交易,用于触发挖矿操作。

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

totalTxSize,所有交易的大小,这里没有包含见证数据,与交易被存储时所占用的大小是不一样的。

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

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

blockSinceLastRollingFeeBump,上一次调整费率之后是否生成过区块。

rollingMinimumFeeRate,上一次调整的最小费率。

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

checksetSanityCheck,setSanityCheck用于设置对交易池进行健全性检查的频率。check则是对整个交易池进行检查,包括:每个交易的输入是否都是现有UTXO或者来自交易池中其他交易的输出;每个交易输入都在mapNextTx中;祖先交易状态正常;能通过mapNextTx找到子孙交易;另外还要对交易的大小、内存占用进行检查。注意这里的检查可以防止双花交易,我们知道每个输出只能被用作一次输入,用完了就会被销毁,但如果一个输出被用了两次,就被称为双重支付或者叫双花,这是数字货币必须要解决的问题。

addUncheckedremoveUnchecked就是添加或移除新的交易。

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

PrioritiseTransactionApplyDeltaClearPrioritisation用于调整交易优先级,一般在挖矿时使用。

RemoveStaged用于从交易池中移除一部分交易。这里移除的原因可能是过期、大小受限、重组、打包入区块、冲突、被替代等等。一个交易被移除后,它相关的祖先和子孙交易中的数据,都会被连带更新。

UpdateTransactionsFromBlock,受挖矿机制的影响,有时候矿工会提前挖出后面的区块,但因为没有链入主链,这个区块中包含的交易会被重新放入交易池,这时它们的子孙交易可能已经在交易池中了,UpdateTransactionsFromBlock将会找到这些子孙交易,并更新相关数据。

CalculateMemPoolAncestorsCalculateDescendants,当一个交易被放入内存池时,CalculateMemPoolAncestors用于找到它的所有祖先交易,CalculateDescendants找到所有子孙交易。

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

TrimToSize用于调整交易池大小,此时可能会移出一部分交易(原因设为大小受限)。

Expire用于将所有早于指定时间的交易,全部设定为过期。

交易池是比特币中非常重要的部分,这一块不啃下来,后面许多内容都不容易理解。好吧,这次就到这里。下一章准备开始琢磨挖矿。


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

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 158,560评论 4 361
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,104评论 1 291
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 108,297评论 0 243
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 43,869评论 0 204
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,275评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,563评论 1 216
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,833评论 2 312
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,543评论 0 197
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,245评论 1 241
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,512评论 2 244
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,011评论 1 258
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,359评论 2 253
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,006评论 3 235
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,062评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,825评论 0 194
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,590评论 2 273
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,501评论 2 268

推荐阅读更多精彩内容

  • 一、快速术语检索 比特币地址:(例如:1DSrfJdB2AnWaFNgSbv3MZC2m74996JafV)由一串...
    不如假如阅读 15,770评论 4 88
  • 以下为金马老师《精通比特币》解读的文稿整理。 第一章 介绍 1.1 什么是比特币 (1)比特币是一个区块链实现 (...
    梦之郎阅读 13,064评论 6 61
  • 本译文首次以“s先生”的笔名发表于公众号:补天遗石(ID:butianys)。译无达诂,欢迎交流指正(微信:dat...
    补天遗石阅读 4,742评论 0 4
  • 人有两种长相, 一种是物理长相,一种是精神长相, 我们无法选择自己的容貌, 但却可以改变自己的面相。 生活把一切都...
    涤荡千秋阅读 312评论 0 1
  • 素字训义 这本书以《素书》为名,素是什么意思呢?这要从宇宙的起源,道生天地万物说起。道是如何造化出来天地万物的呢?...
    云中哥哥阅读 769评论 0 1