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