您的位置:首页 > 其它

比特币源码解析(15) - 可执行程序 - Bitcoind

2017-09-20 10:18 609 查看

0x01 Step 3: parameter-to-internal-flags - continue

由于Step 3中的内容太多,所以上一章未能完成,这一章继续分析Step 3中剩下的内容。

连接超时时间

nConnectTimeout = gArgs.GetArg("-timeout", DEFAULT_CONNECT_TIMEOUT);
if (nConnectTimeout <= 0)
nConnectTimeout = DEFAULT_CONNECT_TIMEOUT;


-timeout
:表示在发起TCP连接时的等待时间,单位是毫秒,默认值为5000。

节点费用设置

if (gArgs.IsArgSet("-minrelaytxfee")) {
CAmount n = 0;
if (!ParseMoney(gArgs.GetArg("-minrelaytxfee", ""), n)) {
return InitError(AmountErrMsg("minrelaytxfee", gArgs.GetArg("-minrelaytxfee", "")));
}
// High fee check is done afterward in WalletParameterInteraction()
::minRelayTxFee = CFeeRate(n);
} else if (incrementalRelayFee > ::minRelayTxFee) {
// Allow only setting incrementalRelayFee to control both
::minRelayTxFee = incrementalRelayFee;
LogPrintf("Increasing minrelaytxfee to %s to match incrementalrelayfee\n",::minRelayTxFee.ToString());
}

// Sanity check argument for min fee for including tx in block
// TODO: Harmonize which arguments need sanity checking and where that happens
if (gArgs.IsArgSet("-blockmintxfee"))
{
CAmount n = 0;
if (!ParseMoney(gArgs.GetArg("-blockmintxfee", ""), n))
return InitError(AmountErrMsg("blockmintxfee", gArgs.GetArg("-blockmintxfee", "")));
}

// Feerate used to define dust.  Shouldn't be changed lightly as old
// implementations may inadvertently create non-standard transactions
if (gArgs.IsArgSet("-dustrelayfee"))
{
CAmount n = 0;
if (!ParseMoney(gArgs.GetArg("-dustrelayfee", ""), n) || 0 == n)
return InitError(AmountErrMsg("dustrelayfee", gArgs.GetArg("-dustrelayfee", "")));
dustRelayFee = CFeeRate(n);
}


在上一章http://blog.csdn.net/pure_lady/article/details/77982837#t3部分我们介绍了几种不同的费用,以及节点在收到交易时的处理流程。现在这里就到了设置这些变量的值的时候,首先是
minrelayfee
,最小转发费用,从代码中可以发现如果
incrementalRelayFee
大于
minRelayFee
,那么
minRelayFee=incrementalRelayFee


-blockmintxfee
:设置交易被打包进区块的最小费用率,单位为BTC/KB,默认值为0.00001。

接下来的
blockmintxfee
是针对矿工而言的,矿工在将交易打包进区块之前先判断交易费是否满足条件,避免出现入不敷出的情况,因为矿工在挖矿时也有一定的成本,而交易费也是收益的一部分来源,所以矿工也要尽量让自己利益最大化。最后的
dustrelayfee
在上一章也介绍过,这里只是读取命令行中设置的值并传给
destRelayFee
变量。

判断非标准交易

fRequireStandard = !gArgs.GetBoolArg("-acceptnonstdtxn", !chainparams.RequireStandard());
if (chainparams.RequireStandard() && !fRequireStandard)
return InitError(strprintf("acceptnonstdtxn is not currently supported for %s chain", chainparams.NetworkIDString()));
nBytesPerSigOp = gArgs.GetArg("-bytespersigop", nBytesPerSigOp);


-acceptnonstdtxn
:是否接受或者转发非标准交易,只适用于testnet和regtest,默认值为1.

-bytespersigop
:设置交易中每个sigop的大小,单位为字节,默认值为20.

接下来就是判断chainparams中的参数和
-acceptnonstdtxn
参数的值是否相互冲突。
-bytespersigop
是用来计算交易大小的参数,配合
nSigOpsCost
,表示交易中操作符的数量,两者的乘积就是交易的大小,像下面的函数中实现的。

int64_t GetVirtualTransactionSize(int64_t nWeight, int64_t nSigOpCost)
{
return (std::max(nWeight, nSigOpCost * nBytesPerSigOp) + WITNESS_SCALE_FACTOR - 1) / WITNESS_SCALE_FACTOR;
}


初始化钱包参数&设定交易中数据大小

#ifdef ENABLE_WALLET
if (!WalletParameterInteraction())
return false;
#endif

