rippled
TransactionEntry_test.cpp
1 //------------------------------------------------------------------------------
2 /*
3  This file is part of rippled: https://github.com/ripple/rippled
4  Copyright (c) 2012-2017 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/protocol/jss.h>
21 #include <test/jtx.h>
22 #include <test/jtx/Env.h>
23 
24 namespace ripple {
25 
26 class TransactionEntry_test : public beast::unit_test::suite
27 {
28  void
30  {
31  testcase("Invalid request params");
32  using namespace test::jtx;
33  Env env{*this};
34 
35  {
36  // no params
37  auto const result =
38  env.client().invoke("transaction_entry", {})[jss::result];
39  BEAST_EXPECT(result[jss::error] == "fieldNotFoundTransaction");
40  BEAST_EXPECT(result[jss::status] == "error");
41  }
42 
43  {
45  params[jss::ledger] = 20;
46  auto const result =
47  env.client().invoke("transaction_entry", params)[jss::result];
48  BEAST_EXPECT(result[jss::error] == "lgrNotFound");
49  BEAST_EXPECT(result[jss::status] == "error");
50  }
51 
52  {
54  params[jss::ledger] = "current";
55  params[jss::tx_hash] = "DEADBEEF";
56  auto const result =
57  env.client().invoke("transaction_entry", params)[jss::result];
58  BEAST_EXPECT(result[jss::error] == "notYetImplemented");
59  BEAST_EXPECT(result[jss::status] == "error");
60  }
61 
62  {
64  params[jss::ledger] = "closed";
65  params[jss::tx_hash] = "DEADBEEF";
66  auto const result =
67  env.client().invoke("transaction_entry", params)[jss::result];
68  BEAST_EXPECT(!result[jss::ledger_hash].asString().empty());
69  BEAST_EXPECT(result[jss::error] == "malformedRequest");
70  BEAST_EXPECT(result[jss::status] == "error");
71  }
72 
73  std::string const txHash{
74  "E2FE8D4AF3FCC3944DDF6CD8CDDC5E3F0AD50863EF8919AFEF10CB6408CD4D05"};
75 
76  // Command line format
77  {
78  // No arguments
79  Json::Value const result{env.rpc("transaction_entry")};
80  BEAST_EXPECT(result[jss::ledger_hash].asString().empty());
81  BEAST_EXPECT(result[jss::error] == "badSyntax");
82  BEAST_EXPECT(result[jss::status] == "error");
83  }
84 
85  {
86  // One argument
87  Json::Value const result{env.rpc("transaction_entry", txHash)};
88  BEAST_EXPECT(result[jss::error] == "badSyntax");
89  BEAST_EXPECT(result[jss::status] == "error");
90  }
91 
92  {
93  // First argument with too few characters
94  Json::Value const result{
95  env.rpc("transaction_entry", txHash.substr(1), "closed")};
96  BEAST_EXPECT(result[jss::error] == "invalidParams");
97  BEAST_EXPECT(result[jss::status] == "error");
98  }
99 
100  {
101  // First argument with too many characters
102  Json::Value const result{
103  env.rpc("transaction_entry", txHash + "A", "closed")};
104  BEAST_EXPECT(result[jss::error] == "invalidParams");
105  BEAST_EXPECT(result[jss::status] == "error");
106  }
107 
108  {
109  // Second argument not valid
110  Json::Value const result{
111  env.rpc("transaction_entry", txHash, "closer")};
112  BEAST_EXPECT(result[jss::error] == "invalidParams");
113  BEAST_EXPECT(result[jss::status] == "error");
114  }
115 
116  {
117  // Ledger index of 0 is not valid
118  Json::Value const result{env.rpc("transaction_entry", txHash, "0")};
119  BEAST_EXPECT(result[jss::error] == "invalidParams");
120  BEAST_EXPECT(result[jss::status] == "error");
121  }
122 
123  {
124  // Three arguments
125  Json::Value const result{
126  env.rpc("transaction_entry", txHash, "closed", "extra")};
127  BEAST_EXPECT(result[jss::error] == "badSyntax");
128  BEAST_EXPECT(result[jss::status] == "error");
129  }
130 
131  {
132  // Valid structure, but transaction not found.
133  Json::Value const result{
134  env.rpc("transaction_entry", txHash, "closed")};
135  BEAST_EXPECT(
136  !result[jss::result][jss::ledger_hash].asString().empty());
137  BEAST_EXPECT(
138  result[jss::result][jss::error] == "transactionNotFound");
139  BEAST_EXPECT(result[jss::result][jss::status] == "error");
140  }
141  }
142 
143  void
145  {
146  testcase("Basic request");
147  using namespace test::jtx;
148  Env env{*this};
149 
150  auto check_tx = [this, &env](
151  int index,
152  std::string const txhash,
153  std::string const type = "") {
154  // first request using ledger_index to lookup
155  Json::Value const resIndex{[&env, index, &txhash]() {
157  params[jss::ledger_index] = index;
158  params[jss::tx_hash] = txhash;
159  return env.client().invoke(
160  "transaction_entry", params)[jss::result];
161  }()};
162 
163  if (!BEAST_EXPECTS(resIndex.isMember(jss::tx_json), txhash))
164  return;
165 
166  BEAST_EXPECT(resIndex[jss::tx_json][jss::hash] == txhash);
167  if (!type.empty())
168  {
169  BEAST_EXPECTS(
170  resIndex[jss::tx_json][jss::TransactionType] == type,
171  txhash + " is " +
172  resIndex[jss::tx_json][jss::TransactionType]
173  .asString());
174  }
175 
176  // second request using ledger_hash to lookup and verify
177  // both responses match
178  {
180  params[jss::ledger_hash] = resIndex[jss::ledger_hash];
181  params[jss::tx_hash] = txhash;
182  Json::Value const resHash = env.client().invoke(
183  "transaction_entry", params)[jss::result];
184  BEAST_EXPECT(resHash == resIndex);
185  }
186 
187  // Use the command line form with the index.
188  {
189  Json::Value const clIndex{env.rpc(
190  "transaction_entry", txhash, std::to_string(index))};
191  BEAST_EXPECT(clIndex["result"] == resIndex);
192  }
193 
194  // Use the command line form with the ledger_hash.
195  {
196  Json::Value const clHash{env.rpc(
197  "transaction_entry",
198  txhash,
199  resIndex[jss::ledger_hash].asString())};
200  BEAST_EXPECT(clHash["result"] == resIndex);
201  }
202  };
203 
204  Account A1{"A1"};
205  Account A2{"A2"};
206 
207  env.fund(XRP(10000), A1);
208  auto fund_1_tx =
209  boost::lexical_cast<std::string>(env.tx()->getTransactionID());
210 
211  env.fund(XRP(10000), A2);
212  auto fund_2_tx =
213  boost::lexical_cast<std::string>(env.tx()->getTransactionID());
214 
215  env.close();
216 
217  // these are actually AccountSet txs because fund does two txs and
218  // env.tx only reports the last one
219  check_tx(env.closed()->seq(), fund_1_tx);
220  check_tx(env.closed()->seq(), fund_2_tx);
221 
222  env.trust(A2["USD"](1000), A1);
223  // the trust tx is actually a payment since the trust method
224  // refunds fees with a payment after TrustSet..so just ignore the type
225  // in the check below
226  auto trust_tx =
227  boost::lexical_cast<std::string>(env.tx()->getTransactionID());
228 
229  env(pay(A2, A1, A2["USD"](5)));
230  auto pay_tx =
231  boost::lexical_cast<std::string>(env.tx()->getTransactionID());
232  env.close();
233 
234  check_tx(env.closed()->seq(), trust_tx);
235  check_tx(env.closed()->seq(), pay_tx, jss::Payment.c_str());
236 
237  env(offer(A2, XRP(100), A2["USD"](1)));
238  auto offer_tx =
239  boost::lexical_cast<std::string>(env.tx()->getTransactionID());
240 
241  env.close();
242 
243  check_tx(env.closed()->seq(), offer_tx, jss::OfferCreate.c_str());
244  }
245 
246 public:
247  void
248  run() override
249  {
250  testBadInput();
251  testRequest();
252  }
253 };
254 
255 BEAST_DEFINE_TESTSUITE(TransactionEntry, rpc, ripple);
256 
257 } // namespace ripple
ripple::TransactionEntry_test::run
void run() override
Definition: TransactionEntry_test.cpp:248
std::string
STL class.
ripple::TransactionEntry_test
Definition: TransactionEntry_test.cpp:26
ripple::BEAST_DEFINE_TESTSUITE
BEAST_DEFINE_TESTSUITE(AccountTxPaging, app, ripple)
Json::objectValue
@ objectValue
object value (collection of name/value pairs).
Definition: json_value.h:43
std::to_string
T to_string(T... args)
ripple
Use hash_* containers for keys that do not need a cryptographically secure hashing algorithm.
Definition: RCLCensorshipDetector.h:29
ripple::TransactionEntry_test::testBadInput
void testBadInput()
Definition: TransactionEntry_test.cpp:29
ripple::TransactionEntry_test::testRequest
void testRequest()
Definition: TransactionEntry_test.cpp:144
Json::Value
Represents a JSON value.
Definition: json_value.h:145