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/protocol/serialize.h>
25 #include <ripple/rpc/CTID.h>
27 #include <test/jtx/Env.h>
28 #include <test/jtx/envconfig.h>
40 using namespace test::jtx;
42 cfg->NETWORK_ID = networkID;
50 testcase(
"Test Range Request");
52 using namespace test::jtx;
55 const char* COMMAND = jss::tx.c_str();
56 const char* BINARY = jss::binary.c_str();
59 const char* EXCESSIVE =
62 Env env{*
this, features};
63 auto const alice = Account(
"alice");
64 env.fund(XRP(1000), alice);
69 auto const startLegSeq = env.current()->info().seq;
70 for (
int i = 0; i < 750; ++i)
76 env.closed()->txRead(env.tx()->getTransactionID()).second);
78 auto const endLegSeq = env.closed()->info().seq;
81 for (
size_t i = 0; i < txns.
size(); ++i)
83 auto const& tx = txns[i];
84 auto const& meta = metas[i];
85 auto const result = env.rpc(
92 BEAST_EXPECT(result[jss::result][jss::status] == jss::success);
94 result[jss::result][jss::tx] ==
95 strHex(tx->getSerializer().getData()));
97 result[jss::result][jss::meta] ==
98 strHex(meta->getSerializer().getData()));
101 auto const tx = env.jt(noop(alice), seq(env.seq(alice))).stx;
102 for (
int deltaEndSeq = 0; deltaEndSeq < 2; ++deltaEndSeq)
104 auto const result = env.rpc(
112 result[jss::result][jss::status] == jss::error &&
113 result[jss::result][jss::error] == NOT_FOUND);
116 BEAST_EXPECT(!result[jss::result][jss::searched_all].asBool());
118 BEAST_EXPECT(result[jss::result][jss::searched_all].asBool());
122 for (
auto&& tx : txns)
124 auto const result = env.rpc(
131 BEAST_EXPECT(result[jss::result][jss::status] == jss::success);
132 BEAST_EXPECT(!result[jss::result][jss::searched_all].asBool());
135 const auto deletedLedger = (startLegSeq + endLegSeq) / 2;
139 ->deleteTransactionByLedgerSeq(deletedLedger);
142 for (
int deltaEndSeq = 0; deltaEndSeq < 2; ++deltaEndSeq)
144 auto const result = env.rpc(
152 result[jss::result][jss::status] == jss::error &&
153 result[jss::result][jss::error] == NOT_FOUND);
154 BEAST_EXPECT(!result[jss::result][jss::searched_all].asBool());
160 auto const result = env.rpc(
167 result[jss::result][jss::status] == jss::error &&
168 result[jss::result][jss::error] == NOT_FOUND);
170 BEAST_EXPECT(!result[jss::result][jss::searched_all].asBool());
176 auto const result = env.rpc(
183 result[jss::result][jss::status] == jss::error &&
184 result[jss::result][jss::error] == NOT_FOUND);
186 BEAST_EXPECT(result[jss::result][jss::searched_all].asBool());
192 auto const result = env.rpc(
198 BEAST_EXPECT(result[jss::result][jss::status] == jss::success);
199 BEAST_EXPECT(!result[jss::result].isMember(jss::searched_all));
204 auto const result = env.rpc(
212 result[jss::result][jss::status] == jss::error &&
213 result[jss::result][jss::error] ==
INVALID);
215 BEAST_EXPECT(!result[jss::result].isMember(jss::searched_all));
220 auto const result = env.rpc(
228 result[jss::result][jss::status] == jss::error &&
229 result[jss::result][jss::error] ==
INVALID);
231 BEAST_EXPECT(!result[jss::result].isMember(jss::searched_all));
236 auto const result = env.rpc(
244 result[jss::result][jss::status] == jss::error &&
245 result[jss::result][jss::error] ==
INVALID);
247 BEAST_EXPECT(!result[jss::result].isMember(jss::searched_all));
252 auto const result = env.rpc(
259 result[jss::result][jss::status] == jss::error &&
260 result[jss::result][jss::error] ==
INVALID);
262 BEAST_EXPECT(!result[jss::result].isMember(jss::searched_all));
267 auto const result = env.rpc(
275 BEAST_EXPECT(result[jss::result][jss::status] == jss::error);
277 BEAST_EXPECT(!result[jss::result].isMember(jss::searched_all));
282 auto const result = env.rpc(
290 result[jss::result][jss::status] == jss::error &&
291 result[jss::result][jss::error] == EXCESSIVE);
293 BEAST_EXPECT(!result[jss::result].isMember(jss::searched_all));
300 testcase(
"ctid_range");
302 using namespace test::jtx;
305 const char* COMMAND = jss::tx.c_str();
306 const char* BINARY = jss::binary.c_str();
309 const char* EXCESSIVE =
313 uint32_t netID = env.app().config().NETWORK_ID;
315 auto const alice = Account(
"alice");
316 env.fund(XRP(1000), alice);
321 auto const startLegSeq = env.current()->info().seq;
322 for (
int i = 0; i < 750; ++i)
328 env.closed()->txRead(env.tx()->getTransactionID()).second);
330 auto const endLegSeq = env.closed()->info().seq;
333 for (
size_t i = 0; i < txns.
size(); ++i)
335 auto const& tx = txns[i];
336 auto const& meta = metas[i];
338 auto const result = env.rpc(
345 BEAST_EXPECT(result[jss::result][jss::status] == jss::success);
347 result[jss::result][jss::tx] ==
348 strHex(tx->getSerializer().getData()));
350 result[jss::result][jss::meta] ==
351 strHex(meta->getSerializer().getData()));
354 auto const tx = env.jt(noop(alice), seq(env.seq(alice))).stx;
357 for (
int deltaEndSeq = 0; deltaEndSeq < 2; ++deltaEndSeq)
359 auto const result = env.rpc(
367 result[jss::result][jss::status] == jss::error &&
368 result[jss::result][jss::error] == NOT_FOUND);
371 BEAST_EXPECT(!result[jss::result][jss::searched_all].asBool());
373 BEAST_EXPECT(!result[jss::result][jss::searched_all].asBool());
377 for (
size_t i = 0; i < txns.
size(); ++i)
380 auto const& meta = metas[i];
382 auto const result = env.rpc(
389 BEAST_EXPECT(result[jss::result][jss::status] == jss::success);
390 BEAST_EXPECT(!result[jss::result][jss::searched_all].asBool());
393 const auto deletedLedger = (startLegSeq + endLegSeq) / 2;
397 ->deleteTransactionByLedgerSeq(deletedLedger);
400 for (
int deltaEndSeq = 0; deltaEndSeq < 2; ++deltaEndSeq)
402 auto const result = env.rpc(
410 result[jss::result][jss::status] == jss::error &&
411 result[jss::result][jss::error] == NOT_FOUND);
412 BEAST_EXPECT(!result[jss::result][jss::searched_all].asBool());
418 auto const result = env.rpc(
422 result[jss::result][jss::status] == jss::error &&
423 result[jss::result][jss::error] == NOT_FOUND);
425 BEAST_EXPECT(!result[jss::result][jss::searched_all].asBool());
431 auto const result = env.rpc(
438 result[jss::result][jss::status] == jss::error &&
439 result[jss::result][jss::error] == NOT_FOUND);
441 BEAST_EXPECT(!result[jss::result][jss::searched_all].asBool());
447 auto const& meta = metas[0];
449 auto const result = env.rpc(
455 BEAST_EXPECT(result[jss::result][jss::status] == jss::success);
456 BEAST_EXPECT(!result[jss::result].isMember(jss::searched_all));
461 auto const result = env.rpc(
469 result[jss::result][jss::status] == jss::error &&
470 result[jss::result][jss::error] ==
INVALID);
472 BEAST_EXPECT(!result[jss::result].isMember(jss::searched_all));
477 auto const result = env.rpc(
485 result[jss::result][jss::status] == jss::error &&
486 result[jss::result][jss::error] ==
INVALID);
488 BEAST_EXPECT(!result[jss::result].isMember(jss::searched_all));
497 result[jss::result][jss::status] == jss::error &&
498 result[jss::result][jss::error] ==
INVALID);
500 BEAST_EXPECT(!result[jss::result].isMember(jss::searched_all));
505 auto const result = env.rpc(COMMAND, ctid, BINARY,
to_string(20));
508 result[jss::result][jss::status] == jss::error &&
509 result[jss::result][jss::error] ==
INVALID);
511 BEAST_EXPECT(!result[jss::result].isMember(jss::searched_all));
516 auto const result = env.rpc(COMMAND, ctid,
to_string(20));
523 BEAST_EXPECT(result[jss::result][jss::status] == jss::error);
525 BEAST_EXPECT(!result[jss::result].isMember(jss::searched_all));
530 auto const result = env.rpc(
538 result[jss::result][jss::status] == jss::error &&
539 result[jss::result][jss::error] == EXCESSIVE);
541 BEAST_EXPECT(!result[jss::result].isMember(jss::searched_all));
548 testcase(
"ctid_validation");
550 using namespace test::jtx;
559 auto const expected12 = std::optional<std::string>("C000000000000000");
560 BEAST_EXPECT(RPC::encodeCTID(0, 0, 0) == expected12);
561 auto const expected13 = std::optional<std::string>("C000000100020003");
562 BEAST_EXPECT(RPC::encodeCTID(1U, 2U, 3U) == expected13);
563 auto const expected14 = std::optional<std::string>("C0CA2AA7326FFFFF");
564 BEAST_EXPECT(RPC::encodeCTID(13249191UL, 12911U, 65535U) == expected14);
566 // Test case 2: ledger_seq greater than 0xFFFFFFF
567 BEAST_EXPECT(!RPC::encodeCTID(0x1000'0000UL, 0xFFFFU, 0xFFFFU));
577 // Test case 4: network_id greater than 0xFFFF
578 // this test case is impossible in c++ due to the type, left in for
580 auto const expected4 = std::optional<std::string>("CFFFFFFFFFFF0000");
582 RPC::encodeCTID(0x0FFF'FFFFUL, 0xFFFFU, (uint16_t)0x1000
'0U) ==
585 // Test case 5: Valid input values
586 auto const expected51 =
587 std::optional<std::tuple<int32_t, uint16_t, uint16_t>>(
588 std::make_tuple(0, 0, 0));
589 BEAST_EXPECT(RPC::decodeCTID("C000000000000000") == expected51);
590 auto const expected52 =
591 std::optional<std::tuple<int32_t, uint16_t, uint16_t>>(
592 std::make_tuple(1U, 2U, 3U));
593 BEAST_EXPECT(RPC::decodeCTID("C000000100020003") == expected52);
594 auto const expected53 =
595 std::optional<std::tuple<int32_t, uint16_t, uint16_t>>(
596 std::make_tuple(13249191UL, 12911U, 49221U));
597 BEAST_EXPECT(RPC::decodeCTID("C0CA2AA7326FC045") == expected53);
599 // Test case 6: ctid not a string or big int
600 BEAST_EXPECT(!RPC::decodeCTID(0xCFF));
602 // Test case 7: ctid not a hexadecimal string
603 BEAST_EXPECT(!RPC::decodeCTID("C003FFFFFFFFFFFG"));
605 // Test case 8: ctid not exactly 16 nibbles
606 BEAST_EXPECT(!RPC::decodeCTID("C003FFFFFFFFFFF"));
608 // Test case 9: ctid too large to be a valid CTID value
609 BEAST_EXPECT(!RPC::decodeCTID("CFFFFFFFFFFFFFFFF"));
611 // Test case 10: ctid doesn't start with a C nibble
617 std::optional<std::tuple<int32_t, uint16_t, uint16_t>>(
618 std::make_tuple(0x0FFF'FFFFUL, 0xFFFFU, 0xFFFFU))));
621 std::optional<std::tuple<int32_t, uint16_t, uint16_t>>(
622 std::make_tuple(0, 0, 0))));
624 (RPC::decodeCTID(0xC000'0001
'0002'0003ULL) ==
629 std::optional<std::tuple<int32_t, uint16_t, uint16_t>>(
630 std::make_tuple(1324'9191UL, 12911U, 49221U))));
635 // Test case 13: ctid too large to be a valid CTID value
636 // this test case is not possible in c++ because it would overflow the
637 // type, left in for completeness
638 // BEAST_EXPECT(!RPC::decodeCTID(0xCFFFFFFFFFFFFFFFFULL));
640 // Test case 14: ctid doesn't start with a C nibble
645 testCTIDRPC(FeatureBitset features)
647 testcase("ctid_rpc");
649 using namespace test::jtx;
651 // test that the ctid AND the hash are in the response
653 Env env{*this, makeNetworkConfig(11111)};
654 uint32_t netID = env.app().config().NETWORK_ID;
656 auto const alice = Account("alice");
657 auto const bob = Account("bob");
659 auto const startLegSeq = env.current()->info().seq;
660 env.fund(XRP(10000), alice, bob);
661 env(pay(alice, bob, XRP(10)));
664 auto const ctid = *RPC::encodeCTID(startLegSeq, 0, netID);
666 jsonTx[jss::binary] = false;
667 jsonTx[jss::ctid] = ctid;
669 auto jrr = env.rpc("json", "tx", to_string(jsonTx))[jss::result];
670 BEAST_EXPECT(jrr[jss::ctid] == ctid);
671 BEAST_EXPECT(jrr[jss::hash]);
674 // test that if the network is 65535 the ctid is not in the response
676 Env env{*this, makeNetworkConfig(65535)};
677 uint32_t netID = env.app().config().NETWORK_ID;
679 auto const alice = Account("alice");
680 auto const bob = Account("bob");
682 auto const startLegSeq = env.current()->info().seq;
683 env.fund(XRP(10000), alice, bob);
684 env(pay(alice, bob, XRP(10)));
687 auto const ctid = *RPC::encodeCTID(startLegSeq, 0, netID);
689 jsonTx[jss::binary] = false;
690 jsonTx[jss::ctid] = ctid;
692 auto jrr = env.rpc("json", "tx", to_string(jsonTx))[jss::result];
693 BEAST_EXPECT(!jrr[jss::ctid]);
694 BEAST_EXPECT(jrr[jss::hash]);
699 testRequest(FeatureBitset features, unsigned apiVersion)
701 testcase("Test Request API version " + std::to_string(apiVersion));
703 using namespace test::jtx;
704 using std::to_string;
707 Account const alice{"alice"};
708 Account const alie{"alie"};
709 Account const gw{"gw"};
710 auto const USD{gw["USD"]};
712 env.fund(XRP(1000000), alice, gw);
719 env(pay(alice, gw, XRP(100)));
721 std::shared_ptr<STTx const> txn = env.tx();
723 std::shared_ptr<STObject const> meta =
724 env.closed()->txRead(env.tx()->getTransactionID()).second;
726 Json::Value expected = txn->getJson(JsonOptions::none);
727 expected[jss::DeliverMax] = expected[jss::Amount];
730 expected.removeMember(jss::hash);
731 expected.removeMember(jss::Amount);
734 Json::Value const result = {[&env, txn, apiVersion]() {
735 Json::Value params{Json::objectValue};
736 params[jss::transaction] = to_string(txn->getTransactionID());
737 params[jss::binary] = false;
738 params[jss::api_version] = apiVersion;
739 return env.client().invoke("tx", params);
742 BEAST_EXPECT(result[jss::result][jss::status] == jss::success);
746 result[jss::result][jss::close_time_iso] ==
747 "2000-01-01T00:00:20Z");
749 result[jss::result][jss::hash] ==
750 to_string(txn->getTransactionID()));
751 BEAST_EXPECT(result[jss::result][jss::validated] == true);
752 BEAST_EXPECT(result[jss::result][jss::ledger_index] == 4);
754 result[jss::result][jss::ledger_hash] ==
755 "B41882E20F0EC6228417D28B9AE0F33833645D35F6799DFB782AC97FC4BB51"
759 for (auto memberIt = expected.begin(); memberIt != expected.end();
762 std::string const name = memberIt.memberName();
763 auto const& result_transaction =
764 (apiVersion > 1 ? result[jss::result][jss::tx_json]
765 : result[jss::result]);
766 if (BEAST_EXPECT(result_transaction.isMember(name)))
768 auto const received = result_transaction[name];
770 received == *memberIt,
771 "Transaction contains \n\"" + name + "\": " //
772 + to_string(received) //
773 + " but expected " //
774 + to_string(expected));
780 testBinaryRequest(unsigned apiVersion)
783 "Test binary request API version " + std::to_string(apiVersion));
785 using namespace test::jtx;
786 using std::to_string;
789 Account const alice{"alice"};
790 Account const gw{"gw"};
791 auto const USD{gw["USD"]};
793 env.fund(XRP(1000000), alice, gw);
794 std::shared_ptr<STTx const> const txn = env.tx();
796 to_string(txn->getTransactionID()) ==
797 "3F8BDE5A5F82C4F4708E5E9255B713E303E6E1A371FD5C7A704AFD1387C23981");
799 std::shared_ptr<STObject const> meta =
800 env.closed()->txRead(txn->getTransactionID()).second;
802 std::string const expected_tx_blob = serializeHex(*txn);
803 std::string const expected_meta_blob = serializeHex(*meta);
805 Json::Value const result = [&env, txn, apiVersion]() {
806 Json::Value params{Json::objectValue};
807 params[jss::transaction] = to_string(txn->getTransactionID());
808 params[jss::binary] = true;
809 params[jss::api_version] = apiVersion;
810 return env.client().invoke("tx", params);
813 if (BEAST_EXPECT(result[jss::status] == "success"))
815 BEAST_EXPECT(result[jss::result][jss::status] == "success");
816 BEAST_EXPECT(result[jss::result][jss::validated] == true);
818 result[jss::result][jss::hash] ==
819 to_string(txn->getTransactionID()));
820 BEAST_EXPECT(result[jss::result][jss::ledger_index] == 3);
821 BEAST_EXPECT(result[jss::result][jss::ctid] == "C000000300030000");
826 result[jss::result][jss::tx_blob] == expected_tx_blob);
828 result[jss::result][jss::meta_blob] == expected_meta_blob);
830 result[jss::result][jss::ledger_hash] ==
831 "2D5150E5A5AA436736A732291E437ABF01BC9E206C2DF3C77C4F856915"
834 result[jss::result][jss::close_time_iso] ==
835 "2000-01-01T00:00:10Z");
839 BEAST_EXPECT(result[jss::result][jss::tx] == expected_tx_blob);
841 result[jss::result][jss::meta] == expected_meta_blob);
842 BEAST_EXPECT(result[jss::result][jss::date] == 10);
851 using namespace test::jtx;
852 test::jtx::forAllApiVersions(
853 std::bind_front(&Transaction_test::testBinaryRequest, this));
855 FeatureBitset const all{supported_amendments()};
860 testWithFeats(FeatureBitset features)
862 testRangeRequest(features);
863 testRangeCTIDRequest(features);
864 testCTIDValidation(features);
865 testCTIDRPC(features);
866 test::jtx::forAllApiVersions(
867 std::bind_front(&Transaction_test::testRequest, this, features));
871 BEAST_DEFINE_TESTSUITE(Transaction, rpc, ripple);
873 } // namespace ripple