Skip to content

Commit d7f56cc

Browse files
committed
Merge bitcoin/bitcoin#31590: descriptors: Try pubkeys of both parities when retrieving the private keys for an xonly pubkey in a descriptor
c0045e6 Add test for multipath miniscript expression (David Gumberg) b4ac480 descriptor: Use InferXOnlyPubkey for miniscript XOnly pubkey from script (Ava Chow) 4c50c21 tests: Check ExpandPrivate matches for both parsed descriptors (Ava Chow) 092569e descriptor: Try the other parity in ConstPubkeyProvider::GetPrivKey() (Ava Chow) Pull request description: When a `ConstPubkeyProvider` is xonly, the stored pubkey does not necessarily have the correct parity bit. `ToPrivateString()` is correctly handling this by looking up the keys for both parity bits, but `GetPrivKey` does not. This results in not finding the private key when it is actually available if its pubkey has the other parity bit value. To fix this, this key finding is refactored into `GetPrivKey()` so that its behavior is corrected, and `ToPrivateString()` is changed to use `GetPrivKey()` as well. Additionally, the descriptor test checks are updated to include a check for `ExpandPrivate()` to verify that both the parsed public and private descriptors produce `SigningProvider`s with the same contents. Fixes #31589 ACKs for top commit: Pttn: ACK c0045e6 davidgumberg: utACK bitcoin/bitcoin@c0045e6 kevkevinpal: Concept and Code review ACK [c0045e6](bitcoin/bitcoin@c0045e6) furszy: ACK c0045e6 theStack: re-ACK c0045e6 rkrux: Concept ACK c0045e6 Tree-SHA512: 3dcf2a802b996e0680a3f819075e5a689eb22e484c81ea79b40ec04197ee4ba3f6b9c87c45dfe8a847c9b805b2fd0fad77ffb92a93e65dc3aad74d69d9e3d97f
2 parents 4601b7c + c0045e6 commit d7f56cc

File tree

3 files changed

+49
-11
lines changed

3 files changed

+49
-11
lines changed

src/script/descriptor.cpp

