20 #include <ripple/app/rdb/backend/SQLiteDatabase.h>
21 #include <ripple/protocol/ErrorCodes.h>
22 #include <ripple/protocol/STBase.h>
23 #include <ripple/protocol/jss.h>
24 #include <ripple/rpc/CTID.h>
27 #include <test/jtx/Env.h>
28 #include <test/jtx/envconfig.h>
38 using namespace test::jtx;
40 cfg->NETWORK_ID = networkID;
48 testcase(
"Test Range Request");
50 using namespace test::jtx;
53 const char* COMMAND = jss::tx.c_str();
54 const char* BINARY = jss::binary.c_str();
57 const char* EXCESSIVE =
60 Env env{*
this, features};
61 auto const alice = Account(
"alice");
62 env.fund(XRP(1000), alice);
67 auto const startLegSeq = env.current()->info().seq;
68 for (
int i = 0; i < 750; ++i)
74 env.closed()->txRead(env.tx()->getTransactionID()).second);
76 auto const endLegSeq = env.closed()->info().seq;
79 for (
size_t i = 0; i < txns.
size(); ++i)
81 auto const& tx = txns[i];
82 auto const& meta = metas[i];
83 auto const result = env.rpc(
90 BEAST_EXPECT(result[jss::result][jss::status] == jss::success);
92 result[jss::result][jss::tx] ==
93 strHex(tx->getSerializer().getData()));
95 result[jss::result][jss::meta] ==
96 strHex(meta->getSerializer().getData()));
99 auto const tx = env.jt(noop(alice), seq(env.seq(alice))).stx;
100 for (
int deltaEndSeq = 0; deltaEndSeq < 2; ++deltaEndSeq)
102 auto const result = env.rpc(
110 result[jss::result][jss::status] == jss::error &&
111 result[jss::result][jss::error] == NOT_FOUND);
114 BEAST_EXPECT(!result[jss::result][jss::searched_all].asBool());
116 BEAST_EXPECT(result[jss::result][jss::searched_all].asBool());
120 for (
auto&& tx : txns)
122 auto const result = env.rpc(
129 BEAST_EXPECT(result[jss::result][jss::status] == jss::success);
130 BEAST_EXPECT(!result[jss::result][jss::searched_all].asBool());
133 const auto deletedLedger = (startLegSeq + endLegSeq) / 2;
137 ->deleteTransactionByLedgerSeq(deletedLedger);
140 for (
int deltaEndSeq = 0; deltaEndSeq < 2; ++deltaEndSeq)
142 auto const result = env.rpc(
150 result[jss::result][jss::status] == jss::error &&
151 result[jss::result][jss::error] == NOT_FOUND);
152 BEAST_EXPECT(!result[jss::result][jss::searched_all].asBool());
158 auto const result = env.rpc(
165 result[jss::result][jss::status] == jss::error &&
166 result[jss::result][jss::error] == NOT_FOUND);
168 BEAST_EXPECT(!result[jss::result][jss::searched_all].asBool());
174 auto const result = env.rpc(
181 result[jss::result][jss::status] == jss::error &&
182 result[jss::result][jss::error] == NOT_FOUND);
184 BEAST_EXPECT(result[jss::result][jss::searched_all].asBool());
190 auto const result = env.rpc(
196 BEAST_EXPECT(result[jss::result][jss::status] == jss::success);
197 BEAST_EXPECT(!result[jss::result].isMember(jss::searched_all));
202 auto const result = env.rpc(
210 result[jss::result][jss::status] == jss::error &&
211 result[jss::result][jss::error] ==
INVALID);
213 BEAST_EXPECT(!result[jss::result].isMember(jss::searched_all));
218 auto const result = env.rpc(
226 result[jss::result][jss::status] == jss::error &&
227 result[jss::result][jss::error] ==
INVALID);
229 BEAST_EXPECT(!result[jss::result].isMember(jss::searched_all));
234 auto const result = env.rpc(
242 result[jss::result][jss::status] == jss::error &&
243 result[jss::result][jss::error] ==
INVALID);
245 BEAST_EXPECT(!result[jss::result].isMember(jss::searched_all));
250 auto const result = env.rpc(
257 result[jss::result][jss::status] == jss::error &&
258 result[jss::result][jss::error] ==
INVALID);
260 BEAST_EXPECT(!result[jss::result].isMember(jss::searched_all));
265 auto const result = env.rpc(
273 BEAST_EXPECT(result[jss::result][jss::status] == jss::error);
275 BEAST_EXPECT(!result[jss::result].isMember(jss::searched_all));
280 auto const result = env.rpc(
288 result[jss::result][jss::status] == jss::error &&
289 result[jss::result][jss::error] == EXCESSIVE);
291 BEAST_EXPECT(!result[jss::result].isMember(jss::searched_all));
298 testcase(
"ctid_range");
300 using namespace test::jtx;
303 const char* COMMAND = jss::tx.c_str();
304 const char* BINARY = jss::binary.c_str();
307 const char* EXCESSIVE =
311 uint32_t netID = env.app().config().NETWORK_ID;
313 auto const alice = Account(
"alice");
314 env.fund(XRP(1000), alice);
319 auto const startLegSeq = env.current()->info().seq;
320 for (
int i = 0; i < 750; ++i)
326 env.closed()->txRead(env.tx()->getTransactionID()).second);
328 auto const endLegSeq = env.closed()->info().seq;
331 for (
size_t i = 0; i < txns.
size(); ++i)
333 auto const& tx = txns[i];
334 auto const& meta = metas[i];
336 auto const result = env.rpc(
343 BEAST_EXPECT(result[jss::result][jss::status] == jss::success);
345 result[jss::result][jss::tx] ==
346 strHex(tx->getSerializer().getData()));
348 result[jss::result][jss::meta] ==
349 strHex(meta->getSerializer().getData()));
352 auto const tx = env.jt(noop(alice), seq(env.seq(alice))).stx;
355 for (
int deltaEndSeq = 0; deltaEndSeq < 2; ++deltaEndSeq)
357 auto const result = env.rpc(
365 result[jss::result][jss::status] == jss::error &&
366 result[jss::result][jss::error] == NOT_FOUND);
369 BEAST_EXPECT(!result[jss::result][jss::searched_all].asBool());
371 BEAST_EXPECT(!result[jss::result][jss::searched_all].asBool());
375 for (
size_t i = 0; i < txns.
size(); ++i)
378 auto const& meta = metas[i];
380 auto const result = env.rpc(
387 BEAST_EXPECT(result[jss::result][jss::status] == jss::success);
388 BEAST_EXPECT(!result[jss::result][jss::searched_all].asBool());
391 const auto deletedLedger = (startLegSeq + endLegSeq) / 2;
395 ->deleteTransactionByLedgerSeq(deletedLedger);
398 for (
int deltaEndSeq = 0; deltaEndSeq < 2; ++deltaEndSeq)
400 auto const result = env.rpc(
408 result[jss::result][jss::status] == jss::error &&
409 result[jss::result][jss::error] == NOT_FOUND);
410 BEAST_EXPECT(!result[jss::result][jss::searched_all].asBool());
416 auto const result = env.rpc(
420 result[jss::result][jss::status] == jss::error &&
421 result[jss::result][jss::error] == NOT_FOUND);
423 BEAST_EXPECT(!result[jss::result][jss::searched_all].asBool());
429 auto const result = env.rpc(
436 result[jss::result][jss::status] == jss::error &&
437 result[jss::result][jss::error] == NOT_FOUND);
439 BEAST_EXPECT(!result[jss::result][jss::searched_all].asBool());
445 auto const& meta = metas[0];
447 auto const result = env.rpc(
453 BEAST_EXPECT(result[jss::result][jss::status] == jss::success);
454 BEAST_EXPECT(!result[jss::result].isMember(jss::searched_all));
459 auto const result = env.rpc(
467 result[jss::result][jss::status] == jss::error &&
468 result[jss::result][jss::error] ==
INVALID);
470 BEAST_EXPECT(!result[jss::result].isMember(jss::searched_all));
475 auto const result = env.rpc(
483 result[jss::result][jss::status] == jss::error &&
484 result[jss::result][jss::error] ==
INVALID);
486 BEAST_EXPECT(!result[jss::result].isMember(jss::searched_all));
495 result[jss::result][jss::status] == jss::error &&
496 result[jss::result][jss::error] ==
INVALID);
498 BEAST_EXPECT(!result[jss::result].isMember(jss::searched_all));
503 auto const result = env.rpc(COMMAND, ctid, BINARY,
to_string(20));
506 result[jss::result][jss::status] == jss::error &&
507 result[jss::result][jss::error] ==
INVALID);
509 BEAST_EXPECT(!result[jss::result].isMember(jss::searched_all));
514 auto const result = env.rpc(COMMAND, ctid,
to_string(20));
521 BEAST_EXPECT(result[jss::result][jss::status] == jss::error);
523 BEAST_EXPECT(!result[jss::result].isMember(jss::searched_all));
528 auto const result = env.rpc(
536 result[jss::result][jss::status] == jss::error &&
537 result[jss::result][jss::error] == EXCESSIVE);
539 BEAST_EXPECT(!result[jss::result].isMember(jss::searched_all));
546 testcase(
"ctid_validation");
548 using namespace test::jtx;
557 auto const expected12 = std::optional<std::string>("C000000000000000");
558 BEAST_EXPECT(RPC::encodeCTID(0, 0, 0) == expected12);
559 auto const expected13 = std::optional<std::string>("C000000100020003");
560 BEAST_EXPECT(RPC::encodeCTID(1U, 2U, 3U) == expected13);
561 auto const expected14 = std::optional<std::string>("C0CA2AA7326FFFFF");
562 BEAST_EXPECT(RPC::encodeCTID(13249191UL, 12911U, 65535U) == expected14);
564 // Test case 2: ledger_seq greater than 0xFFFFFFF
565 BEAST_EXPECT(!RPC::encodeCTID(0x1000'0000UL, 0xFFFFU, 0xFFFFU));
575 // Test case 4: network_id greater than 0xFFFF
576 // this test case is impossible in c++ due to the type, left in for
578 auto const expected4 = std::optional<std::string>("CFFFFFFFFFFF0000");
580 RPC::encodeCTID(0x0FFF'FFFFUL, 0xFFFFU, (uint16_t)0x1000
'0U) ==
583 // Test case 5: Valid input values
584 auto const expected51 =
585 std::optional<std::tuple<int32_t, uint16_t, uint16_t>>(
586 std::make_tuple(0, 0, 0));
587 BEAST_EXPECT(RPC::decodeCTID("C000000000000000") == expected51);
588 auto const expected52 =
589 std::optional<std::tuple<int32_t, uint16_t, uint16_t>>(
590 std::make_tuple(1U, 2U, 3U));
591 BEAST_EXPECT(RPC::decodeCTID("C000000100020003") == expected52);
592 auto const expected53 =
593 std::optional<std::tuple<int32_t, uint16_t, uint16_t>>(
594 std::make_tuple(13249191UL, 12911U, 49221U));
595 BEAST_EXPECT(RPC::decodeCTID("C0CA2AA7326FC045") == expected53);
597 // Test case 6: ctid not a string or big int
598 BEAST_EXPECT(!RPC::decodeCTID(0xCFF));
600 // Test case 7: ctid not a hexadecimal string
601 BEAST_EXPECT(!RPC::decodeCTID("C003FFFFFFFFFFFG"));
603 // Test case 8: ctid not exactly 16 nibbles
604 BEAST_EXPECT(!RPC::decodeCTID("C003FFFFFFFFFFF"));
606 // Test case 9: ctid too large to be a valid CTID value
607 BEAST_EXPECT(!RPC::decodeCTID("CFFFFFFFFFFFFFFFF"));
609 // Test case 10: ctid doesn't start with a C nibble
615 std::optional<std::tuple<int32_t, uint16_t, uint16_t>>(
616 std::make_tuple(0x0FFF'FFFFUL, 0xFFFFU, 0xFFFFU))));
619 std::optional<std::tuple<int32_t, uint16_t, uint16_t>>(
620 std::make_tuple(0, 0, 0))));
622 (RPC::decodeCTID(0xC000'0001
'0002'0003ULL) ==
627 std::optional<std::tuple<int32_t, uint16_t, uint16_t>>(
628 std::make_tuple(1324'9191UL, 12911U, 49221U))));
633 // Test case 13: ctid too large to be a valid CTID value
634 // this test case is not possible in c++ because it would overflow the
635 // type, left in for completeness
636 // BEAST_EXPECT(!RPC::decodeCTID(0xCFFFFFFFFFFFFFFFFULL));
638 // Test case 14: ctid doesn't start with a C nibble
643 testCTIDRPC(FeatureBitset features)
645 testcase("ctid_rpc");
647 using namespace test::jtx;
649 // test that the ctid AND the hash are in the response
651 Env env{*this, makeNetworkConfig(11111)};
652 uint32_t netID = env.app().config().NETWORK_ID;
654 auto const alice = Account("alice");
655 auto const bob = Account("bob");
657 auto const startLegSeq = env.current()->info().seq;
658 env.fund(XRP(10000), alice, bob);
659 env(pay(alice, bob, XRP(10)));
662 auto const ctid = *RPC::encodeCTID(startLegSeq, 0, netID);
664 jsonTx[jss::binary] = false;
665 jsonTx[jss::ctid] = ctid;
667 auto jrr = env.rpc("json", "tx", to_string(jsonTx))[jss::result];
668 BEAST_EXPECT(jrr[jss::ctid] == ctid);
669 BEAST_EXPECT(jrr[jss::hash]);
672 // test that if the network is 65535 the ctid is not in the response
674 Env env{*this, makeNetworkConfig(65535)};
675 uint32_t netID = env.app().config().NETWORK_ID;
677 auto const alice = Account("alice");
678 auto const bob = Account("bob");
680 auto const startLegSeq = env.current()->info().seq;
681 env.fund(XRP(10000), alice, bob);
682 env(pay(alice, bob, XRP(10)));
685 auto const ctid = *RPC::encodeCTID(startLegSeq, 0, netID);
687 jsonTx[jss::binary] = false;
688 jsonTx[jss::ctid] = ctid;
690 auto jrr = env.rpc("json", "tx", to_string(jsonTx))[jss::result];
691 BEAST_EXPECT(!jrr[jss::ctid]);
692 BEAST_EXPECT(jrr[jss::hash]);
697 testRequest(FeatureBitset features, unsigned apiVersion)
699 testcase("Test Request API version " + std::to_string(apiVersion));
701 using namespace test::jtx;
702 using std::to_string;
705 Account const alice{"alice"};
706 Account const alie{"alie"};
707 Account const gw{"gw"};
708 auto const USD{gw["USD"]};
710 env.fund(XRP(1000000), alice, gw);
717 env(pay(alice, gw, XRP(100)));
719 std::shared_ptr<STTx const> txn = env.tx();
721 std::shared_ptr<STObject const> meta =
722 env.closed()->txRead(env.tx()->getTransactionID()).second;
724 Json::Value expected = txn->getJson(JsonOptions::none);
725 expected[jss::DeliverMax] = expected[jss::Amount];
728 expected.removeMember(jss::hash);
729 expected.removeMember(jss::Amount);
732 Json::Value const result = {[&env, txn, apiVersion]() {
733 Json::Value params{Json::objectValue};
734 params[jss::transaction] = to_string(txn->getTransactionID());
735 params[jss::binary] = false;
736 params[jss::api_version] = apiVersion;
737 return env.client().invoke("tx", params);
740 BEAST_EXPECT(result[jss::result][jss::status] == jss::success);
744 result[jss::result][jss::close_time_iso] ==
745 "2000-01-01T00:00:20Z");
747 result[jss::result][jss::hash] ==
748 to_string(txn->getTransactionID()));
749 BEAST_EXPECT(result[jss::result][jss::validated] == true);
750 BEAST_EXPECT(result[jss::result][jss::ledger_index] == 4);
752 result[jss::result][jss::ledger_hash] ==
753 "B41882E20F0EC6228417D28B9AE0F33833645D35F6799DFB782AC97FC4BB51"
757 for (auto memberIt = expected.begin(); memberIt != expected.end();
760 std::string const name = memberIt.memberName();
761 auto const& result_transaction =
762 (apiVersion > 1 ? result[jss::result][jss::tx_json]
763 : result[jss::result]);
764 if (BEAST_EXPECT(result_transaction.isMember(name)))
766 auto const received = result_transaction[name];
768 received == *memberIt,
769 "Transaction contains \n\"" + name + "\": " //
770 + to_string(received) //
771 + " but expected " //
772 + to_string(expected));
781 using namespace test::jtx;
782 FeatureBitset const all{supported_amendments()};
787 testWithFeats(FeatureBitset features)
789 testRangeRequest(features);
790 testRangeCTIDRequest(features);
791 testCTIDValidation(features);
792 testCTIDRPC(features);
793 test::jtx::forAllApiVersions(
794 std::bind_front(&Transaction_test::testRequest, this, features));
798 BEAST_DEFINE_TESTSUITE(Transaction, rpc, ripple);
800 } // namespace ripple