rippled
LedgerToJson.cpp
1 //------------------------------------------------------------------------------
2 /*
3  This file is part of rippled: https://github.com/ripple/rippled
4  Copyright (c) 2012-2015 Ripple Labs Inc.
5 
6  Permission to use, copy, modify, and/or distribute this software for any
7  purpose with or without fee is hereby granted, provided that the above
8  copyright notice and this permission notice appear in all copies.
9 
10  THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11  WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12  MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13  ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14  WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15  ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16  OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17 */
18 //==============================================================================
19 
20 #include <ripple/app/ledger/LedgerMaster.h>
21 #include <ripple/app/ledger/LedgerToJson.h>
22 #include <ripple/app/main/Application.h>
23 #include <ripple/app/misc/DeliverMax.h>
24 #include <ripple/app/misc/TxQ.h>
25 #include <ripple/basics/base_uint.h>
26 #include <ripple/core/Pg.h>
27 #include <ripple/protocol/jss.h>
28 #include <ripple/rpc/Context.h>
29 #include <ripple/rpc/DeliveredAmount.h>
30 #include <ripple/rpc/impl/RPCHelpers.h>
31 
32 namespace ripple {
33 
34 namespace {
35 
36 bool
37 isFull(LedgerFill const& fill)
38 {
39  return fill.options & LedgerFill::full;
40 }
41 
42 bool
43 isExpanded(LedgerFill const& fill)
44 {
45  return isFull(fill) || (fill.options & LedgerFill::expand);
46 }
47 
48 bool
49 isBinary(LedgerFill const& fill)
50 {
51  return fill.options & LedgerFill::binary;
52 }
53 
54 template <class Object>
55 void
56 fillJson(
57  Object& json,
58  bool closed,
59  LedgerInfo const& info,
60  bool bFull,
61  unsigned apiVersion)
62 {
63  json[jss::parent_hash] = to_string(info.parentHash);
64  json[jss::ledger_index] = (apiVersion > 1)
65  ? Json::Value(info.seq)
66  : Json::Value(std::to_string(info.seq));
67 
68  if (closed)
69  {
70  json[jss::closed] = true;
71  }
72  else if (!bFull)
73  {
74  json[jss::closed] = false;
75  return;
76  }
77 
78  json[jss::ledger_hash] = to_string(info.hash);
79  json[jss::transaction_hash] = to_string(info.txHash);
80  json[jss::account_hash] = to_string(info.accountHash);
81  json[jss::total_coins] = to_string(info.drops);
82 
83  json[jss::close_flags] = info.closeFlags;
84 
85  // Always show fields that contribute to the ledger hash
86  json[jss::parent_close_time] =
87  info.parentCloseTime.time_since_epoch().count();
88  json[jss::close_time] = info.closeTime.time_since_epoch().count();
89  json[jss::close_time_resolution] = info.closeTimeResolution.count();
90 
91  if (info.closeTime != NetClock::time_point{})
92  {
93  json[jss::close_time_human] = to_string(info.closeTime);
94  if (!getCloseAgree(info))
95  json[jss::close_time_estimated] = true;
96  json[jss::close_time_iso] = to_string_iso(info.closeTime);
97  }
98 }
99 
100 template <class Object>
101 void
102 fillJsonBinary(Object& json, bool closed, LedgerInfo const& info)
103 {
104  if (!closed)
105  json[jss::closed] = false;
106  else
107  {
108  json[jss::closed] = true;
109 
110  Serializer s;
111  addRaw(info, s);
112  json[jss::ledger_data] = strHex(s.peekData());
113  }
114 }
115 
117 fillJsonTx(
118  LedgerFill const& fill,
119  bool bBinary,
120  bool bExpanded,
121  std::shared_ptr<STTx const> const& txn,
122  std::shared_ptr<STObject const> const& stMeta)
123 {
124  if (!bExpanded)
125  return to_string(txn->getTransactionID());
126 
128  auto const txnType = txn->getTxnType();
129  if (bBinary)
130  {
131  txJson[jss::tx_blob] = serializeHex(*txn);
132  if (fill.context->apiVersion > 1)
133  txJson[jss::hash] = to_string(txn->getTransactionID());
134 
135  auto const json_meta =
136  (fill.context->apiVersion > 1 ? jss::meta_blob : jss::meta);
137  if (stMeta)
138  txJson[json_meta] = serializeHex(*stMeta);
139  }
140  else if (fill.context->apiVersion > 1)
141  {
142  copyFrom(
143  txJson[jss::tx_json],
145  txJson[jss::hash] = to_string(txn->getTransactionID());
147  txJson[jss::tx_json], txnType, fill.context->apiVersion);
148 
149  if (stMeta)
150  {
151  txJson[jss::meta] = stMeta->getJson(JsonOptions::none);
152 
153  // If applicable, insert delivered amount
154  if (txnType == ttPAYMENT || txnType == ttCHECK_CASH)
156  txJson[jss::meta],
157  fill.ledger,
158  txn,
159  {txn->getTransactionID(), fill.ledger.seq(), *stMeta});
160  }
161 
162  if (!fill.ledger.open())
163  txJson[jss::ledger_hash] = to_string(fill.ledger.info().hash);
164 
165  const bool validated =
166  fill.context->ledgerMaster.isValidated(fill.ledger);
167  txJson[jss::validated] = validated;
168  if (validated)
169  {
170  auto const seq = fill.ledger.seq();
171  txJson[jss::ledger_index] = (fill.context->apiVersion > 1)
172  ? Json::Value(seq)
173  : Json::Value(std::to_string(seq));
174  if (fill.closeTime)
175  txJson[jss::close_time_iso] = to_string_iso(*fill.closeTime);
176  }
177  }
178  else
179  {
180  copyFrom(txJson, txn->getJson(JsonOptions::none));
181  RPC::insertDeliverMax(txJson, txnType, fill.context->apiVersion);
182  if (stMeta)
183  {
184  txJson[jss::metaData] = stMeta->getJson(JsonOptions::none);
185 
186  // If applicable, insert delivered amount
187  if (txnType == ttPAYMENT || txnType == ttCHECK_CASH)
189  txJson[jss::metaData],
190  fill.ledger,
191  txn,
192  {txn->getTransactionID(), fill.ledger.seq(), *stMeta});
193  }
194  }
195 
196  if ((fill.options & LedgerFill::ownerFunds) &&
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 
219 template <class Object>
220 void
221 fillJsonTx(Object& json, LedgerFill const& fill)
222 {
223  auto&& txns = setArray(json, jss::transactions);
224  auto bBinary = isBinary(fill);
225  auto bExpanded = isExpanded(fill);
226 
227  try
228  {
229  auto appendAll = [&](auto const& txs) {
230  for (auto& i : txs)
231  {
232  txns.append(
233  fillJsonTx(fill, bBinary, bExpanded, i.first, i.second));
234  }
235  };
236 
237  if (fill.context && fill.context->app.config().reporting())
238  {
239  appendAll(flatFetchTransactions(fill.ledger, fill.context->app));
240  }
241  else
242  {
243  appendAll(fill.ledger.txs);
244  }
245  }
246  catch (std::exception const& ex)
247  {
248  // Nothing the user can do about this.
249  if (fill.context)
250  {
251  JLOG(fill.context->j.error())
252  << "Exception in " << __func__ << ": " << ex.what();
253  }
254  }
255 }
256 
257 template <class Object>
258 void
259 fillJsonState(Object& json, LedgerFill const& fill)
260 {
261  auto& ledger = fill.ledger;
262  auto&& array = Json::setArray(json, jss::accountState);
263  auto expanded = isExpanded(fill);
264  auto binary = isBinary(fill);
265 
266  for (auto const& sle : ledger.sles)
267  {
268  if (fill.type == ltANY || sle->getType() == fill.type)
269  {
270  if (binary)
271  {
272  auto&& obj = appendObject(array);
273  obj[jss::hash] = to_string(sle->key());
274  obj[jss::tx_blob] = serializeHex(*sle);
275  }
276  else if (expanded)
277  array.append(sle->getJson(JsonOptions::none));
278  else
279  array.append(to_string(sle->key()));
280  }
281  }
282 }
283 
284 template <class Object>
285 void
286 fillJsonQueue(Object& json, LedgerFill const& fill)
287 {
288  auto&& queueData = Json::setArray(json, jss::queue_data);
289  auto bBinary = isBinary(fill);
290  auto bExpanded = isExpanded(fill);
291 
292  for (auto const& tx : fill.txQueue)
293  {
294  auto&& txJson = appendObject(queueData);
295  txJson[jss::fee_level] = to_string(tx.feeLevel);
296  if (tx.lastValid)
297  txJson[jss::LastLedgerSequence] = *tx.lastValid;
298 
299  txJson[jss::fee] = to_string(tx.consequences.fee());
300  auto const spend =
301  tx.consequences.potentialSpend() + tx.consequences.fee();
302  txJson[jss::max_spend_drops] = to_string(spend);
303  txJson[jss::auth_change] = tx.consequences.isBlocker();
304 
305  txJson[jss::account] = to_string(tx.account);
306  txJson["retries_remaining"] = tx.retriesRemaining;
307  txJson["preflight_result"] = transToken(tx.preflightResult);
308  if (tx.lastResult)
309  txJson["last_result"] = transToken(*tx.lastResult);
310 
311  auto&& temp = fillJsonTx(fill, bBinary, bExpanded, tx.txn, nullptr);
312  if (fill.context->apiVersion > 1)
313  copyFrom(txJson, temp);
314  else
315  copyFrom(txJson[jss::tx], temp);
316  }
317 }
318 
319 template <class Object>
320 void
321 fillJson(Object& json, LedgerFill const& fill)
322 {
323  // TODO: what happens if bBinary and bExtracted are both set?
324  // Is there a way to report this back?
325  auto bFull = isFull(fill);
326  if (isBinary(fill))
327  fillJsonBinary(json, !fill.ledger.open(), fill.ledger.info());
328  else
329  fillJson(
330  json,
331  !fill.ledger.open(),
332  fill.ledger.info(),
333  bFull,
334  (fill.context ? fill.context->apiVersion
336 
337  if (bFull || fill.options & LedgerFill::dumpTxrp)
338  fillJsonTx(json, fill);
339 
340  if (bFull || fill.options & LedgerFill::dumpState)
341  fillJsonState(json, fill);
342 }
343 
344 } // namespace
345 
346 void
347 addJson(Json::Value& json, LedgerFill const& fill)
348 {
349  auto&& object = Json::addObject(json, jss::ledger);
350  fillJson(object, fill);
351 
352  if ((fill.options & LedgerFill::dumpQueue) && !fill.txQueue.empty())
353  fillJsonQueue(json, fill);
354 }
355 
357 getJson(LedgerFill const& fill)
358 {
359  Json::Value json;
360  fillJson(json, fill);
361  return json;
362 }
363 
364 } // namespace ripple
ripple::JsonOptions::disable_API_prior_V2
@ disable_API_prior_V2
Definition: STBase.h:44
ripple::STTx::getTxnType
TxType getTxnType() const
Definition: STTx.h:181
Json::appendObject
Json::Value & appendObject(Json::Value &)
Append a new subobject to a Json object.
Definition: Object.h:450
ripple::LedgerFill::dumpTxrp
@ dumpTxrp
Definition: LedgerToJson.h:51
std::shared_ptr
STL class.
ripple::ltANY
@ ltANY
A special type, matching any ledger entry type.
Definition: LedgerFormats.h:212
std::exception
STL class.
ripple::RPC::insertDeliverMax
void insertDeliverMax(Json::Value &tx_json, TxType txnType, unsigned int apiVersion)
Copy Amount field to DeliverMax field in transaction output JSON.
Definition: DeliverMax.cpp:28
ripple::LedgerFill::dumpQueue
@ dumpQueue
Definition: LedgerToJson.h:57
ripple::LedgerInfo
LedgerHeader LedgerInfo
Definition: LedgerHeader.h:79
ripple::transToken
std::string transToken(TER code)
Definition: TER.cpp:246
ripple::to_string_iso
std::string to_string_iso(date::sys_time< Duration > tp)
Definition: chrono.h:93
ripple::LedgerFill::expand
@ expand
Definition: LedgerToJson.h:53
Json::copyFrom
void copyFrom(Json::Value &to, Json::Value const &from)
Copy all the keys and values from one object into another.
Definition: Object.cpp:226
std::fill
T fill(T... args)
ripple::ttPAYMENT
@ ttPAYMENT
This transaction type executes a payment.
Definition: TxFormats.h:59
ripple::serializeHex
std::string serializeHex(STObject const &o)
Serialize an object to a hex string.
Definition: serialize.h:41
Json::objectValue
@ objectValue
object value (collection of name/value pairs).
Definition: json_value.h:43
ripple::STObject::getAccountID
AccountID getAccountID(SField const &field) const
Definition: STObject.cpp:605
ripple::LedgerFill::full
@ full
Definition: LedgerToJson.h:54
std::to_string
T to_string(T... args)
ripple::sfTakerGets
const SF_AMOUNT sfTakerGets
beast::Journal
A generic endpoint for log messages.
Definition: Journal.h:58
ripple::STTx::getJson
Json::Value getJson(JsonOptions options) const override
Definition: STTx.cpp:231
ripple::accountFunds
STAmount accountFunds(ReadView const &view, AccountID const &id, STAmount const &saDefault, FreezeHandling freezeHandling, beast::Journal j)
Definition: View.cpp:282
ripple::ttOFFER_CREATE
@ ttOFFER_CREATE
This transaction type creates an offer to trade one asset for another.
Definition: TxFormats.h:80
ripple::STTx::getTransactionID
uint256 getTransactionID() const
Definition: STTx.h:193
Json::setArray
Json::Value & setArray(Json::Value &, Json::StaticString const &key)
Add a new subarray at a named key in a Json object.
Definition: Object.h:414
ripple::LedgerFill::binary
@ binary
Definition: LedgerToJson.h:55
ripple::getJson
Json::Value getJson(LedgerFill const &fill)
Return a new Json::Value representing the ledger with given options.
Definition: LedgerToJson.cpp:357
ripple
Use hash_* containers for keys that do not need a cryptographically secure hashing algorithm.
Definition: RCLCensorshipDetector.h:29
ripple::JsonOptions::none
@ none
Definition: STBase.h:42
ripple::addRaw
void addRaw(LedgerHeader const &info, Serializer &s, bool includeHash)
Definition: protocol/impl/LedgerHeader.cpp:25
ripple::LedgerFill::ownerFunds
@ ownerFunds
Definition: LedgerToJson.h:56
Json::addObject
Json::Value & addObject(Json::Value &, Json::StaticString const &key)
Add a new subobject at a named key in a Json object.
Definition: Object.h:426
ripple::RPC::insertDeliveredAmount
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.
Definition: DeliveredAmount.cpp:143
ripple::ttCHECK_CASH
@ ttCHECK_CASH
This transaction type cashes an existing check.
Definition: TxFormats.h:110
ripple::RPC::apiMaximumSupportedVersion
constexpr unsigned int apiMaximumSupportedVersion
Definition: RPCHelpers.h:235
ripple::addJson
void addJson(Json::Value &json, LedgerFill const &fill)
Given a Ledger and options, fill a Json::Object or Json::Value with a description of the ledger.
Definition: LedgerToJson.cpp:347
ripple::fhIGNORE_FREEZE
@ fhIGNORE_FREEZE
Definition: View.h:79
ripple::to_string
std::string to_string(Manifest const &m)
Format the specified manifest to a string for debugging purposes.
Definition: app/misc/impl/Manifest.cpp:41
ripple::sfAccount
const SF_ACCOUNT sfAccount
ripple::strHex
std::string strHex(FwdIt begin, FwdIt end)
Definition: strHex.h:30
ripple::LedgerFill::dumpState
@ dumpState
Definition: LedgerToJson.h:52
ripple::STObject::getJson
Json::Value getJson(JsonOptions options) const override
Definition: STObject.cpp:766
ripple::LedgerFill
Definition: LedgerToJson.h:36
ripple::NetClock::time_point
std::chrono::time_point< NetClock > time_point
Definition: chrono.h:70
ripple::getCloseAgree
bool getCloseAgree(LedgerHeader const &info)
Definition: LedgerHeader.h:85
ripple::STObject::getFieldAmount
STAmount const & getFieldAmount(SField const &field) const
Definition: STObject.cpp:619
std::exception::what
T what(T... args)
Json::Value
Represents a JSON value.
Definition: json_value.h:145
ripple::flatFetchTransactions
std::vector< std::pair< std::shared_ptr< STTx const >, std::shared_ptr< STObject const > > > flatFetchTransactions(Application &app, std::vector< uint256 > &nodestoreHashes)
Definition: Ledger.cpp:1151