From b3026b10dc9e075d116528435008d8aad25a04d5 Mon Sep 17 00:00:00 2001 From: Sjors Provoost Date: Tue, 6 Jan 2026 18:41:26 +0700 Subject: [PATCH 1/2] fuzz: add HasPrivKey miniscript helper --- src/test/fuzz/miniscript.cpp | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/src/test/fuzz/miniscript.cpp b/src/test/fuzz/miniscript.cpp index edc9772a9ccd..0b7fa8e14423 100644 --- a/src/test/fuzz/miniscript.cpp +++ b/src/test/fuzz/miniscript.cpp @@ -106,6 +106,12 @@ struct TestData { return &it->second; } } + + //! Whether signing material is available for this pubkey in this script context. + bool HasPrivKey(const MsCtx script_ctx, const Key& key) const { + const auto sig_ptr = GetSig(script_ctx, key); + return sig_ptr && sig_ptr->second; + } } TEST_DATA; /** @@ -1162,8 +1168,7 @@ void TestNode(const MsCtx script_ctx, const NodeRef& node, FuzzedDataProvider& p // are identical, this implies that for such nodes, the non-malleable // satisfaction will also match the expected policy. const auto is_key_satisfiable = [script_ctx](const CPubKey& pubkey) -> bool { - auto sig_ptr{TEST_DATA.GetSig(script_ctx, pubkey)}; - return sig_ptr != nullptr && sig_ptr->second; + return TEST_DATA.HasPrivKey(script_ctx, pubkey); }; bool satisfiable = node->IsSatisfiable([&](const Node& node) -> bool { switch (node.fragment) { From fa022433cc4b4b6601a5965e88c8fe34b428f9a1 Mon Sep 17 00:00:00 2001 From: Sjors Provoost Date: Wed, 7 Jan 2026 14:32:31 +0700 Subject: [PATCH 2/2] fuzz: verify has_priv_key Use HasPrivKey to ensure the correct return value for has_priv_key. Add coverage for it in the harness. --- src/test/fuzz/miniscript.cpp | 28 ++++++++++++++++++++-------- 1 file changed, 20 insertions(+), 8 deletions(-) diff --git a/src/test/fuzz/miniscript.cpp b/src/test/fuzz/miniscript.cpp index 0b7fa8e14423..57aae0ef3eb9 100644 --- a/src/test/fuzz/miniscript.cpp +++ b/src/test/fuzz/miniscript.cpp @@ -132,13 +132,10 @@ struct ParserContext { std::optional ToString(const Key& key, bool& has_priv_key) const { - has_priv_key = false; - auto it = TEST_DATA.dummy_key_idx_map.find(key); - if (it == TEST_DATA.dummy_key_idx_map.end()) { - return HexStr(key); - } - has_priv_key = true; - uint8_t idx = it->second; + const auto it = TEST_DATA.dummy_key_idx_map.find(key); + assert(it != TEST_DATA.dummy_key_idx_map.end()); + has_priv_key = TEST_DATA.HasPrivKey(script_ctx, key); + const uint8_t idx = it->second; return HexStr(std::span{&idx, 1}); } @@ -1043,8 +1040,23 @@ void TestNode(const MsCtx script_ctx, const NodeRef& node, FuzzedDataProvider& p // Check that it roundtrips to text representation const ParserContext parser_ctx{script_ctx}; - std::optional str{node->ToString(parser_ctx)}; + bool has_priv_key{false}; + std::optional str{node->ToString(parser_ctx, has_priv_key)}; assert(str); + + // Check has_priv_key against expectation + bool expected_has_priv_key{false}; + std::vector nodes{node.get()}; + while (!nodes.empty() && !expected_has_priv_key) { + const Node* n{nodes.back()}; + nodes.pop_back(); + expected_has_priv_key = std::any_of(n->keys.begin(), n->keys.end(), [&](const auto& key) { + return TEST_DATA.HasPrivKey(script_ctx, key); + }); + for (const auto& sub : n->subs) nodes.push_back(sub.get()); + } + assert(has_priv_key == expected_has_priv_key); + auto parsed = miniscript::FromString(*str, parser_ctx); assert(parsed); assert(*parsed == *node);