(四)我們接著看bitcoind.cpp文件代碼的99-110行
這幾行主要作用是:判斷數(shù)據(jù)目錄是否存在并讀取配置文件參數(shù)指定的配置文件。代碼的內(nèi)容是:

-
GetDataDir(false)函數(shù); -
BITCOIN_CONF_FILENAME宏定義; -
GetArg()函數(shù); -
ReadConfigFile()函數(shù)。
(1)GetDataDir(false)函數(shù)
這個函數(shù)在util.t中的第172行聲明:在util.cpp的第555行實現(xiàn):其中注釋文字內(nèi)容為:
這可以通過LogPrintf()來調(diào)用,因此我們緩存了值,這樣我們就不必在之后執(zhí)行內(nèi)存分配了.
更詳細(xì)的講解可以參考下面的鏈接:
(2)BITCOIN_CONF_FILENAME 宏定義
該定義在util.cpp中的第90行:

(3)
GetArg()函數(shù)此處的函數(shù)使用的代碼為:
gArgs.GetArg("-conf", BITCOIN_CONF_FILENAME)其中
gArgs為ArgsManager類定義的變量,GetArg()聲明在util.h中第221行,屬于ArgsManager類中的方法:std::string GetArg(const std::string& strArg, const std::string& strDefault);它的實現(xiàn)在util.cpp中436-442行:
std::string ArgsManager::GetArg(const std::string& strArg, const std::string& strDefault)
{
LOCK(cs_args);
if (mapArgs.count(strArg))
return mapArgs[strArg];
return strDefault;
}
它在這里實現(xiàn)的功能為:
判斷是否有參數(shù)"-conf",如果有,則使用前面
ParseParmeters()函數(shù)使用后保存的參數(shù)值作為配置文件;如果沒有,則使用默認(rèn)的“bitcoin.conf”。
(4)ReadConfigFile()函數(shù)
這個函數(shù)在util.h的第203行聲明,在util.cpp的第601實現(xiàn),其中它的實現(xiàn)代碼如下圖所示:

ClearDatadirCache()函數(shù)將數(shù)據(jù)文件路徑參數(shù)設(shè)置成空目錄。其中ClearDatadirCache()函數(shù)在util.cpp的第584行實現(xiàn):
總之,對于這部分實現(xiàn)的功能邏輯如下所示:
- 判斷函數(shù)
GetDataDir()得到的路徑是否為規(guī)范的目錄名稱。如果不是,打印指定目錄不存在的錯誤信息,并返回false值,程序退出;如果是,則繼續(xù)下面的程序。- 使用
ReadConfigFile()函數(shù)來讀取配置文件中的參數(shù)名與參數(shù)值,并把得到的參數(shù)信息存入mapArgs和_mapMultiArgs中。
(五)繼續(xù)看bitcoind.cpp中代碼的112-117行

對此段代碼的注釋為:
檢查
- testnet或- regtest參數(shù) (Params()調(diào)用僅在此子句之后有效)
其中- testnet指代比特幣的測試網(wǎng)絡(luò)Testnet,而- regtest指代比特幣的私有網(wǎng)絡(luò)Regression test。那么可以知道這個部分是選擇比特幣網(wǎng)絡(luò)的。
其中主要的是兩個函數(shù):
-
ChainNameFromCommandLine()函數(shù); -
SelectParams()函數(shù)。
其中SelectParams()是以ChainNameFromCommandLine()函數(shù)的返回值當(dāng)作參數(shù)的。下面對這兩個函數(shù)具體分析:
(1)ChainNameFromCommandLine()函數(shù)
這個函數(shù)的聲明在chainparamsbase.h中的第60行:對它的注釋內(nèi)容為:
它的實現(xiàn)在chainparamsbase.cpp中的第90行,具體代碼如圖所示:尋找
- regtest,- testnet并返回適當(dāng)?shù)腂IP70鏈名。
如果給定無效組合,則返回CBaseChainParams::MAX_NETWORK_TYPES;默認(rèn)情況下返回CBaseChainParams::MAIN

