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