Lines changed: 4 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -312,15 +312,7 @@ class ConstPubkeyProvider final : public PubkeyProvider
312312
bool ToPrivateString(const SigningProvider& arg, std::string& ret) const override
313313
{
314314
CKey key;
315-
if (m_xonly) {
316-
for (const auto& keyid : XOnlyPubKey(m_pubkey).GetKeyIDs()) {
317-
arg.GetKey(keyid, key);
318-
if (key.IsValid()) break;
319-
}
320-
} else {
321-
arg.GetKey(m_pubkey.GetID(), key);
322-
}
323-
if (!key.IsValid()) return false;
315+
if (!GetPrivKey(/*pos=*/0, arg, key)) return false;
324316
ret = EncodeSecret(key);
325317
return true;
326318
}
@@ -331,7 +323,8 @@ class ConstPubkeyProvider final : public PubkeyProvider
331323
}
332324
bool GetPrivKey(int pos, const SigningProvider& arg, CKey& key) const override
333325
{
334-
return arg.GetKey(m_pubkey.GetID(), key);
326+
return m_xonly ? arg.GetKeyByXOnly(XOnlyPubKey(m_pubkey), key) :
327+
arg.GetKey(m_pubkey.GetID(), key);
335328
}
336329
std::optional<CPubKey> GetRootPubKey() const override
337330
{
@@ -1716,7 +1709,7 @@ struct KeyParser {
17161709
if (miniscript::IsTapscript(m_script_ctx) && end - begin == 32) {
17171710
XOnlyPubKey pubkey;
17181711
std::copy(begin, end, pubkey.begin());
1719-
if (auto pubkey_provider = InferPubkey(pubkey.GetEvenCorrespondingCPubKey(), ParseContext(), *m_in)) {
1712+
if (auto pubkey_provider = InferXOnlyPubkey(pubkey, ParseContext(), *m_in)) {
17201713
m_keys.emplace_back();
17211714
m_keys.back().push_back(std::move(pubkey_provider));
17221715
return key;

src/script/signingprovider.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -136,6 +136,8 @@ class TaprootBuilder
136136
std::vector<std::tuple<uint8_t, uint8_t, std::vector<unsigned char>>> GetTreeTuples() const;
137137
/** Returns true if there are any tapscripts */
138138
bool HasScripts() const { return !m_branch.empty(); }
139+
140+
bool operator==(const TaprootBuilder& other) const { return GetTreeTuples() == other.GetTreeTuples(); }
139141
};
140142

141143
/** Given a TaprootSpendData and the output key, reconstruct its script tree.

src/test/descriptor_tests.cpp

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,15 @@ bool EqualDescriptor(std::string a, std::string b)
6262
return a == b;
6363
}
6464

65+
bool EqualSigningProviders(const FlatSigningProvider& a, const FlatSigningProvider& b)
66+
{
67+
return a.scripts == b.scripts
68+
&& a.pubkeys == b.pubkeys
69+
&& a.origins == b.origins
70+
&& a.keys == b.keys
71+
&& a.tr_trees == b.tr_trees;
72+
}
73+
6574
std::string UseHInsteadOfApostrophe(const std::string& desc)
6675
{
6776
std::string ret = desc;
@@ -214,6 +223,15 @@ void DoCheck(std::string prv, std::string pub, const std::string& norm_pub, int
214223
BOOST_CHECK_MESSAGE(EqualDescriptor(prv, prv1), "Private ser: " + prv1 + " Private desc: " + prv);
215224
}
216225
BOOST_CHECK(!parse_pub->ToPrivateString(keys_pub, prv1));
226+
227+
// Check that both can ExpandPrivate and get the same SigningProviders
228+
FlatSigningProvider priv_prov;
229+
parse_priv->ExpandPrivate(0, keys_priv, priv_prov);
230+
231+
FlatSigningProvider pub_prov;
232+
parse_pub->ExpandPrivate(0, keys_priv, pub_prov);
233+
234+
BOOST_CHECK_MESSAGE(EqualSigningProviders(priv_prov, pub_prov), "Private desc: " + prv + " Pub desc: " + pub);
217235
}
218236

219237
// Check that private can produce the normalized descriptors
@@ -808,6 +826,31 @@ BOOST_AUTO_TEST_CASE(descriptor_test)
808826
{{0x80000000UL + 48, 0x80000000UL + 1, 0x80000000UL, 0x80000000UL + 2, 1, 0}, {0x80000000UL + 48, 0x80000000UL + 1, 0x80000000UL, 0x80000000UL + 2, 1, 1}, {0x80000000UL + 48, 0x80000000UL + 1, 0x80000000UL, 0x80000000UL + 2, 1, 2}},
809827
}
810828
);
829+
CheckMultipath("tr(xprv9yYge4PS54XkYT9KiLfCRwc8Jeuz8DucxQGtuEecJZYhKNiqbPxYHTPzXtskmzWBqdqkRAGsghNmZzNsfU2wstaB3XjDQFPv567aQSSuPyo,l:pk(xprvA1ADjaN8H3HGnZSmt4VF7YdWoV9oNq8jhqhurxsrYycBAFK555cECoaY22KWt6BTRNLuvobW5VQTF89PN3iA485LAg7epazevPyjCa4xTzd/<2;3>))",
830+
"tr(xpub6CY33ZvKuS63kwDnpNCCo5YrrgkUXgdUKdCVhd4Dru5gCB3z8wGnqFiUP98Za5pYSYF5KmvBHTY3Ra8FAJGggzBjuHS69WzN8gscPupuZwK,l:pk(xpub6E9a95u27Qqa13XEz62FUgaFMWzHnHrb54dWfMHU7K9A33eDccvUkbu1sHYoByHAgJdR326rWqn9pGZgZHz1afDprW5gGwS4gUX8Ri6aGPZ/<2;3>))",
831+
{
832+
"tr(xprv9yYge4PS54XkYT9KiLfCRwc8Jeuz8DucxQGtuEecJZYhKNiqbPxYHTPzXtskmzWBqdqkRAGsghNmZzNsfU2wstaB3XjDQFPv567aQSSuPyo,l:pk(xprvA1ADjaN8H3HGnZSmt4VF7YdWoV9oNq8jhqhurxsrYycBAFK555cECoaY22KWt6BTRNLuvobW5VQTF89PN3iA485LAg7epazevPyjCa4xTzd/2))",
833+
"tr(xprv9yYge4PS54XkYT9KiLfCRwc8Jeuz8DucxQGtuEecJZYhKNiqbPxYHTPzXtskmzWBqdqkRAGsghNmZzNsfU2wstaB3XjDQFPv567aQSSuPyo,l:pk(xprvA1ADjaN8H3HGnZSmt4VF7YdWoV9oNq8jhqhurxsrYycBAFK555cECoaY22KWt6BTRNLuvobW5VQTF89PN3iA485LAg7epazevPyjCa4xTzd/3))",
834+
},
835+
{
836+
"tr(xpub6CY33ZvKuS63kwDnpNCCo5YrrgkUXgdUKdCVhd4Dru5gCB3z8wGnqFiUP98Za5pYSYF5KmvBHTY3Ra8FAJGggzBjuHS69WzN8gscPupuZwK,l:pk(xpub6E9a95u27Qqa13XEz62FUgaFMWzHnHrb54dWfMHU7K9A33eDccvUkbu1sHYoByHAgJdR326rWqn9pGZgZHz1afDprW5gGwS4gUX8Ri6aGPZ/2))",
837+
"tr(xpub6CY33ZvKuS63kwDnpNCCo5YrrgkUXgdUKdCVhd4Dru5gCB3z8wGnqFiUP98Za5pYSYF5KmvBHTY3Ra8FAJGggzBjuHS69WzN8gscPupuZwK,l:pk(xpub6E9a95u27Qqa13XEz62FUgaFMWzHnHrb54dWfMHU7K9A33eDccvUkbu1sHYoByHAgJdR326rWqn9pGZgZHz1afDprW5gGwS4gUX8Ri6aGPZ/3))",
838+
},
839+
{
840+
"tr(xpub6CY33ZvKuS63kwDnpNCCo5YrrgkUXgdUKdCVhd4Dru5gCB3z8wGnqFiUP98Za5pYSYF5KmvBHTY3Ra8FAJGggzBjuHS69WzN8gscPupuZwK,l:pk(xpub6E9a95u27Qqa13XEz62FUgaFMWzHnHrb54dWfMHU7K9A33eDccvUkbu1sHYoByHAgJdR326rWqn9pGZgZHz1afDprW5gGwS4gUX8Ri6aGPZ/2))",
841+
"tr(xpub6CY33ZvKuS63kwDnpNCCo5YrrgkUXgdUKdCVhd4Dru5gCB3z8wGnqFiUP98Za5pYSYF5KmvBHTY3Ra8FAJGggzBjuHS69WzN8gscPupuZwK,l:pk(xpub6E9a95u27Qqa13XEz62FUgaFMWzHnHrb54dWfMHU7K9A33eDccvUkbu1sHYoByHAgJdR326rWqn9pGZgZHz1afDprW5gGwS4gUX8Ri6aGPZ/3))",
842+
},
843+
XONLY_KEYS,
844+
{
845+
{{"512094cb097990da64eebbad7b979b1326f3cbe356357abf4deb4c4ff80c7acbe902"}},
846+
{{"5120f091450b88c606f5cbc3f0cebe89e00bc5dd27f92e22f54da06439bc0c401f41"}},
847+
},
848+
OutputType::BECH32M,
849+
{
850+
{{2}, {}},
851+
{{3}, {}},
852+
}
853+
);
811854
CheckUnparsable("pkh(xprv9s21ZrQH143K31xYSDQpPDxsXRTUcvj2iNHm5NUtrGiGG5e2DtALGdso3pGz6ssrdK4PFmM8NSpSBHNqPqm55Qn3LqFtT2emdEXVYsCzC2U/<0;1>/<2;3>)", "pkh(xpub661MyMwAqRbcFW31YEwpkMuc5THy2PSt5bDMsktWQcFF8syAmRUapSCGu8ED9W6oDMSgv6Zz8idoc4a6mr8BDzTJY47LJhkJ8UB7WEGuduB/<0;1>/<2;3>)", "pkh(): Multiple multipath key path specifiers found");
812855
CheckUnparsable("pkh([deadbeef/<0;1>]xprv9s21ZrQH143K31xYSDQpPDxsXRTUcvj2iNHm5NUtrGiGG5e2DtALGdso3pGz6ssrdK4PFmM8NSpSBHNqPqm55Qn3LqFtT2emdEXVYsCzC2U/0)", "pkh([deadbeef/<0;1>]xpub661MyMwAqRbcFW31YEwpkMuc5THy2PSt5bDMsktWQcFF8syAmRUapSCGu8ED9W6oDMSgv6Zz8idoc4a6mr8BDzTJY47LJhkJ8UB7WEGuduB/0)", "pkh(): Key path value \'<0;1>\' specifies multipath in a section where multipath is not allowed");
813856
CheckUnparsable("tr(xprv9s21ZrQH143K2Zu2kTVKcQi9nKhfgJUkYqG73wXsHuhATm1wkt6kcSZeTYEw2PL7krZtJopEYDvBdYWdAai3n3TWUTCVfHvPHqTYJv7smYe/6/*,{pk(xprvA1RpRA33e1JQ7ifknakTFpgNXPmW2YvmhqLQYMmrj4xJXXWYpDPS3xz7iAxn8L39njGVyuoseXzU6rcxFLJ8HFsTjSyQbLYnMpCqE2VbFWc/<1;2;3>/0/*),pk(xprv9s21ZrQH143K3jUwNHoqQNrtzJnJmx4Yup8NkNLdVQCymYbPbJXnPhwkfTfxZfptcs3rLAPUXS39oDLgrNKQGwbGsEmJJ8BU3RzQuvShEG4/0/0/<3;4>/*)})", "tr(xpub6B4sSbNr8XFYXqqKB7PeUemqgEaVtCLjgd5Lf2VYtezSHozC7ffCvVNCyu9TCgHntRQdimjV3tHbxmNfocxtuh6saNtZEw91gjXLRhQ3Yar/6/*,{pk(xpub6ERApfZwUNrhLCkDtcHTcxd75RbzS1ed54G1LkBUHQVHQKqhMkhgbmJbZRkrgZw4koxb5JaHWkY4ALHY2grBGRjaDMzQLcgJvLJuZZvRcEL/<1;2;3>/0/*),pk(xpub6AhFhZJJGt9YB8i85RfrJ8jT3T2FF5EejDCXqXfm1DAczFEXkk8HD3CXTg2TmKM8wTbSnSw3wPg5JuyLitUrpRmkjn2BQXyZnqJx16AGy94/0/0/<3;4>/*)})", "tr(): Multipath subscripts have mismatched lengths");

0 commit comments

Comments
 (0)