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