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 >>
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::string key{ss.str()};
152
153 if (tally.find(key) == tally.end())
154 tally[key] = {
155 first, // side A vol
156 second, // side B vol
157 rate, // high
158 rate, // low
159 rate, // open
160 rate // close
161 };
162 else
163 {
164 // increment volume
165 auto& entry = tally[key];
166
167 std::get<0>(entry) += first; // side A vol
168 std::get<1>(entry) += second; // side B vol
169
170 if (std::get<2>(entry) < rate) // high
171 std::get<2>(entry) = rate;
172
173 if (std::get<3>(entry) > rate) // low
174 std::get<3>(entry) = rate;
175
176 std::get<5>(entry) = rate; // close
177 }
178 }
179 }
180
182 jvObj[jss::type] = "bookChanges";
183
184 // retrieve validated information from LedgerHeader class
185 jvObj[jss::validated] = lpAccepted->info().validated;
186 jvObj[jss::ledger_index] = lpAccepted->info().seq;
187 jvObj[jss::ledger_hash] = to_string(lpAccepted->info().hash);
188 jvObj[jss::ledger_time] = Json::Value::UInt(
189 lpAccepted->info().closeTime.time_since_epoch().count());
190
191 jvObj[jss::changes] = Json::arrayValue;
192
193 for (auto const& entry : tally)
194 {
195 Json::Value& inner = jvObj[jss::changes].append(Json::objectValue);
196
197 STAmount volA = std::get<0>(entry.second);
198 STAmount volB = std::get<1>(entry.second);
199
200 inner[jss::currency_a] =
201 (isXRP(volA) ? "XRP_drops" : to_string(volA.issue()));
202 inner[jss::currency_b] =
203 (isXRP(volB) ? "XRP_drops" : to_string(volB.issue()));
204
205 inner[jss::volume_a] =
206 (isXRP(volA) ? to_string(volA.xrp()) : to_string(volA.iou()));
207 inner[jss::volume_b] =
208 (isXRP(volB) ? to_string(volB.xrp()) : to_string(volB.iou()));
209
210 inner[jss::high] = to_string(std::get<2>(entry.second).iou());
211 inner[jss::low] = to_string(std::get<3>(entry.second).iou());
212 inner[jss::open] = to_string(std::get<4>(entry.second).iou());
213 inner[jss::close] = to_string(std::get<5>(entry.second).iou());
214 }
215
216 return jvObj;
217}
218
219} // namespace RPC
220} // namespace ripple
221
222#endif
Represents a JSON value.
Definition: json_value.h:150
Json::UInt UInt
Definition: json_value.h:157
Value & append(Value const &value)
Append value to array at the end.
Definition: json_value.cpp:910
Identifies fields.
Definition: SField.h:143
IOUAmount iou() const
Definition: STAmount.cpp:322
XRPAmount xrp() const
Definition: STAmount.cpp:306
Issue const & issue() const
Definition: STAmount.h:496
JSON (JavaScript Object Notation).
Definition: json_errors.h:25
@ arrayValue
array value (ordered list)
Definition: json_value.h:45
@ objectValue
object value (collection of name/value pairs).
Definition: json_value.h:46
Json::Value computeBookChanges(std::shared_ptr< L const > const &lpAccepted)
Definition: BookChanges.h:47
Json::Value rate(Account const &account, double multiplier)
Set a transfer rate.
Definition: rate.cpp:32
Use hash_* containers for keys that do not need a cryptographically secure hashing algorithm.
Definition: algorithm.h:26
STAmount divide(STAmount const &amount, Rate const &rate)
Definition: Rate2.cpp:93
bool isXRP(AccountID const &c)
Definition: AccountID.h:91
Issue const & noIssue()
Returns an asset specifier that represents no account and currency.
Definition: Issue.h:126
std::string to_string(base_uint< Bits, Tag > const &a)
Definition: base_uint.h:630
T str(T... args)