ledgerhash getledgerhash(ledger::pointer ledger, ledgerindex index) { ledgerhash hash; try { hash = ledger->getledgerhash(index); } catch (shamapmissingnode &) { m_journal.warning << "node missing from ledger " << ledger->getledgerseq(); getapp().getinboundledgers().findcreate ( ledger->gethash(), ledger->getledgerseq(), inboundledger::fcgeneric); } return 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 = (ledgerindex + 255) & (~255); 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().getledgermaster().findacquireledger (refindex, refhash); if (referenceledger) ledgerhash = getledgerhash(referenceledger, ledgerindex); } } } else m_journal.warning << "validated ledger is prior to target ledger"; return ledgerhash; }
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(); }
SLE::pointer getLedgerEntryRippleState(Ledger::pointer ledger, TestAccount const& account1, TestAccount const& account2, Currency currency) { auto uNodeIndex = getRippleStateIndex( account1.pk.getAccountID(), account2.pk.getAccountID(), to_currency(currency.getCurrency())); if (!uNodeIndex.isNonZero()) throw std::runtime_error( "!uNodeIndex.isNonZero()"); return ledger->getSLEi(uNodeIndex); }
/** 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; }
// 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 lookupLedgerDeprecated ( Ledger::pointer& ledger, Context& context, Json::Value& result) { if (auto status = ledgerFromRequest (ledger, context)) return status; auto& info = ledger->info(); if (!info.open) { result[jss::ledger_hash] = to_string (info.hash); result[jss::ledger_index] = info.seq; } else { result[jss::ledger_current_index] = info.seq; } result[jss::validated] = getApp().getLedgerMaster().isValidLedger(info); return Status::OK; }
/** 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; }
/** Fill in the fee on behalf of the client. This is called when the client does not explicitly specify the fee. The client may also put a ceiling on the amount of the fee. This ceiling is expressed as a multiplier based on the current ledger's fee schedule. JSON fields "Fee" The fee paid by the transaction. Omitted when the client wants the fee filled in. "fee_mult_max" A multiplier applied to the current ledger's transaction fee that caps the maximum the fee server should auto fill. If this optional field is not specified, then a default multiplier is used. @param tx The JSON corresponding to the transaction to fill in @param ledger A ledger for retrieving the current fee schedule @param result A JSON object for injecting error results, if any @param admin `true` if this is called by an administrative endpoint. */ static void autofill_fee (Json::Value& request, Ledger::pointer ledger, Json::Value& result, bool admin) { Json::Value& tx (request["tx_json"]); if (tx.isMember ("Fee")) return; int mult = DEFAULT_AUTO_FILL_FEE_MULTIPLIER; if (request.isMember ("fee_mult_max")) { if (request["fee_mult_max"].isNumeric ()) { mult = request["fee_mult_max"].asInt(); } else { RPC::inject_error (rpcHIGH_FEE, RPC::expected_field_message ( "fee_mult_max", "a number"), result); return; } } std::uint64_t const feeDefault = getConfig().FEE_DEFAULT; // Administrative endpoints are exempt from local fees std::uint64_t const fee = ledger->scaleFeeLoad (feeDefault, admin); std::uint64_t const limit = mult * feeDefault; if (fee > limit) { std::stringstream ss; ss << "Fee of " << fee << " exceeds the requested tx limit of " << limit; RPC::inject_error (rpcHIGH_FEE, ss.str(), result); return; } tx ["Fee"] = static_cast<int>(fee); }
void OrderBookDB::update (Ledger::pointer ledger) { hash_set< uint256 > seen; OrderBookDB::IssueToOrderBook destMap; OrderBookDB::IssueToOrderBook sourceMap; hash_set< Issue > XDVBooks; WriteLog (lsDEBUG, OrderBookDB) << "OrderBookDB::update>"; // walk through the entire ledger looking for orderbook entries int books = 0; try { ledger->visitStateItems(std::bind(&updateHelper, std::placeholders::_1, std::ref(seen), std::ref(destMap), std::ref(sourceMap), std::ref(XDVBooks), std::ref(books))); } catch (const SHAMapMissingNode&) { WriteLog (lsINFO, OrderBookDB) << "OrderBookDB::update encountered a missing node"; ScopedLockType sl (mLock); mSeq = 0; return; } WriteLog (lsDEBUG, OrderBookDB) << "OrderBookDB::update< " << books << " books found"; { ScopedLockType sl (mLock); mXDVBooks.swap(XDVBooks); mSourceMap.swap(sourceMap); mDestMap.swap(destMap); } getApp().getLedgerMaster().newOrderBookDB(); }
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; }
/** * 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); }
int main (){ std::string db_name = "/home/ivanzjj/radix_tree"; RocksdbInstance::set_db_name (db_name); Ledger::pointer ledger = std::make_shared <Ledger> (); std::string db_name2 = "/home/ivanzjj/ledger.db"; SqliteInstance::set_db_name (db_name2); uint256 hash; char hash_ch[32]; for (int i=0;i<32;i++) hash_ch[i] = i; hash.init (hash_ch); hash.to_string (); Serializer ss; char ch[100]; for (int i=0;i<100;i++) ch[i] = i; ss.add_raw (ch, 100); if (!ledger->add_account_tree_entry (hash, ss)){ printf ("add_account_tree_entry error!\n"); return 1; } // dfs (ledger->get_account_tree ()->get_root (), 0); for (int i = 0; i < 32; i++){ hash_ch[i] = i; } hash_ch[0] = 1; hash.init (hash_ch); hash.to_string (); if (!ledger->add_account_tree_entry (hash, ss)){ printf ("add_account_tree_entry error2\n"); return 1; } // dfs (ledger->get_account_tree ()->get_root (), 0); for (int i = 0; i < 32; i++) hash_ch[i] = i; hash_ch[0] = 1; hash_ch[1] = 2; hash.init (hash_ch); hash.to_string (); if (!ledger->add_account_tree_entry (hash, ss)){ printf ("add_account_tree_entry error2\n"); return 1; } // dfs (ledger->get_account_tree ()->get_root (), 0); for (int i = 0; i < 32; i++){ hash_ch[i] = i; } hash.init (hash_ch); if (ledger->has_account (hash)){ hash.to_string (); printf ("YES\n"); ch[0] = 10; ss.peek_data ().clear(); ss.add_raw (ch, 100); RadixMerkleTreeLeaf::pointer new_item = std::make_shared<RadixMerkleTreeLeaf> (hash, ss); if (!ledger->update_account_tree_entry (new_item)){ printf ("update_account_tree_entry error!\n"); return 1; } } else { printf ("NO\n"); } // dfs (ledger->get_account_tree ()->get_root (), 0); return 0; }
// { // account: <account>|<account_public_key> // account_index: <number> // optional, defaults to 0. // ledger_hash : <ledger> // ledger_index : <ledger_index> // limit: integer // optional // marker: opaque // optional, resume previous query // } Json::Value doAccountOffers (RPC::Context& context) { auto const& params (context.params); if (! params.isMember (jss::account)) return RPC::missing_field_error (jss::account); Ledger::pointer ledger; Json::Value result (RPC::lookupLedger (params, ledger, context.netOps)); if (! ledger) return result; std::string strIdent (params[jss::account].asString ()); bool bIndex (params.isMember (jss::account_index)); int const iIndex (bIndex ? params[jss::account_index].asUInt () : 0); DivvyAddress divvyAddress; Json::Value const jv = RPC::accountFromString ( divvyAddress, bIndex, strIdent, iIndex, false); if (! jv.empty ()) { for (Json::Value::const_iterator it (jv.begin ()); it != jv.end (); ++it) result[it.memberName ()] = it.key (); return result; } // Get info on account. result[jss::account] = divvyAddress.humanAccountID (); if (bIndex) result[jss::account_index] = iIndex; if (! ledger->exists(getAccountRootIndex( divvyAddress.getAccountID()))) return rpcError (rpcACT_NOT_FOUND); unsigned int limit; if (params.isMember (jss::limit)) { auto const& jvLimit (params[jss::limit]); if (! jvLimit.isIntegral ()) return RPC::expected_field_error (jss::limit, "unsigned integer"); limit = jvLimit.isUInt () ? jvLimit.asUInt () : std::max (0, jvLimit.asInt ()); if (context.role != Role::ADMIN) { limit = std::max (RPC::Tuning::minOffersPerRequest, std::min (limit, RPC::Tuning::maxOffersPerRequest)); } } else { limit = RPC::Tuning::defaultOffersPerRequest; } AccountID const& raAccount (divvyAddress.getAccountID ()); Json::Value& jsonOffers (result[jss::offers] = Json::arrayValue); std::vector <std::shared_ptr<SLE const>> offers; unsigned int reserve (limit); uint256 startAfter; std::uint64_t startHint; if (params.isMember(jss::marker)) { // We have a start point. Use limit - 1 from the result and use the // very last one for the resume. Json::Value const& marker (params[jss::marker]); if (! marker.isString ()) return RPC::expected_field_error (jss::marker, "string"); startAfter.SetHex (marker.asString ()); auto const sleOffer = fetch (*ledger, startAfter, getApp().getSLECache()); if (sleOffer == nullptr || sleOffer->getType () != ltOFFER || raAccount != sleOffer->getFieldAccount160 (sfAccount)) { return rpcError (rpcINVALID_PARAMS); } startHint = sleOffer->getFieldU64(sfOwnerNode); // Caller provided the first offer (startAfter), add it as first result Json::Value& obj (jsonOffers.append (Json::objectValue)); sleOffer->getFieldAmount (sfTakerPays).setJson (obj[jss::taker_pays]); sleOffer->getFieldAmount (sfTakerGets).setJson (obj[jss::taker_gets]); obj[jss::seq] = sleOffer->getFieldU32 (sfSequence); obj[jss::flags] = sleOffer->getFieldU32 (sfFlags); offers.reserve (reserve); } else { startHint = 0; // We have no start point, limit should be one higher than requested. offers.reserve (++reserve); } if (! forEachItemAfter(*ledger, raAccount, getApp().getSLECache(), startAfter, startHint, reserve, [&offers](std::shared_ptr<SLE const> const& offer) { if (offer->getType () == ltOFFER) { offers.emplace_back (offer); return true; } return false; })) { return rpcError (rpcINVALID_PARAMS); } if (offers.size () == reserve) { result[jss::limit] = limit; result[jss::marker] = to_string (offers.back ()->getIndex ()); offers.pop_back (); } for (auto const& offer : offers) { Json::Value& obj (jsonOffers.append (Json::objectValue)); offer->getFieldAmount (sfTakerPays).setJson (obj[jss::taker_pays]); offer->getFieldAmount (sfTakerGets).setJson (obj[jss::taker_gets]); obj[jss::seq] = offer->getFieldU32 (sfSequence); obj[jss::flags] = offer->getFieldU32 (sfFlags); } context.loadType = Resource::feeMediumBurdenRPC; return result; }
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 }
// { // transaction: <hex> // } Json::Value doTx (RPC::Context& context) { context.lock_.unlock (); if (!context.params_.isMember (jss::transaction)) return rpcError (rpcINVALID_PARAMS); bool binary = context.params_.isMember (jss::binary) && context.params_[jss::binary].asBool (); std::string strTransaction = context.params_[jss::transaction].asString (); if (Transaction::isHexTxID (strTransaction)) { // transaction by ID uint256 txid (strTransaction); Transaction::pointer txn = getApp().getMasterTransaction ().fetch (txid, true); if (!txn) return rpcError (rpcTXN_NOT_FOUND); #ifdef READY_FOR_NEW_TX_FORMAT Json::Value ret; ret[jss::transaction] = txn->getJson (0, binary); #else Json::Value ret = txn->getJson (0, binary); #endif if (txn->getLedger () != 0) { Ledger::pointer lgr = context.netOps_.getLedgerBySeq (txn->getLedger ()); if (lgr) { bool okay = false; if (binary) { std::string meta; if (lgr->getMetaHex (txid, meta)) { ret[jss::meta] = meta; okay = true; } } else { TransactionMetaSet::pointer set; if (lgr->getTransactionMeta (txid, set)) { okay = true; ret[jss::meta] = set->getJson (0); } } if (okay) ret[jss::validated] = context.netOps_.isValidated (lgr); } } return ret; } return rpcError (rpcNOT_IMPL); }
// 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>|<account_public_key> // account_index: <number> // optional, defaults to 0. // ledger_hash : <ledger> // ledger_index : <ledger_index> // limit: integer // optional // marker: opaque // optional, resume previous query // } Json::Value doAccountLines (RPC::Context& context) { auto const& params (context.params); if (! params.isMember (jss::account)) return RPC::missing_field_error (jss::account); Ledger::pointer ledger; Json::Value result (RPC::lookupLedger (params, ledger, context.netOps)); if (! ledger) return result; std::string strIdent (params[jss::account].asString ()); bool bIndex (params.isMember (jss::account_index)); int iIndex (bIndex ? params[jss::account_index].asUInt () : 0); DivvyAddress divvyAddress; auto jv = RPC::accountFromString ( divvyAddress, bIndex, strIdent, iIndex, false); if (! jv.empty ()) { for (auto it = jv.begin (); it != jv.end (); ++it) result[it.memberName ()] = it.key (); return result; } if (! ledger->exists(getAccountRootIndex( divvyAddress.getAccountID()))) return rpcError (rpcACT_NOT_FOUND); std::string strPeer (params.isMember (jss::peer) ? params[jss::peer].asString () : ""); bool bPeerIndex (params.isMember (jss::peer_index)); int iPeerIndex (bIndex ? params[jss::peer_index].asUInt () : 0); DivvyAddress divvyAddressPeer; if (! strPeer.empty ()) { result[jss::peer] = divvyAddress.humanAccountID (); if (bPeerIndex) result[jss::peer_index] = iPeerIndex; result = RPC::accountFromString ( divvyAddressPeer, bPeerIndex, strPeer, iPeerIndex, false); if (! result.empty ()) return result; } AccountID raPeerAccount; if (divvyAddressPeer.isValid ()) raPeerAccount = divvyAddressPeer.getAccountID (); unsigned int limit; if (params.isMember (jss::limit)) { auto const& jvLimit (params[jss::limit]); if (! jvLimit.isIntegral ()) return RPC::expected_field_error (jss::limit, "unsigned integer"); limit = jvLimit.isUInt () ? jvLimit.asUInt () : std::max (0, jvLimit.asInt ()); if (context.role != Role::ADMIN) { limit = std::max (RPC::Tuning::minLinesPerRequest, std::min (limit, RPC::Tuning::maxLinesPerRequest)); } } else { limit = RPC::Tuning::defaultLinesPerRequest; } Json::Value& jsonLines (result[jss::lines] = Json::arrayValue); AccountID const& raAccount(divvyAddress.getAccountID ()); VisitData visitData = { {}, raAccount, divvyAddressPeer, raPeerAccount }; unsigned int reserve (limit); uint256 startAfter; std::uint64_t startHint; if (params.isMember (jss::marker)) { // We have a start point. Use limit - 1 from the result and use the // very last one for the resume. Json::Value const& marker (params[jss::marker]); if (! marker.isString ()) return RPC::expected_field_error (jss::marker, "string"); startAfter.SetHex (marker.asString ()); auto const sleLine = fetch(*ledger, startAfter, getApp().getSLECache()); if (sleLine == nullptr || sleLine->getType () != ltRIPPLE_STATE) return rpcError (rpcINVALID_PARAMS); if (sleLine->getFieldAmount (sfLowLimit).getIssuer () == raAccount) startHint = sleLine->getFieldU64 (sfLowNode); else if (sleLine->getFieldAmount (sfHighLimit).getIssuer () == raAccount) startHint = sleLine->getFieldU64 (sfHighNode); else return rpcError (rpcINVALID_PARAMS); // Caller provided the first line (startAfter), add it as first result auto const line = DivvyState::makeItem (raAccount, sleLine); if (line == nullptr) return rpcError (rpcINVALID_PARAMS); addLine (jsonLines, *line); visitData.items.reserve (reserve); } else { startHint = 0; // We have no start point, limit should be one higher than requested. visitData.items.reserve (++reserve); } if (! forEachItemAfter(*ledger, raAccount, getApp().getSLECache(), startAfter, startHint, reserve, [&visitData](std::shared_ptr<SLE const> const& sleCur) { auto const line = DivvyState::makeItem (visitData.accountID, sleCur); if (line != nullptr && (! visitData.divvyAddressPeer.isValid () || visitData.raPeerAccount == line->getAccountIDPeer ())) { visitData.items.emplace_back (line); return true; } return false; })) { return rpcError (rpcINVALID_PARAMS); } if (visitData.items.size () == reserve) { result[jss::limit] = limit; DivvyState::pointer line (visitData.items.back ()); result[jss::marker] = to_string (line->key()); visitData.items.pop_back (); } result[jss::account] = divvyAddress.humanAccountID (); for (auto const& item : visitData.items) addLine (jsonLines, *item.get ()); context.loadType = Resource::feeMediumBurdenRPC; return result; }
// ledger [id|index|current|closed] [full] // { // ledger: 'current' | 'closed' | <uint256> | <number>, // optional // full: true | false // optional, defaults to false. // } Json::Value doLedger (RPC::Context& context) { if (!context.params_.isMember ("ledger") && !context.params_.isMember ("ledger_hash") && !context.params_.isMember ("ledger_index")) { Json::Value ret (Json::objectValue), current (Json::objectValue), closed (Json::objectValue); getApp().getLedgerMaster ().getCurrentLedger ()->addJson (current, 0); getApp().getLedgerMaster ().getClosedLedger ()->addJson (closed, 0); ret["open"] = current; ret["closed"] = closed; return ret; } Ledger::pointer lpLedger; Json::Value jvResult = RPC::lookupLedger ( context.params_, lpLedger, context.netOps_); if (!lpLedger) return jvResult; bool bFull = context.params_.isMember ("full") && context.params_["full"].asBool (); bool bTransactions = context.params_.isMember ("transactions") && context.params_["transactions"].asBool (); bool bAccounts = context.params_.isMember ("accounts") && context.params_["accounts"].asBool (); bool bExpand = context.params_.isMember ("expand") && context.params_["expand"].asBool (); int iOptions = (bFull ? LEDGER_JSON_FULL : 0) | (bExpand ? LEDGER_JSON_EXPAND : 0) | (bTransactions ? LEDGER_JSON_DUMP_TXRP : 0) | (bAccounts ? LEDGER_JSON_DUMP_STATE : 0); if (bFull || bAccounts) { if (context.role_ != Config::ADMIN) { // Until some sane way to get full ledgers has been implemented, // disallow retrieving all state nodes. return rpcError (rpcNO_PERMISSION); } if (getApp().getFeeTrack().isLoadedLocal() && context.role_ != Config::ADMIN) { WriteLog (lsDEBUG, Peer) << "Too busy to give full ledger"; return rpcError(rpcTOO_BUSY); } context.loadType_ = Resource::feeHighBurdenRPC; } Json::Value ret (Json::objectValue); lpLedger->addJson (ret, iOptions); return ret; }
// { // 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 }
Json::Value doAccountObjects (RPC::Context& context) { auto const& params = context.params; if (! params.isMember (jss::account)) return RPC::missing_field_error (jss::account); Ledger::pointer ledger; auto result = RPC::lookupLedger (params, ledger, context.netOps); if (ledger == nullptr) return result; DivvyAddress raAccount; { bool bIndex; auto const strIdent = params[jss::account].asString (); auto iIndex = context.params.isMember (jss::account_index) ? context.params[jss::account_index].asUInt () : 0; auto jv = RPC::accountFromString ( raAccount, bIndex, strIdent, iIndex, false); if (! jv.empty ()) { for (auto it = jv.begin (); it != jv.end (); ++it) result[it.memberName ()] = it.key (); return result; } } if (! ledger->exists(getAccountRootIndex( raAccount.getAccountID()))) return rpcError (rpcACT_NOT_FOUND); auto type = ltINVALID; if (params.isMember (jss::type)) { using filter_types = std::vector <std::pair <std::string, LedgerEntryType>>; static beast::static_initializer <filter_types> const types ([]() -> filter_types { return { { "account", ltACCOUNT_ROOT }, { "amendments", ltAMENDMENTS }, { "directory", ltDIR_NODE }, { "fee", ltFEE_SETTINGS }, { "hashes", ltLEDGER_HASHES }, { "offer", ltOFFER }, { "state", ltRIPPLE_STATE }, { "ticket", ltTICKET } }; }()); auto const& p = params[jss::type]; if (! p.isString ()) return RPC::expected_field_error (jss::type, "string"); auto const filter = p.asString (); auto iter = std::find_if (types->begin (), types->end (), [&filter](decltype (types->front ())& t) { return t.first == filter; }); if (iter == types->end ()) return RPC::invalid_field_error (jss::type); type = iter->second; } auto limit = RPC::Tuning::defaultObjectsPerRequest; if (params.isMember (jss::limit)) { auto const& jvLimit = params[jss::limit]; if (! jvLimit.isIntegral ()) return RPC::expected_field_error (jss::limit, "unsigned integer"); limit = jvLimit.isUInt () ? jvLimit.asUInt () : std::max (0, jvLimit.asInt ()); if (context.role != Role::ADMIN) { limit = std::max (RPC::Tuning::minObjectsPerRequest, std::min (limit, RPC::Tuning::maxObjectsPerRequest)); } } uint256 dirIndex; uint256 entryIndex; if (params.isMember (jss::marker)) { auto const& marker = params[jss::marker]; if (! marker.isString ()) return RPC::expected_field_error (jss::marker, "string"); std::stringstream ss (marker.asString ()); std::string s; if (!std::getline(ss, s, ',')) return RPC::invalid_field_error (jss::marker); if (! dirIndex.SetHex (s)) return RPC::invalid_field_error (jss::marker); if (! std::getline (ss, s, ',')) return RPC::invalid_field_error (jss::marker); if (! entryIndex.SetHex (s)) return RPC::invalid_field_error (jss::marker); } if (! RPC::getAccountObjects (*ledger, raAccount.getAccountID (), type, dirIndex, entryIndex, limit, result)) { return RPC::invalid_field_error (jss::marker); } result[jss::account] = raAccount.humanAccountID (); context.loadType = Resource::feeMediumBurdenRPC; return result; }
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; }
// { // account: <account>|<account_public_key> // account_index: <number> // optional, defaults to 0. // ledger_hash : <ledger> // ledger_index : <ledger_index> // limit: integer // optional // marker: opaque // optional, resume previous query // } Json::Value doAccountOffers (RPC::Context& context) { auto const& params (context.params_); Ledger::pointer ledger; Json::Value result (RPC::lookupLedger (params, ledger, context.netOps_)); if (! ledger) return result; if (! params.isMember (jss::account)) return RPC::missing_field_error ("account"); std::string strIdent (params[jss::account].asString ()); bool bIndex (params.isMember (jss::account_index)); int const iIndex (bIndex ? params[jss::account_index].asUInt () : 0); RippleAddress rippleAddress; result = RPC::accountFromString (ledger, rippleAddress, bIndex, strIdent, iIndex, false, context.netOps_); if (! result.empty ()) return result; // Get info on account. result[jss::account] = rippleAddress.humanAccountID (); if (bIndex) result[jss::account_index] = iIndex; if (! ledger->hasAccount (rippleAddress)) return rpcError (rpcACT_NOT_FOUND); unsigned int limit; if (params.isMember (jss::limit)) { limit = std::max (RPC::Tuning::minOffersPerRequest, std::min (params[jss::limit].asUInt (), RPC::Tuning::maxOffersPerRequest)); } else { limit = RPC::Tuning::defaultOffersPerRequest; } Account const& raAccount (rippleAddress.getAccountID ()); Json::Value& jsonOffers (result[jss::offers] = Json::arrayValue); std::vector <SLE::pointer> offers; unsigned int reserve (limit); uint256 startAfter; std::uint64_t startHint; if (params.isMember(jss::marker)) { // We have a start point. Use limit - 1 from the result and use the // very last one for the resume. Json::Value const& marker (params[jss::marker]); if (! marker.isString ()) return rpcError (rpcACT_MALFORMED); startAfter.SetHex (marker.asString ()); SLE::pointer sleOffer (ledger->getSLEi (startAfter)); if (sleOffer == nullptr || sleOffer->getType () != ltOFFER || raAccount != sleOffer->getFieldAccount160 (sfAccount)) { return rpcError (rpcINVALID_PARAMS); } startHint = sleOffer->getFieldU64(sfOwnerNode); // Caller provided the first offer (startAfter), add it as first result Json::Value& obj (jsonOffers.append (Json::objectValue)); sleOffer->getFieldAmount (sfTakerPays).setJson (obj[jss::taker_pays]); sleOffer->getFieldAmount (sfTakerGets).setJson (obj[jss::taker_gets]); obj[jss::seq] = sleOffer->getFieldU32 (sfSequence); obj[jss::flags] = sleOffer->getFieldU32 (sfFlags); offers.reserve (reserve); } else { startHint = 0; // We have no start point, limit should be one higher than requested. offers.reserve (++reserve); } if (! ledger->visitAccountItems (raAccount, startAfter, startHint, reserve, [&offers](SLE::ref offer) { if (offer->getType () == ltOFFER) { offers.emplace_back (offer); return true; } return false; })) { return rpcError (rpcINVALID_PARAMS); } if (offers.size () == reserve) { result[jss::limit] = limit; result[jss::marker] = to_string (offers.back ()->getIndex ()); offers.pop_back (); } for (auto const& offer : offers) { Json::Value& obj (jsonOffers.append (Json::objectValue)); offer->getFieldAmount (sfTakerPays).setJson (obj[jss::taker_pays]); offer->getFieldAmount (sfTakerGets).setJson (obj[jss::taker_gets]); obj[jss::seq] = offer->getFieldU32 (sfSequence); obj[jss::flags] = offer->getFieldU32 (sfFlags); } context.loadType_ = Resource::feeMediumBurdenRPC; return result; }
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"; }
// { // 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 }
// { // transaction: <hex> // } Json::Value RPCHandler::doTx (Json::Value params, Resource::Charge& loadType, Application::ScopedLockType& masterLockHolder) { masterLockHolder.unlock (); if (!params.isMember ("transaction")) return rpcError (rpcINVALID_PARAMS); bool binary = params.isMember ("binary") && params["binary"].asBool (); std::string strTransaction = params["transaction"].asString (); if (Transaction::isHexTxID (strTransaction)) { // transaction by ID uint256 txid (strTransaction); Transaction::pointer txn = getApp().getMasterTransaction ().fetch (txid, true); if (!txn) return rpcError (rpcTXN_NOT_FOUND); #ifdef READY_FOR_NEW_TX_FORMAT Json::Value ret; ret["transaction"] = txn->getJson (0, binary); #else Json::Value ret = txn->getJson (0, binary); #endif if (txn->getLedger () != 0) { Ledger::pointer lgr = mNetOps->getLedgerBySeq (txn->getLedger ()); if (lgr) { bool okay = false; if (binary) { std::string meta; if (lgr->getMetaHex (txid, meta)) { ret["meta"] = meta; okay = true; } } else { TransactionMetaSet::pointer set; if (lgr->getTransactionMeta (txid, set)) { okay = true; ret["meta"] = set->getJson (0); } } if (okay) ret["validated"] = mNetOps->isValidated (lgr); } } return ret; } return rpcError (rpcNOT_IMPL); }