rippled
Loading...
Searching...
No Matches
AccountLines.cpp
1#include <xrpld/rpc/Context.h>
2#include <xrpld/rpc/detail/RPCHelpers.h>
3#include <xrpld/rpc/detail/RPCLedgerHelpers.h>
4#include <xrpld/rpc/detail/TrustLine.h>
5#include <xrpld/rpc/detail/Tuning.h>
6
7#include <xrpl/ledger/ReadView.h>
8#include <xrpl/ledger/helpers/DirectoryHelpers.h>
9#include <xrpl/protocol/ErrorCodes.h>
10#include <xrpl/protocol/RPCErr.h>
11#include <xrpl/protocol/jss.h>
12#include <xrpl/resource/Fees.h>
13
14namespace xrpl {
15
16void
17addLine(Json::Value& jsonLines, RPCTrustLine const& line)
18{
19 STAmount const& saBalance(line.getBalance());
20 STAmount const& saLimit(line.getLimit());
21 STAmount const& saLimitPeer(line.getLimitPeer());
22 Json::Value& jPeer(jsonLines.append(Json::objectValue));
23
24 jPeer[jss::account] = to_string(line.getAccountIDPeer());
25 // Amount reported is positive if current account holds other
26 // account's IOUs.
27 //
28 // Amount reported is negative if other account holds current
29 // account's IOUs.
30 jPeer[jss::balance] = saBalance.getText();
31 jPeer[jss::currency] = to_string(saBalance.issue().currency);
32 jPeer[jss::limit] = saLimit.getText();
33 jPeer[jss::limit_peer] = saLimitPeer.getText();
34 jPeer[jss::quality_in] = line.getQualityIn().value;
35 jPeer[jss::quality_out] = line.getQualityOut().value;
36 if (line.getAuth())
37 jPeer[jss::authorized] = true;
38 if (line.getAuthPeer())
39 jPeer[jss::peer_authorized] = true;
40 if (line.getNoRipple())
41 jPeer[jss::no_ripple] = true;
42 if (line.getNoRipplePeer())
43 jPeer[jss::no_ripple_peer] = true;
44 if (line.getFreeze())
45 jPeer[jss::freeze] = true;
46 if (line.getFreezePeer())
47 jPeer[jss::freeze_peer] = true;
48 if (line.getDeepFreeze())
49 jPeer[jss::deep_freeze] = true;
50 if (line.getDeepFreezePeer())
51 jPeer[jss::deep_freeze_peer] = true;
52}
53
54// {
55// account: <account>
56// ledger_hash : <ledger>
57// ledger_index : <ledger_index>
58// limit: integer // optional
59// marker: opaque // optional, resume previous query
60// ignore_default: bool // do not return lines in default state (on
61// this account's side)
62// }
65{
66 auto const& params(context.params);
67 if (!params.isMember(jss::account))
68 return RPC::missing_field_error(jss::account);
69
70 if (!params[jss::account].isString())
71 return RPC::invalid_field_error(jss::account);
72
74 auto result = RPC::lookupLedger(ledger, context);
75 if (!ledger)
76 return result;
77
78 auto id = parseBase58<AccountID>(params[jss::account].asString());
79 if (!id)
80 {
82 return result;
83 }
84 auto const accountID{id.value()};
85
86 if (!ledger->exists(keylet::account(accountID)))
88
89 std::string strPeer;
90 if (params.isMember(jss::peer))
91 strPeer = params[jss::peer].asString();
92
93 auto const raPeerAccount = [&]() -> std::optional<AccountID> {
94 return strPeer.empty() ? std::nullopt : parseBase58<AccountID>(strPeer);
95 }();
96 if (!strPeer.empty() && !raPeerAccount)
97 {
99 return result;
100 }
101
102 unsigned int limit = 0;
103 if (auto err = readLimitField(limit, RPC::Tuning::accountLines, context))
104 return *err;
105
106 // this flag allows the requester to ask incoming trustlines in default
107 // state be omitted
108 bool const ignoreDefault =
109 params.isMember(jss::ignore_default) && params[jss::ignore_default].asBool();
110
111 Json::Value& jsonLines(result[jss::lines] = Json::arrayValue);
112 struct VisitData
113 {
115 AccountID const& accountID;
116 std::optional<AccountID> const& raPeerAccount;
117 bool ignoreDefault;
118 uint32_t foundCount;
119 };
120 VisitData visitData = {{}, accountID, raPeerAccount, ignoreDefault, 0};
121 uint256 startAfter = beast::zero;
122 std::uint64_t startHint = 0;
123
124 if (params.isMember(jss::marker))
125 {
126 if (!params[jss::marker].isString())
127 return RPC::expected_field_error(jss::marker, "string");
128
129 // Marker is composed of a comma separated index and start hint. The
130 // former will be read as hex, and the latter using boost lexical cast.
131 std::stringstream marker(params[jss::marker].asString());
132 std::string value;
133 if (!std::getline(marker, value, ','))
135
136 if (!startAfter.parseHex(value))
138
139 if (!std::getline(marker, value, ','))
141
142 try
143 {
144 startHint = boost::lexical_cast<std::uint64_t>(value);
145 }
146 catch (boost::bad_lexical_cast&)
147 {
149 }
150
151 // We then must check if the object pointed to by the marker is actually
152 // owned by the account in the request.
153 auto const sle = ledger->read({ltANY, startAfter});
154
155 if (!sle)
157
158 if (!RPC::isRelatedToAccount(*ledger, sle, accountID))
160 }
161
162 auto count = 0;
163 std::optional<uint256> marker = {};
164 std::uint64_t nextHint = 0;
165 {
166 if (!forEachItemAfter(
167 *ledger,
168 accountID,
169 startAfter,
170 startHint,
171 limit + 1,
172 [&visitData, &count, &marker, &limit, &nextHint](
173 std::shared_ptr<SLE const> const& sleCur) {
174 if (!sleCur)
175 {
176 // LCOV_EXCL_START
177 UNREACHABLE("xrpl::doAccountLines : null SLE");
178 return false;
179 // LCOV_EXCL_STOP
180 }
181
182 if (++count == limit)
183 {
184 marker = sleCur->key();
185 nextHint = RPC::getStartHint(sleCur, visitData.accountID);
186 }
187
188 if (sleCur->getType() != ltRIPPLE_STATE)
189 return true;
190
191 bool ignore = false;
192 if (visitData.ignoreDefault)
193 {
194 if (sleCur->getFieldAmount(sfLowLimit).getIssuer() == visitData.accountID)
195 {
196 ignore = !(sleCur->getFieldU32(sfFlags) & lsfLowReserve);
197 }
198 else
199 {
200 ignore = !(sleCur->getFieldU32(sfFlags) & lsfHighReserve);
201 }
202 }
203
204 if (!ignore && count <= limit)
205 {
206 auto const line = RPCTrustLine::makeItem(visitData.accountID, sleCur);
207
208 if (line &&
209 (!visitData.raPeerAccount ||
210 *visitData.raPeerAccount == line->getAccountIDPeer()))
211 {
212 visitData.items.emplace_back(*line);
213 }
214 }
215
216 return true;
217 }))
218 {
220 }
221 }
222
223 // Both conditions need to be checked because marker is set on the limit-th
224 // item, but if there is no item on the limit + 1 iteration, then there is
225 // no need to return a marker.
226 if (count == limit + 1 && marker)
227 {
228 result[jss::limit] = limit;
229 result[jss::marker] = to_string(*marker) + "," + std::to_string(nextHint);
230 }
231
232 result[jss::account] = toBase58(accountID);
233
234 for (auto const& item : visitData.items)
235 addLine(jsonLines, item);
236
238 return result;
239}
240
241} // namespace xrpl
Represents a JSON value.
Definition json_value.h:130
Value & append(Value const &value)
Append value to array at the end.
Currency currency
Definition Issue.h:15
static std::optional< RPCTrustLine > makeItem(AccountID const &accountID, std::shared_ptr< SLE const > const &sle)
Definition TrustLine.cpp:83
Issue const & issue() const
Definition STAmount.h:470
std::string getText() const override
Definition STAmount.cpp:656
constexpr bool parseHex(std::string_view sv)
Parse a hex string into a base_uint.
Definition base_uint.h:476
T empty(T... args)
T getline(T... args)
T is_same_v
@ arrayValue
array value (ordered list)
Definition json_value.h:25
@ objectValue
object value (collection of name/value pairs).
Definition json_value.h:26
static LimitRange constexpr accountLines
Limits for the account_lines command.
Json::Value invalid_field_error(std::string const &name)
Definition ErrorCodes.h:273
bool isRelatedToAccount(ReadView const &ledger, std::shared_ptr< SLE const > const &sle, AccountID const &accountID)
Tests if a ledger entry (SLE) is owned by the specified account.
std::uint64_t getStartHint(std::shared_ptr< SLE const > const &sle, AccountID const &accountID)
Gets the start hint for traversing account objects.
Json::Value expected_field_error(std::string const &name, std::string const &type)
Definition ErrorCodes.h:297
Json::Value missing_field_error(std::string const &name)
Definition ErrorCodes.h:231
void inject_error(error_code_i code, Json::Value &json)
Add or update the json update to reflect the error code.
Status lookupLedger(std::shared_ptr< ReadView const > &ledger, JsonContext const &context, Json::Value &result)
Looks up a ledger from a request and fills a Json::Value with ledger data.
Charge const feeMediumBurdenRPC
Keylet account(AccountID const &id) noexcept
AccountID root.
Definition Indexes.cpp:165
Use hash_* containers for keys that do not need a cryptographically secure hashing algorithm.
Definition algorithm.h:5
std::string to_string(base_uint< Bits, Tag > const &a)
Definition base_uint.h:602
void addLine(Json::Value &jsonLines, RPCTrustLine const &line)
std::string toBase58(AccountID const &v)
Convert AccountID to base58 checked string.
Definition AccountID.cpp:92
Json::Value doAccountLines(RPC::JsonContext &context)
Json::Value rpcError(error_code_i iError)
Definition RPCErr.cpp:12
@ ltANY
A special type, matching any ledger entry type.
bool forEachItemAfter(ReadView const &view, Keylet const &root, uint256 const &after, std::uint64_t const hint, unsigned int limit, std::function< bool(std::shared_ptr< SLE const > const &)> const &f)
Iterate all items after an item in the given directory.
@ rpcACT_NOT_FOUND
Definition ErrorCodes.h:50
@ rpcINVALID_PARAMS
Definition ErrorCodes.h:64
@ rpcACT_MALFORMED
Definition ErrorCodes.h:70
Resource::Charge & loadType
Definition Context.h:22
Json::Value params
Definition Context.h:43
T to_string(T... args)