fIsBareMultisigStd = gArgs.GetBoolArg("-permitbaremultisig", DEFAULT_PERMIT_BAREMULTISIG);
fAcceptDatacarrier = gArgs.GetBoolArg("-datacarrier", DEFAULT_ACCEPT_DATACARRIER);
nMaxDatacarrierBytes = gArgs.GetArg("-datacarriersize", nMaxDatacarrierBytes);


接下来这段代码首先判断是否启用了钱包,如果启用了,那么就进行钱包参数的初始化,点开
WalletParameterInteraction()
函数可以发现,该函数还是比较简单,就是把命令行中相应的参数赋值给
CWallet
中的变量,钱包部分暂且跳过,分析完主要功能部分之后可以再来单独分析钱包的实现。接下来分别设定三个参数,

permitbaremultisig
:是否允许转发
non-P2SH
多签名,默认值为1。

datacarrier
:表示是否允许在交易中写入数据,默认为1.

datacarriersize
:表示交易中写入数据的最大大小,默认值为83.

系统中总共定义了6种交易类型,类型包含在
CTxOut
中的
scriptPubKey
中,交易主要分为两种:标准和非标准,除了非标准的,其他5种都是标准的交易,6种类型分别如下,

交易类型描述
TX_NONSTANDARD
非标准的交易
TX_PUBKEY
公钥
TX_PUBKEYHASH
公钥哈希
TX_SCRIPTHASH
脚本哈希
TX_MULTISIG
多重签名
TX_NULL_DATA
空数据
datacarrier
对应的类型就是最后一种
TX_NULL_DATA
,并由
datacarriersize
指定写入数据的最大长度,具体实现函数位于
src/policy/policy.cpp
中:

bool IsStandard(const CScript& scriptPubKey, txnouttype& whichType, const bool witnessEnabled)
{
std::vector<std::vector<unsigned char> > vSolutions;
if (!Solver(scriptPubKey, whichType, vSolutions))  // Solver函数解析scriptPubKey中的信息
return false;

if (whichType == TX_MULTISIG)
{
unsigned char m = vSolutions.front()[0];
unsigned char n = vSolutions.back()[0];
// Support up to x-of-3 multisig txns as standard
if (n < 1 || n > 3)
return false;
if (m < 1 || m > n)
return false;
} else if (whichType == TX_NULL_DATA &&
(!fAcceptDatacarrier || scriptPubKey.size() > nMaxDatacarrierBytes))
return false;  // 这里判断

else if (!witnessEnabled && (whichType == TX_WITNESS_V0_KEYHASH || whichType == TX_WITNESS_V0_SCRIPTHASH))
return false;

return whichType != TX_NONSTANDARD;
}


Initial Block Download

// Option to startup with mocktime set (used for regression testing):
SetMockTime(gArgs.GetArg("-mocktime", 0)); // SetMockTime(0) is a no-op

if (gArgs.GetBoolArg("-peerbloomfilters", DEFAULT_PEERBLOOMFILTERS))
nLocalServices = ServiceFlags(nLocalServices | NODE_BLOOM);

if (gArgs.GetArg("-rpcserialversion", DEFAULT_RPC_SERIALIZE_VERSION) < 0)
return InitError("rpcserialversion must be non-negative.");

if (gArgs.GetArg("-rpcserialversion", DEFAULT_RPC_SERIALIZE_VERSION) > 1)
return InitError("unknown rpcserialversion requested.");

nMaxTipAge = gArgs.GetArg("-maxtipage", DEFAULT_MAX_TIP_AGE);


上面这段代码主要涉及下面几个参数:

-mocktime=<n>
:设定系统模拟时间,只适用于regression test,模拟时间表示将时间设置为创世后n秒,即时间从0年0月0日0时0分n秒开始。

-peerbloomfilters
:是否支持使用bloom filter来过滤区块和交易,默认为1.

-rpcserialversion
:设置原始交易或者区块在non-verbose模式下的返回值,取值只有两种,0表示non-segwit,1表示segwit,默认值为1.

-maxtipage
:执行IBD(Initial block download)的最大时间间隔,单位为秒,默认值为86400,也就是24小时。

首先模拟时间比较容易理解,就是将当前时间设为0+n秒;接着
peerbloomfilters
参数决定是否开启bloom filter服务,该服务的主要功能是按照一定条件过滤某些特定的交易给自己或者其他节点;然后
rpcserialversion
设定序列化版本,具体在何处使用到还的看接下来的分析;最后
maxtipage
表示如果当前时间和本地区块链最后一个区块生成的时间差大于
maxtipage
那么将执行
IBD
函数,IBD函数表示要一次性下载大量的区块,具体介绍请参考https://bitcoin.org/en/developer-guide#initial-block-download,默认值为24小时,也就是说如果节点一天没有更新本地的区块链信息,那么就会执行IBD来从网络同步区块。

