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