void close_and_advance(Ledger::pointer& ledger, Ledger::pointer& LCL) { std::shared_ptr<SHAMap> set = ledger->peekTransactionMap(); CanonicalTXSet retriableTransactions(set->getHash()); Ledger::pointer newLCL = std::make_shared<Ledger>(false, *LCL); // Set up to write SHAMap changes to our database, // perform updates, extract changes applyTransactions(set, newLCL, newLCL, retriableTransactions, false); newLCL->updateSkipList(); newLCL->setClosed(); newLCL->peekAccountStateMap()->flushDirty( hotACCOUNT_NODE, newLCL->getLedgerSeq()); newLCL->peekTransactionMap()->flushDirty( hotTRANSACTION_NODE, newLCL->getLedgerSeq()); using namespace std::chrono; auto const epoch_offset = days(10957); // 2000-01-01 std::uint32_t closeTime = time_point_cast<seconds> // now (system_clock::now() - epoch_offset). time_since_epoch().count(); int closeResolution = seconds(LEDGER_TIME_ACCURACY).count(); bool closeTimeCorrect = true; newLCL->setAccepted(closeTime, closeResolution, closeTimeCorrect); LCL = newLCL; ledger = std::make_shared<Ledger>(false, *LCL); }
/** * Instantiate an application and replay a ledger history out * of the dump file `filename`. */ void LedgerDump::loadTransactions (std::string const& filename) { std::ifstream in (filename); require ((bool)in, "opening file"); std::unique_ptr <Application> app (make_Application ()); app->setup (); auto &lm = app->getLedgerMaster (); WriteLog (lsINFO, LedgerDump) << "Loading ledgers from " << filename; auto nTxs = 0; // app->setup() when called with START_UP == Config::FRESH calls // ApplicationImp::startNewLedger(). Unfortunately it starts the new // ledger at the wrong timestamp, so we call it again once we've read // the first ledger we're going to apply. However, it's worth // understanding that startNewLedger() establishes 3 ledgers: // // Ledger 0 is the genesis ledger, it's not a real ledger, just a // number. // // Ledger 1 is the root-account deposit ledger, with a single pile of // currency owned by a single account generated by the seed // "masterpassword". // // Ledger 2 is created and closed immediately on start, not sure why. // // Ledger 3 is a new ledger chained to #2 and left open in // ledgermaster. // // Ledger 3 is where replay should be starting, so (once we call // startNewLedger() again) we pull out ledger #2 and use it as the // parent of the new ledger 3 we're replaying, and throw away the #3 // that was made by startNewLedger(). Ledger::pointer parentLedger; while (in) { if ((gLedgerSeq & 0xfff) == 0) { Job j; app->doSweep (j); } Json::Value j = loadJsonRecord (in); Ledger::pointer deserializedLedger; SHAMap::pointer txSet; std::vector<uint256> txOrder; std::tie (deserializedLedger, txSet, txOrder) = loadLedgerAndTransactionsFromJSON (*app, j); if (!parentLedger) { if (getConfig ().START_LEDGER.empty ()) { require (deserializedLedger->getLedgerSeq () == 3, "Initial ledger isn't seq #3"); // On first iteration, restart the app's view of the ledger // history at the same instant as the parent close time of the // first ledger (ledger #3). app->startNewLedger (deserializedLedger->getParentCloseTimeNC ()); parentLedger = lm.getClosedLedger (); require (parentLedger->getLedgerSeq () == 2, "Initial ledger parent isn't seq #2"); } else { // We're being invoked with --ledger, which is where we // will start replay from. require (app->loadOldLedger (getConfig ().START_LEDGER, false), "Reloading old ledger failed."); parentLedger = lm.getClosedLedger (); } auto const parentSeq = parentLedger->getLedgerSeq (); auto seq = j["seq"].asUInt (); while (parentSeq + 1 > seq) { // Fast-scan JSON records until we hit the right one. WriteLog (lsINFO, LedgerDump) << "scanning past ledger: " << seq; j = loadJsonRecord (in); seq = j["seq"].asUInt (); if (parentSeq + 1 <= seq) { require (parentSeq + 1 == seq, "Missing ledgers between loaded and replay-start"); std::tie (deserializedLedger, txSet, txOrder) = loadLedgerAndTransactionsFromJSON (*app, j); } } gLedgerSeq = parentSeq; require(parentLedger->getLedgerSeq () + 1 == deserializedLedger->getLedgerSeq (), "Mismatched ledger-sequence prefix."); } Ledger::pointer currentLedger = boost::make_shared<Ledger> (true, *parentLedger); currentLedger->setCloseTime (deserializedLedger->getCloseTimeNC ()); currentLedger->setCloseFlags (deserializedLedger->getCloseFlags ()); currentLedger->setParentHash (deserializedLedger->getParentHash ()); WriteLog (lsINFO, LedgerDump) << "loading ledger: " << currentLedger->getLedgerSeq(); if (ShouldLog (lsTRACE, LedgerDump)) { WriteLog (lsTRACE, LedgerDump) << "expecting next ledger:"; WriteLog (lsTRACE, LedgerDump) << deserializedLedger->getJson (0); WriteLog (lsTRACE, LedgerDump) << "synthetic next ledger:"; WriteLog (lsTRACE, LedgerDump) << currentLedger->getJson (0); } gLedgerSeq++; // Apply transactions, transitioning from one ledger state to next. WriteLog (lsDEBUG, LedgerDump) << "Applying set of " << txOrder.size() << " transactions"; CanonicalTXSet retriableTransactions (txSet->getHash ()); std::set<uint256> failedTransactions; LedgerConsensus::applyTransactions (txSet, currentLedger, currentLedger, retriableTransactions, failedTransactions, false, txOrder); require (retriableTransactions.empty (), "failed retriable tx set is not empty"); require (failedTransactions.empty (), "failed tx set is not empty"); currentLedger->updateSkipList (); currentLedger->setClosed (); currentLedger->setCloseTime (deserializedLedger->getCloseTimeNC ()); int asf = currentLedger->peekAccountStateMap ()->flushDirty ( hotACCOUNT_NODE, currentLedger->getLedgerSeq()); int tmf = currentLedger->peekTransactionMap ()->flushDirty ( hotTRANSACTION_NODE, currentLedger->getLedgerSeq()); nTxs += tmf; WriteLog (lsDEBUG, LedgerDump) << "Flushed " << asf << " account " << "and " << tmf << "transaction nodes"; // Finalize with the LedgerMaster. currentLedger->setAccepted (); bool alreadyHadLedger = lm.storeLedger (currentLedger); assert (! alreadyHadLedger); lm.pushLedger (currentLedger); WriteLog (lsTRACE, LedgerDump) << "parent ledger:"; traceLedgerContents (*parentLedger); WriteLog (lsTRACE, LedgerDump) << "current ledger:"; traceLedgerContents (*currentLedger); try { checkLedgersEqual (*deserializedLedger, *currentLedger); } catch (...) { WriteLog (lsINFO, LedgerDump) << "bad ledger:"; std::cerr << currentLedger->getJson (LEDGER_JSON_FULL); throw; } parentLedger = currentLedger; } WriteLog (lsINFO, LedgerDump) << "Loaded " << gLedgerSeq << "ledgers, " << nTxs << " transactions from " << filename; exit (0); }