xrpld
Loading...
Searching...
No Matches
AccountInfo.cpp
1#include <xrpld/app/main/Application.h>
2#include <xrpld/app/misc/TxQ.h>
3#include <xrpld/rpc/Context.h>
4#include <xrpld/rpc/detail/RPCLedgerHelpers.h>
5
6#include <xrpl/basics/Blob.h>
7#include <xrpl/basics/Slice.h>
8#include <xrpl/basics/StringUtilities.h>
9#include <xrpl/basics/base_uint.h>
10#include <xrpl/basics/strHex.h>
11#include <xrpl/beast/utility/instrumentation.h>
12#include <xrpl/json/json_forwards.h>
13#include <xrpl/json/json_value.h>
14#include <xrpl/ledger/ReadView.h>
15#include <xrpl/ledger/helpers/AccountRootHelpers.h>
16#include <xrpl/protocol/AccountID.h>
17#include <xrpl/protocol/ErrorCodes.h>
18#include <xrpl/protocol/Feature.h>
19#include <xrpl/protocol/Indexes.h>
20#include <xrpl/protocol/LedgerFormats.h>
21#include <xrpl/protocol/SField.h>
22#include <xrpl/protocol/SeqProxy.h>
23#include <xrpl/protocol/Units.h>
24#include <xrpl/protocol/XRPAmount.h>
25#include <xrpl/protocol/jss.h>
26
27#include <array>
28#include <cstdint>
29#include <format>
30#include <memory>
31#include <optional>
32#include <string>
33#include <string_view>
34#include <tuple>
35#include <utility>
36
37namespace xrpl {
38
51void
52injectSLE(json::Value& jv, SLE const& sle)
53{
55 XRPL_ASSERT(sle.getType() == ltACCOUNT_ROOT, "xrpl::injectSLE : sle is account root");
56 if (sle.isFieldPresent(sfEmailHash))
57 {
58 auto const& hash = sle.getFieldH128(sfEmailHash);
59 Blob const b(hash.begin(), hash.end());
61 md5 = toLower(md5);
62 // VFALCO TODO Give a name to this constant and move it
63 // to a more visible location.
64 jv[jss::urlgravatar] = std::format("https://www.gravatar.com/avatar/{}", md5);
65 }
66}
67
68// {
69// account: <ident>,
70// ledger_hash : <ledger>
71// ledger_index : <ledger_index>
72// signer_lists : <bool> // optional (default false)
73// // if true return SignerList(s).
74// queue : <bool> // optional (default false)
75// // if true return information about transactions
76// // in the current TxQ, only if the requested
77// // ledger is open. Otherwise if true, returns an
78// // error.
79// }
80
81// TODO(tom): what is that "default"?
84{
85 auto& params = context.params;
86
87 std::string strIdent;
88 if (params.isMember(jss::account))
89 {
90 if (!params[jss::account].isString())
91 return rpc::invalidFieldError(jss::account);
92 strIdent = params[jss::account].asString();
93 }
94 else if (params.isMember(jss::ident))
95 {
96 if (!params[jss::ident].isString())
97 return rpc::invalidFieldError(jss::ident);
98 strIdent = params[jss::ident].asString();
99 }
100 else
101 {
102 return rpc::missingFieldError(jss::account);
103 }
104
106 auto result = rpc::lookupLedger(ledger, context);
107
108 if (!ledger)
109 return result;
110
111 // Get info on account.
112 auto id = parseBase58<AccountID>(strIdent);
113 if (!id)
114 {
116 return result;
117 }
118 auto const accountID{id.value()};
119
120 // Flags that are always reported.
121 static constexpr auto kAccountRootFlags =
122 std::to_array<std::pair<std::string_view, LedgerSpecificFlags>>(
123 {{"allowTrustLineClawback", lsfAllowTrustLineClawback},
124 {"defaultRipple", lsfDefaultRipple},
125 {"depositAuth", lsfDepositAuth},
126 {"disableMasterKey", lsfDisableMaster},
127 {"disallowIncomingCheck", lsfDisallowIncomingCheck},
128 {"disallowIncomingNFTokenOffer", lsfDisallowIncomingNFTokenOffer},
129 {"disallowIncomingPayChan", lsfDisallowIncomingPayChan},
130 {"disallowIncomingTrustline", lsfDisallowIncomingTrustline},
131 {"disallowIncomingXRP", lsfDisallowXRP},
132 {"globalFreeze", lsfGlobalFreeze},
133 {"noFreeze", lsfNoFreeze},
134 {"passwordSpent", lsfPasswordSpent},
135 {"requireAuthorization", lsfRequireAuth},
136 {"requireDestinationTag", lsfRequireDestTag}});
137
138 // Flags that are only reported when their amendment is enabled. This can't be `constexpr`,
139 // since the amendment IDs are computed at runtime.
140 static auto const kAmendmentGatedFlags =
141 std::to_array<std::tuple<std::string_view, LedgerSpecificFlags, UInt256 const&>>(
142 {{"allowTrustLineLocking", lsfAllowTrustLineLocking, featureTokenEscrow}});
143
144 // Every `AccountRoot` flag must be reported by `account_info`, so if a new flag is added, it
145 // needs to be added to one of the arrays above. This can't be a `static_assert` because
146 // `getAccountRootFlags()` builds its map at runtime.
147 XRPL_ASSERT_PARTS(
148 kAccountRootFlags.size() + kAmendmentGatedFlags.size() == getAccountRootFlags().size(),
149 "xrpl::doAccountInfo",
150 "number of account flags");
151
152 auto const sleAccepted = ledger->read(keylet::account(accountID));
153 if (sleAccepted)
154 {
155 auto const queue = params.isMember(jss::queue) && params[jss::queue].asBool();
156
157 if (queue && !ledger->open())
158 {
159 // It doesn't make sense to request the queue
160 // with any closed or validated ledger.
162 return result;
163 }
164
166 injectSLE(jvAccepted, *sleAccepted);
167 result[jss::account_data] = jvAccepted;
168
170 for (auto const& [name, flag] : kAccountRootFlags)
171 acctFlags[name.data()] = sleAccepted->isFlag(flag);
172
173 for (auto const& [name, flag, amendment] : kAmendmentGatedFlags)
174 {
175 if (ledger->rules().enabled(amendment))
176 acctFlags[name.data()] = sleAccepted->isFlag(flag);
177 }
178
179 result[jss::account_flags] = std::move(acctFlags);
180
181 auto const pseudoFields = getPseudoAccountFields();
182 for (auto const& pseudoField : pseudoFields)
183 {
184 if (sleAccepted->isFieldPresent(*pseudoField))
185 {
186 std::string name = pseudoField->fieldName;
187 if (name.ends_with("ID"))
188 {
189 // Remove the ID suffix from the field name.
190 name = name.substr(0, name.size() - 2);
191 XRPL_ASSERT_PARTS(!name.empty(), "xrpl::doAccountInfo", "name is not empty");
192 }
193 // ValidPseudoAccounts invariant guarantees that only one field
194 // can be set
195 result[jss::pseudo_account][jss::type] = name;
196 break;
197 }
198 }
199
200 // The document[https://xrpl.org/account_info.html#account_info] states
201 // that signer_lists is a bool, however assigning any string value
202 // works. Do not allow this. This check is for api Version 2 onwards
203 // only
204 if (context.apiVersion > 1u && params.isMember(jss::signer_lists) &&
205 !params[jss::signer_lists].isBool())
206 {
208 return result;
209 }
210
211 // Return SignerList(s) if that is requested.
212 if (params.isMember(jss::signer_lists) && params[jss::signer_lists].asBool())
213 {
214 // We put the SignerList in an array because of an anticipated
215 // future when we support multiple signer lists on one account.
216 json::Value jvSignerList = json::ValueType::Array;
217
218 // This code will need to be revisited if in the future we support
219 // multiple SignerLists on one account.
220 auto const sleSigners = ledger->read(keylet::signerList(accountID));
221 if (sleSigners)
222 jvSignerList.append(sleSigners->getJson(JsonOptions::Values::None));
223
224 // Documentation states this is returned as part of the account_info
225 // response, but previously the code put it under account_data. We
226 // can move this to the documented location from apiVersion 2
227 // onwards.
228 if (context.apiVersion == 1)
229 {
230 result[jss::account_data][jss::signer_lists] = std::move(jvSignerList);
231 }
232 else
233 {
234 result[jss::signer_lists] = std::move(jvSignerList);
235 }
236 }
237 // Return queue info if that is requested
238 if (queue)
239 {
241
242 auto const txs = context.app.getTxQ().getAccountTxs(accountID);
243 if (!txs.empty())
244 {
245 jvQueueData[jss::txn_count] = static_cast<json::UInt>(txs.size());
246
247 auto& jvQueueTx = jvQueueData[jss::transactions];
248 jvQueueTx = json::ValueType::Array;
249
250 std::uint32_t seqCount = 0;
251 std::uint32_t ticketCount = 0;
254 std::optional<std::uint32_t> lowestTicket;
255 std::optional<std::uint32_t> highestTicket;
256 bool anyAuthChanged = false;
257 XRPAmount totalSpend(0);
258
259 // We expect txs to be returned sorted by SeqProxy. Verify
260 // that with a couple of asserts.
261 SeqProxy prevSeqProxy = SeqProxy::rawSequence(0);
262 for (auto const& tx : txs)
263 {
265
266 if (tx.seqProxy.isSeq())
267 {
268 XRPL_ASSERT(
269 prevSeqProxy < tx.seqProxy, "doAccountInfo : first sorted proxy");
270 prevSeqProxy = tx.seqProxy;
271 jvTx[jss::seq] = tx.seqProxy.value();
272 ++seqCount;
273 if (!lowestSeq)
274 lowestSeq = tx.seqProxy.value();
275 highestSeq = tx.seqProxy.value();
276 }
277 else
278 {
279 XRPL_ASSERT(
280 prevSeqProxy < tx.seqProxy, "doAccountInfo : second sorted proxy");
281 prevSeqProxy = tx.seqProxy;
282 jvTx[jss::ticket] = tx.seqProxy.value();
283 ++ticketCount;
284 if (!lowestTicket)
285 lowestTicket = tx.seqProxy.value();
286 highestTicket = tx.seqProxy.value();
287 }
288
289 jvTx[jss::fee_level] = to_string(tx.feeLevel);
290 if (tx.lastValid)
291 jvTx[jss::LastLedgerSequence] = *tx.lastValid;
292
293 jvTx[jss::fee] = to_string(tx.consequences.fee());
294 auto const spend = tx.consequences.potentialSpend() + tx.consequences.fee();
295 jvTx[jss::max_spend_drops] = to_string(spend);
296 totalSpend += spend;
297 bool const authChanged = tx.consequences.isBlocker();
298 if (authChanged)
299 anyAuthChanged = authChanged;
300 jvTx[jss::auth_change] = authChanged;
301
302 jvQueueTx.append(std::move(jvTx));
303 }
304
305 if (seqCount != 0u)
306 jvQueueData[jss::sequence_count] = seqCount;
307 if (ticketCount != 0u)
308 jvQueueData[jss::ticket_count] = ticketCount;
309 if (lowestSeq)
310 jvQueueData[jss::lowest_sequence] = *lowestSeq;
311 if (highestSeq)
312 jvQueueData[jss::highest_sequence] = *highestSeq;
313 if (lowestTicket)
314 jvQueueData[jss::lowest_ticket] = *lowestTicket;
315 if (highestTicket)
316 jvQueueData[jss::highest_ticket] = *highestTicket;
317
318 jvQueueData[jss::auth_change_queued] = anyAuthChanged;
319 jvQueueData[jss::max_spend_drops_total] = to_string(totalSpend);
320 }
321 else
322 {
323 jvQueueData[jss::txn_count] = 0u;
324 }
325
326 result[jss::queue_data] = std::move(jvQueueData);
327 }
328 }
329 else
330 {
331 result[jss::account] = toBase58(accountID);
333 }
334
335 return result;
336}
337
338} // namespace xrpl
Represents a JSON value.
Definition json_value.h:117
Value & append(Value const &value)
Append value to array at the end.
LedgerEntryType getType() const
json::Value getJson(JsonOptions options=JsonOptions::Values::None) const override
bool isFieldPresent(SField const &field) const
Definition STObject.cpp:464
UInt128 getFieldH128(SField const &field) const
Definition STObject.cpp:613
A type that represents either a sequence value or a ticket value.
Definition SeqProxy.h:37
static constexpr SeqProxy rawSequence(std::uint32_t v)
Factory function to return a sequence-based SeqProxy.
Definition SeqProxy.h:62
virtual TxQ & getTxQ()=0
std::vector< TxDetails > getAccountTxs(AccountID const &account) const
Returns information about the transactions currently in the queue for the account.
Definition TxQ.cpp:1835
T empty(T... args)
T ends_with(T... args)
T format(T... args)
unsigned int UInt
@ Array
array value (ordered list)
Definition json_value.h:28
@ Object
object value (collection of name/value pairs).
Definition json_value.h:29
Keylet signerList(AccountID const &account) noexcept
A SignerList.
Definition Indexes.cpp:348
Keylet account(AccountID const &id) noexcept
AccountID root.
Definition Indexes.cpp:220
void injectError(ErrorCodeI code, json::Value &json)
Add or update the json update to reflect the error code.
json::Value invalidFieldError(std::string const &name)
Definition ErrorCodes.h:285
json::Value missingFieldError(std::string const &name)
Definition ErrorCodes.h:243
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.
Use hash_* containers for keys that do not need a cryptographically secure hashing algorithm.
Definition algorithm.h:5
std::vector< SField const * > const & getPseudoAccountFields()
Returns the list of fields that define an ACCOUNT_ROOT as a pseudo-account if set.
@ RpcActNotFound
Definition ErrorCodes.h:53
@ RpcActMalformed
Definition ErrorCodes.h:73
@ RpcInvalidParams
Definition ErrorCodes.h:67
void injectSLE(json::Value &jv, SLE const &sle)
Injects JSON describing a ledger entry.
std::string toLower(std::string str)
Fold ASCII upper case letters to lower case.
std::optional< AccountID > parseBase58(std::string const &s)
Parse AccountID from checked, base58 string.
std::string strHex(FwdIt begin, FwdIt end)
Definition strHex.h:13
std::string toBase58(AccountID const &v)
Convert AccountID to base58 checked string.
Definition AccountID.cpp:95
std::string to_string(BaseUInt< Bits, Tag > const &a)
Definition base_uint.h:657
json::Value doAccountInfo(rpc::JsonContext &context)
STLedgerEntry SLE
Slice makeSlice(std::array< T, N > const &a)
Definition Slice.h:228
std::vector< unsigned char > Blob
Storage for linear binary data.
Definition Blob.h:11
T size(T... args)
unsigned int apiVersion
Definition Context.h:37
Application & app
Definition Context.h:29
json::Value params
Definition Context.h:51
T substr(T... args)
T value(T... args)