對于它的具體實現(xiàn)過程可以參考下面鏈接:
總之:這是一個獲取網(wǎng)絡(luò)名稱的函數(shù)。對于具體的實現(xiàn)邏輯如下:
檢測是否含有
- regtest和- testnet參數(shù),如果只含有- regtest參數(shù),則獲取網(wǎng)絡(luò)名稱為"regtest",即私有網(wǎng)絡(luò);如果只含有- testnet參數(shù),則獲取的網(wǎng)絡(luò)名稱為"test",即測試網(wǎng)絡(luò);當(dāng)兩個參數(shù)同時含有,則會報錯;當(dāng)兩個參數(shù)都沒有則獲取的網(wǎng)絡(luò)名稱為"main",即主網(wǎng)絡(luò)。
(2)SelectParams()函數(shù)
這個函數(shù)的聲明在chainparams.h中的第116行:

它的實現(xiàn)在chainparams.cpp中的第353行:根據(jù)
params()返回的值設(shè)置成指定的BIP70的鏈名稱的參數(shù)。
當(dāng)鏈不被支持時,拋出錯誤信息std::runtime_error

這個函數(shù)調(diào)用了兩個函數(shù):
-
SelectBaseParams()函數(shù); -
CreateChainParams()函數(shù)。
①SelectBaseParams()函數(shù)的聲明在chainparamsbase.h的第54行注釋上對它的解釋是:
根據(jù)
params()返回的值設(shè)置成指定網(wǎng)絡(luò)的參數(shù)。
函數(shù)的實現(xiàn)在chainparamsbase.cpp中的第85行:

CreateBaseChainParams()函數(shù),這個和下面說要說的CreateChainParams()函數(shù)名稱相似,其實這兩個函數(shù)的功能也相似,都是對不同默認(rèn)網(wǎng)絡(luò)的參數(shù)設(shè)置,只是CreateChainParams()函數(shù)設(shè)置的更具體,而CreateBaseChainParams()函數(shù)只是基本設(shè)置。CreateBaseChainParams()函數(shù)在chainparamsbase.h中的第39行聲明,在chainparamsbase.cpp的第73行實現(xiàn):
CBaseMainParams()或CBaseTestNetParams()或CBaseRegTestParams() ,而則三個函數(shù)是分別對私有網(wǎng)絡(luò)、測試網(wǎng)絡(luò)和主網(wǎng)絡(luò)的RPC端口參數(shù)和數(shù)據(jù)目錄。在chainparamsbase.cpp的30-63行:
②
CreateChainParams()函數(shù)這個函數(shù)的聲明在chainparams.h中的第104行,其中注釋對它解釋為:
創(chuàng)建并返回一個
std::unique_ptr <cchainparams>的選擇鏈。
返回所選鏈的CChainParams *指針。
如果這個鏈沒有被支持則拋出一個錯誤信息:std::runtime_error
這個就是根據(jù)上面得到的網(wǎng)絡(luò)名稱設(shè)置成對應(yīng)的網(wǎng)絡(luò)參數(shù),并把參數(shù)賦值給globalChainParams。
它的實現(xiàn)在chainparams.cpp的第342行:

CreateBaseChainParams()函數(shù)類似,它在實現(xiàn)的時候同時也實例化了對應(yīng)網(wǎng)絡(luò)類型的參數(shù)設(shè)置函數(shù):CMainParams() 、CTestNetParams() 和CRegTestParams()。對于這三個函數(shù)在chainparams.cpp的73-333行的大量代碼中有詳細(xì)的代碼實現(xiàn),這里基本上包含了不同網(wǎng)絡(luò)的網(wǎng)絡(luò)名稱、挖礦難度、區(qū)塊信息等等幾乎所有需要的參數(shù)。
所以,如果想要創(chuàng)建自己需要的數(shù)字貨幣,只需要單獨修改這里面的參數(shù)就行了,比如修改難度,獎勵金額等。這個部分是開發(fā)時經(jīng)常要修改的地方。
總結(jié):
SelectParams()函數(shù)實現(xiàn)的功能就是把ChainNameFromCommandLine()函數(shù)獲取得到的網(wǎng)絡(luò)名稱設(shè)置成對應(yīng)的基本的網(wǎng)絡(luò)基本參數(shù)。
(六)繼續(xù)看bitcoind.cpp中代碼的120-125行

這段代碼的注釋為:
當(dāng)命令行中有不準(zhǔn)確的無參數(shù)符號時出現(xiàn)錯誤提示。
這部分有個判斷函數(shù)IsSwitchChar(),它的作用是判斷是否有'-'或'/',含有的將會是正確的。它的實現(xiàn)在util.h中的第186行:

Error: Command line contains unexpected token '%s', see bitcoind -h for a list of options.而且程序會因為異常而退出。
(七)繼續(xù)看bitcoind.cpp中代碼的第128行

