上一篇文章我們大致分析了一下比特幣源碼src文件夾的目錄結(jié)構(gòu)以及數(shù)據(jù)目錄結(jié)構(gòu),接下來(lái)我們將進(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)系。
在編譯完源碼后,我們可以通過(guò)src/bitcoind命令啟動(dòng)比特幣守護(hù)進(jìn)程。根據(jù)該名稱,我們很容易可以在src目錄下找到一個(gè)名為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.lxweimin.com/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定義的三個(gè)信號(hào)量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為例,其對(duì)插槽的要求是函數(shù)返回值為bool且形參分別是std::string&、std::string&、std::string&和unsigned int.據(jù)上分析,我們看下為ThreadSafeMessageBox綁定的插槽noui_ThreadSafeMessageBox的定義(noui.cpp中), 其符合信號(hào)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中的一個(gè)全局ArgsManager變量,我們來(lái)看下其提供的ParseParameters方法,相關(guān)代碼解釋如下。
void ArgsManager::ParseParameters(int argc, const char* const argv[])
{? ??
LOCK(cs_args);//cs_args是一個(gè)boost::recursive_mutex變量,即只有獲取了該鎖,才能往下操作? ??
mapArgs.clear();//類型為std::map <std::string, std::string>,存儲(chǔ)鍵值一一對(duì)應(yīng)的參數(shù)鍵值對(duì)? ?
mapMultiArgs.clear();//類型為std::map<std::string, std::vector<std::string>>,可存儲(chǔ)多值
for (int i = 1; i < argc; i++)
{
std::string str(argv[i]);
std::string strValue;
size_t is_index = str.find('=');//找到參數(shù)鍵值對(duì)的分割位置“=”
if (is_index != std::string::npos)
{
strValue = str.substr(is_index+1);//“=”號(hào)前面的為value
str = str.substr(0, is_index); //“=”號(hào)后面的為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;
}
接下來(lái),程序?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中,方法將通過(guò)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中的一個(gè)常量,其值為bitcoin.conf,表示默認(rèn)的配置文件名。ReadConfigFile方法將會(huì)讀取用戶提供的配置文件路徑或默認(rèn)讀取$datadir/bitcoin.conf文件,并將配置存儲(chǔ)在mapArgs和mapMultiArgs中。代碼如下:
gArgs.ReadConfigFile(gArgs.GetArg("-conf", BITCOIN_CONF_FILENAME));
比特幣源碼中設(shè)置了三個(gè)不同的網(wǎng)絡(luò),分別是main、testnet和regtest。可以在啟動(dòng)bitcoind或bitcoin-qt時(shí),加入?yún)?shù)-testnet或-regtest來(lái)選擇不同的網(wǎng)絡(luò),默認(rèn)為main。函數(shù)ChainNameFromCommandLine將返回一個(gè)CBaseChainParams類型,表示選擇的網(wǎng)絡(luò)類型。SelectParams函數(shù)將根據(jù)網(wǎng)絡(luò)類型創(chuàng)建兩個(gè)全局變量globalChainBaseParams和globalChainParams.?
SelectParams(ChainNameFromCommandLine());
void SelectParams(const std::string& network){
// globalChainBaseParams = CreateBaseChainParams(chain);
SelectBaseParams(network);
globalChainParams = CreateChainParams(network);
}
globalChainBaseParams中存儲(chǔ)了nRPCPort端口號(hào)以及數(shù)據(jù)存儲(chǔ)目錄strDataDir。如主網(wǎng)絡(luò)的該配置中,nRPCPort的默認(rèn)值為8333,testnet的strDataDir為“testnet3”。方法CreateChainParams將根據(jù)網(wǎng)絡(luò)類型創(chuàng)建不同的網(wǎng)絡(luò)參數(shù)存儲(chǔ)對(duì)象。這些對(duì)象分別存儲(chǔ)了每個(gè)網(wǎng)絡(luò)各自擁有的特定參數(shù),可以說(shuō),整個(gè)比特幣網(wǎng)絡(luò)就是通過(guò)這些參數(shù)來(lái)確定兩個(gè)節(jié)點(diǎn)是否位于同一網(wǎng)絡(luò)的依據(jù)。后續(xù)我們也將分析位于chainparams.cpp中的類CMainParams、CTestNetParams以及CRegTestParams,分析各個(gè)網(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)程啟動(dòng)時(shí),程序如何對(duì)用戶提供的參數(shù)進(jìn)行解析,程序?qū)⒏鶕?jù)不同的參數(shù)配置,作出與之對(duì)應(yīng)的操作選擇。
因本人水平有限,如有問題,歡迎大家批評(píng)指出,非常感謝。