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() 부터 시작. 이것 자체로 분량이 많아서 쪼개서 계속 진행.