rippled
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 <ripple/app/ledger/LedgerMaster.h>
21 #include <ripple/app/ledger/OrderBookDB.h>
22 #include <ripple/app/main/Application.h>
23 #include <ripple/basics/Log.h>
24 #include <ripple/core/Config.h>
25 #include <ripple/core/JobQueue.h>
26 #include <ripple/protocol/Indexes.h>
27 
28 namespace ripple {
29 
31  : Stoppable("OrderBookDB", parent)
32  , app_(app)
33  , mSeq(0)
34  , j_(app.journal("OrderBookDB"))
35 {
36 }
37 
38 void
40 {
42  mSeq = 0;
43 }
44 
45 void
47 {
48  {
50  auto seq = ledger->info().seq;
51 
52  // Do a full update every 256 ledgers
53  if (mSeq != 0)
54  {
55  if (seq == mSeq)
56  return;
57  if ((seq > mSeq) && ((seq - mSeq) < 256))
58  return;
59  if ((seq < mSeq) && ((mSeq - seq) < 16))
60  return;
61  }
62 
63  JLOG(j_.debug()) << "Advancing from " << mSeq << " to " << seq;
64 
65  mSeq = seq;
66  }
67 
68  if (app_.config().PATH_SEARCH_MAX == 0)
69  {
70  // nothing to do
71  }
72  else if (app_.config().standalone())
73  update(ledger);
74  else
76  jtUPDATE_PF, "OrderBookDB::update", [this, ledger](Job&) {
77  update(ledger);
78  });
79 }
80 
81 void
83 {
84  hash_set<uint256> seen;
87  hash_set<Issue> XRPBooks;
88 
89  JLOG(j_.debug()) << "OrderBookDB::update>";
90 
91  if (app_.config().PATH_SEARCH_MAX == 0)
92  {
93  // pathfinding has been disabled
94  return;
95  }
96 
97  // walk through the entire ledger looking for orderbook entries
98  int books = 0;
99 
100  try
101  {
102  for (auto& sle : ledger->sles)
103  {
104  if (isStopping())
105  {
106  JLOG(j_.info())
107  << "OrderBookDB::update exiting due to isStopping";
109  mSeq = 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  book.in.currency = sle->getFieldH160(sfTakerPaysCurrency);
119  book.in.account = sle->getFieldH160(sfTakerPaysIssuer);
120  book.out.account = sle->getFieldH160(sfTakerGetsIssuer);
121  book.out.currency = sle->getFieldH160(sfTakerGetsCurrency);
122 
123  uint256 index = getBookBase(book);
124  if (seen.insert(index).second)
125  {
126  auto orderBook = std::make_shared<OrderBook>(index, book);
127  sourceMap[book.in].push_back(orderBook);
128  destMap[book.out].push_back(orderBook);
129  if (isXRP(book.out))
130  XRPBooks.insert(book.in);
131  ++books;
132  }
133  }
134  }
135  }
136  catch (SHAMapMissingNode const& mn)
137  {
138  JLOG(j_.info()) << "OrderBookDB::update: " << mn.what();
140  mSeq = 0;
141  return;
142  }
143 
144  JLOG(j_.debug()) << "OrderBookDB::update< " << books << " books found";
145  {
147 
148  mXRPBooks.swap(XRPBooks);
149  mSourceMap.swap(sourceMap);
150  mDestMap.swap(destMap);
151  }
153 }
154 
155 void
157 {
158  bool toXRP = isXRP(book.out);
160 
161  if (toXRP)
162  {
163  // We don't want to search through all the to-XRP or from-XRP order
164  // books!
165  for (auto ob : mSourceMap[book.in])
166  {
167  if (isXRP(ob->getCurrencyOut())) // also to XRP
168  return;
169  }
170  }
171  else
172  {
173  for (auto ob : mDestMap[book.out])
174  {
175  if (ob->getCurrencyIn() == book.in.currency &&
176  ob->getIssuerIn() == book.in.account)
177  {
178  return;
179  }
180  }
181  }
182  uint256 index = getBookBase(book);
183  auto orderBook = std::make_shared<OrderBook>(index, book);
184 
185  mSourceMap[book.in].push_back(orderBook);
186  mDestMap[book.out].push_back(orderBook);
187  if (toXRP)
188  mXRPBooks.insert(book.in);
189 }
190 
191 // return list of all orderbooks that want this issuerID and currencyID
194 {
196  auto it = mSourceMap.find(issue);
197  return it == mSourceMap.end() ? OrderBook::List() : it->second;
198 }
199 
200 int
202 {
204  auto it = mSourceMap.find(issue);
205  return it == mSourceMap.end() ? 0 : it->second.size();
206 }
207 
208 bool
210 {
212  return mXRPBooks.count(issue) > 0;
213 }
214 
217 {
219  auto ret = getBookListeners(book);
220 
221  if (!ret)
222  {
223  ret = std::make_shared<BookListeners>();
224 
225  mListeners[book] = ret;
226  assert(getBookListeners(book) == ret);
227  }
228 
229  return ret;
230 }
231 
234 {
237 
238  auto it0 = mListeners.find(book);
239  if (it0 != mListeners.end())
240  ret = it0->second;
241 
242  return ret;
243 }
244 
245 // Based on the meta, send the meta to the streams that are listening.
246 // We need to determine which streams a given meta effects.
247 void
249  std::shared_ptr<ReadView const> const& ledger,
250  const AcceptedLedgerTx& alTx,
251  Json::Value const& jvObj)
252 {
254  if (alTx.getResult() == tesSUCCESS)
255  {
256  // For this particular transaction, maintain the set of unique
257  // subscriptions that have already published it. This prevents sending
258  // the transaction multiple times if it touches multiple ltOFFER
259  // entries for the same book, or if it touches multiple books and a
260  // single client has subscribed to those books.
261  hash_set<std::uint64_t> havePublished;
262 
263  // Check if this is an offer or an offer cancel or a payment that
264  // consumes an offer.
265  // Check to see what the meta looks like.
266  for (auto& node : alTx.getMeta()->getNodes())
267  {
268  try
269  {
270  if (node.getFieldU16(sfLedgerEntryType) == ltOFFER)
271  {
272  SField const* field = nullptr;
273 
274  // We need a field that contains the TakerGets and TakerPays
275  // parameters.
276  if (node.getFName() == sfModifiedNode)
277  field = &sfPreviousFields;
278  else if (node.getFName() == sfCreatedNode)
279  field = &sfNewFields;
280  else if (node.getFName() == sfDeletedNode)
281  field = &sfFinalFields;
282 
283  if (field)
284  {
285  auto data = dynamic_cast<const STObject*>(
286  node.peekAtPField(*field));
287 
288  if (data && data->isFieldPresent(sfTakerPays) &&
289  data->isFieldPresent(sfTakerGets))
290  {
291  // determine the OrderBook
292  Book b{
293  data->getFieldAmount(sfTakerGets).issue(),
294  data->getFieldAmount(sfTakerPays).issue()};
295 
296  auto listeners = getBookListeners(b);
297  if (listeners)
298  {
299  listeners->publish(jvObj, havePublished);
300  }
301  }
302  }
303  }
304  }
305  catch (std::exception const&)
306  {
307  JLOG(j_.info())
308  << "Fields not found in OrderBookDB::processTxn";
309  }
310  }
311  }
312 }
313 
314 } // namespace ripple
ripple::Application
Definition: Application.h:101
ripple::Issue
A currency issued by an account.
Definition: Issue.h:34
std::shared_ptr
STL class.
std::exception
STL class.
ripple::Book::out
Issue out
Definition: Book.h:36
ripple::sfTakerGetsCurrency
const SF_HASH160 sfTakerGetsCurrency
std::unordered_set
STL class.
ripple::OrderBookDB::j_
const beast::Journal j_
Definition: OrderBookDB.h:96
ripple::OrderBookDB::mLock
std::recursive_mutex mLock
Definition: OrderBookDB.h:88
ripple::getBookBase
uint256 getBookBase(Book const &book)
Definition: Indexes.cpp:79
std::vector
STL class.
std::unordered_map::find
T find(T... args)
ripple::Issue::currency
Currency currency
Definition: Issue.h:37
ripple::OrderBook::List
std::vector< pointer > List
Definition: OrderBook.h:31
std::lock_guard
STL class.
ripple::OrderBookDB::mDestMap
IssueToOrderBook mDestMap
Definition: OrderBookDB.h:83
ripple::sfFinalFields
const SField sfFinalFields
ripple::JobQueue::addJob
bool addJob(JobType type, std::string const &name, JobHandler &&jobHandler)
Adds a job to the JobQueue.
Definition: JobQueue.h:166
ripple::jtUPDATE_PF
@ jtUPDATE_PF
Definition: Job.h:50
ripple::OrderBookDB::getBooksByTakerPays
OrderBook::List getBooksByTakerPays(Issue const &)
Definition: OrderBookDB.cpp:193
ripple::OrderBookDB::update
void update(std::shared_ptr< ReadView const > const &ledger)
Definition: OrderBookDB.cpp:82
ripple::OrderBookDB::invalidate
void invalidate()
Definition: OrderBookDB.cpp:39
ripple::sfDeletedNode
const SField sfDeletedNode
ripple::OrderBookDB::OrderBookDB
OrderBookDB(Application &app, Stoppable &parent)
Definition: OrderBookDB.cpp:30
ripple::sfTakerGetsIssuer
const SF_HASH160 sfTakerGetsIssuer
ripple::Config::PATH_SEARCH_MAX
int PATH_SEARCH_MAX
Definition: Config.h:165
ripple::SHAMapMissingNode
Definition: SHAMapMissingNode.h:55
ripple::OrderBookDB::isBookToXRP
bool isBookToXRP(Issue const &)
Definition: OrderBookDB.cpp:209
ripple::base_uint< 256 >
ripple::sfTakerPays
const SF_AMOUNT sfTakerPays
ripple::Stoppable
Provides an interface for starting and stopping.
Definition: Stoppable.h:201
ripple::OrderBookDB::getBookSize
int getBookSize(Issue const &)
Definition: OrderBookDB.cpp:201
ripple::OrderBookDB::processTxn
void processTxn(std::shared_ptr< ReadView const > const &ledger, const AcceptedLedgerTx &alTx, Json::Value const &jvObj)
Definition: OrderBookDB.cpp:248
ripple::AcceptedLedgerTx::getMeta
std::shared_ptr< TxMeta > const & getMeta() const
Definition: AcceptedLedgerTx.h:76
ripple::Application::getLedgerMaster
virtual LedgerMaster & getLedgerMaster()=0
ripple::sfTakerPaysIssuer
const SF_HASH160 sfTakerPaysIssuer
ripple::sfNewFields
const SField sfNewFields
ripple::Application::config
virtual Config & config()=0
ripple::Config::standalone
bool standalone() const
Definition: Config.h:262
ripple::Application::getJobQueue
virtual JobQueue & getJobQueue()=0
ripple::AcceptedLedgerTx
A transaction that is in a closed ledger.
Definition: AcceptedLedgerTx.h:50
ripple::sfModifiedNode
const SField sfModifiedNode
ripple::OrderBookDB::mSourceMap
IssueToOrderBook mSourceMap
Definition: OrderBookDB.h:80
beast::Journal::info
Stream info() const
Definition: Journal.h:321
ripple::sfTakerGets
const SF_AMOUNT sfTakerGets
ripple::OrderBookDB::setup
void setup(std::shared_ptr< ReadView const > const &ledger)
Definition: OrderBookDB.cpp:46
ripple::isXRP
bool isXRP(AccountID const &c)
Definition: AccountID.h:89
ripple::Job
Definition: Job.h:82
ripple::sfExchangeRate
const SF_UINT64 sfExchangeRate
ripple::OrderBookDB::makeBookListeners
BookListeners::pointer makeBookListeners(Book const &)
Definition: OrderBookDB.cpp:216
ripple::AcceptedLedgerTx::getResult
TER getResult() const
Definition: AcceptedLedgerTx.h:98
ripple::LedgerMaster::newOrderBookDB
bool newOrderBookDB()
Definition: LedgerMaster.cpp:1535
ripple::sfTakerPaysCurrency
const SF_HASH160 sfTakerPaysCurrency
ripple::OrderBookDB::mSeq
std::uint32_t mSeq
Definition: OrderBookDB.h:94
ripple::OrderBookDB::mListeners
BookToListenersMap mListeners
Definition: OrderBookDB.h:92
ripple::sfPreviousFields
const SField sfPreviousFields
std::unordered_map::swap
T swap(T... args)
ripple::OrderBookDB::getBookListeners
BookListeners::pointer getBookListeners(Book const &)
Definition: OrderBookDB.cpp:233
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:109
std::unordered_set::insert
T insert(T... args)
ripple::sfCreatedNode
const SField sfCreatedNode
ripple::sfRootIndex
const SF_HASH256 sfRootIndex
beast::Journal::debug
Stream debug() const
Definition: Journal.h:315
ripple::OrderBookDB::mXRPBooks
hash_set< Issue > mXRPBooks
Definition: OrderBookDB.h:86
ripple::OrderBookDB::app_
Application & app_
Definition: OrderBookDB.h:77
ripple::Book
Specifies an order book.
Definition: Book.h:32
std::unordered_map::end
T end(T... args)
ripple::ltDIR_NODE
@ ltDIR_NODE
Directory node.
Definition: LedgerFormats.h:64
ripple::ltOFFER
@ ltOFFER
Definition: LedgerFormats.h:72
std::unordered_map
STL class.
ripple::tesSUCCESS
@ tesSUCCESS
Definition: TER.h:216
ripple::Book::in
Issue in
Definition: Book.h:35
ripple::Issue::account
AccountID account
Definition: Issue.h:38
std::runtime_error::what
T what(T... args)
Json::Value
Represents a JSON value.
Definition: json_value.h:145
ripple::Stoppable::isStopping
bool isStopping() const
Returns true if the stoppable should stop.
Definition: Stoppable.cpp:54
ripple::OrderBookDB::addOrderBook
void addOrderBook(Book const &)
Definition: OrderBookDB.cpp:156