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