比特币源码研读之四

本篇文章涉及的文件有:

init.cpp、validation.h、register.h、blockchain.cpp、net.cpp、misc.cpp、server.h、rpcwallet.cpp

我们接着上篇文章继续介绍一些参数初始化的操作。在上一篇文章中我们分析了设置mempool容量的相关参数,也就是说mempool的大小是由限制的,当mempool不足以容纳网络中所有未被打包的交易时,节点可以根据交易费用的大小,有选择的控制哪些交易可以进入交易池。-incrementalrelayfee就是其中之一,故当mempool容量不足时,将根据incrementalrelayfee调整能进入mempool的交易(有待检验具体使用)。

    if (gArgs.IsArgSet("-incrementalrelayfee"))
    {
            CAmount n = 0;
            if (!ParseMoney(gArgs.GetArg("-incrementalrelayfee", ""), n))
                return InitError(AmountErrMsg("incrementalrelayfee", gArgs.GetArg("-incrementalrelayfee", "")));
            incrementalRelayFee = CFeeRate(n);
     }

比特币(UTXO)的所有权是通过记录在交易输出中的锁定脚本来控制的,在验证交易有效性时,很重要的一步就是验证解锁脚本是否能正确执行。可以通过设置参数-par和-nScriptCheckThreads来指定验证解锁脚本的线程数。如果-par没有设置或设置为小于0,则取当前系统支持同时运行的线程数量,但不可超过最大值16。

    // -par=0 means autodetect, but nScriptCheckThreads==0 means no concurrency
    nScriptCheckThreads = gArgs.GetArg("-par", DEFAULT_SCRIPTCHECK_THREADS);
    if (nScriptCheckThreads <= 0)
        nScriptCheckThreads += GetNumCores();//取当前系统支持同时运行的线程数量
    if (nScriptCheckThreads <= 1)
        nScriptCheckThreads = 0;
    else if (nScriptCheckThreads > MAX_SCRIPTCHECK_THREADS)//最大值16
        nScriptCheckThreads = MAX_SCRIPTCHECK_THREADS;

比特币网络自从2009年运行至写本文章时,共产生了490608个块,总数据量约150G(Bitcoin Core钱包在Mac上的统计)。但很多交易历史记录是可以不存储的(如交易的所有输出都已经花费了),故从0.12版本程序中提供了参数-prune来设置存储区块数据(blocks/blk.dat)和恢复数据(blocks/rev.dat)的最大值(https://github.com/bitcoin/bitcoin/blob/v0.12.0/doc/release-notes.md#wallet-pruning
)。-prune参数的单位是M,如果设置为1,表示可以通过rpc调用pruneblockchain(height)进行手动裁剪,容量大小取uint64_t类型的最大值。但设置的-prune的值不能小于MIN_DISK_SPACE_FOR_BLOCK_FILES 550M. 值得注意的是,即使启动-prune模式,节点也还是要下载整个区块链网络的数据的,只不过不会存储所有的数据。另外,如果启动了-prune模式,需要重新建立索引时,需要重新下载整个区块链网络数据。相关代码如下:

    int64_t nPruneArg = gArgs.GetArg("-prune", 0);
        if (nPruneArg < 0) {//不能小于0
        return InitError(_("Prune cannot be configured with a negative value."));
    }
    nPruneTarget = (uint64_t) nPruneArg * 1024 * 1024;
    if (nPruneArg == 1) {  // manual pruning: -prune=1
        LogPrintf("Block pruning enabled.  Use RPC call pruneblockchain(height) to manually prune block and undo files.\n");
        nPruneTarget = std::numeric_limits<uint64_t>::max();//uint64_t类型的最大值
        fPruneMode = true;
    } else if (nPruneTarget) {
        if (nPruneTarget < MIN_DISK_SPACE_FOR_BLOCK_FILES) {
            return InitError(strprintf(_("Prune configured below the minimum of %d MiB.  Please use a higher number."), MIN_DISK_SPACE_FOR_BLOCK_FILES / 1024 / 1024));
        }
        LogPrintf("Prune configured to target %uMiB on disk for block and undo files.\n", nPruneTarget / 1024 / 1024);
        fPruneMode = true;
    }

