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/RPCLedgerHelpers.h>
5#include <xrpld/rpc/detail/Tuning.h>
6
7#include <xrpl/ledger/ReadView.h>
8#include <xrpl/protocol/ErrorCodes.h>
9#include <xrpl/protocol/RPCErr.h>
10#include <xrpl/protocol/jss.h>
11#include <xrpl/resource/Fees.h>
12
13namespace ripple {
14
15void
16addLine(Json::Value& jsonLines, RPCTrustLine const& line)
17{
18 STAmount const& saBalance(line.getBalance());
19 STAmount const& saLimit(line.getLimit());
20 STAmount const& saLimitPeer(line.getLimitPeer());
21 Json::Value& jPeer(jsonLines.append(Json::objectValue));
22
23 jPeer[jss::account] = to_string(line.getAccountIDPeer());
24 // Amount reported is positive if current account holds other
25 // account's IOUs.
26 //
27 // Amount reported is negative if other account holds current
28 // account's IOUs.
29 jPeer[jss::balance] = saBalance.getText();
30 jPeer[jss::currency] = to_string(saBalance.issue().currency);
31 jPeer[jss::limit] = saLimit.getText();
32 jPeer[jss::limit_peer] = saLimitPeer.getText();
33 jPeer[jss::quality_in] = line.getQualityIn().value;
34 jPeer[jss::quality_out] = line.getQualityOut().value;
35 if (line.getAuth())
36 jPeer[jss::authorized] = true;
37 if (line.getAuthPeer())
38 jPeer[jss::peer_authorized] = true;
39 if (line.getNoRipple())
40 jPeer[jss::no_ripple] = true;
41 if (line.getNoRipplePeer())
42 jPeer[jss::no_ripple_peer] = true;
43 if (line.getFreeze())
44 jPeer[jss::freeze] = true;
45 if (line.getFreezePeer())
46 jPeer[jss::freeze_peer] = true;
47 if (line.getDeepFreeze())
48 jPeer[jss::deep_freeze] = true;
49 if (line.getDeepFreezePeer())
50 jPeer[jss::deep_freeze_peer] = true;
51}
52
53// {
54// account: <account>
55// ledger_hash : <ledger>
56// ledger_index : <ledger_index>
57// limit: integer // optional
58// marker: opaque // optional, resume previous query
59// ignore_default: bool // do not return lines in default state (on
60// this account's side)
61// }
64{
65 auto const& params(context.params);
66 if (!params.isMember(jss::account))
67 return RPC::missing_field_error(jss::account);
68
69 if (!params[jss::account].isString())
70 return RPC::invalid_field_error(jss::account);
71
73 auto result = RPC::lookupLedger(ledger, context);
74 if (!ledger)
75 return result;
76
77 auto id = parseBase58<AccountID>(params[jss::account].asString());
78 if (!id)
79 {
81 return result;
82 }
83 auto const accountID{std::move(id.value())};
84
85 if (!ledger->exists(keylet::account(accountID)))
87
88 std::string strPeer;
89 if (params.isMember(jss::peer))
90 strPeer = params[jss::peer].asString();
91
92 auto const raPeerAccount = [&]() -> std::optional<AccountID> {
93 return strPeer.empty() ? std::nullopt : parseBase58<AccountID>(strPeer);
94 }();
95 if (!strPeer.empty() && !raPeerAccount)
96 {
98 return result;
99 }
100
101 unsigned int limit;
102 if (auto err = readLimitField(limit, RPC::Tuning::accountLines, context))
103 return *err;
104
105 // this flag allows the requester to ask incoming trustlines in default
106 // state be omitted
107 bool ignoreDefault = params.isMember(jss::ignore_default) &&
108 params[jss::ignore_default].asBool();
109
110 Json::Value& jsonLines(result[jss::lines] = Json::arrayValue);
111 struct VisitData
112 {
114 AccountID const& accountID;
115 std::optional<AccountID> const& raPeerAccount;
116 bool ignoreDefault;
117 uint32_t foundCount;
118 };
119 VisitData visitData = {{}, accountID, raPeerAccount, ignoreDefault, 0};
120 uint256 startAfter = beast::zero;
121 std::uint64_t startHint = 0;
122
123 if (params.isMember(jss::marker))
124 {
125 if (!params[jss::marker].isString())
126 return RPC::expected_field_error(jss::marker, "string");
127
128 // Marker is composed of a comma separated index and start hint. The
129 // former will be read as hex, and the latter using boost lexical cast.
130 std::stringstream marker(params[jss::marker].asString());
131 std::string value;
132 if (!std::getline(marker, value, ','))
134
135 if (!startAfter.parseHex(value))
137
138 if (!std::getline(marker, value, ','))
140
141 try
142 {
143 startHint = boost::lexical_cast<std::uint64_t>(value);
144 }
145 catch (boost::bad_lexical_cast&)
146 {
148 }
149
150 // We then must check if the object pointed to by the marker is actually
151 // owned by the account in the request.
152 auto const sle = ledger->read({ltANY, startAfter});
153
154 if (!sle)
156
157 if (!RPC::isRelatedToAccount(*ledger, sle, accountID))
159 }
160
161 auto count = 0;
162 std::optional<uint256> marker = {};
163 std::uint64_t nextHint = 0;
164 {
165 if (!forEachItemAfter(
166 *ledger,
167 accountID,
168 startAfter,
169 startHint,
170 limit + 1,
171 [&visitData, &count, &marker, &limit, &nextHint](
172 std::shared_ptr<SLE const> const& sleCur) {
173 if (!sleCur)
174 {
175 // LCOV_EXCL_START
176 UNREACHABLE("ripple::doAccountLines : null SLE");
177 return false;
178 // LCOV_EXCL_STOP
179 }
180
181 if (++count == limit)
182 {
183 marker = sleCur->key();
184 nextHint =
185 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() ==
195 visitData.accountID)
196 ignore =
197 !(sleCur->getFieldU32(sfFlags) & lsfLowReserve);
198 else
199 ignore = !(
200 sleCur->getFieldU32(sfFlags) & lsfHighReserve);
201 }
202
203 if (!ignore && count <= limit)
204 {
205 auto const line =
206 RPCTrustLine::makeItem(visitData.accountID, sleCur);
207
208 if (line &&
209 (!visitData.raPeerAccount ||
210 *visitData.raPeerAccount ==
211 line->getAccountIDPeer()))
212 {
213 visitData.items.emplace_back(*line);
214 }
215 }
216
217 return true;
218 }))
219 {
221 }
222 }
223
224 // Both conditions need to be checked because marker is set on the limit-th
225 // item, but if there is no item on the limit + 1 iteration, then there is
226 // no need to return a marker.
227 if (count == limit + 1 && marker)
228 {
229 result[jss::limit] = limit;
230 result[jss::marker] =
231 to_string(*marker) + "," + std::to_string(nextHint);
232 }
233
234 result[jss::account] = toBase58(accountID);
235
236 for (auto const& item : visitData.items)
237 addLine(jsonLines, item);
238
240 return result;
241}
242
243} // 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)