源码0.13.2版的,在vscode中打开的
目录
9)交易标准与签名字节数
非标准交易
fRequireStandard = !GetBoolArg("-acceptnonstdtxn", !Params().RequireStandard());
if (Params().RequireStandard() && !fRequireStandard)
return InitError(strprintf("acceptnonstdtxn is not currently supported for %s chain", chainparams.NetworkIDString()));
Params().RequireStandard()就是返回fRequireStandard
//chainparams.h class CChainParams
/** Policy: Filter transactions that do not match well-defined patterns */
bool RequireStandard() const { return fRequireStandard; }
bool fRequireStandard;
params()是在step2中定义的,CChainParams是基类,有三个子类这个之前也介绍过了,三个子类是基于不同的网络的,在主网中fRequireStandard=true,测试网和私有网都是false。也就是主网只接受标准交易,测试网与私有网可以接受非标准交易。
签名字节数
nBytesPerSigOp = GetArg("-bytespersigop", nBytesPerSigOp);
默认的签名操作字节数为20
//src/policy/policy.cpp
unsigned int nBytesPerSigOp = DEFAULT_BYTES_PER_SIGOP;
//policy.h
/** Default for -bytespersigop */
static const unsigned int DEFAULT_BYTES_PER_SIGOP = 20;
10)钱包相关参数
I 钱包开关
#ifdef ENABLE_WALLET
if (!CWallet::ParameterInteraction())
return false;
#endif // ENABLE_WALLET
ENABLE_WALLET宏定义开关在configure.ac文件中,可以在源码编译的时候控制该宏定义的开关
//configure.ac
# Enable wallet
AC_ARG_ENABLE([wallet],
[AS_HELP_STRING([--disable-wallet],
[disable wallet (enabled by default)])],
[enable_wallet=$enableval],
[enable_wallet=yes])
可以看到默认是打开钱包的,我们可以在运行比特币钱包客户端时通过disablewallet参数关闭钱包功能。
再看CWallet::ParameterInteraction(),如果设置了开启钱包,那么与钱包相关的交互参数必须设置正确,否则程序返回。CWallet::ParameterInteraction()位于src/wallet/wallet.cpp中,分析函数中的各个参数
II -mintxfee
每kb低于mintxfee的费用被视为交易创建时的未付费
if (mapArgs.count("-mintxfee"))
{
CAmount n = 0;
if (ParseMoney(mapArgs["-mintxfee"], n) && n > 0)
CWallet::minTxFee = CFeeRate(n);
else
return InitError(AmountErrMsg("mintxfee", mapArgs["-mintxfee"]));
}
ParseMoney()位于utilmoneystr.cpp,用于解析传入的数字,转为聪为单位的费用(包括小数的处理,数据格式正确与否)传回计算后的数值。这段的参数处理代码与minRelayTxFee参数的处理基本一致。
//amount.h
static const CAmount COIN = 100000000;
III -fallbackfee
当没有足够的信息用以估算费用时默认使用的费率。
代码中先解析参数是否正确,另外判断参数如果超过最高费率HIGH_TX_FEE_PER_KB会报警告⚠️
//main.h
//! Discourage users to set fees higher than this amount (in satoshis) per kB
static const CAmount HIGH_TX_FEE_PER_KB = 0.01 * COIN;
那么HIGH_TX_FEE_PER_KB为0.01BTC
III -paytxfee与maxtxfee
分别代表支付交易手续费与最高交易手续费,如果高于最高费率HIGH_TX_FEE_PER_KB有警告提示,若是低于最低费率::minRelayTxFee,则报错退出程序。
IV -txconfirmtarget
nTxConfirmTarget = GetArg("-txconfirmtarget", DEFAULT_TX_CONFIRM_TARGET);
从注释来看,txconfirmtarget表示如果没有设置paytxfee,则使用足够的费用,以便交易可能在平均n个块(默认值:%u)内开始确认。
//src/wallet/wallet.h
//! -txconfirmtarget default
static const unsigned int DEFAULT_TX_CONFIRM_TARGET = 2;
这里的默认值为2。如果设为6,你的交易获得首个确认将花费平均6个区块链验证时间。从0.14版本开始就是6个了,也就是我们说的比特币每一笔交易都需要经过6个区块确认才能算真正的交易成功。
V spendzeroconfchange
在发送交易时花费未确认的更改,也就是可以花费0确认的费用
bSpendZeroConfChange = GetBoolArg("-spendzeroconfchange", DEFAULT_SPEND_ZEROCONF_CHANGE);
在这里是默认可以的
//src/wallet/wallet.h
//! Default for -spendzeroconfchange
static const bool DEFAULT_SPEND_ZEROCONF_CHANGE = true;
//! Default for -sendfreetransactions
static const bool DEFAULT_SEND_FREE_TRANSACTIONS = false;
VI fSendFreeTransactions
如果可能,发送0费用的交易
fSendFreeTransactions = GetBoolArg("-sendfreetransactions", DEFAULT_SEND_FREE_TRANSACTIONS);
默认为不可以,默认不可以发送0手续费的交易
11)交易相关参数
fIsBareMultisigStd = GetBoolArg("-permitbaremultisig", DEFAULT_PERMIT_BAREMULTISIG);
fAcceptDatacarrier = GetBoolArg("-datacarrier", DEFAULT_ACCEPT_DATACARRIER);
nMaxDatacarrierBytes = GetArg("-datacarriersize", nMaxDatacarrierBytes);
I -permitbaremultisig
从注释来看是传递非P2SH多重签名脚本,默认是允许的
Relay non-P2SH multisig (default: %u)"), DEFAULT_PERMIT_BAREMULTISIG
//main.h
/** Default for -permitbaremultisig */
static const bool DEFAULT_PERMIT_BAREMULTISIG = true;
也就是默认非P2SH多重签名的交易在全网传播。
II -datacarrier、-datacarriersize
在帮助信息中找到注释,-datacarrier表示传播和挖矿包含交易以外数据信息的交易,默认设置为true;再看datacarriersize表示包含数据的交易大小默认值
//src/script/standard.cpp
unsigned nMaxDatacarrierBytes = MAX_OP_RETURN_RELAY;
//src/script/standard.h
static const unsigned int MAX_OP_RETURN_RELAY = 83;
其默认值为83字节
12)各种参数
I mocktime
用于回归测试,从新时期(这里指1970年,从输出time” : ttt, (numeric) The block time in seconds since epoch (Jan 1 1970 GMT) 推测)开始用n秒替换真实时间
// Option to startup with mocktime set (used for regression testing):
SetMockTime(GetArg("-mocktime", 0)); // SetMockTime(0) is a no-op
调用函数SetMockTime,执行赋值操作,一句代码。
//utlitime.cpp
static int64_t nMockTime = 0; //!< For unit testing
void SetMockTime(int64_t nMockTimeIn)
{
nMockTime = nMockTimeIn;
}
从注释可以看到,mocktime是用于单元测试的,默认值为0。
II peerbloomfilter
if (GetBoolArg("-peerbloomfilters", DEFAULT_PEERBLOOMFILTERS))
nLocalServices = ServiceFlags(nLocalServices | NODE_BLOOM);
//main.h
static const bool DEFAULT_PEERBLOOMFILTERS = true;
Bloom滤波器支持块过滤和事务处理,默认为true,关于bloom的具体信息可以看《精通比特币(第二版)》8.9Bloom过滤器。在支持过滤器的前提下,程序中设置了当前运行节点的服务模式
//net.cpp
ServiceFlags nLocalServices = NODE_NETWORK;
ServiceFlags是枚举类型,nLocalServices是初始赋值为NODE_NETWORK,在上述代码中又增加赋值NODE_BLOOM,即具备全节点信息存储与bloom过滤器功能,这两者是所有客户端默认具备的。
//protocol.h
/** nServices flags */
enum ServiceFlags : uint64_t {
// Nothing
NODE_NONE = 0,
// NODE_NETWORK means that the node is capable of serving the block chain. It is currently
// set by all Bitcoin Core nodes, and is unset by SPV clients or other peers that just want
// network services but don't provide them.
NODE_NETWORK = (1 << 0),
// NODE_GETUTXO means the node is capable of responding to the getutxo protocol request.
// Bitcoin Core does not support this but a patch set called Bitcoin XT does.
// See BIP 64 for details on how this is implemented.
NODE_GETUTXO = (1 << 1),
// NODE_BLOOM means the node is capable and willing to handle bloom-filtered connections.
// Bitcoin Core nodes used to support this by default, without advertising this bit,
// but no longer do as of protocol version 70011 (= NO_BLOOM_VERSION)
NODE_BLOOM = (1 << 2),
// Indicates that a node can be asked for blocks and transactions including
// witness data.
NODE_WITNESS = (1 << 3),
···
};
III -rpcserialversion
从帮助信息来看,该参数表示在非冗长模式、非隔离见证(0)或隔离见证(1)模式下原始交易或区块以16进制序列化方式呈现。默认值为1
//src/rpc/server.h
static const unsigned int DEFAULT_RPC_SERIALIZE_VERSION = 1;
代码主要是对取值做判断,该值只能为0或1
if (GetArg("-rpcserialversion", DEFAULT_RPC_SERIALIZE_VERSION) < 0)
return InitError("rpcserialversion must be non-negative.");
if (GetArg("-rpcserialversion", DEFAULT_RPC_SERIALIZE_VERSION) > 1)
return InitError("unknown rpcserialversion requested.");
IV maxtipage
nMaxTipAge = GetArg("-maxtipage", DEFAULT_MAX_TIP_AGE);
结合帮助信息和默认值来看,该参数表示当我们运行的节点包含的区块信息落后主网最长点24小时后,比特币客户端将进行区块同步下载(initial block download)操作。
//main.h
static const int64_t DEFAULT_MAX_TIP_AGE = 24 * 60 * 60;
关于initial block download(IBD)从官网的解释来看,并不是只能在刚启动时执行,而是当前节点信息落后全网最长链24小时或144个块就会执行。
13)mempoolreplacement
mempoolreplacement:Enable transaction replacement in the memory pool
表示能够替换交易池的交易,这个解释有些笼统,去bitcoinwiki查找transaction replacement,所谓交易替换是指可以在拥有全节点的客户端中替换交易池的交易,即针对同一输入,可以用花费了该部分或全部该输入金额的交易替换交易池中的交易。因此交易池中的交易是可以被替换的,前提是替换的交易产生于同一输入
fEnableReplacement = GetBoolArg("-mempoolreplacement", DEFAULT_ENABLE_REPLACEMENT);
if ((!fEnableReplacement) && mapArgs.count("-mempoolreplacement")) {
// Minimal effort at forwards compatibility
std::string strReplacementModeList = GetArg("-mempoolreplacement", ""); // default is impossible
std::vector<std::string> vstrReplacementModes;
boost::split(vstrReplacementModes, strReplacementModeList, boost::is_any_of(","));
fEnableReplacement = (std::find(vstrReplacementModes.begin(), vstrReplacementModes.end(), "fee") != vstrReplacementModes.end());
}
先看下其默认值
//main.h
/** Default for -mempoolreplacement */
static const bool DEFAULT_ENABLE_REPLACEMENT = true;
默认打开交易替换,解释一下boost::split,其中vstrReplacementModes是用来存储分割的结果的容器,strReplacementModeList是要分割的内容,按“,“分割,这段就是查找交易池替换模式内容,判断是否包含”fee”模式,如果包含则fEnableReplacement=true.
14)bip9params
首先查看帮助信息
-bip9params=deployment:start:end Use given start/end times for specified bip9 deployment (regtest-only)
bip9params是比特币在私有网测试时使用的参数,作为执行部署、执行部署开始和部署结束时间。bip9信号与激活的相关概念可以参考《精通比特币(第二版)》10.14使用区块版本发出软分叉信号。
bip9之前BIP-34,BIP-66 和 BIP-65 使用的机制成功地激活了三个软分叉,但通过使用块版本的整数值来实现,每次只能激活一个分叉,bip9将块版本解释为bit字段而不是一个整数,以及一些其他的规范。
回到代码部分,主要是针对bip9params参数的值进行私有网络测试部署,以测试软分叉后软件是否运行正常。
从这里开始,之前参考的作者写的就不适合我继续参考了,开始参考另一位博主了–Splay–
9.4 application initialization
// ********************************************************* Step 4: application initialization: dir lock, daemonize, pidfile, debug log
1)椭圆加密曲线
初始化椭圆曲线
// Initialize elliptic curve code
ECC_Start();
globalVerifyHandle.reset(new ECCVerifyHandle());
先看ECC_Start(),实现在key.cpp
void ECC_Start() {
assert(secp256k1_context_sign == NULL);
secp256k1_context *ctx = secp256k1_context_create(SECP256K1_CONTEXT_SIGN);
assert(ctx != NULL);
{
// Pass in a random blinding seed to the secp256k1 context.
unsigned char seed[32];
LockObject(seed);
GetRandBytes(seed, 32);
bool ret = secp256k1_context_randomize(ctx, seed);
assert(ret);
UnlockObject(seed);
}
secp256k1_context_sign = ctx;
}
2) initsanitycheck
可用性检测,未通过则报错
// Sanity check
if (!InitSanityCheck())
return InitError(strprintf(_("Initialization sanity check failed. %s is shutting down."), _(PACKAGE_NAME)));
主要功能是确保比特币在可用的环境中运行,并提供所有必要的库支持。
//init.cpp
/** Sanity checks
* Ensure that Bitcoin is running in a usable environment with all
* necessary library support.
*/
bool InitSanityCheck(void)
{
if(!ECC_InitSanityCheck()) {
InitError("Elliptic curve cryptography sanity check failure. Aborting.");
return false;
}
if (!glibc_sanity_test() || !glibcxx_sanity_test())
return false;
return true;
}
主要是三个验证,ECC_InitSanityCheck()为椭圆曲线加密结果的完整性验证,glibc_sanity_test() 和 glibcxx_sanity_test()验证当前运行环境是否支持C/C++运行环境
I ECC_nitSanityCheck
椭圆曲线加密结果验证
//key.cpp
bool ECC_InitSanityCheck() {
CKey key;
key.MakeNewKey(true);
CPubKey pubkey = key.GetPubKey();
return key.VerifyPubKey(pubkey);
}
//key.h
/** Check that required EC support is available at runtime. */
bool ECC_InitSanityCheck(void);
首先定义私钥对象,CKey类型
//key.h
/** An encapsulated private key. */
class CKey
{
private:
//! Whether this private key is valid. We check for correctness when modifying the key
//! data, so fValid should always correspond to the actual state.
bool fValid;
//! Whether the public key corresponding to this private key is (to be) compressed.
bool fCompressed;
//! The actual byte data
unsigned char vch[32];
//! Check whether the 32-byte array pointed to be vch is valid keydata.
bool static Check(const unsigned char* vch);
可以看到私钥是保存在长度为32的字符串vch中,我们知道私钥是256位,256/8=32字节。fValid参数用于表示私钥是否有效,该参数是在私钥发生变化时进行相应的修改,即私钥值有效为true。fCompressed表示与这个私钥相符合的公钥是否被压缩,true为压缩公钥。Check检查这32位字节是否是有效的密钥数据。
//key.h class CKey内
public:
//! Construct an invalid private key.
CKey() : fValid(false), fCompressed(false)
{
LockObject(vch);
}
定义对象调用构造函数时,该私钥是无效的,对应的公钥是不压缩的。调用 LockObject锁定私钥,从注释分析这个函数
//src/support/pagelocker.h
Functions for directly locking/unlocking memory objects. Intended for non-dynamically allocated structures.
//用于直接锁定/解锁内存对象的函数。用于非动态分配结构。
template <typename T>
void LockObject(const T& t)
{
LockedPageManager::Instance().LockRange((void*)(&t), sizeof(T));
}
LockRange就是在所有管理范围内,会新增一个锁的计数,具体代码不再展开
之后要创建私钥,来看MakeNewKey()这个函数,传入的参数true代表与私钥相符合的公钥是压缩的
//key.h
//! Generate a new private key using a cryptographic PRNG.
void MakeNewKey(bool fCompressed);
//key.cpp
void CKey::MakeNewKey(bool fCompressedIn) {
do {
GetStrongRandBytes(vch, sizeof(vch));
} while (!Check(vch));
fValid = true;
fCompressed = fCompressedIn;
}
从注释看这个函数通过使用加密PRNG(伪随机数,可以参考《图解密码技术》第12章随机数也可以百度)生成私钥。通过GetStrongRandBytes循环获取私钥,直到满足 Check的条件为止。找到满足的私钥后,设置该密钥为有效,是否压缩赋值。
//random.cpp
void GetStrongRandBytes(unsigned char* out, int num)
{
assert(num <= 32);
CSHA512 hasher;
unsigned char buf[64];
// First source: OpenSSL's RNG
RandAddSeedPerfmon();
GetRandBytes(buf, 32);
hasher.Write(buf, 32);
// Second source: OS RNG
GetOSRand(buf);
hasher.Write(buf, 32);
// Produce output
hasher.Finalize(buf);
memcpy(out, buf, num);
memory_cleanse(buf, 64);
}
通过OpenSSL’s RNG获得随机数,然后通过OS RNG获取随机数,生成哈希值,这里使用的是SHA512哈希算法,不过私钥是256位,所以我们只需要32位,之后清除随机数的内存。
Check函数的实现
//key.cpp
bool CKey::Check(const unsigned char *vch) {
return secp256k1_ec_seckey_verify(secp256k1_context_sign, vch);
}
这个函数是调用libsecp256k1库实现随机数的验证,libsecp256k1库的源码也包含在比特币源码中,位于src/secp256k1文件夹中,在secp256k1.h中有该函数的声明
/** Verify an ECDSA secret key.
*
* Returns: 1: secret key is valid
* 0: secret key is invalid
* Args: ctx: pointer to a context object (cannot be NULL)
* In: seckey: pointer to a 32-byte secret key (cannot be NULL)
*/
这个函数的功能是验证基于椭圆曲线创建的密钥。
下一步,创建公钥CPubKey pubkey = key.GetPubKey();先看CPubKey类
//pubkey.h class CPubKey
/**
* Just store the serialized data.
* Its length can very cheaply be computed from the first byte.
*/
unsigned char vch[65];
···
包含一个参数vch[65]主要用于存储序列化的公钥值,可以通过第一个字节即vch[0]获取公钥的长度,如果该值为2或3则为压缩公钥长度为33,值为4,6,7则为非压缩公钥长度为65。不是上述值,则该公钥值无效。
再看 key.GetPubKey(),该函数通过调用secp256k1库提供的函数首先通过secp256k1_ec_pubkey_create函数创建公钥值,再通过secp256k1_ec_pubkey_serialize函数实现压缩或非压缩公钥序列值的计算。
最后验证公钥 return key.VerifyPubKey(pubkey);
bool CKey::VerifyPubKey(const CPubKey& pubkey) const {
if (pubkey.IsCompressed() != fCompressed) {
return false;
}
unsigned char rnd[8];
std::string str = "Bitcoin key verification\n";
GetRandBytes(rnd, sizeof(rnd));
uint256 hash;
CHash256().Write((unsigned char*)str.data(), str.size()).Write(rnd, sizeof(rnd)).Finalize(hash.begin());
std::vector<unsigned char> vchSig;
Sign(hash, vchSig);
return pubkey.Verify(hash, vchSig);
}
获取8字节的随机数,把”Bitcoin key verification”和生成的随机数共同计算哈希值,在sign函数通过该哈希值基于ECADSA算法实现签名的计算,利用签名信息验证获取的公钥的有效性,验证函数为Verify()
//pubkey.h
/**
* Verify a DER signature (~72 bytes).
* If this public key is not fully valid, the return value will be false.
*/
bool Verify(const uint256& hash, const std::vector<unsigned char>& vchSig) const;
这个验证函数是验证DER格式的签名,DER是一种编码方案
II C与C++运行环境验证
glibc_sanity_test() 、 !glibcxx_sanity_test()这两个函数就是验证运行环境中C/C++运行库的有效性,即比特币核心软件能否在当前环境中正常运行。
3)锁定目录结构
确保只有一个比特币进程在使用数据目录
// Make sure only a single Bitcoin process is using the data directory.
boost::filesystem::path pathLockFile = GetDataDir() / ".lock";
FILE* file = fopen(pathLockFile.string().c_str(), "a"); // empty lock file; created if it doesn't exist.
if (file) fclose(file);
try {
static boost::interprocess::file_lock lock(pathLockFile.string().c_str());
if (!lock.try_lock())
return InitError(strprintf(_("Cannot obtain a lock on data directory %s. %s is probably already running."), strDataDir, _(PACKAGE_NAME)));
} catch(const boost::interprocess::interprocess_exception& e) {
return InitError(strprintf(_("Cannot obtain a lock on data directory %s. %s is probably already running.") + " %s.", strDataDir, _(PACKAGE_NAME), e.what()));
}
4)创建pid文件
对于非windows系统创建进程的pid文件
#ifndef WIN32
CreatePidFile(GetPidFile(), getpid());
#endif
参考文章http://siwind.iteye.com/blog/1753517可以知道
(1) pid文件的内容:pid文件为文本文件,内容只有一行, 记录了该进程的ID。
用cat命令可以看到。
(2) pid文件的作用:防止进程启动多个副本。只有获得pid文件(固定路径固定文件名)写入权限(F_WRLCK)的进程才能正常启动并把自身的PID写入该文件中。其它同一个程序的多余进程则自动退出。
那么这段代码创建pid文件的目的就是防止进程启动多个副本,从而打乱原有的消息传输。
5)参数设置
I 限定日志大小
if (GetBoolArg("-shrinkdebugfile", !fDebug))
ShrinkDebugFile();
-shrinkdebugfile:Shrink debug.log file on client startup (default: 1 when no -debug)
限制日志文件的大小,如果没有设置-debug参数,那么默认值为1
如果设置了这个参数,则调用函数ShrinkDebugFile
//util.cpp
void ShrinkDebugFile()
{
// Scroll debug.log if it's getting too big
boost::filesystem::path pathLog = GetDataDir() / "debug.log";
FILE* file = fopen(pathLog.string().c_str(), "r");
if (file && boost::filesystem::file_size(pathLog) > 10 * 1000000)
{
// Restart the file with some of the end
std::vector <char> vch(200000,0);
fseek(file, -((long)vch.size()), SEEK_END);
int nBytes = fread(begin_ptr(vch), 1, vch.size(), file);
fclose(file);
file = fopen(pathLog.string().c_str(), "w");
if (file)
{
fwrite(begin_ptr(vch), 1, nBytes, file);
fclose(file);
}
}
else if (file != NULL)
fclose(file);
}
判断debug.log文件大小超过10*1000000(10M)的话就重新读取文件最后200000字节的内容重新保存到debug.log文件中。
II deuglog显示处理
if (fPrintToDebugLog)
OpenDebugLog();
默认是打印到测试日志的
//util.cpp
bool fPrintToDebugLog = true;
OpenDebugLog()函数实现如下
//util.cpp
static void DebugPrintInit()
{
assert(mutexDebugLog == NULL);
mutexDebugLog = new boost::mutex();
vMsgsBeforeOpenLog = new list<string>;
}
void OpenDebugLog()
{
boost::call_once(&DebugPrintInit, debugPrintInitFlag);
boost::mutex::scoped_lock scoped_lock(*mutexDebugLog);
assert(fileout == NULL);
assert(vMsgsBeforeOpenLog);
boost::filesystem::path pathDebug = GetDataDir() / "debug.log";
fileout = fopen(pathDebug.string().c_str(), "a");
if (fileout) setbuf(fileout, NULL); // unbuffered
// dump buffered messages from before we opened the log
while (!vMsgsBeforeOpenLog->empty()) {
FileWriteStr(vMsgsBeforeOpenLog->front(), fileout);
vMsgsBeforeOpenLog->pop_front();
}
delete vMsgsBeforeOpenLog;
vMsgsBeforeOpenLog = NULL;
}
Boost线程库提供了boost::call_once来支持“一次实现”,并且定义了一个标志boost::once_flag及一个初始化这个标志的宏BOOST_ONCE_INIT。并且是在编译期间初始化而不是运行期间
boost::call_once表示在多线程访问该语句时始终只执行一次调用的函数,来确保mutexDebugLog和 vMsgsBeforeOpenLog的初始化是线程安全的。
//util.cpp
static boost::once_flag debugPrintInitFlag = BOOST_ONCE_INIT;
其中第一个参数是被调用的函数地址,第二个参数类型为boost::once_flag,并初始化这个标志为BOOST_ONCE_INIT
在函数DebugPrintInit()中定义了变量mutexDebugLog,类型为boost::mutex(),表示为互斥锁,在OpenDebugLog()函数中是使用了boost::mutex::scoped_lock来上锁,scoped_lock是能够保证在作用域范围内是互斥访问的,离开作用域时由析构函数自动解锁。在DebugPrintInit()中创建了对象vMsgsBeforeOpenLog,类型为链表,此时链表为空,在OpenDebugLog()中需要在打开日志前转储缓冲信息,就是把vMsgsBeforeOpenLog的信息转到debug.log中,那么vMsgsBeforeOpenLog的内容是怎么获得的呢
在util.cpp的LogPrintStr函数中
// buffer if we haven't opened the log yet
if (fileout == NULL) {
assert(vMsgsBeforeOpenLog);
ret = strTimestamped.length();
vMsgsBeforeOpenLog->push_back(strTimestamped);
}
这里就是把日志先存入vMsgsBeforeOpenLog在log文件未打开的时候,所以在OpenDebugLog()之前vMsgsBeforeOpenLog变量已经被创建了,然后LogPrintStr又被调用了很多次,每次都写入一写内容。
III 打印提示信息
if (!fLogTimestamps)
LogPrintf("Startup time: %s\n", DateTimeStrFormat("%Y-%m-%d %H:%M:%S", GetTime()));
LogPrintf("Default data directory %s\n", GetDefaultDataDir().string());
LogPrintf("Using data directory %s\n", strDataDir);
LogPrintf("Using config file %s\n", GetConfigFile().string());
LogPrintf("Using at most %i connections (%i file descriptors available)\n", nMaxConnections, nFD);
std::ostringstream strErrors;
如果没有设置打印时间戳,就打印起始时间,后面打印默认地址、数据目录之类的信息。
(五)草稿中。。。