rippled
AccountTxPaging.cpp
1 //------------------------------------------------------------------------------
2 /*
3  This file is part of rippled: https://github.com/ripple/rippled
4  Copyright (c) 2012, 2013 Ripple Labs Inc.
5 
6  Permission to use, copy, modify, and/or distribute this software for any
7  purpose with or without fee is hereby granted, provided that the above
8  copyright notice and this permission notice appear in all copies.
9 
10  THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11  WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12  MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13  ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14  WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15  ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16  OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17 */
18 //==============================================================================
19 
20 #include <ripple/app/ledger/LedgerToJson.h>
21 #include <ripple/app/ledger/LedgerMaster.h>
22 #include <ripple/app/main/Application.h>
23 #include <ripple/app/misc/Transaction.h>
24 #include <ripple/app/misc/impl/AccountTxPaging.h>
25 #include <ripple/protocol/Serializer.h>
26 #include <ripple/protocol/UintTypes.h>
27 #include <boost/format.hpp>
28 #include <memory>
29 
30 namespace ripple {
31 
32 void
35  std::uint32_t ledger_index,
36  std::string const& status,
37  Blob const& rawTxn,
38  Blob const& rawMeta,
39  Application& app)
40 {
41  SerialIter it (makeSlice(rawTxn));
42  auto txn = std::make_shared<STTx const> (it);
43  std::string reason;
44 
45  auto tr = std::make_shared<Transaction> (txn, reason, app);
46 
47  tr->setStatus (Transaction::sqlTransactionStatus(status));
48  tr->setLedger (ledger_index);
49 
50  auto metaset = std::make_shared<TxMeta> (
51  tr->getID (), tr->getLedger (), rawMeta);
52 
53  to.emplace_back(std::move(tr), metaset);
54 };
55 
56 void
58 {
59  if (auto l = app.getLedgerMaster().getLedgerBySeq(seq))
60  pendSaveValidated(app, l, false, false);
61 }
62 
63 void
65  DatabaseCon& connection,
66  AccountIDCache const& idCache,
67  std::function<void(std::uint32_t)> const& onUnsavedLedger,
68  std::function<void(
70  std::string const&,
71  Blob const&,
72  Blob const&)> const& onTransaction,
73  AccountID const& account,
74  std::int32_t minLedger,
75  std::int32_t maxLedger,
76  bool forward,
78  int limit,
79  bool bAdmin,
80  std::uint32_t page_length)
81 {
82  bool lookingForMarker = marker.has_value();
83 
84  std::uint32_t numberOfResults;
85 
86  if (limit <= 0 || (limit > page_length && !bAdmin))
87  numberOfResults = page_length;
88  else
89  numberOfResults = limit;
90 
91  // As an account can have many thousands of transactions, there is a limit
92  // placed on the amount of transactions returned. If the limit is reached
93  // before the result set has been exhausted (we always query for one more
94  // than the limit), then we return an opaque marker that can be supplied in
95  // a subsequent query.
96  std::uint32_t queryLimit = numberOfResults + 1;
97  std::uint32_t findLedger = 0, findSeq = 0;
98 
99  if (lookingForMarker)
100  {
101  findLedger = marker->ledgerSeq;
102  findSeq = marker->txnSeq;
103  }
104 
105  // marker is also an output parameter, so need to reset
106  marker.reset();
107 
108  static std::string const prefix(
109  R"(SELECT AccountTransactions.LedgerSeq,AccountTransactions.TxnSeq,
110  Status,RawTxn,TxnMeta
111  FROM AccountTransactions INNER JOIN Transactions
112  ON Transactions.TransID = AccountTransactions.TransID
113  AND AccountTransactions.Account = '%s' WHERE
114  )");
115 
116  std::string sql;
117 
118  // SQL's BETWEEN uses a closed interval ([a,b])
119 
120  if (forward && (findLedger == 0))
121  {
122  sql = boost::str(
123  boost::format(
124  prefix + (R"(AccountTransactions.LedgerSeq BETWEEN '%u' AND '%u'
125  ORDER BY AccountTransactions.LedgerSeq ASC,
126  AccountTransactions.TxnSeq ASC
127  LIMIT %u;)")) %
128  idCache.toBase58(account) % minLedger % maxLedger % queryLimit);
129  }
130  else if (forward && (findLedger != 0))
131  {
132  auto b58acct = idCache.toBase58(account);
133  sql = boost::str(
134  boost::format((
135  R"(SELECT AccountTransactions.LedgerSeq,AccountTransactions.TxnSeq,
136  Status,RawTxn,TxnMeta
137  FROM AccountTransactions, Transactions WHERE
138  (AccountTransactions.TransID = Transactions.TransID AND
139  AccountTransactions.Account = '%s' AND
140  AccountTransactions.LedgerSeq BETWEEN '%u' AND '%u')
141  OR
142  (AccountTransactions.TransID = Transactions.TransID AND
143  AccountTransactions.Account = '%s' AND
144  AccountTransactions.LedgerSeq = '%u' AND
145  AccountTransactions.TxnSeq >= '%u')
146  ORDER BY AccountTransactions.LedgerSeq ASC,
147  AccountTransactions.TxnSeq ASC
148  LIMIT %u;
149  )")) %
150  b58acct % (findLedger + 1) % maxLedger % b58acct % findLedger %
151  findSeq % queryLimit);
152  }
153  else if (!forward && (findLedger == 0))
154  {
155  sql = boost::str(
156  boost::format(
157  prefix + (R"(AccountTransactions.LedgerSeq BETWEEN '%u' AND '%u'
158  ORDER BY AccountTransactions.LedgerSeq DESC,
159  AccountTransactions.TxnSeq DESC
160  LIMIT %u;)")) %
161  idCache.toBase58(account) % minLedger % maxLedger % queryLimit);
162  }
163  else if (!forward && (findLedger != 0))
164  {
165  auto b58acct = idCache.toBase58(account);
166  sql = boost::str(
167  boost::format((
168  R"(SELECT AccountTransactions.LedgerSeq,AccountTransactions.TxnSeq,
169  Status,RawTxn,TxnMeta
170  FROM AccountTransactions, Transactions WHERE
171  (AccountTransactions.TransID = Transactions.TransID AND
172  AccountTransactions.Account = '%s' AND
173  AccountTransactions.LedgerSeq BETWEEN '%u' AND '%u')
174  OR
175  (AccountTransactions.TransID = Transactions.TransID AND
176  AccountTransactions.Account = '%s' AND
177  AccountTransactions.LedgerSeq = '%u' AND
178  AccountTransactions.TxnSeq <= '%u')
179  ORDER BY AccountTransactions.LedgerSeq DESC,
180  AccountTransactions.TxnSeq DESC
181  LIMIT %u;
182  )")) %
183  b58acct % minLedger % (findLedger - 1) % b58acct % findLedger %
184  findSeq % queryLimit);
185  }
186  else
187  {
188  assert(false);
189  // sql is empty
190  return;
191  }
192 
193  {
194  auto db(connection.checkoutDb());
195 
196  Blob rawData;
197  Blob rawMeta;
198 
199  boost::optional<std::uint64_t> ledgerSeq;
200  boost::optional<std::uint32_t> txnSeq;
201  boost::optional<std::string> status;
202  soci::blob txnData(*db);
203  soci::blob txnMeta(*db);
204  soci::indicator dataPresent, metaPresent;
205 
206  soci::statement st =
207  (db->prepare << sql,
208  soci::into(ledgerSeq),
209  soci::into(txnSeq),
210  soci::into(status),
211  soci::into(txnData, dataPresent),
212  soci::into(txnMeta, metaPresent));
213 
214  st.execute();
215 
216  while (st.fetch())
217  {
218  if (lookingForMarker)
219  {
220  if (findLedger == ledgerSeq.value_or(0) &&
221  findSeq == txnSeq.value_or(0))
222  {
223  lookingForMarker = false;
224  }
225  }
226  else if (numberOfResults == 0)
227  {
228  marker = {
229  rangeCheckedCast<std::uint32_t>(ledgerSeq.value_or(0)),
230  txnSeq.value_or(0)};
231  break;
232  }
233 
234  if (!lookingForMarker)
235  {
236  if (dataPresent == soci::i_ok)
237  convert(txnData, rawData);
238  else
239  rawData.clear();
240 
241  if (metaPresent == soci::i_ok)
242  convert(txnMeta, rawMeta);
243  else
244  rawMeta.clear();
245 
246  // Work around a bug that could leave the metadata missing
247  if (rawMeta.size() == 0)
248  onUnsavedLedger(ledgerSeq.value_or(0));
249 
250  onTransaction(
251  rangeCheckedCast<std::uint32_t>(ledgerSeq.value_or(0)),
252  *status,
253  rawData,
254  rawMeta);
255  --numberOfResults;
256  }
257  }
258  }
259 
260  return;
261 }
262 }
ripple::Transaction::sqlTransactionStatus
static TransStatus sqlTransactionStatus(boost::optional< std::string > const &status)
Definition: Transaction.cpp:65
ripple::Application
Definition: Application.h:85
std::optional::has_value
T has_value(T... args)
ripple::makeSlice
std::enable_if_t< std::is_same< T, char >::value||std::is_same< T, unsigned char >::value, Slice > makeSlice(std::array< T, N > const &a)
Definition: Slice.h:199
std::string
STL class.
ripple::convert
void convert(soci::blob &from, std::vector< std::uint8_t > &to)
Definition: SociDB.cpp:150
std::vector
STL class.
std::vector::size
T size(T... args)
ripple::accountTxPage
void accountTxPage(DatabaseCon &connection, AccountIDCache const &idCache, std::function< void(std::uint32_t)> const &onUnsavedLedger, std::function< void(std::uint32_t, std::string const &, Blob const &, Blob const &)> const &onTransaction, AccountID const &account, std::int32_t minLedger, std::int32_t maxLedger, bool forward, std::optional< NetworkOPs::AccountTxMarker > &marker, int limit, bool bAdmin, std::uint32_t page_length)
Definition: AccountTxPaging.cpp:64
std::optional::value_or
T value_or(T... args)
std::function
ripple::convertBlobsToTxResult
void convertBlobsToTxResult(NetworkOPs::AccountTxs &to, std::uint32_t ledger_index, std::string const &status, Blob const &rawTxn, Blob const &rawMeta, Application &app)
Definition: AccountTxPaging.cpp:33
std::optional::reset
T reset(T... args)
ripple::AccountIDCache
Caches the base58 representations of AccountIDs.
Definition: AccountID.h:150
std::vector::clear
T clear(T... args)
ripple::base_uint
Definition: base_uint.h:65
ripple::DatabaseCon::checkoutDb
LockedSociSession checkoutDb()
Definition: DatabaseCon.h:123
ripple::Application::getLedgerMaster
virtual LedgerMaster & getLedgerMaster()=0
ripple::saveLedgerAsync
void saveLedgerAsync(Application &app, std::uint32_t seq)
Definition: AccountTxPaging.cpp:57
ripple::LedgerMaster::getLedgerBySeq
std::shared_ptr< Ledger const > getLedgerBySeq(std::uint32_t index)
Definition: LedgerMaster.cpp:1641
ripple::SerialIter
Definition: Serializer.h:311
std::uint32_t
memory
std::vector::emplace_back
T emplace_back(T... args)
ripple
Use hash_* containers for keys that do not need a cryptographically secure hashing algorithm.
Definition: RCLCensorshipDetector.h:29
ripple::DatabaseCon
Definition: DatabaseCon.h:77
std::optional
ripple::pendSaveValidated
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:979
ripple::AccountIDCache::toBase58
std::string toBase58(AccountID const &) const
Return ripple::toBase58 for the AccountID.
Definition: AccountID.cpp:199