bool
HistoryManager::initializeHistoryArchive(Application& app, std::string arch)
{
    auto const& cfg = app.getConfig();
    auto i = cfg.HISTORY.find(arch);
    if (i == cfg.HISTORY.end())
    {
        CLOG(FATAL, "History") << "Can't initialize unknown history archive '"
                               << arch << "'";
        return false;
    }

    auto& wm = app.getWorkManager();

    // First check that there's no existing HAS in the archive
    HistoryArchiveState existing;
    CLOG(INFO, "History") << "Probing history archive '" << arch << "' for existing state";
    auto getHas = wm.addWork<GetHistoryArchiveStateWork>(existing, 0,
                                                         std::chrono::seconds(0),
                                                         i->second, 0);
    wm.advanceChildren();
    while (!wm.allChildrenDone())
    {
        app.getClock().crank(false);
    }
    if (getHas->getState() == Work::WORK_SUCCESS)
    {
        CLOG(ERROR, "History") << "History archive '" << arch << "' already initialized!";
        return false;
    }
    CLOG(INFO, "History") << "History archive '" << arch << "' appears uninitialized";

    HistoryArchiveState has;
    CLOG(INFO, "History") << "Initializing history archive '" << arch << "'";
    has.resolveAllFutures();

    auto putHas = wm.addWork<PutHistoryArchiveStateWork>(has, i->second);
    wm.advanceChildren();
    while (!wm.allChildrenDone())
    {
        app.getClock().crank(false);
    }
    if (putHas->getState() == Work::WORK_SUCCESS)
    {
        CLOG(INFO, "History") << "Initialized history archive '" << arch << "'";
        return true;
    }
    else
    {
        CLOG(FATAL, "History") << "Failed to initialize history archive '"
                               << arch << "'";
        return false;
    }
}
size_t
HistoryManagerImpl::publishQueuedHistory()
{
    std::string state;

    auto prep = mApp.getDatabase().getPreparedStatement(
        "SELECT state FROM publishqueue"
        " ORDER BY ledger ASC LIMIT 1;");
    auto& st = prep.statement();
    soci::indicator stateIndicator;
    st.exchange(soci::into(state, stateIndicator));
    st.define_and_bind();
    st.execute(true);
    if (st.got_data() && stateIndicator == soci::indicator::i_ok)
    {
        HistoryArchiveState has;
        has.fromString(state);
        takeSnapshotAndPublish(has);
        return 1;
    }
    return 0;
}
std::vector<std::string>
BucketManagerImpl::checkForMissingBucketsFiles(HistoryArchiveState const& has)
{
    std::vector<std::string> buckets = has.allBuckets();
    std::vector<std::string> result;
    std::copy_if(buckets.begin(), buckets.end(), std::back_inserter(result),
                 [&](std::string b) {
                     auto filename = bucketFilename(b);
                     return !isZero(hexToBin256(b)) && !fs::exists(filename);
                 });

    return result;
}
void
LedgerManagerImpl::loadLastKnownLedger(
    function<void(asio::error_code const& ec)> handler)
{
    auto ledgerTime = mLedgerClose.TimeScope();

    string lastLedger =
        mApp.getPersistentState().getState(PersistentState::kLastClosedLedger);

    if (lastLedger.empty())
    {
        throw std::runtime_error("No ledger in the DB");
    }
    else
    {
        LOG(INFO) << "Loading last known ledger";
        Hash lastLedgerHash = hexToBin256(lastLedger);

        mCurrentLedger =
            LedgerHeaderFrame::loadByHash(lastLedgerHash, getDatabase());
        if (!mCurrentLedger)
        {
            throw std::runtime_error("Could not load ledger from database");
        }

        if (handler)
        {
            string hasString = mApp.getPersistentState().getState(
                PersistentState::kHistoryArchiveState);
            HistoryArchiveState has;
            has.fromString(hasString);

            auto continuation = [this, handler, has](asio::error_code const& ec)
            {
                if (ec)
                {
                    handler(ec);
                }
                else
                {
                    mApp.getBucketManager().assumeState(has);

                    CLOG(INFO, "Ledger") << "Loaded last known ledger: "
                                         << ledgerAbbrev(mCurrentLedger);

                    advanceLedgerPointers();
                    handler(ec);
                }
            };

            auto missing =
                mApp.getBucketManager().checkForMissingBucketsFiles(has);
            if (!missing.empty())
            {
                CLOG(WARNING, "Ledger")
                    << "Some buckets are missing in '"
                    << mApp.getBucketManager().getBucketDir() << "'.";
                CLOG(WARNING, "Ledger")
                    << "Attempting to recover from the history store.";
                mApp.getHistoryManager().downloadMissingBuckets(has,
                                                                continuation);
            }
            else
            {
                continuation(asio::error_code());
            }
        }
        else
        {
            advanceLedgerPointers();
        }
    }
}