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