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  STObject& finalFields = (const_cast<STObject&>(node))
93  .downcast<STObject>();
94 
95  STObject& previousFields = (const_cast<STObject&>(node))
97  .downcast<STObject>();
98 
99  // defensive case that should never be hit
100  if (!finalFields.isFieldPresent(sfTakerGets) ||
101  !finalFields.isFieldPresent(sfTakerPays) ||
102  !previousFields.isFieldPresent(sfTakerGets) ||
103  !previousFields.isFieldPresent(sfTakerPays))
104  continue;
105 
106  // filter out any offers deleted by explicit offer cancels
107  if (metaType == sfDeletedNode && offerCancel &&
108  finalFields.getFieldU32(sfSequence) == *offerCancel)
109  continue;
110 
111  // compute the difference in gets and pays actually
112  // affected onto the offer
113  STAmount deltaGets = finalFields.getFieldAmount(sfTakerGets) -
114  previousFields.getFieldAmount(sfTakerGets);
115  STAmount deltaPays = finalFields.getFieldAmount(sfTakerPays) -
116  previousFields.getFieldAmount(sfTakerPays);
117 
118  std::string g{to_string(deltaGets.issue())};
119  std::string p{to_string(deltaPays.issue())};
120 
121  bool const noswap =
122  isXRP(deltaGets) ? true : (isXRP(deltaPays) ? false : (g < p));
123 
124  STAmount first = noswap ? deltaGets : deltaPays;
125  STAmount second = noswap ? deltaPays : deltaGets;
126 
127  // defensively programmed, should (probably) never happen
128  if (second == beast::zero)
129  continue;
130 
131  STAmount rate = divide(first, second, noIssue());
132 
133  if (first < beast::zero)
134  first = -first;
135 
136  if (second < beast::zero)
137  second = -second;
138 
140  if (noswap)
141  ss << g << "|" << p;
142  else
143  ss << p << "|" << g;
144 
145  std::string key{ss.str()};
146 
147  if (tally.find(key) == tally.end())
148  tally[key] = {
149  first, // side A vol
150  second, // side B vol
151  rate, // high
152  rate, // low
153  rate, // open
154  rate // close
155  };
156  else
157  {
158  // increment volume
159  auto& entry = tally[key];
160 
161  std::get<0>(entry) += first; // side A vol
162  std::get<1>(entry) += second; // side B vol
163 
164  if (std::get<2>(entry) < rate) // high
165  std::get<2>(entry) = rate;
166 
167  if (std::get<3>(entry) > rate) // low
168  std::get<3>(entry) = rate;
169 
170  std::get<5>(entry) = rate; // close
171  }
172  }
173  }
174 
176  jvObj[jss::type] = "bookChanges";
177  jvObj[jss::ledger_index] = lpAccepted->info().seq;
178  jvObj[jss::ledger_hash] = to_string(lpAccepted->info().hash);
179  jvObj[jss::ledger_time] = Json::Value::UInt(
180  lpAccepted->info().closeTime.time_since_epoch().count());
181 
182  jvObj[jss::changes] = Json::arrayValue;
183 
184  for (auto const& entry : tally)
185  {
186  Json::Value& inner = jvObj[jss::changes].append(Json::objectValue);
187 
188  STAmount volA = std::get<0>(entry.second);
189  STAmount volB = std::get<1>(entry.second);
190 
191  inner[jss::currency_a] =
192  (isXRP(volA) ? "XRP_drops" : to_string(volA.issue()));
193  inner[jss::currency_b] =
194  (isXRP(volB) ? "XRP_drops" : to_string(volB.issue()));
195 
196  inner[jss::volume_a] =
197  (isXRP(volA) ? to_string(volA.xrp()) : to_string(volA.iou()));
198  inner[jss::volume_b] =
199  (isXRP(volB) ? to_string(volB.xrp()) : to_string(volB.iou()));
200 
201  inner[jss::high] = to_string(std::get<2>(entry.second).iou());
202  inner[jss::low] = to_string(std::get<3>(entry.second).iou());
203  inner[jss::open] = to_string(std::get<4>(entry.second).iou());
204  inner[jss::close] = to_string(std::get<5>(entry.second).iou());
205  }
206 
207  return jvObj;
208 }
209 
210 } // namespace RPC
211 } // namespace ripple
212 
213 #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:339
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:105
std::tuple
ripple::STAmount::iou
IOUAmount iou() const
Definition: STAmount.cpp:328
ripple::sfDeletedNode
const SField sfDeletedNode
ripple::STAmount::xrp
XRPAmount xrp() const
Definition: STAmount.cpp:313
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:44
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::STObject
Definition: STObject.h:51
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::STObject::getField
STBase & getField(SField const &field)
Definition: STObject.cpp:384
ripple::STObject::isFieldPresent
bool isFieldPresent(SField const &field) const
Definition: STObject.cpp:428
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
ripple::STBase::downcast
D & downcast()
Definition: STBase.h:145
ripple::STObject::getFieldU32
std::uint32_t getFieldU32(SField const &field) const
Definition: STObject.cpp:559
ripple::STObject::getFieldAmount
STAmount const & getFieldAmount(SField const &field) const
Definition: STObject.cpp:603
Json::Value
Represents a JSON value.
Definition: json_value.h:145