rippled
Transaction_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/app/rdb/backend/SQLiteDatabase.h>
21 #include <ripple/protocol/ErrorCodes.h>
22 #include <ripple/protocol/jss.h>
23 #include <ripple/rpc/CTID.h>
24 #include <optional>
25 #include <test/jtx.h>
26 #include <test/jtx/Env.h>
27 #include <test/jtx/envconfig.h>
28 #include <tuple>
29 
30 namespace ripple {
31 
32 class Transaction_test : public beast::unit_test::suite
33 {
35  makeNetworkConfig(uint32_t networkID)
36  {
37  using namespace test::jtx;
38  return envconfig([&](std::unique_ptr<Config> cfg) {
39  cfg->NETWORK_ID = networkID;
40  return cfg;
41  });
42  }
43 
44  void
46  {
47  testcase("Test Range Request");
48 
49  using namespace test::jtx;
50  using std::to_string;
51 
52  const char* COMMAND = jss::tx.c_str();
53  const char* BINARY = jss::binary.c_str();
54  const char* NOT_FOUND = RPC::get_error_info(rpcTXN_NOT_FOUND).token;
56  const char* EXCESSIVE =
58 
59  Env env{*this, features};
60  auto const alice = Account("alice");
61  env.fund(XRP(1000), alice);
62  env.close();
63 
66  auto const startLegSeq = env.current()->info().seq;
67  for (int i = 0; i < 750; ++i)
68  {
69  env(noop(alice));
70  txns.emplace_back(env.tx());
71  env.close();
72  metas.emplace_back(
73  env.closed()->txRead(env.tx()->getTransactionID()).second);
74  }
75  auto const endLegSeq = env.closed()->info().seq;
76 
77  // Find the existing transactions
78  for (size_t i = 0; i < txns.size(); ++i)
79  {
80  auto const& tx = txns[i];
81  auto const& meta = metas[i];
82  auto const result = env.rpc(
83  COMMAND,
84  to_string(tx->getTransactionID()),
85  BINARY,
86  to_string(startLegSeq),
87  to_string(endLegSeq));
88 
89  BEAST_EXPECT(result[jss::result][jss::status] == jss::success);
90  BEAST_EXPECT(
91  result[jss::result][jss::tx] ==
92  strHex(tx->getSerializer().getData()));
93  BEAST_EXPECT(
94  result[jss::result][jss::meta] ==
95  strHex(meta->getSerializer().getData()));
96  }
97 
98  auto const tx = env.jt(noop(alice), seq(env.seq(alice))).stx;
99  for (int deltaEndSeq = 0; deltaEndSeq < 2; ++deltaEndSeq)
100  {
101  auto const result = env.rpc(
102  COMMAND,
103  to_string(tx->getTransactionID()),
104  BINARY,
105  to_string(startLegSeq),
106  to_string(endLegSeq + deltaEndSeq));
107 
108  BEAST_EXPECT(
109  result[jss::result][jss::status] == jss::error &&
110  result[jss::result][jss::error] == NOT_FOUND);
111 
112  if (deltaEndSeq)
113  BEAST_EXPECT(!result[jss::result][jss::searched_all].asBool());
114  else
115  BEAST_EXPECT(result[jss::result][jss::searched_all].asBool());
116  }
117 
118  // Find transactions outside of provided range.
119  for (auto&& tx : txns)
120  {
121  auto const result = env.rpc(
122  COMMAND,
123  to_string(tx->getTransactionID()),
124  BINARY,
125  to_string(endLegSeq + 1),
126  to_string(endLegSeq + 100));
127 
128  BEAST_EXPECT(result[jss::result][jss::status] == jss::success);
129  BEAST_EXPECT(!result[jss::result][jss::searched_all].asBool());
130  }
131 
132  const auto deletedLedger = (startLegSeq + endLegSeq) / 2;
133  {
134  // Remove one of the ledgers from the database directly
135  dynamic_cast<SQLiteDatabase*>(&env.app().getRelationalDatabase())
136  ->deleteTransactionByLedgerSeq(deletedLedger);
137  }
138 
139  for (int deltaEndSeq = 0; deltaEndSeq < 2; ++deltaEndSeq)
140  {
141  auto const result = env.rpc(
142  COMMAND,
143  to_string(tx->getTransactionID()),
144  BINARY,
145  to_string(startLegSeq),
146  to_string(endLegSeq + deltaEndSeq));
147 
148  BEAST_EXPECT(
149  result[jss::result][jss::status] == jss::error &&
150  result[jss::result][jss::error] == NOT_FOUND);
151  BEAST_EXPECT(!result[jss::result][jss::searched_all].asBool());
152  }
153 
154  // Provide range without providing the `binary`
155  // field. (Tests parameter parsing)
156  {
157  auto const result = env.rpc(
158  COMMAND,
159  to_string(tx->getTransactionID()),
160  to_string(startLegSeq),
161  to_string(endLegSeq));
162 
163  BEAST_EXPECT(
164  result[jss::result][jss::status] == jss::error &&
165  result[jss::result][jss::error] == NOT_FOUND);
166 
167  BEAST_EXPECT(!result[jss::result][jss::searched_all].asBool());
168  }
169 
170  // Provide range without providing the `binary`
171  // field. (Tests parameter parsing)
172  {
173  auto const result = env.rpc(
174  COMMAND,
175  to_string(tx->getTransactionID()),
176  to_string(startLegSeq),
177  to_string(deletedLedger - 1));
178 
179  BEAST_EXPECT(
180  result[jss::result][jss::status] == jss::error &&
181  result[jss::result][jss::error] == NOT_FOUND);
182 
183  BEAST_EXPECT(result[jss::result][jss::searched_all].asBool());
184  }
185 
186  // Provide range without providing the `binary`
187  // field. (Tests parameter parsing)
188  {
189  auto const result = env.rpc(
190  COMMAND,
191  to_string(txns[0]->getTransactionID()),
192  to_string(startLegSeq),
193  to_string(deletedLedger - 1));
194 
195  BEAST_EXPECT(result[jss::result][jss::status] == jss::success);
196  BEAST_EXPECT(!result[jss::result].isMember(jss::searched_all));
197  }
198 
199  // Provide an invalid range: (min > max)
200  {
201  auto const result = env.rpc(
202  COMMAND,
203  to_string(tx->getTransactionID()),
204  BINARY,
205  to_string(deletedLedger - 1),
206  to_string(startLegSeq));
207 
208  BEAST_EXPECT(
209  result[jss::result][jss::status] == jss::error &&
210  result[jss::result][jss::error] == INVALID);
211 
212  BEAST_EXPECT(!result[jss::result].isMember(jss::searched_all));
213  }
214 
215  // Provide an invalid range: (min < 0)
216  {
217  auto const result = env.rpc(
218  COMMAND,
219  to_string(tx->getTransactionID()),
220  BINARY,
221  to_string(-1),
222  to_string(deletedLedger - 1));
223 
224  BEAST_EXPECT(
225  result[jss::result][jss::status] == jss::error &&
226  result[jss::result][jss::error] == INVALID);
227 
228  BEAST_EXPECT(!result[jss::result].isMember(jss::searched_all));
229  }
230 
231  // Provide an invalid range: (min < 0, max < 0)
232  {
233  auto const result = env.rpc(
234  COMMAND,
235  to_string(tx->getTransactionID()),
236  BINARY,
237  to_string(-20),
238  to_string(-10));
239 
240  BEAST_EXPECT(
241  result[jss::result][jss::status] == jss::error &&
242  result[jss::result][jss::error] == INVALID);
243 
244  BEAST_EXPECT(!result[jss::result].isMember(jss::searched_all));
245  }
246 
247  // Provide an invalid range: (only one value)
248  {
249  auto const result = env.rpc(
250  COMMAND,
251  to_string(tx->getTransactionID()),
252  BINARY,
253  to_string(20));
254 
255  BEAST_EXPECT(
256  result[jss::result][jss::status] == jss::error &&
257  result[jss::result][jss::error] == INVALID);
258 
259  BEAST_EXPECT(!result[jss::result].isMember(jss::searched_all));
260  }
261 
262  // Provide an invalid range: (only one value)
263  {
264  auto const result = env.rpc(
265  COMMAND, to_string(tx->getTransactionID()), to_string(20));
266 
267  // Since we only provided one value for the range,
268  // the interface parses it as a false binary flag,
269  // as single-value ranges are not accepted. Since
270  // the error this causes differs depending on the platform
271  // we don't call out a specific error here.
272  BEAST_EXPECT(result[jss::result][jss::status] == jss::error);
273 
274  BEAST_EXPECT(!result[jss::result].isMember(jss::searched_all));
275  }
276 
277  // Provide an invalid range: (max - min > 1000)
278  {
279  auto const result = env.rpc(
280  COMMAND,
281  to_string(tx->getTransactionID()),
282  BINARY,
283  to_string(startLegSeq),
284  to_string(startLegSeq + 1001));
285 
286  BEAST_EXPECT(
287  result[jss::result][jss::status] == jss::error &&
288  result[jss::result][jss::error] == EXCESSIVE);
289 
290  BEAST_EXPECT(!result[jss::result].isMember(jss::searched_all));
291  }
292  }
293 
294  void
296  {
297  testcase("ctid_range");
298 
299  using namespace test::jtx;
300  using std::to_string;
301 
302  const char* COMMAND = jss::tx.c_str();
303  const char* BINARY = jss::binary.c_str();
304  const char* NOT_FOUND = RPC::get_error_info(rpcTXN_NOT_FOUND).token;
306  const char* EXCESSIVE =
308 
309  Env env{*this, makeNetworkConfig(11111)};
310  uint32_t netID = env.app().config().NETWORK_ID;
311 
312  auto const alice = Account("alice");
313  env.fund(XRP(1000), alice);
314  env.close();
315 
318  auto const startLegSeq = env.current()->info().seq;
319  for (int i = 0; i < 750; ++i)
320  {
321  env(noop(alice));
322  txns.emplace_back(env.tx());
323  env.close();
324  metas.emplace_back(
325  env.closed()->txRead(env.tx()->getTransactionID()).second);
326  }
327  auto const endLegSeq = env.closed()->info().seq;
328 
329  // Find the existing transactions
330  for (size_t i = 0; i < txns.size(); ++i)
331  {
332  auto const& tx = txns[i];
333  auto const& meta = metas[i];
334  uint32_t txnIdx = meta->getFieldU32(sfTransactionIndex);
335  auto const result = env.rpc(
336  COMMAND,
337  *RPC::encodeCTID(startLegSeq + i, txnIdx, netID),
338  BINARY,
339  to_string(startLegSeq),
340  to_string(endLegSeq));
341 
342  BEAST_EXPECT(result[jss::result][jss::status] == jss::success);
343  BEAST_EXPECT(
344  result[jss::result][jss::tx] ==
345  strHex(tx->getSerializer().getData()));
346  BEAST_EXPECT(
347  result[jss::result][jss::meta] ==
348  strHex(meta->getSerializer().getData()));
349  }
350 
351  auto const tx = env.jt(noop(alice), seq(env.seq(alice))).stx;
352  auto const ctid =
353  *RPC::encodeCTID(endLegSeq, tx->getSeqProxy().value(), netID);
354  for (int deltaEndSeq = 0; deltaEndSeq < 2; ++deltaEndSeq)
355  {
356  auto const result = env.rpc(
357  COMMAND,
358  ctid,
359  BINARY,
360  to_string(startLegSeq),
361  to_string(endLegSeq + deltaEndSeq));
362 
363  BEAST_EXPECT(
364  result[jss::result][jss::status] == jss::error &&
365  result[jss::result][jss::error] == NOT_FOUND);
366 
367  if (deltaEndSeq)
368  BEAST_EXPECT(!result[jss::result][jss::searched_all].asBool());
369  else
370  BEAST_EXPECT(!result[jss::result][jss::searched_all].asBool());
371  }
372 
373  // Find transactions outside of provided range.
374  for (size_t i = 0; i < txns.size(); ++i)
375  {
376  // auto const& tx = txns[i];
377  auto const& meta = metas[i];
378  uint32_t txnIdx = meta->getFieldU32(sfTransactionIndex);
379  auto const result = env.rpc(
380  COMMAND,
381  *RPC::encodeCTID(startLegSeq + i, txnIdx, netID),
382  BINARY,
383  to_string(endLegSeq + 1),
384  to_string(endLegSeq + 100));
385 
386  BEAST_EXPECT(result[jss::result][jss::status] == jss::success);
387  BEAST_EXPECT(!result[jss::result][jss::searched_all].asBool());
388  }
389 
390  const auto deletedLedger = (startLegSeq + endLegSeq) / 2;
391  {
392  // Remove one of the ledgers from the database directly
393  dynamic_cast<SQLiteDatabase*>(&env.app().getRelationalDatabase())
394  ->deleteTransactionByLedgerSeq(deletedLedger);
395  }
396 
397  for (int deltaEndSeq = 0; deltaEndSeq < 2; ++deltaEndSeq)
398  {
399  auto const result = env.rpc(
400  COMMAND,
401  ctid,
402  BINARY,
403  to_string(startLegSeq),
404  to_string(endLegSeq + deltaEndSeq));
405 
406  BEAST_EXPECT(
407  result[jss::result][jss::status] == jss::error &&
408  result[jss::result][jss::error] == NOT_FOUND);
409  BEAST_EXPECT(!result[jss::result][jss::searched_all].asBool());
410  }
411 
412  // Provide range without providing the `binary`
413  // field. (Tests parameter parsing)
414  {
415  auto const result = env.rpc(
416  COMMAND, ctid, to_string(startLegSeq), to_string(endLegSeq));
417 
418  BEAST_EXPECT(
419  result[jss::result][jss::status] == jss::error &&
420  result[jss::result][jss::error] == NOT_FOUND);
421 
422  BEAST_EXPECT(!result[jss::result][jss::searched_all].asBool());
423  }
424 
425  // Provide range without providing the `binary`
426  // field. (Tests parameter parsing)
427  {
428  auto const result = env.rpc(
429  COMMAND,
430  ctid,
431  to_string(startLegSeq),
432  to_string(deletedLedger - 1));
433 
434  BEAST_EXPECT(
435  result[jss::result][jss::status] == jss::error &&
436  result[jss::result][jss::error] == NOT_FOUND);
437 
438  BEAST_EXPECT(!result[jss::result][jss::searched_all].asBool());
439  }
440 
441  // Provide range without providing the `binary`
442  // field. (Tests parameter parsing)
443  {
444  auto const& meta = metas[0];
445  uint32_t txnIdx = meta->getFieldU32(sfTransactionIndex);
446  auto const result = env.rpc(
447  COMMAND,
448  *RPC::encodeCTID(endLegSeq, txnIdx, netID),
449  to_string(startLegSeq),
450  to_string(deletedLedger - 1));
451 
452  BEAST_EXPECT(result[jss::result][jss::status] == jss::success);
453  BEAST_EXPECT(!result[jss::result].isMember(jss::searched_all));
454  }
455 
456  // Provide an invalid range: (min > max)
457  {
458  auto const result = env.rpc(
459  COMMAND,
460  ctid,
461  BINARY,
462  to_string(deletedLedger - 1),
463  to_string(startLegSeq));
464 
465  BEAST_EXPECT(
466  result[jss::result][jss::status] == jss::error &&
467  result[jss::result][jss::error] == INVALID);
468 
469  BEAST_EXPECT(!result[jss::result].isMember(jss::searched_all));
470  }
471 
472  // Provide an invalid range: (min < 0)
473  {
474  auto const result = env.rpc(
475  COMMAND,
476  ctid,
477  BINARY,
478  to_string(-1),
479  to_string(deletedLedger - 1));
480 
481  BEAST_EXPECT(
482  result[jss::result][jss::status] == jss::error &&
483  result[jss::result][jss::error] == INVALID);
484 
485  BEAST_EXPECT(!result[jss::result].isMember(jss::searched_all));
486  }
487 
488  // Provide an invalid range: (min < 0, max < 0)
489  {
490  auto const result =
491  env.rpc(COMMAND, ctid, BINARY, to_string(-20), to_string(-10));
492 
493  BEAST_EXPECT(
494  result[jss::result][jss::status] == jss::error &&
495  result[jss::result][jss::error] == INVALID);
496 
497  BEAST_EXPECT(!result[jss::result].isMember(jss::searched_all));
498  }
499 
500  // Provide an invalid range: (only one value)
501  {
502  auto const result = env.rpc(COMMAND, ctid, BINARY, to_string(20));
503 
504  BEAST_EXPECT(
505  result[jss::result][jss::status] == jss::error &&
506  result[jss::result][jss::error] == INVALID);
507 
508  BEAST_EXPECT(!result[jss::result].isMember(jss::searched_all));
509  }
510 
511  // Provide an invalid range: (only one value)
512  {
513  auto const result = env.rpc(COMMAND, ctid, to_string(20));
514 
515  // Since we only provided one value for the range,
516  // the interface parses it as a false binary flag,
517  // as single-value ranges are not accepted. Since
518  // the error this causes differs depending on the platform
519  // we don't call out a specific error here.
520  BEAST_EXPECT(result[jss::result][jss::status] == jss::error);
521 
522  BEAST_EXPECT(!result[jss::result].isMember(jss::searched_all));
523  }
524 
525  // Provide an invalid range: (max - min > 1000)
526  {
527  auto const result = env.rpc(
528  COMMAND,
529  ctid,
530  BINARY,
531  to_string(startLegSeq),
532  to_string(startLegSeq + 1001));
533 
534  BEAST_EXPECT(
535  result[jss::result][jss::status] == jss::error &&
536  result[jss::result][jss::error] == EXCESSIVE);
537 
538  BEAST_EXPECT(!result[jss::result].isMember(jss::searched_all));
539  }
540  }
541 
542  void
544  {
545  testcase("ctid_validation");
546 
547  using namespace test::jtx;
548  using std::to_string;
549 
550  Env env{*this, makeNetworkConfig(11111)};
551 
552  // Test case 1: Valid input values
553  auto const expected11 = std::optional<std::string>("CFFFFFFFFFFFFFFF");
554  BEAST_EXPECT(
555  RPC::encodeCTID(0x0FFF'FFFFUL, 0xFFFFU, 0xFFFFU) == expected11);
556  auto const expected12 = std::optional<std::string>("C000000000000000");
557  BEAST_EXPECT(RPC::encodeCTID(0, 0, 0) == expected12);
558  auto const expected13 = std::optional<std::string>("C000000100020003");
559  BEAST_EXPECT(RPC::encodeCTID(1U, 2U, 3U) == expected13);
560  auto const expected14 = std::optional<std::string>("C0CA2AA7326FFFFF");
561  BEAST_EXPECT(RPC::encodeCTID(13249191UL, 12911U, 65535U) == expected14);
562 
563  // Test case 2: ledger_seq greater than 0xFFFFFFF
564  BEAST_EXPECT(!RPC::encodeCTID(0x1000'0000UL, 0xFFFFU, 0xFFFFU));
565 
566  // Test case 3: txn_index greater than 0xFFFF
567  // this test case is impossible in c++ due to the type, left in for
568  // completeness
569  auto const expected3 = std::optional<std::string>("CFFFFFFF0000FFFF");
570  BEAST_EXPECT(
571  RPC::encodeCTID(0x0FFF'FFFF, (uint16_t)0x10000, 0xFFFF) ==
572  expected3);
573 
574  // Test case 4: network_id greater than 0xFFFF
575  // this test case is impossible in c++ due to the type, left in for
576  // completeness
577  auto const expected4 = std::optional<std::string>("CFFFFFFFFFFF0000");
578  BEAST_EXPECT(
579  RPC::encodeCTID(0x0FFF'FFFFUL, 0xFFFFU, (uint16_t)0x1000'0U) ==
580  expected4);
581 
582  // Test case 5: Valid input values
583  auto const expected51 =
584  std::optional<std::tuple<int32_t, uint16_t, uint16_t>>(
585  std::make_tuple(0, 0, 0));
586  BEAST_EXPECT(RPC::decodeCTID("C000000000000000") == expected51);
587  auto const expected52 =
588  std::optional<std::tuple<int32_t, uint16_t, uint16_t>>(
589  std::make_tuple(1U, 2U, 3U));
590  BEAST_EXPECT(RPC::decodeCTID("C000000100020003") == expected52);
591  auto const expected53 =
592  std::optional<std::tuple<int32_t, uint16_t, uint16_t>>(
593  std::make_tuple(13249191UL, 12911U, 49221U));
594  BEAST_EXPECT(RPC::decodeCTID("C0CA2AA7326FC045") == expected53);
595 
596  // Test case 6: ctid not a string or big int
597  BEAST_EXPECT(!RPC::decodeCTID(0xCFF));
598 
599  // Test case 7: ctid not a hexadecimal string
600  BEAST_EXPECT(!RPC::decodeCTID("C003FFFFFFFFFFFG"));
601 
602  // Test case 8: ctid not exactly 16 nibbles
603  BEAST_EXPECT(!RPC::decodeCTID("C003FFFFFFFFFFF"));
604 
605  // Test case 9: ctid too large to be a valid CTID value
606  BEAST_EXPECT(!RPC::decodeCTID("CFFFFFFFFFFFFFFFF"));
607 
608  // Test case 10: ctid doesn't start with a C nibble
609  BEAST_EXPECT(!RPC::decodeCTID("FFFFFFFFFFFFFFFF"));
610 
611  // Test case 11: Valid input values
612  BEAST_EXPECT(
613  (RPC::decodeCTID(0xCFFF'FFFF'FFFF'FFFFULL) ==
614  std::optional<std::tuple<int32_t, uint16_t, uint16_t>>(
615  std::make_tuple(0x0FFF'FFFFUL, 0xFFFFU, 0xFFFFU))));
616  BEAST_EXPECT(
617  (RPC::decodeCTID(0xC000'0000'0000'0000ULL) ==
618  std::optional<std::tuple<int32_t, uint16_t, uint16_t>>(
619  std::make_tuple(0, 0, 0))));
620  BEAST_EXPECT(
621  (RPC::decodeCTID(0xC000'0001'0002'0003ULL) ==
623  std::make_tuple(1U, 2U, 3U))));
624  BEAST_EXPECT(
625  (RPC::decodeCTID(0xC0CA'2AA7'326F'C045ULL) ==
626  std::optional<std::tuple<int32_t, uint16_t, uint16_t>>(
627  std::make_tuple(1324'9191UL, 12911U, 49221U))));
628 
629  // Test case 12: ctid not exactly 16 nibbles
630  BEAST_EXPECT(!RPC::decodeCTID(0xC003'FFFF'FFFF'FFF));
631 
632  // Test case 13: ctid too large to be a valid CTID value
633  // this test case is not possible in c++ because it would overflow the
634  // type, left in for completeness
635  // BEAST_EXPECT(!RPC::decodeCTID(0xCFFFFFFFFFFFFFFFFULL));
636 
637  // Test case 14: ctid doesn't start with a C nibble
638  BEAST_EXPECT(!RPC::decodeCTID(0xFFFF'FFFF'FFFF'FFFFULL));
639  }
640 
641  void
642  testCTIDRPC(FeatureBitset features)
643  {
644  testcase("ctid_rpc");
645 
646  using namespace test::jtx;
647 
648  // test that the ctid AND the hash are in the response
649  {
650  Env env{*this, makeNetworkConfig(11111)};
651  uint32_t netID = env.app().config().NETWORK_ID;
652 
653  auto const alice = Account("alice");
654  auto const bob = Account("bob");
655 
656  auto const startLegSeq = env.current()->info().seq;
657  env.fund(XRP(10000), alice, bob);
658  env(pay(alice, bob, XRP(10)));
659  env.close();
660 
661  auto const ctid = *RPC::encodeCTID(startLegSeq, 0, netID);
662  Json::Value jsonTx;
663  jsonTx[jss::binary] = false;
664  jsonTx[jss::ctid] = ctid;
665  jsonTx[jss::id] = 1;
666  auto jrr = env.rpc("json", "tx", to_string(jsonTx))[jss::result];
667  BEAST_EXPECT(jrr[jss::ctid] == ctid);
668  BEAST_EXPECT(jrr[jss::hash]);
669  }
670 
671  // test that if the network is 65535 the ctid is not in the response
672  {
673  Env env{*this, makeNetworkConfig(65535)};
674  uint32_t netID = env.app().config().NETWORK_ID;
675 
676  auto const alice = Account("alice");
677  auto const bob = Account("bob");
678 
679  auto const startLegSeq = env.current()->info().seq;
680  env.fund(XRP(10000), alice, bob);
681  env(pay(alice, bob, XRP(10)));
682  env.close();
683 
684  auto const ctid = *RPC::encodeCTID(startLegSeq, 0, netID);
685  Json::Value jsonTx;
686  jsonTx[jss::binary] = false;
687  jsonTx[jss::ctid] = ctid;
688  jsonTx[jss::id] = 1;
689  auto jrr = env.rpc("json", "tx", to_string(jsonTx))[jss::result];
690  BEAST_EXPECT(!jrr[jss::ctid]);
691  BEAST_EXPECT(jrr[jss::hash]);
692  }
693  }
694 
695 public:
696  void
697  run() override
698  {
699  using namespace test::jtx;
700  FeatureBitset const all{supported_amendments()};
701  testWithFeats(all);
702  }
703 
704  void
705  testWithFeats(FeatureBitset features)
706  {
707  testRangeRequest(features);
708  testRangeCTIDRequest(features);
709  testCTIDValidation(features);
710  testCTIDRPC(features);
711  }
712 };
713 
714 BEAST_DEFINE_TESTSUITE(Transaction, rpc, ripple);
715 
716 } // namespace ripple
ripple::SQLiteDatabase
Definition: SQLiteDatabase.h:27
std::make_tuple
T make_tuple(T... args)
ripple::RPC::get_error_info
ErrorInfo const & get_error_info(error_code_i code)
Returns an ErrorInfo that reflects the error code.
Definition: ErrorCodes.cpp:171
std::vector
STL class.
std::vector::size
T size(T... args)
ripple::rpcEXCESSIVE_LGR_RANGE
@ rpcEXCESSIVE_LGR_RANGE
Definition: ErrorCodes.h:135
tuple
ripple::Transaction_test::testCTIDValidation
void testCTIDValidation(FeatureBitset features)
Definition: Transaction_test.cpp:543
ripple::RPC::decodeCTID
std::optional< std::tuple< uint32_t, uint16_t, uint16_t > > decodeCTID(const T ctid) noexcept
Definition: CTID.h:54
ripple::Transaction_test
Definition: Transaction_test.cpp:32
ripple::Transaction_test::testRangeCTIDRequest
void testRangeCTIDRequest(FeatureBitset features)
Definition: Transaction_test.cpp:295
std::to_string
T to_string(T... args)
ripple::rpcTXN_NOT_FOUND
@ rpcTXN_NOT_FOUND
Definition: ErrorCodes.h:80
ripple::RPC::encodeCTID
std::optional< std::string > encodeCTID(uint32_t ledger_seq, uint16_t txn_index, uint16_t network_id) noexcept
Definition: CTID.h:34
ripple::sfTransactionIndex
const SF_UINT32 sfTransactionIndex
std::vector::emplace_back
T emplace_back(T... args)
ripple
Use hash_* containers for keys that do not need a cryptographically secure hashing algorithm.
Definition: RCLCensorshipDetector.h:29
ripple::Transaction_test::testRangeRequest
void testRangeRequest(FeatureBitset features)
Definition: Transaction_test.cpp:45
ripple::INVALID
@ INVALID
Definition: Transaction.h:47
ripple::FeatureBitset
Definition: Feature.h:113
optional
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
ripple::strHex
std::string strHex(FwdIt begin, FwdIt end)
Definition: strHex.h:30
ripple::rpcINVALID_LGR_RANGE
@ rpcINVALID_LGR_RANGE
Definition: ErrorCodes.h:136
std::unique_ptr
STL class.
ripple::RPC::ErrorInfo::token
Json::StaticString token
Definition: ErrorCodes.h:201
ripple::Transaction_test::makeNetworkConfig
std::unique_ptr< Config > makeNetworkConfig(uint32_t networkID)
Definition: Transaction_test.cpp:35