void LedgerHistory::validatedLedger (Ledger::ref ledger) { LedgerIndex index = ledger->getLedgerSeq(); LedgerHash hash = ledger->getHash(); assert (!hash.isZero()); ConsensusValidated::ScopedLockType sl ( m_consensus_validated.peekMutex()); std::shared_ptr< std::pair< LedgerHash, LedgerHash > > entry = std::make_shared<std::pair< LedgerHash, LedgerHash >>(); m_consensus_validated.canonicalize(index, entry, false); if (entry->second != hash) { bool mismatch (false); if (entry->second.isNonZero() && (entry->second != hash)) { WriteLog (lsERROR, LedgerMaster) << "MISMATCH: seq=" << index << " validated:" << entry->second << " then:" << hash; mismatch = true; } if (entry->first.isNonZero() && (entry->first != hash)) { WriteLog (lsERROR, LedgerMaster) << "MISMATCH: seq=" << index << " built:" << entry->first << " validated:" << hash; mismatch = true; } if (mismatch) handleMismatch (entry->second, hash); entry->second = hash; } }
/** Returns the hash of the specified ledger. @param ledgerIndex The index of the desired ledger. @param referenceLedger [out] An optional known good subsequent ledger. @return The hash of the ledger. This will be all-bits-zero if not found. */ LedgerHash getHash( LedgerIndex const& ledgerIndex, std::shared_ptr<ReadView const>& referenceLedger) { LedgerHash ledgerHash; if (!referenceLedger || (referenceLedger->info().seq < ledgerIndex)) { referenceLedger = app_.getLedgerMaster().getValidatedLedger(); if (!referenceLedger) { JLOG (j_.warn()) << "No validated ledger"; return ledgerHash; // Nothing we can do. No validated ledger. } } if (referenceLedger->info().seq >= ledgerIndex) { // See if the hash for the ledger we need is in the reference ledger ledgerHash = getLedgerHash(referenceLedger, ledgerIndex); if (ledgerHash.isZero()) { // No. Try to get another ledger that might have the hash we // need: compute the index and hash of a ledger that will have // the hash we need. LedgerIndex refIndex = getCandidateLedger (ledgerIndex); LedgerHash refHash = getLedgerHash (referenceLedger, refIndex); bool const nonzero (refHash.isNonZero ()); assert (nonzero); if (nonzero) { // We found the hash and sequence of a better reference // ledger. referenceLedger = app_.getInboundLedgers().acquire( refHash, refIndex, InboundLedger::fcGENERIC); if (referenceLedger) ledgerHash = getLedgerHash( referenceLedger, ledgerIndex); } } } else JLOG (j_.warn()) << "Validated ledger is prior to target ledger"; return ledgerHash; }
bool hasLedger (LedgerHash const& hash) { assert (hash.isNonZero ()); ScopedLockType sl (mLock); return mLedgers.find (hash) != mLedgers.end (); }
void dropLedger (LedgerHash const& hash) { assert (hash.isNonZero ()); ScopedLockType sl (mLock); mLedgers.erase (hash); }
/** Returns the hash of the specified ledger. @param ledgerIndex The index of the desired ledger. @param referenceLedger [out] An optional known good subsequent ledger. @return The hash of the ledger. This will be all-bits-zero if not found. */ LedgerHash getHash( LedgerIndex const& ledgerIndex, Ledger::pointer& referenceLedger) { LedgerHash ledgerHash; if (!referenceLedger || (referenceLedger->getLedgerSeq() < ledgerIndex)) { referenceLedger = getApp().getLedgerMaster().getValidatedLedger(); if (!referenceLedger) { m_journal.warning << "No validated ledger"; return ledgerHash; // Nothing we can do. No validated ledger. } } if (referenceLedger->getLedgerSeq() >= ledgerIndex) { // See if the hash for the ledger we need is in the reference ledger ledgerHash = getLedgerHash(referenceLedger, ledgerIndex); if (ledgerHash.isZero()) { // No, Try to get another ledger that might have the hash we need // Compute the index and hash of a ledger that will have the hash we need LedgerIndex refIndex = getCandidateLedger (ledgerIndex); LedgerHash refHash = getLedgerHash (referenceLedger, refIndex); bool const nonzero (refHash.isNonZero ()); assert (nonzero); if (nonzero) { // We found the hash and sequence of a better reference ledger referenceLedger = getApp().getInboundLedgers().acquire( refHash, refIndex, InboundLedger::fcGENERIC); if (referenceLedger) ledgerHash = getLedgerHash(referenceLedger, ledgerIndex); } } } else m_journal.warning << "Validated ledger is prior to target ledger"; return ledgerHash; }
void LedgerHistory::validatedLedger (Ledger::ref ledger) { LedgerIndex index = ledger->getLedgerSeq(); LedgerHash hash = ledger->getHash(); assert (!hash.isZero()); TaggedCache::ScopedLockType sl(mConsensusValidated.peekMutex(), __FILE__, __LINE__); boost::shared_ptr< std::pair< LedgerHash, LedgerHash > > entry = boost::make_shared<std::pair< LedgerHash, LedgerHash >>(); mConsensusValidated.canonicalize(index, entry, false); if (entry->second != hash) { if (entry->second.isNonZero() && (entry->second != hash)) { WriteLog (lsERROR, LedgerMaster) << "MISMATCH: seq=" << index << " validated:" << entry->second << " then:" << hash; } if (entry->first.isNonZero() && (entry->first != hash)) { WriteLog (lsERROR, LedgerMaster) << "MISMATCH: seq=" << index << " built:" << entry->first << " validated:" << hash; } entry->second = hash; } }
// { // ledger_hash : <ledger> // ledger_index : <ledger_index> // } Json::Value doLedgerRequest (RPC::Context& context) { auto const hasHash = context.params.isMember (jss::ledger_hash); auto const hasIndex = context.params.isMember (jss::ledger_index); auto& ledgerMaster = getApp().getLedgerMaster(); LedgerHash ledgerHash; if ((hasHash && hasIndex) || !(hasHash || hasIndex)) { return RPC::make_param_error( "Exactly one of ledger_hash and ledger_index can be set."); } if (hasHash) { auto const& jsonHash = context.params[jss::ledger_hash]; if (!jsonHash.isString() || !ledgerHash.SetHex (jsonHash.asString ())) return RPC::invalid_field_message (jss::ledger_hash); } else { auto const& jsonIndex = context.params[jss::ledger_index]; if (!jsonIndex.isNumeric ()) return RPC::invalid_field_message (jss::ledger_index); // We need a validated ledger to get the hash from the sequence if (ledgerMaster.getValidatedLedgerAge() > 120) return rpcError (rpcNO_CURRENT); auto ledgerIndex = jsonIndex.asInt(); auto ledger = ledgerMaster.getValidatedLedger(); if (ledgerIndex >= ledger->getLedgerSeq()) return RPC::make_param_error("Ledger index too large"); // Try to get the hash of the desired ledger from the validated ledger ledgerHash = ledger->getLedgerHash (ledgerIndex); if (ledgerHash == zero) { // Find a ledger more likely to have the hash of the desired ledger auto refIndex = (ledgerIndex + 255) & (~255); auto refHash = ledger->getLedgerHash (refIndex); assert (refHash.isNonZero ()); ledger = ledgerMaster.getLedgerByHash (refHash); if (!ledger) { // We don't have the ledger we need to figure out which ledger // they want. Try to get it. if (auto il = getApp().getInboundLedgers().acquire ( refHash, refIndex, InboundLedger::fcGENERIC)) return getJson (LedgerFill (*il)); if (auto il = getApp().getInboundLedgers().find (refHash)) { Json::Value jvResult = il->getJson (0); jvResult[jss::error] = "ledgerNotFound"; return jvResult; } // Likely the app is shutting down return Json::Value(); } ledgerHash = ledger->getLedgerHash (ledgerIndex); assert (ledgerHash.isNonZero ()); } } auto ledger = ledgerMaster.getLedgerByHash (ledgerHash); if (ledger) { // We already have the ledger they want Json::Value jvResult; jvResult[jss::ledger_index] = ledger->getLedgerSeq(); addJson (jvResult, {*ledger, 0}); return jvResult; } else { // Try to get the desired ledger if (auto il = getApp ().getInboundLedgers ().acquire ( ledgerHash, 0, InboundLedger::fcGENERIC)) return getJson (LedgerFill (*il)); if (auto il = getApp().getInboundLedgers().find (ledgerHash)) return il->getJson (0); return RPC::make_error ( rpcNOT_READY, "findCreate failed to return an inbound ledger"); } }
/** Run the ledger cleaner. */ void doLedgerCleaner() { Ledger::pointer goodLedger; while (! this->threadShouldExit()) { LedgerIndex ledgerIndex; LedgerHash ledgerHash; bool doNodes; bool doTxns; while (getApp().getFeeTrack().isLoadedLocal()) { m_journal.debug << "Waiting for load to subside"; std::this_thread::sleep_for(std::chrono::seconds(5)); if (this->threadShouldExit ()) return; } { SharedState::Access state (m_state); if ((state->minRange > state->maxRange) || (state->maxRange == 0) || (state->minRange == 0)) { state->minRange = state->maxRange = 0; return; } ledgerIndex = state->maxRange; doNodes = state->checkNodes; doTxns = state->fixTxns; } ledgerHash = getHash(ledgerIndex, goodLedger); bool fail = false; if (ledgerHash.isZero()) { m_journal.info << "Unable to get hash for ledger " << ledgerIndex; fail = true; } else if (!doLedger(ledgerIndex, ledgerHash, doNodes, doTxns)) { m_journal.info << "Failed to process ledger " << ledgerIndex; fail = true; } if (fail) { { SharedState::Access state (m_state); ++state->failures; } // Wait for acquiring to catch up to us std::this_thread::sleep_for(std::chrono::seconds(2)); } else { { SharedState::Access state (m_state); if (ledgerIndex == state->minRange) ++state->minRange; if (ledgerIndex == state->maxRange) --state->maxRange; state->failures = 0; } // Reduce I/O pressure and wait for acquiring to catch up to us std::this_thread::sleep_for(std::chrono::milliseconds(100)); } } }
/** Run the ledger cleaner. */ void doLedgerCleaner() { auto shouldExit = [this]() { std::lock_guard<std::mutex> lock(mutex_); return shouldExit_; }; std::shared_ptr<ReadView const> goodLedger; while (! shouldExit()) { LedgerIndex ledgerIndex; LedgerHash ledgerHash; bool doNodes; bool doTxns; while (app_.getFeeTrack().isLoadedLocal()) { JLOG (j_.debug()) << "Waiting for load to subside"; std::this_thread::sleep_for(std::chrono::seconds(5)); if (shouldExit()) return; } { std::lock_guard<std::mutex> lock (mutex_); if ((minRange_ > maxRange_) || (maxRange_ == 0) || (minRange_ == 0)) { minRange_ = maxRange_ = 0; state_ = State::readyToClean; return; } ledgerIndex = maxRange_; doNodes = checkNodes_; doTxns = fixTxns_; } ledgerHash = getHash(ledgerIndex, goodLedger); bool fail = false; if (ledgerHash.isZero()) { JLOG (j_.info()) << "Unable to get hash for ledger " << ledgerIndex; fail = true; } else if (!doLedger(ledgerIndex, ledgerHash, doNodes, doTxns)) { JLOG (j_.info()) << "Failed to process ledger " << ledgerIndex; fail = true; } if (fail) { { std::lock_guard<std::mutex> lock (mutex_); ++failures_; } // Wait for acquiring to catch up to us std::this_thread::sleep_for(std::chrono::seconds(2)); } else { { std::lock_guard<std::mutex> lock (mutex_); if (ledgerIndex == minRange_) ++minRange_; if (ledgerIndex == maxRange_) --maxRange_; failures_ = 0; } // Reduce I/O pressure and wait for acquiring to catch up to us std::this_thread::sleep_for(std::chrono::milliseconds(100)); } } }