rippled
Tx.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/ledger/TransactionMaster.h>
22 #include <ripple/app/misc/NetworkOPs.h>
23 #include <ripple/app/misc/Transaction.h>
24 #include <ripple/basics/ToString.h>
25 #include <ripple/net/RPCErr.h>
26 #include <ripple/protocol/ErrorCodes.h>
27 #include <ripple/protocol/jss.h>
28 #include <ripple/rpc/Context.h>
29 #include <ripple/rpc/DeliveredAmount.h>
30 #include <ripple/rpc/GRPCHandlers.h>
31 #include <ripple/rpc/NFTSyntheticSerializer.h>
32 #include <ripple/rpc/impl/RPCHelpers.h>
33 
34 namespace ripple {
35 
36 // {
37 // transaction: <hex>
38 // }
39 
40 static bool
41 isValidated(LedgerMaster& ledgerMaster, std::uint32_t seq, uint256 const& hash)
42 {
43  if (!ledgerMaster.haveLedger(seq))
44  return false;
45 
46  if (seq > ledgerMaster.getValidatedLedger()->info().seq)
47  return false;
48 
49  return ledgerMaster.getHashBySeq(seq) == hash;
50 }
51 
52 struct TxResult
53 {
56  bool validated = false;
58 };
59 
60 struct TxArgs
61 {
63  bool binary = false;
65 };
66 
68 doTxPostgres(RPC::Context& context, TxArgs const& args)
69 {
70  if (!context.app.config().reporting())
71  {
72  assert(false);
73  Throw<std::runtime_error>(
74  "Called doTxPostgres yet not in reporting mode");
75  }
76  TxResult res;
78 
79  JLOG(context.j.debug()) << "Fetching from postgres";
80  Transaction::Locator locator = Transaction::locate(args.hash, context.app);
81 
83  pair;
84  // database returned the nodestore hash. Fetch the txn directly from the
85  // nodestore. Don't traverse the transaction SHAMap
86  if (locator.isFound())
87  {
88  auto start = std::chrono::system_clock::now();
89  // The second argument of fetch is ignored when not using shards
90  if (auto obj = context.app.getNodeFamily().db().fetchNodeObject(
91  locator.getNodestoreHash(), locator.getLedgerSequence()))
92  {
94  makeSlice(obj->getData()),
95  SHAMapHash{locator.getNodestoreHash()});
96  if (!node)
97  {
98  assert(false);
99  return {res, {rpcINTERNAL, "Error making SHAMap node"}};
100  }
101  auto item = (static_cast<SHAMapLeafNode*>(node.get()))->peekItem();
102  if (!item)
103  {
104  assert(false);
105  return {res, {rpcINTERNAL, "Error reading SHAMap node"}};
106  }
107 
108  auto [sttx, meta] = deserializeTxPlusMeta(*item);
109  JLOG(context.j.debug()) << "Successfully fetched from db";
110 
111  if (!sttx || !meta)
112  {
113  assert(false);
114  return {res, {rpcINTERNAL, "Error deserializing SHAMap node"}};
115  }
116  std::string reason;
117  res.txn = std::make_shared<Transaction>(sttx, reason, context.app);
118  res.txn->setLedger(locator.getLedgerSequence());
119  res.txn->setStatus(COMMITTED);
120  if (args.binary)
121  {
122  SerialIter it(item->slice());
123  it.skip(it.getVLDataLength()); // skip transaction
124  Blob blob = it.getVL();
125  res.meta = std::move(blob);
126  }
127  else
128  {
129  res.meta = std::make_shared<TxMeta>(
130  args.hash, res.txn->getLedger(), *meta);
131  }
132  res.validated = true;
133  return {res, rpcSUCCESS};
134  }
135  else
136  {
137  JLOG(context.j.error()) << "Failed to fetch from db";
138  assert(false);
139  return {res, {rpcINTERNAL, "Containing SHAMap node not found"}};
140  }
141  auto end = std::chrono::system_clock::now();
142  JLOG(context.j.debug()) << "tx flat fetch time : "
143  << ((end - start).count() / 1000000000.0);
144  }
145  // database did not find the transaction, and returned the ledger range
146  // that was searched
147  else
148  {
149  if (args.ledgerRange)
150  {
151  auto range = locator.getLedgerRangeSearched();
152  auto min = args.ledgerRange->first;
153  auto max = args.ledgerRange->second;
154  if (min >= range.lower() && max <= range.upper())
155  {
157  }
158  else
159  {
161  }
162  }
163  return {res, rpcTXN_NOT_FOUND};
164  }
165  // database didn't return anything. This shouldn't happen
166  assert(false);
167  return {res, {rpcINTERNAL, "unexpected Postgres response"}};
168 }
169 
171 doTxHelp(RPC::Context& context, TxArgs const& args)
172 {
173  if (context.app.config().reporting())
174  return doTxPostgres(context, args);
175  TxResult result;
176 
178 
179  if (args.ledgerRange)
180  {
181  constexpr uint16_t MAX_RANGE = 1000;
182 
183  if (args.ledgerRange->second < args.ledgerRange->first)
184  return {result, rpcINVALID_LGR_RANGE};
185 
186  if (args.ledgerRange->second - args.ledgerRange->first > MAX_RANGE)
187  return {result, rpcEXCESSIVE_LGR_RANGE};
188 
190  args.ledgerRange->first, args.ledgerRange->second);
191  }
192 
193  auto ec{rpcSUCCESS};
194 
195  using TxPair =
197 
199 
201  if (args.ledgerRange)
202  {
203  v = context.app.getMasterTransaction().fetch(args.hash, range, ec);
204  }
205  else
206  {
207  v = context.app.getMasterTransaction().fetch(args.hash, ec);
208  }
209 
210  if (auto e = std::get_if<TxSearched>(&v))
211  {
212  result.searchedAll = *e;
213  return {result, rpcTXN_NOT_FOUND};
214  }
215 
216  auto [txn, meta] = std::get<TxPair>(v);
217 
218  if (ec == rpcDB_DESERIALIZATION)
219  {
220  return {result, ec};
221  }
222  if (!txn)
223  {
224  return {result, rpcTXN_NOT_FOUND};
225  }
226 
227  // populate transaction data
228  result.txn = txn;
229  if (txn->getLedger() == 0)
230  {
231  return {result, rpcSUCCESS};
232  }
233 
235  context.ledgerMaster.getLedgerBySeq(txn->getLedger());
236 
237  if (ledger && meta)
238  {
239  if (args.binary)
240  {
241  result.meta = meta->getAsObject().getSerializer().getData();
242  }
243  else
244  {
245  result.meta = meta;
246  }
247  result.validated = isValidated(
248  context.ledgerMaster, ledger->info().seq, ledger->info().hash);
249  }
250 
251  return {result, rpcSUCCESS};
252 }
253 
257  TxArgs const& args,
258  RPC::JsonContext const& context)
259 {
260  Json::Value response;
261  RPC::Status const& error = res.second;
262  TxResult const& result = res.first;
263  // handle errors
264  if (error.toErrorCode() != rpcSUCCESS)
265  {
266  if (error.toErrorCode() == rpcTXN_NOT_FOUND &&
268  {
269  response = Json::Value(Json::objectValue);
270  response[jss::searched_all] =
271  (result.searchedAll == TxSearched::all);
272  error.inject(response);
273  }
274  else
275  {
276  error.inject(response);
277  }
278  }
279  // no errors
280  else if (result.txn)
281  {
282  response = result.txn->getJson(JsonOptions::include_date, args.binary);
283 
284  // populate binary metadata
285  if (auto blob = std::get_if<Blob>(&result.meta))
286  {
287  assert(args.binary);
288  response[jss::meta] = strHex(makeSlice(*blob));
289  }
290  // populate meta data
291  else if (auto m = std::get_if<std::shared_ptr<TxMeta>>(&result.meta))
292  {
293  auto& meta = *m;
294  if (meta)
295  {
296  response[jss::meta] = meta->getJson(JsonOptions::none);
297  insertDeliveredAmount(
298  response[jss::meta], context, result.txn, *meta);
299  insertNFTSyntheticInJson(
300  response, context, result.txn->getSTransaction(), *meta);
301  }
302  }
303  response[jss::validated] = result.validated;
304  }
305  return response;
306 }
307 
310 {
311  if (!context.app.config().useTxTables())
312  return rpcError(rpcNOT_ENABLED);
313 
314  // Deserialize and validate JSON arguments
315 
316  if (!context.params.isMember(jss::transaction))
317  return rpcError(rpcINVALID_PARAMS);
318 
319  TxArgs args;
320 
321  if (!args.hash.parseHex(context.params[jss::transaction].asString()))
322  return rpcError(rpcNOT_IMPL);
323 
324  args.binary = context.params.isMember(jss::binary) &&
325  context.params[jss::binary].asBool();
326 
327  if (context.params.isMember(jss::min_ledger) &&
328  context.params.isMember(jss::max_ledger))
329  {
330  try
331  {
333  context.params[jss::min_ledger].asUInt(),
334  context.params[jss::max_ledger].asUInt());
335  }
336  catch (...)
337  {
338  // One of the calls to `asUInt ()` failed.
340  }
341  }
342 
343  std::pair<TxResult, RPC::Status> res = doTxHelp(context, args);
344  return populateJsonResponse(res, args, context);
345 }
346 
347 } // namespace ripple
ripple::COMMITTED
@ COMMITTED
Definition: Transaction.h:50
ripple::TxArgs::binary
bool binary
Definition: Tx.cpp:63
ripple::JsonOptions::include_date
@ include_date
ripple::Application::getNodeFamily
virtual Family & getNodeFamily()=0
ripple::rpcDB_DESERIALIZATION
@ rpcDB_DESERIALIZATION
Definition: ErrorCodes.h:134
ripple::RPC::JsonContext
Definition: Context.h:53
ripple::HashPrefix::ledgerMaster
@ ledgerMaster
ledger master data for signing
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:241
ripple::TxSearched::unknown
@ unknown
std::string
STL class.
std::shared_ptr< Transaction >
ripple::Transaction::getJson
Json::Value getJson(JsonOptions options, bool binary=false) const
Definition: Transaction.cpp:168
ripple::rpcINVALID_PARAMS
@ rpcINVALID_PARAMS
Definition: ErrorCodes.h:84
ripple::Transaction::getSTransaction
std::shared_ptr< STTx const > const & getSTransaction()
Definition: Transaction.h:88
ripple::rpcError
Json::Value rpcError(int iError)
Definition: RPCErr.cpp:29
std::pair
ripple::LedgerInfo::hash
uint256 hash
Definition: ReadView.h:91
ripple::TxSearched::all
@ all
ripple::Transaction::Locator::getNodestoreHash
uint256 const & getNodestoreHash()
Definition: Transaction.h:336
ripple::LedgerMaster
Definition: LedgerMaster.h:70
ripple::TxSearched
TxSearched
Definition: Transaction.h:57
std::vector< unsigned char >
ripple::Transaction::setLedger
void setLedger(LedgerIndex ledger)
Definition: Transaction.h:139
ripple::RPC::Context::ledgerMaster
LedgerMaster & ledgerMaster
Definition: Context.h:45
ripple::rpcEXCESSIVE_LGR_RANGE
@ rpcEXCESSIVE_LGR_RANGE
Definition: ErrorCodes.h:135
ripple::LedgerInfo::seq
LedgerIndex seq
Definition: ReadView.h:83
ripple::TxResult
Definition: Tx.cpp:52
ripple::Family::db
virtual NodeStore::Database & db()=0
ripple::SHAMapLeafNode
Definition: SHAMapLeafNode.h:32
ripple::SHAMapHash
Definition: SHAMapHash.h:32
Json::Value::asBool
bool asBool() const
Definition: json_value.cpp:619
ripple::RPC::Context::j
const beast::Journal j
Definition: Context.h:41
ripple::base_uint< 256 >
ripple::Ledger::info
LedgerInfo const & info() const override
Returns information about the ledger.
Definition: Ledger.h:152
ripple::rpcSUCCESS
@ rpcSUCCESS
Definition: ErrorCodes.h:44
ripple::Config::reporting
bool reporting() const
Definition: Config.h:337
Json::objectValue
@ objectValue
object value (collection of name/value pairs).
Definition: json_value.h:43
ripple::Transaction::Locator
Definition: Transaction.h:315
ripple::deserializeTxPlusMeta
std::pair< std::shared_ptr< STTx const >, std::shared_ptr< STObject const > > deserializeTxPlusMeta(SHAMapItem const &item)
Deserialize a SHAMapItem containing STTx + STObject metadata.
Definition: Ledger.cpp:410
ripple::Transaction::getLedger
LedgerIndex getLedger() const
Definition: Transaction.h:100
ripple::TxArgs::ledgerRange
std::optional< std::pair< uint32_t, uint32_t > > ledgerRange
Definition: Tx.cpp:64
ripple::JsonOptions::none
@ none
ripple::Application::config
virtual Config & config()=0
ripple::Config::useTxTables
bool useTxTables() const
Definition: Config.h:343
ripple::rpcTXN_NOT_FOUND
@ rpcTXN_NOT_FOUND
Definition: ErrorCodes.h:80
ripple::RPC::Context::app
Application & app
Definition: Context.h:42
beast::Journal::error
Stream error() const
Definition: Journal.h:333
ripple::Transaction::locate
static Locator locate(uint256 const &id, Application &app)
Definition: Transaction.cpp:134
ripple::LedgerMaster::getLedgerBySeq
std::shared_ptr< Ledger const > getLedgerBySeq(std::uint32_t index)
Definition: LedgerMaster.cpp:1818
ripple::rpcNOT_ENABLED
@ rpcNOT_ENABLED
Definition: ErrorCodes.h:59
ripple::SerialIter
Definition: Serializer.h:310
ripple::SerialIter::getVL
Blob getVL()
Definition: Serializer.cpp:508
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::SerialIter::skip
void skip(int num)
Definition: Serializer.cpp:352
ripple::range
ClosedInterval< T > range(T low, T high)
Create a closed range interval.
Definition: RangeSet.h:53
ripple::TxResult::validated
bool validated
Definition: Tx.cpp:56
ripple::RPC::Status
Status represents the results of an operation that might fail.
Definition: Status.h:39
ripple::rpcINTERNAL
@ rpcINTERNAL
Definition: ErrorCodes.h:130
ripple::SHAMapTreeNode::makeFromPrefix
static std::shared_ptr< SHAMapTreeNode > makeFromPrefix(Slice rawNode, SHAMapHash const &hash)
Definition: SHAMapTreeNode.cpp:148
ripple::TxArgs
Definition: Tx.cpp:60
ripple::rpcNOT_IMPL
@ rpcNOT_IMPL
Definition: ErrorCodes.h:131
ripple
Use hash_* containers for keys that do not need a cryptographically secure hashing algorithm.
Definition: RCLCensorshipDetector.h:29
ripple::populateJsonResponse
Json::Value populateJsonResponse(std::pair< AccountTxResult, RPC::Status > const &res, AccountTxArgs const &args, RPC::JsonContext const &context)
Definition: AccountTx.cpp:272
ripple::doTxPostgres
std::pair< TxResult, RPC::Status > doTxPostgres(RPC::Context &context, TxArgs const &args)
Definition: Tx.cpp:68
ripple::doTxHelp
std::pair< TxResult, RPC::Status > doTxHelp(RPC::Context &context, TxArgs const &args)
Definition: Tx.cpp:171
ripple::Transaction::setStatus
void setStatus(TransStatus status, std::uint32_t ledgerSeq)
Definition: Transaction.cpp:62
Json::Value::asUInt
UInt asUInt() const
Definition: json_value.cpp:545
ripple::SerialIter::getVLDataLength
int getVLDataLength()
Definition: Serializer.cpp:470
ripple::doTxJson
Json::Value doTxJson(RPC::JsonContext &)
Definition: Tx.cpp:309
std::get_if
T get_if(T... args)
ripple::TxResult::meta
std::variant< std::shared_ptr< TxMeta >, Blob > meta
Definition: Tx.cpp:55
std::optional
beast::Journal::debug
Stream debug() const
Definition: Journal.h:315
ripple::TransactionMaster::fetch
std::variant< std::pair< std::shared_ptr< Transaction >, std::shared_ptr< TxMeta > >, TxSearched > fetch(uint256 const &, error_code_i &ec)
Definition: TransactionMaster.cpp:60
std::make_pair
T make_pair(T... args)
ripple::strHex
std::string strHex(FwdIt begin, FwdIt end)
Definition: strHex.h:30
ripple::NodeStore::Database::fetchNodeObject
std::shared_ptr< NodeObject > fetchNodeObject(uint256 const &hash, std::uint32_t ledgerSeq=0, FetchType fetchType=FetchType::synchronous, bool duplicate=false)
Fetch a node object.
Definition: Database.cpp:252
ripple::rpcINVALID_LGR_RANGE
@ rpcINVALID_LGR_RANGE
Definition: ErrorCodes.h:136
ripple::ClosedInterval
boost::icl::closed_interval< T > ClosedInterval
A closed interval over the domain T.
Definition: RangeSet.h:44
ripple::base_uint::parseHex
constexpr bool parseHex(std::string_view sv)
Parse a hex string into a base_uint.
Definition: base_uint.h:496
ripple::isValidated
static bool isValidated(LedgerMaster &ledgerMaster, std::uint32_t seq, uint256 const &hash)
Definition: Tx.cpp:41
ripple::Transaction::Locator::getLedgerSequence
uint32_t getLedgerSequence()
Definition: Transaction.h:345
ripple::TxArgs::hash
uint256 hash
Definition: Tx.cpp:62
ripple::RPC::JsonContext::params
Json::Value params
Definition: Context.h:64
ripple::RPC::Context
The context of information needed to call an RPC.
Definition: Context.h:39
ripple::TxResult::searchedAll
TxSearched searchedAll
Definition: Tx.cpp:57
ripple::Transaction::Locator::isFound
bool isFound()
Definition: Transaction.h:326
ripple::TxSearched::some
@ some
Json::Value
Represents a JSON value.
Definition: json_value.h:145
ripple::TxResult::txn
Transaction::pointer txn
Definition: Tx.cpp:54
std::variant
Json::Value::asString
std::string asString() const
Returns the unquoted string value.
Definition: json_value.cpp:469
ripple::Application::getMasterTransaction
virtual TransactionMaster & getMasterTransaction()=0
ripple::Transaction::Locator::getLedgerRangeSearched
ClosedInterval< uint32_t > const & getLedgerRangeSearched()
Definition: Transaction.h:354
std::chrono::system_clock::now
T now(T... args)