Pathfinder::Pathfinder (RippleLineCache::ref cache, const RippleAddress& uSrcAccountID, const RippleAddress& uDstAccountID, const uint160& uSrcCurrencyID, const uint160& uSrcIssuerID, const STAmount& saDstAmount, bool& bValid) : mSrcAccountID (uSrcAccountID.getAccountID ()), mDstAccountID (uDstAccountID.getAccountID ()), mDstAmount (saDstAmount), mSrcCurrencyID (uSrcCurrencyID), mSrcIssuerID (uSrcIssuerID), mSrcAmount (uSrcCurrencyID, uSrcIssuerID, 1u, 0, true), mLedger (cache->getLedger ()), mRLCache (cache) { if (((mSrcAccountID == mDstAccountID) && (mSrcCurrencyID == mDstAmount.getCurrency ())) || mDstAmount.isZero ()) { // no need to send to same account with same currency, must send non-zero bValid = false; mLedger.reset (); return; } bValid = true; m_loadEvent = getApp().getJobQueue ().getLoadEvent (jtPATH_FIND, "FindPath"); bool bIssuer = mSrcCurrencyID.isNonZero() && mSrcIssuerID.isNonZero() && (mSrcIssuerID != mSrcAccountID); mSource = STPathElement( // Where does an empty path start? bIssuer ? mSrcIssuerID : mSrcAccountID, // On the source account or issuer account mSrcCurrencyID, // In the source currency mSrcCurrencyID.isZero() ? uint160() : (bIssuer ? mSrcIssuerID : mSrcAccountID)); }
CurrencySet accountDestCurrencies ( RippleAddress const& raAccountID, RippleLineCache::ref lrCache, bool includeXRP) { CurrencySet currencies; if (includeXRP) currencies.insert (xrpCurrency()); // Even if account doesn't exist // List of ripple lines. auto& rippleLines (lrCache->getRippleLines (raAccountID.getAccountID ())); for (auto const& item : rippleLines) { auto rspEntry = (RippleState*) item.get (); assert (rspEntry); if (!rspEntry) continue; auto& saBalance = rspEntry->getBalance (); if (saBalance < rspEntry->getLimit ()) // Can take more currencies.insert (saBalance.getCurrency ()); } currencies.erase (badCurrency()); return currencies; }
bool LedgerFacade::findPathsForOneIssuer ( RippleAddress const& dstAccountID, Issue const& srcIssue, STAmount const& dstAmount, int searchLevel, unsigned int const maxPaths, STPathSet& pathsOut, STPath& fullLiquidityPath) const { if (!ledger_) // Unit testing. // Note that unit tests don't (yet) need pathsOut or fullLiquidityPath. return true; auto cache = std::make_shared<RippleLineCache> (ledger_); return ripple::findPathsForOneIssuer ( cache, accountID_.getAccountID (), dstAccountID.getAccountID (), srcIssue, dstAmount, searchLevel, maxPaths, pathsOut, fullLiquidityPath); }
bool to_issuer(Account& issuer, std::string const& s) { if (s.size () == (160 / 4)) { issuer.SetHex (s); return true; } RippleAddress address; bool success = address.setAccountID (s); if (success) issuer = address.getAccountID (); return success; }
AccountState::AccountState (RippleAddress const& naAccountID) : mAccountID (naAccountID) , mValid (false) { if (naAccountID.isValid ()) { mValid = true; mLedgerEntry = std::make_shared <STLedgerEntry> ( ltACCOUNT_ROOT, getAccountRootIndex (naAccountID)); mLedgerEntry->setFieldAccount (sfAccount, naAccountID.getAccountID ()); } }
CurrencySet accountSourceCurrencies ( RippleAddress const& raAccountID, RippleLineCache::ref lrCache, bool includeXRP) { CurrencySet currencies; // YYY Only bother if they are above reserve if (includeXRP) currencies.insert (xrpCurrency()); // List of ripple lines. auto& rippleLines (lrCache->getRippleLines (raAccountID.getAccountID ())); for (auto const& item : rippleLines) { auto rspEntry = (RippleState*) item.get (); assert (rspEntry); if (!rspEntry) continue; auto& saBalance = rspEntry->getBalance (); // Filter out non if (saBalance > zero // Have IOUs to send. || (rspEntry->getLimitPeer () // Peer extends credit. && ((-saBalance) < rspEntry->getLimitPeer ()))) // Credit left. { currencies.insert (saBalance.getCurrency ()); } } currencies.erase (badCurrency()); return currencies; }
Json::Value doAccountCurrencies (RPC::Context& context) { auto& params = context.params; // Get the current ledger Ledger::pointer ledger; Json::Value result (RPC::lookupLedger (params, ledger, context.netOps)); if (!ledger) return result; if (!(params.isMember (jss::account) || params.isMember (jss::ident))) return RPC::missing_field_error (jss::account); std::string const strIdent (params.isMember (jss::account) ? params[jss::account].asString () : params[jss::ident].asString ()); int iIndex = 0; if (params.isMember (jss::account_index)) { auto const& accountIndex = params[jss::account_index]; if (!accountIndex.isUInt() && !accountIndex.isInt ()) return RPC::invalid_field_message (jss::account_index); iIndex = accountIndex.asUInt (); } bool const bStrict = params.isMember (jss::strict) && params[jss::strict].asBool (); // Get info on account. bool bIndex; // out param RippleAddress naAccount; // out param Json::Value jvAccepted (RPC::accountFromString ( ledger, naAccount, bIndex, strIdent, iIndex, bStrict, context.netOps)); if (!jvAccepted.empty ()) return jvAccepted; std::set<Currency> send, receive; for (auto const& item : getRippleStateItems (naAccount.getAccountID (), ledger)) { RippleState* rspEntry = (RippleState*) item.get (); STAmount const& saBalance = rspEntry->getBalance (); if (saBalance < rspEntry->getLimit ()) receive.insert (saBalance.getCurrency ()); if ((-saBalance) < rspEntry->getLimitPeer ()) send.insert (saBalance.getCurrency ()); } send.erase (badCurrency()); receive.erase (badCurrency()); Json::Value& sendCurrencies = (result[jss::send_currencies] = Json::arrayValue); for (auto const& c: send) sendCurrencies.append (to_string (c)); Json::Value& recvCurrencies = (result[jss::receive_currencies] = Json::arrayValue); for (auto const& c: receive) recvCurrencies.append (to_string (c)); return result; }
// { // 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; }
Json::Value doBookOffers (RPC::Context& context) { // VFALCO TODO Here is a terrible place for this kind of business // logic. It needs to be moved elsewhere and documented, // and encapsulated into a function. if (getApp().getJobQueue ().getJobCountGE (jtCLIENT) > 200) return rpcError (rpcTOO_BUSY); Ledger::pointer lpLedger; Json::Value jvResult ( RPC::lookupLedger (context.params_, lpLedger, context.netOps_)); if (!lpLedger) return jvResult; if (!context.params_.isMember ("taker_pays")) return RPC::missing_field_error ("taker_pays"); if (!context.params_.isMember ("taker_gets")) return RPC::missing_field_error ("taker_gets"); if (!context.params_["taker_pays"].isObject ()) return RPC::object_field_error ("taker_pays"); if (!context.params_["taker_gets"].isObject ()) return RPC::object_field_error ("taker_gets"); Json::Value const& taker_pays (context.params_["taker_pays"]); if (!taker_pays.isMember ("currency")) return RPC::missing_field_error ("taker_pays.currency"); if (! taker_pays ["currency"].isString ()) return RPC::expected_field_error ("taker_pays.currency", "string"); Json::Value const& taker_gets = context.params_["taker_gets"]; if (! taker_gets.isMember ("currency")) return RPC::missing_field_error ("taker_gets.currency"); if (! taker_gets ["currency"].isString ()) return RPC::expected_field_error ("taker_gets.currency", "string"); Currency pay_currency; if (!to_currency (pay_currency, taker_pays ["currency"].asString ())) { WriteLog (lsINFO, RPCHandler) << "Bad taker_pays currency."; return RPC::make_error (rpcSRC_CUR_MALFORMED, "Invalid field 'taker_pays.currency', bad currency."); } Currency get_currency; if (!to_currency (get_currency, taker_gets ["currency"].asString ())) { WriteLog (lsINFO, RPCHandler) << "Bad taker_gets currency."; return RPC::make_error (rpcDST_AMT_MALFORMED, "Invalid field 'taker_gets.currency', bad currency."); } Account pay_issuer; if (taker_pays.isMember ("issuer")) { if (! taker_pays ["issuer"].isString()) return RPC::expected_field_error ("taker_pays.issuer", "string"); if (!to_issuer( pay_issuer, taker_pays ["issuer"].asString ())) return RPC::make_error (rpcSRC_ISR_MALFORMED, "Invalid field 'taker_pays.issuer', bad issuer."); if (pay_issuer == noAccount ()) return RPC::make_error (rpcSRC_ISR_MALFORMED, "Invalid field 'taker_pays.issuer', bad issuer account one."); } else { pay_issuer = xrpAccount (); } if (isXRP (pay_currency) && ! isXRP (pay_issuer)) return RPC::make_error ( rpcSRC_ISR_MALFORMED, "Unneeded field 'taker_pays.issuer' for " "XRP currency specification."); if (!isXRP (pay_currency) && isXRP (pay_issuer)) return RPC::make_error (rpcSRC_ISR_MALFORMED, "Invalid field 'taker_pays.issuer', expected non-XRP issuer."); Account get_issuer; if (taker_gets.isMember ("issuer")) { if (! taker_gets ["issuer"].isString()) return RPC::expected_field_error ("taker_gets.issuer", "string"); if (! to_issuer ( get_issuer, taker_gets ["issuer"].asString ())) return RPC::make_error (rpcDST_ISR_MALFORMED, "Invalid field 'taker_gets.issuer', bad issuer."); if (get_issuer == noAccount ()) return RPC::make_error (rpcDST_ISR_MALFORMED, "Invalid field 'taker_gets.issuer', bad issuer account one."); } else { get_issuer = xrpAccount (); } if (isXRP (get_currency) && ! isXRP (get_issuer)) return RPC::make_error (rpcDST_ISR_MALFORMED, "Unneeded field 'taker_gets.issuer' for " "XRP currency specification."); if (!isXRP (get_currency) && isXRP (get_issuer)) return RPC::make_error (rpcDST_ISR_MALFORMED, "Invalid field 'taker_gets.issuer', expected non-XRP issuer."); RippleAddress raTakerID; if (context.params_.isMember ("taker")) { if (! context.params_ ["taker"].isString ()) return RPC::expected_field_error ("taker", "string"); if (! raTakerID.setAccountID (context.params_ ["taker"].asString ())) return RPC::invalid_field_error ("taker"); } else { raTakerID.setAccountID (noAccount()); } if (pay_currency == get_currency && pay_issuer == get_issuer) { WriteLog (lsINFO, RPCHandler) << "taker_gets same as taker_pays."; return RPC::make_error (rpcBAD_MARKET); } unsigned int iLimit; if (context.params_.isMember (jss::limit)) { auto const& jvLimit (context.params_[jss::limit]); if (! jvLimit.isIntegral ()) return RPC::expected_field_error ("limit", "unsigned integer"); iLimit = jvLimit.isUInt () ? jvLimit.asUInt () : std::max (0, jvLimit.asInt ()); } else { iLimit = 0; } bool const bProof (context.params_.isMember ("proof")); Json::Value const jvMarker (context.params_.isMember ("marker") ? context.params_["marker"] : Json::Value (Json::nullValue)); context.netOps_.getBookPage ( context.role_ == Config::ADMIN, lpLedger, {{pay_currency, pay_issuer}, {get_currency, get_issuer}}, raTakerID.getAccountID (), bProof, iLimit, jvMarker, jvResult); context.loadType_ = Resource::feeMediumBurdenRPC; return jvResult; }
Json::Value doGatewayBalances (RPC::Context& context) { auto& params = context.params; // Get the current ledger Ledger::pointer ledger; Json::Value result (RPC::lookupLedger (params, ledger, context.netOps)); if (!ledger) return result; if (!(params.isMember (jss::account) || params.isMember (jss::ident))) return RPC::missing_field_error (jss::account); std::string const strIdent (params.isMember (jss::account) ? params[jss::account].asString () : params[jss::ident].asString ()); int iIndex = 0; if (params.isMember (jss::account_index)) { auto const& accountIndex = params[jss::account_index]; if (!accountIndex.isUInt() && !accountIndex.isInt ()) return RPC::invalid_field_message (jss::account_index); iIndex = accountIndex.asUInt (); } bool const bStrict = params.isMember (jss::strict) && params[jss::strict].asBool (); // Get info on account. bool bIndex; // out param RippleAddress naAccount; // out param Json::Value jvAccepted (RPC::accountFromString ( naAccount, bIndex, strIdent, iIndex, bStrict)); if (!jvAccepted.empty ()) return jvAccepted; context.loadType = Resource::feeHighBurdenRPC; result[jss::account] = naAccount.humanAccountID(); auto accountID = naAccount.getAccountID(); // Parse the specified hotwallet(s), if any std::set <AccountID> hotWallets; if (params.isMember ("hotwallet")) { Json::Value const& hw = params["hotwallet"]; bool valid = true; auto addHotWallet = [&valid, &hotWallets](Json::Value const& j) { if (j.isString()) { RippleAddress ra; if (! ra.setAccountPublic (j.asString ()) && ! ra.setAccountID (j.asString())) { valid = false; } else hotWallets.insert (ra.getAccountID ()); } else { valid = false; } }; if (hw.isArray()) { for (unsigned i = 0; i < hw.size(); ++i) addHotWallet (hw[i]); } else if (hw.isString()) { addHotWallet (hw); } else { valid = false; } if (! valid) { result[jss::error] = "invalidHotWallet"; return result; } } std::map <Currency, STAmount> sums; std::map <AccountID, std::vector <STAmount>> hotBalances; std::map <AccountID, std::vector <STAmount>> assets; // Traverse the cold wallet's trust lines forEachItem(*ledger, accountID, getApp().getSLECache(), [&](std::shared_ptr<SLE const> const& sle) { auto rs = RippleState::makeItem (accountID, sle); if (!rs) return; int balSign = rs->getBalance().signum(); if (balSign == 0) return; auto const& peer = rs->getAccountIDPeer(); // Here, a negative balance means the cold wallet owes (normal) // A positive balance means the cold wallet has an asset (unusual) if (hotWallets.count (peer) > 0) { // This is a specified hot wallt hotBalances[peer].push_back (-rs->getBalance ()); } else if (balSign > 0) { // This is a gateway asset assets[peer].push_back (rs->getBalance ()); } else { // normal negative balance, obligation to customer auto& bal = sums[rs->getBalance().getCurrency()]; if (bal == zero) { // This is needed to set the currency code correctly bal = -rs->getBalance(); } else bal -= rs->getBalance(); } }); if (! sums.empty()) { Json::Value& j = (result [jss::obligations] = Json::objectValue); for (auto const& e : sums) { j[to_string (e.first)] = e.second.getText (); } } if (! hotBalances.empty()) { Json::Value& j = (result [jss::balances] = Json::objectValue); for (auto const& account : hotBalances) { Json::Value& balanceArray = (j[to_string (account.first)] = Json::arrayValue); for (auto const& balance : account.second) { Json::Value& entry = balanceArray.append (Json::objectValue); entry[jss::currency] = to_string (balance.issue ().currency); entry[jss::value] = balance.getText(); } } } if (! assets.empty()) { Json::Value& j = (result [jss::assets] = Json::objectValue); for (auto const& account : assets) { Json::Value& balanceArray = (j[to_string (account.first)] = Json::arrayValue); for (auto const& balance : account.second) { Json::Value& entry = balanceArray.append (Json::objectValue); entry[jss::currency] = to_string (balance.issue ().currency); entry[jss::value] = balance.getText(); } } } return result; }
Json::Value doAccountCurrencies (RPC::Context& context) { auto& params = context.params_; // Get the current ledger Ledger::pointer ledger; Json::Value result (RPC::lookupLedger (params, ledger, context.netOps_)); if (!ledger) return result; if (!(params.isMember ("account") || params.isMember ("ident"))) return RPC::missing_field_error ("account"); std::string const strIdent (params.isMember ("account") ? params["account"].asString () : params["ident"].asString ()); int const iIndex (params.isMember ("account_index") ? params["account_index"].asUInt () : 0); bool const bStrict = params.isMember ("strict") && params["strict"].asBool (); // Get info on account. bool bIndex; // out param RippleAddress naAccount; // out param Json::Value jvAccepted (RPC::accountFromString ( ledger, naAccount, bIndex, strIdent, iIndex, bStrict, context.netOps_)); if (!jvAccepted.empty ()) return jvAccepted; std::set<Currency> send, receive; AccountItems rippleLines ( naAccount.getAccountID (), ledger, AccountItem::pointer (new RippleState ())); for (auto item: rippleLines.getItems ()) { RippleState* rspEntry = (RippleState*) item.get (); STAmount const& saBalance = rspEntry->getBalance (); if (saBalance < rspEntry->getLimit ()) receive.insert (saBalance.getCurrency ()); if ((-saBalance) < rspEntry->getLimitPeer ()) send.insert (saBalance.getCurrency ()); } send.erase (badCurrency()); receive.erase (badCurrency()); Json::Value& sendCurrencies = (result["send_currencies"] = Json::arrayValue); for (auto const& c: send) sendCurrencies.append (to_string (c)); Json::Value& recvCurrencies = (result["receive_currencies"] = Json::arrayValue); for (auto const& c: receive) recvCurrencies.append (to_string (c)); return result; }
// VFALCO TODO This function should take a reference to the params, modify it // as needed, and then there should be a separate function to // submit the transaction // Json::Value transactionSign ( Json::Value params, bool bSubmit, bool bFailHard, Application::ScopedLockType& mlh, NetworkOPs& netOps, int role) { Json::Value jvResult; WriteLog (lsDEBUG, RPCHandler) << "transactionSign: " << params; if (! params.isMember ("secret")) return RPC::missing_field_error ("secret"); if (! params.isMember ("tx_json")) return RPC::missing_field_error ("tx_json"); RippleAddress naSeed; if (! naSeed.setSeedGeneric (params["secret"].asString ())) return RPC::make_error (rpcBAD_SEED, RPC::invalid_field_message ("secret")); Json::Value& tx_json (params ["tx_json"]); if (! tx_json.isObject ()) return RPC::object_field_error ("tx_json"); if (! tx_json.isMember ("TransactionType")) return RPC::missing_field_error ("tx_json.TransactionType"); std::string const sType = tx_json ["TransactionType"].asString (); if (! tx_json.isMember ("Account")) return RPC::make_error (rpcSRC_ACT_MISSING, RPC::missing_field_message ("tx_json.Account")); RippleAddress raSrcAddressID; if (! raSrcAddressID.setAccountID (tx_json["Account"].asString ())) return RPC::make_error (rpcSRC_ACT_MALFORMED, RPC::invalid_field_message ("tx_json.Account")); bool const verify = !(params.isMember ("offline") && params["offline"].asBool ()); if (!tx_json.isMember ("Sequence") && !verify) return RPC::missing_field_error ("tx_json.Sequence"); // Check for current ledger if (verify && !getConfig ().RUN_STANDALONE && (getApp().getLedgerMaster().getValidatedLedgerAge() > 120)) return rpcError (rpcNO_CURRENT); // Check for load if (getApp().getFeeTrack().isLoadedCluster() && (role != Config::ADMIN)) return rpcError(rpcTOO_BUSY); Ledger::pointer lSnapshot = netOps.getCurrentLedger (); AccountState::pointer asSrc; if (verify) { asSrc = netOps.getAccountState (lSnapshot, raSrcAddressID); if (!asSrc) { // If not offline and did not find account, error. WriteLog (lsDEBUG, RPCHandler) << "transactionSign: Failed to find source account in current ledger: " << raSrcAddressID.humanAccountID (); return rpcError (rpcSRC_ACT_NOT_FOUND); } } autofill_fee (params, lSnapshot, jvResult, role == Config::ADMIN); if (RPC::contains_error (jvResult)) return jvResult; if ("Payment" == sType) { auto e = signPayment(params, tx_json, raSrcAddressID, lSnapshot, role); if (contains_error(e)) return e; } if ("Genesis" == sType) { auto e = signGenesis(params, tx_json, raSrcAddressID, lSnapshot, role); if (contains_error(e)) return e; } if ("Transfer" == sType) { auto e = signTransfer(params, tx_json, raSrcAddressID, lSnapshot, role); if (contains_error(e)) return e; } if ("AccountCreate" == sType) { auto e = signAccountCreate(params, tx_json, raSrcAddressID, lSnapshot, role); if (contains_error(e)) return e; } if (!tx_json.isMember ("Fee")) { auto const& transactionType = tx_json["TransactionType"].asString (); if ("AccountSet" == transactionType || "OfferCreate" == transactionType || "OfferCancel" == transactionType || "TrustSet" == transactionType) { tx_json["Fee"] = (int) getConfig ().FEE_DEFAULT; } } if (!tx_json.isMember ("Sequence")) tx_json["Sequence"] = asSrc->getSeq (); if (!tx_json.isMember ("Flags")) tx_json["Flags"] = tfFullyCanonicalSig; if (verify) { SLE::pointer sleAccountRoot = netOps.getSLEi (lSnapshot, Ledger::getAccountRootIndex (raSrcAddressID.getAccountID ())); if (!sleAccountRoot) // XXX Ignore transactions for accounts not created. return rpcError (rpcSRC_ACT_NOT_FOUND); } RippleAddress naSecret = RippleAddress::createSeedGeneric (params["secret"].asString ()); RippleAddress masterAccountPublic = RippleAddress::createAccountPublic(naSecret); if (verify) { auto account = masterAccountPublic.getAccountID(); auto const& sle = asSrc->peekSLE(); WriteLog (lsWARNING, RPCHandler) << "verify: " << masterAccountPublic.humanAccountID () << " : " << raSrcAddressID.humanAccountID (); if (raSrcAddressID.getAccountID () == account) { if (sle.isFlag(lsfDisableMaster) && "Inflation" != sType) return rpcError (rpcMASTER_DISABLED); } else if (!sle.isFieldPresent(sfRegularKey) || account != sle.getFieldAccount160 (sfRegularKey)) { return rpcError (rpcBAD_SECRET); } } STParsedJSON parsed ("tx_json", tx_json); if (!parsed.object.get()) { jvResult ["error"] = parsed.error ["error"]; jvResult ["error_code"] = parsed.error ["error_code"]; jvResult ["error_message"] = parsed.error ["error_message"]; return jvResult; } std::unique_ptr<STObject> sopTrans = std::move(parsed.object); sopTrans->setFieldVL (sfSigningPubKey, masterAccountPublic.getAccountPublic ()); SerializedTransaction::pointer stpTrans; try { stpTrans = boost::make_shared<SerializedTransaction> (*sopTrans); } catch (std::exception&) { return RPC::make_error (rpcINTERNAL, "Exception occurred during transaction"); } std::string reason; if (!passesLocalChecks (*stpTrans, reason)) return RPC::make_error (rpcINVALID_PARAMS, reason); if (params.isMember ("debug_signing")) { jvResult["tx_unsigned"] = strHex ( stpTrans->getSerializer ().peekData ()); jvResult["tx_signing_hash"] = to_string (stpTrans->getSigningHash ()); } // FIXME: For performance, transactions should not be signed in this code path. RippleAddress naAccountPrivate = RippleAddress::createAccountPrivate (naSecret); stpTrans->sign (naAccountPrivate); Transaction::pointer tpTrans; tpTrans = getApp().getMasterTransaction().fetch(stpTrans->getTransactionID(), false); if (tpTrans) { TER res = tpTrans->getResult(); if (!(isTelLocal(res) || isTemMalformed(res) || isTefFailure(res))) { tpTrans = Transaction::pointer(); } } if (!tpTrans) { try { tpTrans = boost::make_shared<Transaction> (stpTrans, false); } catch (std::exception&) { return RPC::make_error (rpcINTERNAL, "Exception occurred during transaction"); } try { // FIXME: For performance, should use asynch interface tpTrans = netOps.submitTransactionSync (tpTrans, role == Config::ADMIN, true, bFailHard, bSubmit); if (!tpTrans) { return RPC::make_error (rpcINTERNAL, "Unable to sterilize transaction."); } } catch (std::exception&) { return RPC::make_error (rpcINTERNAL, "Exception occurred during transaction submission."); } } try { jvResult["tx_json"] = tpTrans->getJson (0); jvResult["tx_blob"] = strHex ( tpTrans->getSTransaction ()->getSerializer ().peekData ()); if (temUNCERTAIN != tpTrans->getResult ()) { std::string sToken; std::string sHuman; transResultInfo (tpTrans->getResult (), sToken, sHuman); jvResult["engine_result"] = sToken; jvResult["engine_result_code"] = tpTrans->getResult (); jvResult["engine_result_message"] = sHuman; } return jvResult; } catch (std::exception&) { return RPC::make_error (rpcINTERNAL, "Exception occurred during JSON handling."); } }
// This interface is deprecated. Json::Value doRipplePathFind (RPC::Context& context) { RPC::LegacyPathFind lpf (context.role == Role::ADMIN); if (!lpf.isOk ()) return rpcError (rpcTOO_BUSY); context.loadType = Resource::feeHighBurdenRPC; RippleAddress raSrc; RippleAddress raDst; STAmount saDstAmount; Ledger::pointer lpLedger; Json::Value jvResult; if (getConfig().RUN_STANDALONE || context.params.isMember(jss::ledger) || context.params.isMember(jss::ledger_index) || context.params.isMember(jss::ledger_hash)) { // The caller specified a ledger jvResult = RPC::lookupLedger ( context.params, lpLedger, context.netOps); if (!lpLedger) return jvResult; } if (!context.params.isMember ("source_account")) { jvResult = rpcError (rpcSRC_ACT_MISSING); } else if (!context.params["source_account"].isString () || !raSrc.setAccountID ( context.params["source_account"].asString ())) { jvResult = rpcError (rpcSRC_ACT_MALFORMED); } else if (!context.params.isMember ("destination_account")) { jvResult = rpcError (rpcDST_ACT_MISSING); } else if (!context.params["destination_account"].isString () || !raDst.setAccountID ( context.params["destination_account"].asString ())) { jvResult = rpcError (rpcDST_ACT_MALFORMED); } else if ( // Parse saDstAmount. !context.params.isMember ("destination_amount") || ! amountFromJsonNoThrow(saDstAmount, context.params["destination_amount"]) || saDstAmount <= zero || (!isXRP(saDstAmount.getCurrency ()) && (!saDstAmount.getIssuer () || noAccount() == saDstAmount.getIssuer ()))) { WriteLog (lsINFO, RPCHandler) << "Bad destination_amount."; jvResult = rpcError (rpcINVALID_PARAMS); } else if ( // Checks on source_currencies. context.params.isMember ("source_currencies") && (!context.params["source_currencies"].isArray () || !context.params["source_currencies"].size ()) // Don't allow empty currencies. ) { WriteLog (lsINFO, RPCHandler) << "Bad source_currencies."; jvResult = rpcError (rpcINVALID_PARAMS); } else { context.loadType = Resource::feeHighBurdenRPC; RippleLineCache::pointer cache; if (lpLedger) { // The caller specified a ledger lpLedger = std::make_shared<Ledger> (std::ref (*lpLedger), false); cache = std::make_shared<RippleLineCache>(lpLedger); } else { // The closed ledger is recent and any nodes made resident // have the best chance to persist lpLedger = context.netOps.getClosedLedger(); cache = getApp().getPathRequests().getLineCache(lpLedger, false); } Json::Value jvSrcCurrencies; if (context.params.isMember ("source_currencies")) { jvSrcCurrencies = context.params["source_currencies"]; } else { auto currencies = accountSourceCurrencies (raSrc, cache, true); jvSrcCurrencies = Json::Value (Json::arrayValue); for (auto const& uCurrency: currencies) { Json::Value jvCurrency (Json::objectValue); jvCurrency["currency"] = to_string(uCurrency); jvSrcCurrencies.append (jvCurrency); } } // Fill in currencies destination will accept Json::Value jvDestCur (Json::arrayValue); // TODO(tom): this could be optimized the same way that // PathRequest::doUpdate() is - if we don't obsolete this code first. auto usDestCurrID = accountDestCurrencies (raDst, cache, true); for (auto const& uCurrency: usDestCurrID) jvDestCur.append (to_string (uCurrency)); jvResult["destination_currencies"] = jvDestCur; jvResult["destination_account"] = raDst.humanAccountID (); Json::Value jvArray (Json::arrayValue); int level = getConfig().PATH_SEARCH_OLD; if ((getConfig().PATH_SEARCH_MAX > level) && !getApp().getFeeTrack().isLoadedLocal()) { ++level; } if (context.params.isMember("search_depth") && context.params["search_depth"].isIntegral()) { int rLev = context.params["search_depth"].asInt (); if ((rLev < level) || (context.role == Role::ADMIN)) level = rLev; } FindPaths fp ( cache, raSrc.getAccountID(), raDst.getAccountID(), saDstAmount, level, 4); // max paths for (unsigned int i = 0; i != jvSrcCurrencies.size (); ++i) { Json::Value jvSource = jvSrcCurrencies[i]; Currency uSrcCurrencyID; Account uSrcIssuerID; if (!jvSource.isObject ()) return rpcError (rpcINVALID_PARAMS); // Parse mandatory currency. if (!jvSource.isMember ("currency") || !to_currency ( uSrcCurrencyID, jvSource["currency"].asString ())) { WriteLog (lsINFO, RPCHandler) << "Bad currency."; return rpcError (rpcSRC_CUR_MALFORMED); } if (uSrcCurrencyID.isNonZero ()) uSrcIssuerID = raSrc.getAccountID (); // Parse optional issuer. if (jvSource.isMember ("issuer") && ((!jvSource["issuer"].isString () || !to_issuer (uSrcIssuerID, jvSource["issuer"].asString ())) || (uSrcIssuerID.isZero () != uSrcCurrencyID.isZero ()) || (noAccount() == uSrcIssuerID))) { WriteLog (lsINFO, RPCHandler) << "Bad issuer."; return rpcError (rpcSRC_ISR_MALFORMED); } STPathSet spsComputed; if (context.params.isMember("paths")) { Json::Value pathSet = Json::objectValue; pathSet["Paths"] = context.params["paths"]; STParsedJSONObject paths ("pathSet", pathSet); if (paths.object.get() == nullptr) return paths.error; else { spsComputed = paths.object.get()->getFieldPathSet (sfPaths); WriteLog (lsTRACE, RPCHandler) << "ripple_path_find: Paths: " << spsComputed.getJson (0); } } STPath fullLiquidityPath; auto valid = fp.findPathsForIssue ( {uSrcCurrencyID, uSrcIssuerID}, spsComputed, fullLiquidityPath); if (!valid) { WriteLog (lsWARNING, RPCHandler) << "ripple_path_find: No paths found."; } else { auto& issuer = isXRP (uSrcIssuerID) ? isXRP (uSrcCurrencyID) ? // Default to source account. xrpAccount() : Account (raSrc.getAccountID ()) : uSrcIssuerID; // Use specifed issuer. STAmount saMaxAmount ({uSrcCurrencyID, issuer}, 1); saMaxAmount.negate (); LedgerEntrySet lesSandbox (lpLedger, tapNONE); auto rc = path::RippleCalc::rippleCalculate ( lesSandbox, saMaxAmount, // --> Amount to send is unlimited // to get an estimate. saDstAmount, // --> Amount to deliver. raDst.getAccountID (), // --> Account to deliver to. raSrc.getAccountID (), // --> Account sending from. spsComputed); // --> Path set. WriteLog (lsWARNING, RPCHandler) << "ripple_path_find:" << " saMaxAmount=" << saMaxAmount << " saDstAmount=" << saDstAmount << " saMaxAmountAct=" << rc.actualAmountIn << " saDstAmountAct=" << rc.actualAmountOut; if (fullLiquidityPath.size() > 0 && (rc.result() == terNO_LINE || rc.result() == tecPATH_PARTIAL)) { WriteLog (lsDEBUG, PathRequest) << "Trying with an extra path element"; spsComputed.push_back (fullLiquidityPath); lesSandbox.clear (); rc = path::RippleCalc::rippleCalculate ( lesSandbox, saMaxAmount, // --> Amount to send is unlimited // to get an estimate. saDstAmount, // --> Amount to deliver. raDst.getAccountID (), // --> Account to deliver to. raSrc.getAccountID (), // --> Account sending from. spsComputed); // --> Path set. WriteLog (lsDEBUG, PathRequest) << "Extra path element gives " << transHuman (rc.result ()); } if (rc.result () == tesSUCCESS) { Json::Value jvEntry (Json::objectValue); STPathSet spsCanonical; // Reuse the expanded as it would need to be calcuated // anyway to produce the canonical. (At least unless we // make a direct canonical.) jvEntry["source_amount"] = rc.actualAmountIn.getJson (0); jvEntry["paths_canonical"] = Json::arrayValue; jvEntry["paths_computed"] = spsComputed.getJson (0); jvArray.append (jvEntry); } else { std::string strToken; std::string strHuman; transResultInfo (rc.result (), strToken, strHuman); WriteLog (lsDEBUG, RPCHandler) << "ripple_path_find: " << strToken << " " << strHuman << " " << spsComputed.getJson (0); } } } // Each alternative differs by source currency. jvResult["alternatives"] = jvArray; } WriteLog (lsDEBUG, RPCHandler) << boost::str (boost::format ("ripple_path_find< %s") % jvResult); return jvResult; }
// TODO(tom): what is that "default"? Json::Value doAccountInfo (RPC::Context& context) { auto& params = context.params; Ledger::pointer ledger; Json::Value result = RPC::lookupLedger (params, ledger, context.netOps); if (!ledger) return result; if (!params.isMember (jss::account) && !params.isMember (jss::ident)) return RPC::missing_field_error (jss::account); std::string strIdent = params.isMember (jss::account) ? params[jss::account].asString () : params[jss::ident].asString (); bool bIndex; int iIndex = params.isMember (jss::account_index) ? params[jss::account_index].asUInt () : 0; bool bStrict = params.isMember (jss::strict) && params[jss::strict].asBool (); RippleAddress naAccount; // Get info on account. auto jvAccepted = RPC::accountFromString ( naAccount, bIndex, strIdent, iIndex, bStrict); if (!jvAccepted.empty ()) return jvAccepted; AccountState::pointer asAccepted = getAccountState(*ledger, naAccount, getApp().getSLECache()); if (asAccepted) { asAccepted->addJson (jvAccepted); // See if there's a SignerEntries for this account. AccountID const account = naAccount.getAccountID (); uint256 const signerListIndex = getSignerListIndex (account); auto const signerList = fetch(*ledger, signerListIndex, getApp().getSLECache()); if (signerList) { // Return multi-signing information if there are multi-signers. static const Json::StaticString multiSignersName("multisigners"); jvAccepted[multiSignersName] = signerList->getJson (0); Json::Value& multiSignerJson = jvAccepted[multiSignersName]; // Remove unwanted fields. multiSignerJson.removeMember (sfFlags.getName ()); multiSignerJson.removeMember (sfLedgerEntryType.getName ()); multiSignerJson.removeMember (sfOwnerNode.getName ()); multiSignerJson.removeMember ("index"); } result[jss::account_data] = jvAccepted; } else { result[jss::account] = naAccount.humanAccountID (); RPC::inject_error (rpcACT_NOT_FOUND, result); } return result; }
Json::Value RPCHandler::doSubscribe (Json::Value params, Resource::Charge& loadType, Application::ScopedLockType& masterLockHolder) { // FIXME: This needs to release the master lock immediately // Subscriptions need to be protected by their own lock InfoSub::pointer ispSub; Json::Value jvResult (Json::objectValue); std::uint32_t uLedgerIndex = params.isMember (jss::ledger_index) && params[jss::ledger_index].isNumeric () ? params[jss::ledger_index].asUInt () : 0; if (!mInfoSub && !params.isMember ("url")) { // Must be a JSON-RPC call. WriteLog (lsINFO, RPCHandler) << boost::str (boost::format ("doSubscribe: RPC subscribe requires a url")); return rpcError (rpcINVALID_PARAMS); } if (params.isMember ("url")) { if (mRole != Config::ADMIN) return rpcError (rpcNO_PERMISSION); std::string strUrl = params["url"].asString (); std::string strUsername = params.isMember ("url_username") ? params["url_username"].asString () : ""; std::string strPassword = params.isMember ("url_password") ? params["url_password"].asString () : ""; // DEPRECATED if (params.isMember ("username")) strUsername = params["username"].asString (); // DEPRECATED if (params.isMember ("password")) strPassword = params["password"].asString (); ispSub = mNetOps->findRpcSub (strUrl); if (!ispSub) { WriteLog (lsDEBUG, RPCHandler) << boost::str (boost::format ("doSubscribe: building: %s") % strUrl); RPCSub::pointer rspSub = RPCSub::New (getApp ().getOPs (), getApp ().getIOService (), getApp ().getJobQueue (), strUrl, strUsername, strPassword); ispSub = mNetOps->addRpcSub (strUrl, boost::dynamic_pointer_cast<InfoSub> (rspSub)); } else { WriteLog (lsTRACE, RPCHandler) << boost::str (boost::format ("doSubscribe: reusing: %s") % strUrl); if (params.isMember ("username")) dynamic_cast<RPCSub*> (&*ispSub)->setUsername (strUsername); if (params.isMember ("password")) dynamic_cast<RPCSub*> (&*ispSub)->setPassword (strPassword); } } else { ispSub = mInfoSub; } if (!params.isMember ("streams")) { nothing (); } else if (!params["streams"].isArray ()) { WriteLog (lsINFO, RPCHandler) << boost::str (boost::format ("doSubscribe: streams requires an array.")); return rpcError (rpcINVALID_PARAMS); } else { for (Json::Value::iterator it = params["streams"].begin (); it != params["streams"].end (); it++) { if ((*it).isString ()) { std::string streamName = (*it).asString (); if (streamName == "server") { mNetOps->subServer (ispSub, jvResult); } else if (streamName == "ledger") { mNetOps->subLedger (ispSub, jvResult); } else if (streamName == "transactions") { mNetOps->subTransactions (ispSub); } else if (streamName == "transactions_rt" ) { mNetOps->subRTTransactions (ispSub); } else { jvResult[jss::error] = "unknownStream"; } } else { jvResult[jss::error] = "malformedStream"; } } } std::string strAccountsProposed = params.isMember ("accounts_rt") ? "accounts_proposed" : "rt_accounts"; // DEPRECATED if (!params.isMember (strAccountsProposed)) { nothing (); } else if (!params[strAccountsProposed].isArray ()) { return rpcError (rpcINVALID_PARAMS); } else { boost::unordered_set<RippleAddress> usnaAccoundIds = RPC::parseAccountIds (params[strAccountsProposed]); if (usnaAccoundIds.empty ()) { jvResult[jss::error] = "malformedAccount"; } else { mNetOps->subAccount (ispSub, usnaAccoundIds, uLedgerIndex, true); } } if (!params.isMember ("accounts")) { nothing (); } else if (!params["accounts"].isArray ()) { return rpcError (rpcINVALID_PARAMS); } else { boost::unordered_set<RippleAddress> usnaAccoundIds = RPC::parseAccountIds (params["accounts"]); if (usnaAccoundIds.empty ()) { jvResult[jss::error] = "malformedAccount"; } else { mNetOps->subAccount (ispSub, usnaAccoundIds, uLedgerIndex, false); WriteLog (lsDEBUG, RPCHandler) << boost::str (boost::format ("doSubscribe: accounts: %d") % usnaAccoundIds.size ()); } } bool bHaveMasterLock = true; if (!params.isMember ("books")) { nothing (); } else if (!params["books"].isArray ()) { return rpcError (rpcINVALID_PARAMS); } else { for (Json::Value::iterator it = params["books"].begin (); it != params["books"].end (); it++) { Json::Value& jvSubRequest = *it; if (!jvSubRequest.isObject () || !jvSubRequest.isMember (jss::taker_pays) || !jvSubRequest.isMember (jss::taker_gets) || !jvSubRequest[jss::taker_pays].isObject () || !jvSubRequest[jss::taker_gets].isObject ()) return rpcError (rpcINVALID_PARAMS); // VFALCO TODO Use RippleAsset here RippleCurrency pay_currency; RippleIssuer pay_issuer; RippleCurrency get_currency; RippleIssuer get_issuer; bool bBoth = (jvSubRequest.isMember ("both") && jvSubRequest["both"].asBool ()) || (jvSubRequest.isMember ("both_sides") && jvSubRequest["both_sides"].asBool ()); // DEPRECATED bool bSnapshot = (jvSubRequest.isMember ("snapshot") && jvSubRequest["snapshot"].asBool ()) || (jvSubRequest.isMember ("state_now") && jvSubRequest["state_now"].asBool ()); // DEPRECATED Json::Value taker_pays = jvSubRequest[jss::taker_pays]; Json::Value taker_gets = jvSubRequest[jss::taker_gets]; // Parse mandatory currency. if (!taker_pays.isMember (jss::currency) || !STAmount::currencyFromString (pay_currency, taker_pays[jss::currency].asString ())) { WriteLog (lsINFO, RPCHandler) << "Bad taker_pays currency."; return rpcError (rpcSRC_CUR_MALFORMED); } // Parse optional issuer. else if (((taker_pays.isMember (jss::issuer)) && (!taker_pays[jss::issuer].isString () || !STAmount::issuerFromString (pay_issuer, taker_pays[jss::issuer].asString ()))) // Don't allow illegal issuers. || (!pay_currency != !pay_issuer) || ACCOUNT_ONE == pay_issuer) { WriteLog (lsINFO, RPCHandler) << "Bad taker_pays issuer."; return rpcError (rpcSRC_ISR_MALFORMED); } // Parse mandatory currency. if (!taker_gets.isMember (jss::currency) || !STAmount::currencyFromString (get_currency, taker_gets[jss::currency].asString ())) { WriteLog (lsINFO, RPCHandler) << "Bad taker_pays currency."; return rpcError (rpcSRC_CUR_MALFORMED); } // Parse optional issuer. else if (((taker_gets.isMember (jss::issuer)) && (!taker_gets[jss::issuer].isString () || !STAmount::issuerFromString (get_issuer, taker_gets[jss::issuer].asString ()))) // Don't allow illegal issuers. || (!get_currency != !get_issuer) || ACCOUNT_ONE == get_issuer) { WriteLog (lsINFO, RPCHandler) << "Bad taker_gets issuer."; return rpcError (rpcDST_ISR_MALFORMED); } if (pay_currency == get_currency && pay_issuer == get_issuer) { WriteLog (lsINFO, RPCHandler) << "taker_gets same as taker_pays."; return rpcError (rpcBAD_MARKET); } RippleAddress raTakerID; if (!jvSubRequest.isMember ("taker")) { raTakerID.setAccountID (ACCOUNT_ONE); } else if (!raTakerID.setAccountID (jvSubRequest["taker"].asString ())) { return rpcError (rpcBAD_ISSUER); } if (!Ledger::isValidBook (pay_currency, pay_issuer, get_currency, get_issuer)) { WriteLog (lsWARNING, RPCHandler) << "Bad market: " << pay_currency << ":" << pay_issuer << " -> " << get_currency << ":" << get_issuer; return rpcError (rpcBAD_MARKET); } mNetOps->subBook (ispSub, pay_currency, get_currency, pay_issuer, get_issuer); if (bBoth) mNetOps->subBook (ispSub, get_currency, pay_currency, get_issuer, pay_issuer); if (bSnapshot) { if (bHaveMasterLock) { masterLockHolder.unlock (); bHaveMasterLock = false; } bool verbose = false; if (jvSubRequest.isMember("verbose")) verbose = true; loadType = Resource::feeMediumBurdenRPC; Ledger::pointer lpLedger = getApp().getLedgerMaster ().getPublishedLedger (); if (lpLedger) { const Json::Value jvMarker = Json::Value (Json::nullValue); if (bBoth) { Json::Value jvBids (Json::objectValue); Json::Value jvAsks (Json::objectValue); mNetOps->getBookPage (lpLedger, pay_currency, pay_issuer, get_currency, get_issuer, raTakerID.getAccountID (), false, verbose, 0, jvMarker, jvBids); if (jvBids.isMember (jss::offers)) jvResult[jss::bids] = jvBids[jss::offers]; mNetOps->getBookPage(lpLedger, get_currency, get_issuer, pay_currency, pay_issuer, raTakerID.getAccountID(), false, verbose, 0, jvMarker, jvAsks); if (jvAsks.isMember (jss::offers)) jvResult[jss::asks] = jvAsks[jss::offers]; } else { mNetOps->getBookPage(lpLedger, pay_currency, pay_issuer, get_currency, get_issuer, raTakerID.getAccountID(), false, verbose, 0, jvMarker, jvResult); } } } } } return jvResult; }
Json::Value doSubscribe (RPC::Context& context) { auto lock = getApp().masterLock(); // FIXME: This needs to release the master lock immediately // Subscriptions need to be protected by their own lock InfoSub::pointer ispSub; Json::Value jvResult (Json::objectValue); std::uint32_t uLedgerIndex = context.params_.isMember (jss::ledger_index) && context.params_[jss::ledger_index].isNumeric () ? context.params_[jss::ledger_index].asUInt () : 0; if (!context.infoSub_ && !context.params_.isMember ("url")) { // Must be a JSON-RPC call. WriteLog (lsINFO, RPCHandler) << "doSubscribe: RPC subscribe requires a url"; return rpcError (rpcINVALID_PARAMS); } if (context.params_.isMember ("url")) { if (context.role_ != Config::ADMIN) return rpcError (rpcNO_PERMISSION); std::string strUrl = context.params_["url"].asString (); std::string strUsername = context.params_.isMember ("url_username") ? context.params_["url_username"].asString () : ""; std::string strPassword = context.params_.isMember ("url_password") ? context.params_["url_password"].asString () : ""; // DEPRECATED if (context.params_.isMember ("username")) strUsername = context.params_["username"].asString (); // DEPRECATED if (context.params_.isMember ("password")) strPassword = context.params_["password"].asString (); ispSub = context.netOps_.findRpcSub (strUrl); if (!ispSub) { WriteLog (lsDEBUG, RPCHandler) << "doSubscribe: building: " << strUrl; RPCSub::pointer rspSub = RPCSub::New (getApp ().getOPs (), getApp ().getIOService (), getApp ().getJobQueue (), strUrl, strUsername, strPassword); ispSub = context.netOps_.addRpcSub ( strUrl, std::dynamic_pointer_cast<InfoSub> (rspSub)); } else { WriteLog (lsTRACE, RPCHandler) << "doSubscribe: reusing: " << strUrl; if (context.params_.isMember ("username")) dynamic_cast<RPCSub*> (&*ispSub)->setUsername (strUsername); if (context.params_.isMember ("password")) dynamic_cast<RPCSub*> (&*ispSub)->setPassword (strPassword); } } else { ispSub = context.infoSub_; } if (!context.params_.isMember ("streams")) { } else if (!context.params_["streams"].isArray ()) { WriteLog (lsINFO, RPCHandler) << "doSubscribe: streams requires an array."; return rpcError (rpcINVALID_PARAMS); } else { for (auto& it: context.params_["streams"]) { if (it.isString ()) { std::string streamName = it.asString (); if (streamName == "server") { context.netOps_.subServer (ispSub, jvResult, context.role_ == Config::ADMIN); } else if (streamName == "ledger") { context.netOps_.subLedger (ispSub, jvResult); } else if (streamName == "transactions") { context.netOps_.subTransactions (ispSub); } else if (streamName == "transactions_proposed" || streamName == "rt_transactions") // DEPRECATED { context.netOps_.subRTTransactions (ispSub); } else { jvResult[jss::error] = "unknownStream"; } } else { jvResult[jss::error] = "malformedStream"; } } } std::string strAccountsProposed = context.params_.isMember ("accounts_proposed") ? "accounts_proposed" : "rt_accounts"; // DEPRECATED if (!context.params_.isMember (strAccountsProposed)) { } else if (!context.params_[strAccountsProposed].isArray ()) { return rpcError (rpcINVALID_PARAMS); } else { auto ids = RPC::parseAccountIds (context.params_[strAccountsProposed]); if (ids.empty ()) jvResult[jss::error] = "malformedAccount"; else context.netOps_.subAccount (ispSub, ids, uLedgerIndex, true); } if (!context.params_.isMember ("accounts")) { } else if (!context.params_["accounts"].isArray ()) { return rpcError (rpcINVALID_PARAMS); } else { auto ids = RPC::parseAccountIds (context.params_["accounts"]); if (ids.empty ()) { jvResult[jss::error] = "malformedAccount"; } else { context.netOps_.subAccount (ispSub, ids, uLedgerIndex, false); WriteLog (lsDEBUG, RPCHandler) << "doSubscribe: accounts: " << ids.size (); } } bool bHaveMasterLock = true; if (!context.params_.isMember ("books")) { } else if (!context.params_["books"].isArray ()) { return rpcError (rpcINVALID_PARAMS); } else { for (auto& j: context.params_["books"]) { if (!j.isObject () || !j.isMember (jss::taker_pays) || !j.isMember (jss::taker_gets) || !j[jss::taker_pays].isObject () || !j[jss::taker_gets].isObject ()) return rpcError (rpcINVALID_PARAMS); Book book; bool bBoth = (j.isMember ("both") && j["both"].asBool ()) || (j.isMember ("both_sides") && j["both_sides"].asBool ()); bool bSnapshot = (j.isMember ("snapshot") && j["snapshot"].asBool ()) || (j.isMember ("state_now") && j["state_now"].asBool ()); // TODO(tom): both_sides and state_now are apparently deprecated... // where is this documented? Json::Value taker_pays = j[jss::taker_pays]; Json::Value taker_gets = j[jss::taker_gets]; // Parse mandatory currency. if (!taker_pays.isMember (jss::currency) || !to_currency (book.in.currency, taker_pays[jss::currency].asString ())) { WriteLog (lsINFO, RPCHandler) << "Bad taker_pays currency."; return rpcError (rpcSRC_CUR_MALFORMED); } // Parse optional issuer. else if (((taker_pays.isMember (jss::issuer)) && (!taker_pays[jss::issuer].isString () || !to_issuer (book.in.account, taker_pays[jss::issuer].asString ()))) // Don't allow illegal issuers. || (!book.in.currency != !book.in.account) || noAccount() == book.in.account) { WriteLog (lsINFO, RPCHandler) << "Bad taker_pays issuer."; return rpcError (rpcSRC_ISR_MALFORMED); } // Parse mandatory currency. if (!taker_gets.isMember (jss::currency) || !to_currency (book.out.currency, taker_gets[jss::currency].asString ())) { WriteLog (lsINFO, RPCHandler) << "Bad taker_pays currency."; return rpcError (rpcSRC_CUR_MALFORMED); } // Parse optional issuer. else if (((taker_gets.isMember (jss::issuer)) && (!taker_gets[jss::issuer].isString () || !to_issuer (book.out.account, taker_gets[jss::issuer].asString ()))) // Don't allow illegal issuers. || (!book.out.currency != !book.out.account) || noAccount() == book.out.account) { WriteLog (lsINFO, RPCHandler) << "Bad taker_gets issuer."; return rpcError (rpcDST_ISR_MALFORMED); } if (book.in.currency == book.out.currency && book.in.account == book.out.account) { WriteLog (lsINFO, RPCHandler) << "taker_gets same as taker_pays."; return rpcError (rpcBAD_MARKET); } RippleAddress raTakerID; if (!j.isMember ("taker")) raTakerID.setAccountID (noAccount()); else if (!raTakerID.setAccountID (j["taker"].asString ())) return rpcError (rpcBAD_ISSUER); if (!isConsistent (book) || !isConsistentVBC(book)) { WriteLog (lsWARNING, RPCHandler) << "Bad market: " << book; return rpcError (rpcBAD_MARKET); } context.netOps_.subBook (ispSub, book); if (bBoth) context.netOps_.subBook (ispSub, book); if (bSnapshot) { if (bHaveMasterLock) { lock->unlock (); bHaveMasterLock = false; } context.loadType_ = Resource::feeMediumBurdenRPC; auto lpLedger = getApp().getLedgerMaster (). getPublishedLedger (); if (lpLedger) { const Json::Value jvMarker = Json::Value (Json::nullValue); if (bBoth) { Json::Value jvBids (Json::objectValue); Json::Value jvAsks (Json::objectValue); context.netOps_.getBookPage ( lpLedger, book, raTakerID.getAccountID (), false, 0, jvMarker, jvBids); if (jvBids.isMember (jss::offers)) jvResult[jss::bids] = jvBids[jss::offers]; context.netOps_.getBookPage ( lpLedger, book, raTakerID.getAccountID (), false, 0, jvMarker, jvAsks); if (jvAsks.isMember (jss::offers)) jvResult[jss::asks] = jvAsks[jss::offers]; } else { context.netOps_.getBookPage ( lpLedger, book, raTakerID.getAccountID (), false, 0, jvMarker, jvResult); } } } } } return jvResult; }
std::pair<bool, Json::Value> ripplePathFind(RippleLineCache::pointer const& cache, RippleAddress const& raSrc, RippleAddress const& raDst, STAmount const& saDstAmount, Ledger::pointer const& lpLedger, Json::Value const& jvSrcCurrencies, boost::optional<Json::Value> const& contextPaths, int const& level) { FindPaths fp( cache, raSrc.getAccountID(), raDst.getAccountID(), saDstAmount, level, 4); // max paths Json::Value jvArray(Json::arrayValue); for (unsigned int i = 0; i != jvSrcCurrencies.size(); ++i) { Json::Value jvSource = jvSrcCurrencies[i]; Currency uSrcCurrencyID; Account uSrcIssuerID; if (!jvSource.isObject()) return std::make_pair(false, rpcError(rpcINVALID_PARAMS)); // Parse mandatory currency. if (!jvSource.isMember(jss::currency) || !to_currency( uSrcCurrencyID, jvSource[jss::currency].asString())) { WriteLog(lsINFO, RPCHandler) << "Bad currency."; return std::make_pair(false, rpcError(rpcSRC_CUR_MALFORMED)); } if (uSrcCurrencyID.isNonZero()) uSrcIssuerID = raSrc.getAccountID(); // Parse optional issuer. if (jvSource.isMember(jss::issuer) && ((!jvSource[jss::issuer].isString() || !to_issuer(uSrcIssuerID, jvSource[jss::issuer].asString())) || (uSrcIssuerID.isZero() != uSrcCurrencyID.isZero()) || (noAccount() == uSrcIssuerID))) { WriteLog(lsINFO, RPCHandler) << "Bad issuer."; return std::make_pair(false, rpcError(rpcSRC_ISR_MALFORMED)); } STPathSet spsComputed; if (contextPaths) { Json::Value pathSet = Json::objectValue; pathSet[jss::Paths] = contextPaths.get(); STParsedJSONObject paths("pathSet", pathSet); if (paths.object.get() == nullptr) return std::make_pair(false, paths.error); else { spsComputed = paths.object.get()->getFieldPathSet(sfPaths); WriteLog(lsTRACE, RPCHandler) << "ripple_path_find: Paths: " << spsComputed.getJson(0); } } STPath fullLiquidityPath; auto valid = fp.findPathsForIssue( { uSrcCurrencyID, uSrcIssuerID }, spsComputed, fullLiquidityPath); if (!valid) { WriteLog(lsWARNING, RPCHandler) << "ripple_path_find: No paths found."; } else { auto& issuer = isXRP(uSrcIssuerID) ? isXRP(uSrcCurrencyID) ? // Default to source account. xrpAccount() : Account(raSrc.getAccountID()) : uSrcIssuerID; // Use specifed issuer. STAmount saMaxAmount({ uSrcCurrencyID, issuer }, 1); saMaxAmount.negate(); LedgerEntrySet lesSandbox(lpLedger, tapNONE); auto rc = path::RippleCalc::rippleCalculate( lesSandbox, saMaxAmount, // --> Amount to send is unlimited // to get an estimate. saDstAmount, // --> Amount to deliver. raDst.getAccountID(), // --> Account to deliver to. raSrc.getAccountID(), // --> Account sending from. spsComputed); // --> Path set. WriteLog(lsWARNING, RPCHandler) << "ripple_path_find:" << " saMaxAmount=" << saMaxAmount << " saDstAmount=" << saDstAmount << " saMaxAmountAct=" << rc.actualAmountIn << " saDstAmountAct=" << rc.actualAmountOut; if (fullLiquidityPath.size() > 0 && (rc.result() == terNO_LINE || rc.result() == tecPATH_PARTIAL)) { WriteLog(lsDEBUG, PathRequest) << "Trying with an extra path element"; spsComputed.push_back(fullLiquidityPath); lesSandbox.clear(); rc = path::RippleCalc::rippleCalculate( lesSandbox, saMaxAmount, // --> Amount to send is unlimited // to get an estimate. saDstAmount, // --> Amount to deliver. raDst.getAccountID(), // --> Account to deliver to. raSrc.getAccountID(), // --> Account sending from. spsComputed); // --> Path set. WriteLog(lsDEBUG, PathRequest) << "Extra path element gives " << transHuman(rc.result()); } if (rc.result() == tesSUCCESS) { Json::Value jvEntry(Json::objectValue); STPathSet spsCanonical; // Reuse the expanded as it would need to be calcuated // anyway to produce the canonical. (At least unless we // make a direct canonical.) jvEntry[jss::source_amount] = rc.actualAmountIn.getJson(0); jvEntry[jss::paths_canonical] = Json::arrayValue; jvEntry[jss::paths_computed] = spsComputed.getJson(0); jvArray.append(jvEntry); } else { std::string strToken; std::string strHuman; transResultInfo(rc.result(), strToken, strHuman); WriteLog(lsDEBUG, RPCHandler) << "ripple_path_find: " << strToken << " " << strHuman << " " << spsComputed.getJson(0); } } } return std::make_pair(true, jvArray); }
// VFALCO TODO This function should take a reference to the params, modify it // as needed, and then there should be a separate function to // submit the transaction. // Json::Value transactionSign ( Json::Value params, bool bSubmit, bool bFailHard, RPCDetail::LedgerFacade& ledgerFacade, Role role) { Json::Value jvResult; WriteLog (lsDEBUG, RPCHandler) << "transactionSign: " << params; if (! params.isMember ("secret")) return RPC::missing_field_error ("secret"); if (! params.isMember ("tx_json")) return RPC::missing_field_error ("tx_json"); RippleAddress naSeed; if (! naSeed.setSeedGeneric (params["secret"].asString ())) return RPC::make_error (rpcBAD_SEED, RPC::invalid_field_message ("secret")); Json::Value& tx_json (params ["tx_json"]); if (! tx_json.isObject ()) return RPC::object_field_error ("tx_json"); if (! tx_json.isMember ("TransactionType")) return RPC::missing_field_error ("tx_json.TransactionType"); std::string const sType = tx_json ["TransactionType"].asString (); if (! tx_json.isMember ("Account")) return RPC::make_error (rpcSRC_ACT_MISSING, RPC::missing_field_message ("tx_json.Account")); RippleAddress raSrcAddressID; if (! raSrcAddressID.setAccountID (tx_json["Account"].asString ())) return RPC::make_error (rpcSRC_ACT_MALFORMED, RPC::invalid_field_message ("tx_json.Account")); bool const verify = !(params.isMember ("offline") && params["offline"].asBool ()); if (!tx_json.isMember ("Sequence") && !verify) return RPC::missing_field_error ("tx_json.Sequence"); // Check for current ledger. if (verify && !getConfig ().RUN_STANDALONE && (ledgerFacade.getValidatedLedgerAge () > 120)) return rpcError (rpcNO_CURRENT); // Check for load. if (ledgerFacade.isLoadedCluster () && (role != Role::ADMIN)) return rpcError (rpcTOO_BUSY); ledgerFacade.snapshotAccountState (raSrcAddressID); if (verify) { if (!ledgerFacade.isValidAccount ()) { // If not offline and did not find account, error. WriteLog (lsDEBUG, RPCHandler) << "transactionSign: Failed to find source account " << "in current ledger: " << raSrcAddressID.humanAccountID (); return rpcError (rpcSRC_ACT_NOT_FOUND); } } autofill_fee (params, ledgerFacade, jvResult, role == Role::ADMIN); if (RPC::contains_error (jvResult)) return jvResult; if ("Payment" == sType) { auto e = signPayment( params, tx_json, raSrcAddressID, ledgerFacade, role); if (contains_error(e)) return e; } if (!tx_json.isMember ("Sequence")) tx_json["Sequence"] = ledgerFacade.getSeq (); if (!tx_json.isMember ("Flags")) tx_json["Flags"] = tfFullyCanonicalSig; if (verify) { if (!ledgerFacade.hasAccountRoot ()) // XXX Ignore transactions for accounts not created. return rpcError (rpcSRC_ACT_NOT_FOUND); } RippleAddress secret = RippleAddress::createSeedGeneric ( params["secret"].asString ()); RippleAddress masterGenerator = RippleAddress::createGeneratorPublic ( secret); RippleAddress masterAccountPublic = RippleAddress::createAccountPublic ( masterGenerator, 0); if (verify) { WriteLog (lsTRACE, RPCHandler) << "verify: " << masterAccountPublic.humanAccountID () << " : " << raSrcAddressID.humanAccountID (); auto const secretAccountID = masterAccountPublic.getAccountID(); if (raSrcAddressID.getAccountID () == secretAccountID) { if (ledgerFacade.accountMasterDisabled ()) return rpcError (rpcMASTER_DISABLED); } else if (!ledgerFacade.accountMatchesRegularKey (secretAccountID)) { return rpcError (rpcBAD_SECRET); } } STParsedJSONObject parsed ("tx_json", tx_json); if (!parsed.object.get()) { jvResult ["error"] = parsed.error ["error"]; jvResult ["error_code"] = parsed.error ["error_code"]; jvResult ["error_message"] = parsed.error ["error_message"]; return jvResult; } std::unique_ptr<STObject> sopTrans = std::move(parsed.object); sopTrans->setFieldVL ( sfSigningPubKey, masterAccountPublic.getAccountPublic ()); STTx::pointer stpTrans; try { stpTrans = std::make_shared<STTx> (*sopTrans); //WriteLog(lsINFO, RPCHandler) << "radar: before sign " << stpTrans->getFieldAmount(sfAmount); } catch (std::exception&) { return RPC::make_error (rpcINTERNAL, "Exception occurred during transaction"); } std::string reason; if (!passesLocalChecks (*stpTrans, reason)) return RPC::make_error (rpcINVALID_PARAMS, reason); if (params.isMember ("debug_signing")) { jvResult["tx_unsigned"] = strHex ( stpTrans->getSerializer ().peekData ()); jvResult["tx_signing_hash"] = to_string (stpTrans->getSigningHash ()); } // FIXME: For performance, transactions should not be signed in this code // path. RippleAddress naAccountPrivate = RippleAddress::createAccountPrivate ( masterGenerator, secret, 0); stpTrans->sign (naAccountPrivate); Transaction::pointer tpTrans; try { //WriteLog(lsINFO, RPCHandler) << "radar: after sign " << stpTrans->getFieldAmount(sfAmount); tpTrans = std::make_shared<Transaction>(stpTrans, Validate::NO); //WriteLog(lsINFO, RPCHandler) << "radar: after copy" << tpTrans->getSTransaction()->getFieldAmount(sfAmount); } catch (std::exception&) { return RPC::make_error (rpcINTERNAL, "Exception occurred during transaction"); } try { // FIXME: For performance, should use asynch interface. tpTrans = ledgerFacade.submitTransactionSync (tpTrans, role == Role::ADMIN, true, bFailHard, bSubmit); if (!tpTrans) { return RPC::make_error (rpcINTERNAL, "Unable to sterilize transaction."); } } catch (std::exception&) { return RPC::make_error (rpcINTERNAL, "Exception occurred during transaction submission."); } try { jvResult["tx_json"] = tpTrans->getJson (0); jvResult["tx_blob"] = strHex ( tpTrans->getSTransaction ()->getSerializer ().peekData ()); if (temUNCERTAIN != tpTrans->getResult ()) { std::string sToken; std::string sHuman; transResultInfo (tpTrans->getResult (), sToken, sHuman); jvResult["engine_result"] = sToken; jvResult["engine_result_code"] = tpTrans->getResult (); jvResult["engine_result_message"] = sHuman; } return jvResult; } catch (std::exception&) { return RPC::make_error (rpcINTERNAL, "Exception occurred during JSON handling."); } }
static Json::Value signPayment( Json::Value const& params, Json::Value& tx_json, RippleAddress const& raSrcAddressID, RPCDetail::LedgerFacade& ledgerFacade, Role role) { RippleAddress dstAccountID; if (!tx_json.isMember ("Amount")) return RPC::missing_field_error ("tx_json.Amount"); STAmount amount; if (! amountFromJsonNoThrow (amount, tx_json ["Amount"])) return RPC::invalid_field_error ("tx_json.Amount"); if (!tx_json.isMember ("Destination")) return RPC::missing_field_error ("tx_json.Destination"); if (!dstAccountID.setAccountID (tx_json["Destination"].asString ())) return RPC::invalid_field_error ("tx_json.Destination"); if (tx_json.isMember ("Paths") && params.isMember ("build_path")) return RPC::make_error (rpcINVALID_PARAMS, "Cannot specify both 'tx_json.Paths' and 'build_path'"); if (!tx_json.isMember ("Paths") && tx_json.isMember ("Amount") && params.isMember ("build_path")) { // Need a ripple path. Currency uSrcCurrencyID; Account uSrcIssuerID; STAmount saSendMax; if (tx_json.isMember ("SendMax")) { if (! amountFromJsonNoThrow (saSendMax, tx_json ["SendMax"])) return RPC::invalid_field_error ("tx_json.SendMax"); } else { // If no SendMax, default to Amount with sender as issuer. saSendMax = amount; saSendMax.setIssuer (raSrcAddressID.getAccountID ()); } if (saSendMax.isNative () && amount.isNative ()) return RPC::make_error (rpcINVALID_PARAMS, "Cannot build XRP to XRP paths."); { LegacyPathFind lpf (role == Role::ADMIN); if (!lpf.isOk ()) return rpcError (rpcTOO_BUSY); STPathSet spsPaths; STPath fullLiquidityPath; bool valid = ledgerFacade.findPathsForOneIssuer ( dstAccountID, saSendMax.issue (), amount, getConfig ().PATH_SEARCH_OLD, 4, // iMaxPaths spsPaths, fullLiquidityPath); if (!valid) { WriteLog (lsDEBUG, RPCHandler) << "transactionSign: build_path: No paths found."; return rpcError (rpcNO_PATH); } WriteLog (lsDEBUG, RPCHandler) << "transactionSign: build_path: " << spsPaths.getJson (0); if (!spsPaths.empty ()) tx_json["Paths"] = spsPaths.getJson (0); } } return Json::Value(); }
/** 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, RPCDetail::LedgerFacade& ledgerFacade, Json::Value& result, bool admin) { Json::Value& tx (request["tx_json"]); if (tx.isMember ("Fee")) return; std::uint64_t feeByTrans = 0; if (tx.isMember("TransactionType") && tx["TransactionType"].asString() == "Payment") { if (!tx.isMember("Destination")) { RPC::inject_error (rpcINVALID_PARAMS, "no destination account", result); return; } Config d; std::string dstAccountID = tx["Destination"].asString(); RippleAddress dstAddress; if (!dstAddress.setAccountID(dstAccountID)) { RPC::inject_error (rpcINVALID_PARAMS, "invalid account id", result); return; } //dst account not exist yet, charge a fix amount of fee(0.01) for creating if (!ledgerFacade.isAccountExist(dstAddress.getAccountID())) { feeByTrans = d.FEE_DEFAULT_CREATE; } //if currency is native(VRP/VBC), charge 1/1000 of transfer amount, //otherwise charge a fix amount of fee(0.001) if (tx.isMember("Amount")) { STAmount amount; if (!amountFromJsonNoThrow(amount, tx["Amount"])) { RPC::inject_error (rpcINVALID_PARAMS, "wrong amount format", result); return; } feeByTrans += amount.isNative() ? amount.getNValue() * d.FEE_DEFAULT_RATE_NATIVE : d.FEE_DEFAULT_NONE_NATIVE; } } int mult = Tuning::defaultAutoFillFeeMultiplier; 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; } } // Default fee in fee units. std::uint64_t const feeDefault = getConfig().TRANSACTION_FEE_BASE; // Administrative endpoints are exempt from local fees. std::uint64_t const fee = ledgerFacade.scaleFeeLoad (feeDefault, admin); std::uint64_t const limit = mult * ledgerFacade.scaleFeeBase (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; } std::stringstream ss; ss << std::max(fee, feeByTrans); tx["Fee"] = ss.str(); }
Json::Value doSubscribe (RPC::Context& context) { InfoSub::pointer ispSub; Json::Value jvResult (Json::objectValue); if (!context.infoSub && !context.params.isMember (jss::url)) { // Must be a JSON-RPC call. WriteLog (lsINFO, RPCHandler) << "doSubscribe: RPC subscribe requires a url"; return rpcError (rpcINVALID_PARAMS); } if (context.params.isMember (jss::url)) { if (context.role != Role::ADMIN) return rpcError (rpcNO_PERMISSION); std::string strUrl = context.params[jss::url].asString (); std::string strUsername = context.params.isMember (jss::url_username) ? context.params[jss::url_username].asString () : ""; std::string strPassword = context.params.isMember (jss::url_password) ? context.params[jss::url_password].asString () : ""; // DEPRECATED if (context.params.isMember (jss::username)) strUsername = context.params[jss::username].asString (); // DEPRECATED if (context.params.isMember (jss::password)) strPassword = context.params[jss::password].asString (); ispSub = context.netOps.findRpcSub (strUrl); if (!ispSub) { WriteLog (lsDEBUG, RPCHandler) << "doSubscribe: building: " << strUrl; RPCSub::pointer rspSub = RPCSub::New (getApp ().getOPs (), getApp ().getIOService (), getApp ().getJobQueue (), strUrl, strUsername, strPassword); ispSub = context.netOps.addRpcSub ( strUrl, std::dynamic_pointer_cast<InfoSub> (rspSub)); } else { WriteLog (lsTRACE, RPCHandler) << "doSubscribe: reusing: " << strUrl; if (context.params.isMember (jss::username)) dynamic_cast<RPCSub*> (&*ispSub)->setUsername (strUsername); if (context.params.isMember (jss::password)) dynamic_cast<RPCSub*> (&*ispSub)->setPassword (strPassword); } } else { ispSub = context.infoSub; } if (!context.params.isMember (jss::streams)) { } else if (!context.params[jss::streams].isArray ()) { WriteLog (lsINFO, RPCHandler) << "doSubscribe: streams requires an array."; return rpcError (rpcINVALID_PARAMS); } else { for (auto& it: context.params[jss::streams]) { if (it.isString ()) { std::string streamName = it.asString (); if (streamName == "server") { context.netOps.subServer (ispSub, jvResult, context.role == Role::ADMIN); } else if (streamName == "ledger") { context.netOps.subLedger (ispSub, jvResult); } else if (streamName == "transactions") { context.netOps.subTransactions (ispSub); } else if (streamName == "transactions_proposed" || streamName == "rt_transactions") // DEPRECATED { context.netOps.subRTTransactions (ispSub); } else { jvResult[jss::error] = "unknownStream"; } } else { jvResult[jss::error] = "malformedStream"; } } } auto strAccountsProposed = context.params.isMember (jss::accounts_proposed) ? jss::accounts_proposed : jss::rt_accounts; // DEPRECATED if (!context.params.isMember (strAccountsProposed)) { } else if (!context.params[strAccountsProposed].isArray ()) { return rpcError (rpcINVALID_PARAMS); } else { auto ids = RPC::parseAccountIds (context.params[strAccountsProposed]); if (ids.empty ()) jvResult[jss::error] = "malformedAccount"; else context.netOps.subAccount (ispSub, ids, true); } if (!context.params.isMember (jss::accounts)) { } else if (!context.params[jss::accounts].isArray ()) { return rpcError (rpcINVALID_PARAMS); } else { auto ids = RPC::parseAccountIds (context.params[jss::accounts]); if (ids.empty ()) { jvResult[jss::error] = "malformedAccount"; } else { context.netOps.subAccount (ispSub, ids, false); WriteLog (lsDEBUG, RPCHandler) << "doSubscribe: accounts: " << ids.size (); } } if (!context.params.isMember (jss::books)) { } else if (!context.params[jss::books].isArray ()) { return rpcError (rpcINVALID_PARAMS); } else { for (auto& j: context.params[jss::books]) { if (!j.isObject () || !j.isMember (jss::taker_pays) || !j.isMember (jss::taker_gets) || !j[jss::taker_pays].isObject () || !j[jss::taker_gets].isObject ()) return rpcError (rpcINVALID_PARAMS); Book book; bool bBoth = (j.isMember (jss::both) && j[jss::both].asBool ()) || (j.isMember (jss::both_sides) && j[jss::both_sides].asBool ()); bool bSnapshot = (j.isMember (jss::snapshot) && j[jss::snapshot].asBool ()) || (j.isMember (jss::state_now) && j[jss::state_now].asBool ()); // TODO(tom): both_sides and state_now are apparently deprecated... // where is this documented? Json::Value taker_pays = j[jss::taker_pays]; Json::Value taker_gets = j[jss::taker_gets]; // Parse mandatory currency. if (!taker_pays.isMember (jss::currency) || !to_currency (book.in.currency, taker_pays[jss::currency].asString ())) { WriteLog (lsINFO, RPCHandler) << "Bad taker_pays currency."; return rpcError (rpcSRC_CUR_MALFORMED); } // Parse optional issuer. else if (((taker_pays.isMember (jss::issuer)) && (!taker_pays[jss::issuer].isString () || !to_issuer (book.in.account, taker_pays[jss::issuer].asString ()))) // Don't allow illegal issuers. || (!book.in.currency != !book.in.account) || noAccount() == book.in.account) { WriteLog (lsINFO, RPCHandler) << "Bad taker_pays issuer."; return rpcError (rpcSRC_ISR_MALFORMED); } // Parse mandatory currency. if (!taker_gets.isMember (jss::currency) || !to_currency (book.out.currency, taker_gets[jss::currency].asString ())) { WriteLog (lsINFO, RPCHandler) << "Bad taker_pays currency."; return rpcError (rpcSRC_CUR_MALFORMED); } // Parse optional issuer. else if (((taker_gets.isMember (jss::issuer)) && (!taker_gets[jss::issuer].isString () || !to_issuer (book.out.account, taker_gets[jss::issuer].asString ()))) // Don't allow illegal issuers. || (!book.out.currency != !book.out.account) || noAccount() == book.out.account) { WriteLog (lsINFO, RPCHandler) << "Bad taker_gets issuer."; return rpcError (rpcDST_ISR_MALFORMED); } if (book.in.currency == book.out.currency && book.in.account == book.out.account) { WriteLog (lsINFO, RPCHandler) << "taker_gets same as taker_pays."; return rpcError (rpcBAD_MARKET); } RippleAddress raTakerID; if (!j.isMember (jss::taker)) raTakerID.setAccountID (noAccount()); else if (!raTakerID.setAccountID (j[jss::taker].asString ())) return rpcError (rpcBAD_ISSUER); if (!isConsistent (book)) { WriteLog (lsWARNING, RPCHandler) << "Bad market: " << book; return rpcError (rpcBAD_MARKET); } context.netOps.subBook (ispSub, book); if (bBoth) context.netOps.subBook (ispSub, reversed (book)); if (bSnapshot) { context.loadType = Resource::feeMediumBurdenRPC; auto lpLedger = getApp().getLedgerMaster (). getPublishedLedger (); if (lpLedger) { const Json::Value jvMarker = Json::Value (Json::nullValue); Json::Value jvOffers (Json::objectValue); auto add = [&](Json::StaticString field) { context.netOps.getBookPage (context.role == Role::ADMIN, lpLedger, field == jss::asks ? reversed (book) : book, raTakerID.getAccountID(), false, 0, jvMarker, jvOffers); if (jvResult.isMember (field)) { Json::Value& results (jvResult[field]); for (auto const& e : jvOffers[jss::offers]) results.append (e); } else { jvResult[field] = jvOffers[jss::offers]; } }; if (bBoth) { add (jss::bids); add (jss::asks); } else { add (jss::offers); } } } } } return jvResult; }
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; RippleAddress 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 STParsedJSON::parse (std::string const& json_name, Json::Value const& json, SField::ref inName, int depth, std::unique_ptr <STObject>& sub_object) { if (! json.isObject ()) { error = not_an_object (json_name); return false; } SField::ptr name (&inName); boost::ptr_vector<SerializedType> data; Json::Value::Members members (json.getMemberNames ()); for (Json::Value::Members::iterator it (members.begin ()); it != members.end (); ++it) { std::string const& fieldName = *it; Json::Value const& value = json [fieldName]; SField::ref field = SField::getField (fieldName); if (field == sfInvalid) { error = unknown_field (json_name, fieldName); return false; } switch (field.fieldType) { case STI_UINT8: try { if (value.isString ()) { // VFALCO TODO wtf? } else if (value.isInt ()) { if (value.asInt () < 0 || value.asInt () > 255) { error = out_of_range (json_name, fieldName); return false; } data.push_back (new STUInt8 (field, range_check_cast <unsigned char> ( value.asInt (), 0, 255))); } else if (value.isUInt ()) { if (value.asUInt () > 255) { error = out_of_range (json_name, fieldName); return false; } data.push_back (new STUInt8 (field, range_check_cast <unsigned char> ( value.asUInt (), 0, 255))); } else { error = bad_type (json_name, fieldName); return false; } } catch (...) { error = invalid_data (json_name, fieldName); return false; } break; case STI_UINT16: try { if (value.isString ()) { std::string strValue = value.asString (); if (! strValue.empty () && ((strValue[0] < '0') || (strValue[0] > '9'))) { if (field == sfTransactionType) { TxType const txType (TxFormats::getInstance()-> findTypeByName (strValue)); data.push_back (new STUInt16 (field, static_cast <std::uint16_t> (txType))); if (*name == sfGeneric) name = &sfTransaction; } else if (field == sfLedgerEntryType) { LedgerEntryType const type (LedgerFormats::getInstance()-> findTypeByName (strValue)); data.push_back (new STUInt16 (field, static_cast <std::uint16_t> (type))); if (*name == sfGeneric) name = &sfLedgerEntry; } else { error = invalid_data (json_name, fieldName); return false; } } else { data.push_back (new STUInt16 (field, beast::lexicalCastThrow <std::uint16_t> (strValue))); } } else if (value.isInt ()) { data.push_back (new STUInt16 (field, range_check_cast <std::uint16_t> ( value.asInt (), 0, 65535))); } else if (value.isUInt ()) { data.push_back (new STUInt16 (field, range_check_cast <std::uint16_t> ( value.asUInt (), 0, 65535))); } else { error = bad_type (json_name, fieldName); return false; } } catch (...) { error = invalid_data (json_name, fieldName); return false; } break; case STI_UINT32: try { if (value.isString ()) { data.push_back (new STUInt32 (field, beast::lexicalCastThrow <std::uint32_t> (value.asString ()))); } else if (value.isInt ()) { data.push_back (new STUInt32 (field, range_check_cast <std::uint32_t> (value.asInt (), 0u, 4294967295u))); } else if (value.isUInt ()) { data.push_back (new STUInt32 (field, static_cast <std::uint32_t> (value.asUInt ()))); } else { error = bad_type (json_name, fieldName); return false; } } catch (...) { error = invalid_data (json_name, fieldName); return false; } break; case STI_UINT64: try { if (value.isString ()) { data.push_back (new STUInt64 (field, uintFromHex (value.asString ()))); } else if (value.isInt ()) { data.push_back (new STUInt64 (field, range_check_cast<std::uint64_t> ( value.asInt (), 0, 18446744073709551615ull))); } else if (value.isUInt ()) { data.push_back (new STUInt64 (field, static_cast <std::uint64_t> (value.asUInt ()))); } else { error = bad_type (json_name, fieldName); return false; } } catch (...) { error = invalid_data (json_name, fieldName); return false; } break; case STI_HASH128: try { if (value.isString ()) { data.push_back (new STHash128 (field, value.asString ())); } else { error = bad_type (json_name, fieldName); return false; } } catch (...) { error = invalid_data (json_name, fieldName); return false; } break; case STI_HASH160: try { if (value.isString ()) { data.push_back (new STHash160 (field, value.asString ())); } else { error = bad_type (json_name, fieldName); return false; } } catch (...) { error = invalid_data (json_name, fieldName); return false; } break; case STI_HASH256: try { if (value.isString ()) { data.push_back (new STHash256 (field, value.asString ())); } else { error = bad_type (json_name, fieldName); return false; } } catch (...) { error = invalid_data (json_name, fieldName); return false; } break; case STI_VL: if (! value.isString ()) { error = bad_type (json_name, fieldName); return false; } try { std::pair<Blob, bool> ret(strUnHex (value.asString ())); if (!ret.second) throw std::invalid_argument ("invalid data"); data.push_back (new STVariableLength (field, ret.first)); } catch (...) { error = invalid_data (json_name, fieldName); return false; } break; case STI_AMOUNT: try { data.push_back (new STAmount (field, value)); } catch (...) { error = invalid_data (json_name, fieldName); return false; } break; case STI_VECTOR256: if (! value.isArray ()) { error = array_expected (json_name, fieldName); return false; } try { data.push_back (new STVector256 (field)); STVector256* tail (dynamic_cast <STVector256*> (&data.back ())); assert (tail); for (Json::UInt i = 0; !json.isValidIndex (i); ++i) { uint256 s; s.SetHex (json[i].asString ()); tail->addValue (s); } } catch (...) { error = invalid_data (json_name, fieldName); return false; } break; case STI_PATHSET: if (!value.isArray ()) { error = array_expected (json_name, fieldName); return false; } try { data.push_back (new STPathSet (field)); STPathSet* tail = dynamic_cast <STPathSet*> (&data.back ()); assert (tail); for (Json::UInt i = 0; value.isValidIndex (i); ++i) { STPath p; if (!value[i].isArray ()) { std::stringstream ss; ss << fieldName << "[" << i << "]"; error = array_expected (json_name, ss.str ()); return false; } for (Json::UInt j = 0; value[i].isValidIndex (j); ++j) { std::stringstream ss; ss << fieldName << "[" << i << "][" << j << "]"; std::string const element_name ( json_name + "." + ss.str()); // each element in this path has some combination of account, // currency, or issuer Json::Value pathEl = value[i][j]; if (!pathEl.isObject ()) { error = not_an_object (element_name); return false; } const Json::Value& account = pathEl["account"]; const Json::Value& currency = pathEl["currency"]; const Json::Value& issuer = pathEl["issuer"]; bool hasCurrency = false; uint160 uAccount, uCurrency, uIssuer; if (! account.isNull ()) { // human account id if (! account.isString ()) { error = string_expected (element_name, "account"); return false; } std::string const strValue (account.asString ()); if (value.size () == 40) // 160-bit hex account value uAccount.SetHex (strValue); { RippleAddress a; if (! a.setAccountID (strValue)) { error = invalid_data (element_name, "account"); return false; } uAccount = a.getAccountID (); } } if (!currency.isNull ()) { // human currency if (!currency.isString ()) { error = string_expected (element_name, "currency"); return false; } hasCurrency = true; if (currency.asString ().size () == 40) { uCurrency.SetHex (currency.asString ()); } else if (!STAmount::currencyFromString ( uCurrency, currency.asString ())) { error = invalid_data (element_name, "currency"); return false; } } if (!issuer.isNull ()) { // human account id if (!issuer.isString ()) { error = string_expected (element_name, "issuer"); return false; } if (issuer.asString ().size () == 40) { uIssuer.SetHex (issuer.asString ()); } else { RippleAddress a; if (!a.setAccountID (issuer.asString ())) { error = invalid_data (element_name, "issuer"); return false; } uIssuer = a.getAccountID (); } } p.addElement (STPathElement (uAccount, uCurrency, uIssuer, hasCurrency)); } tail->addPath (p); } } catch (...) { error = invalid_data (json_name, fieldName); return false; } break; case STI_ACCOUNT: { if (! value.isString ()) { error = bad_type (json_name, fieldName); return false; } std::string strValue = value.asString (); try { if (value.size () == 40) // 160-bit hex account value { uint160 v; v.SetHex (strValue); data.push_back (new STAccount (field, v)); } else { // ripple address RippleAddress a; if (!a.setAccountID (strValue)) { error = invalid_data (json_name, fieldName); return false; } data.push_back (new STAccount (field, a.getAccountID ())); } } catch (...) { error = invalid_data (json_name, fieldName); return false; } } break; case STI_OBJECT: case STI_TRANSACTION: case STI_LEDGERENTRY: case STI_VALIDATION: if (! value.isObject ()) { error = not_an_object (json_name, fieldName); return false; } if (depth > 64) { error = too_deep (json_name, fieldName); return false; } try { std::unique_ptr <STObject> sub_object_; bool const success (parse (json_name + "." + fieldName, value, field, depth + 1, sub_object_)); if (! success) return false; data.push_back (sub_object_.release ()); } catch (...) { error = invalid_data (json_name, fieldName); return false; } break; case STI_ARRAY: if (! value.isArray ()) { error = array_expected (json_name, fieldName); return false; } try { data.push_back (new STArray (field)); STArray* tail = dynamic_cast<STArray*> (&data.back ()); assert (tail); for (Json::UInt i = 0; value.isValidIndex (i); ++i) { bool const isObject (value[i].isObject()); bool const singleKey (isObject ? value [i].size() == 1 : true); if (!isObject || !singleKey) { std::stringstream ss; ss << json_name << "." << fieldName << "[" << i << "]"; error = singleton_expected (ss.str ()); return false; } // TODO: There doesn't seem to be a nice way to get just the // first/only key in an object without copying all keys into // a vector std::string const objectName (value[i].getMemberNames()[0]);; SField::ref nameField (SField::getField(objectName)); if (nameField == sfInvalid) { error = unknown_field (json_name, objectName); return false; } Json::Value const objectFields (value[i][objectName]); std::unique_ptr <STObject> sub_object_; { std::stringstream ss; ss << json_name << "." << fieldName << "[" << i << "]." << objectName; bool const success (parse (ss.str (), objectFields, nameField, depth + 1, sub_object_)); if (! success) return false; } tail->push_back (*sub_object_); } } catch (...) { error = invalid_data (json_name, fieldName); return false; } break; default: error = bad_type (json_name, fieldName); return false; } } sub_object.reset (new STObject (*name, data)); return true; }
// { // ledger_hash : <ledger> // ledger_index : <ledger_index> // ... // } Json::Value RPCHandler::doLedgerEntry (Json::Value params, Resource::Charge& loadType, Application::ScopedLockType& masterLockHolder) { masterLockHolder.unlock (); Ledger::pointer lpLedger; Json::Value jvResult = RPC::lookupLedger (params, lpLedger, *mNetOps); if (!lpLedger) return jvResult; uint256 uNodeIndex; bool bNodeBinary = false; if (params.isMember ("index")) { // XXX Needs to provide proof. uNodeIndex.SetHex (params["index"].asString ()); bNodeBinary = true; } else if (params.isMember ("account_root")) { RippleAddress naAccount; if (!naAccount.setAccountID (params["account_root"].asString ()) || !naAccount.getAccountID ()) { jvResult["error"] = "malformedAddress"; } else { uNodeIndex = Ledger::getAccountRootIndex (naAccount.getAccountID ()); } } else if (params.isMember ("directory")) { if (!params["directory"].isObject ()) { uNodeIndex.SetHex (params["directory"].asString ()); } else if (params["directory"].isMember ("sub_index") && !params["directory"]["sub_index"].isIntegral ()) { jvResult["error"] = "malformedRequest"; } else { std::uint64_t uSubIndex = params["directory"].isMember ("sub_index") ? params["directory"]["sub_index"].asUInt () : 0; if (params["directory"].isMember ("dir_root")) { uint256 uDirRoot; uDirRoot.SetHex (params["dir_root"].asString ()); uNodeIndex = Ledger::getDirNodeIndex (uDirRoot, uSubIndex); } else if (params["directory"].isMember ("owner")) { RippleAddress naOwnerID; if (!naOwnerID.setAccountID (params["directory"]["owner"].asString ())) { jvResult["error"] = "malformedAddress"; } else { uint256 uDirRoot = Ledger::getOwnerDirIndex (naOwnerID.getAccountID ()); uNodeIndex = Ledger::getDirNodeIndex (uDirRoot, uSubIndex); } } else { jvResult["error"] = "malformedRequest"; } } } else if (params.isMember ("generator")) { RippleAddress naGeneratorID; if (!params["generator"].isObject ()) { uNodeIndex.SetHex (params["generator"].asString ()); } else if (!params["generator"].isMember ("regular_seed")) { jvResult["error"] = "malformedRequest"; } else if (!naGeneratorID.setSeedGeneric (params["generator"]["regular_seed"].asString ())) { jvResult["error"] = "malformedAddress"; } else { RippleAddress na0Public; // To find the generator's index. RippleAddress naGenerator = RippleAddress::createGeneratorPublic (naGeneratorID); na0Public.setAccountPublic (naGenerator, 0); uNodeIndex = Ledger::getGeneratorIndex (na0Public.getAccountID ()); } } else if (params.isMember ("offer")) { RippleAddress naAccountID; if (!params["offer"].isObject ()) { uNodeIndex.SetHex (params["offer"].asString ()); } else if (!params["offer"].isMember ("account") || !params["offer"].isMember ("seq") || !params["offer"]["seq"].isIntegral ()) { jvResult["error"] = "malformedRequest"; } else if (!naAccountID.setAccountID (params["offer"]["account"].asString ())) { jvResult["error"] = "malformedAddress"; } else { std::uint32_t uSequence = params["offer"]["seq"].asUInt (); uNodeIndex = Ledger::getOfferIndex (naAccountID.getAccountID (), uSequence); } } else if (params.isMember ("ripple_state")) { RippleAddress naA; RippleAddress naB; uint160 uCurrency; Json::Value jvRippleState = params["ripple_state"]; if (!jvRippleState.isObject () || !jvRippleState.isMember ("currency") || !jvRippleState.isMember ("accounts") || !jvRippleState["accounts"].isArray () || 2 != jvRippleState["accounts"].size () || !jvRippleState["accounts"][0u].isString () || !jvRippleState["accounts"][1u].isString () || jvRippleState["accounts"][0u].asString () == jvRippleState["accounts"][1u].asString () ) { jvResult["error"] = "malformedRequest"; } else if (!naA.setAccountID (jvRippleState["accounts"][0u].asString ()) || !naB.setAccountID (jvRippleState["accounts"][1u].asString ())) { jvResult["error"] = "malformedAddress"; } else if (!STAmount::currencyFromString (uCurrency, jvRippleState["currency"].asString ())) { jvResult["error"] = "malformedCurrency"; } else { uNodeIndex = Ledger::getRippleStateIndex (naA, naB, uCurrency); } } else { jvResult["error"] = "unknownOption"; } if (uNodeIndex.isNonZero ()) { SLE::pointer sleNode = mNetOps->getSLEi (lpLedger, uNodeIndex); if (params.isMember("binary")) bNodeBinary = params["binary"].asBool(); if (!sleNode) { // Not found. // XXX Should also provide proof. jvResult["error"] = "entryNotFound"; } else if (bNodeBinary) { // XXX Should also provide proof. Serializer s; sleNode->add (s); jvResult["node_binary"] = strHex (s.peekData ()); jvResult["index"] = uNodeIndex.ToString (); } else { jvResult["node"] = sleNode->getJson (0); jvResult["index"] = uNodeIndex.ToString (); } } return jvResult; }
void STAccount::setValueNCA(const RippleAddress& nca) { setValueH160(nca.getAccountID()); }
static Json::Value signPayment( Json::Value const& params, Json::Value& tx_json, RippleAddress const& raSrcAddressID, Ledger::pointer lSnapshot, int role) { RippleAddress dstAccountID; if (!tx_json.isMember ("Amount")) return RPC::missing_field_error ("tx_json.Amount"); STAmount amount; if (!amount.bSetJson (tx_json ["Amount"])) return RPC::invalid_field_error ("tx_json.Amount"); if (!tx_json.isMember ("Destination")) return RPC::missing_field_error ("tx_json.Destination"); if (!dstAccountID.setAccountID (tx_json["Destination"].asString ())) return RPC::invalid_field_error ("tx_json.Destination"); if (tx_json.isMember ("Paths") && params.isMember ("build_path")) return RPC::make_error (rpcINVALID_PARAMS, "Cannot specify both 'tx_json.Paths' and 'tx_json.build_path'"); if (!tx_json.isMember ("Paths") && tx_json.isMember ("Amount") && params.isMember ("build_path")) { // Need a ripple path. STPathSet spsPaths; uint160 uSrcCurrencyID; uint160 uSrcIssuerID; STAmount saSendMax; if (tx_json.isMember ("SendMax")) { if (!saSendMax.bSetJson (tx_json ["SendMax"])) return RPC::invalid_field_error ("tx_json.SendMax"); } else { // If no SendMax, default to Amount with sender as issuer. saSendMax = amount; saSendMax.setIssuer (raSrcAddressID.getAccountID ()); } if (saSendMax.isNative () && amount.isNative ()) return RPC::make_error (rpcINVALID_PARAMS, "Cannot build STR to STR paths."); { LegacyPathFind lpf (role == Config::ADMIN); if (!lpf.isOk ()) return rpcError (rpcTOO_BUSY); bool bValid; auto cache = boost::make_shared<RippleLineCache> (lSnapshot); Pathfinder pf (cache, raSrcAddressID, dstAccountID, saSendMax.getCurrency (), saSendMax.getIssuer (), amount, bValid); STPath extraPath; if (!bValid || !pf.findPaths (getConfig ().PATH_SEARCH_OLD, 4, spsPaths, extraPath)) { WriteLog (lsDEBUG, RPCHandler) << "transactionSign: build_path: No paths found."; return rpcError (rpcNO_PATH); } WriteLog (lsDEBUG, RPCHandler) << "transactionSign: build_path: " << spsPaths.getJson (0); if (!spsPaths.isEmpty ()) tx_json["Paths"] = spsPaths.getJson (0); } } return Json::Value(); }