rippled
Loading...
Searching...
No Matches
LedgerToJson.cpp
1#include <xrpld/app/ledger/LedgerMaster.h>
2#include <xrpld/app/ledger/LedgerToJson.h>
3#include <xrpld/app/misc/DeliverMax.h>
4#include <xrpld/app/misc/TxQ.h>
5#include <xrpld/rpc/Context.h>
6#include <xrpld/rpc/DeliveredAmount.h>
7#include <xrpld/rpc/MPTokenIssuanceID.h>
8
9#include <xrpl/basics/base_uint.h>
10#include <xrpl/protocol/ApiVersion.h>
11#include <xrpl/protocol/jss.h>
12
13namespace xrpl {
14
15namespace {
16
17bool
18isFull(LedgerFill const& fill)
19{
20 return fill.options & LedgerFill::full;
21}
22
23bool
24isExpanded(LedgerFill const& fill)
25{
26 return isFull(fill) || (fill.options & LedgerFill::expand);
27}
28
29bool
30isBinary(LedgerFill const& fill)
31{
32 return fill.options & LedgerFill::binary;
33}
34
35void
36fillJson(
37 Json::Value& json,
38 bool closed,
39 LedgerHeader const& info,
40 bool bFull,
41 unsigned apiVersion)
42{
43 json[jss::parent_hash] = to_string(info.parentHash);
44 json[jss::ledger_index] = (apiVersion > 1)
45 ? Json::Value(info.seq)
46 : Json::Value(std::to_string(info.seq));
47
48 if (closed)
49 {
50 json[jss::closed] = true;
51 }
52 else if (!bFull)
53 {
54 json[jss::closed] = false;
55 return;
56 }
57
58 json[jss::ledger_hash] = to_string(info.hash);
59 json[jss::transaction_hash] = to_string(info.txHash);
60 json[jss::account_hash] = to_string(info.accountHash);
61 json[jss::total_coins] = to_string(info.drops);
62
63 json[jss::close_flags] = info.closeFlags;
64
65 // Always show fields that contribute to the ledger hash
66 json[jss::parent_close_time] =
67 info.parentCloseTime.time_since_epoch().count();
68 json[jss::close_time] = info.closeTime.time_since_epoch().count();
69 json[jss::close_time_resolution] = info.closeTimeResolution.count();
70
71 if (info.closeTime != NetClock::time_point{})
72 {
73 json[jss::close_time_human] = to_string(info.closeTime);
74 if (!getCloseAgree(info))
75 json[jss::close_time_estimated] = true;
76 json[jss::close_time_iso] = to_string_iso(info.closeTime);
77 }
78}
79
80void
81fillJsonBinary(Json::Value& json, bool closed, LedgerHeader const& info)
82{
83 if (!closed)
84 json[jss::closed] = false;
85 else
86 {
87 json[jss::closed] = true;
88
89 Serializer s;
90 addRaw(info, s);
91 json[jss::ledger_data] = strHex(s.peekData());
92 }
93}
94
96fillJsonTx(
97 LedgerFill const& fill,
98 bool bBinary,
99 bool bExpanded,
102{
103 if (!bExpanded)
104 return to_string(txn->getTransactionID());
105
107 auto const txnType = txn->getTxnType();
108 if (bBinary)
109 {
110 txJson[jss::tx_blob] = serializeHex(*txn);
111 if (fill.context->apiVersion > 1)
112 txJson[jss::hash] = to_string(txn->getTransactionID());
113
114 auto const json_meta =
115 (fill.context->apiVersion > 1 ? jss::meta_blob : jss::meta);
116 if (stMeta)
117 txJson[json_meta] = serializeHex(*stMeta);
118 }
119 else if (fill.context->apiVersion > 1)
120 {
121 copyFrom(
122 txJson[jss::tx_json],
123 txn->getJson(JsonOptions::disable_API_prior_V2, false));
124 txJson[jss::hash] = to_string(txn->getTransactionID());
126 txJson[jss::tx_json], txnType, fill.context->apiVersion);
127
128 if (stMeta)
129 {
130 txJson[jss::meta] = stMeta->getJson(JsonOptions::none);
131
132 // If applicable, insert delivered amount
133 if (txnType == ttPAYMENT || txnType == ttCHECK_CASH)
135 txJson[jss::meta],
136 fill.ledger,
137 txn,
138 {txn->getTransactionID(), fill.ledger.seq(), *stMeta});
139
140 // If applicable, insert mpt issuance id
142 txJson[jss::meta],
143 txn,
144 {txn->getTransactionID(), fill.ledger.seq(), *stMeta});
145 }
146
147 if (!fill.ledger.open())
148 txJson[jss::ledger_hash] = to_string(fill.ledger.header().hash);
149
150 bool const validated =
151 fill.context->ledgerMaster.isValidated(fill.ledger);
152 txJson[jss::validated] = validated;
153 if (validated)
154 {
155 auto const seq = fill.ledger.seq();
156 txJson[jss::ledger_index] = seq;
157 if (fill.closeTime)
158 txJson[jss::close_time_iso] = to_string_iso(*fill.closeTime);
159 }
160 }
161 else
162 {
163 copyFrom(txJson, txn->getJson(JsonOptions::none));
164 RPC::insertDeliverMax(txJson, txnType, fill.context->apiVersion);
165 if (stMeta)
166 {
167 txJson[jss::metaData] = stMeta->getJson(JsonOptions::none);
168
169 // If applicable, insert delivered amount
170 if (txnType == ttPAYMENT || txnType == ttCHECK_CASH)
172 txJson[jss::metaData],
173 fill.ledger,
174 txn,
175 {txn->getTransactionID(), fill.ledger.seq(), *stMeta});
176
177 // If applicable, insert mpt issuance id
179 txJson[jss::metaData],
180 txn,
181 {txn->getTransactionID(), fill.ledger.seq(), *stMeta});
182 }
183 }
184
185 if ((fill.options & LedgerFill::ownerFunds) &&
186 txn->getTxnType() == ttOFFER_CREATE)
187 {
188 auto const account = txn->getAccountID(sfAccount);
189 auto const amount = txn->getFieldAmount(sfTakerGets);
190
191 // If the offer create is not self funded then add the
192 // owner balance
193 if (account != amount.getIssuer())
194 {
195 auto const ownerFunds = accountFunds(
196 fill.ledger,
197 account,
198 amount,
200 beast::Journal{beast::Journal::getNullSink()});
201 txJson[jss::owner_funds] = ownerFunds.getText();
202 }
203 }
204
205 return txJson;
206}
207
208void
209fillJsonTx(Json::Value& json, LedgerFill const& fill)
210{
211 auto& txns = json[jss::transactions] = Json::arrayValue;
212 auto bBinary = isBinary(fill);
213 auto bExpanded = isExpanded(fill);
214
215 try
216 {
217 auto appendAll = [&](auto const& txs) {
218 for (auto& i : txs)
219 {
220 txns.append(
221 fillJsonTx(fill, bBinary, bExpanded, i.first, i.second));
222 }
223 };
224
225 appendAll(fill.ledger.txs);
226 }
227 catch (std::exception const& ex)
228 {
229 // Nothing the user can do about this.
230 if (fill.context)
231 {
232 JLOG(fill.context->j.error())
233 << "Exception in " << __func__ << ": " << ex.what();
234 }
235 }
236}
237
238void
239fillJsonState(Json::Value& json, LedgerFill const& fill)
240{
241 auto& ledger = fill.ledger;
242 auto& array = json[jss::accountState] = Json::arrayValue;
243 auto expanded = isExpanded(fill);
244 auto binary = isBinary(fill);
245
246 for (auto const& sle : ledger.sles)
247 {
248 if (binary)
249 {
250 auto& obj = array.append(Json::objectValue);
251 obj[jss::hash] = to_string(sle->key());
252 obj[jss::tx_blob] = serializeHex(*sle);
253 }
254 else if (expanded)
255 array.append(sle->getJson(JsonOptions::none));
256 else
257 array.append(to_string(sle->key()));
258 }
259}
260
261void
262fillJsonQueue(Json::Value& json, LedgerFill const& fill)
263{
264 auto& queueData = json[jss::queue_data] = Json::arrayValue;
265 auto bBinary = isBinary(fill);
266 auto bExpanded = isExpanded(fill);
267
268 for (auto const& tx : fill.txQueue)
269 {
270 auto& txJson = queueData.append(Json::objectValue);
271 txJson[jss::fee_level] = to_string(tx.feeLevel);
272 if (tx.lastValid)
273 txJson[jss::LastLedgerSequence] = *tx.lastValid;
274
275 txJson[jss::fee] = to_string(tx.consequences.fee());
276 auto const spend =
277 tx.consequences.potentialSpend() + tx.consequences.fee();
278 txJson[jss::max_spend_drops] = to_string(spend);
279 txJson[jss::auth_change] = tx.consequences.isBlocker();
280
281 txJson[jss::account] = to_string(tx.account);
282 txJson["retries_remaining"] = tx.retriesRemaining;
283 txJson["preflight_result"] = transToken(tx.preflightResult);
284 if (tx.lastResult)
285 txJson["last_result"] = transToken(*tx.lastResult);
286
287 auto&& temp = fillJsonTx(fill, bBinary, bExpanded, tx.txn, nullptr);
288 if (fill.context->apiVersion > 1)
289 copyFrom(txJson, temp);
290 else
291 copyFrom(txJson[jss::tx], temp);
292 }
293}
294
295void
296fillJson(Json::Value& json, LedgerFill const& fill)
297{
298 // TODO: what happens if bBinary and bExtracted are both set?
299 // Is there a way to report this back?
300 auto bFull = isFull(fill);
301 if (isBinary(fill))
302 fillJsonBinary(json, !fill.ledger.open(), fill.ledger.header());
303 else
304 fillJson(
305 json,
306 !fill.ledger.open(),
307 fill.ledger.header(),
308 bFull,
309 (fill.context ? fill.context->apiVersion
310 : RPC::apiMaximumSupportedVersion));
311
312 if (bFull || fill.options & LedgerFill::dumpTxrp)
313 fillJsonTx(json, fill);
314
315 if (bFull || fill.options & LedgerFill::dumpState)
316 fillJsonState(json, fill);
317}
318
319} // namespace
320
321void
322addJson(Json::Value& json, LedgerFill const& fill)
323{
324 auto& object = json[jss::ledger] = Json::objectValue;
325 fillJson(object, fill);
326
327 if ((fill.options & LedgerFill::dumpQueue) && !fill.txQueue.empty())
328 fillJsonQueue(json, fill);
329}
330
332getJson(LedgerFill const& fill)
333{
334 Json::Value json;
335 fillJson(json, fill);
336 return json;
337}
338
339void
341{
342 if (!to) // Short circuit this very common case.
343 to = from;
344 else
345 {
346 // TODO: figure out if there is a way to remove this clause
347 // or check that it does/needs to do a deep copy
348 XRPL_ASSERT(from.isObjectOrNull(), "copyFrom : invalid input type");
349 auto const members = from.getMemberNames();
350 for (auto const& m : members)
351 to[m] = from[m];
352 }
353}
354
355} // namespace xrpl
Represents a JSON value.
Definition json_value.h:131
bool isObjectOrNull() const
Members getMemberNames() const
Return a list of the member names.
A generic endpoint for log messages.
Definition Journal.h:41
std::chrono::time_point< NetClock > time_point
Definition chrono.h:50
T fill(T... args)
@ arrayValue
array value (ordered list)
Definition json_value.h:26
@ objectValue
object value (collection of name/value pairs).
Definition json_value.h:27
void insertDeliverMax(Json::Value &tx_json, TxType txnType, unsigned int apiVersion)
Copy Amount field to DeliverMax field in transaction output JSON.
Definition DeliverMax.cpp:9
void insertMPTokenIssuanceID(Json::Value &response, std::shared_ptr< STTx const > const &transaction, TxMeta const &transactionMeta)
void insertDeliveredAmount(Json::Value &meta, ReadView const &, std::shared_ptr< STTx const > const &serializedTx, TxMeta const &)
Add a delivered_amount field to the meta input/output parameter.
Keylet account(AccountID const &id) noexcept
AccountID root.
Definition Indexes.cpp:166
auto const amount
Use hash_* containers for keys that do not need a cryptographically secure hashing algorithm.
Definition algorithm.h:6
@ fhIGNORE_FREEZE
Definition View.h:59
Json::Value getJson(LedgerFill const &fill)
Return a new Json::Value representing the ledger with given options.
std::string to_string(base_uint< Bits, Tag > const &a)
Definition base_uint.h:611
std::string strHex(FwdIt begin, FwdIt end)
Definition strHex.h:11
std::string transToken(TER code)
Definition TER.cpp:245
bool getCloseAgree(LedgerHeader const &info)
STAmount accountFunds(ReadView const &view, AccountID const &id, STAmount const &saDefault, FreezeHandling freezeHandling, beast::Journal j)
Definition View.cpp:657
void copyFrom(Json::Value &to, Json::Value const &from)
Copy all the keys and values from one object into another.
void addRaw(LedgerHeader const &, Serializer &, bool includeHash=false)
std::string serializeHex(STObject const &o)
Serialize an object to a hex string.
Definition serialize.h:22
std::string to_string_iso(date::sys_time< Duration > tp)
Definition chrono.h:73
void addJson(Json::Value &json, LedgerFill const &fill)
Given a Ledger and options, fill a Json::Value with a description of the ledger.
T to_string(T... args)
T what(T... args)