// --> strIdent: public key, account ID, or regular seed. // --> bStrict: Only allow account id or public key. // <-- bIndex: true if iIndex > 0 and used the index. Json::Value accountFromString (Ledger::ref lrLedger, RippleAddress& naAccount, bool& bIndex, const std::string& strIdent, const int iIndex, const bool bStrict, NetworkOPs& netOps) { RippleAddress naSeed; if (naAccount.setAccountPublic (strIdent) || naAccount.setAccountID (strIdent)) { // Got the account. bIndex = false; } else if (bStrict) { return naAccount.setAccountID (strIdent, Base58::getBitcoinAlphabet ()) ? rpcError (rpcACT_BITCOIN) : rpcError (rpcACT_MALFORMED); } // Must be a seed. else if (!naSeed.setSeedGeneric (strIdent)) { return rpcError (rpcBAD_SEED); } else { rpcError(rpcACT_MALFORMED); } return Json::Value (Json::objectValue); }
// TODO: Get index from an alternate syntax: rXYZ:<index> Json::Value parseAccountRaw (const Json::Value& jvParams, bool bPeer) { std::string strIdent = jvParams[0u].asString (); unsigned int iCursor = jvParams.size (); bool bStrict = false; std::string strPeer; if (!bPeer && iCursor >= 2 && jvParams[iCursor - 1] == "strict") { bStrict = true; --iCursor; } if (bPeer && iCursor >= 2) strPeer = jvParams[iCursor].asString (); int iIndex = 0; // int iIndex = jvParams.size() >= 2 ? lexicalCast <int>(jvParams[1u].asString()) : 0; RippleAddress raAddress; if (!raAddress.setAccountPublic (strIdent) && !raAddress.setAccountID (strIdent) && !raAddress.setSeedGeneric (strIdent)) return rpcError (rpcACT_MALFORMED); // Get info on account. Json::Value jvRequest (Json::objectValue); jvRequest["account"] = strIdent; if (bStrict) jvRequest["strict"] = 1; if (iIndex) jvRequest["account_index"] = iIndex; if (!strPeer.empty ()) { RippleAddress raPeer; if (!raPeer.setAccountPublic (strPeer) && !raPeer.setAccountID (strPeer) && !raPeer.setSeedGeneric (strPeer)) return rpcError (rpcACT_MALFORMED); jvRequest["peer"] = strPeer; } if (iCursor == (2 + bPeer) && !jvParseLedger (jvRequest, jvParams[1u + bPeer].asString ())) return rpcError (rpcLGR_IDX_MALFORMED); return jvRequest; }
std::vector<RippleAddress> STTx::getMentionedAccounts () const { std::vector<RippleAddress> accounts; for (auto const& it : *this) { if (auto sa = dynamic_cast<STAccount const*> (&it)) { auto const na = sa->getValueNCA (); if (std::find (accounts.cbegin (), accounts.cend (), na) == accounts.cend ()) accounts.push_back (na); } else if (auto sa = dynamic_cast<STAmount const*> (&it)) { auto const& issuer = sa->getIssuer (); if (isXRP (issuer)) continue; RippleAddress na; na.setAccountID (issuer); if (std::find (accounts.cbegin (), accounts.cend (), na) == accounts.cend ()) accounts.push_back (na); } } return accounts; }
bool RippleAddress::setSeedGeneric (const std::string& strText) { RippleAddress naTemp; bool bResult = true; uint256 uSeed; if (strText.empty () || naTemp.setAccountID (strText) || naTemp.setAccountPublic (strText) || naTemp.setAccountPrivate (strText) || naTemp.setNodePublic (strText) || naTemp.setNodePrivate (strText)) { bResult = false; } else if (strText.length () == 32 && uSeed.SetHex (strText, true)) { setSeed (uSeed); } else if (setSeed (strText)) { // Log::out() << "Recognized seed."; nothing (); } else { // Log::out() << "Creating seed from pass phrase."; setSeed (EdKeyPair::passPhraseToKey (strText)); } return bResult; }
std::string RippleAddress::humanAccountID () const { switch (nVersion) { case VER_NONE: throw std::runtime_error ("unset source - humanAccountID"); case VER_ACCOUNT_ID: { boost::mutex::scoped_lock sl (rncLock); boost::unordered_map< Blob , std::string >::iterator it = rncMap.find (vchData); if (it != rncMap.end ()) return it->second; if (rncMap.size () > 10000) rncMap.clear (); return rncMap[vchData] = ToString (); } case VER_ACCOUNT_PUBLIC: { RippleAddress accountID; (void) accountID.setAccountID (getAccountID ()); return accountID.ToString (); } default: throw std::runtime_error (str (boost::format ("bad source: %d") % int (nVersion))); } }
RippleAddress STAccount::getValueNCA() const { RippleAddress a; uint160 v; if (getValueH160(v)) a.setAccountID(v); return a; }
void STPath::printDebug() { std::cerr << "STPath:" << std::endl; for(int i =0; i < mPath.size(); i++) { RippleAddress nad; nad.setAccountID(mPath[i].mAccountID); std::cerr << " " << i << ": " << nad.humanAccountID() << std::endl; } }
RippleAddress RippleAddress::createAccountID(const uint160& uiAccountID) { RippleAddress na; na.setAccountID(uiAccountID); return na; }
std::string STAccount::getText() const { uint160 u; RippleAddress a; if (!getValueH160(u)) return STVariableLength::getText(); a.setAccountID(u); return a.humanAccountID(); }
void STPath::printDebug () { Log::out() << "STPath:"; for (int i = 0; i < mPath.size (); i++) { RippleAddress nad; nad.setAccountID (mPath[i].mAccountID); Log::out() << " " << i << ": " << nad.humanAccountID (); } }
std::vector<RippleAddress> TransactionMetaSet::getAffectedAccounts () { std::vector<RippleAddress> accounts; accounts.reserve (10); // This code should match the behavior of the JS method: // Meta#getAffectedAccounts for (auto const& it : mNodes) { int index = it.getFieldIndex ((it.getFName () == sfCreatedNode) ? sfNewFields : sfFinalFields); if (index != -1) { const STObject* inner = dynamic_cast<const STObject*> (&it.peekAtIndex (index)); if (inner) { for (auto const& field : *inner) { const STAccount* sa = dynamic_cast<const STAccount*> (&field); if (sa) addIfUnique (accounts, sa->getValueNCA ()); else if ((field.getFName () == sfLowLimit) || (field.getFName () == sfHighLimit) || (field.getFName () == sfTakerPays) || (field.getFName () == sfTakerGets)) { const STAmount* lim = dynamic_cast<const STAmount*> (&field); if (lim != nullptr) { auto issuer = lim->getIssuer (); if (issuer.isNonZero ()) { RippleAddress na; na.setAccountID (issuer); addIfUnique (accounts, na); } } else { WriteLog (lsFATAL, TransactionMetaSet) << "limit is not amount " << field.getJson (0); } } } } else assert (false); } } return accounts; }
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; }
void STPathSet::printDebug() { for (int i = 0; i < value.size(); i++) { std::cerr << i << ": "; for (int j = 0; j < value[i].mPath.size(); j++) { //STPathElement pe = value[i].mPath[j]; RippleAddress nad; nad.setAccountID(value[i].mPath[j].mAccountID); std::cerr << " " << nad.humanAccountID(); //std::cerr << " " << pe.mAccountID.GetHex(); } std::cerr << std::endl; } }
std::vector<RippleAddress> SerializedTransaction::getMentionedAccounts () const { std::vector<RippleAddress> accounts; BOOST_FOREACH (const SerializedType & it, peekData ()) { const STAccount* sa = dynamic_cast<const STAccount*> (&it); if (sa != nullptr) { bool found = false; RippleAddress na = sa->getValueNCA (); BOOST_FOREACH (const RippleAddress & it, accounts) { if (it == na) { found = true; break; } } if (!found) accounts.push_back (na); } const STAmount* sam = dynamic_cast<const STAmount*> (&it); if (sam) { uint160 issuer = sam->getIssuer (); if (issuer.isNonZero ()) { RippleAddress na; na.setAccountID (issuer); bool found = false; BOOST_FOREACH (const RippleAddress & it, accounts) { if (it == na) { found = true; break; } } if (!found) accounts.push_back (na); } }
static Json::Value signGenesis( Json::Value const& params, Json::Value& tx_json, RippleAddress const& raSrcAddressID, Ledger::pointer lSnapshot, int role) { RippleAddress dstAccountID; 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"); return Json::Value(); }
boost::unordered_set<RippleAddress> parseAccountIds (const Json::Value& jvArray) { boost::unordered_set<RippleAddress> usnaResult; for (Json::Value::const_iterator it = jvArray.begin (); it != jvArray.end (); it++) { RippleAddress naString; if (! (*it).isString () || !naString.setAccountID ((*it).asString ())) { usnaResult.clear (); break; } else { (void) usnaResult.insert (naString); } } return usnaResult; }
std::string RippleAddress::humanAccountID () const { switch (nVersion) { case VER_NONE: throw std::runtime_error ("unset source - humanAccountID"); case VER_ACCOUNT_ID: { StaticScopedLockType sl (s_lock); auto it = rncMap.find (vchData); if (it != rncMap.end ()) return it->second; // VFALCO NOTE Why do we throw everything out? We could keep two maps // here, switch back and forth keep one of them full and clear the // other on a swap - but always check both maps for cache hits. // if (rncMap.size () > 250000) rncMap.clear (); return rncMap[vchData] = ToString (); } case VER_ACCOUNT_PUBLIC: { RippleAddress accountID; (void) accountID.setAccountID (getAccountID ()); return accountID.ToString (); } default: throw std::runtime_error (str (boost::format ("bad source: %d") % int (nVersion))); } }
std::string RippleAddress::humanAccountID() const { switch (nVersion) { case VER_NONE: throw std::runtime_error("unset source - humanAccountID"); case VER_ACCOUNT_ID: return ToString(); case VER_ACCOUNT_PUBLIC: { RippleAddress accountID; (void) accountID.setAccountID(getAccountID()); return accountID.ToString(); } default: throw std::runtime_error(str(boost::format("bad source: %d") % int(nVersion))); } }
bool RippleAddress::setSeedGeneric(const std::string& strText) { RippleAddress naTemp; bool bResult = true; uint128 uSeed; if (strText.empty() || naTemp.setAccountID(strText) || naTemp.setAccountPublic(strText) || naTemp.setAccountPrivate(strText) || naTemp.setNodePublic(strText) || naTemp.setNodePrivate(strText)) { bResult = false; } else if (strText.length() == 32 && uSeed.SetHex(strText, true)) { setSeed(uSeed); } else if (setSeed(strText)) { // std::cerr << "Recognized seed." << std::endl; nothing(); } else if (1 == setSeed1751(strText)) { // std::cerr << "Recognized 1751 seed." << std::endl; nothing(); } else { // std::cerr << "Creating seed from pass phrase." << std::endl; setSeed(CKey::PassPhraseToKey(strText)); } return bResult; }
void run() override { std::string data_path; if (auto const fixtures = std::getenv("TEST_FIXTURES")) data_path = fixtures; if (data_path.empty ()) { fail("The 'TEST_FIXTURES' environment variable is empty."); return; } DatabaseCon::Setup dbConf; dbConf.dataDir = data_path + "/"; db_ = std::make_unique <DatabaseCon> ( dbConf, "account-tx-transactions.db", nullptr, 0); account_.setAccountID("rfu6L5p3azwPzQZsbTafuVk884N9YoKvVG"); testAccountTxPaging(); }
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; }
// tx_account accountID [ledger_min [ledger_max [limit]]]] [binary] [count] [forward] Json::Value parseTxAccount (const Json::Value& jvParams) { Json::Value jvRequest (Json::objectValue); RippleAddress raAccount; unsigned int iParams = jvParams.size (); if (!raAccount.setAccountID (jvParams[0u].asString ())) return rpcError (rpcACT_MALFORMED); jvRequest["account"] = raAccount.humanAccountID (); bool bDone = false; while (!bDone && iParams >= 2) { if (jvParams[iParams - 1].asString () == "binary") { jvRequest["binary"] = true; --iParams; } else if (jvParams[iParams - 1].asString () == "count") { jvRequest["count"] = true; --iParams; } else if (jvParams[iParams - 1].asString () == "forward") { jvRequest["forward"] = true; --iParams; } else { bDone = true; } } if (1 == iParams) { nothing (); } else if (2 == iParams) { if (!jvParseLedger (jvRequest, jvParams[1u].asString ())) return jvRequest; } else { int64 uLedgerMin = jvParams[1u].asInt (); int64 uLedgerMax = jvParams[2u].asInt (); if (uLedgerMax != -1 && uLedgerMax < uLedgerMin) { return rpcError (rpcLGR_IDXS_INVALID); } jvRequest["ledger_index_min"] = jvParams[1u].asInt (); jvRequest["ledger_index_max"] = jvParams[2u].asInt (); if (iParams >= 4) jvRequest["limit"] = jvParams[3u].asInt (); } return jvRequest; }
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; }
// { // account: account, // ledger_index_min: ledger_index, // ledger_index_max: ledger_index, // binary: boolean, // optional, defaults to false // count: boolean, // optional, defaults to false // descending: boolean, // optional, defaults to false // offset: integer, // optional, defaults to 0 // limit: integer // optional // } Json::Value doAccountTxOld (RPC::Context& context) { RippleAddress raAccount; std::uint32_t offset = context.params_.isMember ("offset") ? context.params_["offset"].asUInt () : 0; int limit = context.params_.isMember ("limit") ? context.params_["limit"].asUInt () : -1; bool bBinary = context.params_.isMember ("binary") && context.params_["binary"].asBool (); bool bDescending = context.params_.isMember ("descending") && context.params_["descending"].asBool (); bool bCount = context.params_.isMember ("count") && context.params_["count"].asBool (); std::uint32_t uLedgerMin; std::uint32_t uLedgerMax; std::uint32_t uValidatedMin; std::uint32_t uValidatedMax; bool bValidated = context.netOps_.getValidatedRange ( uValidatedMin, uValidatedMax); if (!context.params_.isMember ("account")) return rpcError (rpcINVALID_PARAMS); if (!raAccount.setAccountID (context.params_["account"].asString ())) return rpcError (rpcACT_MALFORMED); if (offset > 3000) return rpcError (rpcATX_DEPRECATED); context.loadType_ = Resource::feeHighBurdenRPC; // DEPRECATED if (context.params_.isMember ("ledger_min")) { context.params_["ledger_index_min"] = context.params_["ledger_min"]; bDescending = true; } // DEPRECATED if (context.params_.isMember ("ledger_max")) { context.params_["ledger_index_max"] = context.params_["ledger_max"]; bDescending = true; } if (context.params_.isMember ("ledger_index_min") || context.params_.isMember ("ledger_index_max")) { std::int64_t iLedgerMin = context.params_.isMember ("ledger_index_min") ? context.params_["ledger_index_min"].asInt () : -1; std::int64_t iLedgerMax = context.params_.isMember ("ledger_index_max") ? context.params_["ledger_index_max"].asInt () : -1; if (!bValidated && (iLedgerMin == -1 || iLedgerMax == -1)) { // Don't have a validated ledger range. return rpcError (rpcLGR_IDXS_INVALID); } uLedgerMin = iLedgerMin == -1 ? uValidatedMin : iLedgerMin; uLedgerMax = iLedgerMax == -1 ? uValidatedMax : iLedgerMax; if (uLedgerMax < uLedgerMin) { return rpcError (rpcLGR_IDXS_INVALID); } } else { Ledger::pointer l; Json::Value ret = RPC::lookupLedger (context.params_, l, context.netOps_); if (!l) return ret; uLedgerMin = uLedgerMax = l->getLedgerSeq (); } int count = 0; #ifndef BEAST_DEBUG try { #endif Json::Value ret (Json::objectValue); ret["account"] = raAccount.humanAccountID (); Json::Value& jvTxns = (ret["transactions"] = Json::arrayValue); if (bBinary) { auto txns = context.netOps_.getAccountTxsB ( raAccount, uLedgerMin, uLedgerMax, bDescending, offset, limit, context.role_ == Config::ADMIN); for (auto it = txns.begin (), end = txns.end (); it != end; ++it) { ++count; Json::Value& jvObj = jvTxns.append (Json::objectValue); std::uint32_t uLedgerIndex = std::get<2> (*it); jvObj["tx_blob"] = std::get<0> (*it); jvObj["meta"] = std::get<1> (*it); jvObj["ledger_index"] = uLedgerIndex; jvObj["validated"] = bValidated && uValidatedMin <= uLedgerIndex && uValidatedMax >= uLedgerIndex; } } else { auto txns = context.netOps_.getAccountTxs ( raAccount, uLedgerMin, uLedgerMax, bDescending, offset, limit, context.role_ == Config::ADMIN); for (auto it = txns.begin (), end = txns.end (); it != end; ++it) { ++count; Json::Value& jvObj = jvTxns.append (Json::objectValue); if (it->first) jvObj["tx"] = it->first->getJson (1); if (it->second) { std::uint32_t uLedgerIndex = it->second->getLgrSeq (); jvObj["meta"] = it->second->getJson (0); jvObj["validated"] = bValidated && uValidatedMin <= uLedgerIndex && uValidatedMax >= uLedgerIndex; } } } //Add information about the original query ret["ledger_index_min"] = uLedgerMin; ret["ledger_index_max"] = uLedgerMax; ret["validated"] = bValidated && uValidatedMin <= uLedgerMin && uValidatedMax >= uLedgerMax; ret["offset"] = offset; // We no longer return the full count but only the count of returned // transactions. Computing this count was two expensive and this API is // deprecated anyway. if (bCount) ret["count"] = count; if (context.params_.isMember ("limit")) ret["limit"] = limit; return ret; #ifndef BEAST_DEBUG } catch (...) { return rpcError (rpcINTERNAL); } #endif }
// { // 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; }
// 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; }
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; }
// --> strIdent: public key, account ID, or regular seed. // --> bStrict: Only allow account id or public key. // <-- bIndex: true if iIndex > 0 and used the index. Json::Value accountFromString (Ledger::ref lrLedger, RippleAddress& naAccount, bool& bIndex, const std::string& strIdent, const int iIndex, const bool bStrict, NetworkOPs& netOps) { RippleAddress naSeed; if (naAccount.setAccountPublic (strIdent) || naAccount.setAccountID (strIdent)) { // Got the account. bIndex = false; } else if (bStrict) { return naAccount.setAccountID (strIdent, Base58::getBitcoinAlphabet ()) ? rpcError (rpcACT_BITCOIN) : rpcError (rpcACT_MALFORMED); } // Must be a seed. else if (!naSeed.setSeedGeneric (strIdent)) { return rpcError (rpcBAD_SEED); } else { // We allow the use of the seeds to access #0. // This is poor practice and merely for debuging convenience. RippleAddress naRegular0Public; RippleAddress naRegular0Private; RippleAddress naGenerator = RippleAddress::createGeneratorPublic (naSeed); naRegular0Public.setAccountPublic (naGenerator, 0); naRegular0Private.setAccountPrivate (naGenerator, naSeed, 0); // Account uGeneratorID = naRegular0Public.getAccountID(); SLE::pointer sleGen = netOps.getGenerator (lrLedger, naRegular0Public.getAccountID ()); if (!sleGen) { // Didn't find a generator map, assume it is a master generator. } else { // Found master public key. Blob vucCipher = sleGen->getFieldVL (sfGenerator); Blob vucMasterGenerator = naRegular0Private.accountPrivateDecrypt (naRegular0Public, vucCipher); if (vucMasterGenerator.empty ()) { rpcError (rpcNO_GEN_DECRYPT); } naGenerator.setGenerator (vucMasterGenerator); } bIndex = !iIndex; naAccount.setAccountPublic (naGenerator, iIndex); } return Json::Value (Json::objectValue); }
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; }
// 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."); } }