应用程序一般都是通过RPC与Bitcoin节点进行交互,通过节点提供的RPC服务获取钱包、交易、块等一系列信息。我们来看一下这些RPC命令在程序中的定义:

    RegisterAllCoreRPCCommands(tableRPC);
    static inline void RegisterAllCoreRPCCommands(CRPCTable &t)
    {
        RegisterBlockchainRPCCommands(t);// 注册链、块、交易等相关的RPC命令
        RegisterNetRPCCommands(t);//注册网络相关的RPC命令
        RegisterMiscRPCCommands(t);// 注册一些混合的命令,日志、验证地址等
        RegisterMiningRPCCommands(t);// 注册挖矿相关的RPC命令
        RegisterRawTransactionRPCCommands(t);// 注册raw交易(编码级)相关的RPC命令
    }

我们以RegisterBlockchainRPCCommands为例看下具体的RPC命令注册流程,其他方法都是类似的,就不展开说明了。

   void RegisterBlockchainRPCCommands(CRPCTable &t)
   {
    for (unsigned int vcidx = 0; vcidx < ARRAYLEN(commands); vcidx++)
        t.appendCommand(commands[vcidx].name, &commands[vcidx]);
   }

RegisterBlockchainRPCCommands会把一系列RPC命令的定义注册在CRPCTable中,即当接到RPC请求时,将从CRPCTable找到匹配的RPC调用。CRPCTable中定义一个键值对mapCommands,值为RPC命令的名称,键为命令对应的信息(CRPCCommand),并且可以通过appendCommand方法将该对应关系存储于CRPCTable中。

    std::map<std::string, const CRPCCommand*> mapCommands;//定义于CRPCTable中。

    class CRPCCommand
    {
    public:
        std::string category;//种类
        std::string name;//名称
        rpcfn_type actor;//执行方法
        bool okSafeMode;//是否允许在安全模式下被调用
        std::vector<std::string> argNames;//参数
    };

我们可以看下RegisterBlockchainRPCCommands中涉及的CRPCCommand的定义:

   static const CRPCCommand commands[] =
   { //  category              name                      actor (function)         okSafe argNames
    //  --------------------- ------------------------  -----------------------  ------ ----------
    { "blockchain",         "getblockchaininfo",      &getblockchaininfo,      true,  {} },
    { "blockchain",         "getchaintxstats",        &getchaintxstats,        true,  {"nblocks", "blockhash"} },
    { "blockchain",         "getbestblockhash",       &getbestblockhash,       true,  {} },
    { "blockchain",         "getblockcount",          &getblockcount,          true,  {} },
    { "blockchain",         "getblock",               &getblock,               true,  {"blockhash","verbosity|verbose"} },
    { "blockchain",         "getblockhash",           &getblockhash,           true,  {"height"} },
    { "blockchain",         "getblockheader",         &getblockheader,         true,  {"blockhash","verbose"} },
    { "blockchain",         "getchaintips",           &getchaintips,           true,  {} },
    { "blockchain",         "getdifficulty",          &getdifficulty,          true,  {} },
    { "blockchain",         "getmempoolancestors",    &getmempoolancestors,    true,  {"txid","verbose"} },
    { "blockchain",         "getmempooldescendants",  &getmempooldescendants,  true,  {"txid","verbose"} },
    { "blockchain",         "getmempoolentry",        &getmempoolentry,        true,  {"txid"} },
    { "blockchain",         "getmempoolinfo",         &getmempoolinfo,         true,  {} },
    { "blockchain",         "getrawmempool",          &getrawmempool,          true,  {"verbose"} },
    { "blockchain",         "gettxout",               &gettxout,               true,  {"txid","n","include_mempool"} },
    { "blockchain",         "gettxoutsetinfo",        &gettxoutsetinfo,        true,  {} },
    { "blockchain",         "pruneblockchain",        &pruneblockchain,        true,  {"height"} },
    { "blockchain",         "verifychain",            &verifychain,            true,  {"checklevel","nblocks"} },

    { "blockchain",         "preciousblock",          &preciousblock,          true,  {"blockhash"} }
  };

如果节点也作为钱包使用,可以注册与钱包相关的RPC命令,如获取新地址、获取余额、导出私钥等,这里就不展开讲了,跟上面介绍的RegisterBlockchainRPCCommands类似。

   #ifdef ENABLE_WALLET
       RegisterWalletRPCCommands(tableRPC);
   #endif

