Skip to content

Commit ee41b18

Browse files
Merge #6902: fix: remove incorrect request generation with bad serialization hash
a8506ec test: add tests for `inlock` id generation (UdjinM6) 5456354 fix: trivial safety belt (UdjinM6) 58f671c refactor: trivial s/val/outpoint/ (UdjinM6) f43be22 fix: remove incorrect request generation with bad serialization hash (Kittywhiskers Van Gogh) Pull request description: ## Issue being fixed or feature implemented We should not be generating the hash using a CTxIn, only a COutPoint ## What was done? ## How Has This Been Tested? ## Breaking Changes ## Checklist: - [ ] I have performed a self-review of my own code - [ ] I have commented my code, particularly in hard-to-understand areas - [ ] I have added or updated relevant unit/integration/functional/e2e tests - [ ] I have made corresponding changes to the documentation - [ ] I have assigned this pull request to a milestone _(for repository code-owners and collaborators only)_ ACKs for top commit: PastaPastaPasta: utACK a8506ec kwvg: utACK a8506ec Tree-SHA512: ec3f8eb6700496ad42c0935435c5acae28e3616ebcde8f1d4c6f8e445524f7134451cbda76f3bed2809b422484d8eb6a0c4b02674b1668e0215b069aabcc8982
2 parents 764893d + a8506ec commit ee41b18

File tree

5 files changed

+114
-10
lines changed

5 files changed

+114
-10
lines changed

src/instantsend/instantsend.cpp

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,13 @@ Uint256HashSet GetIdsFromLockable(const std::vector<T>& vec)
4444
if (vec.empty()) return ret;
4545
ret.reserve(vec.size());
4646
for (const auto& in : vec) {
47-
ret.emplace(instantsend::GenInputLockRequestId(in));
47+
if constexpr (std::is_same_v<T, COutPoint>) {
48+
ret.emplace(instantsend::GenInputLockRequestId(in));
49+
} else if constexpr (std::is_same_v<T, CTxIn>) {
50+
ret.emplace(instantsend::GenInputLockRequestId(in.prevout));
51+
} else {
52+
assert(false);
53+
}
4854
}
4955
return ret;
5056
}

src/instantsend/lock.cpp

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -38,11 +38,8 @@ bool InstantSendLock::TriviallyValid() const
3838
return inputs_set.size() == inputs.size();
3939
}
4040

41-
template <typename T>
42-
uint256 GenInputLockRequestId(const T& val)
41+
uint256 GenInputLockRequestId(const COutPoint& outpoint)
4342
{
44-
return ::SerializeHash(std::make_pair(INPUTLOCK_REQUESTID_PREFIX, val));
43+
return ::SerializeHash(std::make_pair(INPUTLOCK_REQUESTID_PREFIX, outpoint));
4544
}
46-
template uint256 GenInputLockRequestId(const COutPoint& val);
47-
template uint256 GenInputLockRequestId(const CTxIn& val);
4845
} // namespace instantsend

src/instantsend/lock.h

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -40,8 +40,7 @@ struct InstantSendLock {
4040
bool TriviallyValid() const;
4141
};
4242

43-
template <typename T>
44-
uint256 GenInputLockRequestId(const T& val);
43+
uint256 GenInputLockRequestId(const COutPoint& outpoint);
4544

4645
using InstantSendLockPtr = std::shared_ptr<InstantSendLock>;
4746
} // namespace instantsend

