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/net/RPCErr.h>
25 #include <ripple/protocol/ErrorCodes.h>
26 #include <ripple/protocol/jss.h>
27 #include <ripple/rpc/Context.h>
28 #include <ripple/rpc/DeliveredAmount.h>
29 #include <ripple/rpc/impl/RPCHelpers.h>
30 #include <ripple/rpc/impl/GRPCHelpers.h>
31 #include <ripple/rpc/GRPCHandlers.h>
32 #include <ripple/basics/ToString.h>
33 
34 namespace ripple {
35 
36 // {
37 // transaction: <hex>
38 // }
39 
40 static
41 bool
42 isHexTxID (std::string const& txid)
43 {
44  if (txid.size () != 64)
45  return false;
46 
47  auto const ret = std::find_if (txid.begin (), txid.end (),
48  [](std::string::value_type c)
49  {
50  return !std::isxdigit (static_cast<unsigned char>(c));
51  });
52 
53  return (ret == txid.end ());
54 }
55 
56 static
57 bool
58 isValidated(LedgerMaster& ledgerMaster, std::uint32_t seq, uint256 const& hash)
59 {
60  if (!ledgerMaster.haveLedger (seq))
61  return false;
62 
63  if (seq > ledgerMaster.getValidatedLedger ()->info().seq)
64  return false;
65 
66  return ledgerMaster.getHashBySeq (seq) == hash;
67 }
68 
69 bool
70 getMetaHex (Ledger const& ledger,
71  uint256 const& transID, std::string& hex)
72 {
74  auto const item =
75  ledger.txMap().peekItem (transID, type);
76 
77  if (!item)
78  return false;
79 
81  return false;
82 
83  SerialIter it (item->slice());
84  it.getVL (); // skip transaction
85  hex = strHex (makeSlice(it.getVL ()));
86  return true;
87 }
88 
89 enum class SearchedAll { no, yes, unknown };
90 
91 struct TxResult
92 {
95  bool validated = false;
97 };
98 
99 struct TxArgs
100 {
102  bool binary = false;
104 };
105 
107 doTxHelp(RPC::Context& context, TxArgs const& args)
108 {
109  TxResult result;
110 
112 
113  if (args.ledgerRange)
114  {
115  constexpr uint16_t MAX_RANGE = 1000;
116 
117  if (args.ledgerRange->second < args.ledgerRange->first)
118  return {result, rpcINVALID_LGR_RANGE};
119 
120  if (args.ledgerRange->second - args.ledgerRange->first > MAX_RANGE)
121  return {result, rpcEXCESSIVE_LGR_RANGE};
122 
124  args.ledgerRange->first, args.ledgerRange->second);
125  }
126 
128  auto ec{rpcSUCCESS};
129 
131  if (args.ledgerRange)
132  {
133  boost::variant<std::shared_ptr<Transaction>, bool> v =
134  context.app.getMasterTransaction().fetch(args.hash, range, ec);
135 
136  if (v.which() == 1)
137  {
138  result.searchedAll =
139  boost::get<bool>(v) ? SearchedAll::yes : SearchedAll::no;
140  return {result, rpcTXN_NOT_FOUND};
141  }
142  else
143  {
144  txn = boost::get<std::shared_ptr<Transaction>>(v);
145  }
146  }
147  else
148  {
149  txn = context.app.getMasterTransaction().fetch(args.hash, ec);
150  }
151 
152  if (ec == rpcDB_DESERIALIZATION)
153  {
154  return {result, ec};
155  }
156  if (!txn)
157  {
158  return {result, rpcTXN_NOT_FOUND};
159  }
160 
161  // populate transaction data
162  result.txn = txn;
163  if (txn->getLedger() == 0)
164  {
165  return {result, rpcSUCCESS};
166  }
167 
169  context.ledgerMaster.getLedgerBySeq(txn->getLedger());
170  // get meta data
171  if (ledger)
172  {
173  bool ok = false;
174  if (args.binary)
175  {
177  auto const item = ledger->txMap().peekItem(txn->getID(), type);
178 
179  if (item && type == SHAMapTreeNode::tnTRANSACTION_MD)
180  {
181  ok = true;
182  SerialIter it(item->slice());
183  it.skip(it.getVLDataLength()); // skip transaction
184  Blob blob = it.getVL();
185  result.meta = std::move(blob);
186  }
187  }
188  else
189  {
190  auto rawMeta = ledger->txRead(txn->getID()).second;
191  if (rawMeta)
192  {
193  ok = true;
194  result.meta = std::make_shared<TxMeta>(
195  txn->getID(), ledger->seq(), *rawMeta);
196  }
197  }
198  if (ok)
199  {
200  result.validated = isValidated(
201  context.ledgerMaster, ledger->info().seq, ledger->info().hash);
202  }
203  }
204 
205  return {result, rpcSUCCESS};
206 }
207 
211  TxArgs const& args,
213 {
214  org::xrpl::rpc::v1::GetTransactionResponse response;
215  grpc::Status status = grpc::Status::OK;
216  RPC::Status const& error = res.second;
217  TxResult const& result = res.first;
218  // handle errors
219  if (error.toErrorCode() != rpcSUCCESS)
220  {
221  if (error.toErrorCode() == rpcTXN_NOT_FOUND &&
223  {
224  status = {
225  grpc::StatusCode::NOT_FOUND,
226  "txn not found. searched_all = " +
227  to_string(
228  (result.searchedAll == SearchedAll::yes ? "true"
229  : "false"))};
230  }
231  else
232  {
233  if (error.toErrorCode() == rpcTXN_NOT_FOUND)
234  status = {grpc::StatusCode::NOT_FOUND, "txn not found"};
235  else
236  status = {grpc::StatusCode::INTERNAL, error.message()};
237  }
238  }
239  // no errors
240  else if (result.txn)
241  {
242  auto& txn = result.txn;
243 
244  std::shared_ptr<STTx const> stTxn = txn->getSTransaction();
245  if (args.binary)
246  {
247  Serializer s = stTxn->getSerializer();
248  response.set_transaction_binary(s.data(), s.size());
249  }
250  else
251  {
252  RPC::convert(*response.mutable_transaction(), stTxn);
253  }
254 
255  response.set_hash(context.params.hash());
256 
257  auto ledgerIndex = txn->getLedger();
258  response.set_ledger_index(ledgerIndex);
259  if (ledgerIndex)
260  {
261  auto ct =
262  context.app.getLedgerMaster().getCloseTimeBySeq(ledgerIndex);
263  if (ct)
264  response.mutable_date()->set_value(
265  ct->time_since_epoch().count());
266  }
267 
268  RPC::convert(
269  *response.mutable_meta()->mutable_transaction_result(),
270  txn->getResult());
271  response.mutable_meta()->mutable_transaction_result()->set_result(
272  transToken(txn->getResult()));
273 
274  // populate binary metadata
275  if (auto blob = std::get_if<Blob>(&result.meta))
276  {
277  assert(args.binary);
278  Slice slice = makeSlice(*blob);
279  response.set_meta_binary(slice.data(), slice.size());
280  }
281  // populate meta data
282  else if (auto m = std::get_if<std::shared_ptr<TxMeta>>(&result.meta))
283  {
284  auto& meta = *m;
285  if (meta)
286  {
287  RPC::convert(*response.mutable_meta(), meta);
288  auto amt =
289  getDeliveredAmount(context, stTxn, *meta, txn->getLedger());
290  if (amt)
291  {
292  RPC::convert(
293  *response.mutable_meta()->mutable_delivered_amount(),
294  *amt);
295  }
296  }
297  }
298  response.set_validated(result.validated);
299  }
300  return {response, status};
301 }
302 
306  TxArgs const& args,
307  RPC::JsonContext const& context)
308 {
309  Json::Value response;
310  RPC::Status const& error = res.second;
311  TxResult const& result = res.first;
312  // handle errors
313  if (error.toErrorCode() != rpcSUCCESS)
314  {
315  if (error.toErrorCode() == rpcTXN_NOT_FOUND &&
317  {
318  response = Json::Value(Json::objectValue);
319  response[jss::searched_all] =
320  (result.searchedAll == SearchedAll::yes);
321  error.inject(response);
322  }
323  else
324  {
325  error.inject(response);
326  }
327  }
328  // no errors
329  else if (result.txn)
330  {
331  response = result.txn->getJson(JsonOptions::include_date, args.binary);
332 
333  // populate binary metadata
334  if (auto blob = std::get_if<Blob>(&result.meta))
335  {
336  assert(args.binary);
337  response[jss::meta] = strHex(makeSlice(*blob));
338  }
339  // populate meta data
340  else if (auto m = std::get_if<std::shared_ptr<TxMeta>>(&result.meta))
341  {
342  auto& meta = *m;
343  if (meta)
344  {
345  response[jss::meta] = meta->getJson(JsonOptions::none);
346  insertDeliveredAmount(
347  response[jss::meta], context, result.txn, *meta);
348  }
349  }
350  response[jss::validated] = result.validated;
351  }
352  return response;
353 }
354 
357 {
358  // Deserialize and validate JSON arguments
359 
360  if (!context.params.isMember(jss::transaction))
361  return rpcError(rpcINVALID_PARAMS);
362 
363  std::string txHash = context.params[jss::transaction].asString();
364  if (!isHexTxID(txHash))
365  return rpcError(rpcNOT_IMPL);
366 
367  TxArgs args;
368  args.hash = from_hex_text<uint256>(txHash);
369 
370  args.binary = context.params.isMember(jss::binary) &&
371  context.params[jss::binary].asBool();
372 
373  if (context.params.isMember(jss::min_ledger) &&
374  context.params.isMember(jss::max_ledger))
375  {
376  try
377  {
379  context.params[jss::min_ledger].asUInt(),
380  context.params[jss::max_ledger].asUInt());
381  }
382  catch (...)
383  {
384  // One of the calls to `asUInt ()` failed.
386  }
387  }
388 
389  std::pair<TxResult, RPC::Status> res = doTxHelp(context, args);
390  return populateJsonResponse(res, args, context);
391 }
392 
395 {
396  // return values
397  org::xrpl::rpc::v1::GetTransactionResponse response;
398  grpc::Status status = grpc::Status::OK;
399 
400  // input
401  org::xrpl::rpc::v1::GetTransactionRequest& request = context.params;
402 
403  TxArgs args;
404 
405  std::string const& hashBytes = request.hash();
406  args.hash = uint256::fromVoid(hashBytes.data());
407  if (args.hash.size() != hashBytes.size())
408  {
409  grpc::Status errorStatus{grpc::StatusCode::INVALID_ARGUMENT,
410  "ledger hash malformed"};
411  return {response, errorStatus};
412  }
413 
414  args.binary = request.binary();
415 
416  if (request.ledger_range().ledger_index_min() != 0 &&
417  request.ledger_range().ledger_index_max() != 0)
418  {
420  request.ledger_range().ledger_index_min(),
421  request.ledger_range().ledger_index_max());
422  }
423 
424  std::pair<TxResult, RPC::Status> res = doTxHelp(context, args);
425  return populateProtoResponse(res, args, context);
426 }
427 
428 } // namespace ripple
ripple::TxArgs::binary
bool binary
Definition: Tx.cpp:102
ripple::Slice::size
std::size_t size() const noexcept
Returns the number of bytes in the storage.
Definition: Slice.h:77
ripple::JsonOptions::include_date
@ include_date
ripple::rpcDB_DESERIALIZATION
@ rpcDB_DESERIALIZATION
Definition: ErrorCodes.h:135
ripple::RPC::JsonContext
Definition: Context.h:52
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:199
ripple::Ledger::txRead
tx_type txRead(key_type const &key) const override
Read a transaction from the tx map.
Definition: Ledger.cpp:483
std::string
STL class.
std::shared_ptr< Transaction >
ripple::Transaction::getJson
Json::Value getJson(JsonOptions options, bool binary=false) const
Definition: Transaction.cpp:182
ripple::rpcINVALID_PARAMS
@ rpcINVALID_PARAMS
Definition: ErrorCodes.h:85
ripple::Slice
An immutable linear range of bytes.
Definition: Slice.h:43
std::pair
ripple::LedgerInfo::hash
uint256 hash
Definition: ReadView.h:95
ripple::LedgerMaster
Definition: LedgerMaster.h:60
std::vector< unsigned char >
std::find_if
T find_if(T... args)
std::string::size
T size(T... args)
ripple::TransactionMaster::fetch
std::shared_ptr< Transaction > fetch(uint256 const &, error_code_i &ec)
Definition: TransactionMaster.cpp:53
ripple::populateProtoResponse
std::pair< org::xrpl::rpc::v1::GetAccountTransactionHistoryResponse, grpc::Status > populateProtoResponse(std::pair< AccountTxResult, RPC::Status > const &res, AccountTxArgs const &args, RPC::GRPCContext< org::xrpl::rpc::v1::GetAccountTransactionHistoryRequest > const &context)
Definition: AccountTx.cpp:317
ripple::SHAMap::peekItem
std::shared_ptr< SHAMapItem const > const & peekItem(uint256 const &id) const
Definition: SHAMap.cpp:515
ripple::SearchedAll::no
@ no
ripple::RPC::Context::ledgerMaster
LedgerMaster & ledgerMaster
Definition: Context.h:45
ripple::Slice::data
std::uint8_t const * data() const noexcept
Return a pointer to beginning of the storage.
Definition: Slice.h:87
ripple::STObject::getSerializer
Serializer getSerializer() const
Definition: STObject.h:356
ripple::transToken
std::string transToken(TER code)
Definition: TER.cpp:183
ripple::rpcEXCESSIVE_LGR_RANGE
@ rpcEXCESSIVE_LGR_RANGE
Definition: ErrorCodes.h:136
ripple::SearchedAll::yes
@ yes
ripple::LedgerInfo::seq
LedgerIndex seq
Definition: ReadView.h:87
ripple::to_string
std::string to_string(ListDisposition disposition)
Definition: ValidatorList.cpp:41
ripple::TxResult::searchedAll
SearchedAll searchedAll
Definition: Tx.cpp:96
ripple::Transaction::getID
uint256 const & getID() const
Definition: Transaction.h:88
ripple::TxResult
Definition: Tx.cpp:91
ripple::TxArgs::ledgerRange
std::optional< std::pair< uint32_t, uint32_t > > ledgerRange
Definition: Tx.cpp:103
ripple::base_uint::size
constexpr static std::size_t size()
Definition: base_uint.h:417
Json::Value::asBool
bool asBool() const
Definition: json_value.cpp:626
ripple::Serializer::data
void const * data() const noexcept
Definition: Serializer.h:79
ripple::base_uint< 256 >
ripple::Ledger::info
LedgerInfo const & info() const override
Returns information about the ledger.
Definition: Ledger.h:153
ripple::RPC::convert
void convert(org::xrpl::rpc::v1::TransactionResult &to, TER from)
Definition: GRPCHelpers.cpp:897
ripple::rpcSUCCESS
@ rpcSUCCESS
Definition: ErrorCodes.h:45
Json::objectValue
@ objectValue
object value (collection of name/value pairs).
Definition: json_value.h:45
ripple::Ledger
Holds a ledger.
Definition: Ledger.h:77
ripple::Application::getLedgerMaster
virtual LedgerMaster & getLedgerMaster()=0
ripple::Transaction::getLedger
LedgerIndex getLedger() const
Definition: Transaction.h:93
ripple::getMetaHex
bool getMetaHex(Ledger const &ledger, uint256 const &transID, std::string &hex)
Definition: Tx.cpp:70
ripple::JsonOptions::none
@ none
ripple::RPC::GRPCContext
Definition: Context.h:70
ripple::doTxGrpc
std::pair< org::xrpl::rpc::v1::GetTransactionResponse, grpc::Status > doTxGrpc(RPC::GRPCContext< org::xrpl::rpc::v1::GetTransactionRequest > &context)
Definition: Tx.cpp:394
ripple::rpcTXN_NOT_FOUND
@ rpcTXN_NOT_FOUND
Definition: ErrorCodes.h:81
ripple::RPC::Context::app
Application & app
Definition: Context.h:42
ripple::SearchedAll
SearchedAll
Definition: Tx.cpp:89
ripple::LedgerMaster::getLedgerBySeq
std::shared_ptr< Ledger const > getLedgerBySeq(std::uint32_t index)
Definition: LedgerMaster.cpp:1641
ripple::SerialIter
Definition: Serializer.h:311
ripple::SerialIter::getVL
Blob getVL()
Definition: Serializer.cpp:592
Json::Value::isMember
bool isMember(const char *key) const
Return true if the object has a member named key.
Definition: json_value.cpp:961
std::uint32_t
ripple::SerialIter::skip
void skip(int num)
Definition: Serializer.cpp:419
ripple::range
ClosedInterval< T > range(T low, T high)
Create a closed range interval.
Definition: RangeSet.h:54
ripple::isHexTxID
static bool isHexTxID(std::string const &txid)
Definition: Tx.cpp:42
ripple::rpcError
Json::Value rpcError(int iError, Json::Value jvResult)
Definition: RPCErr.cpp:28
ripple::SHAMapAbstractNode::TNType
TNType
Definition: SHAMapTreeNode.h:95
ripple::TxResult::validated
bool validated
Definition: Tx.cpp:95
ripple::RPC::Status
Status represents the results of an operation that might fail.
Definition: Status.h:39
ripple::Serializer
Definition: Serializer.h:43
ripple::Ledger::txMap
SHAMap const & txMap() const
Definition: Ledger.h:295
ripple::TxArgs
Definition: Tx.cpp:99
ripple::rpcNOT_IMPL
@ rpcNOT_IMPL
Definition: ErrorCodes.h:132
ripple::RPC::GRPCContext::params
RequestType params
Definition: Context.h:72
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:434
ripple::Serializer::size
std::size_t size() const noexcept
Definition: Serializer.h:73
ripple::doTxHelp
std::pair< TxResult, RPC::Status > doTxHelp(RPC::Context &context, TxArgs const &args)
Definition: Tx.cpp:107
ripple::ReadView::seq
LedgerIndex seq() const
Returns the sequence number of the base ledger.
Definition: ReadView.h:258
std::string::begin
T begin(T... args)
ripple::LedgerMaster::getCloseTimeBySeq
boost::optional< NetClock::time_point > getCloseTimeBySeq(LedgerIndex ledgerIndex)
Definition: LedgerMaster.cpp:1538
ripple::base_uint< 256 >::fromVoid
static base_uint fromVoid(void const *data)
Definition: base_uint.h:184
ripple::SearchedAll::unknown
@ unknown
Json::Value::asUInt
UInt asUInt() const
Definition: json_value.cpp:555
ripple::SerialIter::getVLDataLength
int getVLDataLength()
Definition: Serializer.cpp:553
ripple::doTxJson
Json::Value doTxJson(RPC::JsonContext &)
Definition: Tx.cpp:356
std::get_if
T get_if(T... args)
ripple::TxResult::meta
std::variant< std::shared_ptr< TxMeta >, Blob > meta
Definition: Tx.cpp:94
ripple::SHAMapAbstractNode::tnTRANSACTION_MD
@ tnTRANSACTION_MD
Definition: SHAMapTreeNode.h:100
std::optional
std::make_pair
T make_pair(T... args)
ripple::strHex
std::string strHex(FwdIt begin, FwdIt end)
Definition: strHex.h:70
std::string::end
T end(T... args)
ripple::rpcINVALID_LGR_RANGE
@ rpcINVALID_LGR_RANGE
Definition: ErrorCodes.h:137
ripple::ClosedInterval
boost::icl::closed_interval< T > ClosedInterval
A closed interval over the domain T.
Definition: RangeSet.h:45
ripple::isValidated
static bool isValidated(LedgerMaster &ledgerMaster, std::uint32_t seq, uint256 const &hash)
Definition: Tx.cpp:58
ripple::TxArgs::hash
uint256 hash
Definition: Tx.cpp:101
ripple::RPC::JsonContext::params
Json::Value params
Definition: Context.h:63
std::string::data
T data(T... args)
ripple::RPC::Context
The context of information needed to call an RPC.
Definition: Context.h:39
Json::Value
Represents a JSON value.
Definition: json_value.h:141
ripple::TxResult::txn
Transaction::pointer txn
Definition: Tx.cpp:93
std::variant
Json::Value::asString
std::string asString() const
Returns the unquoted string value.
Definition: json_value.cpp:482
ripple::Application::getMasterTransaction
virtual TransactionMaster & getMasterTransaction()=0