這部分的代碼就只有一行,注釋對它的說明為:
對于bitcoind來說-server默認(rèn)是開啟的,但對于GUI(圖形界面)-server默認(rèn)則是關(guān)閉的,所以在此添加代碼。
這里面有個主要的函數(shù):SoftSetBoolArg(),這個函數(shù)的聲明在util.h的第257行:

如果它還沒有值,就設(shè)置一個布爾參數(shù)
它的實現(xiàn)在util.cpp中的第469行:

SoftSetArg(),這個函數(shù)在util.h中聲明,主要是:
如果它還沒有值,就設(shè)置一個參數(shù)
它的實現(xiàn)在util.cpp中的第460行:

這個函數(shù)就有很多熟悉面孔,它也是ArgsManager(即參數(shù)管理類)的一個方法;也有個互斥鎖:
LOCK(cs_args);;也有mapArgs.count(strArg)函數(shù),這個是關(guān)于mapArgs參數(shù)的驗證。還有個我們不是很熟悉的函數(shù):ForceSetArg(),這個函數(shù)在當(dāng)前文件中的第477行實現(xiàn):
mapArgs和 mapMultiArgs這兩個參數(shù)的操作。
總的來看:
gArgs.SoftSetBoolArg("-server", true);函數(shù)的實現(xiàn)邏輯是:
解析
mapArgs參數(shù),判斷其中是否有-server這個參數(shù),如果存在就無需設(shè)置了;如果沒有這個參數(shù),就根據(jù)SoftSetBoolArg()傳入的fValue值進行設(shè)置。
(八)繼續(xù)看bitcoind.cpp中代碼的130-131行

這個部分只有兩行,分別有兩個函數(shù),其中對它的注釋為:
提前設(shè)置這部分,使參數(shù)交互內(nèi)容進入控制臺中。
①InitLogging()函數(shù)
這個函數(shù)的聲明在init.h的第25行:

初始化日志基礎(chǔ)設(shè)施
它的實現(xiàn)在init.cpp的821行:

可以了解這是一個初始化日志的函數(shù),而且通過查詢也知道這個函數(shù)作用的是把數(shù)據(jù)存儲在debug.log文件中,這個文件在ubuntu系統(tǒng)是在$HOME/.bitcoin/文件夾中,接下來對它初始化實現(xiàn)的6個設(shè)置內(nèi)容較詳細(xì)說明:
a.函數(shù)的第一行代碼主要是檢測參數(shù)命令中是否含有
-printtoconsole命令,如果有則讓日志信息發(fā)送跟蹤/調(diào)試信息到控制臺中,但默認(rèn)是false,即默認(rèn)只是記錄在日志文件debug.log中,而不是在控制臺中顯示;///////////////////////////////////////////////////////////////////
補充一點:
DEFAULT_LOGTIMESTAMPS DEFAULT_LOGTIMEMICROS和DEFAULT_LOGIPS這三個靜態(tài)常量在util.h中有定義:
b.函數(shù)的第二行代碼主要是檢測參數(shù)命令中是否含有
-logtimestamps,該參數(shù)的含義是在日志中打印時間戳,由上面的補充內(nèi)容知道默認(rèn)是在日志文件中打印時間戳的;c.函數(shù)的第三行代碼主要是檢測參數(shù)命令中是否含有
-logtimemicros,該參數(shù)的含義是打印日志單位精確到微妙(μs),由上面的補充內(nèi)容知道默認(rèn)是false,即默認(rèn)初始化日志文件精確到秒(s);d. 函數(shù)的第四行代碼主要是檢測參數(shù)命令中是否含有
-logips,該參數(shù)的含義是打印IP地址,由上面的補充內(nèi)容知道默認(rèn)是false,即默認(rèn)在日志文件中是不打印IP地址的;e.函數(shù)第五行是在日志文件中空19行;
f.函數(shù)最后一行是打印“Bitcoin version”,并緊跟上比特幣客戶端的版本信息。
如下圖為初始化日志的基本格式:由上面可以知道,這個函數(shù)只是一個基本的初始化日志文件函數(shù)。而接下來的
InitParameterInteraction函數(shù)是日志文件中存儲參數(shù)交互信息。

②InitParameterInteraction()函數(shù)
開始還是解析這個函數(shù)代碼:
這個函數(shù)的聲明在init.h第27行:

