Repository: bitcoin/bitcoin
File: src/bitcoin-chainstate.cpp (C++)

Code before:
        void fatalError(const bilingual_str& message) override
        {
            std::cerr << "Error: " << message.original << std::endl;
        }
    };
    auto notifications = std::make_unique<KernelNotifications>();

    node::CacheSizes cache_sizes;
    cache_sizes.block_tree_db = 2 << 20;
    cache_sizes.coins_db = 2 << 22;
    cache_sizes.coins = (450 << 20) - (2 << 20) - (2 << 22);

    // SETUP: Chainstate
    auto chainparams = CChainParams::Main();
    const ChainstateManager::Options chainman_opts{
        .chainparams = *chainparams,
        .datadir = abs_datadir,
        .notifications = *notifications,
        .signals = &validation_signals,
    };
    const node::BlockManager::Options blockman_opts{
        .chainparams = chainman_opts.chainparams,
        .blocks_dir = abs_datadir / "blocks",
        .notifications = chainman_opts.notifications,
        .block_tree_db_dir = abs_datadir / "blocks" / "index",
        .block_tree_db_cache_size = cache_sizes.block_tree_db,
    };
    util::SignalInterrupt interrupt;
    ChainstateManager chainman{interrupt, chainman_opts, blockman_opts};

    node::ChainstateLoadOptions options;
    auto [status, error] = node::LoadChainstate(chainman, cache_sizes, options);
    if (status != node::ChainstateLoadStatus::SUCCESS) {
        std::cerr << "Failed to load Chain state from your datadir." << std::endl;
        goto epilogue;
    } else {
        std::tie(status, error) = node::VerifyLoadedChainstate(chainman, options);
        if (status != node::ChainstateLoadStatus::SUCCESS) {
            std::cerr << "Failed to verify loaded Chain state from your datadir." << std::endl;
            goto epilogue;
        }
    }

    for (Chainstate* chainstate : WITH_LOCK(::cs_main, return chainman.GetAll())) {
        BlockValidationState state;
        if (!chainstate->ActivateBestChain(state, nullptr)) {
            std::cerr << "Failed to connect best block (" << state.ToString() << ")" << std::endl;
            goto epilogue;
        }
    }


Review comment (performance):
In commit "kernel: Move block tree db open to block manager" (182945e946ac6a92daad012d6579e5441b9641cc)

It seems far from ideal for bitcoin-chainstate and other kernel library users (not to mention our own test code) to need to hardcode the "blocks/index" path and to manually copy the cache size from one struct to another. I think a better API would allow these option fields to be unset and internally use a `Flatten` method similar to the ones in `validation.cpp` and `txmempool.cpp` to set appropriate default values when the fields are unset.

Code after:
        void fatalError(const bilingual_str& message) override
        {
            std::cerr << "Error: " << message.original << std::endl;
        }
    };
    auto notifications = std::make_unique<KernelNotifications>();

    kernel::CacheSizes cache_sizes{DEFAULT_KERNEL_CACHE};

    // SETUP: Chainstate
    auto chainparams = CChainParams::Main();
    const ChainstateManager::Options chainman_opts{
        .chainparams = *chainparams,
        .datadir = abs_datadir,
        .notifications = *notifications,
        .signals = &validation_signals,
    };
    const node::BlockManager::Options blockman_opts{
        .chainparams = chainman_opts.chainparams,
        .blocks_dir = abs_datadir / "blocks",
        .notifications = chainman_opts.notifications,
        .block_tree_db_params = DBParams{
            .path = abs_datadir / "blocks" / "index",
            .cache_bytes = cache_sizes.block_tree_db,
        },
    };
    util::SignalInterrupt interrupt;
    ChainstateManager chainman{interrupt, chainman_opts, blockman_opts};

    node::ChainstateLoadOptions options;
    auto [status, error] = node::LoadChainstate(chainman, cache_sizes, options);
    if (status != node::ChainstateLoadStatus::SUCCESS) {
        std::cerr << "Failed to load Chain state from your datadir." << std::endl;
        goto epilogue;
    } else {
        std::tie(status, error) = node::VerifyLoadedChainstate(chainman, options);
        if (status != node::ChainstateLoadStatus::SUCCESS) {
            std::cerr << "Failed to verify loaded Chain state from your datadir." << std::endl;
            goto epilogue;
        }
    }

    for (Chainstate* chainstate : WITH_LOCK(::cs_main, return chainman.GetAll())) {
        BlockValidationState state;
        if (!chainstate->ActivateBestChain(state, nullptr)) {
            std::cerr << "Failed to connect best block (" << state.ToString() << ")" << std::endl;
            goto epilogue;
        }
    }

    // Main program logic starts here