From 092569e8580b7c2c13b6cc9d29bcb4c5e85bbb44 Mon Sep 17 00:00:00 2001 From: Ava Chow Date: Wed, 1 Jan 2025 23:17:44 -0500 Subject: [PATCH 1/4] descriptor: Try the other parity in ConstPubkeyProvider::GetPrivKey() GetPrivKey() needs the same handling of all keyids for xonly keys that ToPrivateString() does. Refactor that into GetPrivKey() and reuse it in ToPrivateString() to resolve this. --- src/script/descriptor.cpp | 13 +++---------- 1 file changed, 3 insertions(+), 10 deletions(-) diff --git a/src/script/descriptor.cpp b/src/script/descriptor.cpp index 2e1a30744ec..687fc72d1b8 100644 --- a/src/script/descriptor.cpp +++ b/src/script/descriptor.cpp @@ -312,15 +312,7 @@ public: bool ToPrivateString(const SigningProvider& arg, std::string& ret) const override { CKey key; - if (m_xonly) { - for (const auto& keyid : XOnlyPubKey(m_pubkey).GetKeyIDs()) { - arg.GetKey(keyid, key); - if (key.IsValid()) break; - } - } else { - arg.GetKey(m_pubkey.GetID(), key); - } - if (!key.IsValid()) return false; + if (!GetPrivKey(/*pos=*/0, arg, key)) return false; ret = EncodeSecret(key); return true; } @@ -331,7 +323,8 @@ public: } bool GetPrivKey(int pos, const SigningProvider& arg, CKey& key) const override { - return arg.GetKey(m_pubkey.GetID(), key); + return m_xonly ? arg.GetKeyByXOnly(XOnlyPubKey(m_pubkey), key) : + arg.GetKey(m_pubkey.GetID(), key); } std::optional GetRootPubKey() const override { From 4c50c21f6bfc1d88846be571055b481ab14b086f Mon Sep 17 00:00:00 2001 From: Ava Chow Date: Wed, 1 Jan 2025 23:52:33 -0500 Subject: [PATCH 2/4] tests: Check ExpandPrivate matches for both parsed descriptors --- src/script/signingprovider.h | 2 ++ src/test/descriptor_tests.cpp | 18 ++++++++++++++++++ 2 files changed, 20 insertions(+) diff --git a/src/script/signingprovider.h b/src/script/signingprovider.h index efdfd9ee566..5b1da681f83 100644 --- a/src/script/signingprovider.h +++ b/src/script/signingprovider.h @@ -136,6 +136,8 @@ public: std::vector>> GetTreeTuples() const; /** Returns true if there are any tapscripts */ bool HasScripts() const { return !m_branch.empty(); } + + bool operator==(const TaprootBuilder& other) const { return GetTreeTuples() == other.GetTreeTuples(); } }; /** Given a TaprootSpendData and the output key, reconstruct its script tree. diff --git a/src/test/descriptor_tests.cpp b/src/test/descriptor_tests.cpp index 288ffc66eb6..5d4bb2ef113 100644 --- a/src/test/descriptor_tests.cpp +++ b/src/test/descriptor_tests.cpp @@ -62,6 +62,15 @@ bool EqualDescriptor(std::string a, std::string b) return a == b; } +bool EqualSigningProviders(const FlatSigningProvider& a, const FlatSigningProvider& b) +{ + return a.scripts == b.scripts + && a.pubkeys == b.pubkeys + && a.origins == b.origins + && a.keys == b.keys + && a.tr_trees == b.tr_trees; +} + std::string UseHInsteadOfApostrophe(const std::string& desc) { std::string ret = desc; @@ -214,6 +223,15 @@ void DoCheck(std::string prv, std::string pub, const std::string& norm_pub, int BOOST_CHECK_MESSAGE(EqualDescriptor(prv, prv1), "Private ser: " + prv1 + " Private desc: " + prv); } BOOST_CHECK(!parse_pub->ToPrivateString(keys_pub, prv1)); + + // Check that both can ExpandPrivate and get the same SigningProviders + FlatSigningProvider priv_prov; + parse_priv->ExpandPrivate(0, keys_priv, priv_prov); + + FlatSigningProvider pub_prov; + parse_pub->ExpandPrivate(0, keys_priv, pub_prov); + + BOOST_CHECK_MESSAGE(EqualSigningProviders(priv_prov, pub_prov), "Private desc: " + prv + " Pub desc: " + pub); } // Check that private can produce the normalized descriptors From b4ac48090f259dbef567b49fa36a8bf192209710 Mon Sep 17 00:00:00 2001 From: Ava Chow Date: Mon, 6 Jan 2025 19:10:15 -0500 Subject: [PATCH 3/4] descriptor: Use InferXOnlyPubkey for miniscript XOnly pubkey from script --- src/script/descriptor.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/script/descriptor.cpp b/src/script/descriptor.cpp index 687fc72d1b8..499af47ee55 100644 --- a/src/script/descriptor.cpp +++ b/src/script/descriptor.cpp @@ -1709,7 +1709,7 @@ struct KeyParser { if (miniscript::IsTapscript(m_script_ctx) && end - begin == 32) { XOnlyPubKey pubkey; std::copy(begin, end, pubkey.begin()); - if (auto pubkey_provider = InferPubkey(pubkey.GetEvenCorrespondingCPubKey(), ParseContext(), *m_in)) { + if (auto pubkey_provider = InferXOnlyPubkey(pubkey, ParseContext(), *m_in)) { m_keys.emplace_back(); m_keys.back().push_back(std::move(pubkey_provider)); return key; From c0045e6cee06bc0029fb79b5a531aa1f2b817424 Mon Sep 17 00:00:00 2001 From: David Gumberg Date: Mon, 6 Jan 2025 14:25:44 -0800 Subject: [PATCH 4/4] Add test for multipath miniscript expression --- src/test/descriptor_tests.cpp | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/src/test/descriptor_tests.cpp b/src/test/descriptor_tests.cpp index 5d4bb2ef113..1b4020e0a06 100644 --- a/src/test/descriptor_tests.cpp +++ b/src/test/descriptor_tests.cpp @@ -826,6 +826,31 @@ BOOST_AUTO_TEST_CASE(descriptor_test) {{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}}, } ); + CheckMultipath("tr(xprv9yYge4PS54XkYT9KiLfCRwc8Jeuz8DucxQGtuEecJZYhKNiqbPxYHTPzXtskmzWBqdqkRAGsghNmZzNsfU2wstaB3XjDQFPv567aQSSuPyo,l:pk(xprvA1ADjaN8H3HGnZSmt4VF7YdWoV9oNq8jhqhurxsrYycBAFK555cECoaY22KWt6BTRNLuvobW5VQTF89PN3iA485LAg7epazevPyjCa4xTzd/<2;3>))", + "tr(xpub6CY33ZvKuS63kwDnpNCCo5YrrgkUXgdUKdCVhd4Dru5gCB3z8wGnqFiUP98Za5pYSYF5KmvBHTY3Ra8FAJGggzBjuHS69WzN8gscPupuZwK,l:pk(xpub6E9a95u27Qqa13XEz62FUgaFMWzHnHrb54dWfMHU7K9A33eDccvUkbu1sHYoByHAgJdR326rWqn9pGZgZHz1afDprW5gGwS4gUX8Ri6aGPZ/<2;3>))", + { + "tr(xprv9yYge4PS54XkYT9KiLfCRwc8Jeuz8DucxQGtuEecJZYhKNiqbPxYHTPzXtskmzWBqdqkRAGsghNmZzNsfU2wstaB3XjDQFPv567aQSSuPyo,l:pk(xprvA1ADjaN8H3HGnZSmt4VF7YdWoV9oNq8jhqhurxsrYycBAFK555cECoaY22KWt6BTRNLuvobW5VQTF89PN3iA485LAg7epazevPyjCa4xTzd/2))", + "tr(xprv9yYge4PS54XkYT9KiLfCRwc8Jeuz8DucxQGtuEecJZYhKNiqbPxYHTPzXtskmzWBqdqkRAGsghNmZzNsfU2wstaB3XjDQFPv567aQSSuPyo,l:pk(xprvA1ADjaN8H3HGnZSmt4VF7YdWoV9oNq8jhqhurxsrYycBAFK555cECoaY22KWt6BTRNLuvobW5VQTF89PN3iA485LAg7epazevPyjCa4xTzd/3))", + }, + { + "tr(xpub6CY33ZvKuS63kwDnpNCCo5YrrgkUXgdUKdCVhd4Dru5gCB3z8wGnqFiUP98Za5pYSYF5KmvBHTY3Ra8FAJGggzBjuHS69WzN8gscPupuZwK,l:pk(xpub6E9a95u27Qqa13XEz62FUgaFMWzHnHrb54dWfMHU7K9A33eDccvUkbu1sHYoByHAgJdR326rWqn9pGZgZHz1afDprW5gGwS4gUX8Ri6aGPZ/2))", + "tr(xpub6CY33ZvKuS63kwDnpNCCo5YrrgkUXgdUKdCVhd4Dru5gCB3z8wGnqFiUP98Za5pYSYF5KmvBHTY3Ra8FAJGggzBjuHS69WzN8gscPupuZwK,l:pk(xpub6E9a95u27Qqa13XEz62FUgaFMWzHnHrb54dWfMHU7K9A33eDccvUkbu1sHYoByHAgJdR326rWqn9pGZgZHz1afDprW5gGwS4gUX8Ri6aGPZ/3))", + }, + { + "tr(xpub6CY33ZvKuS63kwDnpNCCo5YrrgkUXgdUKdCVhd4Dru5gCB3z8wGnqFiUP98Za5pYSYF5KmvBHTY3Ra8FAJGggzBjuHS69WzN8gscPupuZwK,l:pk(xpub6E9a95u27Qqa13XEz62FUgaFMWzHnHrb54dWfMHU7K9A33eDccvUkbu1sHYoByHAgJdR326rWqn9pGZgZHz1afDprW5gGwS4gUX8Ri6aGPZ/2))", + "tr(xpub6CY33ZvKuS63kwDnpNCCo5YrrgkUXgdUKdCVhd4Dru5gCB3z8wGnqFiUP98Za5pYSYF5KmvBHTY3Ra8FAJGggzBjuHS69WzN8gscPupuZwK,l:pk(xpub6E9a95u27Qqa13XEz62FUgaFMWzHnHrb54dWfMHU7K9A33eDccvUkbu1sHYoByHAgJdR326rWqn9pGZgZHz1afDprW5gGwS4gUX8Ri6aGPZ/3))", + }, + XONLY_KEYS, + { + {{"512094cb097990da64eebbad7b979b1326f3cbe356357abf4deb4c4ff80c7acbe902"}}, + {{"5120f091450b88c606f5cbc3f0cebe89e00bc5dd27f92e22f54da06439bc0c401f41"}}, + }, + OutputType::BECH32M, + { + {{2}, {}}, + {{3}, {}}, + } + ); CheckUnparsable("pkh(xprv9s21ZrQH143K31xYSDQpPDxsXRTUcvj2iNHm5NUtrGiGG5e2DtALGdso3pGz6ssrdK4PFmM8NSpSBHNqPqm55Qn3LqFtT2emdEXVYsCzC2U/<0;1>/<2;3>)", "pkh(xpub661MyMwAqRbcFW31YEwpkMuc5THy2PSt5bDMsktWQcFF8syAmRUapSCGu8ED9W6oDMSgv6Zz8idoc4a6mr8BDzTJY47LJhkJ8UB7WEGuduB/<0;1>/<2;3>)", "pkh(): Multiple multipath key path specifiers found"); 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"); 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");