xrpld
Loading...
Searching...
No Matches
Node.cpp
1#include <xrpld/app/rdb/backend/detail/Node.h>
2
3#include <xrpld/app/ledger/AcceptedLedger.h>
4#include <xrpld/app/ledger/LedgerMaster.h>
5#include <xrpld/app/ledger/LedgerPersistence.h>
6#include <xrpld/app/ledger/LedgerToJson.h>
7#include <xrpld/app/ledger/TransactionMaster.h>
8#include <xrpld/core/Config.h>
9
10#include <xrpl/basics/Blob.h>
11#include <xrpl/basics/ByteUtilities.h>
12#include <xrpl/basics/Log.h>
13#include <xrpl/basics/RangeSet.h>
14#include <xrpl/basics/Slice.h>
15#include <xrpl/basics/base_uint.h>
16#include <xrpl/basics/chrono.h>
17#include <xrpl/basics/contract.h>
18#include <xrpl/basics/safe_cast.h>
19#include <xrpl/beast/utility/Journal.h>
20#include <xrpl/beast/utility/instrumentation.h>
21#include <xrpl/config/Constants.h>
22#include <xrpl/core/NetworkIDService.h>
23#include <xrpl/core/StartUpType.h>
24#include <xrpl/json/to_string.h> // IWYU pragma: keep
25#include <xrpl/ledger/PendingSaves.h>
26#include <xrpl/nodestore/NodeObject.h>
27#include <xrpl/protocol/AccountID.h>
28#include <xrpl/protocol/ErrorCodes.h>
29#include <xrpl/protocol/HashPrefix.h>
30#include <xrpl/protocol/LedgerHeader.h>
31#include <xrpl/protocol/Protocol.h>
32#include <xrpl/protocol/SField.h>
33#include <xrpl/protocol/STTx.h>
34#include <xrpl/protocol/Serializer.h>
35#include <xrpl/protocol/TxMeta.h>
36#include <xrpl/protocol/TxSearched.h>
37#include <xrpl/protocol/XRPAmount.h>
38#include <xrpl/rdb/DBInit.h>
39#include <xrpl/rdb/DatabaseCon.h>
40#include <xrpl/rdb/RelationalDatabase.h>
41#include <xrpl/rdb/SociDB.h>
42
43#include <boost/optional/optional.hpp> // IWYU pragma: keep
44#include <boost/system/detail/error_code.hpp>
45
46#include <soci/blob-exchange.h> // IWYU pragma: keep
47#include <soci/blob.h>
48#include <soci/boost-optional.h> // IWYU pragma: keep
49#include <soci/into.h>
50#include <soci/soci-backend.h>
51#include <soci/statement.h>
52#include <soci/transaction.h>
53#include <soci/use.h>
54
55#include <algorithm>
56#include <cstddef>
57#include <cstdint>
58#include <exception>
59#include <filesystem>
60#include <format>
61#include <functional>
62#include <limits>
63#include <map>
64#include <memory>
65#include <optional>
66#include <sstream>
67#include <stdexcept>
68#include <string>
69#include <system_error>
70#include <utility>
71#include <variant>
72#include <vector>
73
74namespace xrpl::detail {
75
81static std::string
83{
84 static_assert(kTableTypeCount == 3, "Need to modify switch statement if enum is modified");
85
86 switch (type)
87 {
89 return "Ledgers";
91 return "Transactions";
93 return "AccountTransactions";
94 // LCOV_EXCL_START
95 default:
96 UNREACHABLE("xrpl::detail::toString : invalid TableType");
97 return "Unknown";
98 // LCOV_EXCL_STOP
99 }
100}
101
102DatabasePairValid
104 Config const& config,
105 DatabaseCon::Setup const& setup,
106 DatabaseCon::CheckpointerSetup const& checkpointerSetup,
108{
109 // ledger database
111 setup, kLgrDbName, setup.lgrPragma, kLgrDbInit, checkpointerSetup, j)};
112 lgr->getSession() << std::format(
113 "PRAGMA cache_size=-{};", kilobytes(config.getValueFor(SizedItem::LgrDbCache)));
114
115 if (config.useTxTables())
116 {
117 // transaction database
119 setup, kTxDbName, setup.txPragma, kTxDbInit, checkpointerSetup, j)};
120 tx->getSession() << std::format(
121 "PRAGMA cache_size=-{};", kilobytes(config.getValueFor(SizedItem::TxnDbCache)));
122
123 if (!setup.standAlone || setup.startUp == StartUpType::Load ||
125 {
126 // Check if AccountTransactions has primary key
127 std::string cid, name, type;
128 std::size_t notnull = 0, dfltValue = 0, pk = 0;
129 soci::indicator ind = soci::i_null;
130 soci::statement st =
131 (tx->getSession().prepare << "PRAGMA table_info(AccountTransactions);",
132 soci::into(cid),
133 soci::into(name),
134 soci::into(type),
135 soci::into(notnull),
136 soci::into(dfltValue, ind),
137 soci::into(pk));
138
139 st.execute();
140 while (st.fetch())
141 {
142 if (pk == 1)
143 {
144 return {
145 .ledgerDb = std::move(lgr), .transactionDb = std::move(tx), .valid = false};
146 }
147 }
148 }
149
150 return {.ledgerDb = std::move(lgr), .transactionDb = std::move(tx), .valid = true};
151 }
152
153 return {.ledgerDb = std::move(lgr), .transactionDb = {}, .valid = true};
154}
155
157getMinLedgerSeq(soci::session& session, TableType type)
158{
159 std::string const query = "SELECT MIN(LedgerSeq) FROM " + toString(type) + ";";
160 // SOCI requires boost::optional (not std::optional) as the parameter.
161 boost::optional<LedgerIndex> m;
162 session << query, soci::into(m);
163 return m ? *m : std::optional<LedgerIndex>();
164}
165
167getMaxLedgerSeq(soci::session& session, TableType type)
168{
169 std::string const query = "SELECT MAX(LedgerSeq) FROM " + toString(type) + ";";
170 // SOCI requires boost::optional (not std::optional) as the parameter.
171 boost::optional<LedgerIndex> m;
172 session << query, soci::into(m);
173 return m ? *m : std::optional<LedgerIndex>();
174}
175
176void
177deleteByLedgerSeq(soci::session& session, TableType type, LedgerIndex ledgerSeq)
178{
179 session << "DELETE FROM " << toString(type) << " WHERE LedgerSeq == " << ledgerSeq << ";";
180}
181
182void
183deleteBeforeLedgerSeq(soci::session& session, TableType type, LedgerIndex ledgerSeq)
184{
185 session << "DELETE FROM " << toString(type) << " WHERE LedgerSeq < " << ledgerSeq << ";";
186}
187
189getRows(soci::session& session, TableType type)
190{
191 std::size_t rows = 0;
192 session << "SELECT COUNT(*) AS rows "
193 "FROM "
194 << toString(type) << ";",
195 soci::into(rows);
196
197 return rows;
198}
199
201getRowsMinMax(soci::session& session, TableType type)
202{
204 session << "SELECT COUNT(*) AS rows, "
205 "MIN(LedgerSeq) AS first, "
206 "MAX(LedgerSeq) AS last "
207 "FROM "
208 << toString(type) << ";",
209 soci::into(res.numberOfRows), soci::into(res.minLedgerSequence),
210 soci::into(res.maxLedgerSequence);
211
212 return res;
213}
214
215bool
217 DatabaseCon& ldgDB,
218 std::unique_ptr<DatabaseCon> const& txnDB,
219 Application& app,
220 std::shared_ptr<Ledger const> const& ledger,
221 bool current)
222{
223 auto j = app.getJournal("Ledger");
224 auto seq = ledger->header().seq;
225
226 // TODO(tom): Fix this hard-coded SQL!
227 JLOG(j.trace()) << "saveValidatedLedger " << (current ? "" : "fromAcquire ") << seq;
228
229 if (!ledger->header().accountHash.isNonZero())
230 {
231 // LCOV_EXCL_START
232 JLOG(j.fatal()) << "AH is zero: " << getJson({*ledger, {}});
233 UNREACHABLE("xrpl::detail::saveValidatedLedger : zero account hash");
234 // LCOV_EXCL_STOP
235 }
236
237 if (ledger->header().accountHash != ledger->stateMap().getHash().asUInt256())
238 {
239 // LCOV_EXCL_START
240 JLOG(j.fatal()) << "sAL: " << ledger->header().accountHash
241 << " != " << ledger->stateMap().getHash();
242 JLOG(j.fatal()) << "saveAcceptedLedger: seq=" << seq << ", current=" << current;
243 UNREACHABLE("xrpl::detail::saveValidatedLedger : mismatched account hash");
244 // LCOV_EXCL_STOP
245 }
246
247 XRPL_ASSERT(
248 ledger->header().txHash == ledger->txMap().getHash().asUInt256(),
249 "xrpl::detail::saveValidatedLedger : transaction hash match");
250
251 // Save the ledger header in the hashed object store
252 {
253 Serializer s(128);
255 addRaw(ledger->header(), s);
256 app.getNodeStore().store(
257 NodeObjectType::Ledger, std::move(s.modData()), ledger->header().hash, seq);
258 }
259
261 try
262 {
263 aLedger = app.getAcceptedLedgerCache().fetch(ledger->header().hash);
264 if (!aLedger)
265 {
266 aLedger = std::make_shared<AcceptedLedger>(ledger);
267 app.getAcceptedLedgerCache().canonicalizeReplaceClient(ledger->header().hash, aLedger);
268 }
269 }
270 catch (std::exception const&)
271 {
272 JLOG(j.warn()) << "An accepted ledger was missing nodes";
273 app.getLedgerMaster().failedSave(seq, ledger->header().hash);
274 // Clients can now trust the database for information about this
275 // ledger sequence.
276 app.getPendingSaves().finishWork(seq);
277 return false;
278 }
279
280 {
281 static constexpr char const* kDeleteLedger = "DELETE FROM Ledgers WHERE LedgerSeq = {};";
282 static constexpr char const* kDeleteTranS1 =
283 "DELETE FROM Transactions WHERE LedgerSeq = {};";
284 static constexpr char const* kDeleteTranS2 =
285 "DELETE FROM AccountTransactions WHERE LedgerSeq = {};";
286 static constexpr char const* kDeleteAcctTrans =
287 "DELETE FROM AccountTransactions WHERE TransID = '{}';";
288
289 {
290 auto db = ldgDB.checkoutDb();
291 *db << std::format(kDeleteLedger, seq);
292 }
293
294 if (app.config().useTxTables())
295 {
296 if (!txnDB)
297 {
298 // LCOV_EXCL_START
299 JLOG(j.fatal()) << "TxTables db isn't available";
300 Throw<std::runtime_error>("TxTables db isn't available");
301 // LCOV_EXCL_STOP
302 }
303
304 auto db = txnDB->checkoutDb();
305
306 soci::transaction tr(*db);
307
308 *db << std::format(kDeleteTranS1, seq);
309 *db << std::format(kDeleteTranS2, seq);
310
311 std::string const ledgerSeq(std::to_string(seq));
312
313 for (auto const& acceptedLedgerTx : *aLedger)
314 {
315 uint256 const transactionID = acceptedLedgerTx->getTransactionID();
316
317 std::string const txnId(to_string(transactionID));
318 std::string const txnSeq(std::to_string(acceptedLedgerTx->getTxnSeq()));
319
320 *db << std::format(kDeleteAcctTrans, txnId);
321
322 auto const& accts = acceptedLedgerTx->getAffected();
323
324 if (!accts.empty())
325 {
326 std::string sql(
327 "INSERT INTO AccountTransactions "
328 "(TransID, Account, LedgerSeq, TxnSeq) VALUES ");
329
330 // Try to make an educated guess on how much space we'll
331 // need for our arguments. In argument order we have: 64
332 // + 34 + 10 + 10 = 118 + 10 extra = 128 bytes
333 sql.reserve(sql.length() + (accts.size() * 128));
334
335 bool first = true;
336 for (auto const& account : accts)
337 {
338 if (!first)
339 {
340 sql += ", ('";
341 }
342 else
343 {
344 sql += "('";
345 first = false;
346 }
347
348 sql += txnId;
349 sql += "','";
350 sql += toBase58(account);
351 sql += "',";
352 sql += ledgerSeq;
353 sql += ",";
354 sql += txnSeq;
355 sql += ")";
356 }
357 sql += ";";
358 JLOG(j.trace()) << "ActTx: " << sql;
359 *db << sql;
360 }
361 else if (auto const& sleTxn = acceptedLedgerTx->getTxn(); !isPseudoTx(*sleTxn))
362 {
363 // It's okay for pseudo transactions to not affect any
364 // accounts. But otherwise...
365 JLOG(j.warn()) << "Transaction in ledger " << seq << " affects no accounts";
366 JLOG(j.warn()) << sleTxn->getJson(JsonOptions::Values::None);
367 }
368
369 *db
371 acceptedLedgerTx->getTxn()->getMetaSQL(
372 seq, acceptedLedgerTx->getEscMeta()) +
373 ";");
374
376 transactionID,
377 seq,
378 acceptedLedgerTx->getTxnSeq(),
380 }
381
382 tr.commit();
383 }
384
385 {
386 static std::string const kAddLedger(
387 R"sql(INSERT OR REPLACE INTO Ledgers
388 (LedgerHash,LedgerSeq,PrevHash,TotalCoins,ClosingTime,PrevClosingTime,
389 CloseTimeRes,CloseFlags,AccountSetHash,TransSetHash)
390 VALUES
391 (:ledgerHash,:ledgerSeq,:prevHash,:totalCoins,:closingTime,:prevClosingTime,
392 :closeTimeRes,:closeFlags,:accountSetHash,:transSetHash);)sql");
393
394 auto db(ldgDB.checkoutDb());
395
396 soci::transaction tr(*db);
397
398 auto const hash = to_string(ledger->header().hash);
399 auto const parentHash = to_string(ledger->header().parentHash);
400 auto const drops = to_string(ledger->header().drops);
401 auto const closeTime = ledger->header().closeTime.time_since_epoch().count();
402 auto const parentCloseTime =
403 ledger->header().parentCloseTime.time_since_epoch().count();
404 auto const closeTimeResolution = ledger->header().closeTimeResolution.count();
405 auto const closeFlags = ledger->header().closeFlags;
406 auto const accountHash = to_string(ledger->header().accountHash);
407 auto const txHash = to_string(ledger->header().txHash);
408
409 *db << kAddLedger, soci::use(hash), soci::use(seq), soci::use(parentHash),
410 soci::use(drops), soci::use(closeTime), soci::use(parentCloseTime),
411 soci::use(closeTimeResolution), soci::use(closeFlags), soci::use(accountHash),
412 soci::use(txHash);
413
414 tr.commit();
415 }
416 }
417
418 return true;
419}
420
425 * @param sqlSuffix SQL string used to specify the sought ledger.
426 * @param j Journal.
427 * @return Ledger info or no value if the ledger was not found.
428 */
430getLedgerInfo(soci::session& session, std::string const& sqlSuffix, beast::Journal j)
431{
432 // SOCI requires boost::optional (not std::optional) as parameters.
433 boost::optional<std::string> hash, parentHash, accountHash, txHash;
434 boost::optional<std::uint64_t> seq, drops, closeTime, parentCloseTime, closeTimeResolution,
435 closeFlags;
436
437 std::string const sql =
438 "SELECT "
439 "LedgerHash, PrevHash, AccountSetHash, TransSetHash, "
440 "TotalCoins,"
441 "ClosingTime, PrevClosingTime, CloseTimeRes, CloseFlags,"
442 "LedgerSeq FROM Ledgers " +
443 sqlSuffix + ";";
444
445 session << sql, soci::into(hash), soci::into(parentHash), soci::into(accountHash),
446 soci::into(txHash), soci::into(drops), soci::into(closeTime), soci::into(parentCloseTime),
447 soci::into(closeTimeResolution), soci::into(closeFlags), soci::into(seq);
448
449 if (!session.got_data())
450 {
451 JLOG(j.debug()) << "Ledger not found: " << sqlSuffix;
452 return {};
453 }
454
455 using time_point = NetClock::time_point;
456 using duration = NetClock::duration;
457
458 LedgerHeader info;
459
460 if (hash && !info.hash.parseHex(*hash))
461 {
462 JLOG(j.debug()) << "Hash parse error for ledger: " << sqlSuffix;
463 return {};
464 }
465
466 if (parentHash && !info.parentHash.parseHex(*parentHash))
467 {
468 JLOG(j.debug()) << "parentHash parse error for ledger: " << sqlSuffix;
469 return {};
470 }
471
472 if (accountHash && !info.accountHash.parseHex(*accountHash))
473 {
474 JLOG(j.debug()) << "accountHash parse error for ledger: " << sqlSuffix;
475 return {};
476 }
477
478 if (txHash && !info.txHash.parseHex(*txHash))
479 {
480 JLOG(j.debug()) << "txHash parse error for ledger: " << sqlSuffix;
481 return {};
482 }
483
484 info.seq = rangeCheckedCast<std::uint32_t>(seq.value_or(0));
485 info.drops = drops.value_or(0);
486 info.closeTime = time_point{duration{closeTime.value_or(0)}};
487 info.parentCloseTime = time_point{duration{parentCloseTime.value_or(0)}};
488 info.closeFlags = closeFlags.value_or(0);
489 info.closeTimeResolution = duration{closeTimeResolution.value_or(0)};
491 return info;
492}
493
495getLedgerInfoByIndex(soci::session& session, LedgerIndex ledgerSeq, beast::Journal j)
496{
498 s << "WHERE LedgerSeq = " << ledgerSeq;
499 return getLedgerInfo(session, s.str(), j);
500}
501
503getNewestLedgerInfo(soci::session& session, beast::Journal j)
504{
506 s << "ORDER BY LedgerSeq DESC LIMIT 1";
507 return getLedgerInfo(session, s.str(), j);
508}
509
511getLimitedOldestLedgerInfo(soci::session& session, LedgerIndex ledgerFirstIndex, beast::Journal j)
512{
514 s << "WHERE LedgerSeq >= " + std::to_string(ledgerFirstIndex) +
515 " ORDER BY LedgerSeq ASC LIMIT 1";
516 return getLedgerInfo(session, s.str(), j);
517}
518
520getLimitedNewestLedgerInfo(soci::session& session, LedgerIndex ledgerFirstIndex, beast::Journal j)
521{
523 s << "WHERE LedgerSeq >= " + std::to_string(ledgerFirstIndex) +
524 " ORDER BY LedgerSeq DESC LIMIT 1";
525 return getLedgerInfo(session, s.str(), j);
526}
527
529getLedgerInfoByHash(soci::session& session, uint256 const& ledgerHash, beast::Journal j)
530{
532 s << "WHERE LedgerHash = '" << ledgerHash << "'";
533 return getLedgerInfo(session, s.str(), j);
534}
535
537getHashByIndex(soci::session& session, LedgerIndex ledgerIndex)
538{
539 uint256 ret;
540
541 std::string sql = "SELECT LedgerHash FROM Ledgers INDEXED BY SeqLedger WHERE LedgerSeq='";
542 sql.append(std::to_string(ledgerIndex));
543 sql.append("';");
544
545 std::string hash;
546 {
547 // SOCI requires boost::optional (not std::optional) as the parameter.
548 boost::optional<std::string> lh;
549 session << sql, soci::into(lh);
550
551 if (!session.got_data() || !lh)
552 return ret;
553
554 hash = *lh;
555 if (hash.empty())
556 return ret;
557 }
558
559 if (!ret.parseHex(hash))
560 return ret;
562 return ret;
563}
564
566getHashesByIndex(soci::session& session, LedgerIndex ledgerIndex, beast::Journal j)
567{
568 // SOCI requires boost::optional (not std::optional) as the parameter.
569 boost::optional<std::string> lhO, phO;
570
571 session << "SELECT LedgerHash,PrevHash FROM Ledgers "
572 "INDEXED BY SeqLedger WHERE LedgerSeq = :ls;",
573 soci::into(lhO), soci::into(phO), soci::use(ledgerIndex);
574
575 if (!lhO || !phO)
576 {
577 auto stream = j.trace();
578 JLOG(stream) << "Don't have ledger " << ledgerIndex;
579 return {};
580 }
581
582 LedgerHashPair hashes;
583 if (!hashes.ledgerHash.parseHex(*lhO) || !hashes.parentHash.parseHex(*phO))
584 {
585 auto stream = j.trace();
586 JLOG(stream) << "Error parse hashes for ledger " << ledgerIndex;
587 return {};
588 }
590 return hashes;
591}
592
594getHashesByIndex(soci::session& session, LedgerIndex minSeq, LedgerIndex maxSeq, beast::Journal j)
595{
596 std::string sql = "SELECT LedgerSeq,LedgerHash,PrevHash FROM Ledgers WHERE LedgerSeq >= ";
597 sql.append(std::to_string(minSeq));
598 sql.append(" AND LedgerSeq <= ");
599 sql.append(std::to_string(maxSeq));
600 sql.append(";");
601
602 std::uint64_t ls = 0;
603 std::string lh;
604 // SOCI requires boost::optional (not std::optional) as the parameter.
605 boost::optional<std::string> ph;
606 soci::statement st = (session.prepare << sql, soci::into(ls), soci::into(lh), soci::into(ph));
607
608 st.execute();
610 while (st.fetch())
611 {
613 if (!hashes.ledgerHash.parseHex(lh))
614 {
615 JLOG(j.warn()) << "Error parsed hash for ledger seq: " << ls;
616 }
617 if (!ph)
618 {
619 JLOG(j.warn()) << "Null prev hash for ledger seq: " << ls;
620 }
621 else if (!hashes.parentHash.parseHex(*ph))
622 {
623 JLOG(j.warn()) << "Error parsed prev hash for ledger seq: " << ls;
624 }
626 return res;
627}
628
630getTxHistory(soci::session& session, Application& app, LedgerIndex startIndex, int quantity)
631{
632 std::string const sql = std::format(
633 "SELECT LedgerSeq, Status, RawTxn "
634 "FROM Transactions ORDER BY LedgerSeq DESC LIMIT {},{};",
635 startIndex,
636 quantity);
637
639 int total = 0;
640
641 {
642 // SOCI requires boost::optional (not std::optional) as parameters.
643 boost::optional<std::uint64_t> ledgerSeq;
644 boost::optional<std::string> status;
645 soci::blob sociRawTxnBlob(session);
646 soci::indicator rti = soci::i_null;
647 Blob rawTxn;
648
649 soci::statement st =
650 (session.prepare << sql,
651 soci::into(ledgerSeq),
652 soci::into(status),
653 soci::into(sociRawTxnBlob, rti));
654
655 st.execute();
656 while (st.fetch())
657 {
658 if (soci::i_ok == rti)
659 {
660 convert(sociRawTxnBlob, rawTxn);
661 }
662 else
663 {
664 rawTxn.clear();
665 }
666
667 if (auto trans = Transaction::transactionFromSQL(ledgerSeq, status, rawTxn, app))
668 {
669 total++;
670 txs.push_back(trans);
671 }
672 }
673 }
674
675 return {txs, total};
676}
677
691 * selecting them.
692 * @param j Journal.
693 * @return SQL query string.
694 */
695static std::string
697 Application& app,
698 std::string selection,
700 bool descending,
701 bool binary,
702 bool count,
704{
705 static constexpr std::uint32_t kNonbinaryPageLength = 200;
706 static constexpr std::uint32_t kBinaryPageLength = 500;
707
708 std::uint32_t numberOfResults = 0;
709
710 if (count)
711 {
712 numberOfResults = std::numeric_limits<std::uint32_t>::max();
713 }
714 else if (options.limit == UINT32_MAX)
715 {
716 numberOfResults = binary ? kBinaryPageLength : kNonbinaryPageLength;
717 }
718 else if (!options.bUnlimited)
719 {
720 numberOfResults =
721 std::min(binary ? kBinaryPageLength : kNonbinaryPageLength, options.limit);
722 }
723 else
724 {
725 numberOfResults = options.limit;
726 }
727
728 std::string maxClause;
729 std::string minClause;
730
731 if (options.ledgerRange.max != 0u)
732 {
733 maxClause =
734 std::format("AND AccountTransactions.LedgerSeq <= '{}'", options.ledgerRange.max);
735 }
736
737 if (options.ledgerRange.min != 0u)
738 {
739 minClause =
740 std::format("AND AccountTransactions.LedgerSeq >= '{}'", options.ledgerRange.min);
741 }
742
743 std::string sql;
744
745 if (count)
746 {
747 sql = std::format(
748 "SELECT {} FROM AccountTransactions "
749 "WHERE Account = '{}' {} {} LIMIT {}, {};",
750 selection,
751 toBase58(options.account),
752 maxClause,
753 minClause,
754 options.offset,
755 numberOfResults);
756 }
757 else
758 {
759 char const* const order = descending ? "DESC" : "ASC";
760 sql = std::format(
761 "SELECT {} FROM "
762 "AccountTransactions INNER JOIN Transactions "
763 "ON Transactions.TransID = AccountTransactions.TransID "
764 "WHERE Account = '{}' {} {} "
765 "ORDER BY AccountTransactions.LedgerSeq {}, "
766 "AccountTransactions.TxnSeq {}, AccountTransactions.TransID {} "
767 "LIMIT {}, {};",
768 selection,
769 toBase58(options.account),
770 maxClause,
771 minClause,
772 order,
773 order,
774 order,
775 options.offset,
776 numberOfResults);
777 }
778 JLOG(j.trace()) << "txSQL query: " << sql;
779 return sql;
780}
781
799 * -number represents the number of transactions skipped. We need to
800 * skip some number of transactions if option offset is > 0 in the
801 * options structure.
802 */
805 soci::session& session,
806 Application& app,
807 LedgerMaster& ledgerMaster,
809 bool descending,
811{
813
814 std::string const sql = transactionsSQL(
815 app,
816 "AccountTransactions.LedgerSeq,Status,RawTxn,TxnMeta",
817 options,
818 descending,
819 false,
820 false,
821 j);
822 if (sql.empty())
823 return {ret, 0};
824
825 int total = 0;
826 {
827 // SOCI requires boost::optional (not std::optional) as parameters.
828 boost::optional<std::uint64_t> ledgerSeq;
829 boost::optional<std::string> status;
830 soci::blob sociTxnBlob(session), sociTxnMetaBlob(session);
831 soci::indicator rti = soci::i_null, tmi = soci::i_null;
832 Blob rawTxn, txnMeta;
833
834 soci::statement st =
835 (session.prepare << sql,
836 soci::into(ledgerSeq),
837 soci::into(status),
838 soci::into(sociTxnBlob, rti),
839 soci::into(sociTxnMetaBlob, tmi));
840
841 st.execute();
842 while (st.fetch())
843 {
844 if (soci::i_ok == rti)
845 {
846 convert(sociTxnBlob, rawTxn);
847 }
848 else
849 {
850 rawTxn.clear();
851 }
852
853 if (soci::i_ok == tmi)
854 {
855 convert(sociTxnMetaBlob, txnMeta);
856 }
857 else
858 {
859 txnMeta.clear();
860 }
861
862 auto txn = Transaction::transactionFromSQL(ledgerSeq, status, rawTxn, app);
863
864 if (txnMeta.empty())
865 { // Work around a bug that could leave the metadata missing
866 auto const seq = rangeCheckedCast<std::uint32_t>(ledgerSeq.value_or(0));
867
868 JLOG(j.warn()) << "Recovering ledger " << seq << ", txn " << txn->getID();
869
870 if (auto l = ledgerMaster.getLedgerBySeq(seq))
871 pendSaveValidated(app, l, false, false);
872 }
873
874 if (txn)
875 {
876 ret.emplace_back(
877 txn, std::make_shared<TxMeta>(txn->getID(), txn->getLedger(), txnMeta));
878 total++;
879 }
880 }
881 }
883 return {ret, total};
884}
885
888 soci::session& session,
889 Application& app,
890 LedgerMaster& ledgerMaster,
891 RelationalDatabase::AccountTxOptions const& options,
894 return getAccountTxs(session, app, ledgerMaster, options, false, j);
895}
896
899 soci::session& session,
900 Application& app,
901 LedgerMaster& ledgerMaster,
904{
905 return getAccountTxs(session, app, ledgerMaster, options, true, j);
906}
907
924 * transactions processed, if it is < 0, then -number represents the
925 * number of transactions skipped. We need to skip some number of
926 * transactions if option offset is > 0 in the options structure.
927 */
930 soci::session& session,
931 Application& app,
933 bool descending,
935{
937
938 std::string const sql = transactionsSQL(
939 app,
940 "AccountTransactions.LedgerSeq,Status,RawTxn,TxnMeta",
941 options,
942 descending,
943 true /*binary*/,
944 false,
945 j);
946 if (sql.empty())
947 return {ret, 0};
948
949 int total = 0;
950
951 {
952 // SOCI requires boost::optional (not std::optional) as parameters.
953 boost::optional<std::uint64_t> ledgerSeq;
954 boost::optional<std::string> status;
955 soci::blob sociTxnBlob(session), sociTxnMetaBlob(session);
956 soci::indicator rti = soci::i_null, tmi = soci::i_null;
957
958 soci::statement st =
959 (session.prepare << sql,
960 soci::into(ledgerSeq),
961 soci::into(status),
962 soci::into(sociTxnBlob, rti),
963 soci::into(sociTxnMetaBlob, tmi));
964
965 st.execute();
966 while (st.fetch())
967 {
968 Blob rawTxn;
969 if (soci::i_ok == rti)
970 convert(sociTxnBlob, rawTxn);
971 Blob txnMeta;
972 if (soci::i_ok == tmi)
973 convert(sociTxnMetaBlob, txnMeta);
974
975 auto const seq = rangeCheckedCast<std::uint32_t>(ledgerSeq.value_or(0));
976
977 ret.emplace_back(std::move(rawTxn), std::move(txnMeta), seq);
978 total++;
979 }
980 }
982 return {ret, total};
983}
984
987 soci::session& session,
988 Application& app,
989 RelationalDatabase::AccountTxOptions const& options,
992 return getAccountTxsB(session, app, options, false, j);
993}
994
997 soci::session& session,
998 Application& app,
1001{
1002 return getAccountTxsB(session, app, options, true, j);
1003}
1004
1011 * @param contextAccount The account passed to account_tx (the queried account).
1012 * @return True if the transaction passes the filter and should be included,
1013 * false if it should be skipped.
1014 */
1015static bool
1017 Blob const& rawData,
1018 DelegateFilter const& filter,
1019 AccountID const& contextAccount)
1020{
1021 SerialIter sit{makeSlice(rawData)};
1022 STTx const tx{sit};
1023
1024 AccountID const txOwner = tx.getAccountID(sfAccount);
1025
1026 if (!tx.isFieldPresent(sfDelegate))
1027 return false;
1028
1029 AccountID const txSigner = tx.getAccountID(sfDelegate);
1030
1031 switch (filter.type)
1032 {
1033 case DelegateType::Actor: {
1034 // Keep txns where the queried account (A) is the owner but
1035 // another account (C) was the delegatee that signed.
1036 bool const isDelegated = (txOwner == contextAccount) && (txSigner != contextAccount);
1037 if (!isDelegated)
1038 return false;
1039 return !filter.counterparty || (txSigner == *filter.counterparty);
1040 }
1041
1043 // Keep txns where the queried account (C) is the signer acting
1044 // on behalf of another account (A, the delegator/owner).
1045 bool const isActingAsDelegate =
1046 (txSigner == contextAccount) && (txOwner != contextAccount);
1047 if (!isActingAsDelegate)
1048 return false;
1049 return !filter.counterparty || (txOwner == *filter.counterparty);
1050 }
1051 }
1052
1053 return false; // LCOV_EXCL_LINE
1054}
1055
1071 * sequences sorted in the specified order by account sequence, a marker
1072 * for the next search if the search was not finished and the number of
1073 * transactions processed during this call.
1074 */
1077 soci::session& session,
1078 std::function<void(std::uint32_t)> const& onUnsavedLedger,
1079 std::function<void(std::uint32_t, std::string const&, Blob&&, Blob&&)> const& onTransaction,
1081 std::uint32_t pageLength,
1082 bool forward)
1083{
1084 int total = 0;
1085
1086 bool const hasDelegateFilter = options.delegate.has_value();
1087 bool lookingForMarker = options.marker.has_value();
1088
1089 std::uint32_t numberOfResults = 0;
1090
1091 if (options.limit == 0 || options.limit == UINT32_MAX ||
1092 (options.limit > pageLength && !options.bAdmin))
1093 {
1094 numberOfResults = pageLength;
1095 }
1096 else
1097 {
1098 numberOfResults = options.limit;
1099 }
1100
1101 // As an account can have many thousands of transactions, there is a limit
1102 // placed on the amount of transactions returned. If the limit is reached
1103 // before the result set has been exhausted (we always query for one more
1104 // than the limit), then we return an opaque marker that can be supplied in
1105 // a subsequent query.
1106 std::uint32_t queryLimit = numberOfResults + 1;
1107 std::uint32_t findLedger = 0, findSeq = 0;
1108
1109 if (lookingForMarker)
1110 {
1111 findLedger = options.marker->ledgerSeq;
1112 findSeq = options.marker->txnSeq;
1113 }
1114
1116
1117 std::string sql;
1118
1119 // SQL's BETWEEN uses a closed interval ([a,b])
1120
1121 char const* const order = forward ? "ASC" : "DESC";
1122
1123 if (findLedger == 0)
1124 {
1125 sql = std::format(
1126 R"(SELECT AccountTransactions.LedgerSeq,AccountTransactions.TxnSeq,
1127 Status,RawTxn,TxnMeta
1128 FROM AccountTransactions INNER JOIN Transactions
1129 ON Transactions.TransID = AccountTransactions.TransID
1130 AND AccountTransactions.Account = '{}' WHERE
1131 AccountTransactions.LedgerSeq BETWEEN {} AND {}
1132 ORDER BY AccountTransactions.LedgerSeq {},
1133 AccountTransactions.TxnSeq {}
1134 LIMIT {};)",
1135 toBase58(options.account),
1136 options.ledgerRange.min,
1137 options.ledgerRange.max,
1138 order,
1139 order,
1140 queryLimit);
1141 }
1142 else
1143 {
1144 char const* const compare = forward ? ">=" : "<=";
1145 std::uint32_t const minLedger = forward ? findLedger + 1 : options.ledgerRange.min;
1146 std::uint32_t const maxLedger = forward ? options.ledgerRange.max : findLedger - 1;
1147
1148 auto b58acct = toBase58(options.account);
1149 sql = std::format(
1150 R"(SELECT AccountTransactions.LedgerSeq,AccountTransactions.TxnSeq,
1151 Status,RawTxn,TxnMeta
1152 FROM AccountTransactions, Transactions WHERE
1153 (AccountTransactions.TransID = Transactions.TransID AND
1154 AccountTransactions.Account = '{}' AND
1155 AccountTransactions.LedgerSeq BETWEEN {} AND {})
1156 UNION
1157 SELECT AccountTransactions.LedgerSeq,AccountTransactions.TxnSeq,Status,RawTxn,TxnMeta
1158 FROM AccountTransactions, Transactions WHERE
1159 (AccountTransactions.TransID = Transactions.TransID AND
1160 AccountTransactions.Account = '{}' AND
1161 AccountTransactions.LedgerSeq = {} AND
1162 AccountTransactions.TxnSeq {} {})
1163 ORDER BY AccountTransactions.LedgerSeq {},
1164 AccountTransactions.TxnSeq {}
1165 LIMIT {};
1166 )",
1167 b58acct,
1168 minLedger,
1169 maxLedger,
1170 b58acct,
1171 findLedger,
1172 compare,
1173 findSeq,
1174 order,
1175 order,
1176 queryLimit);
1177 }
1178
1179 {
1180 Blob rawData;
1181 Blob rawMeta;
1182 // Delegate filtering happens after SQL, so skipped rows need their own
1183 // continuation marker accounting.
1184 std::uint32_t fetchedRows = 0;
1187
1188 // SOCI requires boost::optional (not std::optional) as parameters.
1189 boost::optional<std::uint64_t> ledgerSeq;
1190 boost::optional<std::uint32_t> txnSeq;
1191 boost::optional<std::string> status;
1192 soci::blob txnData(session);
1193 soci::blob txnMeta(session);
1194 soci::indicator dataPresent = soci::i_null, metaPresent = soci::i_null;
1195
1196 soci::statement st =
1197 (session.prepare << sql,
1198 soci::into(ledgerSeq),
1199 soci::into(txnSeq),
1200 soci::into(status),
1201 soci::into(txnData, dataPresent),
1202 soci::into(txnMeta, metaPresent));
1203
1204 st.execute();
1205
1206 while (st.fetch())
1207 {
1208 if (hasDelegateFilter)
1209 {
1210 ++fetchedRows;
1211 lastScanned = {
1212 .ledgerSeq = rangeCheckedCast<std::uint32_t>(ledgerSeq.value_or(0)),
1213 .txnSeq = txnSeq.value_or(0)};
1214 }
1215
1216 if (lookingForMarker)
1217 {
1218 if (findLedger == ledgerSeq.value_or(0) && findSeq == txnSeq.value_or(0))
1219 {
1220 lookingForMarker = false;
1221 // Delegate markers are continuation cursors for the last
1222 // scanned row, so resume after the marker row.
1223 if (hasDelegateFilter)
1224 continue;
1225 }
1226 else
1227 {
1228 continue;
1229 }
1230 }
1231
1232 if (!hasDelegateFilter && numberOfResults == 0)
1233 {
1234 newmarker = {
1235 .ledgerSeq = rangeCheckedCast<std::uint32_t>(ledgerSeq.value_or(0)),
1236 .txnSeq = txnSeq.value_or(0)};
1237 break;
1238 }
1239
1240 if (dataPresent == soci::i_ok)
1241 {
1242 convert(txnData, rawData);
1243 }
1244 else
1245 {
1246 rawData.clear();
1247 }
1248
1249 if (metaPresent == soci::i_ok)
1250 {
1251 convert(txnMeta, rawMeta);
1252 }
1253 else
1254 {
1255 rawMeta.clear();
1256 }
1257
1258 if (hasDelegateFilter)
1259 {
1260 if (rawData.empty() ||
1261 !passesDelegateFilter(rawData, options.delegate.value(), options.account))
1262 {
1263 rawData.clear();
1264 rawMeta.clear();
1265 continue;
1266 }
1267
1268 if (numberOfResults == 0)
1269 {
1270 newmarker = lastEmitted;
1271 break;
1272 }
1273 }
1274
1275 // Work around a bug that could leave the metadata missing
1276 if (rawMeta.empty())
1277 onUnsavedLedger(ledgerSeq.value_or(0));
1278
1279 // `rawData` and `rawMeta` will be used after they are moved.
1280 // That's OK.
1281 onTransaction(
1282 rangeCheckedCast<std::uint32_t>(ledgerSeq.value_or(0)),
1283 *status,
1284 std::move(rawData),
1285 std::move(rawMeta));
1286 // Note some callbacks will move the data, some will not. Clear
1287 // them so code doesn't depend on if the data was actually moved
1288 // or not. The code will be more efficient if `rawData` and
1289 // `rawMeta` don't have to allocate in `convert`, so don't
1290 // refactor my moving these variables into loop scope.
1291 rawData.clear();
1292 rawMeta.clear();
1293
1294 --numberOfResults;
1295 total++;
1296 if (hasDelegateFilter)
1297 {
1298 lastEmitted = {
1299 .ledgerSeq = rangeCheckedCast<std::uint32_t>(ledgerSeq.value_or(0)),
1300 .txnSeq = txnSeq.value_or(0)};
1301 }
1302 }
1303
1304 // If this filtered page did not fill the requested number of results,
1305 // still return a marker so the caller can continue scanning later rows.
1306 if (hasDelegateFilter && !newmarker && !lookingForMarker && fetchedRows == queryLimit)
1307 newmarker = lastScanned;
1308 }
1309
1310 return {newmarker, total};
1311}
1312
1313std::pair<std::optional<RelationalDatabase::AccountTxMarker>, int>
1315 soci::session& session,
1316 std::function<void(std::uint32_t)> const& onUnsavedLedger,
1317 std::function<void(std::uint32_t, std::string const&, Blob&&, Blob&&)> const& onTransaction,
1318 RelationalDatabase::AccountTxPageOptions const& options,
1319 std::uint32_t pageLength)
1320{
1321 return accountTxPage(session, onUnsavedLedger, onTransaction, options, pageLength, true);
1322}
1323
1324std::pair<std::optional<RelationalDatabase::AccountTxMarker>, int>
1326 soci::session& session,
1327 std::function<void(std::uint32_t)> const& onUnsavedLedger,
1328 std::function<void(std::uint32_t, std::string const&, Blob&&, Blob&&)> const& onTransaction,
1329 RelationalDatabase::AccountTxPageOptions const& options,
1330 std::uint32_t pageLength)
1331{
1332 return accountTxPage(session, onUnsavedLedger, onTransaction, options, pageLength, false);
1333}
1334
1335std::variant<RelationalDatabase::AccountTx, TxSearched>
1337 soci::session& session,
1338 Application& app,
1339 uint256 const& id,
1340 std::optional<ClosedInterval<uint32_t>> const& range,
1341 ErrorCodeI& ec)
1342{
1343 std::string sql =
1344 "SELECT LedgerSeq,Status,RawTxn,TxnMeta "
1345 "FROM Transactions WHERE TransID='";
1346
1347 sql.append(to_string(id));
1348 sql.append("';");
1349
1350 // SOCI requires boost::optional (not std::optional) as parameters.
1351 boost::optional<std::uint64_t> ledgerSeq;
1352 boost::optional<std::string> status;
1353 Blob rawTxn, rawMeta;
1354 {
1355 soci::blob sociRawTxnBlob(session), sociRawMetaBlob(session);
1356 soci::indicator txn = soci::i_null, meta = soci::i_null;
1357
1358 session << sql, soci::into(ledgerSeq), soci::into(status), soci::into(sociRawTxnBlob, txn),
1359 soci::into(sociRawMetaBlob, meta);
1360
1361 auto const gotData = session.got_data();
1362
1363 if ((!gotData || txn != soci::i_ok || meta != soci::i_ok) && !range)
1364 return TxSearched::Unknown;
1365
1366 if (!gotData)
1367 {
1368 uint64_t count = 0;
1369 soci::indicator rti = soci::i_null;
1370
1371 session << "SELECT COUNT(DISTINCT LedgerSeq) FROM Transactions WHERE "
1372 "LedgerSeq BETWEEN "
1373 << range->first() << " AND " << range->last() << ";",
1374 soci::into(count, rti);
1375
1376 if (!session.got_data() || rti != soci::i_ok)
1377 return TxSearched::Some;
1378
1379 return count == (range->last() - range->first() + 1) ? TxSearched::All
1381 }
1382
1383 convert(sociRawTxnBlob, rawTxn);
1384 convert(sociRawMetaBlob, rawMeta);
1385 }
1386
1387 try
1388 {
1389 auto txn = Transaction::transactionFromSQL(ledgerSeq, status, rawTxn, app);
1390
1391 if (!ledgerSeq)
1392 return std::pair{std::move(txn), nullptr};
1393
1394 auto const inLedger = rangeCheckedCast<std::uint32_t>(ledgerSeq.value());
1395
1396 auto txMeta = std::make_shared<TxMeta>(id, inLedger, rawMeta);
1397
1398 return std::pair{std::move(txn), std::move(txMeta)};
1399 }
1400 catch (std::exception& e)
1401 {
1402 JLOG(app.getJournal("Ledger").warn())
1403 << "Unable to deserialize transaction from raw SQL value. Error: " << e.what();
1404
1406 }
1407
1408 return TxSearched::Unknown;
1409}
1410
1411bool
1412dbHasSpace(soci::session& session, Config const& config, beast::Journal j)
1413{
1414 std::filesystem::space_info const space =
1416
1417 if (space.available < megabytes(512))
1418 {
1419 JLOG(j.fatal()) << "Remaining free disk space is less than 512MB";
1420 return false;
1421 }
1422
1423 if (config.useTxTables())
1424 {
1425 DatabaseCon::Setup const dbSetup = setupDatabaseCon(config);
1426 std::filesystem::path const dbPath = dbSetup.dataDir / kTxDbName;
1427 std::error_code ec;
1428 std::optional<std::uint64_t> dbSize = std::filesystem::file_size(dbPath, ec);
1429 if (ec)
1430 {
1431 JLOG(j.error()) << "Error checking transaction db file size: " << ec.message();
1432 dbSize.reset();
1433 }
1434
1435 static auto const kPageSize = [&] {
1436 std::uint32_t ps = 0;
1437 session << "PRAGMA page_size;", soci::into(ps);
1438 return ps;
1439 }();
1440 static auto const kMaxPages = [&] {
1441 std::uint32_t mp = 0;
1442 session << "PRAGMA max_page_count;", soci::into(mp);
1443 return mp;
1444 }();
1445 std::uint32_t pageCount = 0;
1446 session << "PRAGMA page_count;", soci::into(pageCount);
1447 std::uint32_t const freePages = kMaxPages - pageCount;
1448 std::uint64_t const freeSpace = safeCast<std::uint64_t>(freePages) * kPageSize;
1449 JLOG(j.info()) << "Transaction DB pathname: " << dbPath.string()
1450 << "; file size: " << dbSize.value_or(-1) << " bytes"
1451 << "; SQLite page size: " << kPageSize << " bytes"
1452 << "; Free pages: " << freePages << "; Free space: " << freeSpace
1453 << " bytes; "
1454 << "Note that this does not take into account available disk "
1455 "space.";
1456
1457 if (freeSpace < megabytes(512))
1458 {
1459 JLOG(j.fatal()) << "Free SQLite space for transaction db is less than "
1460 "512MB. To fix this, xrpld must be executed with the "
1461 "vacuum parameter before restarting. "
1462 "Note that this activity can take multiple days, "
1463 "depending on database size.";
1464 return false;
1465 }
1466 }
1467
1468 return true;
1469}
1470
1471} // namespace xrpl::detail
T append(T... args)
A generic endpoint for log messages.
Definition Journal.h:44
Stream fatal() const
Definition Journal.h:368
Stream error() const
Definition Journal.h:362
Stream debug() const
Definition Journal.h:344
Stream info() const
Definition Journal.h:350
Stream trace() const
Severity stream access functions.
Definition Journal.h:338
Stream warn() const
Definition Journal.h:356
virtual Config & config()=0
constexpr bool parseHex(std::string_view sv)
Parse a hex string into a base_uint.
Definition base_uint.h:525
bool useTxTables() const
int getValueFor(SizedItem item, std::optional< std::size_t > node=std::nullopt) const
Retrieve the default value for the item at the specified node size.
LockedSociSession checkoutDb()
std::shared_ptr< Ledger const > getLedgerBySeq(std::uint32_t index)
void failedSave(std::uint32_t seq, uint256 const &hash)
std::chrono::time_point< NetClock > time_point
Definition chrono.h:48
std::chrono::duration< rep, period > duration
Definition chrono.h:47
virtual std::uint32_t getNetworkID() const noexcept=0
Get the configured network ID.
void finishWork(LedgerIndex seq)
Finish working on a ledger.
std::vector< AccountTx > AccountTxs
bool isFieldPresent(SField const &field) const
Definition STObject.cpp:464
AccountID getAccountID(SField const &field) const
Definition STObject.cpp:643
static std::string const & getMetaSQLInsertReplaceHeader()
Definition STTx.cpp:375
virtual PendingSaves & getPendingSaves()=0
virtual beast::Journal getJournal(std::string const &name)=0
virtual TransactionMaster & getMasterTransaction()=0
virtual NetworkIDService & getNetworkIDService()=0
virtual node_store::Database & getNodeStore()=0
virtual LedgerMaster & getLedgerMaster()=0
virtual TaggedCache< uint256, AcceptedLedger > & getAcceptedLedgerCache()=0
bool inLedger(uint256 const &hash, std::uint32_t ledger, std::optional< uint32_t > tseq, std::optional< uint32_t > netID)
static Transaction::pointer transactionFromSQL(boost::optional< std::uint64_t > const &ledgerSeq, boost::optional< std::string > const &status, Blob const &rawTxn, Application &app)
virtual void store(NodeObjectType type, Blob &&data, uint256 const &hash, std::uint32_t ledgerSeq)=0
Store the object.
T clear(T... args)
T count(T... args)
T emplace_back(T... args)
T empty(T... args)
T file_size(T... args)
T format(T... args)
T make_shared(T... args)
T make_unique(T... args)
T max(T... args)
T message(T... args)
T min(T... args)
std::optional< LedgerHeader > getLedgerInfoByHash(soci::session &session, uint256 const &ledgerHash, beast::Journal j)
getLedgerInfoByHash Returns info of ledger with given hash.
Definition Node.cpp:524
static std::pair< std::optional< RelationalDatabase::AccountTxMarker >, int > accountTxPage(soci::session &session, std::function< void(std::uint32_t)> const &onUnsavedLedger, std::function< void(std::uint32_t, std::string const &, Blob &&, Blob &&)> const &onTransaction, RelationalDatabase::AccountTxPageOptions const &options, std::uint32_t pageLength, bool forward)
accountTxPage Searches for the oldest or newest transactions for the account that matches the given c...
Definition Node.cpp:1071
std::pair< std::optional< RelationalDatabase::AccountTxMarker >, int > newestAccountTxPage(soci::session &session, std::function< void(std::uint32_t)> const &onUnsavedLedger, std::function< void(std::uint32_t, std::string const &, Blob &&, Blob &&)> const &onTransaction, RelationalDatabase::AccountTxPageOptions const &options, std::uint32_t pageLength)
newestAccountTxPage Searches newest transactions for given account which match given criteria startin...
Definition Node.cpp:1296
constexpr int kTableTypeCount
Definition Node.h:36
bool saveValidatedLedger(DatabaseCon &ldgDB, std::unique_ptr< DatabaseCon > const &txnDB, Application &app, std::shared_ptr< Ledger const > const &ledger, bool current)
saveValidatedLedger Saves ledger into database.
Definition Node.cpp:216
std::pair< std::optional< RelationalDatabase::AccountTxMarker >, int > oldestAccountTxPage(soci::session &session, std::function< void(std::uint32_t)> const &onUnsavedLedger, std::function< void(std::uint32_t, std::string const &, Blob &&, Blob &&)> const &onTransaction, RelationalDatabase::AccountTxPageOptions const &options, std::uint32_t pageLength)
oldestAccountTxPage Searches oldest transactions for given account which match given criteria startin...
Definition Node.cpp:1285
void deleteBeforeLedgerSeq(soci::session &session, TableType type, LedgerIndex ledgerSeq)
deleteBeforeLedgerSeq Deletes all entries in given table for the ledgers with given sequence and all ...
Definition Node.cpp:183
std::optional< LedgerIndex > getMinLedgerSeq(soci::session &session, TableType type)
getMinLedgerSeq Returns minimum ledger sequence in given table.
Definition Node.cpp:157
std::optional< LedgerIndex > getMaxLedgerSeq(soci::session &session, TableType type)
getMaxLedgerSeq Returns maximum ledger sequence in given table.
Definition Node.cpp:167
std::pair< RelationalDatabase::AccountTxs, int > getNewestAccountTxs(soci::session &session, Application &app, LedgerMaster &ledgerMaster, RelationalDatabase::AccountTxOptions const &options, beast::Journal j)
getNewestAccountTxs Returns newest transactions for given account which match given criteria starting...
Definition Node.cpp:893
std::pair< std::vector< std::shared_ptr< Transaction > >, int > getTxHistory(soci::session &session, Application &app, LedgerIndex startIndex, int quantity)
getTxHistory Returns given number of most recent transactions starting from given number of entry.
Definition Node.cpp:625
std::pair< std::vector< RelationalDatabase::txnMetaLedgerType >, int > getOldestAccountTxsB(soci::session &session, Application &app, RelationalDatabase::AccountTxOptions const &options, beast::Journal j)
getOldestAccountTxsB Returns oldest transactions in binary form for given account which match given c...
Definition Node.cpp:981
std::optional< LedgerHeader > getNewestLedgerInfo(soci::session &session, beast::Journal j)
getNewestLedgerInfo Returns info of newest saved ledger.
Definition Node.cpp:498
std::optional< LedgerHeader > getLimitedOldestLedgerInfo(soci::session &session, LedgerIndex ledgerFirstIndex, beast::Journal j)
getLimitedOldestLedgerInfo Returns info of oldest ledger from ledgers with sequences greater or equal...
Definition Node.cpp:506
DatabasePairValid makeLedgerDBs(Config const &config, DatabaseCon::Setup const &setup, DatabaseCon::CheckpointerSetup const &checkpointerSetup, beast::Journal j)
makeLedgerDBs Opens ledger and transactions databases.
Definition Node.cpp:103
RelationalDatabase::CountMinMax getRowsMinMax(soci::session &session, TableType type)
getRowsMinMax Returns minimum ledger sequence, maximum ledger sequence and total number of rows in gi...
Definition Node.cpp:201
static std::pair< RelationalDatabase::AccountTxs, int > getAccountTxs(soci::session &session, Application &app, LedgerMaster &ledgerMaster, RelationalDatabase::AccountTxOptions const &options, bool descending, beast::Journal j)
getAccountTxs Returns the oldest or newest transactions for the account that matches the given criter...
Definition Node.cpp:799
static std::optional< LedgerHeader > getLedgerInfo(soci::session &session, std::string const &sqlSuffix, beast::Journal j)
getLedgerInfo Returns the info of the ledger retrieved from the database by using the provided SQL qu...
Definition Node.cpp:425
static bool passesDelegateFilter(Blob const &rawData, DelegateFilter const &filter, AccountID const &contextAccount)
Determines whether a transaction should be included in account_tx results based on a delegation filte...
Definition Node.cpp:1011
std::size_t getRows(soci::session &session, TableType type)
getRows Returns number of rows in given table.
Definition Node.cpp:189
std::optional< LedgerHeader > getLedgerInfoByIndex(soci::session &session, LedgerIndex ledgerSeq, beast::Journal j)
getLedgerInfoByIndex Returns ledger by its sequence.
Definition Node.cpp:490
std::pair< std::vector< RelationalDatabase::txnMetaLedgerType >, int > getNewestAccountTxsB(soci::session &session, Application &app, RelationalDatabase::AccountTxOptions const &options, beast::Journal j)
getNewestAccountTxsB Returns newest transactions in binary form for given account which match given c...
Definition Node.cpp:991
uint256 getHashByIndex(soci::session &session, LedgerIndex ledgerIndex)
getHashByIndex Returns hash of ledger with given sequence.
Definition Node.cpp:532
void deleteByLedgerSeq(soci::session &session, TableType type, LedgerIndex ledgerSeq)
deleteByLedgerSeq Deletes all entries in given table for the ledger with given sequence.
Definition Node.cpp:177
std::pair< RelationalDatabase::AccountTxs, int > getOldestAccountTxs(soci::session &session, Application &app, LedgerMaster &ledgerMaster, RelationalDatabase::AccountTxOptions const &options, beast::Journal j)
getOldestAccountTxs Returns oldest transactions for given account which match given criteria starting...
Definition Node.cpp:882
bool dbHasSpace(soci::session &session, Config const &config, beast::Journal j)
dbHasSpace Checks if given database has available space.
Definition Node.cpp:1383
std::optional< LedgerHeader > getLimitedNewestLedgerInfo(soci::session &session, LedgerIndex ledgerFirstIndex, beast::Journal j)
getLimitedNewestLedgerInfo Returns info of newest ledger from ledgers with sequences greater or equal...
Definition Node.cpp:515
static std::pair< std::vector< RelationalDatabase::txnMetaLedgerType >, int > getAccountTxsB(soci::session &session, Application &app, RelationalDatabase::AccountTxOptions const &options, bool descending, beast::Journal j)
getAccountTxsB Returns the oldest or newest transactions in binary form for the account that matches ...
Definition Node.cpp:924
std::variant< RelationalDatabase::AccountTx, TxSearched > getTransaction(soci::session &session, Application &app, uint256 const &id, std::optional< ClosedInterval< uint32_t > > const &range, ErrorCodeI &ec)
getTransaction Returns transaction with given hash.
Definition Node.cpp:1307
static std::string toString(TableType type)
to_string Returns the name of a table according to its TableType.
Definition Node.cpp:82
static std::string transactionsSQL(Application &app, std::string selection, RelationalDatabase::AccountTxOptions const &options, bool descending, bool binary, bool count, beast::Journal j)
transactionsSQL Returns a SQL query for selecting the oldest or newest transactions in decoded or bin...
Definition Node.cpp:691
std::optional< LedgerHashPair > getHashesByIndex(soci::session &session, LedgerIndex ledgerIndex, beast::Journal j)
getHashesByIndex Returns hash of the ledger and hash of parent ledger for the ledger of given sequenc...
Definition Node.cpp:561
constexpr std::array< char const *, 5 > kLgrDbInit
Definition DBInit.h:48
ErrorCodeI
Definition ErrorCodes.h:23
@ RpcDbDeserialization
Definition ErrorCodes.h:117
bool pendSaveValidated(ServiceRegistry &registry, std::shared_ptr< Ledger const > const &ledger, bool isSynchronous, bool isCurrent)
Save, or arrange to save, a fully-validated ledger.
std::uint32_t LedgerIndex
A ledger index.
Definition Protocol.h:370
ClosedInterval< T > range(T low, T high)
Create a closed range interval.
Definition RangeSet.h:37
constexpr std::array< char const *, 8 > kTxDbInit
Definition DBInit.h:75
T rangeCheckedCast(C c)
std::string toBase58(AccountID const &v)
Convert AccountID to base58 checked string.
Definition AccountID.cpp:95
constexpr Dest safeCast(Src s) noexcept
Definition safe_cast.h:21
constexpr auto kLgrDbName
Definition DBInit.h:46
std::string to_string(BaseUInt< Bits, Tag > const &a)
Definition base_uint.h:651
@ Authorizer
This account signed and submitted transactions on behalf of another account (this account is the sign...
@ Actor
Another account signed and submitted transactions on behalf of this account (this account is the owne...
Slice makeSlice(std::array< T, N > const &a)
Definition Slice.h:228
constexpr auto megabytes(T value) noexcept
void addRaw(LedgerHeader const &, Serializer &, bool includeHash=false)
json::Value getJson(LedgerFill const &fill)
Return a new json::Value representing the ledger with given options.
boost::icl::closed_interval< T > ClosedInterval
A closed interval over the domain T.
Definition RangeSet.h:27
constexpr auto kilobytes(T value) noexcept
BaseUInt< 160, detail::AccountIDTag > AccountID
A 160-bit unsigned that uniquely identifies an account.
Definition AccountID.h:34
@ LedgerMaster
ledger master data for signing
Definition HashPrefix.h:59
DatabaseCon::Setup setupDatabaseCon(Config const &c, std::optional< beast::Journal > j=std::nullopt)
std::vector< unsigned char > Blob
Storage for linear binary data.
Definition Blob.h:11
constexpr auto kTxDbName
Definition DBInit.h:73
bool isPseudoTx(STObject const &tx)
Check whether a transaction is a pseudo-transaction.
Definition STTx.cpp:886
BaseUInt< 256 > uint256
Definition base_uint.h:580
void convert(soci::blob &from, std::vector< std::uint8_t > &to)
Definition SociDB.cpp:145
XRPL_NO_SANITIZE_ADDRESS void Throw(Args &&... args)
Definition contract.h:52
T push_back(T... args)
T reserve(T... args)
T reset(T... args)
T length(T... args)
T str(T... args)
std::array< std::string, 4 > txPragma
Definition DatabaseCon.h:98
std::array< std::string, 1 > lgrPragma
Definition DatabaseCon.h:99
std::optional< AccountID > counterparty
Information about the notional ledger backing the view.
NetClock::time_point parentCloseTime
NetClock::duration closeTimeResolution
NetClock::time_point closeTime
LedgerRange ledgerRange
Ledger sequence range to search.
static constexpr auto kDatabasePath
Definition Constants.h:13
T to_string(T... args)
T value_or(T... args)
T what(T... args)