由于近期比較忙,所以源碼研讀系列更新較之前有點慢,但不管怎么樣源碼研讀系列將會繼續(xù)寫下去的,保證每周至少有一篇,這樣才能源碼研讀的持續(xù)性。
自從開始對比特幣源碼進行研讀之后,在看比特幣相關的技術文章時,讓我可以很清晰地理解文章中提到的一些專業(yè)術語,比如前幾天看到的segwit、setwit2x以及比特幣實行setwit后要修改的DNSSeed,這些術語我都能聯(lián)想到其所在源碼位置以及其底層實現(xiàn)。有種內(nèi)行看門道的感覺,所以,更加堅定了我繼續(xù)堅持進行源碼研讀的信心。
話不多說,讓我們繼續(xù)源碼研讀的征程。同時再次希望大家在看我文章時能提出寶貴意見,一起討論一起進步。
本文將繼續(xù)開展應用程序參數(shù)交互源碼部分(AppInitParameterInteraction)的研讀與分析。
本文主要涉及的源碼文件包括:
src/bitcond.cpp、src/init.h、src/init.cpp、src/util.h、src/util.cpp、src/validation.h、src/RPC/register.h、src/rpc/blockchain.cpp、src/rpc/net.cpp、src/rpc/misc.cpp、src/rpc/mining.cpp、src/rpc/ rawtransaction.cpp、src/wallet/rpcwallet.cpp
一、區(qū)塊裁剪
區(qū)塊裁剪處理的標識參數(shù)為“prune”,該參數(shù)我們已在《比特幣源碼研讀之十》的“區(qū)塊修剪參數(shù)處理”中進行了詳細描述,其主要是針對默克爾樹(Merkle Tree)來說的,所以整個修剪用得很貼切,可以直觀地看出prune是對樹上的節(jié)點進行修剪。其修剪的對象又是誰呢?從回答中我們可以看出其修剪對象有2種:
(1)一種是所有輸出都被花費的葉子節(jié)點(交易);
(2)另一種是節(jié)點包含的所有子節(jié)點均已被修剪。
根據(jù)以上分析我們可以看出修剪的目的是為了節(jié)省存儲空間。
在《比特幣源碼研讀之十》中程序只是解析prune參數(shù),并與txindex參數(shù)進行沖突判斷,因為二者不能同時設置,具體內(nèi)容可參見第十篇文章。
我們通過其注釋可以知道區(qū)塊裁剪是針對預先設定的存儲容量來進行的,即根據(jù)客戶端所在計算機中的存儲情況進行設定,如果超過了設定值,將進行區(qū)塊裁剪,以防超過設定值,并且該設定值為MiB單位。其注釋如下:
block pruning; get the amount of disk space (in MiB) to allot for block & undo files
區(qū)塊裁剪操作根據(jù)其prune參數(shù)的值進行處理的流程如圖所示。
(1)首先是獲取prune參數(shù)值并賦值給nPruneArg,通過GetArg函數(shù)我們可以知道其默認值為0;
(2)判斷nPruneArg是否為小于0,此時程序?qū)⒊鲥e,并退出,因為我們知道如果nPruneArg小于0,表示不會為區(qū)塊提供存儲空間了,程序?qū)o法正常工作,這顯然是不合理的,所以程序直接退出是合理的;
(3)如果nPruneArg大于0,則計算該值所對應的字節(jié)數(shù),其計算公式為
nPruneTarget = (uint64_t) nPruneArg * 1024 * 1024;
(4)此處判斷prune是否為1,如果為1則轉入第5步中,反之則進入第6步;
(5)此處為prune== 1的情況,在該情況下程序不會自動對區(qū)塊進行裁剪,需要我們通過RPC命令pruneblockchain對相應區(qū)塊進行裁剪,同時因為是手動裁剪,也就沒有裁剪限定值一說了,所以程序?qū)⑵湓O置為最大值,并且設置裁剪模式為true,即fPruneMode=true,其中fPruneMode為全局變量,其在src/validation.h中聲明,并在src/validation.cpp中定義,其默認值為false,即不進行裁剪;
(6)此處為prune不等于1的情況,在該情況下首先判斷設定的裁剪值是否小于程序默認設置的用于存儲區(qū)塊的最小硬盤存儲空間MIN_DISK_SPACE_FOR_BLOCK_FILES,該值在src/validation.h中定義,其定義如下:
static const uint64_tMIN_DISK_SPACE_FOR_BLOCK_FILES = 550 * 1024 * 1024;
從其定義我們可以看出,為區(qū)塊設定的最小存儲空間為550MiB,所以我們設置的-prune參數(shù)的值必須大于等于550才能讓程序正常運行,除非像(5)中設置為1。如果nPruneTarget>=550,程序?qū)⒗^續(xù)正常運行,并且PruneMode將設置為true。
以上就是區(qū)塊裁剪參數(shù)的具體處理過程,在此處完成裁剪信息的設置后,程序?qū)⒃诤竺娓鶕?jù)這些信息進行有效地開展區(qū)塊裁剪操作。
二、RPC命令注冊
比特幣核心程序在src/RPC/register.h文件中實現(xiàn)了對RegisterAllCoreRPCCommands的定義與實現(xiàn),在該函數(shù)中實現(xiàn)了區(qū)塊鏈、P2P網(wǎng)絡、挖礦、交易以及其他工具等模塊的RPC命令的注冊。該函數(shù)中包含的這些命令注冊函數(shù)分別為:
static inline voidRegisterAllCoreRPCCommands(CRPCTable &t)
{
RegisterBlockchainRPCCommands(t);—區(qū)塊鏈RPC命令注冊
RegisterNetRPCCommands(t);—P2P網(wǎng)絡RPC命令注冊
RegisterMiscRPCCommands(t);—其他工具RPC命令注冊
RegisterMiningRPCCommands(t);—挖礦RPC命令注冊
RegisterRawTransactionRPCCommands(t);—交易PRC命令注冊
}
下面我們分別來看每個RPC命令注冊實現(xiàn)的具體位置:
(1)區(qū)塊鏈RPC命令:在命令在src/rpc/blockchain.cpp中實現(xiàn),其包含的RPC命令通過命令常量數(shù)組commands進行存儲,具體包含的命令如下:
該常量數(shù)組中的name項即為RPC命令,程序在RegisterBlockchainRPCCommands中通過對commands的遍歷實現(xiàn)這些命令的添加,即為注冊:
voidRegisterBlockchainRPCCommands(CRPCTable &t)
{
? ? ? for (unsigned int vcidx = 0; vcidx
? ? ? t.appendCommand(commands[vcidx].name,&commands[vcidx]);
}
(2)P2P網(wǎng)絡RPC命令:在命令在src/rpc/net.cpp中實現(xiàn),其包含的RPC命令也是通過命令常量數(shù)組commands進行存儲,具體包含的命令如下:
P2P網(wǎng)絡RPC命令的注冊方法與區(qū)塊鏈的一致,其通過RegisterNetRPCCommands實現(xiàn),具體實現(xiàn)如下:
void RegisterNetRPCCommands(CRPCTable &t)
{
for(unsigned int vcidx = 0; vcidx < ARRAYLEN(commands); vcidx++)
t.appendCommand(commands[vcidx].name, &commands[vcidx]);
}
(3)其他工具RPC命令:在命令在src/rpc/misc.cpp中實現(xiàn),其包含的RPC命令通過命令常量數(shù)組commands進行存儲,具體包含的命令如下:
其他工具RPC命令的注冊方法與區(qū)塊鏈的一致,其通過RegisterMiscRPCCommands實現(xiàn),具體實現(xiàn)代碼與區(qū)塊鏈的一致;
(4)挖礦RPC命令:在命令在src/rpc/mining.cpp中實現(xiàn),其包含的RPC命令也是通過命令常量數(shù)組commands進行存儲,具體包含的命令如下:
挖礦RPC命令的注冊方法與區(qū)塊鏈的一致,其通過RegisterMiningRPCCommands實現(xiàn),具體實現(xiàn)代碼與區(qū)塊鏈的一致;
(5)交易PRC命令:在命令在src/rpc/ rawtransaction.cpp中實現(xiàn),其包含的RPC命令通過命令常量數(shù)組commands進行存儲,具體包含的命令如下:
交易RPC命令的注冊方法與區(qū)塊鏈的一致,其通過RegisterRawTransactionRPCCommands實現(xiàn),具體實現(xiàn)代碼與區(qū)塊鏈的一致。
完成這些命令的注冊后,后面是針對錢包RPC命令的注冊,錢包命令是否注冊是根據(jù)程序編譯時是否包含錢包模塊而定的,其判斷條件為
#ifdef ENABLE_WALLET
即如果錢包功能打開,則需進行錢包RPC命令的注冊,反之則不需要。錢包RPC命令注冊在src/wallet/rpcwallet.cpp中實現(xiàn),其實現(xiàn)方法與區(qū)塊鏈是一樣的,通過commands數(shù)組進行存儲,然后在RegisterWalletRPCCommands函數(shù)中進行命令的注冊。
說了這么多RPC命令的注冊,那RPC命令是怎么用的呢?在哪可以用這些命令呢?這些命令是在比特幣核心客戶端中使用的,具體來說其在比特幣核心的“Help(幫助)”菜單下的“Debug Window(調(diào)試窗口)”中使用,調(diào)試窗口界面如下:
我們可在該調(diào)試窗口的命令框中輸入相應的RPC命令,然后回車即可執(zhí)行相應命令,調(diào)試窗口中將顯示其執(zhí)行結果。具體命令的運行大家可根據(jù)實際情況輸入。如果臨時不記得命令,可在輸入框中輸入help獲取命令提示幫助。
以上即為本次研讀內(nèi)容,在本文我們分析了區(qū)塊裁剪的處理以及比特幣中各模塊包含的RPC命令、命令注冊以及命令的運行。希望對大家理解這塊源碼有幫助,也歡迎大家留言討論。
區(qū)塊鏈研習社源碼研讀班 菜菜子