比特幣源碼研讀之二

上一篇文章我們大致分析了一下比特幣源碼src文件夾的目錄結(jié)構(gòu)以及數(shù)據(jù)目錄結(jié)構(gòu),接下來我們將進(jìn)入源碼的分析。本篇涉及的文件包括:

bitcoind.cpp、noui.cpp、ui_interface.hui_interface.h、util.cpp、init.cpp、chainparamsbase.cpp、chainparams.cpp

下圖整理了本篇文章中涉及的一些主要方法調(diào)用,以及相互的調(diào)用關(guān)系。

在編譯完源碼后,我們可以通過src/bitcoind命令啟動比特幣守護(hù)進(jìn)程。根據(jù)該名稱,我們很容易可以在src目錄下找到一個名為bitcoind.cpp的文件。打開該文件,其main函數(shù)的內(nèi)容如下:

int main(int argc, char* argv[])

{

SetupEnvironment();

// Connect bitcoind signal handlers

noui_connect();

return (AppInit(argc, argv) ? EXIT_SUCCESS : EXIT_FAILURE);

}

SetupEnvironment()

該方法中處理了32位虛擬地址分配以及l(fā)ocale相關(guān)的問題,具體可參考http://www.itdecent.cn/p/4fc762796f83 。

noui_connect()

方法定義如下:

void noui_connect()

{

// Connect bitcoind signal handlers

uiInterface.ThreadSafeMessageBox.connect(noui_ThreadSafeMessageBox);

uiInterface.ThreadSafeQuestion.connect(noui_ThreadSafeQuestion);

uiInterface.InitMessage.connect(noui_InitMessage);

}

ThreadSafeMessageBox、ThreadSafeQuestion、InitMessage是在uiInterface定義的三個信號量boost::signal:signal2,其定義如下:

/** Show message box. */?

boost::signals2::signal <bool (const std::string& message, const std::string& caption, unsigned int style), boost::signals2::last_value<bool>>ThreadSafeMessageBox;

/** If possible, ask the user a question. If not, falls back to ThreadSafeMessageBox(noninteractive_message, caption, style) and returns false. */? ??

boost::signals2::signal<bool (const std::string& message, const std::string& noninteractive_message, const std::string& caption, unsigned int style), boost::signals2::last_value<bool>> ThreadSafeQuestion;

/** Progress message during initialization. */

boost::signals2::signal<void (const std::string &message)> InitMessage;

<>中定義了其插槽需要滿足的接口以及返回值需求,以ThreadSafeMessageBox為例,其對插槽的要求是函數(shù)返回值為bool且形參分別是std::string&、std::string&、std::string&和unsigned int.據(jù)上分析,我們看下為ThreadSafeMessageBox綁定的插槽noui_ThreadSafeMessageBox的定義(noui.cpp中), 其符合信號ThreadSafeMessageBox的定義。

static bool noui_ThreadSafeMessageBox(const std::string& message, const std::string& caption, unsigned int style);

AppInit

首先方法處理了用戶提供的運(yùn)行參數(shù)gArgs.ParseParameters(argc, argv)。gArgs為定義在util.cpp中的一個全局ArgsManager變量,我們來看下其提供的ParseParameters方法,相關(guān)代碼解釋如下。

void ArgsManager::ParseParameters(int argc, const char* const argv[])

{? ??

LOCK(cs_args);//cs_args是一個boost::recursive_mutex變量,即只有獲取了該鎖,才能往下操作? ??

mapArgs.clear();//類型為std::map <std::string, std::string>,存儲鍵值一一對應(yīng)的參數(shù)鍵值對? ?

mapMultiArgs.clear();//類型為std::map<std::string, std::vector<std::string>>,可存儲多值

for (int i = 1; i < argc; i++)

{

std::string str(argv[i]);

std::string strValue;

size_t is_index = str.find('=');//找到參數(shù)鍵值對的分割位置“=”

if (is_index != std::string::npos)

{

strValue = str.substr(is_index+1);//“=”號前面的為value

str = str.substr(0, is_index); //“=”號后面的為key

}

#ifdef WIN32//如果是WIN32系統(tǒng)且參數(shù)以“/”開頭,則將“/”替換為“-”

boost::to_lower(str);

if (boost::algorithm::starts_with(str, "/"))

str = "-" + str.substr(1);

#endif

if (str[0] != '-')//如果不是以“-”開頭,則退出循環(huán)

break;

// Interpret --foo as -foo.--foo當(dāng)作-foo處理

// If both --foo and -foo are set, the last takes effect.

if (str.length() > 1 && str[1] == '-')

str = str.substr(1);

InterpretNegativeSetting(str, strValue);//將-noX替換為-X=0

mapArgs[str] = strValue;

mapMultiArgs[str].push_back(strValue);

}

}

我們回到AppInit方法中,在解析完用戶提供的運(yùn)行參數(shù)后,如果參數(shù)中包含了以下幾項(xiàng),將向用戶提供相應(yīng)的信息提示,程序終止。其中HelpMessage定義在init.cpp中,提供了各種參數(shù)的解釋幫助信息。

if (gArgs.IsArgSet("-?") || gArgs.IsArgSet("-h") ||? gArgs.IsArgSet("-help") || gArgs.IsArgSet("-version")){

…………

if (gArgs.IsArgSet("-version")){

strUsage += FormatParagraph(LicenseInfo());

}else{

strUsage += "\n" + HelpMessage(HMM_BITCOIND);

}

fprintf(stdout, "%s", strUsage.c_str());

return true;

}

接下來,程序?qū)z查用戶是否提供了datadir參數(shù),如果沒有,將根據(jù)不同的系統(tǒng)為其設(shè)定默認(rèn)值。

