Bitcoind Source 분석(2)

in #understanding6 years ago (edited)

이전 포스팅에서 쪼개서 설명하기로 했던 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() 부터 시작. 이것 자체로 분량이 많아서 쪼개서 계속 진행.

Coin Marketplace

STEEM 0.28
TRX 0.13
JST 0.032
BTC 66101.68
ETH 3023.14
USDT 1.00
SBD 3.71