rippled
Loading...
Searching...
No Matches
Node.cpp
1//------------------------------------------------------------------------------
2/*
3 This file is part of rippled: https://github.com/ripple/rippled
4 Copyright (c) 2020 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/AcceptedLedger.h>
21#include <xrpld/app/ledger/LedgerMaster.h>
22#include <xrpld/app/ledger/LedgerToJson.h>
23#include <xrpld/app/ledger/PendingSaves.h>
24#include <xrpld/app/ledger/TransactionMaster.h>
25#include <xrpld/app/rdb/RelationalDatabase.h>
26#include <xrpld/app/rdb/backend/detail/Node.h>
27#include <xrpld/core/DatabaseCon.h>
28#include <xrpld/core/SociDB.h>
29
30#include <xrpl/basics/BasicConfig.h>
31#include <xrpl/basics/StringUtilities.h>
32#include <xrpl/json/to_string.h>
33
34#include <boost/range/adaptor/transformed.hpp>
35
36#include <soci/sqlite3/soci-sqlite3.h>
37
38namespace ripple {
39namespace detail {
40
46static std::string
48{
49 static_assert(
50 TableTypeCount == 3,
51 "Need to modify switch statement if enum is modified");
52
53 switch (type)
54 {
56 return "Ledgers";
58 return "Transactions";
60 return "AccountTransactions";
61 default:
62 UNREACHABLE("ripple::detail::to_string : invalid TableType");
63 return "Unknown";
64 }
65}
66
67DatabasePairValid
69 Config const& config,
70 DatabaseCon::Setup const& setup,
71 DatabaseCon::CheckpointerSetup const& checkpointerSetup,
73{
74 // ledger database
75 auto lgr{std::make_unique<DatabaseCon>(
76 setup, LgrDBName, setup.lgrPragma, LgrDBInit, checkpointerSetup, j)};
77 lgr->getSession() << boost::str(
78 boost::format("PRAGMA cache_size=-%d;") %
80
81 if (config.useTxTables())
82 {
83 // transaction database
84 auto tx{std::make_unique<DatabaseCon>(
85 setup, TxDBName, setup.txPragma, TxDBInit, checkpointerSetup, j)};
86 tx->getSession() << boost::str(
87 boost::format("PRAGMA cache_size=-%d;") %
89
90 if (!setup.standAlone || setup.startUp == Config::LOAD ||
91 setup.startUp == Config::LOAD_FILE ||
92 setup.startUp == Config::REPLAY)
93 {
94 // Check if AccountTransactions has primary key
95 std::string cid, name, type;
96 std::size_t notnull, dflt_value, pk;
97 soci::indicator ind;
98 soci::statement st =
99 (tx->getSession().prepare
100 << ("PRAGMA table_info(AccountTransactions);"),
101 soci::into(cid),
102 soci::into(name),
103 soci::into(type),
104 soci::into(notnull),
105 soci::into(dflt_value, ind),
106 soci::into(pk));
107
108 st.execute();
109 while (st.fetch())
110 {
111 if (pk == 1)
112 {
113 return {std::move(lgr), std::move(tx), false};
114 }
115 }
116 }
117
118 return {std::move(lgr), std::move(tx), true};
119 }
120 else
121 return {std::move(lgr), {}, true};
122}
123
125getMinLedgerSeq(soci::session& session, TableType type)
126{
127 std::string query = "SELECT MIN(LedgerSeq) FROM " + to_string(type) + ";";
128 // SOCI requires boost::optional (not std::optional) as the parameter.
129 boost::optional<LedgerIndex> m;
130 session << query, soci::into(m);
131 return m ? *m : std::optional<LedgerIndex>();
132}
133
135getMaxLedgerSeq(soci::session& session, TableType type)
136{
137 std::string query = "SELECT MAX(LedgerSeq) FROM " + to_string(type) + ";";
138 // SOCI requires boost::optional (not std::optional) as the parameter.
139 boost::optional<LedgerIndex> m;
140 session << query, soci::into(m);
141 return m ? *m : std::optional<LedgerIndex>();
142}
143
144void
145deleteByLedgerSeq(soci::session& session, TableType type, LedgerIndex ledgerSeq)
146{
147 session << "DELETE FROM " << to_string(type)
148 << " WHERE LedgerSeq == " << ledgerSeq << ";";
149}
150
151void
153 soci::session& session,
154 TableType type,
155 LedgerIndex ledgerSeq)
156{
157 session << "DELETE FROM " << to_string(type) << " WHERE LedgerSeq < "
158 << ledgerSeq << ";";
159}
160
162getRows(soci::session& session, TableType type)
163{
164 std::size_t rows;
165 session << "SELECT COUNT(*) AS rows "
166 "FROM "
167 << to_string(type) << ";",
168 soci::into(rows);
169
170 return rows;
171}
172
174getRowsMinMax(soci::session& session, TableType type)
175{
177 session << "SELECT COUNT(*) AS rows, "
178 "MIN(LedgerSeq) AS first, "
179 "MAX(LedgerSeq) AS last "
180 "FROM "
181 << to_string(type) << ";",
182 soci::into(res.numberOfRows), soci::into(res.minLedgerSequence),
183 soci::into(res.maxLedgerSequence);
184
185 return res;
186}
187
188bool
190 DatabaseCon& ldgDB,
191 DatabaseCon& txnDB,
192 Application& app,
193 std::shared_ptr<Ledger const> const& ledger,
194 bool current)
195{
196 auto j = app.journal("Ledger");
197 auto seq = ledger->info().seq;
198
199 // TODO(tom): Fix this hard-coded SQL!
200 JLOG(j.trace()) << "saveValidatedLedger " << (current ? "" : "fromAcquire ")
201 << seq;
202
203 if (!ledger->info().accountHash.isNonZero())
204 {
205 JLOG(j.fatal()) << "AH is zero: " << getJson({*ledger, {}});
206 UNREACHABLE("ripple::detail::saveValidatedLedger : zero account hash");
207 }
208
209 if (ledger->info().accountHash != ledger->stateMap().getHash().as_uint256())
210 {
211 JLOG(j.fatal()) << "sAL: " << ledger->info().accountHash
212 << " != " << ledger->stateMap().getHash();
213 JLOG(j.fatal()) << "saveAcceptedLedger: seq=" << seq
214 << ", current=" << current;
215 UNREACHABLE(
216 "ripple::detail::saveValidatedLedger : mismatched account hash");
217 }
218
219 XRPL_ASSERT(
220 ledger->info().txHash == ledger->txMap().getHash().as_uint256(),
221 "ripple::detail::saveValidatedLedger : transaction hash match");
222
223 // Save the ledger header in the hashed object store
224 {
225 Serializer s(128);
227 addRaw(ledger->info(), s);
228 app.getNodeStore().store(
229 hotLEDGER, std::move(s.modData()), ledger->info().hash, seq);
230 }
231
233 try
234 {
235 aLedger = app.getAcceptedLedgerCache().fetch(ledger->info().hash);
236 if (!aLedger)
237 {
238 aLedger = std::make_shared<AcceptedLedger>(ledger, app);
239 app.getAcceptedLedgerCache().canonicalize_replace_client(
240 ledger->info().hash, aLedger);
241 }
242 }
243 catch (std::exception const&)
244 {
245 JLOG(j.warn()) << "An accepted ledger was missing nodes";
246 app.getLedgerMaster().failedSave(seq, ledger->info().hash);
247 // Clients can now trust the database for information about this
248 // ledger sequence.
249 app.pendingSaves().finishWork(seq);
250 return false;
251 }
252
253 {
254 static boost::format deleteLedger(
255 "DELETE FROM Ledgers WHERE LedgerSeq = %u;");
256 static boost::format deleteTrans1(
257 "DELETE FROM Transactions WHERE LedgerSeq = %u;");
258 static boost::format deleteTrans2(
259 "DELETE FROM AccountTransactions WHERE LedgerSeq = %u;");
260 static boost::format deleteAcctTrans(
261 "DELETE FROM AccountTransactions WHERE TransID = '%s';");
262
263 {
264 auto db = ldgDB.checkoutDb();
265 *db << boost::str(deleteLedger % seq);
266 }
267
268 if (app.config().useTxTables())
269 {
270 auto db = txnDB.checkoutDb();
271
272 soci::transaction tr(*db);
273
274 *db << boost::str(deleteTrans1 % seq);
275 *db << boost::str(deleteTrans2 % seq);
276
277 std::string const ledgerSeq(std::to_string(seq));
278
279 for (auto const& acceptedLedgerTx : *aLedger)
280 {
281 uint256 transactionID = acceptedLedgerTx->getTransactionID();
282
283 std::string const txnId(to_string(transactionID));
284 std::string const txnSeq(
285 std::to_string(acceptedLedgerTx->getTxnSeq()));
286
287 *db << boost::str(deleteAcctTrans % transactionID);
288
289 auto const& accts = acceptedLedgerTx->getAffected();
290
291 if (!accts.empty())
292 {
293 std::string sql(
294 "INSERT INTO AccountTransactions "
295 "(TransID, Account, LedgerSeq, TxnSeq) VALUES ");
296
297 // Try to make an educated guess on how much space we'll
298 // need for our arguments. In argument order we have: 64
299 // + 34 + 10 + 10 = 118 + 10 extra = 128 bytes
300 sql.reserve(sql.length() + (accts.size() * 128));
301
302 bool first = true;
303 for (auto const& account : accts)
304 {
305 if (!first)
306 sql += ", ('";
307 else
308 {
309 sql += "('";
310 first = false;
311 }
312
313 sql += txnId;
314 sql += "','";
315 sql += toBase58(account);
316 sql += "',";
317 sql += ledgerSeq;
318 sql += ",";
319 sql += txnSeq;
320 sql += ")";
321 }
322 sql += ";";
323 JLOG(j.trace()) << "ActTx: " << sql;
324 *db << sql;
325 }
326 else if (auto const& sleTxn = acceptedLedgerTx->getTxn();
327 !isPseudoTx(*sleTxn))
328 {
329 // It's okay for pseudo transactions to not affect any
330 // accounts. But otherwise...
331 JLOG(j.warn()) << "Transaction in ledger " << seq
332 << " affects no accounts";
333 JLOG(j.warn()) << sleTxn->getJson(JsonOptions::none);
334 }
335
336 *db
338 acceptedLedgerTx->getTxn()->getMetaSQL(
339 seq, acceptedLedgerTx->getEscMeta()) +
340 ";");
341
343 }
344
345 tr.commit();
346 }
347
348 {
349 static std::string addLedger(
350 R"sql(INSERT OR REPLACE INTO Ledgers
351 (LedgerHash,LedgerSeq,PrevHash,TotalCoins,ClosingTime,PrevClosingTime,
352 CloseTimeRes,CloseFlags,AccountSetHash,TransSetHash)
353 VALUES
354 (:ledgerHash,:ledgerSeq,:prevHash,:totalCoins,:closingTime,:prevClosingTime,
355 :closeTimeRes,:closeFlags,:accountSetHash,:transSetHash);)sql");
356
357 auto db(ldgDB.checkoutDb());
358
359 soci::transaction tr(*db);
360
361 auto const hash = to_string(ledger->info().hash);
362 auto const parentHash = to_string(ledger->info().parentHash);
363 auto const drops = to_string(ledger->info().drops);
364 auto const closeTime =
365 ledger->info().closeTime.time_since_epoch().count();
366 auto const parentCloseTime =
367 ledger->info().parentCloseTime.time_since_epoch().count();
368 auto const closeTimeResolution =
369 ledger->info().closeTimeResolution.count();
370 auto const closeFlags = ledger->info().closeFlags;
371 auto const accountHash = to_string(ledger->info().accountHash);
372 auto const txHash = to_string(ledger->info().txHash);
373
374 *db << addLedger, soci::use(hash), soci::use(seq),
375 soci::use(parentHash), soci::use(drops), soci::use(closeTime),
376 soci::use(parentCloseTime), soci::use(closeTimeResolution),
377 soci::use(closeFlags), soci::use(accountHash),
378 soci::use(txHash);
379
380 tr.commit();
381 }
382 }
383
384 return true;
385}
386
397 soci::session& session,
398 std::string const& sqlSuffix,
400{
401 // SOCI requires boost::optional (not std::optional) as parameters.
402 boost::optional<std::string> hash, parentHash, accountHash, txHash;
403 boost::optional<std::uint64_t> seq, drops, closeTime, parentCloseTime,
404 closeTimeResolution, closeFlags;
405
406 std::string const sql =
407 "SELECT "
408 "LedgerHash, PrevHash, AccountSetHash, TransSetHash, "
409 "TotalCoins,"
410 "ClosingTime, PrevClosingTime, CloseTimeRes, CloseFlags,"
411 "LedgerSeq FROM Ledgers " +
412 sqlSuffix + ";";
413
414 session << sql, soci::into(hash), soci::into(parentHash),
415 soci::into(accountHash), soci::into(txHash), soci::into(drops),
416 soci::into(closeTime), soci::into(parentCloseTime),
417 soci::into(closeTimeResolution), soci::into(closeFlags),
418 soci::into(seq);
419
420 if (!session.got_data())
421 {
422 JLOG(j.debug()) << "Ledger not found: " << sqlSuffix;
423 return {};
424 }
425
426 using time_point = NetClock::time_point;
427 using duration = NetClock::duration;
428
429 LedgerInfo info;
430
431 if (hash && !info.hash.parseHex(*hash))
432 {
433 JLOG(j.debug()) << "Hash parse error for ledger: " << sqlSuffix;
434 return {};
435 }
436
437 if (parentHash && !info.parentHash.parseHex(*parentHash))
438 {
439 JLOG(j.debug()) << "parentHash parse error for ledger: " << sqlSuffix;
440 return {};
441 }
442
443 if (accountHash && !info.accountHash.parseHex(*accountHash))
444 {
445 JLOG(j.debug()) << "accountHash parse error for ledger: " << sqlSuffix;
446 return {};
447 }
448
449 if (txHash && !info.txHash.parseHex(*txHash))
450 {
451 JLOG(j.debug()) << "txHash parse error for ledger: " << sqlSuffix;
452 return {};
453 }
454
455 info.seq = rangeCheckedCast<std::uint32_t>(seq.value_or(0));
456 info.drops = drops.value_or(0);
457 info.closeTime = time_point{duration{closeTime.value_or(0)}};
458 info.parentCloseTime = time_point{duration{parentCloseTime.value_or(0)}};
459 info.closeFlags = closeFlags.value_or(0);
460 info.closeTimeResolution = duration{closeTimeResolution.value_or(0)};
462 return info;
463}
464
467 soci::session& session,
468 LedgerIndex ledgerSeq,
470{
472 s << "WHERE LedgerSeq = " << ledgerSeq;
473 return getLedgerInfo(session, s.str(), j);
474}
475
477getNewestLedgerInfo(soci::session& session, beast::Journal j)
478{
480 s << "ORDER BY LedgerSeq DESC LIMIT 1";
481 return getLedgerInfo(session, s.str(), j);
482}
483
486 soci::session& session,
487 LedgerIndex ledgerFirstIndex,
489{
491 s << "WHERE LedgerSeq >= " + std::to_string(ledgerFirstIndex) +
492 " ORDER BY LedgerSeq ASC LIMIT 1";
493 return getLedgerInfo(session, s.str(), j);
494}
495
498 soci::session& session,
499 LedgerIndex ledgerFirstIndex,
501{
503 s << "WHERE LedgerSeq >= " + std::to_string(ledgerFirstIndex) +
504 " ORDER BY LedgerSeq DESC LIMIT 1";
505 return getLedgerInfo(session, s.str(), j);
506}
507
510 soci::session& session,
511 uint256 const& ledgerHash,
513{
515 s << "WHERE LedgerHash = '" << ledgerHash << "'";
516 return getLedgerInfo(session, s.str(), j);
517}
518
520getHashByIndex(soci::session& session, LedgerIndex ledgerIndex)
521{
522 uint256 ret;
523
524 std::string sql =
525 "SELECT LedgerHash FROM Ledgers INDEXED BY SeqLedger WHERE LedgerSeq='";
526 sql.append(std::to_string(ledgerIndex));
527 sql.append("';");
528
529 std::string hash;
530 {
531 // SOCI requires boost::optional (not std::optional) as the parameter.
532 boost::optional<std::string> lh;
533 session << sql, soci::into(lh);
534
535 if (!session.got_data() || !lh)
536 return ret;
537
538 hash = *lh;
539 if (hash.empty())
540 return ret;
541 }
542
543 if (!ret.parseHex(hash))
544 return ret;
546 return ret;
547}
548
551 soci::session& session,
552 LedgerIndex ledgerIndex,
554{
555 // SOCI requires boost::optional (not std::optional) as the parameter.
556 boost::optional<std::string> lhO, phO;
557
558 session << "SELECT LedgerHash,PrevHash FROM Ledgers "
559 "INDEXED BY SeqLedger WHERE LedgerSeq = :ls;",
560 soci::into(lhO), soci::into(phO), soci::use(ledgerIndex);
561
562 if (!lhO || !phO)
563 {
564 auto stream = j.trace();
565 JLOG(stream) << "Don't have ledger " << ledgerIndex;
566 return {};
567 }
568
569 LedgerHashPair hashes;
570 if (!hashes.ledgerHash.parseHex(*lhO) || !hashes.parentHash.parseHex(*phO))
571 {
572 auto stream = j.trace();
573 JLOG(stream) << "Error parse hashes for ledger " << ledgerIndex;
574 return {};
575 }
577 return hashes;
578}
579
582 soci::session& session,
583 LedgerIndex minSeq,
584 LedgerIndex maxSeq,
586{
587 std::string sql =
588 "SELECT LedgerSeq,LedgerHash,PrevHash FROM Ledgers WHERE LedgerSeq >= ";
589 sql.append(std::to_string(minSeq));
590 sql.append(" AND LedgerSeq <= ");
591 sql.append(std::to_string(maxSeq));
592 sql.append(";");
593
594 std::uint64_t ls;
595 std::string lh;
596 // SOCI requires boost::optional (not std::optional) as the parameter.
597 boost::optional<std::string> ph;
598 soci::statement st =
599 (session.prepare << sql,
600 soci::into(ls),
601 soci::into(lh),
602 soci::into(ph));
603
604 st.execute();
606 while (st.fetch())
607 {
608 LedgerHashPair& hashes = res[rangeCheckedCast<LedgerIndex>(ls)];
609 if (!hashes.ledgerHash.parseHex(lh))
610 {
611 JLOG(j.warn()) << "Error parsed hash for ledger seq: " << ls;
612 }
613 if (!ph)
614 {
615 JLOG(j.warn()) << "Null prev hash for ledger seq: " << ls;
616 }
617 else if (!hashes.parentHash.parseHex(*ph))
618 {
619 JLOG(j.warn()) << "Error parsed prev hash for ledger seq: " << ls;
620 }
622 return res;
623}
624
627 soci::session& session,
628 Application& app,
629 LedgerIndex startIndex,
630 int quantity)
631{
632 std::string sql = boost::str(
633 boost::format(
634 "SELECT LedgerSeq, Status, RawTxn "
635 "FROM Transactions ORDER BY LedgerSeq DESC LIMIT %u,%u;") %
636 startIndex % 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;
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 convert(sociRawTxnBlob, rawTxn);
660 else
661 rawTxn.clear();
662
663 if (auto trans = Transaction::transactionFromSQL(
664 ledgerSeq, status, rawTxn, app))
665 {
666 total++;
667 txs.push_back(trans);
668 }
669 }
670 }
671
672 return {txs, total};
673}
674
692static std::string
694 Application& app,
695 std::string selection,
696 RelationalDatabase::AccountTxOptions const& options,
697 bool descending,
698 bool binary,
699 bool count,
701{
702 constexpr std::uint32_t NONBINARY_PAGE_LENGTH = 200;
703 constexpr std::uint32_t BINARY_PAGE_LENGTH = 500;
704
705 std::uint32_t numberOfResults;
706
707 if (count)
708 {
709 numberOfResults = std::numeric_limits<std::uint32_t>::max();
710 }
711 else if (options.limit == UINT32_MAX)
712 {
713 numberOfResults = binary ? BINARY_PAGE_LENGTH : NONBINARY_PAGE_LENGTH;
714 }
715 else if (!options.bUnlimited)
716 {
717 numberOfResults = std::min(
718 binary ? BINARY_PAGE_LENGTH : NONBINARY_PAGE_LENGTH, options.limit);
719 }
720 else
721 {
722 numberOfResults = options.limit;
723 }
724
725 std::string maxClause = "";
726 std::string minClause = "";
727
728 if (options.maxLedger)
729 {
730 maxClause = boost::str(
731 boost::format("AND AccountTransactions.LedgerSeq <= '%u'") %
732 options.maxLedger);
733 }
734
735 if (options.minLedger)
736 {
737 minClause = boost::str(
738 boost::format("AND AccountTransactions.LedgerSeq >= '%u'") %
739 options.minLedger);
740 }
741
742 std::string sql;
743
744 if (count)
745 sql = boost::str(
746 boost::format("SELECT %s FROM AccountTransactions "
747 "WHERE Account = '%s' %s %s LIMIT %u, %u;") %
748 selection % toBase58(options.account) % maxClause % minClause %
749 options.offset % numberOfResults);
750 else
751 sql = boost::str(
752 boost::format(
753 "SELECT %s FROM "
754 "AccountTransactions INNER JOIN Transactions "
755 "ON Transactions.TransID = AccountTransactions.TransID "
756 "WHERE Account = '%s' %s %s "
757 "ORDER BY AccountTransactions.LedgerSeq %s, "
758 "AccountTransactions.TxnSeq %s, AccountTransactions.TransID %s "
759 "LIMIT %u, %u;") %
760 selection % toBase58(options.account) % maxClause % minClause %
761 (descending ? "DESC" : "ASC") % (descending ? "DESC" : "ASC") %
762 (descending ? "DESC" : "ASC") % options.offset % numberOfResults);
763 JLOG(j.trace()) << "txSQL query: " << sql;
764 return sql;
765}
766
790 soci::session& session,
791 Application& app,
792 LedgerMaster& ledgerMaster,
793 RelationalDatabase::AccountTxOptions const& options,
794 bool descending,
796{
798
800 app,
801 "AccountTransactions.LedgerSeq,Status,RawTxn,TxnMeta",
802 options,
803 descending,
804 false,
805 false,
806 j);
807 if (sql == "")
808 return {ret, 0};
809
810 int total = 0;
811 {
812 // SOCI requires boost::optional (not std::optional) as parameters.
813 boost::optional<std::uint64_t> ledgerSeq;
814 boost::optional<std::string> status;
815 soci::blob sociTxnBlob(session), sociTxnMetaBlob(session);
816 soci::indicator rti, tmi;
817 Blob rawTxn, txnMeta;
818
819 soci::statement st =
820 (session.prepare << sql,
821 soci::into(ledgerSeq),
822 soci::into(status),
823 soci::into(sociTxnBlob, rti),
824 soci::into(sociTxnMetaBlob, tmi));
825
826 st.execute();
827 while (st.fetch())
828 {
829 if (soci::i_ok == rti)
830 convert(sociTxnBlob, rawTxn);
831 else
832 rawTxn.clear();
833
834 if (soci::i_ok == tmi)
835 convert(sociTxnMetaBlob, txnMeta);
836 else
837 txnMeta.clear();
838
839 auto txn =
840 Transaction::transactionFromSQL(ledgerSeq, status, rawTxn, app);
841
842 if (txnMeta.empty())
843 { // Work around a bug that could leave the metadata missing
844 auto const seq =
845 rangeCheckedCast<std::uint32_t>(ledgerSeq.value_or(0));
846
847 JLOG(j.warn())
848 << "Recovering ledger " << seq << ", txn " << txn->getID();
849
850 if (auto l = ledgerMaster.getLedgerBySeq(seq))
851 pendSaveValidated(app, l, false, false);
852 }
853
854 if (txn)
855 {
856 ret.emplace_back(
857 txn,
858 std::make_shared<TxMeta>(
859 txn->getID(), txn->getLedger(), txnMeta));
860 total++;
861 }
862 }
863 }
865 return {ret, total};
866}
867
870 soci::session& session,
871 Application& app,
872 LedgerMaster& ledgerMaster,
873 RelationalDatabase::AccountTxOptions const& options,
876 return getAccountTxs(session, app, ledgerMaster, options, false, j);
877}
878
881 soci::session& session,
882 Application& app,
886{
887 return getAccountTxs(session, app, ledgerMaster, options, true, j);
888}
889
912 soci::session& session,
913 Application& app,
914 RelationalDatabase::AccountTxOptions const& options,
915 bool descending,
917{
919
921 app,
922 "AccountTransactions.LedgerSeq,Status,RawTxn,TxnMeta",
923 options,
924 descending,
925 true /*binary*/,
926 false,
927 j);
928 if (sql == "")
929 return {ret, 0};
930
931 int total = 0;
932
933 {
934 // SOCI requires boost::optional (not std::optional) as parameters.
935 boost::optional<std::uint64_t> ledgerSeq;
936 boost::optional<std::string> status;
937 soci::blob sociTxnBlob(session), sociTxnMetaBlob(session);
938 soci::indicator rti, tmi;
939
940 soci::statement st =
941 (session.prepare << sql,
942 soci::into(ledgerSeq),
943 soci::into(status),
944 soci::into(sociTxnBlob, rti),
945 soci::into(sociTxnMetaBlob, tmi));
946
947 st.execute();
948 while (st.fetch())
949 {
950 Blob rawTxn;
951 if (soci::i_ok == rti)
952 convert(sociTxnBlob, rawTxn);
953 Blob txnMeta;
954 if (soci::i_ok == tmi)
955 convert(sociTxnMetaBlob, txnMeta);
956
957 auto const seq =
958 rangeCheckedCast<std::uint32_t>(ledgerSeq.value_or(0));
959
960 ret.emplace_back(std::move(rawTxn), std::move(txnMeta), seq);
961 total++;
962 }
963 }
965 return {ret, total};
966}
967
970 soci::session& session,
971 Application& app,
972 RelationalDatabase::AccountTxOptions const& options,
975 return getAccountTxsB(session, app, options, false, j);
976}
977
980 soci::session& session,
981 Application& app,
984{
985 return getAccountTxsB(session, app, options, true, j);
986}
987
1009 soci::session& session,
1010 std::function<void(std::uint32_t)> const& onUnsavedLedger,
1012 void(std::uint32_t, std::string const&, Blob&&, Blob&&)> const&
1013 onTransaction,
1014 RelationalDatabase::AccountTxPageOptions const& options,
1015 std::uint32_t page_length,
1016 bool forward)
1017{
1018 int total = 0;
1019
1020 bool lookingForMarker = options.marker.has_value();
1021
1022 std::uint32_t numberOfResults;
1023
1024 if (options.limit == 0 || options.limit == UINT32_MAX ||
1025 (options.limit > page_length && !options.bAdmin))
1026 numberOfResults = page_length;
1027 else
1028 numberOfResults = options.limit;
1029
1030 // As an account can have many thousands of transactions, there is a limit
1031 // placed on the amount of transactions returned. If the limit is reached
1032 // before the result set has been exhausted (we always query for one more
1033 // than the limit), then we return an opaque marker that can be supplied in
1034 // a subsequent query.
1035 std::uint32_t queryLimit = numberOfResults + 1;
1036 std::uint32_t findLedger = 0, findSeq = 0;
1037
1038 if (lookingForMarker)
1039 {
1040 findLedger = options.marker->ledgerSeq;
1041 findSeq = options.marker->txnSeq;
1042 }
1043
1045
1046 static std::string const prefix(
1047 R"(SELECT AccountTransactions.LedgerSeq,AccountTransactions.TxnSeq,
1048 Status,RawTxn,TxnMeta
1049 FROM AccountTransactions INNER JOIN Transactions
1050 ON Transactions.TransID = AccountTransactions.TransID
1051 AND AccountTransactions.Account = '%s' WHERE
1052 )");
1053
1054 std::string sql;
1055
1056 // SQL's BETWEEN uses a closed interval ([a,b])
1057
1058 const char* const order = forward ? "ASC" : "DESC";
1059
1060 if (findLedger == 0)
1061 {
1062 sql = boost::str(
1063 boost::format(
1064 prefix + (R"(AccountTransactions.LedgerSeq BETWEEN %u AND %u
1065 ORDER BY AccountTransactions.LedgerSeq %s,
1066 AccountTransactions.TxnSeq %s
1067 LIMIT %u;)")) %
1068 toBase58(options.account) % options.minLedger % options.maxLedger %
1069 order % order % queryLimit);
1070 }
1071 else
1072 {
1073 const char* const compare = forward ? ">=" : "<=";
1074 const std::uint32_t minLedger =
1075 forward ? findLedger + 1 : options.minLedger;
1076 const std::uint32_t maxLedger =
1077 forward ? options.maxLedger : findLedger - 1;
1078
1079 auto b58acct = toBase58(options.account);
1080 sql = boost::str(
1081 boost::format((
1082 R"(SELECT AccountTransactions.LedgerSeq,AccountTransactions.TxnSeq,
1083 Status,RawTxn,TxnMeta
1084 FROM AccountTransactions, Transactions WHERE
1085 (AccountTransactions.TransID = Transactions.TransID AND
1086 AccountTransactions.Account = '%s' AND
1087 AccountTransactions.LedgerSeq BETWEEN %u AND %u)
1088 UNION
1089 SELECT AccountTransactions.LedgerSeq,AccountTransactions.TxnSeq,Status,RawTxn,TxnMeta
1090 FROM AccountTransactions, Transactions WHERE
1091 (AccountTransactions.TransID = Transactions.TransID AND
1092 AccountTransactions.Account = '%s' AND
1093 AccountTransactions.LedgerSeq = %u AND
1094 AccountTransactions.TxnSeq %s %u)
1095 ORDER BY AccountTransactions.LedgerSeq %s,
1096 AccountTransactions.TxnSeq %s
1097 LIMIT %u;
1098 )")) %
1099 b58acct % minLedger % maxLedger % b58acct % findLedger % compare %
1100 findSeq % order % order % queryLimit);
1101 }
1102
1103 {
1104 Blob rawData;
1105 Blob rawMeta;
1106
1107 // SOCI requires boost::optional (not std::optional) as parameters.
1108 boost::optional<std::uint64_t> ledgerSeq;
1109 boost::optional<std::uint32_t> txnSeq;
1110 boost::optional<std::string> status;
1111 soci::blob txnData(session);
1112 soci::blob txnMeta(session);
1113 soci::indicator dataPresent, metaPresent;
1114
1115 soci::statement st =
1116 (session.prepare << sql,
1117 soci::into(ledgerSeq),
1118 soci::into(txnSeq),
1119 soci::into(status),
1120 soci::into(txnData, dataPresent),
1121 soci::into(txnMeta, metaPresent));
1122
1123 st.execute();
1124
1125 while (st.fetch())
1126 {
1127 if (lookingForMarker)
1128 {
1129 if (findLedger == ledgerSeq.value_or(0) &&
1130 findSeq == txnSeq.value_or(0))
1131 {
1132 lookingForMarker = false;
1133 }
1134 else
1135 continue;
1136 }
1137 else if (numberOfResults == 0)
1138 {
1139 newmarker = {
1140 rangeCheckedCast<std::uint32_t>(ledgerSeq.value_or(0)),
1141 txnSeq.value_or(0)};
1142 break;
1143 }
1144
1145 if (dataPresent == soci::i_ok)
1146 convert(txnData, rawData);
1147 else
1148 rawData.clear();
1149
1150 if (metaPresent == soci::i_ok)
1151 convert(txnMeta, rawMeta);
1152 else
1153 rawMeta.clear();
1154
1155 // Work around a bug that could leave the metadata missing
1156 if (rawMeta.size() == 0)
1157 onUnsavedLedger(ledgerSeq.value_or(0));
1158
1159 // `rawData` and `rawMeta` will be used after they are moved.
1160 // That's OK.
1161 onTransaction(
1162 rangeCheckedCast<std::uint32_t>(ledgerSeq.value_or(0)),
1163 *status,
1164 std::move(rawData),
1165 std::move(rawMeta));
1166 // Note some callbacks will move the data, some will not. Clear
1167 // them so code doesn't depend on if the data was actually moved
1168 // or not. The code will be more efficient if `rawData` and
1169 // `rawMeta` don't have to allocate in `convert`, so don't
1170 // refactor my moving these variables into loop scope.
1171 rawData.clear();
1172 rawMeta.clear();
1173
1174 --numberOfResults;
1175 total++;
1176 }
1177 }
1178
1179 return {newmarker, total};
1180}
1181
1184 soci::session& session,
1185 std::function<void(std::uint32_t)> const& onUnsavedLedger,
1187 void(std::uint32_t, std::string const&, Blob&&, Blob&&)> const&
1188 onTransaction,
1190 std::uint32_t page_length)
1191{
1192 return accountTxPage(
1193 session, onUnsavedLedger, onTransaction, options, page_length, true);
1194}
1195
1198 soci::session& session,
1199 std::function<void(std::uint32_t)> const& onUnsavedLedger,
1201 void(std::uint32_t, std::string const&, Blob&&, Blob&&)> const&
1202 onTransaction,
1204 std::uint32_t page_length)
1205{
1206 return accountTxPage(
1207 session, onUnsavedLedger, onTransaction, options, page_length, false);
1208}
1209
1212 soci::session& session,
1213 Application& app,
1214 uint256 const& id,
1215 std::optional<ClosedInterval<uint32_t>> const& range,
1216 error_code_i& ec)
1217{
1218 std::string sql =
1219 "SELECT LedgerSeq,Status,RawTxn,TxnMeta "
1220 "FROM Transactions WHERE TransID='";
1221
1222 sql.append(to_string(id));
1223 sql.append("';");
1224
1225 // SOCI requires boost::optional (not std::optional) as parameters.
1226 boost::optional<std::uint64_t> ledgerSeq;
1227 boost::optional<std::string> status;
1228 Blob rawTxn, rawMeta;
1229 {
1230 soci::blob sociRawTxnBlob(session), sociRawMetaBlob(session);
1231 soci::indicator txn, meta;
1232
1233 session << sql, soci::into(ledgerSeq), soci::into(status),
1234 soci::into(sociRawTxnBlob, txn), soci::into(sociRawMetaBlob, meta);
1235
1236 auto const got_data = session.got_data();
1237
1238 if ((!got_data || txn != soci::i_ok || meta != soci::i_ok) && !range)
1239 return TxSearched::unknown;
1240
1241 if (!got_data)
1242 {
1243 uint64_t count = 0;
1244 soci::indicator rti;
1245
1246 session
1247 << "SELECT COUNT(DISTINCT LedgerSeq) FROM Transactions WHERE "
1248 "LedgerSeq BETWEEN "
1249 << range->first() << " AND " << range->last() << ";",
1250 soci::into(count, rti);
1251
1252 if (!session.got_data() || rti != soci::i_ok)
1253 return TxSearched::some;
1254
1255 return count == (range->last() - range->first() + 1)
1258 }
1259
1260 convert(sociRawTxnBlob, rawTxn);
1261 convert(sociRawMetaBlob, rawMeta);
1262 }
1263
1264 try
1265 {
1266 auto txn =
1267 Transaction::transactionFromSQL(ledgerSeq, status, rawTxn, app);
1268
1269 if (!ledgerSeq)
1270 return std::pair{std::move(txn), nullptr};
1271
1272 std::uint32_t inLedger =
1273 rangeCheckedCast<std::uint32_t>(ledgerSeq.value());
1274
1275 auto txMeta = std::make_shared<TxMeta>(id, inLedger, rawMeta);
1276
1277 return std::pair{std::move(txn), std::move(txMeta)};
1278 }
1279 catch (std::exception& e)
1280 {
1281 JLOG(app.journal("Ledger").warn())
1282 << "Unable to deserialize transaction from raw SQL value. Error: "
1283 << e.what();
1284
1286 }
1287
1288 return TxSearched::unknown;
1289}
1290
1291bool
1292dbHasSpace(soci::session& session, Config const& config, beast::Journal j)
1293{
1294 boost::filesystem::space_info space =
1295 boost::filesystem::space(config.legacy("database_path"));
1296
1297 if (space.available < megabytes(512))
1298 {
1299 JLOG(j.fatal()) << "Remaining free disk space is less than 512MB";
1300 return false;
1301 }
1302
1303 if (config.useTxTables())
1304 {
1305 DatabaseCon::Setup dbSetup = setup_DatabaseCon(config);
1306 boost::filesystem::path dbPath = dbSetup.dataDir / TxDBName;
1307 boost::system::error_code ec;
1309 boost::filesystem::file_size(dbPath, ec);
1310 if (ec)
1311 {
1312 JLOG(j.error())
1313 << "Error checking transaction db file size: " << ec.message();
1314 dbSize.reset();
1315 }
1316
1317 static auto const pageSize = [&] {
1318 std::uint32_t ps;
1319 session << "PRAGMA page_size;", soci::into(ps);
1320 return ps;
1321 }();
1322 static auto const maxPages = [&] {
1323 std::uint32_t mp;
1324 session << "PRAGMA max_page_count;", soci::into(mp);
1325 return mp;
1326 }();
1327 std::uint32_t pageCount;
1328 session << "PRAGMA page_count;", soci::into(pageCount);
1329 std::uint32_t freePages = maxPages - pageCount;
1330 std::uint64_t freeSpace =
1331 safe_cast<std::uint64_t>(freePages) * pageSize;
1332 JLOG(j.info())
1333 << "Transaction DB pathname: " << dbPath.string()
1334 << "; file size: " << dbSize.value_or(-1) << " bytes"
1335 << "; SQLite page size: " << pageSize << " bytes"
1336 << "; Free pages: " << freePages << "; Free space: " << freeSpace
1337 << " bytes; "
1338 << "Note that this does not take into account available disk "
1339 "space.";
1340
1341 if (freeSpace < megabytes(512))
1342 {
1343 JLOG(j.fatal())
1344 << "Free SQLite space for transaction db is less than "
1345 "512MB. To fix this, rippled must be executed with the "
1346 "vacuum parameter before restarting. "
1347 "Note that this activity can take multiple days, "
1348 "depending on database size.";
1349 return false;
1350 }
1351 }
1352
1353 return true;
1354}
1355
1356} // namespace detail
1357} // namespace ripple
T append(T... args)
A generic endpoint for log messages.
Definition: Journal.h:60
Stream fatal() const
Definition: Journal.h:352
Stream error() const
Definition: Journal.h:346
Stream debug() const
Definition: Journal.h:328
Stream info() const
Definition: Journal.h:334
Stream trace() const
Severity stream access functions.
Definition: Journal.h:322
Stream warn() const
Definition: Journal.h:340
virtual Config & config()=0
virtual beast::Journal journal(std::string const &name)=0
virtual NodeStore::Database & getNodeStore()=0
virtual TaggedCache< uint256, AcceptedLedger > & getAcceptedLedgerCache()=0
virtual LedgerMaster & getLedgerMaster()=0
virtual TransactionMaster & getMasterTransaction()=0
virtual PendingSaves & pendingSaves()=0
bool useTxTables() const
Definition: Config.h:343
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.
Definition: Config.cpp:1079
LockedSociSession checkoutDb()
Definition: DatabaseCon.h:190
void failedSave(std::uint32_t seq, uint256 const &hash)
std::chrono::time_point< NetClock > time_point
Definition: chrono.h:69
std::chrono::duration< rep, period > duration
Definition: chrono.h:68
virtual void store(NodeObjectType type, Blob &&data, uint256 const &hash, std::uint32_t ledgerSeq)=0
Store the object.
void finishWork(LedgerIndex seq)
Finish working on a ledger.
Definition: PendingSaves.h:75
std::vector< AccountTx > AccountTxs
static std::string const & getMetaSQLInsertReplaceHeader()
Definition: STTx.cpp:297
bool inLedger(uint256 const &hash, std::uint32_t ledger)
static Transaction::pointer transactionFromSQL(boost::optional< std::uint64_t > const &ledgerSeq, boost::optional< std::string > const &status, Blob const &rawTxn, Application &app)
Definition: Transaction.cpp:90
constexpr bool parseHex(std::string_view sv)
Parse a hex string into a base_uint.
Definition: base_uint.h:503
T emplace_back(T... args)
T empty(T... args)
T max(T... args)
T min(T... args)
int compare(SemanticVersion const &lhs, SemanticVersion const &rhs)
Compare two SemanticVersions against each other.
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:906
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:174
uint256 getHashByIndex(soci::session &session, LedgerIndex ledgerIndex)
getHashByIndex Returns hash of ledger with given sequence.
Definition: Node.cpp:515
std::optional< LedgerInfo > getLimitedNewestLedgerInfo(soci::session &session, LedgerIndex ledgerFirstIndex, beast::Journal j)
getLimitedNewestLedgerInfo Returns info of newest ledger from ledgers with sequences greather or equa...
Definition: Node.cpp:492
static std::string to_string(TableType type)
to_string Returns the name of a table according to its TableType.
Definition: Node.cpp:47
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 page_length)
newestAccountTxPage Searches newest transactions for given account which match given criteria startin...
Definition: Node.cpp:1171
std::optional< LedgerInfo > getNewestLedgerInfo(soci::session &session, beast::Journal j)
getNewestLedgerInfo Returns info of newest saved ledger.
Definition: Node.cpp:472
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:784
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 page_length)
oldestAccountTxPage Searches oldest transactions for given account which match given criteria startin...
Definition: Node.cpp:1157
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:68
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:145
std::size_t getRows(soci::session &session, TableType type)
getRows Returns number of rows in given table.
Definition: Node.cpp:162
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:152
bool saveValidatedLedger(DatabaseCon &ldgDB, DatabaseCon &txnDB, Application &app, std::shared_ptr< Ledger const > const &ledger, bool current)
saveValidatedLedger Saves ledger into database.
Definition: Node.cpp:189
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:864
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:621
constexpr int TableTypeCount
Definition: Node.h:32
std::optional< LedgerInfo > getLimitedOldestLedgerInfo(soci::session &session, LedgerIndex ledgerFirstIndex, beast::Journal j)
getLimitedOldestLedgerInfo Returns info of oldest ledger from ledgers with sequences greather or equa...
Definition: Node.cpp:480
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:964
std::optional< LedgerInfo > getLedgerInfoByIndex(soci::session &session, LedgerIndex ledgerSeq, beast::Journal j)
getLedgerInfoByIndex Returns ledger by its sequence.
Definition: Node.cpp:461
bool dbHasSpace(soci::session &session, Config const &config, beast::Journal j)
dbHasSpace Checks if given database has available space.
Definition: Node.cpp:1266
std::optional< LedgerInfo > getLedgerInfoByHash(soci::session &session, uint256 const &ledgerHash, beast::Journal j)
getLedgerInfoByHash Returns info of ledger with given hash.
Definition: Node.cpp:504
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:688
std::optional< LedgerIndex > getMinLedgerSeq(soci::session &session, TableType type)
getMinLedgerSeq Returns minimum ledger sequence in given table.
Definition: Node.cpp:125
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:875
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:974
static std::optional< LedgerInfo > 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:391
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:545
std::optional< LedgerIndex > getMaxLedgerSeq(soci::session &session, TableType type)
getMaxLedgerSeq Returns maximum ledger sequence in given table.
Definition: Node.cpp:135
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 page_length, bool forward)
accountTxPage Searches for the oldest or newest transactions for the account that matches the given c...
Definition: Node.cpp:1003
std::variant< RelationalDatabase::AccountTx, TxSearched > getTransaction(soci::session &session, Application &app, uint256 const &id, std::optional< ClosedInterval< uint32_t > > const &range, error_code_i &ec)
getTransaction Returns transaction with given hash.
Definition: Node.cpp:1185
PrettyAmount drops(Integer i)
Returns an XRP PrettyAmount, which is trivially convertible to STAmount.
Use hash_* containers for keys that do not need a cryptographically secure hashing algorithm.
Definition: algorithm.h:26
constexpr std::array< char const *, 8 > TxDBInit
Definition: DBInit.h:72
std::string toBase58(AccountID const &v)
Convert AccountID to base58 checked string.
Definition: AccountID.cpp:114
std::uint32_t LedgerIndex
A ledger index.
Definition: Protocol.h:120
constexpr auto megabytes(T value) noexcept
Definition: ByteUtilities.h:34
DatabaseCon::Setup setup_DatabaseCon(Config const &c, std::optional< beast::Journal > j=std::nullopt)
bool isPseudoTx(STObject const &tx)
Check whether a transaction is a pseudo-transaction.
Definition: STTx.cpp:640
error_code_i
Definition: ErrorCodes.h:40
@ rpcDB_DESERIALIZATION
Definition: ErrorCodes.h:134
base_uint< 256 > uint256
Definition: base_uint.h:558
@ hotLEDGER
Definition: NodeObject.h:34
void convert(soci::blob &from, std::vector< std::uint8_t > &to)
Definition: SociDB.cpp:157
constexpr auto LgrDBName
Definition: DBInit.h:43
@ current
This was a new validation and was added.
constexpr std::array< char const *, 5 > LgrDBInit
Definition: DBInit.h:45
constexpr auto kilobytes(T value) noexcept
Definition: ByteUtilities.h:27
constexpr auto TxDBName
Definition: DBInit.h:70
LedgerHeader LedgerInfo
Definition: LedgerHeader.h:79
std::vector< unsigned char > Blob
Storage for linear binary data.
Definition: Blob.h:30
ClosedInterval< T > range(T low, T high)
Create a closed range interval.
Definition: RangeSet.h:54
Json::Value getJson(LedgerFill const &fill)
Return a new Json::Value representing the ledger with given options.
@ ledgerMaster
ledger master data for signing
@ transactionID
transaction plus signature to give transaction ID
void addRaw(LedgerHeader const &, Serializer &, bool includeHash=false)
bool pendSaveValidated(Application &app, std::shared_ptr< Ledger const > const &ledger, bool isSynchronous, bool isCurrent)
Save, or arrange to save, a fully-validated ledger Returns false on error.
Definition: Ledger.cpp:993
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, 1 > lgrPragma
Definition: DatabaseCon.h:112
Config::StartUpType startUp
Definition: DatabaseCon.h:92
std::array< std::string, 4 > txPragma
Definition: DatabaseCon.h:111
T to_string(T... args)
T value_or(T... args)
T what(T... args)