(四)我們接著看bitcoind.cpp文件代碼的99-110行
這幾行主要作用是:判斷數(shù)據(jù)目錄是否存在并讀取配置文件參數(shù)指定的配置文件谦去。代碼的內(nèi)容是:
-
GetDataDir(false)
函數(shù); -
BITCOIN_CONF_FILENAME
宏定義卦方; -
GetArg()
函數(shù); -
ReadConfigFile()
函數(shù)腐螟。
(1)GetDataDir(false)
函數(shù)
這個(gè)函數(shù)在util.t中的第172行聲明:
這可以通過(guò)LogPrintf()來(lái)調(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);
它的實(shí)現(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;
}
它在這里實(shí)現(xiàn)的功能為:
判斷是否有參數(shù)"-conf",如果有汽绢,則使用前面
ParseParmeters()
函數(shù)使用后保存的參數(shù)值作為配置文件吗跋;如果沒(méi)有,則使用默認(rèn)的“bitcoin.conf”。
(4)ReadConfigFile()
函數(shù)
這個(gè)函數(shù)在util.h的第203行聲明跌宛,在util.cpp的第601實(shí)現(xiàn)酗宋,其中它的實(shí)現(xiàn)代碼如下圖所示:
ClearDatadirCache()
函數(shù)將數(shù)據(jù)文件路徑參數(shù)設(shè)置成空目錄蜕猫。其中ClearDatadirCache()
函數(shù)在util.cpp的第584行實(shí)現(xiàn):總之,對(duì)于這部分實(shí)現(xiàn)的功能邏輯如下所示:
- 判斷函數(shù)
GetDataDir()
得到的路徑是否為規(guī)范的目錄名稱哎迄。如果不是回右,打印指定目錄不存在的錯(cuò)誤信息,并返回false值漱挚,程序退出翔烁;如果是,則繼續(xù)下面的程序旨涝。- 使用
ReadConfigFile()
函數(shù)來(lái)讀取配置文件中的參數(shù)名與參數(shù)值蹬屹,并把得到的參數(shù)信息存入mapArgs和_mapMultiArgs中。
(五)繼續(xù)看bitcoind.cpp中代碼的112-117行
對(duì)此段代碼的注釋為:
檢查
- testnet
或- regtest
參數(shù) (Params()
調(diào)用僅在此子句之后有效)
其中- testnet
指代比特幣的測(cè)試網(wǎng)絡(luò)Testnet白华,而- regtest
指代比特幣的私有網(wǎng)絡(luò)Regression test慨默。那么可以知道這個(gè)部分是選擇比特幣網(wǎng)絡(luò)的。
其中主要的是兩個(gè)函數(shù):
-
ChainNameFromCommandLine()
函數(shù)衬鱼; -
SelectParams()
函數(shù)业筏。
其中SelectParams()
是以ChainNameFromCommandLine()
函數(shù)的返回值當(dāng)作參數(shù)的憔杨。下面對(duì)這兩個(gè)函數(shù)具體分析:
(1)ChainNameFromCommandLine()
函數(shù)
這個(gè)函數(shù)的聲明在chainparamsbase.h中的第60行:
它的實(shí)現(xiàn)在chainparamsbase.cpp中的第90行,具體代碼如圖所示:尋找
- regtest
鸟赫,- testnet
并返回適當(dāng)?shù)腂IP70鏈名。
如果給定無(wú)效組合消别,則返回CBaseChainParams::MAX_NETWORK_TYPES
抛蚤;默認(rèn)情況下返回CBaseChainParams::MAIN
對(duì)于它的具體實(shí)現(xiàn)過(guò)程可以參考下面鏈接:
總之:這是一個(gè)獲取網(wǎng)絡(luò)名稱的函數(shù)寻狂。對(duì)于具體的實(shí)現(xiàn)邏輯如下:
檢測(cè)是否含有
- regtest
和- testnet
參數(shù)岁经,如果只含有- regtest
參數(shù),則獲取網(wǎng)絡(luò)名稱為"regtest"蛇券,即私有網(wǎng)絡(luò)缀壤;如果只含有- testnet
參數(shù),則獲取的網(wǎng)絡(luò)名稱為"test"纠亚,即測(cè)試網(wǎng)絡(luò)塘慕;當(dāng)兩個(gè)參數(shù)同時(shí)含有,則會(huì)報(bào)錯(cuò)蒂胞;當(dāng)兩個(gè)參數(shù)都沒(méi)有則獲取的網(wǎng)絡(luò)名稱為"main"图呢,即主網(wǎng)絡(luò)。
(2)SelectParams()
函數(shù)
這個(gè)函數(shù)的聲明在chainparams.h中的第116行:
它的實(shí)現(xiàn)在chainparams.cpp中的第353行:根據(jù)
params()
返回的值設(shè)置成指定的BIP70的鏈名稱的參數(shù)。
當(dāng)鏈不被支持時(shí)蛤织,拋出錯(cuò)誤信息std::runtime_error
這個(gè)函數(shù)調(diào)用了兩個(gè)函數(shù):
-
SelectBaseParams()
函數(shù)赴叹; -
CreateChainParams()
函數(shù)。
①SelectBaseParams()
函數(shù)的聲明在chainparamsbase.h的第54行注釋上對(duì)它的解釋是:
根據(jù)
params()
返回的值設(shè)置成指定網(wǎng)絡(luò)的參數(shù)指蚜。
函數(shù)的實(shí)現(xiàn)在chainparamsbase.cpp中的第85行:
CreateBaseChainParams()
函數(shù)乞巧,這個(gè)和下面說(shuō)要說(shuō)的CreateChainParams()
函數(shù)名稱相似,其實(shí)這兩個(gè)函數(shù)的功能也相似摊鸡,都是對(duì)不同默認(rèn)網(wǎng)絡(luò)的參數(shù)設(shè)置摊欠,只是CreateChainParams()
函數(shù)設(shè)置的更具體,而CreateBaseChainParams()
函數(shù)只是基本設(shè)置柱宦。CreateBaseChainParams()
函數(shù)在chainparamsbase.h中的第39行聲明些椒,在chainparamsbase.cpp的第73行實(shí)現(xiàn):CBaseMainParams()
或CBaseTestNetParams()
或CBaseRegTestParams()
,而則三個(gè)函數(shù)是分別對(duì)私有網(wǎng)絡(luò)掸刊、測(cè)試網(wǎng)絡(luò)和主網(wǎng)絡(luò)的RPC端口參數(shù)和數(shù)據(jù)目錄免糕。在chainparamsbase.cpp的30-63行:②
CreateChainParams()
函數(shù)這個(gè)函數(shù)的聲明在chainparams.h中的第104行蚓炬,其中注釋對(duì)它解釋為:
創(chuàng)建并返回一個(gè)
std::unique_ptr <cchainparams>
的選擇鏈松逊。
返回所選鏈的CChainParams *指針。
如果這個(gè)鏈沒(méi)有被支持則拋出一個(gè)錯(cuò)誤信息:std::runtime_error
這個(gè)就是根據(jù)上面得到的網(wǎng)絡(luò)名稱設(shè)置成對(duì)應(yīng)的網(wǎng)絡(luò)參數(shù)肯夏,并把參數(shù)賦值給globalChainParams
经宏。
它的實(shí)現(xiàn)在chainparams.cpp的第342行:
CreateBaseChainParams()
函數(shù)類似,它在實(shí)現(xiàn)的時(shí)候同時(shí)也實(shí)例化了對(duì)應(yīng)網(wǎng)絡(luò)類型的參數(shù)設(shè)置函數(shù):CMainParams()
驯击、CTestNetParams()
和CRegTestParams()
烁兰。對(duì)于這三個(gè)函數(shù)在chainparams.cpp的73-333行的大量代碼中有詳細(xì)的代碼實(shí)現(xiàn),這里基本上包含了不同網(wǎng)絡(luò)的網(wǎng)絡(luò)名稱徊都、挖礦難度沪斟、區(qū)塊信息等等幾乎所有需要的參數(shù)。
所以暇矫,如果想要?jiǎng)?chuàng)建自己需要的數(shù)字貨幣主之,只需要單獨(dú)修改這里面的參數(shù)就行了,比如修改難度李根,獎(jiǎng)勵(lì)金額等槽奕。這個(gè)部分是開發(fā)時(shí)經(jīng)常要修改的地方。
總結(jié):
SelectParams()
函數(shù)實(shí)現(xiàn)的功能就是把ChainNameFromCommandLine()
函數(shù)獲取得到的網(wǎng)絡(luò)名稱設(shè)置成對(duì)應(yīng)的基本的網(wǎng)絡(luò)基本參數(shù)朱巨。
(六)繼續(xù)看bitcoind.cpp中代碼的120-125行
這段代碼的注釋為:
當(dāng)命令行中有不準(zhǔn)確的無(wú)參數(shù)符號(hào)時(shí)出現(xiàn)錯(cuò)誤提示史翘。
這部分有個(gè)判斷函數(shù)IsSwitchChar()
,它的作用是判斷是否有'-'或'/',含有的將會(huì)是正確的琼讽。它的實(shí)現(xiàn)在util.h中的第186行:
Error: Command line contains unexpected token '%s', see bitcoind -h for a list of options.
而且程序會(huì)因?yàn)楫惓6顺觥?p>
(七)繼續(xù)看bitcoind.cpp中代碼的第128行
這部分的代碼就只有一行,注釋對(duì)它的說(shuō)明為:
對(duì)于bitcoind來(lái)說(shuō)-server默認(rèn)是開啟的钻蹬,但對(duì)于GUI(圖形界面)-server默認(rèn)則是關(guān)閉的吼蚁,所以在此添加代碼。
這里面有個(gè)主要的函數(shù):SoftSetBoolArg()
,這個(gè)函數(shù)的聲明在util.h的第257行:
如果它還沒(méi)有值问欠,就設(shè)置一個(gè)布爾參數(shù)
它的實(shí)現(xiàn)在util.cpp中的第469行:
SoftSetArg()
肝匆,這個(gè)函數(shù)在util.h中聲明,主要是:
如果它還沒(méi)有值顺献,就設(shè)置一個(gè)參數(shù)
它的實(shí)現(xiàn)在util.cpp中的第460行:
這個(gè)函數(shù)就有很多熟悉面孔旗国,它也是ArgsManager(即參數(shù)管理類)的一個(gè)方法;也有個(gè)互斥鎖:
LOCK(cs_args);
注整;也有mapArgs.count(strArg)
函數(shù)能曾,這個(gè)是關(guān)于mapArgs
參數(shù)的驗(yàn)證。還有個(gè)我們不是很熟悉的函數(shù):ForceSetArg()
肿轨,這個(gè)函數(shù)在當(dāng)前文件中的第477行實(shí)現(xiàn):mapArgs
和 mapMultiArgs
這兩個(gè)參數(shù)的操作寿冕。
總的來(lái)看:
gArgs.SoftSetBoolArg("-server", true);
函數(shù)的實(shí)現(xiàn)邏輯是:
解析
mapArgs
參數(shù),判斷其中是否有-server
這個(gè)參數(shù)椒袍,如果存在就無(wú)需設(shè)置了驼唱;如果沒(méi)有這個(gè)參數(shù),就根據(jù)SoftSetBoolArg()
傳入的fValue
值進(jìn)行設(shè)置驹暑。
(八)繼續(xù)看bitcoind.cpp中代碼的130-131行
這個(gè)部分只有兩行玫恳,分別有兩個(gè)函數(shù),其中對(duì)它的注釋為:
提前設(shè)置這部分岗钩,使參數(shù)交互內(nèi)容進(jìn)入控制臺(tái)中纽窟。
①InitLogging()
函數(shù)
這個(gè)函數(shù)的聲明在init.h的第25行:
初始化日志基礎(chǔ)設(shè)施
它的實(shí)現(xiàn)在init.cpp的821行:
可以了解這是一個(gè)初始化日志的函數(shù)肖油,而且通過(guò)查詢也知道這個(gè)函數(shù)作用的是把數(shù)據(jù)存儲(chǔ)在debug.log文件中兼吓,這個(gè)文件在ubuntu系統(tǒng)是在$HOME/.bitcoin/文件夾中,接下來(lái)對(duì)它初始化實(shí)現(xiàn)的6個(gè)設(shè)置內(nèi)容較詳細(xì)說(shuō)明:
a.函數(shù)的第一行代碼主要是檢測(cè)參數(shù)命令中是否含有
-printtoconsole
命令森枪,如果有則讓日志信息發(fā)送跟蹤/調(diào)試信息到控制臺(tái)中视搏,但默認(rèn)是false,即默認(rèn)只是記錄在日志文件debug.log中县袱,而不是在控制臺(tái)中顯示浑娜;///////////////////////////////////////////////////////////////////
補(bǔ)充一點(diǎn):
DEFAULT_LOGTIMESTAMPS
DEFAULT_LOGTIMEMICROS
和DEFAULT_LOGIPS
這三個(gè)靜態(tài)常量在util.h中有定義:b.函數(shù)的第二行代碼主要是檢測(cè)參數(shù)命令中是否含有
-logtimestamps
,該參數(shù)的含義是在日志中打印時(shí)間戳式散,由上面的補(bǔ)充內(nèi)容知道默認(rèn)是在日志文件中打印時(shí)間戳的筋遭;c.函數(shù)的第三行代碼主要是檢測(cè)參數(shù)命令中是否含有
-logtimemicros
,該參數(shù)的含義是打印日志單位精確到微妙(μs),由上面的補(bǔ)充內(nèi)容知道默認(rèn)是false漓滔,即默認(rèn)初始化日志文件精確到秒(s)编饺;d. 函數(shù)的第四行代碼主要是檢測(cè)參數(shù)命令中是否含有
-logips
,該參數(shù)的含義是打印IP地址响驴,由上面的補(bǔ)充內(nèi)容知道默認(rèn)是false透且,即默認(rèn)在日志文件中是不打印IP地址的;e.函數(shù)第五行是在日志文件中空19行豁鲤;
f.函數(shù)最后一行是打印“Bitcoin version”秽誊,并緊跟上比特幣客戶端的版本信息。
如下圖為初始化日志的基本格式:由上面可以知道琳骡,這個(gè)函數(shù)只是一個(gè)基本的初始化日志文件函數(shù)锅论。而接下來(lái)的
InitParameterInteraction
函數(shù)是日志文件中存儲(chǔ)參數(shù)交互信息。
②InitParameterInteraction()
函數(shù)
開始還是解析這個(gè)函數(shù)代碼:
這個(gè)函數(shù)的聲明在init.h第27行:
參數(shù)交互:根據(jù)不同的規(guī)則改變當(dāng)前參數(shù)楣号。
它的實(shí)現(xiàn)在init.cpp中的744-814行棍厌,它主要包括了8個(gè)規(guī)則:
a.綁定并監(jiān)聽地址
注釋為:當(dāng)顯示指定了綁定地址后,即使指定了-connect和-proxy參數(shù)信息竖席,程序?qū)?huì)接受來(lái)自外部的連接耘纱,并監(jiān)聽該地址。
通過(guò)參數(shù)
-bind
或-whitebind
這兩個(gè)參數(shù)設(shè)置毕荐,并通過(guò)SoftSetBoolArg()
函數(shù)實(shí)現(xiàn)了-listen
參數(shù)的設(shè)置束析,把它設(shè)置成true,代表要監(jiān)聽設(shè)置的外部連接IP地址憎亚。b.連接可信的節(jié)點(diǎn)
首先判斷參數(shù)命令中是否含有
-connect
參數(shù)员寇,如果有將-dnsseed
(使用DNS查找節(jié)點(diǎn))和-listen
(即接受來(lái)自外部的連接,并對(duì)其進(jìn)行監(jiān)聽)設(shè)置為true第美。并進(jìn)行日志打印蝶锋。注意:此處代碼的有效執(zhí)行是在為設(shè)置
-bind
和-whitebind
參數(shù)的情況下進(jìn)行的。c.代理模式
設(shè)置代理參數(shù)的目的是為了保護(hù)隱私什往,則此處將
-listen
扳缕、-upnp
以及-discover
均設(shè)置為false,也就是說(shuō)比特幣后臺(tái)進(jìn)程只使用代理提供的監(jiān)聽地址與端口别威,并且不去查找默認(rèn)的監(jiān)聽地址躯舔。這里的upnp代表的意思是使用全局即插即用(UPNP)映射監(jiān)聽端口,默認(rèn)不使用省古。注意:此處代碼的有效執(zhí)行也是在為設(shè)置
-bind
和-whitebind
參數(shù)的情況下進(jìn)行的粥庄。d.監(jiān)聽設(shè)置處理
當(dāng)不監(jiān)聽時(shí),不要映射端口或嘗試檢索公共IP豺妓。就是如果監(jiān)聽參數(shù)設(shè)置為false惜互,則upnp(端口)布讹、discover(自動(dòng)發(fā)現(xiàn)默認(rèn)地址)以及l(fā)istenonion(匿名地址監(jiān)聽)均設(shè)置為false侄泽。
/////////////////////////////////////////////////////////////////////
補(bǔ)充:這里的listenonion(匿名地址監(jiān)聽)實(shí)際上設(shè)計(jì)一個(gè)通信機(jī)制的一個(gè)概念:第二代洋蔥路由(onion routing)尺铣,對(duì)它的理解可以借鑒網(wǎng)絡(luò)的解釋:
Tor(The Onion Router)是第二代洋蔥路由(onion routing)的一種實(shí)現(xiàn),用戶通過(guò)Tor可以在因特網(wǎng)上進(jìn)行匿名交流娃圆。Tor專門防范流量過(guò)濾蔫慧、嗅探分析挠乳,讓用戶免受其害。最初該項(xiàng)目由美國(guó)海軍研究實(shí)驗(yàn)室贊助姑躲。2004年后期睡扬,Tor成為電子前哨基金會(huì)的一個(gè)項(xiàng)目。2005年后期黍析,EFF不再贊助Tor項(xiàng)目卖怜,但他們繼續(xù)維持Tor的官方網(wǎng)站。
而在bitcoind源碼src文件夾下的torcontrol.h和torcontrol.cpp實(shí)現(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.強(qiáng)制白名單節(jié)點(diǎn)連接參數(shù)處理
強(qiáng)制白名單節(jié)點(diǎn)連接參數(shù)處理意味著比特幣網(wǎng)絡(luò)中的信息將優(yōu)先在白名單節(jié)點(diǎn)間傳遞妙啃。
g.區(qū)塊大小設(shè)置
這個(gè)區(qū)塊大小是設(shè)置默認(rèn)的礦工產(chǎn)生區(qū)塊的大小。
總結(jié):
InitParameterInteraction()
函數(shù)主要是根據(jù)參數(shù)命令俊戳,改變當(dāng)前的參數(shù)揖赴,并把相應(yīng)的信息打印到日志文件中。其中還涉及到一些網(wǎng)絡(luò)中IP地址的監(jiān)聽設(shè)置方法抑胎、白名單的禁用和啟用情況等燥滑。其實(shí)就是日志文件中初始化日志后(即版本信息打印后)的大量的日志內(nèi)容,如圖:
AppInitMain()
函數(shù)中進(jìn)一步講解程序初始化啟動(dòng)時(shí)的調(diào)試日志內(nèi)容羽历。