比特币源码研读之二

上一篇文章我们大致分析了一下比特币源码src文件夹的目录结构以及数据目录结构,接下来我们将进入源码的分析。本篇涉及的文件包括:

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

下图整理了本篇文章中涉及的一些主要方法调用,以及相互的调用关系。

在编译完源码后,我们可以通过src/bitcoind命令启动比特币守护进程。根据该名称,我们很容易可以在src目录下找到一个名为bitcoind.cpp的文件。打开该文件,其main函数的内容如下:

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

{

SetupEnvironment();

// Connect bitcoind signal handlers

noui_connect();

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

}

SetupEnvironment()

该方法中处理了32位虚拟地址分配以及locale相关的问题,具体可参考http://www.jianshu.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定义的三个信号量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为例,其对插槽的要求是函数返回值为bool且形参分别是std::string&、std::string&、std::string&和unsigned int.据上分析,我们看下为ThreadSafeMessageBox绑定的插槽noui_ThreadSafeMessageBox的定义(noui.cpp中), 其符合信号ThreadSafeMessageBox的定义。

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

AppInit

首先方法处理了用户提供的运行参数gArgs.ParseParameters(argc, argv)。gArgs为定义在util.cpp中的一个全局ArgsManager变量,我们来看下其提供的ParseParameters方法,相关代码解释如下。

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>,存储键值一一对应的参数键值对   

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('=');//找到参数键值对的分割位置“=”

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

{

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

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

}

#ifdef WIN32//如果是WIN32系统且参数以“/”开头,则将“/”替换为“-”

boost::to_lower(str);

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

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

#endif

if (str[0] != '-')//如果不是以“-”开头,则退出循环

break;

// Interpret --foo as -foo.--foo当作-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方法中,在解析完用户提供的运行参数后,如果参数中包含了以下几项,将向用户提供相应的信息提示,程序终止。其中HelpMessage定义在init.cpp中,提供了各种参数的解释帮助信息。

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;

}

接下来,程序将检查用户是否提供了datadir参数,如果没有,将根据不同的系统为其设定默认值。

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参数,若没有则将调用util.cpp中的GetDefaultDataDir()获取默认值,根据不同的操作系统数据目录的具体默认位置如下:

// 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

紧接着程序将读取相关的参数配置文件,BITCOIN_CONF_FILENAME为定义在util.cpp中的一个常量,其值为bitcoin.conf,表示默认的配置文件名。ReadConfigFile方法将会读取用户提供的配置文件路径或默认读取$datadir/bitcoin.conf文件,并将配置存储在mapArgs和mapMultiArgs中。代码如下:

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

比特币源码中设置了三个不同的网络,分别是main、testnet和regtest。可以在启动bitcoind或bitcoin-qt时,加入参数-testnet或-regtest来选择不同的网络,默认为main。函数ChainNameFromCommandLine将返回一个CBaseChainParams类型,表示选择的网络类型。SelectParams函数将根据网络类型创建两个全局变量globalChainBaseParams和globalChainParams. 

SelectParams(ChainNameFromCommandLine());

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

// globalChainBaseParams = CreateBaseChainParams(chain);

SelectBaseParams(network);

globalChainParams = CreateChainParams(network);

}

globalChainBaseParams中存储了nRPCPort端口号以及数据存储目录strDataDir。如主网络的该配置中,nRPCPort的默认值为8333,testnet的strDataDir为“testnet3”。方法CreateChainParams将根据网络类型创建不同的网络参数存储对象。这些对象分别存储了每个网络各自拥有的特定参数,可以说,整个比特币网络就是通过这些参数来确定两个节点是否位于同一网络的依据。后续我们也将分析位于chainparams.cpp中的类CMainParams、CTestNetParams以及CRegTestParams,分析各个网络参数的含义。(顺便提一下,基于比特币的山寨币制作,主要就是修改这些网络参数)

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守护进程启动时,程序如何对用户提供的参数进行解析,程序将根据不同的参数配置,作出与之对应的操作选择。

因本人水平有限,如有问题,欢迎大家批评指出,非常感谢。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 194,524评论 5 460
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 81,869评论 2 371
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 141,813评论 0 320
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 52,210评论 1 263
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 61,085评论 4 355
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 46,117评论 1 272
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 36,533评论 3 381
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 35,219评论 0 253
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 39,487评论 1 290
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 34,582评论 2 309
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 36,362评论 1 326
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,218评论 3 312
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 37,589评论 3 299
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 28,899评论 0 17
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,176评论 1 250
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 41,503评论 2 341
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 40,707评论 2 335

推荐阅读更多精彩内容

  • 前一篇文章中已经完成了main函数运行过程的梳理,并且也绘制了其运行流程图,为了更清晰地记录每个过程的详细执行内容...
    菜菜子_forest阅读 5,644评论 8 14
  • 注:纯粹是自己的记录,没有任何参考价值 面对着不知所云的比特币源码,我尝试着采用逐字阅读的笨办法来解读。同时也参考...
    时汝佳阅读 240评论 0 1
  • 区块链研习社比特币源码研读班今天研读第二,第三流程,SetupEnvironment和noui_connect函数...
    剑有偏锋阅读 335评论 0 0
  • 上一节提到了一个问题:比特币默认的日志输出文件是哪个? 不知道大家找到了没,现在答案公布如下: 如果有看过我的第一...
    Jacky_2c9f阅读 593评论 0 0
  • 我妈大嗓门,性格行动大大咧咧,行动风风火火,但是,却是个玻璃心,受不了我对她的任何刺激和打击还有语气,否则就会难过...
    又又双儿阅读 170评论 2 1