From 70c8693747c61517cf424120bfd1200faad8bd20 Mon Sep 17 00:00:00 2001 From: Gautam Manchandani Date: Fri, 25 Sep 2026 16:27:47 +0530 Subject: [PATCH 1/2] fix: Reject CTID network IDs above 16 bits --- src/rpc/RPCHelpers.cpp | 4 ++-- src/rpc/RPCHelpers.hpp | 4 ++-- tests/unit/rpc/RPCHelpersTests.cpp | 6 ++++++ tests/unit/rpc/handlers/TxTests.cpp | 32 +++++++++++++++++++++++++++++ 4 files changed, 42 insertions(+), 4 deletions(-) diff --git a/src/rpc/RPCHelpers.cpp b/src/rpc/RPCHelpers.cpp index b8d36f5e2..9223ad8bb 100644 --- a/src/rpc/RPCHelpers.cpp +++ b/src/rpc/RPCHelpers.cpp @@ -236,7 +236,7 @@ toExpandedJson( data::TransactionAndMetadata const& blobs, std::uint32_t const apiVersion, NFTokenjson nftEnabled, - std::optional networkId + std::optional networkId ) { auto [txn, meta] = deserializeTxPlusMeta(blobs, blobs.ledgerSequence); @@ -269,7 +269,7 @@ toExpandedJson( } std::optional -encodeCTID(uint32_t ledgerSeq, uint16_t txnIndex, uint16_t networkId) noexcept +encodeCTID(uint32_t ledgerSeq, uint16_t txnIndex, uint32_t networkId) noexcept { static constexpr uint32_t kMaxLedgerSeq = 0x0FFF'FFFF; static constexpr uint32_t kMaxTxnIndex = 0xFFFF; diff --git a/src/rpc/RPCHelpers.hpp b/src/rpc/RPCHelpers.hpp index d9a61c971..701ca0a5d 100644 --- a/src/rpc/RPCHelpers.hpp +++ b/src/rpc/RPCHelpers.hpp @@ -144,7 +144,7 @@ toExpandedJson( data::TransactionAndMetadata const& blobs, std::uint32_t apiVersion, NFTokenjson nftEnabled = NFTokenjson::DISABLE, - std::optional networkId = std::nullopt + std::optional networkId = std::nullopt ); /** @@ -779,7 +779,7 @@ getNFTID(boost::json::object const& request); * @return The encoded CTID or std::nullopt if the input is invalid */ std::optional -encodeCTID(uint32_t ledgerSeq, uint16_t txnIndex, uint16_t networkId) noexcept; +encodeCTID(uint32_t ledgerSeq, uint16_t txnIndex, uint32_t networkId) noexcept; /** * @brief Decode the CTID from a string or a uint64_t diff --git a/tests/unit/rpc/RPCHelpersTests.cpp b/tests/unit/rpc/RPCHelpersTests.cpp index f1ace39a2..f5f45edc3 100644 --- a/tests/unit/rpc/RPCHelpersTests.cpp +++ b/tests/unit/rpc/RPCHelpersTests.cpp @@ -344,6 +344,12 @@ TEST_F(RPCHelpersTest, EncodeCTID) EXPECT_FALSE(encodeCTID(0x1FFFFFFF, 0x67, 0x89)); } +TEST_F(RPCHelpersTest, EncodeCTIDRejectsNetworkIdAbove16Bits) +{ + uint32_t networkId = 0x10000; + EXPECT_FALSE(encodeCTID(0x1234, 0x67, networkId)); +} + TEST_F(RPCHelpersTest, DecodeCTIDString) { auto const ctid = decodeCTID("C000123400670089"); diff --git a/tests/unit/rpc/handlers/TxTests.cpp b/tests/unit/rpc/handlers/TxTests.cpp index 06611e3af..b09af9680 100644 --- a/tests/unit/rpc/handlers/TxTests.cpp +++ b/tests/unit/rpc/handlers/TxTests.cpp @@ -991,6 +991,38 @@ TEST_F(RPCTxTest, ReturnCTIDForTxInput) }); } +TEST_F(RPCTxTest, OmitCTIDWhenNetworkIdExceeds16Bits) +{ + TransactionAndMetadata tx; + tx.metadata = + createMetaDataForCreateOffer(kCurrency, kAccount, 100, 200, 300).getSerializer().peekData(); + tx.transaction = + createCreateOfferTransactionObject(kAccount, 2, 100, kCurrency, kAccount2, 200, 300) + .getSerializer() + .peekData(); + tx.ledgerSequence = 100; + + EXPECT_CALL(*backend_, fetchTransaction(xrpl::uint256{kTxnId}, _)).WillOnce(Return(tx)); + EXPECT_CALL(*backend_, fetchLedgerBySequence(tx.ledgerSequence, _)) + .WillOnce(Return(std::nullopt)); + + auto const rawETLPtr = dynamic_cast(mockETLServicePtr_.get()); + ASSERT_NE(rawETLPtr, nullptr); + EXPECT_CALL(*rawETLPtr, getETLState).WillOnce(Return(etl::ETLState{.networkID = 0x10000})); + + runSpawn([this](auto yield) { + auto const handler = AnyHandler{TestTxHandler{backend_, mockETLServicePtr_}}; + auto const req = boost::json::parse( + fmt::format(R"JSON({{"command": "tx", "transaction": "{}"}})JSON", kTxnId) + ); + auto const output = handler.process(req, Context{.yield = yield, .apiVersion = 2u}); + ASSERT_TRUE(output); + auto const& result = output.result->as_object(); + EXPECT_FALSE(result.contains("ctid")); + EXPECT_FALSE(result.at("tx_json").as_object().contains("ctid")); + }); +} + TEST_F(RPCTxTest, NotReturnCTIDIfETLNotAvailable) { static constexpr auto kOut = R"JSON({ From 4b6c13db912e8be760e1804a566fad3126d107d8 Mon Sep 17 00:00:00 2001 From: Gautam Manchandani Date: Fri, 2 Oct 2026 22:31:18 +0530 Subject: [PATCH 2/2] style: Make CTID test network ID const --- tests/unit/rpc/RPCHelpersTests.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/unit/rpc/RPCHelpersTests.cpp b/tests/unit/rpc/RPCHelpersTests.cpp index 16c3f3363..c6205701d 100644 --- a/tests/unit/rpc/RPCHelpersTests.cpp +++ b/tests/unit/rpc/RPCHelpersTests.cpp @@ -346,7 +346,7 @@ TEST_F(RPCHelpersTest, EncodeCTID) TEST_F(RPCHelpersTest, EncodeCTIDRejectsNetworkIdAbove16Bits) { - uint32_t networkId = 0x10000; + uint32_t const networkId = 0x10000; EXPECT_FALSE(encodeCTID(0x1234, 0x67, networkId)); }