mempoolreplacement

fEnableReplacement = gArgs.GetBoolArg("-mempoolreplacement", DEFAULT_ENABLE_REPLACEMENT);
if ((!fEnableReplacement) && gArgs.IsArgSet("-mempoolreplacement")) {
// Minimal effort at forwards compatibility
std::string strReplacementModeList = gArgs.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());
}


-mempoolreplacement
:启用内存池中的交易替换。

所谓交易替换就是指全节点的mempool中如果有多个交易花费了相同的inputs,那么他们之间允许替换。不过代码的
if
语句没有看懂,从第一句
GetBoolArg()
来看,
mempoolreplacement
应该是数值类型,但是后面又要在数值型中查找字符串,那结果肯定是
false
,所以还是不明白为什么这么写。

Regtest测试新deployments

if (gArgs.IsArgSet("-vbparams")) {
// Allow overriding version bits parameters for testing
if (!chainparams.MineBlocksOnDemand()) {
return InitError("Version bits parameters may only be overridden on regtest.");
}
for (const std::string& strDeployment : gArgs.GetArgs("-vbparams")) {
std::vector<std::string> vDeploymentParams;
boost::split(vDeploymentParams, strDeployment, boost::is_any_of(":"));
if (vDeploymentParams.size() != 3) {
return InitError("Version bits parameters malformed, expecting deployment:start:end");
}
int64_t nStartTime, nTimeout;
if (!ParseInt64(vDeploymentParams[1], &nStartTime)) {
return InitError(strprintf("Invalid nStartTime (%s)", vDeploymentParams[1]));
}
if (!ParseInt64(vDeploymentParams[2], &nTimeout)) {
return InitError(strprintf("Invalid nTimeout (%s)", vDeploymentParams[2]));
}
bool found = false;
for (int j=0; j<(int)Consensus::MAX_VERSION_BITS_DEPLOYMENTS; ++j)
{
if (vDeploymentParams[0].compare(VersionBitsDeploymentInfo[j].name) == 0) {
UpdateVersionBitsParameters(Consensus::DeploymentPos(j), nStartTime, nTimeout);
found = true;
LogPrintf("Setting version bits activation parameters for %s to start=%ld, timeout=%ld\n", vDeploymentParams[0], nStartTime, nTimeout);
break;
}
}
if (!found) {
return InitError(strprintf("Invalid deployment (%s)", vDeploymentParams[0]));
}
}
}


-vbparams=deploytment:start:end
:设置新的机制启用时间和终止时间,只用于regtest。

这个参数是用于测试新的功能,所以只用于regtest,首先检测
chainparams
中的
fMineBlocksOnDemand
参数是否为
true
,这个参数的含义是让miner在挖到新的block后停止挖矿,直到接到新的命令,而
fMineBlocksOnDemand
参数在
main
testnet
中都为
false
,只有在
regtest
中才为
true
chainparams.MineBlocksOnDemand()
函数就是直接返回
fMineBlocksOnDemand
变量的值。在命令行中可以指定多个
-vbparams
从而同时启用多个新的机制,代码中接下来的for循环就是枚举每一个机制进行处理,输入的形式是
deployment:start:end
,然后分别解析三个参数的值,其中第一个是
string
类型,后面两个是
int64_t
类型,解析完之后在系统的deployments表中查找对应名字的机制,系统的deployments表在
src/versionbits.cpp
中,如下,

const struct VBDeploymentInfo VersionBitsDeploymentInfo[Consensus::MAX_VERSION_BITS_DEPLOYMENTS] = {
{
/*.name =*/ "testdummy",
/*.gbt_force =*/ true,
},
{
/*.name =*/ "csv",
/*.gbt_force =*/ true,
},
{
/*.name =*/ "segwit",
/*.gbt_force =*/ true,
}
};


可见目前只包含三个机制,每个机制又包含以下几个参数,该文件位于
src/consensus/params.h
中,

/**
* Struct for each individual consensus rule change using BIP9.
*/
struct BIP9Deployment {
/** Bit position to select the particular bit in nVersion. */
int bit;
/** Start MedianTime for version bits miner confirmation. Can be a date in the past */
int64_t nStartTime;
/** Timeout/expiry MedianTime for the deployment attempt. */
int64_t nTimeout;
};


找到对应的名字之后就通过
UpdateVersionBitsParameters()
更新其中的
nstartTime
nTimeout
变量值。

到此,整个
AppInitParameterInteraction()
函数就分析完了,虽然很长,但基本上也都是一些参数的设置,所以还是很容易看懂,其中主要的还是需要查阅大量的资料,有些参数网上很少有相关的介绍,需要自己去在整个代码中搜索相关信息,然后理解它的含义。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: