Bitcoind Source 분석(3)

AppInitMain() 부터 시작. - init.cpp 참고

bool AppInitMain()
{
    // 이전 포스팅에서 "-regtest", "-testnet" 의 설정 여부에 따른 globalChainParams 에서 현재 Chain 에 대한
    // 정보를 획득! (chainparams.cpp 참고 - 이 곳에 각 Chain 의 정보들이 기술되어 있으니 한번씩 읽어 볼 것.)
    const CChainParams& chainparams = Params();

    // ********************************************************* Step 4a: application initialization
#ifndef WIN32
    // GetPidFile(): "-pid" 설정값이 존재한다면 그 값을 사용하여 pid file 의 절대 경로를 생성.
    //                그렇지 않다면, default 로 bitcoind.pid 를 이용하여 pid file 의 절대 경로를 생성.
    //                = data directory + "/" + net + "/" + filename
    // getpid(): 현재 프로세스의 id 를 구해옴.
    // 
    // 파일을 생성 후 해당 파일에 현재 process id 를 저장.
    CreatePidFile(GetPidFile(), getpid());
#endif

    // 전역 logger 설정에 로깅을 파일로도 하라는 설정이 turn on 되어 있다면..
    // https://steemit.com/understanding/@jayson.jeong/bitcoind-source-1 참고.
    if (g_logger->m_print_to_file) {
        // "-shrinkdebugfile=1" 설정되어 있거나 설정되어 있지 않더라도 logging category 가 NONE  이라면, 
        if (gArgs.GetBoolArg("-shrinkdebugfile", g_logger->DefaultShrinkDebugFile())) {
            // Do this first since it both loads a bunch of debug.log into memory,
            // and because this needs to happen before any other debug.log printing

            // 현재 소스상 파일의 크기가 약 11MB 보다 클 경우, 가장 최근 10MB 정도 로그를 포함한 파일의 크기로 trimming.
            g_logger->ShrinkDebugFile();
        }

        // 해당 파일을 일단 append mode 로 오픈하고, 작업 중 발생했던 로그들을 모아뒀던 버퍼에서 로그들을 꺼내서 
        // 이 파일에 쓴다.
        if (!g_logger->OpenDebugLog()) {
            return InitError(strprintf("Could not open debug log file %s",
                                       g_logger->m_file_path.string()));
        }
    }

    // 전역 logger 의 설정 중 m_log_timestamps 이 turn on 되어 있지 않다면..
    if (!g_logger->m_log_timestamps)
        LogPrintf("Startup time: %s\n", FormatISO8601DateTime(GetTime()));

    // 현재 설정에서 정보성 내용들을 출력!
    LogPrintf("Default data directory %s\n", GetDefaultDataDir().string());
    LogPrintf("Using data directory %s\n", GetDataDir().string());
    LogPrintf("Using config file %s\n", GetConfigFile(gArgs.GetArg("-conf", BITCOIN_CONF_FILENAME)).string());
    LogPrintf("Using at most %i automatic connections (%i file descriptors available)\n", nMaxConnections, nFD);

    // Warn about relative -datadir path.
    // "-datadir" 설정 값이 절대 경로가 아니라면... 아래의 경고 메세지 출력...
    if (gArgs.IsArgSet("-datadir") && !fs::path(gArgs.GetArg("-datadir", "")).is_absolute()) {
        LogPrintf("Warning: relative datadir option '%s' specified, which will be interpreted relative to the " /* Continued */
                  "current working directory '%s'. This is fragile, because if bitcoin is started in the future "
                  "from a different location, it will be unable to locate the current data files. There could "
                  "also be data loss if bitcoin is started while in a temporary directory.\n",
            gArgs.GetArg("-datadir", ""), fs::current_path().string());
    }

    // SignatureCache(모든 트랜잭션에 ECDSA 를 두번 체크하는 것을 막기 위한 캐시) 초기화. - script/sigcache.cpp 참고
    // - mempool 에 저장될 때 한번, block chain 에 저장될 때 또 한번. 총 2번.
    // SignatureCache 는 내부적으로 CuckooCache 를 사용하는데 관련 소스는 cuckoocache.cpp, cuckoocache.h 참고
    InitSignatureCache();

    // 이 부분도 위와 동일한 이유로 사용되는 캐쉬
    // 내부적으로 CuckooCache 를 사용하는 것도 동일.
    InitScriptExecutionCache();

    LogPrintf("Using %u threads for script verification\n", nScriptCheckThreads);
    if (nScriptCheckThreads) {
        for (int i=0; i<nScriptCheckThreads-1; i++)
            // boost::thread_group 
            // *worker* 쓰레드 하나 생성. 이름은 "bitcoin-scriptch"
            // 이 *worker* 쓰레드는 CScriptCheck (하나의 script verification 을 나타내는 closure) 작업들이 쌓인 
            // 큐(CCheckQueue)에서 작업들이 들어오길 기다리고 있다가 작업이 들어오면 큐에서 꺼내서 CScriptCheck 의
            // operator()() 메소드(validation.cpp 참고) 를 수행. - 결국 해당 트랜잭션의 scriptSig 와 scriptPubKey 검증.
            // 검증 로직은 script/interpreter.cpp 의 VerifyScript() 참고.
            // - CScriptCheck 는 validation.cpp 와 validation.h 참고
            // - CCheckQueue 는 checkqueue.cpp 와 checkqueue.h 참고
            threadGroup.create_thread(&ThreadScriptCheck);
    }

    // Start the lightweight task scheduler thread
    // serviceLoop 는 &scheduler 인스턴스를 &CScheduler::serviceQueue 함수에 this pointer로  바인딩한 함수.
    CScheduler::Function serviceLoop = boost::bind(&CScheduler::serviceQueue, &scheduler);

    // TraceThread 라는 함수에 인자로 "scheduler"와, serviceLoop 를 바인딩 시킨 후 쓰레드를 생성해서 수행.
    // "bitcoin-scheduler" 라는 이름으로 쓰레드의 이름을 변경하고 serviceLoop() 를 실행.
    threadGroup.create_thread(boost::bind(&TraceThread<CScheduler::Function>, "scheduler", serviceLoop));

    // serviceLoop() 의 대략적인 내용은 다음과 같다.
    // 작업 큐의 타입은 std::multimap<boost::chrono::system_clock::time_point, Function> taskQueue; 로서
    //     - boost::chrono::system_clock::time_point 순으로 정렬되어 있고 multi value (Function) 을 저장할 수 있다.
    // 역시 작업 큐에 작업이 있을 때까지 또는 첫번째 작업의 실행시점이 될 때까지 기다렸다가 실행할 작업이 생기면
    // 꺼내서 Function 에 해당 하는 녀석을 실행.

    // static CMainSignals g_signals;
    // g_signals 의 내부에는 MainSignalsInstance 에 접근할 수 있는 포인터가 존재
    // MainSignalsInstance 는 UpdatedBlockTip/TransactionAddedToMempool/BlockConnected/BlockDisconnected
    // /TransactionRemovedFromMempool/ChainStateFlushed/Inventory/Broadcast/BlockChecked/NewPoWValidBlock
    // 등의 이벤트가 발생할 때 실행할 callback 함수들을 등록할 수 있다.
    // g_signals 는 이 MainSignalsInstance 에 접근할 수 있는 통로(인터페이스)의 역할을 한다.
    // (소스 코드는 validationinterface.cpp 참고.)

    // 전역 변수 g_signals 에 접근할 수 있는 함수 GetMainSignals()
    // 새로운 MainSignalsInstance 를 생성하면서 관련 callback 함수들을 실행할 schduler 인스턴스도 함께 전달.
    GetMainSignals().RegisterBackgroundSignalScheduler(scheduler);

    // CTxMemPool (txmempool.h 참고) 에서 감지할 수 있는 NotifyEntryRemoved 이벤트에
    // 대한 callback 함수(&CMainSignals::MempoolEntryRemoved) 를 등록한다.
    // (역시 소스 코드는 validationinterface.cpp  참고)
    GetMainSignals().RegisterWithMempoolSignals(mempool);

    /* Register RPC commands regardless of -server setting so they will be
     * available in the GUI RPC console even if external calls are disabled.
     */
    // CRPCTable tableRPC 인스턴스에 모든 core rpc command 와 handler 들을 등록.
    //
    // CRPCTable(소스 코드는 rpc/server.h 와  rpc/server.cpp 참고) 는 내부적으로 
    // std::map<std::string, const CRPCCommand *> mapCommands; 타입의 매핑 테이블을 가지고 있음.
    // CRPCCommand(소스 코드는 rcp/server.h 참고) 는 내부적으로 
    // typedef UniValue(*rpcfn_type)(const JSONRPCRequest& jsonRequest); 타입의 함수 포인터를 저장함으로써
    // 대응되는 커맨드에 대한 실행 함수 매핑을 유지하는 형태.
    // 
    // 차례대로 아래의 함수들이 호출되며 소스 코드 내에서 각각의 함수 상위에 관련 테이블 코드가 존재.
    // 각각의 커맨드에 대해서 어떤 함수들이 호출될 지 쉽게 볼 수 있음.
    // RegisterBlockchainRPCCommands() - 소스 코드는 rpc/blockchain.cpp 참고
    // RegisterNetRPCCommands() - 소스 코드는 rpc/net.cpp 참고
    // RegisterMiscRPCCommands() - 소스 코드는 rpc/misc.cpp 참고
    // RegisterMiningRPCCommands() - 소스 코드는 rpc/mining.cpp 참고
    // RegisterMiningRPCCommands() - 소스 코드는 rpc/rawtransaction.cpp 참고.
    // 각 rpc command 의 세부 동작은 추후 포스팅에서 진행해보도록 하자.
    RegisterAllCoreRPCCommands(tableRPC);

    // "-disablewallet" 설정이 존재한다면 단순 리턴. 
    // 그렇지 않다면, 여기서는 RegisterWalletRPCCommands() 가 호출됨. - 소스 코드는 wallet/rpcwallet.cpp 참고. 
    g_wallet_init_interface.RegisterRPC(tableRPC);

    /* Start the RPC server already.  It will be started in "warmup" mode
     * and not really process calls already (but it will signify connections
     * that the server is there and will be ready later).  Warmup mode will
     * be disabled when initialisation is finished.
     */
    // "-server" 설정이 존재한다면...
    if (gArgs.GetBoolArg("-server", false))
    {
        // 이전 포스팅에 언급되었던 CClientUIInterface uiInterface 의 InitMessage callback 에
        // SetRPCWarmupStatus 함수도 등록한다 (rcp/server.cpp 참고)
        uiInterface.InitMessage.connect(SetRPCWarmupStatus);

        // static struct CRPCSignals g_rpcSignals (rpc/server.cpp 참고) 에
        // OnStarted 이벤트 발생시 호출될 callback &OnRPCStarted(init.cpp 참고) 등록
        //    - uiInterface.NotifyBlockTip 이벤트에 &RPCNotifyBlockChange 등록
        // OnStopped 이벤트 발생시 호출될 callback &OnRPCStopped(init.cpp 참고) 등록
        //    - uiInterface.NotifyBlockTip 이벤트에 등록된 &RPCNotifyBlockChange reset 및  RPCNotifyBlockChange(false, nullptr) 호출.
        // InitHttpServer() 호출
        //    - http 요청에 대한 access control list 초기화
        //    - "-rpcssl" 이 설정되어 있는 경우, error. (Not Supported!)
        //    - 관련하여 libevent 관련 초기화 후, address binding 후 요청을 받아들일 수 있는 준비까지 완료.
        //    - rpc 요청에 대한 work queue 관련 구조체 초기화 (WorkQueue<HttpClosure> - httpserver.cpp 및 httpserver.h 참고)
        // StartRPC() 호출
        //    - g_rpcSignals.Started() 호출. -> OnStarted 이벤트시에 호출될 callback 들 모두 호출.
        // StartHTTPRPC() 호출.
        //    - "-rpcpassword" 가 설정되어 있지 않을 경우, random cookie authentication 을 사용하거나 설정되어 있을 경우 
        //       "-rpcuser" : "-rpcpassword" 형태의 authentication 문자열 생성.
        //    - "/" 에 HTTPReq_JSONRPC handler 등록(httprpc.cpp 참고) - 정확하게 일치해야 호출.
        //    - "/wallet/" 에 역시 HTTPReq_JSONRPC handler 등록 - prefix 만 일치해도 호출.
        // "-rest" 설정이 되어 있다면, StartRest() 호출 - rest.cpp 참고. /rest/xxx 에 대한 핸들러들 등록.
        // StartHttpServer() 호출.
        //    - "bitcoin-http" 라는 event dispatcher thread 생성. 상위에서 생성한 work queue 에 작업 등록.
        //    - "-rpcthreads" 수 만큼 "bitcoin-httpworker" 라는 worker thread 생성. work queue 에 등록된 작업을 꺼내서 처리.
        if (!AppInitServers())
            return InitError(_("Unable to start HTTP server. See debug log for details."));
    }

    int64_t nStart;

    // ********************************************************* Step 5: verify wallet database integrity
    // --enable-wallet 으로 컴파일된 경우, wallet database 의 integrity 를 점검한다.
    // g_wallet_init_interface 는 https://steemit.com/understanding/@jayson.jeong/bitcoind-source-2 에서 
    // 초기화 관련 언급을 하였음. 관련된 소스는 wallet/init.cpp 를 참고
    // 참고로 wallet database 는 leveldb 가 아니라 berkeleydb 를 사용하는 듯. (wallet/db.cpp 참고)
    if (!g_wallet_init_interface.Verify()) return false;

아직 관련 소스가 모두 끝난 상황은 아니지만 너무 많으니 여기까지만... 다음 포스팅에서 계속...

Sort:  

Congratulations @jayson.jeong! You received a personal award!

Happy Birthday! - You are on the Steem blockchain for 1 year!

Click here to view your Board

Do not miss the last post from @steemitboard:

Carnival Challenge - Collect badge and win 5 STEEM
Vote for @Steemitboard as a witness and get one more award and increased upvotes!

Congratulations @jayson.jeong! You received a personal award!

Happy Birthday! - You are on the Steem blockchain for 2 years!

You can view your badges on your Steem Board and compare to others on the Steem Ranking

Vote for @Steemitboard as a witness to get one more award and increased upvotes!