美文网首页
比特币源码研读之四

比特币源码研读之四

作者: 自如致知 | 来源:发表于2017-10-22 16:03 被阅读0次

本篇文章涉及的文件有:

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);
}

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

相关文章

  • 比特币源码研读

    forest21000版 比特币源码研读之一比特币源码研读之二比特币源码研读之三比特币源码研读之四比特币源码研读之...

  • 比特币源码研读之十一

    比特币源码研读系列已经发表了十篇了,通过这十篇源码研读系列让我对比特币源码及比特币运行原理有了进一步的理解,也让我...

  • 比特币源码研读之一

    比特币源码研读之一——区块链研习社 《比特币源码研读班》 一看文件夹结构 和 github编译依赖,分析的依赖库 ...

  • 比特币源码研读之四

    比特币源码研读系列已经更新了3篇文章了,每篇文章都得到了很多朋友的关注和讨论。也有很多朋友在看了我的研读系列之后加...

  • 比特币源码研读之四

    本篇文章涉及的文件有: init.cpp、validation.h、register.h、blockchain.c...

  • 比特币源码研读之一

    作者:区块链研习比特币源码研读班 菜菜子 一、源码下载 本文比特币源码下载地址为:https://github.c...

  • 比特币源码研读之四.1

    区块链研习社比特币源码研读班今天研读第五步,参数解析 一总体结构图 二今天阅读的代码段 三 分析《1 解析mai...

  • 比特币源码研读之三

    区块链研习社比特币源码研读班今天研读第四步,AppInit初始化 一总体结构图 二AppInit函数

  • 比特币源码研读班第五期招募令

    比特币源码研读班是由区块链研习社于2017年6月24日组织成立的源码研读班,从成立之初到现在已成功举办了四期,参与...

  • 比特币源码研读之二

    区块链研习社比特币源码研读班今天研读第二,第三流程,SetupEnvironment和noui_connect函数...

网友评论

      本文标题:比特币源码研读之四

      本文链接:https://www.haomeiwen.com/subject/weaguxtx.html