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 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); }
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 (); } }
// 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 }
/** 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; }
bool LedgerMaster::acquireMissingLedger(Ledger::ref origLedger, const uint256& ledgerHash, uint32 ledgerSeq) { // return: false = already gave up recently if (mTooFast) return true; Ledger::pointer ledger = mLedgerHistory.getLedgerBySeq(ledgerSeq); if (ledger && (Ledger::getHashByIndex(ledgerSeq) == ledgerHash)) { cLog(lsTRACE) << "Ledger hash found in database"; mTooFast = true; theApp->getJobQueue().addJob(jtPUBOLDLEDGER, boost::bind(&LedgerMaster::asyncAccept, this, ledger)); return true; } if (theApp->getMasterLedgerAcquire().isFailure(ledgerHash)) return false; mMissingLedger = theApp->getMasterLedgerAcquire().findCreate(ledgerHash); if (mMissingLedger->isComplete()) { Ledger::pointer lgr = mMissingLedger->getLedger(); if (lgr && (lgr->getLedgerSeq() == ledgerSeq)) missingAcquireComplete(mMissingLedger); mMissingLedger.reset(); return true; } else if (mMissingLedger->isDone()) { mMissingLedger.reset(); return false; } mMissingSeq = ledgerSeq; if (mMissingLedger->setAccept()) { if (!mMissingLedger->addOnComplete(boost::bind(&LedgerMaster::missingAcquireComplete, this, _1))) theApp->getIOService().post(boost::bind(&LedgerMaster::missingAcquireComplete, this, mMissingLedger)); } int fetch = theConfig.getSize(siLedgerFetch); if (theApp->getMasterLedgerAcquire().getFetchCount() < fetch) { int count = 0; typedef std::pair<uint32, uint256> u_pair; std::vector<u_pair> vec = origLedger->getLedgerHashes(); BOOST_REVERSE_FOREACH(const u_pair& it, vec) { if ((count < fetch) && (it.first < ledgerSeq) && !mCompleteLedgers.hasValue(it.first) && !theApp->getMasterLedgerAcquire().find(it.second)) { ++count; theApp->getMasterLedgerAcquire().findCreate(it.second); } } }
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(); }
// 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 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; }
/** Get the current RippleLineCache, updating it if necessary. Get the correct ledger to use. */ RippleLineCache::pointer PathRequests::getLineCache (Ledger::pointer& ledger, bool authoritative) { ScopedLockType sl (mLock, __FILE__, __LINE__); uint32 lineSeq = mLineCache ? mLineCache->getLedger()->getLedgerSeq() : 0; uint32 lgrSeq = ledger->getLedgerSeq(); if ( (lineSeq == 0) || // no ledger (authoritative && (lgrSeq > lineSeq)) || // newer authoritative ledger (authoritative && ((lgrSeq + 8) < lineSeq)) || // we jumped way back for some reason (lgrSeq > (lineSeq + 8))) // we jumped way forward for some reason { ledger = boost::make_shared<Ledger>(*ledger, false); // Take a snapshot of the ledger mLineCache = boost::make_shared<RippleLineCache> (ledger); } else { ledger = mLineCache->getLedger(); } return mLineCache; }
void LedgerMaster::asyncAccept(Ledger::pointer ledger) { uint32 seq = ledger->getLedgerSeq(); uint256 prevHash = ledger->getParentHash(); while (seq > 0) { { boost::recursive_mutex::scoped_lock ml(mLock); mCompleteLedgers.setValue(seq); --seq; if (mCompleteLedgers.hasValue(seq)) break; } uint256 tHash, pHash; if (!Ledger::getHashesByIndex(seq, tHash, pHash) || (tHash != prevHash)) break; prevHash = pHash; } resumeAcquiring(); }
// { // account: account, // ledger_index_min: ledger_index // optional, defaults to earliest // ledger_index_max: ledger_index, // optional, defaults to latest // binary: boolean, // optional, defaults to false // forward: boolean, // optional, defaults to false // limit: integer, // optional // marker: opaque // optional, resume previous query // } Json::Value RPCHandler::doAccountTx (Json::Value params, Resource::Charge& loadType, Application::ScopedLockType& masterLockHolder) { masterLockHolder.unlock (); RippleAddress raAccount; int limit = params.isMember (jss::limit) ? params[jss::limit].asUInt () : -1; bool bBinary = params.isMember ("binary") && params["binary"].asBool (); bool bForward = params.isMember ("forward") && params["forward"].asBool (); std::uint32_t uLedgerMin; std::uint32_t uLedgerMax; std::uint32_t uValidatedMin; std::uint32_t uValidatedMax; bool bValidated = mNetOps->getValidatedRange (uValidatedMin, uValidatedMax); if (!bValidated) { // Don't have a validated ledger range. return rpcError (rpcLGR_IDXS_INVALID); } if (!params.isMember ("account")) return rpcError (rpcINVALID_PARAMS); if (!raAccount.setAccountID (params["account"].asString ())) return rpcError (rpcACT_MALFORMED); loadType = Resource::feeMediumBurdenRPC; if (params.isMember ("ledger_index_min") || params.isMember ("ledger_index_max")) { std::int64_t iLedgerMin = params.isMember ("ledger_index_min") ? params["ledger_index_min"].asInt () : -1; std::int64_t iLedgerMax = params.isMember ("ledger_index_max") ? params["ledger_index_max"].asInt () : -1; uLedgerMin = iLedgerMin == -1 ? 0 : iLedgerMin; uLedgerMax = iLedgerMax == -1 ? uValidatedMax : iLedgerMax; if (uLedgerMax < uLedgerMin) { return rpcError (rpcLGR_IDXS_INVALID); } } else { Ledger::pointer l; Json::Value ret = RPC::lookupLedger (params, l, *mNetOps); if (!l) return ret; uLedgerMin = 0; uLedgerMax = l->getLedgerSeq (); } Json::Value resumeToken; if (params.isMember(jss::marker)) { resumeToken = params[jss::marker]; } #ifndef BEAST_DEBUG try { #endif Json::Value ret (Json::objectValue); ret["account"] = raAccount.humanAccountID (); Json::Value& jvTxns = (ret["transactions"] = Json::arrayValue); if (bBinary) { std::vector<NetworkOPs::txnMetaLedgerType> txns = mNetOps->getTxsAccountB (raAccount, uLedgerMin, uLedgerMax, bForward, resumeToken, limit, mRole == Config::ADMIN); for (std::vector<NetworkOPs::txnMetaLedgerType>::const_iterator it = txns.begin (), end = txns.end (); it != end; ++it) { Json::Value& jvObj = jvTxns.append (Json::objectValue); std::uint32_t uLedgerIndex = std::get<2> (*it); jvObj["tx_blob"] = std::get<0> (*it); jvObj["meta"] = std::get<1> (*it); jvObj["ledger_index"] = uLedgerIndex; jvObj[jss::validated] = bValidated && uValidatedMin <= uLedgerIndex && uValidatedMax >= uLedgerIndex; } } else { std::vector< std::pair<Transaction::pointer, TransactionMetaSet::pointer> > txns = mNetOps->getTxsAccount (raAccount, uLedgerMin, uLedgerMax, bForward, resumeToken, limit, mRole == Config::ADMIN); for (std::vector< std::pair<Transaction::pointer, TransactionMetaSet::pointer> >::iterator it = txns.begin (), end = txns.end (); it != end; ++it) { Json::Value& jvObj = jvTxns.append (Json::objectValue); if (it->first) jvObj[jss::tx] = it->first->getJson (1); if (it->second) { std::uint32_t uLedgerIndex = it->second->getLgrSeq (); jvObj[jss::meta] = it->second->getJson (0); jvObj[jss::validated] = bValidated && uValidatedMin <= uLedgerIndex && uValidatedMax >= uLedgerIndex; } } } //Add information about the original query ret[jss::ledger_index_min] = uLedgerMin; ret[jss::ledger_index_max] = uLedgerMax; if (params.isMember (jss::limit)) ret[jss::limit] = limit; if (!resumeToken.isNull()) ret[jss::marker] = resumeToken; return ret; #ifndef BEAST_DEBUG } catch (...) { return rpcError (rpcINTERNAL); } #endif }
void PathRequests::updateAll (Ledger::ref inLedger, CancelCallback shouldCancel) { std::vector<PathRequest::wptr> requests; LoadEvent::autoptr event (getApp().getJobQueue().getLoadEventAP(jtPATH_FIND, "PathRequest::updateAll")); // Get the ledger and cache we should be using Ledger::pointer ledger = inLedger; RippleLineCache::pointer cache; { ScopedLockType sl (mLock); requests = mRequests; cache = getLineCache (ledger, true); } bool newRequests = getApp().getLedgerMaster().isNewPathRequest(); bool mustBreak = false; mJournal.trace << "updateAll seq=" << ledger->getLedgerSeq() << ", " << requests.size() << " requests"; int processed = 0, removed = 0; do { BOOST_FOREACH (PathRequest::wref wRequest, requests) { if (shouldCancel()) break; bool remove = true; PathRequest::pointer pRequest = wRequest.lock (); if (pRequest) { if (!pRequest->needsUpdate (newRequests, ledger->getLedgerSeq ())) remove = false; else { InfoSub::pointer ipSub = pRequest->getSubscriber (); if (ipSub) { ipSub->getConsumer ().charge (Resource::feePathFindUpdate); if (!ipSub->getConsumer ().warn ()) { Json::Value update = pRequest->doUpdate (cache, false); pRequest->updateComplete (); update["type"] = "path_find"; ipSub->send (update, false); remove = false; ++processed; } } } } if (remove) { PathRequest::pointer pRequest = wRequest.lock (); ScopedLockType sl (mLock); // Remove any dangling weak pointers or weak pointers that refer to this path request. std::vector<PathRequest::wptr>::iterator it = mRequests.begin(); while (it != mRequests.end()) { PathRequest::pointer itRequest = it->lock (); if (!itRequest || (itRequest == pRequest)) { ++removed; it = mRequests.erase (it); } else ++it; } } mustBreak = !newRequests && getApp().getLedgerMaster().isNewPathRequest(); if (mustBreak) // We weren't handling new requests and then there was a new request break; } if (mustBreak) { // a new request came in while we were working newRequests = true; } else if (newRequests) { // we only did new requests, so we always need a last pass newRequests = getApp().getLedgerMaster().isNewPathRequest(); } else { // check if there are any new requests, otherwise we are done newRequests = getApp().getLedgerMaster().isNewPathRequest(); if (!newRequests) // We did a full pass and there are no new requests return; } { // Get the latest requests, cache, and ledger for next pass ScopedLockType sl (mLock); if (mRequests.empty()) break; requests = mRequests; cache = getLineCache (ledger, false); } } while (!shouldCancel ()); mJournal.debug << "updateAll complete " << processed << " process and " << removed << " removed"; }
/** * 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); }
void LedgerHistory::handleMismatch (LedgerHash const& built, LedgerHash const& valid) { assert (built != valid); ++mismatch_counter_; Ledger::pointer builtLedger = getLedgerByHash (built); Ledger::pointer validLedger = getLedgerByHash (valid); if (builtLedger && validLedger) { assert (builtLedger->getLedgerSeq() == validLedger->getLedgerSeq()); // Determine the mismatch reason // Distinguish Byzantine failure from transaction processing difference if (builtLedger->getParentHash() != validLedger->getParentHash()) { // Disagreement over prior ledger indicates sync issue WriteLog (lsERROR, LedgerMaster) << "MISMATCH on prior ledger"; } else if (builtLedger->getCloseTimeNC() != validLedger->getCloseTimeNC()) { // Disagreement over close time indicates Byzantine failure WriteLog (lsERROR, LedgerMaster) << "MISMATCH on close time"; } else { std::vector <uint256> builtTx, validTx; builtLedger->peekTransactionMap()->visitLeaves( std::bind (&addLeaf, std::ref (builtTx), std::placeholders::_1)); validLedger->peekTransactionMap()->visitLeaves( std::bind (&addLeaf, std::ref (validTx), std::placeholders::_1)); std::sort (builtTx.begin(), builtTx.end()); std::sort (validTx.begin(), validTx.end()); if (builtTx == validTx) { // Disagreement with same prior ledger, close time, and transactions // indicates a transaction processing difference WriteLog (lsERROR, LedgerMaster) << "MISMATCH with same " << builtTx.size() << " tx"; } else { std::vector <uint256> notBuilt, notValid; std::set_difference ( validTx.begin(), validTx.end(), builtTx.begin(), builtTx.end(), std::inserter (notBuilt, notBuilt.begin())); std::set_difference ( builtTx.begin(), builtTx.end(), validTx.begin(), validTx.end(), std::inserter (notValid, notValid.begin())); // This can be either a disagreement over the consensus // set or difference in which transactions were rejected // as invalid WriteLog (lsERROR, LedgerMaster) << "MISMATCH tx differ " << builtTx.size() << " built, " << validTx.size() << " valid"; for (auto const& t : notBuilt) { WriteLog (lsERROR, LedgerMaster) << "MISMATCH built without " << t; } for (auto const& t : notValid) { WriteLog (lsERROR, LedgerMaster) << "MISMATCH valid without " << t; } } } } else WriteLog (lsERROR, LedgerMaster) << "MISMATCH cannot be analyzed"; }
// { // account: account, // ledger_index_min: ledger_index, // ledger_index_max: ledger_index, // binary: boolean, // optional, defaults to false // count: boolean, // optional, defaults to false // descending: boolean, // optional, defaults to false // offset: integer, // optional, defaults to 0 // limit: integer // optional // } Json::Value doAccountTxOld (RPC::Context& context) { RippleAddress raAccount; std::uint32_t offset = context.params_.isMember ("offset") ? context.params_["offset"].asUInt () : 0; int limit = context.params_.isMember ("limit") ? context.params_["limit"].asUInt () : -1; bool bBinary = context.params_.isMember ("binary") && context.params_["binary"].asBool (); bool bDescending = context.params_.isMember ("descending") && context.params_["descending"].asBool (); bool bCount = context.params_.isMember ("count") && context.params_["count"].asBool (); std::uint32_t uLedgerMin; std::uint32_t uLedgerMax; std::uint32_t uValidatedMin; std::uint32_t uValidatedMax; bool bValidated = context.netOps_.getValidatedRange ( uValidatedMin, uValidatedMax); if (!context.params_.isMember ("account")) return rpcError (rpcINVALID_PARAMS); if (!raAccount.setAccountID (context.params_["account"].asString ())) return rpcError (rpcACT_MALFORMED); if (offset > 3000) return rpcError (rpcATX_DEPRECATED); context.loadType_ = Resource::feeHighBurdenRPC; // DEPRECATED if (context.params_.isMember ("ledger_min")) { context.params_["ledger_index_min"] = context.params_["ledger_min"]; bDescending = true; } // DEPRECATED if (context.params_.isMember ("ledger_max")) { context.params_["ledger_index_max"] = context.params_["ledger_max"]; bDescending = true; } if (context.params_.isMember ("ledger_index_min") || context.params_.isMember ("ledger_index_max")) { std::int64_t iLedgerMin = context.params_.isMember ("ledger_index_min") ? context.params_["ledger_index_min"].asInt () : -1; std::int64_t iLedgerMax = context.params_.isMember ("ledger_index_max") ? context.params_["ledger_index_max"].asInt () : -1; if (!bValidated && (iLedgerMin == -1 || iLedgerMax == -1)) { // Don't have a validated ledger range. return rpcError (rpcLGR_IDXS_INVALID); } uLedgerMin = iLedgerMin == -1 ? uValidatedMin : iLedgerMin; uLedgerMax = iLedgerMax == -1 ? uValidatedMax : iLedgerMax; if (uLedgerMax < uLedgerMin) { return rpcError (rpcLGR_IDXS_INVALID); } } else { Ledger::pointer l; Json::Value ret = RPC::lookupLedger (context.params_, l, context.netOps_); if (!l) return ret; uLedgerMin = uLedgerMax = l->getLedgerSeq (); } int count = 0; #ifndef BEAST_DEBUG try { #endif Json::Value ret (Json::objectValue); ret["account"] = raAccount.humanAccountID (); Json::Value& jvTxns = (ret["transactions"] = Json::arrayValue); if (bBinary) { auto txns = context.netOps_.getAccountTxsB ( raAccount, uLedgerMin, uLedgerMax, bDescending, offset, limit, context.role_ == Config::ADMIN); for (auto it = txns.begin (), end = txns.end (); it != end; ++it) { ++count; Json::Value& jvObj = jvTxns.append (Json::objectValue); std::uint32_t uLedgerIndex = std::get<2> (*it); jvObj["tx_blob"] = std::get<0> (*it); jvObj["meta"] = std::get<1> (*it); jvObj["ledger_index"] = uLedgerIndex; jvObj["validated"] = bValidated && uValidatedMin <= uLedgerIndex && uValidatedMax >= uLedgerIndex; } } else { auto txns = context.netOps_.getAccountTxs ( raAccount, uLedgerMin, uLedgerMax, bDescending, offset, limit, context.role_ == Config::ADMIN); for (auto it = txns.begin (), end = txns.end (); it != end; ++it) { ++count; Json::Value& jvObj = jvTxns.append (Json::objectValue); if (it->first) jvObj["tx"] = it->first->getJson (1); if (it->second) { std::uint32_t uLedgerIndex = it->second->getLgrSeq (); jvObj["meta"] = it->second->getJson (0); jvObj["validated"] = bValidated && uValidatedMin <= uLedgerIndex && uValidatedMax >= uLedgerIndex; } } } //Add information about the original query ret["ledger_index_min"] = uLedgerMin; ret["ledger_index_max"] = uLedgerMax; ret["validated"] = bValidated && uValidatedMin <= uLedgerMin && uValidatedMax >= uLedgerMax; ret["offset"] = offset; // We no longer return the full count but only the count of returned // transactions. Computing this count was two expensive and this API is // deprecated anyway. if (bCount) ret["count"] = count; if (context.params_.isMember ("limit")) ret["limit"] = limit; return ret; #ifndef BEAST_DEBUG } catch (...) { return rpcError (rpcINTERNAL); } #endif }
// 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; }
// { // account: account, // ledger_index_min: ledger_index // optional, defaults to earliest // ledger_index_max: ledger_index, // optional, defaults to latest // binary: boolean, // optional, defaults to false // forward: boolean, // optional, defaults to false // limit: integer, // optional // marker: opaque // optional, resume previous query // } Json::Value doAccountTx (RPC::Context& context) { auto& params = context.params_; RippleAddress raAccount; int limit = params.isMember (jss::limit) ? params[jss::limit].asUInt () : -1; bool bBinary = params.isMember ("binary") && params["binary"].asBool (); bool bForward = params.isMember ("forward") && params["forward"].asBool (); std::uint32_t uLedgerMin; std::uint32_t uLedgerMax; std::uint32_t uValidatedMin; std::uint32_t uValidatedMax; bool bValidated = context.netOps_.getValidatedRange ( uValidatedMin, uValidatedMax); if (!bValidated) { // Don't have a validated ledger range. return rpcError (rpcLGR_IDXS_INVALID); } if (!params.isMember ("account")) return rpcError (rpcINVALID_PARAMS); if (!raAccount.setAccountID (params["account"].asString ())) return rpcError (rpcACT_MALFORMED); context.loadType_ = Resource::feeMediumBurdenRPC; if (params.isMember ("ledger_index_min") || params.isMember ("ledger_index_max")) { std::int64_t iLedgerMin = params.isMember ("ledger_index_min") ? params["ledger_index_min"].asInt () : -1; std::int64_t iLedgerMax = params.isMember ("ledger_index_max") ? params["ledger_index_max"].asInt () : -1; uLedgerMin = iLedgerMin == -1 ? uValidatedMin : iLedgerMin; uLedgerMax = iLedgerMax == -1 ? uValidatedMax : iLedgerMax; if (uLedgerMax < uLedgerMin) return rpcError (rpcLGR_IDXS_INVALID); } else { Ledger::pointer l; Json::Value ret = RPC::lookupLedger (params, l, context.netOps_); if (!l) return ret; uLedgerMin = uLedgerMax = l->getLedgerSeq (); } Json::Value resumeToken; if (params.isMember(jss::marker)) resumeToken = params[jss::marker]; #ifndef BEAST_DEBUG try { #endif Json::Value ret (Json::objectValue); ret["account"] = raAccount.humanAccountID (); Json::Value& jvTxns = (ret["transactions"] = Json::arrayValue); if (bBinary) { auto txns = context.netOps_.getTxsAccountB ( raAccount, uLedgerMin, uLedgerMax, bForward, resumeToken, limit, context.role_ == Config::ADMIN); for (auto& it: txns) { Json::Value& jvObj = jvTxns.append (Json::objectValue); std::uint32_t uLedgerIndex = std::get<2> (it); jvObj["tx_blob"] = std::get<0> (it); jvObj["meta"] = std::get<1> (it); jvObj["ledger_index"] = uLedgerIndex; jvObj[jss::validated] = bValidated && uValidatedMin <= uLedgerIndex && uValidatedMax >= uLedgerIndex; } } else { auto txns = context.netOps_.getTxsAccount ( raAccount, uLedgerMin, uLedgerMax, bForward, resumeToken, limit, context.role_ == Config::ADMIN); for (auto& it: txns) { Json::Value& jvObj = jvTxns.append (Json::objectValue); if (it.first) jvObj[jss::tx] = it.first->getJson (1); if (it.second) { std::uint32_t uLedgerIndex = it.second->getLgrSeq (); jvObj[jss::meta] = it.second->getJson (0); jvObj[jss::validated] = bValidated && uValidatedMin <= uLedgerIndex && uValidatedMax >= uLedgerIndex; } } } //Add information about the original query ret[jss::ledger_index_min] = uLedgerMin; ret[jss::ledger_index_max] = uLedgerMax; if (params.isMember (jss::limit)) ret[jss::limit] = limit; if (!resumeToken.isNull()) ret[jss::marker] = resumeToken; return ret; #ifndef BEAST_DEBUG } catch (...) { return rpcError (rpcINTERNAL); } #endif }
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; }