rippled
BookChanges.h
1 //------------------------------------------------------------------------------
2 /*
3  This file is part of rippled: https://github.com/ripple/rippled
4  Copyright (c) 2019 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 #ifndef RIPPLE_RPC_BOOKCHANGES_H_INCLUDED
21 #define RIPPLE_RPC_BOOKCHANGES_H_INCLUDED
22 
23 namespace Json {
24 class Value;
25 }
26 
27 namespace ripple {
28 
29 class ReadView;
30 class Transaction;
31 class TxMeta;
32 class STTx;
33 
34 namespace RPC {
35 
36 template <class L>
39 {
40  std::map<
42  std::tuple<
43  STAmount, // side A volume
44  STAmount, // side B volume
45  STAmount, // high rate
46  STAmount, // low rate
47  STAmount, // open rate
48  STAmount // close rate
49  >>
50  tally;
51 
52  for (auto& tx : lpAccepted->txs)
53  {
54  if (!tx.first || !tx.second ||
55  !tx.first->isFieldPresent(sfTransactionType))
56  continue;
57 
58  std::optional<uint32_t> offerCancel;
59  uint16_t tt = tx.first->getFieldU16(sfTransactionType);
60  switch (tt)
61  {
62  case ttOFFER_CANCEL:
63  case ttOFFER_CREATE: {
64  if (tx.first->isFieldPresent(sfOfferSequence))
65  offerCancel = tx.first->getFieldU32(sfOfferSequence);
66  break;
67  }
68  // in future if any other ways emerge to cancel an offer
69  // this switch makes them easy to add
70  default:
71  break;
72  }
73 
74  for (auto const& node : tx.second->getFieldArray(sfAffectedNodes))
75  {
76  SField const& metaType = node.getFName();
77  uint16_t nodeType = node.getFieldU16(sfLedgerEntryType);
78 
79  // we only care about ltOFFER objects being modified or
80  // deleted
81  if (nodeType != ltOFFER || metaType == sfCreatedNode)
82  continue;
83 
84  // if either FF or PF are missing we can't compute
85  // but generally these are cancelled rather than crossed
86  // so skipping them is consistent
87  if (!node.isFieldPresent(sfFinalFields) ||
88  !node.isFieldPresent(sfPreviousFields))
89  continue;
90 
91  auto const& ffBase = node.peekAtField(sfFinalFields);
92  auto const& finalFields = ffBase.template downcast<STObject>();
93  auto const& pfBase = node.peekAtField(sfPreviousFields);
94  auto const& previousFields = pfBase.template downcast<STObject>();
95 
96  // defensive case that should never be hit
97  if (!finalFields.isFieldPresent(sfTakerGets) ||
98  !finalFields.isFieldPresent(sfTakerPays) ||
99  !previousFields.isFieldPresent(sfTakerGets) ||
100  !previousFields.isFieldPresent(sfTakerPays))
101  continue;
102 
103  // filter out any offers deleted by explicit offer cancels
104  if (metaType == sfDeletedNode && offerCancel &&
105  finalFields.getFieldU32(sfSequence) == *offerCancel)
106  continue;
107 
108  // compute the difference in gets and pays actually
109  // affected onto the offer
110  STAmount deltaGets = finalFields.getFieldAmount(sfTakerGets) -
111  previousFields.getFieldAmount(sfTakerGets);
112  STAmount deltaPays = finalFields.getFieldAmount(sfTakerPays) -
113  previousFields.getFieldAmount(sfTakerPays);
114 
115  std::string g{to_string(deltaGets.issue())};
116  std::string p{to_string(deltaPays.issue())};
117 
118  bool const noswap =
119  isXRP(deltaGets) ? true : (isXRP(deltaPays) ? false : (g < p));
120 
121  STAmount first = noswap ? deltaGets : deltaPays;
122  STAmount second = noswap ? deltaPays : deltaGets;
123 
124  // defensively programmed, should (probably) never happen
125  if (second == beast::zero)
126  continue;
127 
128  STAmount rate = divide(first, second, noIssue());
129 
130  if (first < beast::zero)
131  first = -first;
132 
133  if (second < beast::zero)
134  second = -second;
135 
137  if (noswap)
138  ss << g << "|" << p;
139  else
140  ss << p << "|" << g;
141 
142  std::string key{ss.str()};
143 
144  if (tally.find(key) == tally.end())
145  tally[key] = {
146  first, // side A vol
147  second, // side B vol
148  rate, // high
149  rate, // low
150  rate, // open
151  rate // close
152  };
153  else
154  {
155  // increment volume
156  auto& entry = tally[key];
157 
158  std::get<0>(entry) += first; // side A vol
159  std::get<1>(entry) += second; // side B vol
160 
161  if (std::get<2>(entry) < rate) // high
162  std::get<2>(entry) = rate;
163 
164  if (std::get<3>(entry) > rate) // low
165  std::get<3>(entry) = rate;
166 
167  std::get<5>(entry) = rate; // close
168  }
169  }
170  }
171 
173  jvObj[jss::type] = "bookChanges";
174  jvObj[jss::ledger_index] = lpAccepted->info().seq;
175  jvObj[jss::ledger_hash] = to_string(lpAccepted->info().hash);
176  jvObj[jss::ledger_time] = Json::Value::UInt(
177  lpAccepted->info().closeTime.time_since_epoch().count());
178 
179  jvObj[jss::changes] = Json::arrayValue;
180 
181  for (auto const& entry : tally)
182  {
183  Json::Value& inner = jvObj[jss::changes].append(Json::objectValue);
184 
185  STAmount volA = std::get<0>(entry.second);
186  STAmount volB = std::get<1>(entry.second);
187 
188  inner[jss::currency_a] =
189  (isXRP(volA) ? "XRP_drops" : to_string(volA.issue()));
190  inner[jss::currency_b] =
191  (isXRP(volB) ? "XRP_drops" : to_string(volB.issue()));
192 
193  inner[jss::volume_a] =
194  (isXRP(volA) ? to_string(volA.xrp()) : to_string(volA.iou()));
195  inner[jss::volume_b] =
196  (isXRP(volB) ? to_string(volB.xrp()) : to_string(volB.iou()));
197 
198  inner[jss::high] = to_string(std::get<2>(entry.second).iou());
199  inner[jss::low] = to_string(std::get<3>(entry.second).iou());
200  inner[jss::open] = to_string(std::get<4>(entry.second).iou());
201  inner[jss::close] = to_string(std::get<5>(entry.second).iou());
202  }
203 
204  return jvObj;
205 }
206 
207 } // namespace RPC
208 } // namespace ripple
209 
210 #endif
ripple::sfOfferSequence
const SF_UINT32 sfOfferSequence
std::string
STL class.
std::shared_ptr
STL class.
ripple::STAmount::issue
Issue const & issue() const
Definition: STAmount.h:347
Json::arrayValue
@ arrayValue
array value (ordered list)
Definition: json_value.h:42
ripple::sfSequence
const SF_UINT32 sfSequence
ripple::ttOFFER_CANCEL
@ ttOFFER_CANCEL
This transaction type cancels existing offers to trade one asset for another.
Definition: TxFormats.h:83
std::stringstream
STL class.
ripple::RPC::computeBookChanges
Json::Value computeBookChanges(std::shared_ptr< L const > const &lpAccepted)
Definition: BookChanges.h:38
ripple::sfFinalFields
const SField sfFinalFields
ripple::noIssue
Issue const & noIssue()
Returns an asset specifier that represents no account and currency.
Definition: Issue.h:103
std::tuple
ripple::STAmount::iou
IOUAmount iou() const
Definition: STAmount.cpp:349
ripple::sfDeletedNode
const SField sfDeletedNode
ripple::STAmount::xrp
XRPAmount xrp() const
Definition: STAmount.cpp:334
ripple::divide
STAmount divide(STAmount const &amount, Rate const &rate)
Definition: Rate2.cpp:86
ripple::sfTakerPays
const SF_AMOUNT sfTakerPays
ripple::sfTransactionType
const SF_UINT16 sfTransactionType
Json
JSON (JavaScript Object Notation).
Definition: json_reader.cpp:27
ripple::ltOFFER
@ ltOFFER
A ledger object which describes an offer on the DEX.
Definition: LedgerFormats.h:92
Json::Value::append
Value & append(const Value &value)
Append value to array at the end.
Definition: json_value.cpp:882
Json::objectValue
@ objectValue
object value (collection of name/value pairs).
Definition: json_value.h:43
ripple::sfAffectedNodes
const SField sfAffectedNodes
ripple::STAmount
Definition: STAmount.h:45
ripple::sfTakerGets
const SF_AMOUNT sfTakerGets
ripple::isXRP
bool isXRP(AccountID const &c)
Definition: AccountID.h:89
std::map
STL class.
ripple::ttOFFER_CREATE
@ ttOFFER_CREATE
This transaction type creates an offer to trade one asset for another.
Definition: TxFormats.h:80
Json::Value::UInt
Json::UInt UInt
Definition: json_value.h:153
ripple::sfPreviousFields
const SField sfPreviousFields
ripple
Use hash_* containers for keys that do not need a cryptographically secure hashing algorithm.
Definition: RCLCensorshipDetector.h:29
ripple::sfLedgerEntryType
const SF_UINT16 sfLedgerEntryType
ripple::SField
Identifies fields.
Definition: SField.h:112
ripple::sfCreatedNode
const SField sfCreatedNode
std::optional< uint32_t >
std::stringstream::str
T str(T... args)
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
Json::Value
Represents a JSON value.
Definition: json_value.h:145