Skip to content

Commit f9032a4

Browse files
committed
Merge bitcoin/bitcoin#31242: wallet, desc spkm: Return SigningProvider only if we have the privkey
f6a6d91 test: add check for getting SigningProvider for a CPubKey (Sebastian Falbesoner) 62a95f5 test: refactor: move `CreateDescriptor` helper to wallet test util module (Sebastian Falbesoner) 4936567 desc spkm: Return SigningProvider only if we have the privkey (Ava Chow) Pull request description: If we know about a pubkey that's in our descriptor, but we don't have the private key, don't return a SigningProvider for that pubkey. This is specifically an issue for Taproot outputs that use the H point as the resulting PSBTs may end up containing irrelevant information because the H point was detected as a pubkey each unrelated descriptor knew about. Split from #29675 ACKs for top commit: fjahr: ACK f6a6d91 theStack: re-ACK f6a6d91 furszy: utACK f6a6d91. Only reviewed the actual change in detail, not the test commit. Tree-SHA512: 30a196e611a0c5d9ebe5baf6d896caaa6af66f1615463dbb0c31e52604d53cf342922bb9967b3c697b47083d76b0485c77a5f545bd6381247c8bc44321c70f97
2 parents 9dc4eed + f6a6d91 commit f9032a4

File tree

8 files changed

+60
-24
lines changed

8 files changed

+60
-24
lines changed

src/script/signingprovider.cpp

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,11 @@ bool FlatSigningProvider::GetKeyOrigin(const CKeyID& keyid, KeyOriginInfo& info)
6262
if (ret) info = std::move(out.second);
6363
return ret;
6464
}
65+
bool FlatSigningProvider::HaveKey(const CKeyID &keyid) const
66+
{
67+
CKey key;
68+
return LookupHelper(keys, keyid, key);
69+
}
6570
bool FlatSigningProvider::GetKey(const CKeyID& keyid, CKey& key) const { return LookupHelper(keys, keyid, key); }
6671
bool FlatSigningProvider::GetTaprootSpendData(const XOnlyPubKey& output_key, TaprootSpendData& spenddata) const
6772
{

src/script/signingprovider.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -215,6 +215,7 @@ struct FlatSigningProvider final : public SigningProvider
215215
bool GetCScript(const CScriptID& scriptid, CScript& script) const override;
216216
bool GetPubKey(const CKeyID& keyid, CPubKey& pubkey) const override;
217217
bool GetKeyOrigin(const CKeyID& keyid, KeyOriginInfo& info) const override;
218+
bool HaveKey(const CKeyID &keyid) const override;
218219
bool GetKey(const CKeyID& keyid, CKey& key) const override;
219220
bool GetTaprootSpendData(const XOnlyPubKey& output_key, TaprootSpendData& spenddata) const override;
220221
bool GetTaprootBuilder(const XOnlyPubKey& output_key, TaprootBuilder& builder) const override;

src/wallet/scriptpubkeyman.cpp

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2468,7 +2468,11 @@ std::unique_ptr<FlatSigningProvider> DescriptorScriptPubKeyMan::GetSigningProvid
24682468
int32_t index = it->second;
24692469

24702470
// Always try to get the signing provider with private keys. This function should only be called during signing anyways
2471-
return GetSigningProvider(index, true);
2471+
std::unique_ptr<FlatSigningProvider> out = GetSigningProvider(index, true);
2472+
if (!out->HaveKey(pubkey.GetID())) {
2473+
return nullptr;
2474+
}
2475+
return out;
24722476
}
24732477

24742478
std::unique_ptr<FlatSigningProvider> DescriptorScriptPubKeyMan::GetSigningProvider(int32_t index, bool include_private) const

src/wallet/scriptpubkeyman.h

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -613,8 +613,6 @@ class DescriptorScriptPubKeyMan : public ScriptPubKeyMan
613613
mutable std::map<int32_t, FlatSigningProvider> m_map_signing_providers;
614614
// Fetch the SigningProvider for the given script and optionally include private keys
615615
std::unique_ptr<FlatSigningProvider> GetSigningProvider(const CScript& script, bool include_private = false) const;
616-
// Fetch the SigningProvider for the given pubkey and always include private keys. This should only be called by signing code.
617-
std::unique_ptr<FlatSigningProvider> GetSigningProvider(const CPubKey& pubkey) const;
618616
// Fetch the SigningProvider for a given index and optionally include private keys. Called by the above functions.
619617
std::unique_ptr<FlatSigningProvider> GetSigningProvider(int32_t index, bool include_private = false) const EXCLUSIVE_LOCKS_REQUIRED(cs_desc_man);
620618

@@ -678,6 +676,9 @@ class DescriptorScriptPubKeyMan : public ScriptPubKeyMan
678676

679677
bool CanProvide(const CScript& script, SignatureData& sigdata) override;
680678

679+
// Fetch the SigningProvider for the given pubkey and always include private keys. This should only be called by signing code.
680+
std::unique_ptr<FlatSigningProvider> GetSigningProvider(const CPubKey& pubkey) const;
681+
681682
bool SignTransaction(CMutableTransaction& tx, const std::map<COutPoint, Coin>& coins, int sighash, std::map<int, bilingual_str>& input_errors) const override;
682683
SigningResult SignMessage(const std::string& message, const PKHash& pkhash, std::string& str_sig) const override;
683684
std::optional<common::PSBTError> FillPSBT(PartiallySignedTransaction& psbt, const PrecomputedTransactionData& txdata, int sighash_type = SIGHASH_DEFAULT, bool sign = true, bool bip32derivs = false, int* n_signed = nullptr, bool finalize = true) const override;

