diff --git a/src/script/sign.h b/src/script/sign.h --- a/src/script/sign.h +++ b/src/script/sign.h @@ -698,4 +698,12 @@ unsigned int nIn, const CTxOut &txout); void UpdateInput(CTxIn &input, const SignatureData &data); +/** + * Check whether we know how to sign for an output like this, assuming we have + * all private keys. While this function does not need private keys, the passed + * keystore is used to look up public keys and redeemscripts by hash. + * Solvability is unrelated to whether we consider this output to be ours. + */ +bool IsSolvable(const SigningProvider &provider, const CScript &script); + #endif // BITCOIN_SCRIPT_SIGN_H diff --git a/src/script/sign.cpp b/src/script/sign.cpp --- a/src/script/sign.cpp +++ b/src/script/sign.cpp @@ -621,3 +621,20 @@ ret.keys.insert(b.keys.begin(), b.keys.end()); return ret; } + +bool IsSolvable(const SigningProvider &provider, const CScript &script) { + // This check is to make sure that the script we created can actually be + // solved for and signed by us if we were to have the private keys. This is + // just to make sure that the script is valid and that, if found in a + // transaction, we would still accept and relay that transaction. + SignatureData sigs; + if (ProduceSignature(provider, DUMMY_SIGNATURE_CREATOR, script, sigs)) { + // VerifyScript check is just defensive, and should never fail. + bool verified = + VerifyScript(sigs.scriptSig, script, STANDARD_SCRIPT_VERIFY_FLAGS, + DUMMY_CHECKER); + assert(verified); + return true; + } + return false; +} diff --git a/src/test/descriptor_tests.cpp b/src/test/descriptor_tests.cpp --- a/src/test/descriptor_tests.cpp +++ b/src/test/descriptor_tests.cpp @@ -30,7 +30,7 @@ // Derivation needs access to private keys constexpr int HARDENED = 2; // This descriptor is not expected to be solvable -// constexpr int UNSOLVABLE = 4; +constexpr int UNSOLVABLE = 4; // We can sign with this descriptor (this is not true when actual BIP32 // derivation is used, as that's not integrated in our signing code) constexpr int SIGNABLE = 8; @@ -103,6 +103,9 @@ for (size_t n = 0; n < spks.size(); ++n) { BOOST_CHECK_EQUAL(ref[n], HexStr(spks[n].begin(), spks[n].end())); + BOOST_CHECK_EQUAL( + IsSolvable(Merge(key_provider, script_provider), spks[n]), + (flags & UNSOLVABLE) == 0); if (flags & SIGNABLE) { CMutableTransaction spend;