请选择 进入手机版 | 继续访问电脑版
开启辅助访问
QQ登录|微信登录|登录 |立即注册

盖茨网区块链技术社区

从0开始分析比特币核心代码(1)

(一)构建代码管理工程
     代码的观看和学习没有一个好的代码编辑工具学习起来是十分难受的,因此我这里使用VS来看代码,VS可以自由的跳跃函数定义,看起来比较容易,但是记住这只是用来观看代码的,如果要用VS编译代码还得另做部署。
     在GITHUB上下载bitcoind源码后解压,打开VS,随便哪个版本都可以。创建一个win32空项目,把源码src目录下的.cpp .h导入进来,不要导入子目录下的文件,暂时这样就够了,后面需要再导入呗。
    %KDBYWI@(G(V8I5QYNUZKGO.png
   然后找到bitcoind.cpp这个文件,这就是比特币核心代码了,然后设置一下头文件路径到 源码/src ,然后开始学习把。
(2)程序入口
  1. int main(int argc, char* argv[])
  2. {
  3.     SetupEnvironment();

  4.     // Connect bitcoind signal handlers
  5.     noui_connect();

  6.     return (AppInit(argc, argv) ? EXIT_SUCCESS : EXIT_FAILURE);
  7. }
复制代码
在bitcoind.cpp这个文件最后的main就是程序的入口了,主函数比较简单就三句,我们分别分析。
  1. void SetupEnvironment()
  2. {
  3. #ifdef HAVE_MALLOPT_ARENA_MAX
  4.     // glibc-specific: On 32-bit systems set the number of arenas to 1.
  5.     // By default, since glibc 2.10, the C library will create up to two heap
  6.     // arenas per core. This is known to cause excessive virtual address space
  7.     // usage in our usage. Work around it by setting the maximum number of
  8.     // arenas to 1.
  9.     if (sizeof(void*) == 4) {
  10.         mallopt(M_ARENA_MAX, 1);
  11.     }
  12. #endif
  13.     // On most POSIX systems (e.g. Linux, but not BSD) the environment's locale
  14.     // may be invalid, in which case the "C" locale is used as fallback.
  15. #if !defined(WIN32) && !defined(MAC_OSX) && !defined(__FreeBSD__) && !defined(__OpenBSD__)
  16.     try {
  17.         std::locale(""); // Raises a runtime error if current locale is invalid
  18.     } catch (const std::runtime_error&) {
  19.         setenv("LC_ALL", "C", 1);
  20.     }
  21. #endif
  22.     // The path locale is lazy initialized and to avoid deinitialization errors
  23.     // in multithreading environments, it is set explicitly by the main thread.
  24.     // A dummy locale is used to extract the internal default locale, used by
  25.     // fs::path, which is then used to explicitly imbue the path.
  26.     std::locale loc = fs::path::imbue(std::locale::classic());
  27.     fs::path::imbue(loc);
  28. }
复制代码
该函数主要设置了程序启动的一些环境,sizeof(void*) == 4表示系统为32位系统,如果为32位系统,mallopt(M_ARENA_MAX, 1)就设置一下malloc一次分配的内存大小,后面设置了一些文件路径的表达,字符编码可以百度看函数用法。

  1. void noui_connect()
  2. {
  3.     // Connect bitcoind signal handlers
  4.     uiInterface.ThreadSafeMessageBox.connect(noui_ThreadSafeMessageBox);
  5.     uiInterface.ThreadSafeQuestion.connect(noui_ThreadSafeQuestion);
  6.     uiInterface.InitMessage.connect(noui_InitMessage);
  7. }
复制代码
设置了一些信号和槽的关联,用的是boost库的信号机制方便程序后续调用。

  1. bool AppInit(int argc, char* argv[])
复制代码
最后进入主程序。

(2)主程序初始化
  1. #if ENABLE_WALLET
  2.     g_wallet_init_interface.reset(new WalletInit);
  3. #else
  4.     g_wallet_init_interface.reset(new DummyWalletInit);
  5. #endif
复制代码
一开始根据一个宏开关构造了一个钱包初始化的类,他们分别是继承的一个WalletInitInterface基类

  1.     gArgs.ParseParameters(argc, argv);

  2.     // Process help and version before taking care about datadir
  3.     if (gArgs.IsArgSet("-?") || gArgs.IsArgSet("-h") ||  gArgs.IsArgSet("-help") || gArgs.IsArgSet("-version"))
  4.     {
  5.         std::string strUsage = strprintf(_("%s Daemon"), _(PACKAGE_NAME)) + " " + _("version") + " " + FormatFullVersion() + "\n";

  6.         if (gArgs.IsArgSet("-version"))
  7.         {
  8.             strUsage += FormatParagraph(LicenseInfo());
  9.         }
  10.         else
  11.         {
  12.             strUsage += "\n" + _("Usage:") + "\n" +
  13.                   "  bitcoind [options]                     " + strprintf(_("Start %s Daemon"), _(PACKAGE_NAME)) + "\n";

  14.             strUsage += "\n" + HelpMessage(HelpMessageMode::BITCOIND);
  15.         }

  16.         fprintf(stdout, "%s", strUsage.c_str());
  17.         return true;
  18.     }
复制代码
然后进行了命令行参数的解析,gArgs.ParseParameters(argc, argv);将命令行参数存入一个map中。
  1. if (gArgs.IsArgSet("-?") || gArgs.IsArgSet("-h") || gArgs.IsArgSet("-help") || gArgs.IsArgSet("-version"))
复制代码
如果参数是需要显示帮助信息或者版本号立即返回显示。

  1. if (!fs::is_directory(GetDataDir(false)))
  2.         {
  3.             fprintf(stderr, "Error: Specified data directory "%s" does not exist.\n", gArgs.GetArg("-datadir", "").c_str());
  4.             return false;
  5.         }
  6.         try
  7.         {
  8.             gArgs.ReadConfigFile(gArgs.GetArg("-conf", BITCOIN_CONF_FILENAME));
  9.         } catch (const std::exception& e) {
  10.             fprintf(stderr,"Error reading configuration file: %s\n", e.what());
  11.             return false;
  12.         }
复制代码
接下来判断-datadir参数指定的目录是否存在,读取-conf指定的配置文件信息,可以不指定-conf

  1. // Check for -testnet or -regtest parameter (Params() calls are only valid after this clause)
  2.         try {
  3.             SelectParams(ChainNameFromCommandLine());
  4.         } catch (const std::exception& e) {
  5.             fprintf(stderr, "Error: %s\n", e.what());
  6.             return false;
  7.         }
复制代码
紧接着根据命令行参数-testnet or -regtest选择网络类型是主网络,测试网络还是本地网络。
  1. std::string ChainNameFromCommandLine()
  2. {
  3.     bool fRegTest = gArgs.GetBoolArg("-regtest", false);
  4.     bool fTestNet = gArgs.GetBoolArg("-testnet", false);

  5.     if (fTestNet && fRegTest)
  6.         throw std::runtime_error("Invalid combination of -regtest and -testnet.");
  7.     if (fRegTest)
  8.         return CBaseChainParams::REGTEST;
  9.     if (fTestNet)
  10.         return CBaseChainParams::TESTNET;
  11.     return CBaseChainParams::MAIN;
  12. }
复制代码
根据命令行参数返回一个代表网络的字符串
  1. void SelectParams(const std::string& network)
  2. {
  3.     SelectBaseParams(network);
  4.     globalChainParams = CreateChainParams(network);
  5. }
复制代码
SelectParams根据返回的字符串构造不同的CChainParams父类, 我们可以看一下其中一个父类 CMainParams()
  1. CMainParams() {
  2.         strNetworkID = "main";
  3.         consensus.nSubsidyHalvingInterval = 210000;
  4.         consensus.BIP16Height = 173805; // 00000000000000ce80a7e057163a4db1d5ad7b20fb6f598c9597b9665c8fb0d4 - April 1, 2012
  5.         consensus.BIP34Height = 227931;
  6.         consensus.BIP34Hash = uint256S("0x000000000000024b89b42a942fe0d9fea3bb44ab7bd1b19115dd6a759c0808b8");
  7.         consensus.BIP65Height = 388381; // 000000000000000004c2b624ed5d7756c508d90fd0da2c7c679febfa6c4735f0
  8.         consensus.BIP66Height = 363725; // 00000000000000000379eaa19dce8c9b722d46ae6a57c2f1a988119488b50931
  9.         consensus.powLimit = uint256S("00000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffff");
  10.         consensus.nPowTargetTimespan = 14 * 24 * 60 * 60; // two weeks
  11.         consensus.nPowTargetSpacing = 10 * 60;
  12.         consensus.fPowAllowMinDifficultyBlocks = false;
  13.         consensus.fPowNoRetargeting = false;
  14.         consensus.nRuleChangeActivationThreshold = 1916; // 95% of 2016
  15.         consensus.nMinerConfirmationWindow = 2016; // nPowTargetTimespan / nPowTargetSpacing
  16.         consensus.vDeployments[Consensus::DEPLOYMENT_TESTDUMMY].bit = 28;
  17.         consensus.vDeployments[Consensus::DEPLOYMENT_TESTDUMMY].nStartTime = 1199145601; // January 1, 2008
  18.         consensus.vDeployments[Consensus::DEPLOYMENT_TESTDUMMY].nTimeout = 1230767999; // December 31, 2008
  19. ……
  20. genesis = CreateGenesisBlock(1231006505, 2083236893, 0x1d00ffff, 1, 50 * COIN);
  21. ……
复制代码
consensus为共识机制类,genesis的类型为一个区块的类型CreateGenesisBlock()在这里创建了创世块。
  1. static CBlock CreateGenesisBlock(uint32_t nTime, uint32_t nNonce, uint32_t nBits, int32_t nVersion, const CAmount& genesisReward)
  2. {
  3.     const char* pszTimestamp = "The Times 03/Jan/2009 Chancellor on brink of second bailout for banks";
  4.     const CScript genesisOutputScript = CScript() << ParseHex("04678afdb0fe5548271967f1a67130b7105cd6a828e03909a67962e0ea1f61deb649f6bc3f4cef38c4f35504e51ec112de5c384df7ba0b8d578a4c702b6bf11d5f") << OP_CHECKSIG;
  5.     return CreateGenesisBlock(pszTimestamp, genesisOutputScript, nTime, nNonce, nBits, nVersion, genesisReward);
  6. }
复制代码
CreateGenesisBlock中我们可以看到中本聪写下的第一句话"The Times 03/Jan/2009 Chancellor on brink of second bailout for banks",
const CScript genesisOutputScript = CScript() << ParseHex("04678afdb0fe5548271967f1a67130b7105cd6a828e03909a67962e0ea1f61deb649f6bc3f4cef38c4f35504e51ec112de5c384df7ba0b8d578a4c702b6bf11d5f") << OP_CHECKSIG;
就是我们大名鼎鼎的数字签名了,是不是很有意思。

回到代码。

  1. // Error out when loose non-argument tokens are encountered on command line
  2.         for (int i = 1; i < argc; i++) {
  3.             if (!IsSwitchChar(argv[i][0])) {
  4.                 fprintf(stderr, "Error: Command line contains unexpected token '%s', see bitcoind -h for a list of options.\n", argv[i]);
  5.                 return false;
  6.             }
  7.         }

  8.         // -server defaults to true for bitcoind but not for the GUI so do this here
  9.         gArgs.SoftSetBoolArg("-server", true);
复制代码
这个for循环检测了命令行参数的合法性,gArgs.SoftSetBoolArg("-server", true)如果没有设置"-server"参数就将其设置为true


  1. InitLogging();
  2.         InitParameterInteraction();//网络参数
  3.         if (!AppInitBasicSetup())//注册相应的消息以及处理方式。
  4.         {
  5.             // InitError will have been called with detailed error, which ends up on console
  6.             return false;
  7.         }
  8.         if (!AppInitParameterInteraction())//设置区块链运行参数,例如交易费等等。
  9.         {
  10.             // InitError will have been called with detailed error, which ends up on console
  11.             return false;
  12.         }
  13.         if (!AppInitSanityChecks())//Sanity Check是用来检查比特币运行时所需要的所有的库是否都运行正常。
复制代码
紧接着就是一连串的各种初始化,不要急,我们在后面一一分析。

##原创内容,未经作者允许不得私自转载用于商业用途(盖茨版权所有)

0 个回复

倒序浏览

快速回复

您需要登录后才可以回帖 登录 or 立即注册

本版积分规则