src/wallet/test/ismine_tests.cpp

Lines changed: 0 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -20,26 +20,6 @@ using namespace util::hex_literals;
2020
namespace wallet {
2121
BOOST_FIXTURE_TEST_SUITE(ismine_tests, BasicTestingSetup)
2222

23-
wallet::ScriptPubKeyMan* CreateDescriptor(CWallet& keystore, const std::string& desc_str, const bool success)
24-
{
25-
keystore.SetWalletFlag(WALLET_FLAG_DESCRIPTORS);
26-
27-
FlatSigningProvider keys;
28-
std::string error;
29-
auto parsed_descs = Parse(desc_str, keys, error, false);
30-
BOOST_CHECK(success == (!parsed_descs.empty()));
31-
if (!success) return nullptr;
32-
auto& desc = parsed_descs.at(0);
33-
34-
const int64_t range_start = 0, range_end = 1, next_index = 0, timestamp = 1;
35-
36-
WalletDescriptor w_desc(std::move(desc), timestamp, range_start, range_end, next_index);
37-
38-
LOCK(keystore.cs_wallet);
39-
40-
return Assert(keystore.AddWalletDescriptor(w_desc, keys,/*label=*/"", /*internal=*/false));
41-
};
42-
4323
BOOST_AUTO_TEST_CASE(ismine_standard)
4424
{
4525
CKey keys[2];

src/wallet/test/scriptpubkeyman_tests.cpp

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
44

55
#include <key.h>
6+
#include <key_io.h>
67
#include <test/util/setup_common.h>
78
#include <script/solver.h>
89
#include <wallet/scriptpubkeyman.h>
@@ -40,5 +41,27 @@ BOOST_AUTO_TEST_CASE(CanProvide)
4041
BOOST_CHECK(keyman.CanProvide(p2sh_script, data));
4142
}
4243

44+
BOOST_AUTO_TEST_CASE(DescriptorScriptPubKeyManTests)
45+
{
46+
std::unique_ptr<interfaces::Chain>& chain = m_node.chain;
47+
48+
CWallet keystore(chain.get(), "", CreateMockableWalletDatabase());
49+
auto key_scriptpath = GenerateRandomKey();
50+
51+
// Verify that a SigningProvider for a pubkey is only returned if its corresponding private key is available
52+
auto key_internal = GenerateRandomKey();
53+
std::string desc_str = "tr(" + EncodeSecret(key_internal) + ",pk(" + HexStr(key_scriptpath.GetPubKey()) + "))";
54+
auto spk_man1 = dynamic_cast<DescriptorScriptPubKeyMan*>(CreateDescriptor(keystore, desc_str, true));
55+
BOOST_CHECK(spk_man1 != nullptr);
56+
auto signprov_keypath_spendable = spk_man1->GetSigningProvider(key_internal.GetPubKey());
57+
BOOST_CHECK(signprov_keypath_spendable != nullptr);
58+
59+
desc_str = "tr(" + HexStr(XOnlyPubKey::NUMS_H) + ",pk(" + HexStr(key_scriptpath.GetPubKey()) + "))";
60+
auto spk_man2 = dynamic_cast<DescriptorScriptPubKeyMan*>(CreateDescriptor(keystore, desc_str, true));
61+
BOOST_CHECK(spk_man2 != nullptr);
62+
auto signprov_keypath_nums_h = spk_man2->GetSigningProvider(XOnlyPubKey::NUMS_H.GetEvenCorrespondingCPubKey());
63+
BOOST_CHECK(signprov_keypath_nums_h == nullptr);
64+
}
65+
4366
BOOST_AUTO_TEST_SUITE_END()
4467
} // namespace wallet

src/wallet/test/util.cpp

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -192,4 +192,24 @@ MockableDatabase& GetMockableDatabase(CWallet& wallet)
192192
{
193193
return dynamic_cast<MockableDatabase&>(wallet.GetDatabase());
194194
}
195+
196+
wallet::ScriptPubKeyMan* CreateDescriptor(CWallet& keystore, const std::string& desc_str, const bool success)
197+
{
198+
keystore.SetWalletFlag(WALLET_FLAG_DESCRIPTORS);
199+
200+
FlatSigningProvider keys;
201+
std::string error;
202+
auto parsed_descs = Parse(desc_str, keys, error, false);
203+
Assert(success == (!parsed_descs.empty()));
204+
if (!success) return nullptr;
205+
auto& desc = parsed_descs.at(0);
206+
207+
const int64_t range_start = 0, range_end = 1, next_index = 0, timestamp = 1;
208+
209+
WalletDescriptor w_desc(std::move(desc), timestamp, range_start, range_end, next_index);
210+
211+
LOCK(keystore.cs_wallet);
212+
213+
return Assert(keystore.AddWalletDescriptor(w_desc, keys,/*label=*/"", /*internal=*/false));
214+
};
195215
} // namespace wallet

src/wallet/test/util.h

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99

1010
#include <addresstype.h>
1111
#include <wallet/db.h>
12+
#include <wallet/scriptpubkeyman.h>
1213

1314
#include <memory>
1415

@@ -127,8 +128,9 @@ class MockableDatabase : public WalletDatabase
127128
};
128129

129130
std::unique_ptr<WalletDatabase> CreateMockableWalletDatabase(MockableData records = {});
130-
131131
MockableDatabase& GetMockableDatabase(CWallet& wallet);
132+
133+
ScriptPubKeyMan* CreateDescriptor(CWallet& keystore, const std::string& desc_str, const bool success);
132134
} // namespace wallet
133135

134136
#endif // BITCOIN_WALLET_TEST_UTIL_H

0 commit comments

Comments
 (0)