이전 포스팅에서 쪼개서 설명하기로 했던 AppInitParameterInteraction() 부터 설명
bool AppInitParameterInteraction()
{
// 이전 포스팅에서 "-regtest", "-testnet" 의 설정 여부에 따른 globalChainParams 에서 현재 Chain 에 대한
// 정보를 획득! (chainparams.cpp 참고 - 이 곳에 각 Chain 의 정보들이 기술되어 있으니 한번씩 읽어 볼 것.)
const CChainParams& chainparams = Params();
// ********************************************************* Step 2: parameter interactions
// also see: InitParameterInteraction()
// blockdir path 가 캐시되어 있다면 그것을 사용.
// 그렇지 않다면, "-blocksdir" 설정이 존재하는지 체크.
// 존재한다면, 해당 설정의 값을 절대 경로로 만들어서 디렉토리인지 체크.
// 디렉토리가 아니라면 fs::path("") 리턴 -> 설정이 잘못되었음.
// 존재하지 않는다면, default data directory path 를 구함.
// 그 하위에 "blocks" 라는 디렉토리 경로를 만들고 디렉토리 생성. 이 fs::path 를 리턴.
if (!fs::is_directory(GetBlocksDir(false))) {
return InitError(strprintf(_("Specified blocks directory \"%s\" does not exist.\n"), gArgs.GetArg("-blocksdir", "").c_str()));
}
// if using block pruning, then disallow txindex
// "-prune" 설정이 존재하고 값이 1 라면, -txindex=1 로 설정될 수 없다.
if (gArgs.GetArg("-prune", 0)) {
if (gArgs.GetBoolArg("-txindex", DEFAULT_TXINDEX))
return InitError(_("Prune mode is incompatible with -txindex."));
}
// -bind and -whitebind can't be set when not listening
// "-bind" 와 "-whitebind" 설정 값이 존재하는데 "-listen=0" 라면 에러.
size_t nUserBind = gArgs.GetArgs("-bind").size() + gArgs.GetArgs("-whitebind").size();
if (nUserBind != 0 && !gArgs.GetBoolArg("-listen", DEFAULT_LISTEN)) {
return InitError("Cannot set -bind or -whitebind together with -listen=0");
}
// Make sure enough file descriptors are available
// 최소 1. nUserBind 가 > 1 이라면 nUserBind.
int nBind = std::max(nUserBind, size_t(1));
// "-maxconnections" 설정 값이 존재한다면 그 값으로, 그렇지 않다면 DEFAULT_MAX_PEER_CONNECTIONS = 125
nUserMaxConnections = gArgs.GetArg("-maxconnections", DEFAULT_MAX_PEER_CONNECTIONS);
// 최소 0. nUserMaxConnections > 0 이라면, nUserMaxConnections
nMaxConnections = std::max(nUserMaxConnections, 0);
// Trim requested connection counts, to fit into system limitations
// FD_SETSIZE(1024) - nBind - MIN_CORE_FILEDESCRIPTORS(150) - MAX_ADDNODE_CONNECTIONS(8)
nMaxConnections = std::max(std::min(nMaxConnections, FD_SETSIZE - nBind - MIN_CORE_FILEDESCRIPTORS - MAX_ADDNODE_CONNECTIONS), 0);
// file descriptor 수를 늘리려고 시도해보고 현재의 limit 을 리턴
nFD = RaiseFileDescriptorLimit(nMaxConnections + MIN_CORE_FILEDESCRIPTORS + MAX_ADDNODE_CONNECTIONS);
if (nFD < MIN_CORE_FILEDESCRIPTORS)
return InitError(_("Not enough file descriptors available."));
nMaxConnections = std::min(nFD - MIN_CORE_FILEDESCRIPTORS - MAX_ADDNODE_CONNECTIONS, nMaxConnections);
// 사용자가 설정한 max connection 수보다 시스템에서 가용한 max connection 수가 작다면...
if (nMaxConnections < nUserMaxConnections)
InitWarning(strprintf(_("Reducing -maxconnections from %d to %d, because of system limitations."), nUserMaxConnections, nMaxConnections));
// ********************************************************* Step 3: parameter-to-internal-flags
// "-debug" 설정이 존재한다면
if (gArgs.IsArgSet("-debug")) {
// Special-case: if -debug=0/-nodebug is set, turn off debugging messages
// -debug 설정에 지정된 모든 카테고리 값을 획득.
const std::vector<std::string> categories = gArgs.GetArgs("-debug");
// 해당 카테고리 값들에 "0" 또는 "none" 이 없다면...
if (std::none_of(categories.begin(), categories.end(),
[](std::string cat){return cat == "0" || cat == "none";})) {
// 명시된 모든 카테고리의 로깅을 enable! (logging.cpp 참고 - LogCategories[] 에 모든 로깅 카테고리 나열되어 있음)
for (const auto& cat : categories) {
if (!g_logger->EnableCategory(cat)) {
InitWarning(strprintf(_("Unsupported logging category %s=%s."), "-debug", cat));
}
}
}
}
// Now remove the logging categories which were explicitly excluded
// "-debugexclude" 설정 값들이 존재한다면, 각각에 대해 g_logger->DisableCategory() 수행.
for (const std::string& cat : gArgs.GetArgs("-debugexclude")) {
if (!g_logger->DisableCategory(cat)) {
InitWarning(strprintf(_("Unsupported logging category %s=%s."), "-debugexclude", cat));
}
}
// Check for -debugnet
// "-debugnet" 설정이 존재하거나, 설정 값이 "1" 이라면...
if (gArgs.GetBoolArg("-debugnet", false))
InitWarning(_("Unsupported argument -debugnet ignored, use -debug=net."));
// Check for -socks - as this is a privacy risk to continue, exit here
// "-socks" 설정이 존재한다면...
if (gArgs.IsArgSet("-socks"))
return InitError(_("Unsupported argument -socks found. Setting SOCKS version isn't possible anymore, only SOCKS5 proxies are supported."));
// Check for -tor - as this is a privacy risk to continue, exit here
// "-tor" 설정이 존재한다면...
if (gArgs.GetBoolArg("-tor", false))
return InitError(_("Unsupported argument -tor found, use -onion."));
// "-benchmark" 설정이 존재하거나, 설정 값이 "1" 이라면...
if (gArgs.GetBoolArg("-benchmark", false))
InitWarning(_("Unsupported argument -benchmark ignored, use -debug=bench."));
// "-whitelistalwaysrelay" 설정이 존재하거나, 설정 값이 "1" 이라면...
if (gArgs.GetBoolArg("-whitelistalwaysrelay", false))
InitWarning(_("Unsupported argument -whitelistalwaysrelay ignored, use -whitelistrelay and/or -whitelistforcerelay."));
// "-blockminsize" 설정이 존재한다면...
if (gArgs.IsArgSet("-blockminsize"))
InitWarning("Unsupported argument -blockminsize ignored.");
// Checkmempool and checkblockindex default to true in regtest mode
// "-checkmempool" 설정 값 또는 chainparams.DefaultConsistencyChecks() - 메인넷, 테스트넷은 false, Regression Test Net 만 true
// 에 따라서 ratio 설정 후 mempool 의 sanity check 비율 설정.
int ratio = std::min<int>(std::max<int>(gArgs.GetArg("-checkmempool", chainparams.DefaultConsistencyChecks() ? 1 : 0), 0), 1000000);
if (ratio != 0) {
mempool.setSanityCheck(1.0 / ratio);
}
// "-checkblockindex" 설정 값 또는 chainparams.DefaultConsistencyChecks() 값 세팅.
fCheckBlockIndex = gArgs.GetBoolArg("-checkblockindex", chainparams.DefaultConsistencyChecks());
// "-checkpoints" 설정 값 또는 DEFAULT_CHECKPOINTS_ENABLED = true 값 세팅.
fCheckpointsEnabled = gArgs.GetBoolArg("-checkpoints", DEFAULT_CHECKPOINTS_ENABLED);
// chainparams.GetConsensus().defaultAssumeValid 에 세팅된 문자열을 뒤집어서 저장했다가 다시 뒤집어서 리턴!
// defaultAssumeValid 는 메인넷의 경우 506067 블록의 블록해쉬로 현 시점에 valid 하다고 생각되는 블록의 해쉬를 의미하나 봄.
// uint256.cpp 참고
hashAssumeValid = uint256S(gArgs.GetArg("-assumevalid", chainparams.GetConsensus().defaultAssumeValid.GetHex()));
if (!hashAssumeValid.IsNull())
LogPrintf("Assuming ancestors of block %s have valid signatures.\n", hashAssumeValid.GetHex());
else
LogPrintf("Validating signatures for all blocks.\n");
// "-minimumchainwork" 설정이 존재한다면...
if (gArgs.IsArgSet("-minimumchainwork")) {
const std::string minChainWorkStr = gArgs.GetArg("-minimumchainwork", "");
// 값이 hex 값으로 설정되어 있지 않다면...
if (!IsHexNumber(minChainWorkStr)) {
return InitError(strprintf("Invalid non-hex (%s) minimum chain work value specified", minChainWorkStr));
}
// uint256 내에 저장된 문자열을 4byte 씩 읽어서 byte swap (32bit 단위로) 을 수행 후 저장.
nMinimumChainWork = UintToArith256(uint256S(minChainWorkStr));
} else {
nMinimumChainWork = UintToArith256(chainparams.GetConsensus().nMinimumChainWork);
}
// 아마 해쉬 값의 문자열 표시와 숫자형 비교를 위해서 이렇게 복잡하게(?) 조작하는 듯.
// 한 해쉬에 대해서 uint256 은 문자열 타입, arith_uint256 은 숫자형 타입
// arith_uint256 에 compact format 으로의 변환 함수도 존재.
LogPrintf("Setting nMinimumChainWork=%s\n", nMinimumChainWork.GetHex());
// 설정된 nMinimumChainWork 이 chainparams.GetConsensus().nMinimumChainWork 보다 작다면...
if (nMinimumChainWork < UintToArith256(chainparams.GetConsensus().nMinimumChainWork)) {
LogPrintf("Warning: nMinimumChainWork set below default value of %s\n", chainparams.GetConsensus().nMinimumChainWork.GetHex());
}
// mempool limits
// DEFAULT_MAX_MEMPOOL_SIZE = 300, 사용자가 별도 설정하지 않으면 약 300 MB 정도
int64_t nMempoolSizeMax = gArgs.GetArg("-maxmempool", DEFAULT_MAX_MEMPOOL_SIZE) * 1000000;
// DEFAULT_DESCENDANT_SIZE_LIMIT = 101 - maximum killobytes of in-mempool descendants
// 사용자가 별도로 설정하지 않으면, 약 4MB 정도
int64_t nMempoolSizeMin = gArgs.GetArg("-limitdescendantsize", DEFAULT_DESCENDANT_SIZE_LIMIT) * 1000 * 40;
if (nMempoolSizeMax < 0 || nMempoolSizeMax < nMempoolSizeMin)
return InitError(strprintf(_("-maxmempool must be at least %d MB"), std::ceil(nMempoolSizeMin / 1000000.0)));
// incremental relay fee sets the minimum feerate increase necessary for BIP 125 replacement in the mempool
// and the amount the mempool min fee increases above the feerate of txs evicted due to mempool limiting.
// BIP 125 의 내용은 현재의 노드들은 자신의 mempool 에 존재하는 트랜잭션과 동일한 input 을 사용하는 또 다른
// 트랜잭션으로의 대체(replacement)를 허용하지 않고 있는 상황이라서, unexpected confirmation delays 와 또
// 다른 유용한 대체들을 처리할 방법에 어려움을 겪고 있음. 이에 대한 improvement proposal.
// https://github.com/bitcoin/bips/blob/master/bip-0125.mediawiki
if (gArgs.IsArgSet("-incrementalrelayfee"))
{
CAmount n = 0;
if (!ParseMoney(gArgs.GetArg("-incrementalrelayfee", ""), n))
return InitError(AmountErrMsg("incrementalrelayfee", gArgs.GetArg("-incrementalrelayfee", "")));
// 여기서 n 은 satoshi 단위 per killobyte. policy/feerate.h 참고.
incrementalRelayFee = CFeeRate(n);
}
// -par=0 means autodetect, but nScriptCheckThreads==0 means no concurrency
// DEFAULT_SCRIPTCHECK_THREADS = 0
// nScriptCheckThreads 수는 최대 MAX_SCRIPTCHECK_THREADS(16) 개
// 설정이 존재하지 않거나, '-par=0' 으로 설정되었다면, core 1 개인 경우, nScriptCheckThreads = 0
nScriptCheckThreads = gArgs.GetArg("-par", DEFAULT_SCRIPTCHECK_THREADS);
if (nScriptCheckThreads <= 0)
nScriptCheckThreads += GetNumCores();
if (nScriptCheckThreads <= 1)
nScriptCheckThreads = 0;
else if (nScriptCheckThreads > MAX_SCRIPTCHECK_THREADS)
nScriptCheckThreads = MAX_SCRIPTCHECK_THREADS;
// block pruning; get the amount of disk space (in MiB) to allot for block & undo files
// 해당 값이 1 일 경우 manual 로 사용자가 pruning 을 수행하겠다는 의미. 주석에서 설명한 것 처럼 인자는 amount of disk space (in MiB)
int64_t nPruneArg = gArgs.GetArg("-prune", 0);
if (nPruneArg < 0) {
return InitError(_("Prune cannot be configured with a negative value."));
}
nPruneTarget = (uint64_t) nPruneArg * 1024 * 1024;
if (nPruneArg == 1) { // manual pruning: -prune=1
LogPrintf("Block pruning enabled. Use RPC call pruneblockchain(height) to manually prune block and undo files.\n");
nPruneTarget = std::numeric_limits<uint64_t>::max();
fPruneMode = true;
} else if (nPruneTarget) {
// nPruneTarget 은 최소 550MB 이상은 되어야 함. MIN_DISK_SPACE_FOR_BLOCK_FILES = 550 * 1024 * 1024
// 관련 이유는 다음의 주석 참고.
// Require that user allocate at least 550MB for block & undo files (blk???.dat and rev???.dat)
// At 1MB per block, 288 blocks = 288MB. - 여기서 288 은 prune 되지 않고 최소 이 정도는 유지해야 할 블록 수.
// Add 15% for Undo data = 331MB
// Add 20% for Orphan block rate = 397MB
// We want the low water mark after pruning to be at least 397 MB and since we prune in
// full block file chunks, we need the high water mark which triggers the prune to be
// one 128MB block file + added 15% undo data = 147MB greater for a total of 545MB
// Setting the target to > than 550MB will make it likely we can respect the target.
if (nPruneTarget < MIN_DISK_SPACE_FOR_BLOCK_FILES) {
return InitError(strprintf(_("Prune configured below the minimum of %d MiB. Please use a higher number."), MIN_DISK_SPACE_FOR_BLOCK_FILES / 1024 / 1024));
}
LogPrintf("Prune configured to target %uMiB on disk for block and undo files.\n", nPruneTarget / 1024 / 1024);
fPruneMode = true;
}
// 설정하지 않았다면, DEFAULT_CONNECT_TIMEOUT = 5000 (millisecond 단위)
nConnectTimeout = gArgs.GetArg("-timeout", DEFAULT_CONNECT_TIMEOUT);
if (nConnectTimeout <= 0)
nConnectTimeout = DEFAULT_CONNECT_TIMEOUT;
// "-minrelaytxfee" 가 설정되었다면...
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))
return InitError(AmountErrMsg("dustrelayfee", gArgs.GetArg("-dustrelayfee", "")));
dustRelayFee = CFeeRate(n);
}
// Stanadard transaction 만 수용할지의 여부 결정.
fRequireStandard = !gArgs.GetBoolArg("-acceptnonstdtxn", !chainparams.RequireStandard());
if (chainparams.RequireStandard() && !fRequireStandard)
return InitError(strprintf("acceptnonstdtxn is not currently supported for %s chain", chainparams.NetworkIDString()));
// nBytesPerSigOp 는 policy/policy.cpp 참고, default 값은 DEFAULT_BYTES_PER_SIGOP = 20
nBytesPerSigOp = gArgs.GetArg("-bytespersigop", nBytesPerSigOp);
// g_wallet_init_interface 는 configure 시 --enable-wallet 을 한 경우, WalletInit(). wallet/init.cpp 참고
// 그렇지 않은 경우, DummyWalletInit(). init.cpp 참고. 이 경우는 단순 return true.
// 여기서는 configure --enable-wallet 한 경우를 가정하고 소스 분석.
// "-disablewallet" 설정을 한 경우, "-wallet" 설정들은 무시.
// "-wallet" 설정들이 여러 개일 경우 is_multiwallet = true
// "-blocksonly=1" 인데 "-walletbroadcast" 가 설정되어 있지 않다면, "-walletbroadcast=0" 으로 변경.
// "-salvagewallet=1" 이고 is_multiwallet = true 라면 에러.
// 그렇지 않다면, "-rescan" 설정이 되어 있지 않다면, true 로 세팅.
// ... 관련해서 다른 내용들도 좀 있음. 위와 유사한 내용이므로 여기서 skip.
// 소스는 wallet/init.cpp 를 참고
if (!g_wallet_init_interface.ParameterInteraction()) return false;
// Relay non-P2SH multisig (default: true)
fIsBareMultisigStd = gArgs.GetBoolArg("-permitbaremultisig", DEFAULT_PERMIT_BAREMULTISIG);
// Relay and mine data carrier transactions (default: true)
fAcceptDatacarrier = gArgs.GetBoolArg("-datacarrier", DEFAULT_ACCEPT_DATACARRIER);
// 이 노드가 표준이라고 생각하는 TX_NULL_DATA script들의 최대 사이즈
// 80 bytes of data, +1 for OP_RETURN, +2 for the pushdata opcodes
// Maximum size of data in data carrier transactions we relay and mine (default: 83)
nMaxDatacarrierBytes = gArgs.GetArg("-datacarriersize", nMaxDatacarrierBytes);
// 주석에 설명된 것처럼 regression testing 시에만 사용됨.
// Option to startup with mocktime set (used for regression testing):
SetMockTime(gArgs.GetArg("-mocktime", 0)); // SetMockTime(0) is a no-op
// Support filtering of blocks and transaction with bloom filters (default: true)
// peer 의 bloomfilter 사용을 허용하는 노드임을 표시하기 위해서 서비스 플래그에도 세팅.
if (gArgs.GetBoolArg("-peerbloomfilters", DEFAULT_PEERBLOOMFILTERS))
nLocalServices = ServiceFlags(nLocalServices | NODE_BLOOM);
// Sets the serialization of raw transaction or block hex returned in non-verbose mode, non-segwit(0)
// or segwit(1) (default: 1)
// 현재는 0 또는 1만 존재하는 듯. 0 보다 작거나 1 보다 큰 경우는 에러!
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.");
// DEFAULT_MAX_TIP_AGE = 24 * 60 * 60 (in seconds)
// Tip 이 이 시간보다 더 오래된 경우, 해당 노드는 initial block download 중이라고 여겨짐.
nMaxTipAge = gArgs.GetArg("-maxtipage", DEFAULT_MAX_TIP_AGE);
// Enable transaction replacement in the memory pool (default: true)
// 위에서 간략히 설명한 BIP 125 관련 설정.
fEnableReplacement = gArgs.GetBoolArg("-mempoolreplacement", DEFAULT_ENABLE_REPLACEMENT);
// "-mempoolreplacement=0" 또는 "-mempoolreplacement=xxx" 또는 -nomempoolreplacement" 설정이 존재한다면...
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(","));
// mode 로 기술된 문자열 중에 "fee" 가 있으면 fEnableReplacement = true
fEnableReplacement = (std::find(vstrReplacementModes.begin(), vstrReplacementModes.end(), "fee") != vstrReplacementModes.end());
}
// version bit parameters 테스트를 위한 부분.
// https://bitcoincore.org/en/2016/06/08/version-bits-miners-faq/ 참고
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]));
}
}
}
return true;
}
일단 지난 포스팅에서 분석하기로 했던 AppInitParameterInteraction() 부분은 완료!
다음 포스팅은 AppInitMain() 부터 시작. 이것 자체로 분량이 많아서 쪼개서 계속 진행.