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/json/json_reader.h>
21 #include <ripple/json/json_value.h>
22 #include <ripple/protocol/jss.h>
23 #include <ripple/rpc/impl/RPCHelpers.h>
24 #include <test/jtx.h>
25 #include <test/jtx/Env.h>
26 
27 #include <functional>
28 
29 namespace ripple {
30 
31 class TransactionEntry_test : public beast::unit_test::suite
32 {
33  void
35  {
36  testcase("Invalid request params");
37  using namespace test::jtx;
38  Env env{*this};
39 
40  {
41  // no params
42  auto const result =
43  env.client().invoke("transaction_entry", {})[jss::result];
44  BEAST_EXPECT(result[jss::error] == "fieldNotFoundTransaction");
45  BEAST_EXPECT(result[jss::status] == "error");
46  }
47 
48  {
50  params[jss::ledger] = 20;
51  auto const result =
52  env.client().invoke("transaction_entry", params)[jss::result];
53  BEAST_EXPECT(result[jss::error] == "lgrNotFound");
54  BEAST_EXPECT(result[jss::status] == "error");
55  }
56 
57  {
59  params[jss::ledger] = "current";
60  params[jss::tx_hash] = "DEADBEEF";
61  auto const result =
62  env.client().invoke("transaction_entry", params)[jss::result];
63  BEAST_EXPECT(result[jss::error] == "notYetImplemented");
64  BEAST_EXPECT(result[jss::status] == "error");
65  }
66 
67  {
69  params[jss::ledger] = "closed";
70  params[jss::tx_hash] = "DEADBEEF";
71  auto const result =
72  env.client().invoke("transaction_entry", params)[jss::result];
73  BEAST_EXPECT(!result[jss::ledger_hash].asString().empty());
74  BEAST_EXPECT(result[jss::error] == "malformedRequest");
75  BEAST_EXPECT(result[jss::status] == "error");
76  }
77 
78  std::string const txHash{
79  "E2FE8D4AF3FCC3944DDF6CD8CDDC5E3F0AD50863EF8919AFEF10CB6408CD4D05"};
80 
81  // Command line format
82  {
83  // No arguments
84  Json::Value const result{env.rpc("transaction_entry")};
85  BEAST_EXPECT(result[jss::ledger_hash].asString().empty());
86  BEAST_EXPECT(result[jss::error] == "badSyntax");
87  BEAST_EXPECT(result[jss::status] == "error");
88  }
89 
90  {
91  // One argument
92  Json::Value const result{env.rpc("transaction_entry", txHash)};
93  BEAST_EXPECT(result[jss::error] == "badSyntax");
94  BEAST_EXPECT(result[jss::status] == "error");
95  }
96 
97  {
98  // First argument with too few characters
99  Json::Value const result{
100  env.rpc("transaction_entry", txHash.substr(1), "closed")};
101  BEAST_EXPECT(result[jss::error] == "invalidParams");
102  BEAST_EXPECT(result[jss::status] == "error");
103  }
104 
105  {
106  // First argument with too many characters
107  Json::Value const result{
108  env.rpc("transaction_entry", txHash + "A", "closed")};
109  BEAST_EXPECT(result[jss::error] == "invalidParams");
110  BEAST_EXPECT(result[jss::status] == "error");
111  }
112 
113  {
114  // Second argument not valid
115  Json::Value const result{
116  env.rpc("transaction_entry", txHash, "closer")};
117  BEAST_EXPECT(result[jss::error] == "invalidParams");
118  BEAST_EXPECT(result[jss::status] == "error");
119  }
120 
121  {
122  // Ledger index of 0 is not valid
123  Json::Value const result{env.rpc("transaction_entry", txHash, "0")};
124  BEAST_EXPECT(result[jss::error] == "invalidParams");
125  BEAST_EXPECT(result[jss::status] == "error");
126  }
127 
128  {
129  // Three arguments
130  Json::Value const result{
131  env.rpc("transaction_entry", txHash, "closed", "extra")};
132  BEAST_EXPECT(result[jss::error] == "badSyntax");
133  BEAST_EXPECT(result[jss::status] == "error");
134  }
135 
136  {
137  // Valid structure, but transaction not found.
138  Json::Value const result{
139  env.rpc("transaction_entry", txHash, "closed")};
140  BEAST_EXPECT(
141  !result[jss::result][jss::ledger_hash].asString().empty());
142  BEAST_EXPECT(
143  result[jss::result][jss::error] == "transactionNotFound");
144  BEAST_EXPECT(result[jss::result][jss::status] == "error");
145  }
146  }
147 
148  void
149  testRequest(unsigned apiVersion)
150  {
151  testcase("Basic request API version " + std::to_string(apiVersion));
152  using namespace test::jtx;
153  Env env{*this};
154 
155  auto check_tx = [this, &env, apiVersion](
156  int index,
157  std::string const txhash,
158  std::string const expected_json = "",
159  std::string const expected_ledger_hash = "",
160  std::string const close_time_iso = "") {
161  // first request using ledger_index to lookup
162  Json::Value const resIndex{[&env, index, &txhash, apiVersion]() {
164  params[jss::ledger_index] = index;
165  params[jss::tx_hash] = txhash;
166  params[jss::api_version] = apiVersion;
167  return env.client().invoke(
168  "transaction_entry", params)[jss::result];
169  }()};
170 
171  if (!BEAST_EXPECT(resIndex.isMember(jss::tx_json)))
172  return;
173 
174  BEAST_EXPECT(resIndex[jss::validated] == true);
175  BEAST_EXPECT(resIndex[jss::ledger_index] == index);
176  BEAST_EXPECT(resIndex[jss::ledger_hash] == expected_ledger_hash);
177  if (apiVersion > 1)
178  {
179  BEAST_EXPECT(resIndex[jss::hash] == txhash);
180  BEAST_EXPECT(!resIndex[jss::tx_json].isMember(jss::hash));
181  BEAST_EXPECT(!resIndex[jss::tx_json].isMember(jss::Amount));
182 
183  if (BEAST_EXPECT(!close_time_iso.empty()))
184  BEAST_EXPECT(
185  resIndex[jss::close_time_iso] == close_time_iso);
186  }
187  else
188  {
189  BEAST_EXPECT(resIndex[jss::tx_json][jss::hash] == txhash);
190  BEAST_EXPECT(!resIndex.isMember(jss::hash));
191  BEAST_EXPECT(!resIndex.isMember(jss::close_time_iso));
192  }
193 
194  if (!expected_json.empty())
195  {
196  Json::Value expected;
197  Json::Reader().parse(expected_json, expected);
198  if (RPC::contains_error(expected))
199  Throw<std::runtime_error>(
200  "Internal JSONRPC_test error. Bad test JSON.");
201 
202  for (auto memberIt = expected.begin();
203  memberIt != expected.end();
204  memberIt++)
205  {
206  auto const name = memberIt.memberName();
207  if (BEAST_EXPECT(resIndex[jss::tx_json].isMember(name)))
208  {
209  auto const received = resIndex[jss::tx_json][name];
210  BEAST_EXPECTS(
211  received == *memberIt,
212  txhash + " contains \n\"" + name + "\": " //
213  + to_string(received) //
214  + " but expected " //
215  + to_string(expected));
216  }
217  }
218  }
219 
220  // second request using ledger_hash to lookup and verify
221  // both responses match
222  {
224  params[jss::ledger_hash] = resIndex[jss::ledger_hash];
225  params[jss::tx_hash] = txhash;
226  params[jss::api_version] = apiVersion;
227  Json::Value const resHash = env.client().invoke(
228  "transaction_entry", params)[jss::result];
229  BEAST_EXPECT(resHash == resIndex);
230  }
231 
232  // Use the command line form with the index.
233  Json::Value const clIndex{env.rpc(
234  apiVersion,
235  "transaction_entry",
236  txhash,
237  std::to_string(index))};
238  BEAST_EXPECT(clIndex["result"] == resIndex);
239 
240  // Use the command line form with the ledger_hash.
241  Json::Value const clHash{env.rpc(
242  apiVersion,
243  "transaction_entry",
244  txhash,
245  resIndex[jss::ledger_hash].asString())};
246  BEAST_EXPECT(clHash["result"] == resIndex);
247  };
248 
249  Account A1{"A1"};
250  Account A2{"A2"};
251 
252  env.fund(XRP(10000), A1);
253  auto fund_1_tx =
254  boost::lexical_cast<std::string>(env.tx()->getTransactionID());
255  BEAST_EXPECT(
256  fund_1_tx ==
257  "F4E9DF90D829A9E8B423FF68C34413E240D8D8BB0EFD080DF08114ED398E2506");
258 
259  env.fund(XRP(10000), A2);
260  auto fund_2_tx =
261  boost::lexical_cast<std::string>(env.tx()->getTransactionID());
262  BEAST_EXPECT(
263  fund_2_tx ==
264  "6853CD8226A05068C951CB1F54889FF4E40C5B440DC1C5BA38F114C4E0B1E705");
265 
266  env.close();
267 
268  // these are actually AccountSet txs because fund does two txs and
269  // env.tx only reports the last one
270  check_tx(
271  env.closed()->seq(),
272  fund_1_tx,
273  R"({
274  "Account" : "r4nmQNH4Fhjfh6cHDbvVSsBv7KySbj4cBf",
275  "Fee" : "10",
276  "Sequence" : 3,
277  "SetFlag" : 8,
278  "SigningPubKey" : "0324CAAFA2212D2AEAB9D42D481535614AED486293E1FB1380FF070C3DD7FB4264",
279  "TransactionType" : "AccountSet",
280  "TxnSignature" : "3044022007B35E3B99460534FF6BC3A66FBBA03591C355CC38E38588968E87CCD01BE229022071A443026DE45041B55ABB1CC76812A87EA701E475BBB7E165513B4B242D3474",
281 })",
282  "ADB727BCC74B29421BB01B847740B179B8A0ED3248D76A89ED2E39B02C427784",
283  "2000-01-01T00:00:10Z");
284  check_tx(
285  env.closed()->seq(),
286  fund_2_tx,
287  R"({
288  "Account" : "rGpeQzUWFu4fMhJHZ1Via5aqFC3A5twZUD",
289  "Fee" : "10",
290  "Sequence" : 3,
291  "SetFlag" : 8,
292  "SigningPubKey" : "03CFF28E067A2CCE6CC5A598C0B845CBD3F30A7863BE9C0DD55F4960EFABCCF4D0",
293  "TransactionType" : "AccountSet",
294  "TxnSignature" : "3045022100C8857FC0759A2AC0D2F320684691A66EAD252EAED9EF88C79791BC58BFCC9D860220421722286487DD0ED6BBA626CE6FCBDD14289F7F4726870C3465A4054C2702D7",
295 })",
296  "ADB727BCC74B29421BB01B847740B179B8A0ED3248D76A89ED2E39B02C427784",
297  "2000-01-01T00:00:10Z");
298 
299  env.trust(A2["USD"](1000), A1);
300  // the trust tx is actually a payment since the trust method
301  // refunds fees with a payment after TrustSet..so just ignore the type
302  // in the check below
303  auto trust_tx =
304  boost::lexical_cast<std::string>(env.tx()->getTransactionID());
305  BEAST_EXPECT(
306  trust_tx ==
307  "C992D97D88FF444A1AB0C06B27557EC54B7F7DA28254778E60238BEA88E0C101");
308 
309  env(pay(A2, A1, A2["USD"](5)));
310  auto pay_tx =
311  boost::lexical_cast<std::string>(env.tx()->getTransactionID());
312  env.close();
313  BEAST_EXPECT(
314  pay_tx ==
315  "988046D484ACE9F5F6A8C792D89C6EA2DB307B5DDA9864AEBA88E6782ABD0865");
316 
317  check_tx(
318  env.closed()->seq(),
319  trust_tx,
320  R"({
321  "Account" : "rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh",
322  "DeliverMax" : "10",
323  "Destination" : "r4nmQNH4Fhjfh6cHDbvVSsBv7KySbj4cBf",
324  "Fee" : "10",
325  "Flags" : 2147483648,
326  "Sequence" : 3,
327  "SigningPubKey" : "0330E7FC9D56BB25D6893BA3F317AE5BCF33B3291BD63DB32654A313222F7FD020",
328  "TransactionType" : "Payment",
329  "TxnSignature" : "3044022033D9EBF7F02950AF2F6B13C07AEE641C8FEBDD540A338FCB9027A965A4AED35B02206E4E227DCC226A3456C0FEF953449D21645A24EB63CA0BB7C5B62470147FD1D1",
330 })",
331  "39AA166131D56622EFD96CB4B2BD58003ACD37091C90977FF6B81419DB451775",
332  "2000-01-01T00:00:20Z");
333 
334  check_tx(
335  env.closed()->seq(),
336  pay_tx,
337  R"({
338  "Account" : "rGpeQzUWFu4fMhJHZ1Via5aqFC3A5twZUD",
339  "DeliverMax" :
340  {
341  "currency" : "USD",
342  "issuer" : "rGpeQzUWFu4fMhJHZ1Via5aqFC3A5twZUD",
343  "value" : "5"
344  },
345  "Destination" : "r4nmQNH4Fhjfh6cHDbvVSsBv7KySbj4cBf",
346  "Fee" : "10",
347  "Flags" : 2147483648,
348  "Sequence" : 4,
349  "SigningPubKey" : "03CFF28E067A2CCE6CC5A598C0B845CBD3F30A7863BE9C0DD55F4960EFABCCF4D0",
350  "TransactionType" : "Payment",
351  "TxnSignature" : "30450221008A722B7F16EDB2348886E88ED4EC682AE9973CC1EE0FF37C93BB2CEC821D3EDF022059E464472031BA5E0D88A93E944B6A8B8DB3E1D5E5D1399A805F615789DB0BED",
352 })",
353  "39AA166131D56622EFD96CB4B2BD58003ACD37091C90977FF6B81419DB451775",
354  "2000-01-01T00:00:20Z");
355 
356  env(offer(A2, XRP(100), A2["USD"](1)));
357  auto offer_tx =
358  boost::lexical_cast<std::string>(env.tx()->getTransactionID());
359  BEAST_EXPECT(
360  offer_tx ==
361  "5FCC1A27A7664F82A0CC4BE5766FBBB7C560D52B93AA7B550CD33B27AEC7EFFB");
362 
363  env.close();
364  check_tx(
365  env.closed()->seq(),
366  offer_tx,
367  R"({
368  "Account" : "rGpeQzUWFu4fMhJHZ1Via5aqFC3A5twZUD",
369  "Fee" : "10",
370  "Sequence" : 5,
371  "SigningPubKey" : "03CFF28E067A2CCE6CC5A598C0B845CBD3F30A7863BE9C0DD55F4960EFABCCF4D0",
372  "TakerGets" :
373  {
374  "currency" : "USD",
375  "issuer" : "rGpeQzUWFu4fMhJHZ1Via5aqFC3A5twZUD",
376  "value" : "1"
377  },
378  "TakerPays" : "100000000",
379  "TransactionType" : "OfferCreate",
380  "TxnSignature" : "304502210093FC93ACB77B4E3DE3315441BD010096734859080C1797AB735EB47EBD541BD102205020BB1A7C3B4141279EE4C287C13671E2450EA78914EFD0C6DB2A18344CD4F2",
381 })",
382  "0589B876DF5AFE335781E8FC12C2EC62A80151DF13BBAFE9EB2DA62E798ED434",
383  "2000-01-01T00:00:30Z");
384  }
385 
386 public:
387  void
388  run() override
389  {
390  testBadInput();
393  }
394 };
395 
396 BEAST_DEFINE_TESTSUITE(TransactionEntry, rpc, ripple);
397 
398 } // namespace ripple
ripple::TransactionEntry_test::run
void run() override
Definition: TransactionEntry_test.cpp:333
std::string
STL class.
ripple::TransactionEntry_test
Definition: TransactionEntry_test.cpp:31
ripple::BEAST_DEFINE_TESTSUITE
BEAST_DEFINE_TESTSUITE(AccountTxPaging, app, ripple)
functional
ripple::TransactionEntry_test::testRequest
void testRequest(unsigned apiVersion)
Definition: TransactionEntry_test.cpp:149
ripple::test::jtx::forAllApiVersions
void forAllApiVersions(VersionedTestCallable auto... testCallable)
Definition: Env.h:781
std::bind_front
T bind_front(T... args)
Json::Value::end
const_iterator end() const
Definition: json_value.cpp:1064
Json::Reader
Unserialize a JSON document into a Value.
Definition: json_reader.h:36
Json::objectValue
@ objectValue
object value (collection of name/value pairs).
Definition: json_value.h:43
ripple::RPC::contains_error
bool contains_error(Json::Value const &json)
Returns true if the json contains an rpc error specification.
Definition: ErrorCodes.cpp:196
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:34
Json::Reader::parse
bool parse(std::string const &document, Value &root)
Read a Value from a JSON document.
Definition: json_reader.cpp:74
std::string::empty
T empty(T... args)
ripple::to_string
std::string to_string(Manifest const &m)
Format the specified manifest to a string for debugging purposes.
Definition: app/misc/impl/Manifest.cpp:41
Json::Value::begin
const_iterator begin() const
Definition: json_value.cpp:1046
Json::Value
Represents a JSON value.
Definition: json_value.h:145