上一節(jié)提到了一個(gè)問題:比特幣默認(rèn)的日志輸出文件是哪個(gè)? 不知道大家找到了沒德绿,現(xiàn)在答案公布如下:
如果有看過我的第一篇文章“比特幣源碼研讀1——下載與編譯” 就知道移稳,我們安裝的路徑為個(gè)人的目錄下个粱,那么比特幣默認(rèn)的日志文件就在:
~/.bitcoin/debug.log
tips: 這里說明下都许,有一種情況是看不到debug.log這個(gè)日志文件的——你從未運(yùn)行過程序,解決方案就是跑以下的命令:
bitcoind
或是
bitcoind -daemon
如果不想下載一百多G的數(shù)據(jù)文件塞椎,對(duì)于第一個(gè)命令bitcoind忱屑,直接ctrl+c即可;第二個(gè)命令的話殺掉后臺(tái)進(jìn)程就可以了伴嗡。
好了瘪校,我們接下來繼續(xù)講解參數(shù)的初始化設(shè)置阱扬。
InitParameterInteraction(): 該函數(shù)具體實(shí)現(xiàn)于init.cpp文件中麻惶,主要分為7部分,先看第一部分:
(1) 綁定并監(jiān)聽地址
// when specifying an explicit binding address, you want to listen on it
// even when -connect or -proxy is specified
if (IsArgSet("-bind")) {
if (SoftSetBoolArg("-listen", true))
LogPrintf("%s: parameter interaction: -bind set -> setting -listen=1\n", __func__);
}
if (IsArgSet("-whitebind")) {
if (SoftSetBoolArg("-listen", true))
LogPrintf("%s: parameter interaction: -whitebind set -> setting -listen=1\n", __func__);
}
就是當(dāng)你顯式指定一個(gè)綁定地址卡啰,就監(jiān)控該地址匈辱,即使你指定了“-connect”或者“-proxy”參數(shù)亡脸。
從代碼可以看出浅碾,有兩種方式來綁定地址:"-bind" 和 "-whitebind"及穗,這兩種的處理方式也一樣埂陆,都是通過函數(shù)SoftSetBoolArg設(shè)置 參數(shù)“-listen”為 true,表示對(duì)地址進(jìn)行監(jiān)聽购裙。
另外躏率,關(guān)于記錄日志的函數(shù) LogPrintf民鼓,以后會(huì)經(jīng)常碰面丰嘉,這里介紹一下饮亏,它是以預(yù)編譯方式定義于util.h文件中。從定義中看出荐开,真正實(shí)現(xiàn)打印功能的函數(shù)是 LogPrintStr, 其定義于util.h文件中晃听。
其中杂伟,“\” 表示換行, 實(shí)現(xiàn)代碼在util.cpp文件中:
int LogPrintStr(const std::string &str)
{
int ret = 0; // Returns total number of characters written
static std::atomic_bool fStartedNewLine(true);
std::string strTimestamped = LogTimestampStr(str, &fStartedNewLine);
if (fPrintToConsole)
{
// print to console
ret = fwrite(strTimestamped.data(), 1, strTimestamped.size(), stdout);
fflush(stdout);
}
else if (fPrintToDebugLog)
{
boost::call_once(&DebugPrintInit, debugPrintInitFlag);
boost::mutex::scoped_lock scoped_lock(*mutexDebugLog);
// buffer if we haven't opened the log yet
if (fileout == NULL) {
assert(vMsgsBeforeOpenLog);
ret = strTimestamped.length();
vMsgsBeforeOpenLog->push_back(strTimestamped);
}
else
{
// reopen the log file, if requested
if (fReopenDebugLog) {
fReopenDebugLog = false;
fs::path pathDebug = GetDataDir() / "debug.log";
if (fsbridge::freopen(pathDebug,"a",fileout) != NULL)
setbuf(fileout, NULL); // unbuffered
}
ret = FileWriteStr(strTimestamped, fileout);
}
}
return ret;
}
大概邏輯如下:
1. 通過調(diào)用函數(shù)LogTimestampStr為日志加上時(shí)間戳赫粥;
2. 根據(jù)全局變量fPrintToConsole判斷是否打印到控制臺(tái)越平,默認(rèn)值為false, 即不打忧嘏选瀑粥;
3. 根據(jù)全局變量fPrintToDebugLog判斷是否打印到日志文件debug.log,默認(rèn)是true避咆,即打印路媚;
好了整慎,繼續(xù)InitParameterInteraction函數(shù)講解围苫。
(2) 連接可信任節(jié)點(diǎn)
if (gArgs.IsArgSet("-connect")) {
// when only connecting to trusted nodes, do not seed via DNS, or listen by default
if (SoftSetBoolArg("-dnsseed", false))
LogPrintf("%s: parameter interaction: -connect set -> setting -dnsseed=0\n", __func__);
if (SoftSetBoolArg("-listen", false))
LogPrintf("%s: parameter interaction: -connect set -> setting -listen=0\n", __func__);
}
首先判斷gArgs是否有"-connect"參數(shù)剂府,如果有周循,將“dnsseed”和"-listen"參數(shù)設(shè)置為false湾笛,即只有當(dāng)網(wǎng)絡(luò)連接到可信任節(jié)點(diǎn)時(shí)闰歪,才取消通過DNS廣播方式查找節(jié)點(diǎn)地址库倘,不監(jiān)聽默認(rèn)的地址。
這里需要注意的是杆勇,全局參數(shù)對(duì)象mapArgs中如果有“-listen” 參數(shù)蚜退,即在此之前已經(jīng)設(shè)置了“-listen” 參數(shù)钻注,則不會(huì)再重新設(shè)置了配猫,即“-listen” 參數(shù)的值就不會(huì)變了。
這也就是當(dāng)你在(1)中設(shè)置了”-bind”和”-whitebind”參數(shù)淑翼,那么即使你指定了“-connect”或者“-proxy”參數(shù)也無效窒舟。
(3)代理模式
if (IsArgSet("-proxy")) {
// to protect privacy, do not listen by default if a default proxy server is specified
if (SoftSetBoolArg("-listen", false))
LogPrintf("%s: parameter interaction: -proxy set -> setting -listen=0\n", __func__);
// to protect privacy, do not use UPNP when a proxy is set. The user may still specify -listen=1
// to listen locally, so don't rely on this happening through -listen below.
if (SoftSetBoolArg("-upnp", false))
LogPrintf("%s: parameter interaction: -proxy set -> setting -upnp=0\n", __func__);
// to protect privacy, do not discover addresses by default
if (SoftSetBoolArg("-discover", false))
LogPrintf("%s: parameter interaction: -proxy set -> setting -discover=0\n", __func__);
}
如注釋惠豺,設(shè)置代理是為了保護(hù)隱私风宁,假如已經(jīng)指定了代理服務(wù)器戒财,將"-listen"、"-upnp"以及"-discover"均設(shè)為false孝扛,表示只使用指定的代理服務(wù)器苦始,不使用UPNP監(jiān)聽端口慌申,不去查找默認(rèn)的地址。換言之咨油,只使用代理參數(shù)提供的監(jiān)聽地址及端口柒爵。
以下是微軟官方網(wǎng)站對(duì)UPnP的解釋:
通用即插即用 (UPnP) 是一種用于 PC 機(jī)和智能設(shè)備(或儀器)的常見對(duì)等網(wǎng)絡(luò)連接的體系結(jié)構(gòu)棉胀,尤其是在家庭中膏蚓。UPnP 以Internet標(biāo)準(zhǔn)和技術(shù)(例如 TCP/IP、HTTP 和 XML)為基礎(chǔ)氓扛,使這樣的設(shè)備彼此可自動(dòng)連接和協(xié)同工作千所,從而使網(wǎng)絡(luò)(尤其是家庭網(wǎng)絡(luò))對(duì)更多的人成為可能蒜埋。
(4)監(jiān)聽設(shè)置
全局變量DEFAULT_LISTEN 默認(rèn)值為true, 即不設(shè)置監(jiān)聽整份,將 "-upnp"烈评、"-discover"以及"-listenonion"(匿名地址監(jiān)聽)均設(shè)為false
if (!GetBoolArg("-listen", DEFAULT_LISTEN)) {
// do not map ports or try to retrieve public IP when not listening (pointless)
if (SoftSetBoolArg("-upnp", false))
LogPrintf("%s: parameter interaction: -listen=0 -> setting -upnp=0\n", __func__);
if (SoftSetBoolArg("-discover", false))
LogPrintf("%s: parameter interaction: -listen=0 -> setting -discover=0\n", __func__);
if (SoftSetBoolArg("-listenonion", false))
LogPrintf("%s: parameter interaction: -listen=0 -> setting -listenonion=0\n", __func__);
}
tips: listenonion讲冠,即匿名地址監(jiān)聽竿开,這里涉及到通信機(jī)制的一個(gè)概念:第二代洋蔥路由(onion routing)否彩。
Tor(The Onion Router)是第二代洋蔥路由(onion routing)的一種實(shí)現(xiàn)胳搞,用戶通過Tor可以在因特網(wǎng)上進(jìn)行匿名交流肌毅。Tor專門防范流量過濾姑原、嗅探分析锭汛,讓用戶免受其害唤殴。最初該項(xiàng)目由美國海軍研究實(shí)驗(yàn)室贊助。2004年后期蔚袍,Tor成為電子前哨基金會(huì)的一個(gè)項(xiàng)目啤咽。2005年后期,EFF不再贊助Tor項(xiàng)目瓶佳,但他們繼續(xù)維持Tor的官方網(wǎng)站霸饲。
(5)外部IP地址設(shè)置
if (IsArgSet("-externalip")) {
// if an explicit public IP is specified, do not try to find others
if (SoftSetBoolArg("-discover", false))
LogPrintf("%s: parameter interaction: -externalip set -> setting -discover=0\n", __func__);
}
如果顯式指定了公共IP地址贴彼,就不用查找其他的監(jiān)聽地址了器仗。
(6)區(qū)塊模式設(shè)置
// disable whitelistrelay in blocksonly mode
if (GetBoolArg("-blocksonly", DEFAULT_BLOCKSONLY)) {
if (SoftSetBoolArg("-whitelistrelay", false))
LogPrintf("%s: parameter interaction: -blocksonly=1 -> setting -whitelistrelay=0\n", __func__);
}
全局變量DEFAULT_BLOCKSONLY默認(rèn)值為False精钮,只有DEFAULT_BLOCKSONLY的值為true,GetBoolArg才為true剃斧,這時(shí)才會(huì)將"-whitelistrelay"設(shè)置為false轨香,即區(qū)塊模式下白名單列表失效。
tips: 這里的blocksonly參數(shù)是比特幣客戶端以調(diào)試狀態(tài)啟動(dòng)時(shí)才會(huì)使用的幼东,我們可以從src/init.cpp里面的幫助信息函數(shù)HelpMessage中得到相關(guān)信息:
if (showDebug)
strUsage += HelpMessageOpt("-blocksonly", strprintf(_("Whether to operate in a blocks only mode (default: %u)"), DEFAULT_BLOCKSONLY));
關(guān)于DEFAULT_BLOCKSONLY臂容, 我們已經(jīng)知道其默認(rèn)值為False,即默認(rèn)情況下不會(huì)只以區(qū)塊模式運(yùn)行根蟹。假定只在該模式下運(yùn)行脓杉,那么此時(shí)全網(wǎng)的交易都不會(huì)被打包,錢包的交易廣播功能將失效简逮,也就是我們看到的walletbroadcast參數(shù)此時(shí)需要設(shè)置為false球散,否則互斥。
(7)強(qiáng)制白名單節(jié)點(diǎn)連接參數(shù)設(shè)置
// Forcing relay from whitelisted hosts implies we will accept relays from them in the first place.
if (GetBoolArg("-whitelistforcerelay", DEFAULT_WHITELISTFORCERELAY)) {
if (SoftSetBoolArg("-whitelistrelay", true))
LogPrintf("%s: parameter interaction: -whitelistforcerelay=1 -> setting -whitelistrelay=1\n", __func__);
}
DEFAULT_WHITELISTFORCERELAY默認(rèn)為true,則參數(shù)"-whitelistrelay"設(shè)置為true屋讶,即比特幣網(wǎng)絡(luò)中的信息將優(yōu)先在白名單節(jié)點(diǎn)間傳遞。
至此InitParameterInteraction函數(shù)已經(jīng)解讀完畢秒旋。下一節(jié)將講解初始化基本環(huán)境搭建,敬請(qǐng)期待!
作者:區(qū)塊鏈研習(xí)社比特幣源碼研讀班 Jacky