// { // account: <account>|<account_public_key> // ledger_hash : <ledger> // ledger_index : <ledger_index> // limit: integer // optional // marker: opaque // optional, resume previous query // } Json::Value doAccountLines (RPC::Context& context) { auto const& params (context.params); if (! params.isMember (jss::account)) return RPC::missing_field_error (jss::account); std::shared_ptr<ReadView const> ledger; auto result = RPC::lookupLedger (ledger, context); if (! ledger) return result; std::string strIdent (params[jss::account].asString ()); AccountID accountID; // Intentional assignment in if. Extra parentheses silence warning. if ((result = RPC::accountFromString (accountID, strIdent))) return result; if (! ledger->exists(keylet::account (accountID))) return rpcError (rpcACT_NOT_FOUND); std::string strPeer; if (params.isMember (jss::peer)) strPeer = params[jss::peer].asString (); auto hasPeer = ! strPeer.empty (); AccountID raPeerAccount; if (hasPeer) { // Intentional assignment in if. Extra parentheses silence warning. if ((result = RPC::accountFromString (raPeerAccount, strPeer))) return result; } unsigned int limit; if (auto err = readLimitField(limit, RPC::Tuning::accountLines, context)) return *err; Json::Value& jsonLines (result[jss::lines] = Json::arrayValue); VisitData visitData = {{}, accountID, hasPeer, raPeerAccount}; unsigned int reserve (limit); uint256 startAfter; std::uint64_t startHint; if (params.isMember (jss::marker)) { // We have a start point. Use limit - 1 from the result and use the // very last one for the resume. Json::Value const& marker (params[jss::marker]); if (! marker.isString ()) return RPC::expected_field_error (jss::marker, "string"); startAfter.SetHex (marker.asString ()); auto const sleLine = ledger->read({ltRIPPLE_STATE, startAfter}); if (! sleLine) return rpcError (rpcINVALID_PARAMS); if (sleLine->getFieldAmount (sfLowLimit).getIssuer () == accountID) startHint = sleLine->getFieldU64 (sfLowNode); else if (sleLine->getFieldAmount (sfHighLimit).getIssuer () == accountID) startHint = sleLine->getFieldU64 (sfHighNode); else return rpcError (rpcINVALID_PARAMS); // Caller provided the first line (startAfter), add it as first result auto const line = RippleState::makeItem (accountID, sleLine); if (line == nullptr) return rpcError (rpcINVALID_PARAMS); addLine (jsonLines, *line); visitData.items.reserve (reserve); } else { startHint = 0; // We have no start point, limit should be one higher than requested. visitData.items.reserve (++reserve); } { if (! forEachItemAfter(*ledger, accountID, startAfter, startHint, reserve, [&visitData](std::shared_ptr<SLE const> const& sleCur) { auto const line = RippleState::makeItem (visitData.accountID, sleCur); if (line != nullptr && (! visitData.hasPeer || visitData.raPeerAccount == line->getAccountIDPeer ())) { visitData.items.emplace_back (line); return true; } return false; })) { return rpcError (rpcINVALID_PARAMS); } } if (visitData.items.size () == reserve) { result[jss::limit] = limit; RippleState::pointer line (visitData.items.back ()); result[jss::marker] = to_string (line->key()); visitData.items.pop_back (); } result[jss::account] = context.app.accountIDCache().toBase58 (accountID); for (auto const& item : visitData.items) addLine (jsonLines, *item.get ()); context.loadType = Resource::feeMediumBurdenRPC; return result; }
// { // account: <account>|<account_public_key> // ledger_hash : <ledger> // ledger_index : <ledger_index> // limit: integer // optional // marker: opaque // optional, resume previous query // } Json::Value doAccountChannels (RPC::Context& context) { auto const& params (context.params); if (! params.isMember (jss::account)) return RPC::missing_field_error (jss::account); std::shared_ptr<ReadView const> ledger; auto result = RPC::lookupLedger (ledger, context); if (! ledger) return result; std::string strIdent (params[jss::account].asString ()); AccountID accountID; result = RPC::accountFromString (accountID, strIdent); if (result) return result; if (! ledger->exists(keylet::account (accountID))) return rpcError (rpcACT_NOT_FOUND); std::string strDst; if (params.isMember (jss::destination_account)) strDst = params[jss::destination_account].asString (); auto hasDst = ! strDst.empty (); AccountID raDstAccount; if (hasDst) { result = RPC::accountFromString (raDstAccount, strDst); if (result) return result; } unsigned int limit; if (auto err = readLimitField(limit, RPC::Tuning::accountChannels, context)) return *err; Json::Value jsonChannels{Json::arrayValue}; struct VisitData { std::vector <std::shared_ptr<SLE const>> items; AccountID const& accountID; bool hasDst; AccountID const& raDstAccount; }; VisitData visitData = {{}, accountID, hasDst, raDstAccount}; unsigned int reserve (limit); uint256 startAfter; std::uint64_t startHint; if (params.isMember (jss::marker)) { // We have a start point. Use limit - 1 from the result and use the // very last one for the resume. Json::Value const& marker (params[jss::marker]); if (! marker.isString ()) return RPC::expected_field_error (jss::marker, "string"); startAfter.SetHex (marker.asString ()); auto const sleChannel = ledger->read({ltPAYCHAN, startAfter}); if (! sleChannel) return rpcError (rpcINVALID_PARAMS); if (sleChannel->getFieldAmount (sfLowLimit).getIssuer () == accountID) startHint = sleChannel->getFieldU64 (sfLowNode); else if (sleChannel->getFieldAmount (sfHighLimit).getIssuer () == accountID) startHint = sleChannel->getFieldU64 (sfHighNode); else return rpcError (rpcINVALID_PARAMS); addChannel (jsonChannels, *sleChannel); visitData.items.reserve (reserve); } else { startHint = 0; // We have no start point, limit should be one higher than requested. visitData.items.reserve (++reserve); } if (! forEachItemAfter(*ledger, accountID, startAfter, startHint, reserve, [&visitData](std::shared_ptr<SLE const> const& sleCur) { if (sleCur && sleCur->getType () == ltPAYCHAN && (! visitData.hasDst || visitData.raDstAccount == (*sleCur)[sfDestination])) { visitData.items.emplace_back (sleCur); return true; } return false; })) { return rpcError (rpcINVALID_PARAMS); } if (visitData.items.size () == reserve) { result[jss::limit] = limit; result[jss::marker] = to_string (visitData.items.back()->key()); visitData.items.pop_back (); } result[jss::account] = context.app.accountIDCache().toBase58 (accountID); for (auto const& item : visitData.items) addChannel (jsonChannels, *item); context.loadType = Resource::feeMediumBurdenRPC; result[jss::channels] = std::move(jsonChannels); return result; }
Json::Value doAccountObjects (RPC::Context& context) { auto const& params = context.params; if (! params.isMember (jss::account)) return RPC::missing_field_error (jss::account); std::shared_ptr<ReadView const> ledger; auto result = RPC::lookupLedger (ledger, context); if (ledger == nullptr) return result; AccountID accountID; { auto const strIdent = params[jss::account].asString (); if (auto jv = RPC::accountFromString (accountID, strIdent)) { for (auto it = jv.begin (); it != jv.end (); ++it) result[it.memberName ()] = it.key (); return result; } } if (! ledger->exists(keylet::account (accountID))) return rpcError (rpcACT_NOT_FOUND); auto type = ltINVALID; if (params.isMember (jss::type)) { static std::array<std::pair<char const *, LedgerEntryType>, 9> const types {{ { jss::account, ltACCOUNT_ROOT }, { jss::amendments, ltAMENDMENTS }, { jss::directory, ltDIR_NODE }, { jss::fee, ltFEE_SETTINGS }, { jss::hashes, ltLEDGER_HASHES }, { jss::offer, ltOFFER }, { jss::signer_list, ltSIGNER_LIST }, { jss::state, ltRIPPLE_STATE }, { jss::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; } unsigned int limit; if (auto err = readLimitField(limit, RPC::Tuning::accountObjects, context)) return *err; 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, accountID, type, dirIndex, entryIndex, limit, result)) { result[jss::account_objects] = Json::arrayValue; } result[jss::account] = context.app.accountIDCache().toBase58 (accountID); context.loadType = Resource::feeMediumBurdenRPC; return result; }