LCOV - code coverage report
Current view: top level - src/test - evo_islock_tests.cpp (source / functions) Hit Total Coverage
Test: test_dash_coverage.info Lines: 103 103 100.0 %
Date: 2026-06-25 07:23:51 Functions: 25 25 100.0 %

          Line data    Source code
       1             : // Copyright (c) 2025 The Dash Core developers
       2             : // Distributed under the MIT software license, see the accompanying
       3             : // file COPYING or http://www.opensource.org/licenses/mit-license.php.
       4             : 
       5             : #include <consensus/consensus.h>
       6             : #include <hash.h>
       7             : #include <instantsend/lock.h>
       8             : #include <llmq/signhash.h>
       9             : #include <primitives/transaction.h>
      10             : #include <streams.h>
      11             : #include <uint256.h>
      12             : #include <util/strencodings.h>
      13             : 
      14             : #include <string_view>
      15             : 
      16             : #include <boost/test/unit_test.hpp>
      17             : 
      18         146 : BOOST_AUTO_TEST_SUITE(evo_islock_tests)
      19             : 
      20           3 : uint256 CalculateRequestId(const std::vector<COutPoint>& inputs)
      21             : {
      22           3 :     CHashWriter hw(SER_GETHASH, 0);
      23           3 :     hw << std::string_view("islock");
      24           3 :     hw << inputs;
      25           3 :     return hw.GetHash();
      26             : }
      27             : 
      28         148 : BOOST_AUTO_TEST_CASE(getrequestid)
      29             : {
      30             :     // Create an empty InstantSendLock
      31           1 :     instantsend::InstantSendLock islock;
      32             : 
      33             :     // Compute expected hash for an empty inputs vector.
      34             :     // Note: InstantSendLock::GetRequestId() serializes the prefix "islock"
      35             :     // followed by the 'inputs' vector.
      36             :     {
      37           1 :         const uint256 expected = CalculateRequestId(islock.inputs);
      38             : 
      39           1 :         BOOST_CHECK(islock.GetRequestId() == expected);
      40             :     }
      41             : 
      42             :     // Now add two dummy inputs to the lock
      43           1 :     islock.inputs.clear();
      44             :     // Construct two dummy outpoints (using uint256S for a dummy hash)
      45           1 :     COutPoint op1(uint256::ONE, 0);
      46           1 :     COutPoint op2(uint256::TWO, 1);
      47           1 :     islock.inputs.push_back(op1);
      48           1 :     islock.inputs.push_back(op2);
      49             : 
      50           1 :     const uint256 expected = CalculateRequestId(islock.inputs);
      51             : 
      52           1 :     BOOST_CHECK(islock.GetRequestId() == expected);
      53           1 : }
      54             : 
      55         148 : BOOST_AUTO_TEST_CASE(deserialize_instantlock_from_realdata2)
      56             : {
      57             :     // Expected values from the provided getislocks output:
      58           1 :     const std::string_view expectedTxidStr = "7b33968effa613e8ea9c1b5734c9bbbe467ff4650f8060caf8a5c213c6059d5b";
      59           1 :     const std::string_view expectedCycleHashStr = "000000000000000bbd0b1bb95540351e7ee99c5b08efde076b3d712a57ea74d6";
      60             :     const std::string_view expectedSignatureStr =
      61           1 :         "997d0b36738a9eef46ceeb4405998ff7235317708f277402799ffe05258015cae9b6bae"
      62             :         "43683f992b2f50f70f8f0cb9c0f26af340b00903e93995c1345d1b2c5b697ebecdbe581"
      63             :         "1dd112e11889101dcb4553b2bc206ab304026b96c07dec4f24";
      64           1 :     const std::string quorumHashStr = "0000000000000019756ecc9c9c5f476d3f66876b1dcfa5dde1ea82f0d99334a2";
      65           1 :     const std::string_view expectedSignHashStr = "6a3c37bc610c4efd5babd8941068a8eca9e7bec942fe175b8ca9cae31b67e838";
      66             :     // The serialized InstantSend lock from the "hex" field of getislocks:
      67             :     const std::string_view islockHex =
      68           1 :         "0101497915895c30eebfad0c5fcfb9e0e72308c7e92cd3749be2fd49c8320c4c58b6010000005b9d05c613c2a5f8ca60800f65f47f46be"
      69             :         "bbc934571b9ceae813a6ff8e96337bd674ea572a713d6b07deef085b9ce97e1e354055b91b0bbd0b00000000000000997d0b36738a9eef"
      70             :         "46ceeb4405998ff7235317708f277402799ffe05258015cae9b6bae43683f992b2f50f70f8f0cb9c0f26af340b00903e93995c1345d1b2"
      71             :         "c5b697ebecdbe5811dd112e11889101dcb4553b2bc206ab304026b96c07dec4f24";
      72             : 
      73             :     // This islock was created with non-legacy. Using legacy will result in the signature being all zeros.
      74           1 :     bls::bls_legacy_scheme.store(false);
      75             : 
      76             :     // Convert hex string to a byte vector and deserialize.
      77           1 :     std::vector<unsigned char> islockData = ParseHex(islockHex);
      78           1 :     CDataStream ss(islockData, SER_NETWORK, PROTOCOL_VERSION);
      79           1 :     instantsend::InstantSendLock islock;
      80           1 :     ss >> islock;
      81             : 
      82             :     // Verify the calculated signHash
      83             :     auto signHash =
      84           1 :         llmq::SignHash(Consensus::LLMQType::LLMQ_60_75, uint256S(quorumHashStr), islock.GetRequestId(), islock.txid).Get();
      85           1 :     BOOST_CHECK_EQUAL(signHash.ToString(), expectedSignHashStr);
      86             : 
      87             :     // Verify the txid field.
      88           1 :     BOOST_CHECK_EQUAL(islock.txid.ToString(), expectedTxidStr);
      89             : 
      90             :     // Verify the cycleHash field.
      91           1 :     BOOST_CHECK_EQUAL(islock.cycleHash.ToString(), expectedCycleHashStr);
      92             : 
      93             :     // Verify the inputs vector has exactly one element.
      94           1 :     BOOST_REQUIRE_EQUAL(islock.inputs.size(), 1U);
      95           1 :     const COutPoint& input = islock.inputs.front();
      96           1 :     const std::string expectedInputTxid = "b6584c0c32c849fde29b74d32ce9c70823e7e0b9cf5f0cadbfee305c89157949";
      97           1 :     const unsigned int expectedInputN = 1;
      98           1 :     BOOST_CHECK_EQUAL(input.hash.ToString(), expectedInputTxid);
      99           1 :     BOOST_CHECK_EQUAL(input.n, expectedInputN);
     100             : 
     101             :     // Compute the expected request ID: it is the hash of the constant prefix "islock" followed by the inputs.
     102           1 :     uint256 expectedRequestId = CalculateRequestId(islock.inputs);
     103           1 :     BOOST_CHECK_EQUAL(islock.GetRequestId().ToString(), expectedRequestId.ToString());
     104             : 
     105             :     // Verify the signature field.
     106           1 :     BOOST_CHECK_EQUAL(islock.sig.Get().ToString(), expectedSignatureStr);
     107           1 : }
     108             : 
     109         148 : BOOST_AUTO_TEST_CASE(geninputlockrequestid_basic)
     110             : {
     111             :     // Test that GenInputLockRequestId generates consistent hashes for the same outpoint
     112           1 :     const uint256 txHash = uint256S("0x1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef");
     113           1 :     const uint32_t outputIndex = 5;
     114             : 
     115           1 :     COutPoint outpoint1(txHash, outputIndex);
     116           1 :     COutPoint outpoint2(txHash, outputIndex);
     117             : 
     118             :     // Same outpoints should produce identical request IDs
     119           1 :     const uint256 requestId1 = instantsend::GenInputLockRequestId(outpoint1);
     120           1 :     const uint256 requestId2 = instantsend::GenInputLockRequestId(outpoint2);
     121             : 
     122           1 :     BOOST_CHECK(requestId1 == requestId2);
     123           1 :     BOOST_CHECK(!requestId1.IsNull());
     124           1 : }
     125             : 
     126         148 : BOOST_AUTO_TEST_CASE(geninputlockrequestid_different_outpoints)
     127             : {
     128             :     // Test that different outpoints produce different request IDs
     129           1 :     const uint256 txHash1 = uint256S("0x1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef");
     130           1 :     const uint256 txHash2 = uint256S("0xfedcba0987654321fedcba0987654321fedcba0987654321fedcba0987654321");
     131             : 
     132           1 :     COutPoint outpoint1(txHash1, 0);
     133           1 :     COutPoint outpoint2(txHash2, 0);
     134           1 :     COutPoint outpoint3(txHash1, 1); // Same hash, different index
     135             : 
     136           1 :     const uint256 requestId1 = instantsend::GenInputLockRequestId(outpoint1);
     137           1 :     const uint256 requestId2 = instantsend::GenInputLockRequestId(outpoint2);
     138           1 :     const uint256 requestId3 = instantsend::GenInputLockRequestId(outpoint3);
     139             : 
     140             :     // All should be different
     141           1 :     BOOST_CHECK(requestId1 != requestId2);
     142           1 :     BOOST_CHECK(requestId1 != requestId3);
     143           1 :     BOOST_CHECK(requestId2 != requestId3);
     144           1 : }
     145             : 
     146         148 : BOOST_AUTO_TEST_CASE(geninputlockrequestid_only_outpoint_matters)
     147             : {
     148             :     // Critical test: Verify that only the COutPoint is hashed, not scriptSig or nSequence
     149             :     // This validates the fix where CTxIn was incorrectly used before
     150           1 :     const uint256 txHash = uint256S("0xabcdef1234567890abcdef1234567890abcdef1234567890abcdef1234567890");
     151           1 :     const uint32_t outputIndex = 3;
     152             : 
     153           1 :     COutPoint outpoint(txHash, outputIndex);
     154             : 
     155             :     // Create two CTxIn objects with the same prevout but different scriptSig and nSequence
     156           1 :     CTxIn txin1;
     157           1 :     txin1.prevout = outpoint;
     158           1 :     txin1.scriptSig = CScript() << OP_1 << OP_2;
     159           1 :     txin1.nSequence = 0xFFFFFFFF;
     160             : 
     161           1 :     CTxIn txin2;
     162           1 :     txin2.prevout = outpoint;
     163           1 :     txin2.scriptSig = CScript() << OP_3 << OP_4 << OP_5; // Different scriptSig
     164           1 :     txin2.nSequence = 0x12345678;                        // Different nSequence
     165             : 
     166             :     // The request IDs should be identical because they share the same prevout (COutPoint)
     167           1 :     const uint256 requestId1 = instantsend::GenInputLockRequestId(txin1.prevout);
     168           1 :     const uint256 requestId2 = instantsend::GenInputLockRequestId(txin2.prevout);
     169             : 
     170           1 :     BOOST_CHECK(requestId1 == requestId2);
     171             : 
     172             :     // Also verify against the direct outpoint
     173           1 :     const uint256 requestId3 = instantsend::GenInputLockRequestId(outpoint);
     174           1 :     BOOST_CHECK(requestId1 == requestId3);
     175           1 : }
     176             : 
     177         148 : BOOST_AUTO_TEST_CASE(geninputlockrequestid_serialization_format)
     178             : {
     179             :     // Test that the serialization format is: SerializeHash(pair("inlock", outpoint))
     180           1 :     const uint256 txHash = uint256S("0x0000000000000000000000000000000000000000000000000000000000000001");
     181           1 :     const uint32_t outputIndex = 0;
     182             : 
     183           1 :     COutPoint outpoint(txHash, outputIndex);
     184             : 
     185             :     // Calculate the expected hash manually
     186           1 :     const uint256 expectedHash = ::SerializeHash(std::make_pair(std::string_view("inlock"), outpoint));
     187             : 
     188             :     // Get the actual hash from the function
     189           1 :     const uint256 actualHash = instantsend::GenInputLockRequestId(outpoint);
     190             : 
     191           1 :     BOOST_CHECK(actualHash == expectedHash);
     192           1 : }
     193             : 
     194         148 : BOOST_AUTO_TEST_CASE(geninputlockrequestid_edge_cases)
     195             : {
     196             :     // Test edge cases: null hash, max index
     197           1 :     COutPoint nullOutpoint(uint256(), 0);
     198           1 :     COutPoint maxIndexOutpoint(uint256::ONE, COutPoint::NULL_INDEX);
     199             : 
     200           1 :     const uint256 nullRequestId = instantsend::GenInputLockRequestId(nullOutpoint);
     201           1 :     const uint256 maxIndexRequestId = instantsend::GenInputLockRequestId(maxIndexOutpoint);
     202             : 
     203             :     // Both should produce valid (non-null) request IDs
     204           1 :     BOOST_CHECK(!nullRequestId.IsNull());
     205           1 :     BOOST_CHECK(!maxIndexRequestId.IsNull());
     206             : 
     207             :     // And they should be different from each other
     208           1 :     BOOST_CHECK(nullRequestId != maxIndexRequestId);
     209           1 : }
     210             : 
     211         146 : BOOST_AUTO_TEST_SUITE_END()

Generated by: LCOV version 1.16