Ledger::pointer LedgerHistory::getLedgerBySeq (std::uint32_t index) { { LedgersByHash::ScopedLockType sl (m_ledgers_by_hash.peekMutex ()); std::map <std::uint32_t, uint256>::iterator it (mLedgersByIndex.find (index)); if (it != mLedgersByIndex.end ()) { uint256 hash = it->second; sl.unlock (); return getLedgerByHash (hash); } } Ledger::pointer ret (Ledger::loadByIndex (index)); if (!ret) return ret; assert (ret->getLedgerSeq () == index); { // Add this ledger to the local tracking by index LedgersByHash::ScopedLockType sl (m_ledgers_by_hash.peekMutex ()); assert (ret->isImmutable ()); m_ledgers_by_hash.canonicalize (ret->getHash (), ret); mLedgersByIndex[ret->getLedgerSeq ()] = ret->getHash (); return (ret->getLedgerSeq () == index) ? ret : Ledger::pointer (); } }
Ledger::pointer LedgerHistory::getLedgerBySeq (uint32 index) { TaggedCache::ScopedLockType sl (mLedgersByHash.peekMutex (), __FILE__, __LINE__); std::map<uint32, uint256>::iterator it (mLedgersByIndex.find (index)); if (it != mLedgersByIndex.end ()) { uint256 hash = it->second; sl.unlock (); return getLedgerByHash (hash); } sl.unlock (); Ledger::pointer ret (Ledger::loadByIndex (index)); if (!ret) return ret; assert (ret->getLedgerSeq () == index); sl.lock (__FILE__, __LINE__); assert (ret->isImmutable ()); mLedgersByHash.canonicalize (ret->getHash (), ret); mLedgersByIndex[ret->getLedgerSeq ()] = ret->getHash (); return (ret->getLedgerSeq () == index) ? ret : Ledger::pointer (); }
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; }
Ledger::pointer LedgerMaster::closeLedger(bool recover) { boost::recursive_mutex::scoped_lock sl(mLock); Ledger::pointer closingLedger = mCurrentLedger; if (recover) { int recovers = 0; for (CanonicalTXSet::iterator it = mHeldTransactions.begin(), end = mHeldTransactions.end(); it != end; ++it) { try { bool didApply; mEngine.applyTransaction(*it->second, tapOPEN_LEDGER, didApply); if (didApply) ++recovers; } catch (...) { cLog(lsWARNING) << "Held transaction throws"; } } tLog(recovers != 0, lsINFO) << "Recovered " << recovers << " held transactions"; mHeldTransactions.reset(closingLedger->getHash()); } mCurrentLedger = boost::make_shared<Ledger>(boost::ref(*closingLedger), true); mEngine.setLedger(mCurrentLedger); return closingLedger; }
Ledger::pointer LedgerHistory::getLedgerByHash (uint256 const& hash) { Ledger::pointer ret = m_ledgers_by_hash.fetch (hash); if (ret) { assert (ret->isImmutable ()); assert (ret->getHash () == hash); return ret; } ret = Ledger::loadByHash (hash); if (!ret) return ret; assert (ret->isImmutable ()); assert (ret->getHash () == hash); m_ledgers_by_hash.canonicalize (ret->getHash (), ret); assert (ret->getHash () == hash); return ret; }
/** Process a single ledger @param ledgerIndex The index of the ledger to process. @param ledgerHash The known correct hash of the ledger. @param doNodes Ensure all ledger nodes are in the node db. @param doTxns Reprocess (account) transactions to SQL databases. @return `true` if the ledger was cleaned. */ bool doLedger( LedgerIndex const& ledgerIndex, LedgerHash const& ledgerHash, bool doNodes, bool doTxns) { Ledger::pointer nodeLedger = getApp().getInboundLedgers().acquire ( ledgerHash, ledgerIndex, InboundLedger::fcGENERIC); if (!nodeLedger) { m_journal.debug << "Ledger " << ledgerIndex << " not available"; return false; } Ledger::pointer dbLedger = Ledger::loadByIndex(ledgerIndex); if (! dbLedger || (dbLedger->getHash() != ledgerHash) || (dbLedger->getParentHash() != nodeLedger->getParentHash())) { // Ideally we'd also check for more than one ledger with that index m_journal.debug << "Ledger " << ledgerIndex << " mismatches SQL DB"; doTxns = true; } if(! getApp().getLedgerMaster().fixIndex(ledgerIndex, ledgerHash)) { m_journal.debug << "ledger " << ledgerIndex << " had wrong entry in history"; doTxns = true; } if (doNodes && !nodeLedger->walkLedger()) { m_journal.debug << "Ledger " << ledgerIndex << " is missing nodes"; getApp().getInboundLedgers().acquire( ledgerHash, ledgerIndex, InboundLedger::fcGENERIC); return false; } if (doTxns && !nodeLedger->pendSaveValidated(true, false)) { m_journal.debug << "Failed to save ledger " << ledgerIndex; return false; } return true; }
// VFALCO TODO This should return boost::optional<uint256> LedgerHash getLedgerHash(Ledger::pointer ledger, LedgerIndex index) { boost::optional<LedgerHash> hash; try { hash = hashOfSeq(*ledger, index, getApp().getSLECache(), m_journal); } catch (SHAMapMissingNode &) { m_journal.warning << "Node missing from ledger " << ledger->getLedgerSeq(); getApp().getInboundLedgers().acquire ( ledger->getHash(), ledger->getLedgerSeq(), InboundLedger::fcGENERIC); } return hash ? *hash : zero; // kludge }
// The previous version of the lookupLedger command would accept the // "ledger_index" argument as a string and silently treat it as a request to // return the current ledger which, while not strictly wrong, could cause a lot // of confusion. // // The code now robustly validates the input and ensures that the only possible // values for the "ledger_index" parameter are the index of a ledger passed as // an integer or one of the strings "current", "closed" or "validated". // Additionally, the code ensures that the value passed in "ledger_hash" is a // string and a valid hash. Invalid values will return an appropriate error // code. // // In the absence of the "ledger_hash" or "ledger_index" parameters, the code // assumes that "ledger_index" has the value "current". // // Returns a Json::objectValue. If there was an error, it will be in that // return value. Otherwise, the object contains the field "validated" and // optionally the fields "ledger_hash", "ledger_index" and // "ledger_current_index", if they are defined. Status lookupLedger ( Json::Value const& params, Ledger::pointer& ledger, NetworkOPs& netOps, Json::Value& jsonResult) { if (auto status = ledgerFromRequest (params, ledger, netOps)) return status; if (ledger->isClosed ()) { jsonResult[jss::ledger_hash] = to_string (ledger->getHash()); jsonResult[jss::ledger_index] = ledger->getLedgerSeq(); } else { jsonResult[jss::ledger_current_index] = ledger->getLedgerSeq(); } jsonResult[jss::validated] = isValidated (*ledger); return Status::OK; }
bool PathRequest::isValid (RippleLineCache::ref crCache) { ScopedLockType sl (mLock); bValid = raSrcAccount.isSet () && raDstAccount.isSet () && saDstAmount > zero; Ledger::pointer lrLedger = crCache->getLedger (); if (bValid) { AccountState::pointer asSrc = getApp().getOPs ().getAccountState (crCache->getLedger(), raSrcAccount); if (!asSrc) { // no source account bValid = false; jvStatus = rpcError (rpcSRC_ACT_NOT_FOUND); } else { AccountState::pointer asDst = getApp().getOPs ().getAccountState (lrLedger, raDstAccount); Json::Value& jvDestCur = (jvStatus["destination_currencies"] = Json::arrayValue); if (!asDst) { // no destination account jvDestCur.append (Json::Value ("XRP")); if (!saDstAmount.isNative ()) { // only XRP can be send to a non-existent account bValid = false; jvStatus = rpcError (rpcACT_NOT_FOUND); } else if (saDstAmount < STAmount (lrLedger->getReserve (0))) { // payment must meet reserve bValid = false; jvStatus = rpcError (rpcDST_AMT_MALFORMED); } } else { bool const disallowXRP ( asDst->peekSLE ().getFlags() & lsfDisallowXRP); CurrencySet usDestCurrID = usAccountDestCurrencies (raDstAccount, crCache, !disallowXRP); for (auto const& currency : usDestCurrID) jvDestCur.append (to_string (currency)); jvStatus["destination_tag"] = (asDst->peekSLE ().getFlags () & lsfRequireDestTag) != 0; } } } if (bValid) { jvStatus["ledger_hash"] = to_string (lrLedger->getHash ()); jvStatus["ledger_index"] = lrLedger->getLedgerSeq (); } return bValid; }
// 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; }