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); }
void LedgerHistory::addLedger (Ledger::pointer ledger, bool validated) { assert (ledger && ledger->isImmutable ()); assert (ledger->peekAccountStateMap ()->getHash ().isNonZero ()); TaggedCache::ScopedLockType sl (mLedgersByHash.peekMutex (), __FILE__, __LINE__); mLedgersByHash.canonicalize (ledger->getHash(), ledger, true); if (validated) mLedgersByIndex[ledger->getLedgerSeq()] = ledger->getHash(); }
bool LedgerHistory::addLedger (Ledger::pointer ledger, bool validated) { assert (ledger && ledger->isImmutable ()); assert (ledger->peekAccountStateMap ()->getHash ().isNonZero ()); LedgersByHash::ScopedLockType sl (m_ledgers_by_hash.peekMutex ()); const bool alreadyHad = m_ledgers_by_hash.canonicalize (ledger->getHash(), ledger, true); if (validated) mLedgersByIndex[ledger->getLedgerSeq()] = ledger->getHash(); return alreadyHad; }
/** * 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); }
// Get state nodes from a ledger // Inputs: // limit: integer, maximum number of entries // marker: opaque, resume point // binary: boolean, format // Outputs: // ledger_hash: chosen ledger's hash // ledger_index: chosen ledger's index // state: array of state nodes // marker: resume point, if any Json::Value doLedgerData (RPC::Context& context) { context.lock_.unlock (); int const BINARY_PAGE_LENGTH = 256; int const JSON_PAGE_LENGTH = 2048; Ledger::pointer lpLedger; Json::Value jvResult = RPC::lookupLedger (context.params_, lpLedger, context.netOps_); if (!lpLedger) return jvResult; uint256 resumePoint; if (context.params_.isMember ("marker")) { Json::Value const& jMarker = context.params_["marker"]; if (!jMarker.isString ()) return RPC::expected_field_error ("marker", "valid"); if (!resumePoint.SetHex (jMarker.asString ())) return RPC::expected_field_error ("marker", "valid"); } bool isBinary = false; if (context.params_.isMember ("binary")) { Json::Value const& jBinary = context.params_["binary"]; if (!jBinary.isBool ()) return RPC::expected_field_error ("binary", "bool"); isBinary = jBinary.asBool (); } int limit = -1; int maxLimit = isBinary ? BINARY_PAGE_LENGTH : JSON_PAGE_LENGTH; if (context.params_.isMember ("limit")) { Json::Value const& jLimit = context.params_["limit"]; if (!jLimit.isIntegral ()) return RPC::expected_field_error ("limit", "integer"); limit = jLimit.asInt (); } if ((limit < 0) || ((limit > maxLimit) && (context.role_ != Config::ADMIN))) limit = maxLimit; Json::Value jvReply = Json::objectValue; jvReply["ledger_hash"] = to_string (lpLedger->getHash()); jvReply["ledger_index"] = beast::lexicalCastThrow <std::string> (lpLedger->getLedgerSeq ()); Json::Value& nodes = (jvReply["state"] = Json::arrayValue); SHAMap& map = *(lpLedger->peekAccountStateMap ()); for (;;) { SHAMapItem::pointer item = map.peekNextItem (resumePoint); if (!item) break; resumePoint = item->getTag(); if (limit-- <= 0) { --resumePoint; jvReply["marker"] = to_string (resumePoint); break; } if (isBinary) { Json::Value& entry = nodes.append (Json::objectValue); entry["data"] = strHex (item->peekData().begin(), item->peekData().size()); entry["index"] = to_string (item->getTag ()); } else { SLE sle (item->peekSerializer(), item->getTag ()); Json::Value& entry = nodes.append (sle.getJson (0)); entry["index"] = to_string (item->getTag ()); } } return jvReply; }