比特币线程模型

最近在研究比特币源码, 发现很难有个突破点. 作为程序员, 在分析一个东西的时候, 很喜欢站在线程的角度来看这个东西. 于是就有了这篇文章. 作为以后深入分析源码的契机.

本文分析基于当前master(2018.7), 最近一个tag是v0.16.2rc1

基本线程模型

下图展示了新交易, 新区块和挖矿三个过程的线程交互模式.

其中Bitcoind process是一个不包含挖矿功能的全节点(新版本中已经移除了单独的挖矿线程), OtherNode是逻辑上与Bitcoind对等的其他节点, SpendProducer即交易产生节点, 大多数情况下它来自钱包或者其他节点的广播, SingleMiner可以理解为一个矿池中的矿工角色(单挖矿功能).

Bitcoind process包含了很多线程, 这儿只列出了比较核心的三个:

  • HttpServer(接受并处理rpc请求, 是一组线程), 所有通过tableRPC注册的命令最终都是在此线程中执行
  • ThreadSocketHandler(线程入口函数名), 处理节点间TCP连接的底层交互操作, socket读写等
  • ThreadMesageHandler(线程入口函数名), 主要处理由ThreadSocketHandler读写来的消息, 节点间交互逻辑的主要执行者


过程详细说明:

此处以新交易处理流程, 新区块处理流程和矿池中的矿工挖矿交互流程为例. 从这些交互的例子中也能有大概印象HttpServer和ThreadSocketHandler的交互侧重点差异所在.

新交易过程: (从New Transaction开始)

  1. 交易产生节点创建一笔交易后(交易数据结构,以下简称ntx, 是new transaction的简写), 通过inv消息将ntx发送该其连接的节点.
  2. 转换为全节点视角, 当全节点收到ntx后, 会将ntx异步发送给ThreadMesageHandler(共享内存方式, 线程间通信).
  3. ThreadMesageHandler处理对应消息(ProcessMessage函数的NetMsgType::TX对应逻辑):
  • checkWork, 主要在AcceptToMemoryPoolWorker函数中执行, 检查包括合规性检查, 最小交易费, 双花检查, 执行解锁脚本等等
  • memPool update, 基于上一步的检查结果, 将新交易加入memPool. memPool中包含了很多集合, 此步骤具体细节将在以后的文章中说明.
  • RELAY, 将ntx广播出去. 具体做法是将一个inv消息追加到每个node的发送队列中, 等ThreadMesageHandler线程自动检测到此消息并发送给对应节点.
  • 其他节点收到ntx后, 如果是全节点, 将会重复此新交易过程处理步骤.

新区块到达:(从New Block消息开始)

  1. 当一个矿池挖出一个新区块后, 这个矿池会尽快将此区块广播出去.
  2. 转换为收到新区块的全节点视角, 当全节点收到一个新区块后, 会消息异步发送给ThreadMesageHandler(同新交易过程).
  3. ThreadMesageHandler处理对应消息(ProcessMessage函数的NetMsgType::CMPCTBLOCK对应逻辑):
  • CheckHeader 主要检查新区块与当前最长链的关系, 此处假设新区块的previous block就是当前的最长链的最后一个区块, 假设其他所有检查都通过.
  • 进入ProcessNewBlock函数, 进行区块检查, 包括MerkleRoot, 所有交易合法性, SigOps等等.
  • 主链接受新区块
  • 更新memPool, 主要是将新区块中包含的交易信息从memPool中移除.
  • RELAY, 将新区块广播出去, 具体做法就是发送一个NewPoWValidBlock, 其连接的处理函数会将一个CMPCTBLOCK类型的消息写入所有连接Node的发送队列中, 等ThreadMesageHandler线程自动检测到此消息并发送给对应节点.
  • 其他节点收到新区块后, 如果是全节点, 将重复此新区块到达处理步骤.

矿工挖矿流程:(从getblocktemplate开始)

新版本去掉了内置的挖矿线程, 挖矿程序默认是一个单独进程, 通过此接口获得包括最新交易在内的区块模板, 并在此模板基础上挖矿.

  1. 矿工向全节点请求一个最新区块模板
  2. 全节点从其memPool中选出有价值的交易,产生区块头及CoinBase交易组装成区块模板
  3. 全节点将区块模板及其他限制参数(范围,大小等)写入返回值中, 返回给矿工
  4. 矿工开始挖矿
  5. 矿工挖矿成功后, 将新区块回传给全节点
  6. 全节点收到新区块后, 对其进行简单检查(区块hash, previousHash等), 然后进行新区块处理流程. 具体流程与新区块到达一致, 此不赘述
  7. RELAY, 中心节点将新区块广播出去, 与新区块处理流程一致, 此不赘述
  8. 收到新区块的其他节点, 如果是全节点, 将重复新区块到达处理步骤

挖矿核心代码

在前文一再说明, 新版本已经将挖矿的线程移除, 挖矿程序一般是一个独立运行的进程, 但在新分支中依然能够看到挖矿过程的代码. 此处去掉了与挖矿无关的操作, 只留下了核心代码以方便理解. 通过这些代码可以让我们对挖矿过程有个感性上的认识.

    bool mine(CBLock* pblock, long nMaxTries, int nInnerLoopCount){
         while(true){
             updateExternNonce(pblock); // modify part of baseCoin tx and, at the same time, merkleRoot has been changed
             while(blockTemplate->nonce == nInnerLoopCount ||  nMaxTries  == 0 || !CheckProofOfWork(pblock->GetHash(), pblock->nBits)){
                 ++blockTemplate->nonce ;
                 nMaxTries --;
             }
             if(blockTemplate->nonce == nInnerLoopCount ){
                  blockTemplate->nonce=0;
                  continue;
             }
             if(nMaxTries  == 0) break;
             return true;
        }
        return false;
    }

从代码中我们看到, 调用挖矿方法时, 调用者通过设置最大重试次数的方式控制挖矿运行时长. 每次挖矿事实上是通过调整nonce或者extraNonce的方式改变区块hash值, 进而完成一次挖矿尝试, 当找到小于难度值的hash值时代表挖矿成功.

nonce是区块头中专门为调整区块hash头而预留的可变字段.
extraNonce是区块第一笔交易(CoinBase交易)的解锁脚本的一部分. 因为CoinBase交易不需要解锁脚本, 所以这个区域可以被用作其他一些用途. CoinBase的解锁脚本被更改后, 其hash值会产生变化, 进而使整个区块的merkleRoot值发生变化, 进而导致区块头发生变化. 区块头发生变化后, 挖矿就又可以从0开始尝试nonce字段了.

BTech原创,未经许可不得转载

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

推荐阅读更多精彩内容