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/app/misc/AMMUtils.h>
24 #include <ripple/app/misc/NetworkOPs.h>
25 #include <ripple/basics/Log.h>
26 #include <ripple/core/Config.h>
27 #include <ripple/core/JobQueue.h>
28 #include <ripple/protocol/Indexes.h>
29 
30 namespace ripple {
31 
33  : app_(app), seq_(0), j_(app.journal("OrderBookDB"))
34 {
35 }
36 
37 void
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 ((seq > ledger->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 
75 void
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];
133  auto const issue2 = (*sle)[sfAsset2];
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 
167 void
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 {
184  std::vector<Book> ret;
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 
201 int
203 {
205  if (auto it = allBooks_.find(issue); it != allBooks_.end())
206  return static_cast<int>(it->second.size());
207  return 0;
208 }
209 
210 bool
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  assert(getBookListeners(book) == ret);
229  }
230 
231  return ret;
232 }
233 
236 {
239 
240  auto it0 = mListeners.find(book);
241  if (it0 != mListeners.end())
242  ret = it0->second;
243 
244  return ret;
245 }
246 
247 // Based on the meta, send the meta to the streams that are listening.
248 // We need to determine which streams a given meta effects.
249 void
251  std::shared_ptr<ReadView const> const& ledger,
252  const AcceptedLedgerTx& alTx,
253  MultiApiJson const& jvObj)
254 {
256 
257  // For this particular transaction, maintain the set of unique
258  // subscriptions that have already published it. This prevents sending
259  // the transaction multiple times if it touches multiple ltOFFER
260  // entries for the same book, or if it touches multiple books and a
261  // single client has subscribed to those books.
262  hash_set<std::uint64_t> havePublished;
263 
264  for (auto const& node : alTx.getMeta().getNodes())
265  {
266  try
267  {
268  if (node.getFieldU16(sfLedgerEntryType) == ltOFFER)
269  {
270  auto process = [&, this](SField const& field) {
271  if (auto data = dynamic_cast<STObject const*>(
272  node.peekAtPField(field));
273  data && data->isFieldPresent(sfTakerPays) &&
274  data->isFieldPresent(sfTakerGets))
275  {
276  auto listeners = getBookListeners(
277  {data->getFieldAmount(sfTakerGets).issue(),
278  data->getFieldAmount(sfTakerPays).issue()});
279  if (listeners)
280  listeners->publish(jvObj, havePublished);
281  }
282  };
283 
284  // We need a field that contains the TakerGets and TakerPays
285  // parameters.
286  if (node.getFName() == sfModifiedNode)
287  process(sfPreviousFields);
288  else if (node.getFName() == sfCreatedNode)
289  process(sfNewFields);
290  else if (node.getFName() == sfDeletedNode)
291  process(sfFinalFields);
292  }
293  }
294  catch (std::exception const& ex)
295  {
296  JLOG(j_.info())
297  << "processTxn: field not found (" << ex.what() << ")";
298  }
299  }
300 }
301 
302 } // namespace ripple
ripple::Application
Definition: Application.h:116
ripple::sfRootIndex
const SF_UINT256 sfRootIndex
ripple::Issue
A currency issued by an account.
Definition: Issue.h:35
ripple::sfAsset
const SF_ISSUE sfAsset
std::shared_ptr
STL class.
std::exception
STL class.
ripple::Book::out
Issue out
Definition: Book.h:37
std::unordered_set
STL class.
ripple::OrderBookDB::j_
const beast::Journal j_
Definition: OrderBookDB.h:87
ripple::OrderBookDB::mLock
std::recursive_mutex mLock
Definition: OrderBookDB.h:79
std::vector::reserve
T reserve(T... args)
std::vector
STL class.
std::unordered_map::find
T find(T... args)
ripple::sfTakerPaysCurrency
const SF_UINT160 sfTakerPaysCurrency
ripple::Issue::currency
Currency currency
Definition: Issue.h:38
beast::Journal::warn
Stream warn() const
Definition: Journal.h:326
std::lock_guard
STL class.
ripple::sfFinalFields
const SField sfFinalFields
ripple::QualityDirection::in
@ in
ripple::OrderBookDB::seq_
std::atomic< std::uint32_t > seq_
Definition: OrderBookDB.h:85
ripple::Application::isStopping
virtual bool isStopping() const =0
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:57
ripple::OrderBookDB::update
void update(std::shared_ptr< ReadView const > const &ledger)
Definition: OrderBookDB.cpp:76
ripple::sfTakerGetsCurrency
const SF_UINT160 sfTakerGetsCurrency
ripple::sfDeletedNode
const SField sfDeletedNode
ripple::OrderBookDB::xrpBooks_
hash_set< Issue > xrpBooks_
Definition: OrderBookDB.h:77
ripple::Application::getOPs
virtual NetworkOPs & getOPs()=0
ripple::Config::PATH_SEARCH_MAX
int PATH_SEARCH_MAX
Definition: Config.h:208
ripple::ltDIR_NODE
@ ltDIR_NODE
A ledger object which contains a list of object identifiers.
Definition: LedgerFormats.h:66
ripple::SHAMapMissingNode
Definition: SHAMapMissingNode.h:55
std::vector::push_back
T push_back(T... args)
ripple::OrderBookDB::isBookToXRP
bool isBookToXRP(Issue const &)
Definition: OrderBookDB.cpp:211
ripple::sfTakerGetsIssuer
const SF_UINT160 sfTakerGetsIssuer
ripple::sfTakerPays
const SF_AMOUNT sfTakerPays
ripple::ltOFFER
@ ltOFFER
A ledger object which describes an offer on the DEX.
Definition: LedgerFormats.h:92
ripple::sfAsset2
const SF_ISSUE sfAsset2
ripple::OrderBookDB::getBookSize
int getBookSize(Issue const &)
Definition: OrderBookDB.cpp:202
ripple::QualityDirection::out
@ out
ripple::NetworkOPs::isNeedNetworkLedger
virtual bool isNeedNetworkLedger()=0
ripple::Application::getLedgerMaster
virtual LedgerMaster & getLedgerMaster()=0
std::atomic::load
T load(T... args)
ripple::sfNewFields
const SField sfNewFields
ripple::Application::config
virtual Config & config()=0
ripple::Config::standalone
bool standalone() const
Definition: Config.h:345
std::to_string
T to_string(T... args)
ripple::Application::getJobQueue
virtual JobQueue & getJobQueue()=0
ripple::AcceptedLedgerTx
A transaction that is in a closed ledger.
Definition: AcceptedLedgerTx.h:43
ripple::sfModifiedNode
const SField sfModifiedNode
beast::Journal::info
Stream info() const
Definition: Journal.h:320
ripple::sfTakerGets
const SF_AMOUNT sfTakerGets
ripple::OrderBookDB::setup
void setup(std::shared_ptr< ReadView const > const &ledger)
Definition: OrderBookDB.cpp:38
ripple::isXRP
bool isXRP(AccountID const &c)
Definition: AccountID.h:91
ripple::sfExchangeRate
const SF_UINT64 sfExchangeRate
ripple::OrderBookDB::makeBookListeners
BookListeners::pointer makeBookListeners(Book const &)
Definition: OrderBookDB.cpp:218
ripple::LedgerMaster::newOrderBookDB
bool newOrderBookDB()
Definition: LedgerMaster.cpp:1662
ripple::OrderBookDB::allBooks_
hardened_hash_map< Issue, hardened_hash_set< Issue > > allBooks_
Definition: OrderBookDB.h:74
ripple::OrderBookDB::mListeners
BookToListenersMap mListeners
Definition: OrderBookDB.h:83
ripple::sfPreviousFields
const SField sfPreviousFields
ripple::OrderBookDB::getBookListeners
BookListeners::pointer getBookListeners(Book const &)
Definition: OrderBookDB.cpp:235
ripple::STObject
Definition: STObject.h:53
ripple
Use hash_* containers for keys that do not need a cryptographically secure hashing algorithm.
Definition: RCLCensorshipDetector.h:29
std::atomic::exchange
T exchange(T... args)
ripple::TxMeta::getNodes
STArray & getNodes()
Definition: TxMeta.h:100
ripple::sfLedgerEntryType
const SF_UINT16 sfLedgerEntryType
ripple::OrderBookDB::processTxn
void processTxn(std::shared_ptr< ReadView const > const &ledger, const AcceptedLedgerTx &alTx, MultiApiJson const &jvObj)
Definition: OrderBookDB.cpp:250
ripple::SField
Identifies fields.
Definition: SField.h:139
ripple::AcceptedLedgerTx::getMeta
TxMeta const & getMeta() const
Definition: AcceptedLedgerTx.h:57
ripple::sfTakerPaysIssuer
const SF_UINT160 sfTakerPaysIssuer
ripple::sfCreatedNode
const SField sfCreatedNode
ripple::OrderBookDB::OrderBookDB
OrderBookDB(Application &app)
Definition: OrderBookDB.cpp:32
std::atomic::store
T store(T... args)
beast::Journal::debug
Stream debug() const
Definition: Journal.h:314
ripple::MultivarJson
Definition: MultivarJson.h:34
ripple::OrderBookDB::app_
Application & app_
Definition: OrderBookDB.h:71
ripple::Book
Specifies an order book.
Definition: Book.h:33
std::unordered_map::end
T end(T... args)
ripple::OrderBookDB::getBooksByTakerPays
std::vector< Book > getBooksByTakerPays(Issue const &)
Definition: OrderBookDB.cpp:182
ripple::ltAMM
@ ltAMM
The ledger object which tracks the AMM.
Definition: LedgerFormats.h:187
ripple::Book::in
Issue in
Definition: Book.h:36
ripple::Issue::account
AccountID account
Definition: Issue.h:39
std::runtime_error::what
T what(T... args)
ripple::OrderBookDB::addOrderBook
void addOrderBook(Book const &)
Definition: OrderBookDB.cpp:168