rippled
Loading...
Searching...
No Matches
OrderBookDB.cpp
1//------------------------------------------------------------------------------
2/*
3 This file is part of rippled: https://github.com/ripple/rippled
4 Copyright (c) 2012, 2013 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 <xrpld/app/ledger/LedgerMaster.h>
21#include <xrpld/app/ledger/OrderBookDB.h>
22#include <xrpld/app/main/Application.h>
23#include <xrpld/app/misc/AMMUtils.h>
24#include <xrpld/app/misc/NetworkOPs.h>
25#include <xrpld/core/Config.h>
26#include <xrpld/core/JobQueue.h>
27
28#include <xrpl/basics/Log.h>
29#include <xrpl/protocol/Indexes.h>
30
31namespace ripple {
32
34 : app_(app), seq_(0), j_(app.journal("OrderBookDB"))
35{
36}
37
38void
40{
42 {
43 JLOG(j_.warn()) << "Eliding full order book update: no ledger";
44 return;
45 }
46
47 auto seq = seq_.load();
48
49 if (seq != 0)
50 {
51 if ((ledger->seq() > seq) && ((ledger->seq() - seq) < 25600))
52 return;
53
54 if ((ledger->seq() <= seq) && ((seq - ledger->seq()) < 16))
55 return;
56 }
57
58 if (seq_.exchange(ledger->seq()) != seq)
59 return;
60
61 JLOG(j_.debug()) << "Full order book update: " << seq << " to "
62 << ledger->seq();
63
64 if (app_.config().PATH_SEARCH_MAX != 0)
65 {
66 if (app_.config().standalone())
67 update(ledger);
68 else
71 "OrderBookDB::update: " + std::to_string(ledger->seq()),
72 [this, ledger]() { update(ledger); });
73 }
74}
75
76void
78{
79 if (app_.config().PATH_SEARCH_MAX == 0)
80 return; // pathfinding has been disabled
81
82 // A newer full update job is pending
83 if (auto const seq = seq_.load(); seq > ledger->seq())
84 {
85 JLOG(j_.debug()) << "Eliding update for " << ledger->seq()
86 << " because of pending update to later " << seq;
87 return;
88 }
89
90 decltype(allBooks_) allBooks;
91 decltype(xrpBooks_) xrpBooks;
92
93 allBooks.reserve(allBooks_.size());
94 xrpBooks.reserve(xrpBooks_.size());
95
96 JLOG(j_.debug()) << "Beginning update (" << ledger->seq() << ")";
97
98 // walk through the entire ledger looking for orderbook/AMM entries
99 int cnt = 0;
100
101 try
102 {
103 for (auto& sle : ledger->sles)
104 {
105 if (app_.isStopping())
106 {
107 JLOG(j_.info())
108 << "Update halted because the process is stopping";
109 seq_.store(0);
110 return;
111 }
112
113 if (sle->getType() == ltDIR_NODE &&
114 sle->isFieldPresent(sfExchangeRate) &&
115 sle->getFieldH256(sfRootIndex) == sle->key())
116 {
117 Book book;
118
119 book.in.currency = sle->getFieldH160(sfTakerPaysCurrency);
120 book.in.account = sle->getFieldH160(sfTakerPaysIssuer);
121 book.out.currency = sle->getFieldH160(sfTakerGetsCurrency);
122 book.out.account = sle->getFieldH160(sfTakerGetsIssuer);
123
124 allBooks[book.in].insert(book.out);
125
126 if (isXRP(book.out))
127 xrpBooks.insert(book.in);
128
129 ++cnt;
130 }
131 else if (sle->getType() == ltAMM)
132 {
133 auto const issue1 = (*sle)[sfAsset].get<Issue>();
134 auto const issue2 = (*sle)[sfAsset2].get<Issue>();
135 auto addBook = [&](Issue const& in, Issue const& out) {
136 allBooks[in].insert(out);
137
138 if (isXRP(out))
139 xrpBooks.insert(in);
140
141 ++cnt;
142 };
143 addBook(issue1, issue2);
144 addBook(issue2, issue1);
145 }
146 }
147 }
148 catch (SHAMapMissingNode const& mn)
149 {
150 JLOG(j_.info()) << "Missing node in " << ledger->seq()
151 << " during update: " << mn.what();
152 seq_.store(0);
153 return;
154 }
155
156 JLOG(j_.debug()) << "Update completed (" << ledger->seq() << "): " << cnt
157 << " books found";
158
159 {
161 allBooks_.swap(allBooks);
162 xrpBooks_.swap(xrpBooks);
163 }
164
166}
167
168void
170{
171 bool toXRP = isXRP(book.out);
172
174
175 allBooks_[book.in].insert(book.out);
176
177 if (toXRP)
178 xrpBooks_.insert(book.in);
179}
180
181// return list of all orderbooks that want this issuerID and currencyID
184{
186
187 {
189
190 if (auto it = allBooks_.find(issue); it != allBooks_.end())
191 {
192 ret.reserve(it->second.size());
193
194 for (auto const& gets : it->second)
195 ret.push_back(Book(issue, gets));
196 }
197 }
198
199 return ret;
200}
201
202int
204{
206 if (auto it = allBooks_.find(issue); it != allBooks_.end())
207 return static_cast<int>(it->second.size());
208 return 0;
209}
210
211bool
213{
215 return xrpBooks_.count(issue) > 0;
216}
217
220{
222 auto ret = getBookListeners(book);
223
224 if (!ret)
225 {
226 ret = std::make_shared<BookListeners>();
227
228 mListeners[book] = ret;
229 XRPL_ASSERT(
230 getBookListeners(book) == ret,
231 "ripple::OrderBookDB::makeBookListeners : result roundtrip lookup");
232 }
233
234 return ret;
235}
236
239{
242
243 auto it0 = mListeners.find(book);
244 if (it0 != mListeners.end())
245 ret = it0->second;
246
247 return ret;
248}
249
250// Based on the meta, send the meta to the streams that are listening.
251// We need to determine which streams a given meta effects.
252void
255 AcceptedLedgerTx const& alTx,
256 MultiApiJson const& jvObj)
257{
259
260 // For this particular transaction, maintain the set of unique
261 // subscriptions that have already published it. This prevents sending
262 // the transaction multiple times if it touches multiple ltOFFER
263 // entries for the same book, or if it touches multiple books and a
264 // single client has subscribed to those books.
265 hash_set<std::uint64_t> havePublished;
266
267 for (auto const& node : alTx.getMeta().getNodes())
268 {
269 try
270 {
271 if (node.getFieldU16(sfLedgerEntryType) == ltOFFER)
272 {
273 auto process = [&, this](SField const& field) {
274 if (auto data = dynamic_cast<STObject const*>(
275 node.peekAtPField(field));
276 data && data->isFieldPresent(sfTakerPays) &&
277 data->isFieldPresent(sfTakerGets))
278 {
279 auto listeners = getBookListeners(
280 {data->getFieldAmount(sfTakerGets).issue(),
281 data->getFieldAmount(sfTakerPays).issue()});
282 if (listeners)
283 listeners->publish(jvObj, havePublished);
284 }
285 };
286
287 // We need a field that contains the TakerGets and TakerPays
288 // parameters.
289 if (node.getFName() == sfModifiedNode)
290 process(sfPreviousFields);
291 else if (node.getFName() == sfCreatedNode)
292 process(sfNewFields);
293 else if (node.getFName() == sfDeletedNode)
294 process(sfFinalFields);
295 }
296 }
297 catch (std::exception const& ex)
298 {
299 JLOG(j_.info())
300 << "processTxn: field not found (" << ex.what() << ")";
301 }
302 }
303}
304
305} // namespace ripple
Stream debug() const
Definition: Journal.h:328
Stream info() const
Definition: Journal.h:334
Stream warn() const
Definition: Journal.h:340
A transaction that is in a closed ledger.
TxMeta const & getMeta() const
virtual Config & config()=0
virtual bool isStopping() const =0
virtual JobQueue & getJobQueue()=0
virtual NetworkOPs & getOPs()=0
virtual LedgerMaster & getLedgerMaster()=0
Specifies an order book.
Definition: Book.h:35
Issue in
Definition: Book.h:37
Issue out
Definition: Book.h:38
bool standalone() const
Definition: Config.h:337
int PATH_SEARCH_MAX
Definition: Config.h:198
A currency issued by an account.
Definition: Issue.h:36
AccountID account
Definition: Issue.h:39
Currency currency
Definition: Issue.h:38
bool addJob(JobType type, std::string const &name, JobHandler &&jobHandler)
Adds a job to the JobQueue.
Definition: JobQueue.h:166
virtual bool isNeedNetworkLedger()=0
BookListeners::pointer getBookListeners(Book const &)
void addOrderBook(Book const &)
void processTxn(std::shared_ptr< ReadView const > const &ledger, AcceptedLedgerTx const &alTx, MultiApiJson const &jvObj)
bool isBookToXRP(Issue const &)
BookListeners::pointer makeBookListeners(Book const &)
std::atomic< std::uint32_t > seq_
Definition: OrderBookDB.h:86
OrderBookDB(Application &app)
Definition: OrderBookDB.cpp:33
std::vector< Book > getBooksByTakerPays(Issue const &)
void update(std::shared_ptr< ReadView const > const &ledger)
Definition: OrderBookDB.cpp:77
int getBookSize(Issue const &)
std::recursive_mutex mLock
Definition: OrderBookDB.h:80
beast::Journal const j_
Definition: OrderBookDB.h:88
BookToListenersMap mListeners
Definition: OrderBookDB.h:84
hash_set< Issue > xrpBooks_
Definition: OrderBookDB.h:78
hardened_hash_map< Issue, hardened_hash_set< Issue > > allBooks_
Definition: OrderBookDB.h:75
void setup(std::shared_ptr< ReadView const > const &ledger)
Definition: OrderBookDB.cpp:39
Application & app_
Definition: OrderBookDB.h:72
Identifies fields.
Definition: SField.h:143
STArray & getNodes()
Definition: TxMeta.h:105
T end(T... args)
T exchange(T... args)
T find(T... args)
T load(T... args)
Use hash_* containers for keys that do not need a cryptographically secure hashing algorithm.
Definition: algorithm.h:26
bool isXRP(AccountID const &c)
Definition: AccountID.h:91
@ jtUPDATE_PF
Definition: Job.h:56
T push_back(T... args)
T reserve(T... args)
T store(T... args)
Set the sequence number on a JTx.
Definition: seq.h:34
T to_string(T... args)
T what(T... args)