区块链节点之间组成一个P2P网络,节点之间的建立连接的超时设置通过-timeout参数来指定,默认值为5000毫秒。

    nConnectTimeout = gArgs.GetArg("-timeout", DEFAULT_CONNECT_TIMEOUT);
    if (nConnectTimeout <= 0)
        nConnectTimeout = DEFAULT_CONNECT_TIMEOUT;

在0.14版本的release note(https://bitcoin.org/en/release/v0.14.0)中提到:

Since the changes in 0.12 to automatically limit the size of the mempool and improve the performance of block creation in mining code it has not been important for relay nodes or miners to set -minrelaytxfee. With this release the following concepts that were tied to this option have been separated out:
• incremental relay fee used for calculating BIP 125 replacement and mempool limiting. (1000 satoshis/kB)
• calculation of threshold for a dust output. (effectively 3 * 1000 satoshis/kB)
• minimum fee rate of a package of transactions to be included in a block created by the mining code. If miners wish to set this minimum they can use the new -blockmintxfee option. (defaults to 1000 satoshis/kB)
The -minrelaytxfee option continues to exist but is recommended to be left unset.

我们可以看出参数-minrelaytxfee一分为三:-incrementalRelayFee、-dustrelayfee、-blockmintxfee。分别用于控制交易费用增长率、dust(灰尘)交易费率、交易被打包到块中的最小费率。同时,也建议不要设置- minrelaytxfee了。

    if (gArgs.IsArgSet("-minrelaytxfee")) {
        CAmount n = 0;
        if (!ParseMoney(gArgs.GetArg("-minrelaytxfee", ""), n)) {
            return InitError(AmountErrMsg("minrelaytxfee", gArgs.GetArg("-minrelaytxfee", "")));
        }
        // High fee check is done afterward in CWallet::ParameterInteraction()
        ::minRelayTxFee = CFeeRate(n);
    } else if (incrementalRelayFee > ::minRelayTxFee) {//最低不能低于incrementalRelayFee
        // Allow only setting incrementalRelayFee to control both
        ::minRelayTxFee = incrementalRelayFee;
        LogPrintf("Increasing minrelaytxfee to %s to match incrementalrelayfee\n",::minRelayTxFee.ToString());
    }
    // Sanity check argument for min fee for including tx in block
    // TODO: Harmonize which arguments need sanity checking and where that happens
    if (gArgs.IsArgSet("-blockmintxfee"))//检查参数blockmintxfee的值是否符合Money的格式要求
    {
        CAmount n = 0;
        if (!ParseMoney(gArgs.GetArg("-blockmintxfee", ""), n))
            return InitError(AmountErrMsg("blockmintxfee", gArgs.GetArg("-blockmintxfee", "")));
    }

    // Feerate used to define dust.  Shouldn't be changed lightly as old
    // implementations may inadvertently create non-standard transactions
    if (gArgs.IsArgSet("-dustrelayfee"))//后续将分析什么是dust utxo以及怎么处理和避免生成
    {
        CAmount n = 0;
        if (!ParseMoney(gArgs.GetArg("-dustrelayfee", ""), n) || 0 == n)
            return InitError(AmountErrMsg("dustrelayfee", gArgs.GetArg("-dustrelayfee", "")));
        dustRelayFee = CFeeRate(n);
}

好了,本篇文章就先分析到这。因本人水平有限,如有问题,欢迎大家批评指出,非常感谢。

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

推荐阅读更多精彩内容

  • 由于近期比较忙,所以源码研读系列更新较之前有点慢,但不管怎么样源码研读系列将会继续写下去的,保证每周至少有一篇,这...
    菜菜子_forest阅读 2,774评论 2 4
  • 区块链研习社比特币源码研读班今天研读第五步,参数解析 一总体结构图 二今天阅读的代码段 三 分析《1 解析mai...
    剑有偏锋阅读 306评论 0 0
  • 在如今日新月异的年代,时间就是金钱,谁能在最短的时间里拿出方案,做出结果,谁就能更多赢得成功的机会。其中出色的决断...
    幽兰君阅读 725评论 2 1
  • 惠子曰︰“子非魚,安知魚之樂?”莊子曰:“子非我,安知我不知魚之樂?”苦樂瞬息反覆,知之或疑之,均無不可。 莊子知...
    凌谷阅读 1,754评论 0 2
  • 今天熟男看到一遍文章,给标题党摄住了: 古巴酿酒师使用避孕套酿造葡萄酒 古巴的一位酿酒师用避孕套、热带香料和古巴葡...
    张锦全阅读 145评论 0 0