rippled
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/GRPCHandlers.h>
5#include <xrpld/rpc/detail/RPCLedgerHelpers.h>
6
7#include <xrpl/json/json_value.h>
8#include <xrpl/ledger/ReadView.h>
9#include <xrpl/ledger/helpers/AccountRootHelpers.h>
10#include <xrpl/protocol/ErrorCodes.h>
11#include <xrpl/protocol/Indexes.h>
12#include <xrpl/protocol/UintTypes.h>
13#include <xrpl/protocol/jss.h>
14
15#include <boost/algorithm/string/case_conv.hpp>
16
17namespace xrpl {
18
31void
32injectSLE(Json::Value& jv, SLE const& sle)
33{
34 jv = sle.getJson(JsonOptions::none);
35 if (sle.getType() == ltACCOUNT_ROOT)
36 {
37 if (sle.isFieldPresent(sfEmailHash))
38 {
39 auto const& hash = sle.getFieldH128(sfEmailHash);
40 Blob const b(hash.begin(), hash.end());
42 boost::to_lower(md5);
43 // VFALCO TODO Give a name and move this constant
44 // to a more visible location. Also
45 // shouldn't this be https?
46 jv[jss::urlgravatar] = str(boost::format("http://www.gravatar.com/avatar/%s") % md5);
47 }
48 }
49 else
50 {
51 jv[jss::Invalid] = true;
52 }
53}
54
55// {
56// account: <ident>,
57// ledger_hash : <ledger>
58// ledger_index : <ledger_index>
59// signer_lists : <bool> // optional (default false)
60// // if true return SignerList(s).
61// queue : <bool> // optional (default false)
62// // if true return information about transactions
63// // in the current TxQ, only if the requested
64// // ledger is open. Otherwise if true, returns an
65// // error.
66// }
67
68// TODO(tom): what is that "default"?
71{
72 auto& params = context.params;
73
74 std::string strIdent;
75 if (params.isMember(jss::account))
76 {
77 if (!params[jss::account].isString())
78 return RPC::invalid_field_error(jss::account);
79 strIdent = params[jss::account].asString();
80 }
81 else if (params.isMember(jss::ident))
82 {
83 if (!params[jss::ident].isString())
84 return RPC::invalid_field_error(jss::ident);
85 strIdent = params[jss::ident].asString();
86 }
87 else
88 {
89 return RPC::missing_field_error(jss::account);
90 }
91
93 auto result = RPC::lookupLedger(ledger, context);
94
95 if (!ledger)
96 return result;
97
98 // Get info on account.
99 auto id = parseBase58<AccountID>(strIdent);
100 if (!id)
101 {
103 return result;
104 }
105 auto const accountID{id.value()};
106
108 {{"defaultRipple", lsfDefaultRipple},
109 {"depositAuth", lsfDepositAuth},
110 {"disableMasterKey", lsfDisableMaster},
111 {"disallowIncomingXRP", lsfDisallowXRP},
112 {"globalFreeze", lsfGlobalFreeze},
113 {"noFreeze", lsfNoFreeze},
114 {"passwordSpent", lsfPasswordSpent},
115 {"requireAuthorization", lsfRequireAuth},
116 {"requireDestinationTag", lsfRequireDestTag}}};
117
119 disallowIncomingFlags{
120 {{"disallowIncomingNFTokenOffer", lsfDisallowIncomingNFTokenOffer},
121 {"disallowIncomingCheck", lsfDisallowIncomingCheck},
122 {"disallowIncomingPayChan", lsfDisallowIncomingPayChan},
123 {"disallowIncomingTrustline", lsfDisallowIncomingTrustline}}};
124
125 static constexpr std::pair<std::string_view, LedgerSpecificFlags> allowTrustLineClawbackFlag{
126 "allowTrustLineClawback", lsfAllowTrustLineClawback};
127
128 static constexpr std::pair<std::string_view, LedgerSpecificFlags> allowTrustLineLockingFlag{
129 "allowTrustLineLocking", lsfAllowTrustLineLocking};
130
131 auto const sleAccepted = ledger->read(keylet::account(accountID));
132 if (sleAccepted)
133 {
134 auto const queue = params.isMember(jss::queue) && params[jss::queue].asBool();
135
136 if (queue && !ledger->open())
137 {
138 // It doesn't make sense to request the queue
139 // with any closed or validated ledger.
141 return result;
142 }
143
144 Json::Value jvAccepted(Json::objectValue);
145 injectSLE(jvAccepted, *sleAccepted);
146 result[jss::account_data] = jvAccepted;
147
149 for (auto const& lsf : lsFlags)
150 acctFlags[lsf.first.data()] = sleAccepted->isFlag(lsf.second);
151
152 for (auto const& lsf : disallowIncomingFlags)
153 acctFlags[lsf.first.data()] = sleAccepted->isFlag(lsf.second);
154
155 if (ledger->rules().enabled(featureClawback))
156 {
157 acctFlags[allowTrustLineClawbackFlag.first.data()] =
158 sleAccepted->isFlag(allowTrustLineClawbackFlag.second);
159 }
160
161 if (ledger->rules().enabled(featureTokenEscrow))
162 {
163 acctFlags[allowTrustLineLockingFlag.first.data()] =
164 sleAccepted->isFlag(allowTrustLineLockingFlag.second);
165 }
166
167 result[jss::account_flags] = std::move(acctFlags);
168
169 auto const pseudoFields = getPseudoAccountFields();
170 for (auto const& pseudoField : pseudoFields)
171 {
172 if (sleAccepted->isFieldPresent(*pseudoField))
173 {
174 std::string name = pseudoField->fieldName;
175 if (name.ends_with("ID"))
176 {
177 // Remove the ID suffix from the field name.
178 name = name.substr(0, name.size() - 2);
179 XRPL_ASSERT_PARTS(!name.empty(), "xrpl::doAccountInfo", "name is not empty");
180 }
181 // ValidPseudoAccounts invariant guarantees that only one field
182 // can be set
183 result[jss::pseudo_account][jss::type] = name;
184 break;
185 }
186 }
187
188 // The document[https://xrpl.org/account_info.html#account_info] states
189 // that signer_lists is a bool, however assigning any string value
190 // works. Do not allow this. This check is for api Version 2 onwards
191 // only
192 if (context.apiVersion > 1u && params.isMember(jss::signer_lists) &&
193 !params[jss::signer_lists].isBool())
194 {
196 return result;
197 }
198
199 // Return SignerList(s) if that is requested.
200 if (params.isMember(jss::signer_lists) && params[jss::signer_lists].asBool())
201 {
202 // We put the SignerList in an array because of an anticipated
203 // future when we support multiple signer lists on one account.
204 Json::Value jvSignerList = Json::arrayValue;
205
206 // This code will need to be revisited if in the future we support
207 // multiple SignerLists on one account.
208 auto const sleSigners = ledger->read(keylet::signers(accountID));
209 if (sleSigners)
210 jvSignerList.append(sleSigners->getJson(JsonOptions::none));
211
212 // Documentation states this is returned as part of the account_info
213 // response, but previously the code put it under account_data. We
214 // can move this to the documented location from apiVersion 2
215 // onwards.
216 if (context.apiVersion == 1)
217 {
218 result[jss::account_data][jss::signer_lists] = std::move(jvSignerList);
219 }
220 else
221 {
222 result[jss::signer_lists] = std::move(jvSignerList);
223 }
224 }
225 // Return queue info if that is requested
226 if (queue)
227 {
228 Json::Value jvQueueData = Json::objectValue;
229
230 auto const txs = context.app.getTxQ().getAccountTxs(accountID);
231 if (!txs.empty())
232 {
233 jvQueueData[jss::txn_count] = static_cast<Json::UInt>(txs.size());
234
235 auto& jvQueueTx = jvQueueData[jss::transactions];
236 jvQueueTx = Json::arrayValue;
237
238 std::uint32_t seqCount = 0;
239 std::uint32_t ticketCount = 0;
242 std::optional<std::uint32_t> lowestTicket;
243 std::optional<std::uint32_t> highestTicket;
244 bool anyAuthChanged = false;
245 XRPAmount totalSpend(0);
246
247 // We expect txs to be returned sorted by SeqProxy. Verify
248 // that with a couple of asserts.
249 SeqProxy prevSeqProxy = SeqProxy::sequence(0);
250 for (auto const& tx : txs)
251 {
253
254 if (tx.seqProxy.isSeq())
255 {
256 XRPL_ASSERT(
257 prevSeqProxy < tx.seqProxy, "doAccountInfo : first sorted proxy");
258 prevSeqProxy = tx.seqProxy;
259 jvTx[jss::seq] = tx.seqProxy.value();
260 ++seqCount;
261 if (!lowestSeq)
262 lowestSeq = tx.seqProxy.value();
263 highestSeq = tx.seqProxy.value();
264 }
265 else
266 {
267 XRPL_ASSERT(
268 prevSeqProxy < tx.seqProxy, "doAccountInfo : second sorted proxy");
269 prevSeqProxy = tx.seqProxy;
270 jvTx[jss::ticket] = tx.seqProxy.value();
271 ++ticketCount;
272 if (!lowestTicket)
273 lowestTicket = tx.seqProxy.value();
274 highestTicket = tx.seqProxy.value();
275 }
276
277 jvTx[jss::fee_level] = to_string(tx.feeLevel);
278 if (tx.lastValid)
279 jvTx[jss::LastLedgerSequence] = *tx.lastValid;
280
281 jvTx[jss::fee] = to_string(tx.consequences.fee());
282 auto const spend = tx.consequences.potentialSpend() + tx.consequences.fee();
283 jvTx[jss::max_spend_drops] = to_string(spend);
284 totalSpend += spend;
285 bool const authChanged = tx.consequences.isBlocker();
286 if (authChanged)
287 anyAuthChanged = authChanged;
288 jvTx[jss::auth_change] = authChanged;
289
290 jvQueueTx.append(std::move(jvTx));
291 }
292
293 if (seqCount != 0u)
294 jvQueueData[jss::sequence_count] = seqCount;
295 if (ticketCount != 0u)
296 jvQueueData[jss::ticket_count] = ticketCount;
297 if (lowestSeq)
298 jvQueueData[jss::lowest_sequence] = *lowestSeq;
299 if (highestSeq)
300 jvQueueData[jss::highest_sequence] = *highestSeq;
301 if (lowestTicket)
302 jvQueueData[jss::lowest_ticket] = *lowestTicket;
303 if (highestTicket)
304 jvQueueData[jss::highest_ticket] = *highestTicket;
305
306 jvQueueData[jss::auth_change_queued] = anyAuthChanged;
307 jvQueueData[jss::max_spend_drops_total] = to_string(totalSpend);
308 }
309 else
310 {
311 jvQueueData[jss::txn_count] = 0u;
312 }
313
314 result[jss::queue_data] = std::move(jvQueueData);
315 }
316 }
317 else
318 {
319 result[jss::account] = toBase58(accountID);
321 }
322
323 return result;
324}
325
326} // namespace xrpl
Represents a JSON value.
Definition json_value.h:130
Value & append(Value const &value)
Append value to array at the end.
Json::Value getJson(JsonOptions options=JsonOptions::none) const override
LedgerEntryType getType() const
uint128 getFieldH128(SField const &field) const
Definition STObject.cpp:605
bool isFieldPresent(SField const &field) const
Definition STObject.cpp:456
A type that represents either a sequence value or a ticket value.
Definition SeqProxy.h:36
static constexpr SeqProxy sequence(std::uint32_t v)
Factory function to return a sequence-based SeqProxy.
Definition SeqProxy.h:56
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:1735
T empty(T... args)
T ends_with(T... args)
@ arrayValue
array value (ordered list)
Definition json_value.h:25
@ objectValue
object value (collection of name/value pairs).
Definition json_value.h:26
unsigned int UInt
Json::Value invalid_field_error(std::string const &name)
Definition ErrorCodes.h:273
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.
Keylet signers(AccountID const &account) noexcept
A SignerList.
Definition Indexes.cpp:295
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::vector< SField const * > const & getPseudoAccountFields()
Returns the list of fields that define an ACCOUNT_ROOT as a pseudo-account if set.
std::string to_string(base_uint< Bits, Tag > const &a)
Definition base_uint.h:602
std::string strHex(FwdIt begin, FwdIt end)
Definition strHex.h:10
std::string toBase58(AccountID const &v)
Convert AccountID to base58 checked string.
Definition AccountID.cpp:92
void injectSLE(Json::Value &jv, SLE const &sle)
Injects JSON describing a ledger entry.
Json::Value doAccountInfo(RPC::JsonContext &context)
std::enable_if_t< std::is_same< T, char >::value||std::is_same< T, unsigned char >::value, Slice > makeSlice(std::array< T, N > const &a)
Definition Slice.h:215
@ rpcACT_NOT_FOUND
Definition ErrorCodes.h:50
@ rpcINVALID_PARAMS
Definition ErrorCodes.h:64
@ rpcACT_MALFORMED
Definition ErrorCodes.h:70
T size(T... args)
Application & app
Definition Context.h:21
unsigned int apiVersion
Definition Context.h:29
Json::Value params
Definition Context.h:43
T substr(T... args)
T value(T... args)