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