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