參數(shù)交互:根據(jù)不同的規(guī)則改變當(dāng)前參數(shù)。
它的實現(xiàn)在init.cpp中的744-814行,它主要包括了8個規(guī)則:


a.綁定并監(jiān)聽地址
注釋為:當(dāng)顯示指定了綁定地址后,即使指定了-connect和-proxy參數(shù)信息,程序?qū)邮軄碜酝獠康倪B接,并監(jiān)聽該地址。
通過參數(shù)
-bind或-whitebind這兩個參數(shù)設(shè)置,并通過SoftSetBoolArg()函數(shù)實現(xiàn)了-listen參數(shù)的設(shè)置,把它設(shè)置成true,代表要監(jiān)聽設(shè)置的外部連接IP地址。b.連接可信的節(jié)點
首先判斷參數(shù)命令中是否含有
-connect參數(shù),如果有將-dnsseed(使用DNS查找節(jié)點)和-listen(即接受來自外部的連接,并對其進行監(jiān)聽)設(shè)置為true。并進行日志打印。注意:此處代碼的有效執(zhí)行是在為設(shè)置
-bind和-whitebind參數(shù)的情況下進行的。c.代理模式
設(shè)置代理參數(shù)的目的是為了保護隱私,則此處將
-listen、-upnp以及-discover均設(shè)置為false,也就是說比特幣后臺進程只使用代理提供的監(jiān)聽地址與端口,并且不去查找默認(rèn)的監(jiān)聽地址。這里的upnp代表的意思是使用全局即插即用(UPNP)映射監(jiān)聽端口,默認(rèn)不使用。注意:此處代碼的有效執(zhí)行也是在為設(shè)置
-bind和-whitebind參數(shù)的情況下進行的。d.監(jiān)聽設(shè)置處理
當(dāng)不監(jiān)聽時,不要映射端口或嘗試檢索公共IP。就是如果監(jiān)聽參數(shù)設(shè)置為false,則upnp(端口)、discover(自動發(fā)現(xiàn)默認(rèn)地址)以及l(fā)istenonion(匿名地址監(jiān)聽)均設(shè)置為false。
/////////////////////////////////////////////////////////////////////
補充:這里的listenonion(匿名地址監(jiān)聽)實際上設(shè)計一個通信機制的一個概念:第二代洋蔥路由(onion routing),對它的理解可以借鑒網(wǎng)絡(luò)的解釋:
Tor(The Onion Router)是第二代洋蔥路由(onion routing)的一種實現(xiàn),用戶通過Tor可以在因特網(wǎng)上進行匿名交流。Tor專門防范流量過濾、嗅探分析,讓用戶免受其害。最初該項目由美國海軍研究實驗室贊助。2004年后期,Tor成為電子前哨基金會的一個項目。2005年后期,EFF不再贊助Tor項目,但他們繼續(xù)維持Tor的官方網(wǎng)站。
而在bitcoind源碼src文件夾下的torcontrol.h和torcontrol.cpp實現(xiàn)了Tor的控制。
/////////////////////////////////////////////////////////////////////
e.外部IP參數(shù)處理
如果顯示指定了公共IP地址,那么bitcoind就不需要查找其他監(jiān)聽地址了。
f.區(qū)塊模式參數(shù)設(shè)置
DEFAULT_BLOCKSONLY在net.h中定義,默認(rèn)值為false。表示在blocksonly模式(區(qū)塊模式)下禁用whitelistrelay。即在區(qū)塊模式下白名單列表失效。
g.強制白名單節(jié)點連接參數(shù)處理
強制白名單節(jié)點連接參數(shù)處理意味著比特幣網(wǎng)絡(luò)中的信息將優(yōu)先在白名單節(jié)點間傳遞。
g.區(qū)塊大小設(shè)置
這個區(qū)塊大小是設(shè)置默認(rèn)的礦工產(chǎn)生區(qū)塊的大小。
總結(jié):
InitParameterInteraction()函數(shù)主要是根據(jù)參數(shù)命令,改變當(dāng)前的參數(shù),并把相應(yīng)的信息打印到日志文件中。其中還涉及到一些網(wǎng)絡(luò)中IP地址的監(jiān)聽設(shè)置方法、白名單的禁用和啟用情況等。其實就是日志文件中初始化日志后(即版本信息打印后)的大量的日志內(nèi)容,如圖:

AppInitMain()函數(shù)中進一步講解程序初始化啟動時的調(diào)試日志內(nèi)容。


