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