// { // 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> // account_index: <number> // optional, defaults to 0. // ledger_hash : <ledger> // ledger_index : <ledger_index> // limit: integer // optional // marker: opaque // optional, resume previous query // } Json::Value doAccountLines (RPC::Context& context) { auto const& params (context.params); if (! params.isMember (jss::account)) return RPC::missing_field_error (jss::account); Ledger::pointer ledger; Json::Value result (RPC::lookupLedger (params, ledger, context.netOps)); if (! ledger) return result; std::string strIdent (params[jss::account].asString ()); bool bIndex (params.isMember (jss::account_index)); int iIndex (bIndex ? params[jss::account_index].asUInt () : 0); DivvyAddress divvyAddress; auto jv = RPC::accountFromString ( divvyAddress, bIndex, strIdent, iIndex, false); if (! jv.empty ()) { for (auto it = jv.begin (); it != jv.end (); ++it) result[it.memberName ()] = it.key (); return result; } if (! ledger->exists(getAccountRootIndex( divvyAddress.getAccountID()))) return rpcError (rpcACT_NOT_FOUND); std::string strPeer (params.isMember (jss::peer) ? params[jss::peer].asString () : ""); bool bPeerIndex (params.isMember (jss::peer_index)); int iPeerIndex (bIndex ? params[jss::peer_index].asUInt () : 0); DivvyAddress divvyAddressPeer; if (! strPeer.empty ()) { result[jss::peer] = divvyAddress.humanAccountID (); if (bPeerIndex) result[jss::peer_index] = iPeerIndex; result = RPC::accountFromString ( divvyAddressPeer, bPeerIndex, strPeer, iPeerIndex, false); if (! result.empty ()) return result; } AccountID raPeerAccount; if (divvyAddressPeer.isValid ()) raPeerAccount = divvyAddressPeer.getAccountID (); unsigned int limit; if (params.isMember (jss::limit)) { auto const& jvLimit (params[jss::limit]); if (! jvLimit.isIntegral ()) return RPC::expected_field_error (jss::limit, "unsigned integer"); limit = jvLimit.isUInt () ? jvLimit.asUInt () : std::max (0, jvLimit.asInt ()); if (context.role != Role::ADMIN) { limit = std::max (RPC::Tuning::minLinesPerRequest, std::min (limit, RPC::Tuning::maxLinesPerRequest)); } } else { limit = RPC::Tuning::defaultLinesPerRequest; } Json::Value& jsonLines (result[jss::lines] = Json::arrayValue); AccountID const& raAccount(divvyAddress.getAccountID ()); VisitData visitData = { {}, raAccount, divvyAddressPeer, raPeerAccount }; unsigned int reserve (limit); uint256 startAfter; std::uint64_t startHint; if (params.isMember (jss::marker)) { // We have a start point. Use limit - 1 from the result and use the // very last one for the resume. Json::Value const& marker (params[jss::marker]); if (! marker.isString ()) return RPC::expected_field_error (jss::marker, "string"); startAfter.SetHex (marker.asString ()); auto const sleLine = fetch(*ledger, startAfter, getApp().getSLECache()); if (sleLine == nullptr || sleLine->getType () != ltRIPPLE_STATE) return rpcError (rpcINVALID_PARAMS); if (sleLine->getFieldAmount (sfLowLimit).getIssuer () == raAccount) startHint = sleLine->getFieldU64 (sfLowNode); else if (sleLine->getFieldAmount (sfHighLimit).getIssuer () == raAccount) startHint = sleLine->getFieldU64 (sfHighNode); else return rpcError (rpcINVALID_PARAMS); // Caller provided the first line (startAfter), add it as first result auto const line = DivvyState::makeItem (raAccount, sleLine); if (line == nullptr) return rpcError (rpcINVALID_PARAMS); addLine (jsonLines, *line); visitData.items.reserve (reserve); } else { startHint = 0; // We have no start point, limit should be one higher than requested. visitData.items.reserve (++reserve); } if (! forEachItemAfter(*ledger, raAccount, getApp().getSLECache(), startAfter, startHint, reserve, [&visitData](std::shared_ptr<SLE const> const& sleCur) { auto const line = DivvyState::makeItem (visitData.accountID, sleCur); if (line != nullptr && (! visitData.divvyAddressPeer.isValid () || visitData.raPeerAccount == line->getAccountIDPeer ())) { visitData.items.emplace_back (line); return true; } return false; })) { return rpcError (rpcINVALID_PARAMS); } if (visitData.items.size () == reserve) { result[jss::limit] = limit; DivvyState::pointer line (visitData.items.back ()); result[jss::marker] = to_string (line->key()); visitData.items.pop_back (); } result[jss::account] = divvyAddress.humanAccountID (); for (auto const& item : visitData.items) addLine (jsonLines, *item.get ()); context.loadType = Resource::feeMediumBurdenRPC; return result; }