1#include <xrpld/app/ledger/OrderBookDBImpl.h>
3#include <xrpld/app/ledger/LedgerMaster.h>
5#include <xrpl/basics/Log.h>
6#include <xrpl/basics/base_uint.h>
7#include <xrpl/beast/utility/instrumentation.h>
8#include <xrpl/core/Job.h>
9#include <xrpl/core/JobQueue.h>
10#include <xrpl/core/ServiceRegistry.h>
11#include <xrpl/ledger/AcceptedLedgerTx.h>
12#include <xrpl/ledger/OrderBookDB.h>
13#include <xrpl/ledger/ReadView.h>
14#include <xrpl/protocol/Asset.h>
15#include <xrpl/protocol/Book.h>
16#include <xrpl/protocol/Issue.h>
17#include <xrpl/protocol/LedgerFormats.h>
18#include <xrpl/protocol/SField.h>
19#include <xrpl/protocol/UintTypes.h>
20#include <xrpl/server/NetworkOPs.h>
21#include <xrpl/shamap/SHAMapMissingNode.h>
38 ,
j_(registry.getJournal(
"OrderBookDB"))
53 JLOG(
j_.warn()) <<
"Eliding full order book update: no ledger";
57 auto seq =
seq_.load();
61 if ((ledger->seq() > seq) && ((ledger->seq() - seq) < 25600))
64 if ((ledger->seq() <= seq) && ((seq - ledger->seq()) < 16))
68 if (
seq_.exchange(ledger->seq()) != seq)
71 JLOG(
j_.debug()) <<
"Full order book update: " << seq <<
" to " << ledger->seq();
98 if (
auto const seq =
seq_.load(); seq > ledger->seq())
100 JLOG(
j_.debug()) <<
"Eliding update for " << ledger->seq()
101 <<
" because of pending update to later " << seq;
113 JLOG(
j_.debug()) <<
"Beginning update (" << ledger->seq() <<
")";
120 for (
auto& sle : ledger->sles)
124 JLOG(
j_.info()) <<
"Update halted because the process is stopping";
129 if (sle->getType() == ltDIR_NODE && sle->isFieldPresent(sfExchangeRate) &&
130 sle->getFieldH256(sfRootIndex) == sle->key())
134 if (sle->isFieldPresent(sfTakerPaysCurrency))
137 issue.
currency = sle->getFieldH160(sfTakerPaysCurrency);
138 issue.
account = sle->getFieldH160(sfTakerPaysIssuer);
144 sle->isFieldPresent(sfTakerPaysMPT),
145 "OrderBookDB::update, must be TakerPaysMPT");
146 book.in = sle->getFieldH192(sfTakerPaysMPT);
148 if (sle->isFieldPresent(sfTakerGetsCurrency))
151 issue.
currency = sle->getFieldH160(sfTakerGetsCurrency);
152 issue.
account = sle->getFieldH160(sfTakerGetsIssuer);
158 sle->isFieldPresent(sfTakerGetsMPT),
159 "OrderBookDB::update, must be TakerGetsMPT");
160 book.out = sle->getFieldH192(sfTakerGetsMPT);
162 book.domain = (*sle)[~sfDomainID];
166 domainBooks[{book.in, *book.domain}].insert(book.out);
170 allBooks[book.in].insert(book.out);
173 if (book.domain &&
isXRP(book.out))
175 xrpDomainBooks.insert({book.in, *book.domain});
177 else if (
isXRP(book.out))
179 xrpBooks.insert(book.in);
184 else if (sle->getType() == ltAMM)
186 auto const asset1 = (*sle)[sfAsset];
187 auto const asset2 = (*sle)[sfAsset2];
188 auto addBook = [&](
Asset const& in,
Asset const& out) {
189 allBooks[in].insert(out);
196 addBook(asset1, asset2);
197 addBook(asset2, asset1);
203 JLOG(
j_.info()) <<
"Missing node in " << ledger->seq() <<
" during update: " << mn.
what();
208 JLOG(
j_.debug()) <<
"Update completed (" << ledger->seq() <<
"): " << cnt <<
" books found";
218 registry_.get().getLedgerMaster().newOrderBookDB();
224 bool const toXRP =
isXRP(book.out);
237 if (book.domain && toXRP)
256 auto getBooks = [&](
auto const& container,
auto const& key) {
257 if (
auto it = container.find(key); it != container.end())
259 auto const& books = it->second;
262 for (
auto const& gets : books)
288 return static_cast<int>(it->second.size());
293 return static_cast<int>(it->second.size());
317 if (node.getFieldU16(sfLedgerEntryType) == ltOFFER)
320 if (
auto data =
dynamic_cast<STObject const*
>(node.peekAtPField(field)); data &&
321 data->isFieldPresent(sfTakerPays) && data->isFieldPresent(sfTakerGets))
324 data->getFieldAmount(sfTakerGets).asset(),
325 data->getFieldAmount(sfTakerPays).asset(),
326 (*data)[~sfDomainID]);
330 if (node.getFName() == sfModifiedNode)
334 else if (node.getFName() == sfCreatedNode)
338 else if (node.getFName() == sfDeletedNode)
349 JLOG(j.
warn()) <<
"affectedBooks: skipping malformed node (" << ex.
what() <<
")";
A generic endpoint for log messages.
A transaction that is in a closed ledger.
TxMeta const & getMeta() const
A currency issued by an account.
hardened_hash_map< std::pair< Asset, Domain >, hardened_hash_set< Asset > > domainBooks_
void addOrderBook(Book const &book) override
Add an order book to track.
std::vector< Book > getBooksByTakerPays(Asset const &asset, std::optional< Domain > const &domain=std::nullopt) override
Get all order books that want a specific issue.
hash_set< Asset > xrpBooks_
void setup(std::shared_ptr< ReadView const > const &ledger) override
Initialize or update the order book database with a new ledger.
bool isBookToXRP(Asset const &asset, std::optional< Domain > const &domain=std::nullopt) override
Check if an order book to XRP exists for the given issue.
OrderBookDBImpl(ServiceRegistry ®istry, OrderBookDBConfig const &config)
hardened_hash_map< Asset, hardened_hash_set< Asset > > allBooks_
std::atomic< std::uint32_t > seq_
int getBookSize(Asset const &asset, std::optional< Domain > const &domain=std::nullopt) override
Get the count of order books that want a specific issue.
hash_set< std::pair< Asset, Domain > > xrpDomainBooks_
void update(std::shared_ptr< ReadView const > const &ledger)
std::recursive_mutex lock_
std::reference_wrapper< ServiceRegistry > registry_
Service registry for dependency injection.
T emplace_back(T... args)
Use hash_* containers for keys that do not need a cryptographically secure hashing algorithm.
std::size_t extract(uint256 const &key)
bool isXRP(AccountID const &c)
std::unordered_set< Value, Hash, Pred, Allocator > hash_set
hash_set< Book > affectedBooks(AcceptedLedgerTx const &alTx, beast::Journal const &j)
Extract the set of books affected by a transaction.
std::unique_ptr< OrderBookDB > makeOrderBookDb(ServiceRegistry ®istry, OrderBookDBConfig const &config)
Create an OrderBookDB instance.
Configuration for OrderBookDB.