首页
学习
活动
专区
工具
TVP
发布
精选内容/技术社群/优惠产品,尽在小程序
立即前往

比特币源码研读之二十二

我们今天将继续深入AppInitMain函数,下面我们一起来对其进行详细分析。

本文将着重分析钱包数据库文件验证与钱包文件恢复的代码。本文主要涉及的源码文件包括:

src/bitcond.cpp、src/init.h、src/init.cpp、src/util.h、src/util.cpp、src/wallet/wallet.h、src/wallet/wallet.cpp、src/wallet/db.h、src/wallet/db.cpp、src/wallet/walletdb.h、src/wallet/walletdb.cpp

这部分代码位于src/wallet/wallet.h中的CWallet类中,我们首先来看Verify函数在头文件中的声明:

//! Verify the wallet database and perform salvage ifrequired

static bool Verify();

该函数为静态全局函数,其作用是验证钱包数据库并在必要时执行钱包恢复操作。它是如何验证和执行钱包恢复的,我们将从其函数的代码中详细解读。

一、 钱包关闭参数

Verify()函数中首选进行钱包功能是否关闭判断,其判断依据为DEFAULT_DISABLE_WALLET参数,该参数定义于wallet.h中:

static const bool DEFAULT_DISABLE_WALLET = false;

从其定义可以看出钱包默认是开启的,但如果我们在启动比特币全节点钱包时,设置了disablewallet参数的话,钱包功能将被关闭,此时也就无需对钱包进行验证了,程序将退出。

二、钱包文件验证

比特币钱包数据库使用的是BerkeleyDB,那有人会说,我在用比特币全节点钱包的时候,并没有让我安装这个数据库啊,那这数据库功能怎么起作用的,如何实现钱包的存储功能呢?我们先来看下BerkeleyDB的功能介绍:

Berkeley DB是一个开源的文件数据库,介于关系数据库与内存数据库之间,使用方式与内存数据库类似,它提供的是一系列直接访问数据库的函数,而不是像关系数据库那样需要网络通讯、SQL解析等步骤。

从上述介绍我们可以看出,BerkeleyDB并不是Oracle、SQL Server或者MySQL那样是独立的关系型数据库,而是类似内存型数据库,直接在内存中进行数据库操作,主要应用在UNIX/LINUX操作系统上,其设计思想是简单、小巧、可靠、高性能。从这也可以看出中本聪在设计比特币时尽量以实用为宗旨进行实现,不会依赖多余的外部软件,这样可以更好地保证比特币节点的独立性。

现在我们再来看下钱包文件验证相关的具体代码:

1、LogPrintf("UsingBerkeleyDB version %s\n", DbEnv::version(0, 0, 0));

2、std::stringwalletFile = GetArg("-wallet", DEFAULT_WALLET_DAT);

3、LogPrintf("Usingwallet %s\n", walletFile);

4、uiInterface.InitMessage(_("Verifyingwallet..."));

// Wallet file must be a plain filename without adirectory

5、If (walletFile!= boost::filesystem::basename(walletFile) +boost::filesystem::extension(walletFile))

6、return InitError(strprintf(_("Wallet %s resides outside data directory %s"), walletFile, GetDataDir().string()));

上述代码中主要包含6行代码:

(1)在日志中打印BerkeleyDB的版本信息,其版本信息通过DbEnv::version(0,0,0)获得,DBEnv我们在wallet.h和wallet.cpp中并没有找到其定义,那么它是在哪定义的呢?DBEnv是berkeleydb的全局环境类,version函数是#include 中定义,我在berkeleydb的API介绍文档中找到了version的定义:

从该文档可以看出其返回值为berkeleydb的版本信息,返回的版本信息包括主版本、次版本以及修订版本3个信息。我们知道berkeleydb的版本信息通过LogPrintf函数将其打印至日志文件中,我们打开日志文件看下其打印信息:

绿色选中部分即为berkeleydb的版本打印信息,为4.8.3。

(2)第二行代码是获取代码文件,代码文件默认为DEFAULT_WALLET_DAT,其定义于wallet.cpp中,其定义如下:

const char * DEFAULT_WALLET_DAT ="wallet.dat";

虽然在这行代码中有-wallet参数,表示我们在启动比特币钱包时可以设置钱包文件的名称,但一般来说,大家还是默认使用wallet.dat文件。

(3)第3行和第4行代码中,分别在日志中打印了钱包文件名,同时会显示当前钱包状态为“Verifying wallet……”;

(4)第5行和第6行代码中验证钱包名称,通过其注释以及错误提示可以看出,钱包名称必须是且仅是文件名,不包含任何路径信息,而且钱包文件必须是在数据目录下。

三、 打开钱包数据库

此处将通过bitdb.open()打开钱包数据库,其中bitdb变量为CDBEnv的对象,该变量为全局变量,在src/wallet/db.h中声明,定义于db.cpp中:

extern CDBEnv bitdb; //db.h

CDBEnv bitdb; //db.cpp

CDBEnv中的open函数实现代码为用户db.cpp中,具体代码大家可以去该文件中查看,在这个函数中主要实现了database文件夹与db.log文件的创建,同时对钱包数据库环境进行初始化,设置相应参数。

如果数据库环境创建失败,程序将通过boost::filesystem::rename(pathDatabase,

pathDatabaseBak);把当前创建的database文件夹重命名,命名后的文件夹名称包含当前时间。

然后程序再次尝试创建钱包数据库,对其进行初始化,如果这次再次失败,程序将提示钱包数据库环境创建失败,并退出程序。

四、 钱包文件恢复

当我们的钱包文件出现损坏时,我们是可以尝试恢复的,恢复方式为在钱包文件启动时,设置salvgewallet参数,该参数的意义是:

-salvgewallet:参数的功能为试图在比特币客户端启动时从损坏的钱包中恢复私钥。

这个参数我们在《比特币源码研读之十六》中也解释过,大家有兴趣可以再回头看下第十六篇记录。

如果设置了salvgewallet参数后,程序此时将进行钱包恢复操作,其恢复操作是进入CWalletDB的Recover函数中。在该函数的实现中,我们可以看到在该函数的开始处就描述了其恢复过程:

(2)通过调用CDBEnv的Salvage函数,并且参数fAggressive=true的情况下恢复尽可能多的数据;

(3)将恢复的数据重写至新的钱包文件中;

(4)通过-rescan参数对区块进行重新扫描,进而恢复丢失的交易。

五、 钱包文件验证

程序将通过bitdb对钱包文件(无论是已有的还是刚恢复的)进行验证,如果验证出错,该Verify函数中的回调函数将尝试恢复钱包文件,如果恢复成功则返回RECOVER_OK,否则返回RECOVER_FAIL。

以上即为钱包验证函数的代码分析,从本文的分析我们可以看出比特币的钱包文件为wallet.dat,钱包信息存储用的数据库为BerkeleyDB,同时我们还可以通过salvagewallet参数对钱包进行恢复操作。

区块链研习社比特币源码研读班 菜菜子

  • 发表于:
  • 原文链接http://kuaibao.qq.com/s/20180406G0CU9900?refer=cp_1026
  • 腾讯「腾讯云开发者社区」是腾讯内容开放平台帐号(企鹅号)传播渠道之一,根据《腾讯内容开放平台服务协议》转载发布内容。
  • 如有侵权,请联系 cloudcommunity@tencent.com 删除。

扫码

添加站长 进交流群

领取专属 10元无门槛券

私享最新 技术干货

扫码加入开发者社群
领券