if (!fs::is_directory(GetDataDir(false))){

fprintf(stderr, "Error: Specified data directory \"%s\" does not exist.\n", gArgs.GetArg("-datadir", "").c_str());

return false;

}

GetDataDir()方法位于util.cpp中,方法將通過gArgs.IsArgSet("-datadir")檢查用戶是否提供datadir參數(shù),若沒有則將調(diào)用util.cpp中的GetDefaultDataDir()獲取默認(rèn)值,根據(jù)不同的操作系統(tǒng)數(shù)據(jù)目錄的具體默認(rèn)位置如下:

// Windows < Vista: C:\Documents and Settings\Username\Application Data\Bitcoin

// Windows >= Vista: C:\Users\Username\AppData\Roaming\Bitcoin

// Mac: ~/Library/Application Support/Bitcoin

// Unix: ~/.bitcoin

緊接著程序?qū)⒆x取相關(guān)的參數(shù)配置文件,BITCOIN_CONF_FILENAME為定義在util.cpp中的一個常量,其值為bitcoin.conf,表示默認(rèn)的配置文件名。ReadConfigFile方法將會讀取用戶提供的配置文件路徑或默認(rèn)讀取$datadir/bitcoin.conf文件,并將配置存儲在mapArgs和mapMultiArgs中。代碼如下:

gArgs.ReadConfigFile(gArgs.GetArg("-conf", BITCOIN_CONF_FILENAME));

比特幣源碼中設(shè)置了三個不同的網(wǎng)絡(luò),分別是main、testnet和regtest。可以在啟動bitcoind或bitcoin-qt時,加入?yún)?shù)-testnet或-regtest來選擇不同的網(wǎng)絡(luò),默認(rèn)為main。函數(shù)ChainNameFromCommandLine將返回一個CBaseChainParams類型,表示選擇的網(wǎng)絡(luò)類型。SelectParams函數(shù)將根據(jù)網(wǎng)絡(luò)類型創(chuàng)建兩個全局變量globalChainBaseParams和globalChainParams.?

SelectParams(ChainNameFromCommandLine());

void SelectParams(const std::string& network){

// globalChainBaseParams = CreateBaseChainParams(chain);

SelectBaseParams(network);

globalChainParams = CreateChainParams(network);

}

globalChainBaseParams中存儲了nRPCPort端口號以及數(shù)據(jù)存儲目錄strDataDir。如主網(wǎng)絡(luò)的該配置中,nRPCPort的默認(rèn)值為8333,testnet的strDataDir為“testnet3”。方法CreateChainParams將根據(jù)網(wǎng)絡(luò)類型創(chuàng)建不同的網(wǎng)絡(luò)參數(shù)存儲對象。這些對象分別存儲了每個網(wǎng)絡(luò)各自擁有的特定參數(shù),可以說,整個比特幣網(wǎng)絡(luò)就是通過這些參數(shù)來確定兩個節(jié)點(diǎn)是否位于同一網(wǎng)絡(luò)的依據(jù)。后續(xù)我們也將分析位于chainparams.cpp中的類CMainParams、CTestNetParams以及CRegTestParams,分析各個網(wǎng)絡(luò)參數(shù)的含義。(順便提一下,基于比特幣的山寨幣制作,主要就是修改這些網(wǎng)絡(luò)參數(shù))

std::unique_ptr<CChainParams> CreateChainParams(const std::string& chain){? ??

if (chain == CBaseChainParams::MAIN)? ? ? ??

? ? ?return std::unique_ptr(new CMainParams());? ??

else if (chain == CBaseChainParams::TESTNET)? ? ? ??

? ? ?return std::unique_ptr(new CTestNetParams());? ?

else if (chain == CBaseChainParams::REGTEST)? ? ? ??

? ? ?return std::unique_ptr(new CRegTestParams());

throw std::runtime_error(strprintf("%s: Unknown chain %s.", __func__, chain));

}

本篇主要分析了bitcoind守護(hù)進(jìn)程啟動時,程序如何對用戶提供的參數(shù)進(jìn)行解析,程序?qū)⒏鶕?jù)不同的參數(shù)配置,作出與之對應(yīng)的操作選擇。

因本人水平有限,如有問題,歡迎大家批評指出,非常感謝。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • 前一篇文章中已經(jīng)完成了main函數(shù)運(yùn)行過程的梳理,并且也繪制了其運(yùn)行流程圖,為了更清晰地記錄每個過程的詳細(xì)執(zhí)行內(nèi)容...
    菜菜子_forest閱讀 5,786評論 8 14
  • 注:純粹是自己的記錄,沒有任何參考價值 面對著不知所云的比特幣源碼,我嘗試著采用逐字閱讀的笨辦法來解讀。同時也參考...
    時汝佳閱讀 318評論 0 1
  • 區(qū)塊鏈研習(xí)社比特幣源碼研讀班今天研讀第二,第三流程,SetupEnvironment和noui_connect函數(shù)...
    劍有偏鋒閱讀 402評論 0 0
  • 上一節(jié)提到了一個問題:比特幣默認(rèn)的日志輸出文件是哪個? 不知道大家找到了沒,現(xiàn)在答案公布如下: 如果有看過我的第一...
    Jacky_2c9f閱讀 689評論 0 0
  • 我媽大嗓門,性格行動大大咧咧,行動風(fēng)風(fēng)火火,但是,卻是個玻璃心,受不了我對她的任何刺激和打擊還有語氣,否則就會難過...
    又又雙兒閱讀 208評論 2 1

友情鏈接更多精彩內(nèi)容