rippled
Transaction.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/LedgerMaster.h>
21 #include <ripple/app/main/Application.h>
22 #include <ripple/app/misc/HashRouter.h>
23 #include <ripple/app/misc/Transaction.h>
24 #include <ripple/app/tx/apply.h>
25 #include <ripple/basics/Log.h>
26 #include <ripple/basics/safe_cast.h>
27 #include <ripple/core/DatabaseCon.h>
28 #include <ripple/core/Pg.h>
29 #include <ripple/json/json_reader.h>
30 #include <ripple/protocol/ErrorCodes.h>
31 #include <ripple/protocol/Feature.h>
32 #include <ripple/protocol/jss.h>
33 #include <boost/optional.hpp>
34 
35 namespace ripple {
36 
38  std::shared_ptr<STTx const> const& stx,
39  std::string& reason,
40  Application& app) noexcept
41  : mTransaction(stx), mApp(app), j_(app.journal("Ledger"))
42 {
43  try
44  {
45  mTransactionID = mTransaction->getTransactionID();
46  }
47  catch (std::exception& e)
48  {
49  reason = e.what();
50  return;
51  }
52 
53  mStatus = NEW;
54 }
55 
56 //
57 // Misc.
58 //
59 
60 void
62 {
63  mStatus = ts;
64  mInLedger = lseq;
65 }
66 
68 Transaction::sqlTransactionStatus(boost::optional<std::string> const& status)
69 {
70  char const c = (status) ? (*status)[0] : safe_cast<char>(txnSqlUnknown);
71 
72  switch (c)
73  {
74  case txnSqlNew:
75  return NEW;
76  case txnSqlConflict:
77  return CONFLICTED;
78  case txnSqlHeld:
79  return HELD;
80  case txnSqlValidated:
81  return COMMITTED;
82  case txnSqlIncluded:
83  return INCLUDED;
84  }
85 
86  assert(c == txnSqlUnknown);
87  return INVALID;
88 }
89 
92  boost::optional<std::uint64_t> const& ledgerSeq,
93  boost::optional<std::string> const& status,
94  Blob const& rawTxn,
95  Application& app)
96 {
97  std::uint32_t const inLedger =
98  rangeCheckedCast<std::uint32_t>(ledgerSeq.value_or(0));
99 
100  SerialIter it(makeSlice(rawTxn));
101  auto txn = std::make_shared<STTx const>(it);
102  std::string reason;
103  auto tr = std::make_shared<Transaction>(txn, reason, app);
104 
105  tr->setStatus(sqlTransactionStatus(status));
106  tr->setLedger(inLedger);
107  return tr;
108 }
109 
112  TxSearched>
114 {
115  return load(id, app, boost::none, ec);
116 }
117 
120  TxSearched>
122  uint256 const& id,
123  Application& app,
125  error_code_i& ec)
126 {
127  using op = boost::optional<ClosedInterval<uint32_t>>;
128 
129  return load(id, app, op{range}, ec);
130 }
131 
134 {
135 #ifdef RIPPLED_REPORTING
136  auto baseCmd = boost::format(R"(SELECT tx('%s');)");
137 
138  std::string txHash = "\\x" + strHex(id);
139  std::string sql = boost::str(baseCmd % txHash);
140 
141  auto res = PgQuery(app.getPgPool())(sql.data());
142 
143  if (!res)
144  {
145  JLOG(app.journal("Transaction").error())
146  << __func__
147  << " : Postgres response is null - tx ID = " << strHex(id);
148  assert(false);
149  return {};
150  }
151  else if (res.status() != PGRES_TUPLES_OK)
152  {
153  JLOG(app.journal("Transaction").error())
154  << __func__
155  << " : Postgres response should have been "
156  "PGRES_TUPLES_OK but instead was "
157  << res.status() << " - msg = " << res.msg()
158  << " - tx ID = " << strHex(id);
159  assert(false);
160  return {};
161  }
162 
163  JLOG(app.journal("Transaction").trace())
164  << __func__ << " Postgres result msg : " << res.msg();
165  if (res.isNull() || res.ntuples() == 0)
166  {
167  JLOG(app.journal("Transaction").debug())
168  << __func__
169  << " : No data returned from Postgres : tx ID = " << strHex(id);
170  // This shouldn't happen
171  assert(false);
172  return {};
173  }
174 
175  char const* resultStr = res.c_str();
176  JLOG(app.journal("Transaction").debug())
177  << "postgres result = " << resultStr;
178 
179  Json::Value v;
180  Json::Reader reader;
181  bool success = reader.parse(resultStr, resultStr + strlen(resultStr), v);
182  if (success)
183  {
184  if (v.isMember("nodestore_hash") && v.isMember("ledger_seq"))
185  {
186  uint256 nodestoreHash;
187  if (!nodestoreHash.parseHex(
188  v["nodestore_hash"].asString().substr(2)))
189  assert(false);
190  uint32_t ledgerSeq = v["ledger_seq"].asUInt();
191  if (nodestoreHash.isNonZero())
192  return {std::make_pair(nodestoreHash, ledgerSeq)};
193  }
194  if (v.isMember("min_seq") && v.isMember("max_seq"))
195  {
196  return {ClosedInterval<uint32_t>(
197  v["min_seq"].asUInt(), v["max_seq"].asUInt())};
198  }
199  }
200 #endif
201  // Shouldn' happen. Postgres should return the ledger range searched if
202  // the transaction was not found
203  assert(false);
204  Throw<std::runtime_error>(
205  "Transaction::Locate - Invalid Postgres response");
206 }
207 
210  TxSearched>
212  uint256 const& id,
213  Application& app,
214  boost::optional<ClosedInterval<uint32_t>> const& range,
215  error_code_i& ec)
216 {
217  std::string sql =
218  "SELECT LedgerSeq,Status,RawTxn,TxnMeta "
219  "FROM Transactions WHERE TransID='";
220 
221  sql.append(to_string(id));
222  sql.append("';");
223 
224  boost::optional<std::uint64_t> ledgerSeq;
225  boost::optional<std::string> status;
226  Blob rawTxn, rawMeta;
227  {
228  auto db = app.getTxnDB().checkoutDb();
229  soci::blob sociRawTxnBlob(*db), sociRawMetaBlob(*db);
230  soci::indicator txn, meta;
231 
232  *db << sql, soci::into(ledgerSeq), soci::into(status),
233  soci::into(sociRawTxnBlob, txn), soci::into(sociRawMetaBlob, meta);
234 
235  auto const got_data = db->got_data();
236 
237  if ((!got_data || txn != soci::i_ok || meta != soci::i_ok) && !range)
238  return TxSearched::unknown;
239 
240  if (!got_data)
241  {
242  uint64_t count = 0;
243  soci::indicator rti;
244 
245  *db << "SELECT COUNT(DISTINCT LedgerSeq) FROM Transactions WHERE "
246  "LedgerSeq BETWEEN "
247  << range->first() << " AND " << range->last() << ";",
248  soci::into(count, rti);
249 
250  if (!db->got_data() || rti != soci::i_ok)
251  return TxSearched::some;
252 
253  return count == (range->last() - range->first() + 1)
256  }
257 
258  convert(sociRawTxnBlob, rawTxn);
259  convert(sociRawMetaBlob, rawMeta);
260  }
261 
262  try
263  {
264  auto txn =
265  Transaction::transactionFromSQL(ledgerSeq, status, rawTxn, app);
266 
267  if (!ledgerSeq)
268  return std::pair{std::move(txn), nullptr};
269 
270  std::uint32_t inLedger =
271  rangeCheckedCast<std::uint32_t>(ledgerSeq.value());
272 
273  auto txMeta = std::make_shared<TxMeta>(id, inLedger, rawMeta);
274 
275  return std::pair{std::move(txn), std::move(txMeta)};
276  }
277  catch (std::exception& e)
278  {
279  JLOG(app.journal("Ledger").warn())
280  << "Unable to deserialize transaction from raw SQL value. Error: "
281  << e.what();
282 
284  }
285 
286  return TxSearched::unknown;
287 }
288 
289 // options 1 to include the date of the transaction
291 Transaction::getJson(JsonOptions options, bool binary) const
292 {
294 
295  if (mInLedger)
296  {
297  ret[jss::inLedger] = mInLedger; // Deprecated.
298  ret[jss::ledger_index] = mInLedger;
299 
300  if (options == JsonOptions::include_date)
301  {
303  if (ct)
304  ret[jss::date] = ct->time_since_epoch().count();
305  }
306  }
307 
308  return ret;
309 }
310 
311 } // namespace ripple
ripple::COMMITTED
@ COMMITTED
Definition: Transaction.h:49
ripple::Transaction::sqlTransactionStatus
static TransStatus sqlTransactionStatus(boost::optional< std::string > const &status)
Definition: Transaction.cpp:68
ripple::Application
Definition: Application.h:102
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::JsonOptions::include_date
@ include_date
ripple::rpcDB_DESERIALIZATION
@ rpcDB_DESERIALIZATION
Definition: ErrorCodes.h:134
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:240
ripple::TxSearched::unknown
@ unknown
std::string
STL class.
std::shared_ptr
STL class.
ripple::Transaction::getJson
Json::Value getJson(JsonOptions options, bool binary=false) const
Definition: Transaction.cpp:291
std::exception
STL class.
ripple::base_uint::isNonZero
bool isNonZero() const
Definition: base_uint.h:444
ripple::JsonOptions
JsonOptions
Definition: STBase.h:34
beast::Journal::trace
Stream trace() const
Severity stream access functions.
Definition: Journal.h:309
std::pair
ripple::TxSearched::all
@ all
ripple::convert
void convert(soci::blob &from, std::vector< std::uint8_t > &to)
Definition: SociDB.cpp:156
ripple::TxSearched
TxSearched
Definition: Transaction.h:56
std::vector< unsigned char >
ripple::NEW
@ NEW
Definition: Transaction.h:45
ripple::HELD
@ HELD
Definition: Transaction.h:50
beast::Journal::warn
Stream warn() const
Definition: Journal.h:327
ripple::to_string
std::string to_string(ListDisposition disposition)
Definition: ValidatorList.cpp:45
Json::Reader
Unserialize a JSON document into a Value.
Definition: json_reader.h:36
ripple::Transaction::mStatus
TransStatus mStatus
Definition: Transaction.h:385
ripple::error_code_i
error_code_i
Definition: ErrorCodes.h:40
ripple::TransStatus
TransStatus
Definition: Transaction.h:44
ripple::Transaction::mTransaction
std::shared_ptr< STTx const > mTransaction
Definition: Transaction.h:394
ripple::base_uint< 256 >
ripple::INCLUDED
@ INCLUDED
Definition: Transaction.h:47
ripple::txnSqlUnknown
@ txnSqlUnknown
Definition: STTx.h:39
ripple::DatabaseCon::checkoutDb
LockedSociSession checkoutDb()
Definition: DatabaseCon.h:178
ripple::Transaction::Transaction
Transaction(std::shared_ptr< STTx const > const &, std::string &, Application &) noexcept
Definition: Transaction.cpp:37
ripple::Transaction::Locator
Definition: Transaction.h:310
ripple::Application::getLedgerMaster
virtual LedgerMaster & getLedgerMaster()=0
ripple::JsonOptions::none
@ none
beast::Journal::error
Stream error() const
Definition: Journal.h:333
ripple::txnSqlValidated
@ txnSqlValidated
Definition: STTx.h:37
ripple::Transaction::locate
static Locator locate(uint256 const &id, Application &app)
Definition: Transaction.cpp:133
ripple::SerialIter
Definition: Serializer.h:308
Json::Value::isMember
bool isMember(const char *key) const
Return true if the object has a member named key.
Definition: json_value.cpp:932
std::uint32_t
ripple::txnSqlHeld
@ txnSqlHeld
Definition: STTx.h:36
ripple::CONFLICTED
@ CONFLICTED
Definition: Transaction.h:48
ripple::range
ClosedInterval< T > range(T low, T high)
Create a closed range interval.
Definition: RangeSet.h:53
ripple::Transaction::mInLedger
LedgerIndex mInLedger
Definition: Transaction.h:384
std::string::append
T append(T... args)
std::string::substr
T substr(T... args)
ripple
Use hash_* containers for keys that do not need a cryptographically secure hashing algorithm.
Definition: RCLCensorshipDetector.h:29
ripple::Transaction::load
static std::variant< std::pair< std::shared_ptr< Transaction >, std::shared_ptr< TxMeta > >, TxSearched > load(uint256 const &id, Application &app, error_code_i &ec)
Definition: Transaction.cpp:113
ripple::Application::journal
virtual beast::Journal journal(std::string const &name)=0
ripple::INVALID
@ INVALID
Definition: Transaction.h:46
ripple::base_uint::parseHex
bool parseHex(std::string_view sv)
Parse a hex string into a base_uint.
Definition: base_uint.h:384
ripple::LedgerMaster::getCloseTimeBySeq
boost::optional< NetClock::time_point > getCloseTimeBySeq(LedgerIndex ledgerIndex)
Definition: LedgerMaster.cpp:1676
ripple::Transaction::setStatus
void setStatus(TransStatus status, std::uint32_t ledgerSeq)
Definition: Transaction.cpp:61
ripple::txnSqlIncluded
@ txnSqlIncluded
Definition: STTx.h:38
Json::Value::asUInt
UInt asUInt() const
Definition: json_value.cpp:545
Json::Reader::parse
bool parse(std::string const &document, Value &root)
Read a Value from a JSON document.
Definition: json_reader.cpp:74
ripple::txnSqlConflict
@ txnSqlConflict
Definition: STTx.h:35
ripple::Transaction::mApp
Application & mApp
Definition: Transaction.h:395
ripple::txnSqlNew
@ txnSqlNew
Definition: STTx.h:34
beast::Journal::debug
Stream debug() const
Definition: Journal.h:315
std::make_pair
T make_pair(T... args)
ripple::strHex
std::string strHex(FwdIt begin, FwdIt end)
Definition: strHex.h:45
ripple::ClosedInterval
boost::icl::closed_interval< T > ClosedInterval
A closed interval over the domain T.
Definition: RangeSet.h:44
ripple::DataFormat::binary
@ binary
std::string::data
T data(T... args)
std::exception::what
T what(T... args)
ripple::TxSearched::some
@ some
Json::Value
Represents a JSON value.
Definition: json_value.h:145
ripple::Application::getTxnDB
virtual DatabaseCon & getTxnDB()=0
std::variant
Json::Value::asString
std::string asString() const
Returns the unquoted string value.
Definition: json_value.cpp:469