src/instantsend/signing.cpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -295,7 +295,7 @@ bool InstantSendSigner::TrySignInputLocks(const CTransaction& tx, bool fRetroact
295295

296296
size_t alreadyVotedCount = 0;
297297
for (const auto& in : tx.vin) {
298-
auto id = GenInputLockRequestId(in);
298+
auto id = GenInputLockRequestId(in.prevout);
299299
ids.emplace_back(id);
300300

301301
uint256 otherTxHash;
@@ -344,7 +344,7 @@ void InstantSendSigner::TrySignInstantSendLock(const CTransaction& tx)
344344
const auto llmqType = Params().GetConsensus().llmqTypeDIP0024InstantSend;
345345

346346
for (const auto& in : tx.vin) {
347-
auto id = GenInputLockRequestId(in);
347+
auto id = GenInputLockRequestId(in.prevout);
348348
if (!m_sigman.HasRecoveredSig(llmqType, id, tx.GetHash())) {
349349
return;
350350
}

src/test/evo_islock_tests.cpp

Lines changed: 102 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -107,4 +107,106 @@ BOOST_AUTO_TEST_CASE(deserialize_instantlock_from_realdata2)
107107
BOOST_CHECK_EQUAL(islock.sig.Get().ToString(), expectedSignatureStr);
108108
}
109109

110+
BOOST_AUTO_TEST_CASE(geninputlockrequestid_basic)
111+
{
112+
// Test that GenInputLockRequestId generates consistent hashes for the same outpoint
113+
const uint256 txHash = uint256S("0x1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef");
114+
const uint32_t outputIndex = 5;
115+
116+
COutPoint outpoint1(txHash, outputIndex);
117+
COutPoint outpoint2(txHash, outputIndex);
118+
119+
// Same outpoints should produce identical request IDs
120+
const uint256 requestId1 = instantsend::GenInputLockRequestId(outpoint1);
121+
const uint256 requestId2 = instantsend::GenInputLockRequestId(outpoint2);
122+
123+
BOOST_CHECK(requestId1 == requestId2);
124+
BOOST_CHECK(!requestId1.IsNull());
125+
}
126+
127+
BOOST_AUTO_TEST_CASE(geninputlockrequestid_different_outpoints)
128+
{
129+
// Test that different outpoints produce different request IDs
130+
const uint256 txHash1 = uint256S("0x1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef");
131+
const uint256 txHash2 = uint256S("0xfedcba0987654321fedcba0987654321fedcba0987654321fedcba0987654321");
132+
133+
COutPoint outpoint1(txHash1, 0);
134+
COutPoint outpoint2(txHash2, 0);
135+
COutPoint outpoint3(txHash1, 1); // Same hash, different index
136+
137+
const uint256 requestId1 = instantsend::GenInputLockRequestId(outpoint1);
138+
const uint256 requestId2 = instantsend::GenInputLockRequestId(outpoint2);
139+
const uint256 requestId3 = instantsend::GenInputLockRequestId(outpoint3);
140+
141+
// All should be different
142+
BOOST_CHECK(requestId1 != requestId2);
143+
BOOST_CHECK(requestId1 != requestId3);
144+
BOOST_CHECK(requestId2 != requestId3);
145+
}
146+
147+
BOOST_AUTO_TEST_CASE(geninputlockrequestid_only_outpoint_matters)
148+
{
149+
// Critical test: Verify that only the COutPoint is hashed, not scriptSig or nSequence
150+
// This validates the fix where CTxIn was incorrectly used before
151+
const uint256 txHash = uint256S("0xabcdef1234567890abcdef1234567890abcdef1234567890abcdef1234567890");
152+
const uint32_t outputIndex = 3;
153+
154+
COutPoint outpoint(txHash, outputIndex);
155+
156+
// Create two CTxIn objects with the same prevout but different scriptSig and nSequence
157+
CTxIn txin1;
158+
txin1.prevout = outpoint;
159+
txin1.scriptSig = CScript() << OP_1 << OP_2;
160+
txin1.nSequence = 0xFFFFFFFF;
161+
162+
CTxIn txin2;
163+
txin2.prevout = outpoint;
164+
txin2.scriptSig = CScript() << OP_3 << OP_4 << OP_5; // Different scriptSig
165+
txin2.nSequence = 0x12345678; // Different nSequence
166+
167+
// The request IDs should be identical because they share the same prevout (COutPoint)
168+
const uint256 requestId1 = instantsend::GenInputLockRequestId(txin1.prevout);
169+
const uint256 requestId2 = instantsend::GenInputLockRequestId(txin2.prevout);
170+
171+
BOOST_CHECK(requestId1 == requestId2);
172+
173+
// Also verify against the direct outpoint
174+
const uint256 requestId3 = instantsend::GenInputLockRequestId(outpoint);
175+
BOOST_CHECK(requestId1 == requestId3);
176+
}
177+
178+
BOOST_AUTO_TEST_CASE(geninputlockrequestid_serialization_format)
179+
{
180+
// Test that the serialization format is: SerializeHash(pair("inlock", outpoint))
181+
const uint256 txHash = uint256S("0x0000000000000000000000000000000000000000000000000000000000000001");
182+
const uint32_t outputIndex = 0;
183+
184+
COutPoint outpoint(txHash, outputIndex);
185+
186+
// Calculate the expected hash manually
187+
const uint256 expectedHash = ::SerializeHash(std::make_pair(std::string_view("inlock"), outpoint));
188+
189+
// Get the actual hash from the function
190+
const uint256 actualHash = instantsend::GenInputLockRequestId(outpoint);
191+
192+
BOOST_CHECK(actualHash == expectedHash);
193+
}
194+
195+
BOOST_AUTO_TEST_CASE(geninputlockrequestid_edge_cases)
196+
{
197+
// Test edge cases: null hash, max index
198+
COutPoint nullOutpoint(uint256(), 0);
199+
COutPoint maxIndexOutpoint(uint256::ONE, COutPoint::NULL_INDEX);
200+
201+
const uint256 nullRequestId = instantsend::GenInputLockRequestId(nullOutpoint);
202+
const uint256 maxIndexRequestId = instantsend::GenInputLockRequestId(maxIndexOutpoint);
203+
204+
// Both should produce valid (non-null) request IDs
205+
BOOST_CHECK(!nullRequestId.IsNull());
206+
BOOST_CHECK(!maxIndexRequestId.IsNull());
207+
208+
// And they should be different from each other
209+
BOOST_CHECK(nullRequestId != maxIndexRequestId);
210+
}
211+
110212
BOOST_AUTO_TEST_SUITE_END()

0 commit comments

Comments
 (0)