rippled
TxHistory.cpp
1 //------------------------------------------------------------------------------
2 /*
3  This file is part of rippled: https://github.com/ripple/rippled
4  Copyright (c) 2012-2014 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/LedgerMaster.h>
21 #include <ripple/app/main/Application.h>
22 #include <ripple/app/misc/Transaction.h>
23 #include <ripple/core/DatabaseCon.h>
24 #include <ripple/core/Pg.h>
25 #include <ripple/core/SociDB.h>
26 #include <ripple/net/RPCErr.h>
27 #include <ripple/protocol/ErrorCodes.h>
28 #include <ripple/protocol/jss.h>
29 #include <ripple/resource/Fees.h>
30 #include <ripple/rpc/Context.h>
31 #include <ripple/rpc/Role.h>
32 #include <ripple/rpc/Status.h>
33 #include <boost/format.hpp>
34 
35 namespace ripple {
36 
39 {
40  Json::Value ret;
41 #ifdef RIPPLED_REPORTING
42  if (!context.app.config().reporting())
43  {
44  assert(false);
45  Throw<std::runtime_error>(
46  "called doTxHistoryReporting but not in reporting mode");
47  }
49 
50  if (!context.params.isMember(jss::start))
52 
53  unsigned int startIndex = context.params[jss::start].asUInt();
54 
55  if ((startIndex > 10000) && (!isUnlimited(context.role)))
56  return rpcError(rpcNO_PERMISSION);
57 
58  std::string sql = boost::str(
59  boost::format("SELECT nodestore_hash, ledger_seq "
60  " FROM transactions"
61  " ORDER BY ledger_seq DESC LIMIT 20 "
62  "OFFSET %u;") %
63  startIndex);
64 
65  auto res = PgQuery(context.app.getPgPool())(sql.data());
66 
67  if (!res)
68  {
69  JLOG(context.j.error())
70  << __func__ << " : Postgres response is null - sql = " << sql;
71  assert(false);
72  return {};
73  }
74  else if (res.status() != PGRES_TUPLES_OK)
75  {
76  JLOG(context.j.error())
77  << __func__
78  << " : Postgres response should have been "
79  "PGRES_TUPLES_OK but instead was "
80  << res.status() << " - msg = " << res.msg() << " - sql = " << sql;
81  assert(false);
82  return {};
83  }
84 
85  JLOG(context.j.trace())
86  << __func__ << " Postgres result msg : " << res.msg();
87 
88  if (res.isNull() || res.ntuples() == 0)
89  {
90  JLOG(context.j.debug()) << __func__ << " : Empty postgres response";
91  assert(false);
92  return {};
93  }
94  else if (res.ntuples() > 0)
95  {
96  if (res.nfields() != 2)
97  {
98  JLOG(context.j.error()) << __func__
99  << " : Wrong number of fields in Postgres "
100  "response. Expected 1, but got "
101  << res.nfields() << " . sql = " << sql;
102  assert(false);
103  return {};
104  }
105  }
106 
107  JLOG(context.j.trace())
108  << __func__ << " : Postgres result = " << res.c_str();
109 
110  Json::Value txs;
111 
112  std::vector<uint256> nodestoreHashes;
113  std::vector<uint32_t> ledgerSequences;
114  for (size_t i = 0; i < res.ntuples(); ++i)
115  {
116  uint256 hash;
117  if (!hash.parseHex(res.c_str(i, 0) + 2))
118  assert(false);
119  nodestoreHashes.push_back(hash);
120  ledgerSequences.push_back(res.asBigInt(i, 1));
121  }
122 
123  auto txns = flatFetchTransactions(context.app, nodestoreHashes);
124  for (size_t i = 0; i < txns.size(); ++i)
125  {
126  auto const& [sttx, meta] = txns[i];
127  assert(sttx);
128 
129  std::string reason;
130  auto txn = std::make_shared<Transaction>(sttx, reason, context.app);
131  txn->setLedger(ledgerSequences[i]);
132  txn->setStatus(COMMITTED);
133  txs.append(txn->getJson(JsonOptions::none));
134  }
135 
136  ret[jss::index] = startIndex;
137  ret[jss::txs] = txs;
138  ret["used_postgres"] = true;
139 
140 #endif
141  return ret;
142 }
143 
144 // {
145 // start: <index>
146 // }
149 {
150  if (!context.app.config().useTxTables())
151  return rpcError(rpcNOT_ENABLED);
152 
153  if (context.app.config().reporting())
154  return doTxHistoryReporting(context);
156 
157  if (!context.params.isMember(jss::start))
158  return rpcError(rpcINVALID_PARAMS);
159 
160  unsigned int startIndex = context.params[jss::start].asUInt();
161 
162  if ((startIndex > 10000) && (!isUnlimited(context.role)))
163  return rpcError(rpcNO_PERMISSION);
164 
165  Json::Value obj;
166  Json::Value txs;
167 
168  obj[jss::index] = startIndex;
169 
170  std::string sql = boost::str(
171  boost::format(
172  "SELECT LedgerSeq, Status, RawTxn "
173  "FROM Transactions ORDER BY LedgerSeq desc LIMIT %u,20;") %
174  startIndex);
175 
176  {
177  auto db = context.app.getTxnDB().checkoutDb();
178 
179  boost::optional<std::uint64_t> ledgerSeq;
180  boost::optional<std::string> status;
181  soci::blob sociRawTxnBlob(*db);
182  soci::indicator rti;
183  Blob rawTxn;
184 
185  soci::statement st =
186  (db->prepare << sql,
187  soci::into(ledgerSeq),
188  soci::into(status),
189  soci::into(sociRawTxnBlob, rti));
190 
191  st.execute();
192  while (st.fetch())
193  {
194  if (soci::i_ok == rti)
195  convert(sociRawTxnBlob, rawTxn);
196  else
197  rawTxn.clear();
198 
199  if (auto trans = Transaction::transactionFromSQL(
200  ledgerSeq, status, rawTxn, context.app))
201  txs.append(trans->getJson(JsonOptions::none));
202  }
203  }
204 
205  obj[jss::txs] = txs;
206 
207  return obj;
208 }
209 
210 } // namespace ripple
ripple::COMMITTED
@ COMMITTED
Definition: Transaction.h:49
ripple::Transaction::transactionFromSQL
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:91
ripple::RPC::JsonContext
Definition: Context.h:53
std::string
STL class.
ripple::rpcINVALID_PARAMS
@ rpcINVALID_PARAMS
Definition: ErrorCodes.h:84
beast::Journal::trace
Stream trace() const
Severity stream access functions.
Definition: Journal.h:309
ripple::Resource::feeMediumBurdenRPC
const Charge feeMediumBurdenRPC
ripple::convert
void convert(soci::blob &from, std::vector< std::uint8_t > &to)
Definition: SociDB.cpp:156
ripple::RPC::Context::loadType
Resource::Charge & loadType
Definition: Context.h:43
std::vector
STL class.
ripple::RPC::Context::role
Role role
Definition: Context.h:47
std::vector::clear
T clear(T... args)
ripple::RPC::Context::j
const beast::Journal j
Definition: Context.h:41
std::vector::push_back
T push_back(T... args)
ripple::base_uint
Integers of any length that is a multiple of 32-bits.
Definition: base_uint.h:73
ripple::doTxHistory
Json::Value doTxHistory(RPC::JsonContext &)
Definition: TxHistory.cpp:148
Json::Value::append
Value & append(const Value &value)
Append value to array at the end.
Definition: json_value.cpp:882
ripple::Config::reporting
bool reporting() const
Definition: Config.h:267
ripple::DatabaseCon::checkoutDb
LockedSociSession checkoutDb()
Definition: DatabaseCon.h:178
ripple::JsonOptions::none
@ none
ripple::Application::config
virtual Config & config()=0
ripple::Config::useTxTables
bool useTxTables() const
Definition: Config.h:273
ripple::RPC::Context::app
Application & app
Definition: Context.h:42
beast::Journal::error
Stream error() const
Definition: Journal.h:333
ripple::rpcNOT_ENABLED
@ rpcNOT_ENABLED
Definition: ErrorCodes.h:59
Json::Value::isMember
bool isMember(const char *key) const
Return true if the object has a member named key.
Definition: json_value.cpp:932
ripple::rpcError
Json::Value rpcError(int iError, Json::Value jvResult)
Definition: RPCErr.cpp:29
ripple::isUnlimited
bool isUnlimited(Role const &role)
ADMIN and IDENTIFIED roles shall have unlimited resources.
Definition: Role.cpp:94
ripple
Use hash_* containers for keys that do not need a cryptographically secure hashing algorithm.
Definition: RCLCensorshipDetector.h:29
ripple::base_uint::parseHex
bool parseHex(std::string_view sv)
Parse a hex string into a base_uint.
Definition: base_uint.h:384
ripple::rpcNO_PERMISSION
@ rpcNO_PERMISSION
Definition: ErrorCodes.h:53
Json::Value::asUInt
UInt asUInt() const
Definition: json_value.cpp:545
beast::Journal::debug
Stream debug() const
Definition: Journal.h:315
ripple::RPC::JsonContext::params
Json::Value params
Definition: Context.h:64
std::string::data
T data(T... args)
ripple::doTxHistoryReporting
Json::Value doTxHistoryReporting(RPC::JsonContext &context)
Definition: TxHistory.cpp:38
Json::Value
Represents a JSON value.
Definition: json_value.h:145
ripple::Application::getTxnDB
virtual DatabaseCon & getTxnDB()=0
ripple::flatFetchTransactions
std::vector< std::pair< std::shared_ptr< STTx const >, std::shared_ptr< STObject const > > > flatFetchTransactions(Application &app, std::vector< uint256 > &nodestoreHashes)
Definition: Ledger.cpp:1704