New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
descriptors: do not return top-level only funcs as sub descriptors #28067
descriptors: do not return top-level only funcs as sub descriptors #28067
Conversation
The `UNKNOWN_DESCRIPTOR` error comes from the `WalletDescriptor::DeserializeDescriptor` std::ios_base exception, which contains further information about the parsing error.
The following sections might be updated with supplementary metadata relevant to reviewers and maintainers. ReviewsSee the guideline for information on the review process.
If your review is incorrectly listed, please react with 👎 to this comment and the bot will ignore it on the next update. |
The added functional test doesn't crash, does it? |
Yeah. Without the fix commit, the migration process in the test will pass "successfully" and then, at the wallet restart verification, the loading process will throw the "Unrecognized descriptor found" error. Which denotes that the migrated wallet is in an unusable state. With the fix commit, the migration and the restart verification will pass successfully. For your issue crash cause, I think that I'm getting closer to it. But yeah, it shouldn't be fixed by this PR. Still, try it please. The first commit adds logging that might give us some useful information. |
This is clearly an issue; we should not be inferring descriptors that the code itself doesn't accept back. On the other hand, maybe we should just permit |
Nice, I was asking myself the same question. So, great. Will explore #24114 further tomorrow and maybe cook something for it to see how it would look like. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@sipa, how the allowance of internal raw()
subscripts would play out with previous releases?.
I'm thinking on the current wallet migration process, which is broken for pubkey unknown sh(pkh)
scripts (also for wsh(pkh)
).
We might want this PR to fix the bug and be backported. Then, after having all the pertinent discussions etc. We could implement the new feature (compat breaking change?) for the next release.
Allowing new types of descriptors only disallows downgrading of wallets that have imported them. Downgrade protection is automatic for such wallets since descriptor wallets were introduced. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@furszy I think there is only a downgrade issue, no? If someone were to import a sh(raw(...)) as watch-only address in a descriptor wallet after allowing that, and then downgrades to older software which does not support it.
Yeah good @sipa and @achow101. We are sync then.
My point was more about agreeing that the bug fix should go first, so it can be backported for users of v24 and v25. Preventing them from entering an unrecoverable state after migration.
And then we can move forward implementing the "raw()
subscript" allowance new feature. Which is downgrade incompatible. (happy to work on it).
Agree?
6480b61
to
a2b7b05
Compare
a2b7b05
to
47abfe1
Compare
ACK 47abfe1 |
47abfe1
to
569748e
Compare
Little push to update the missing todo in the test. Tiny diff. |
Failing CI task isn't related. Failure is on |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Concept ACK. Good catch. However i think you reintroduce the same bug in the second commit by returning early as raw()
when in the first case it should be an address descriptor and the second it should fail if not at top-level.
This diff fixes it:
diff --git a/src/script/descriptor.cpp b/src/script/descriptor.cpp
index 9e4f775f41..09ded5fc61 100644
--- a/src/script/descriptor.cpp
+++ b/src/script/descriptor.cpp
@@ -1662,12 +1662,7 @@ std::unique_ptr<DescriptorImpl> InferScript(const CScript& script, ParseScriptCo
CScript subscript;
if (provider.GetCScript(scriptid, subscript)) {
auto sub = InferScript(subscript, ParseScriptContext::P2SH, provider);
- if (sub) {
- return std::make_unique<SHDescriptor>(std::move(sub));
- } else {
- // When the sh subscript is unknown or unsolvable, parse it as a raw descriptor
- return std::make_unique<RawDescriptor>(script);
- }
+ if (sub) return std::make_unique<SHDescriptor>(std::move(sub));
}
}
if (txntype == TxoutType::WITNESS_V0_SCRIPTHASH && (ctx == ParseScriptContext::TOP || ctx == ParseScriptContext::P2SH)) {
@@ -1675,12 +1670,7 @@ std::unique_ptr<DescriptorImpl> InferScript(const CScript& script, ParseScriptCo
CScript subscript;
if (provider.GetCScript(scriptid, subscript)) {
auto sub = InferScript(subscript, ParseScriptContext::P2WSH, provider);
- if (sub) {
- return std::make_unique<WSHDescriptor>(std::move(sub));
- } else {
- // When the wsh subscript is unknown or unsolvable, parse it as a raw descriptor
- return std::make_unique<RawDescriptor>(script);
- }
+ if (sub) return std::make_unique<WSHDescriptor>(std::move(sub));
}
}
if (txntype == TxoutType::WITNESS_V1_TAPROOT && ctx == ParseScriptContext::TOP) {
diff --git a/test/functional/data/rpc_decodescript.json b/test/functional/data/rpc_decodescript.json
index fd0f30d86e..4a15ae8792 100644
--- a/test/functional/data/rpc_decodescript.json
+++ b/test/functional/data/rpc_decodescript.json
@@ -69,7 +69,7 @@
"p2sh": "2N34iiGoUUkVSPiaaTFpJjB1FR9TXQu3PGM",
"segwit": {
"asm": "0 96c2368fc30514a438a8bd909f93c49a1549d77198ccbdb792043b666cb24f42",
- "desc": "raw(002096c2368fc30514a438a8bd909f93c49a1549d77198ccbdb792043b666cb24f42)#33khjalh",
+ "desc": "addr(bcrt1qjmprdr7rq522gw9ghkgfly7yng25n4m3nrxtmdujqsakvm9jfapqk795l5)#5akkdska",
"hex": "002096c2368fc30514a438a8bd909f93c49a1549d77198ccbdb792043b666cb24f42",
"address": "bcrt1qjmprdr7rq522gw9ghkgfly7yng25n4m3nrxtmdujqsakvm9jfapqk795l5",
"type": "witness_v0_scripthash",
diff --git a/test/functional/rpc_decodescript.py b/test/functional/rpc_decodescript.py
index c8390a66b3..854363ac68 100755
--- a/test/functional/rpc_decodescript.py
+++ b/test/functional/rpc_decodescript.py
@@ -278,7 +278,7 @@ class DecodeScriptTest(BitcoinTestFramework):
res = self.nodes[0].decodescript("82012088a914ffffffffffffffffffffffffffffffffffffffff882102ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffacb2")
# Check v0 wsh. Output will be OP_0 + sha256(script)
script_pubkey = CScript([OP_0, sha256(bytes.fromhex("82012088a914ffffffffffffffffffffffffffffffffffffffff882102ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffacb2"))])
- assert res["segwit"]["desc"] == descsum_create(f"raw({script_pubkey.hex()})")
+ assert res["segwit"]["desc"] == descsum_create(f"addr(bcrt1q73qyfypp47hvgnkjqnav0j3k2lq3v76wg22dk8tmwuz5sfgv66xsvxg6uu)")
# Miniscript-compatible multisig bigger than 520 byte P2SH limit.
res = self.nodes[0].decodescript("5b21020e0338c96a8870479f2396c373cc7696ba124e8635d41b0ea581112b678172612102675333a4e4b8fb51d9d4e22fa5a8eaced3fdac8a8cbf9be8c030f75712e6af992102896807d54bc55c24981f24a453c60ad3e8993d693732288068a23df3d9f50d4821029e51a5ef5db3137051de8323b001749932f2ff0d34c82e96a2c2461de96ae56c2102a4e1a9638d46923272c266631d94d36bdb03a64ee0e14c7518e49d2f29bc401021031c41fdbcebe17bec8d49816e00ca1b5ac34766b91c9f2ac37d39c63e5e008afb2103079e252e85abffd3c401a69b087e590a9b86f33f574f08129ccbd3521ecf516b2103111cf405b627e22135b3b3733a4a34aa5723fb0f58379a16d32861bf576b0ec2210318f331b3e5d38156da6633b31929c5b220349859cc9ca3d33fb4e68aa08401742103230dae6b4ac93480aeab26d000841298e3b8f6157028e47b0897c1e025165de121035abff4281ff00660f99ab27bb53e6b33689c2cd8dcd364bc3c90ca5aea0d71a62103bd45cddfacf2083b14310ae4a84e25de61e451637346325222747b157446614c2103cc297026b06c71cbfa52089149157b5ff23de027ac5ab781800a578192d175462103d3bde5d63bdb3a6379b461be64dad45eabff42f758543a9645afd42f6d4248282103ed1e8d5109c9ed66f7941bc53cc71137baa76d50d274bda8d5e8ffbd6e61fe9a5fae736402c00fb269522103aab896d53a8e7d6433137bbba940f9c521e085dd07e60994579b64a6d992cf79210291b7d0b1b692f8f524516ed950872e5da10fb1b808b5a526dedc6fed1cf29807210386aa9372fbab374593466bc5451dc59954e90787f08060964d95c87ef34ca5bb53ae68")
assert_equal(res["segwit"]["desc"], "wsh(or_d(multi(11,020e0338c96a8870479f2396c373cc7696ba124e8635d41b0ea581112b67817261,02675333a4e4b8fb51d9d4e22fa5a8eaced3fdac8a8cbf9be8c030f75712e6af99,02896807d54bc55c24981f24a453c60ad3e8993d693732288068a23df3d9f50d48,029e51a5ef5db3137051de8323b001749932f2ff0d34c82e96a2c2461de96ae56c,02a4e1a9638d46923272c266631d94d36bdb03a64ee0e14c7518e49d2f29bc4010,031c41fdbcebe17bec8d49816e00ca1b5ac34766b91c9f2ac37d39c63e5e008afb,03079e252e85abffd3c401a69b087e590a9b86f33f574f08129ccbd3521ecf516b,03111cf405b627e22135b3b3733a4a34aa5723fb0f58379a16d32861bf576b0ec2,0318f331b3e5d38156da6633b31929c5b220349859cc9ca3d33fb4e68aa0840174,03230dae6b4ac93480aeab26d000841298e3b8f6157028e47b0897c1e025165de1,035abff4281ff00660f99ab27bb53e6b33689c2cd8dcd364bc3c90ca5aea0d71a6,03bd45cddfacf2083b14310ae4a84e25de61e451637346325222747b157446614c,03cc297026b06c71cbfa52089149157b5ff23de027ac5ab781800a578192d17546,03d3bde5d63bdb3a6379b461be64dad45eabff42f758543a9645afd42f6d424828,03ed1e8d5109c9ed66f7941bc53cc71137baa76d50d274bda8d5e8ffbd6e61fe9a),and_v(v:older(4032),multi(2,03aab896d53a8e7d6433137bbba940f9c521e085dd07e60994579b64a6d992cf79,0291b7d0b1b692f8f524516ed950872e5da10fb1b808b5a526dedc6fed1cf29807,0386aa9372fbab374593466bc5451dc59954e90787f08060964d95c87ef34ca5bb))))#7jwwklk4")
…criptor e.g. sh(addr(ADDR)) or sh(raw(HEX)) are invalid descriptors. Making sh and wsh top level functions to return addr/raw descriptors when the subscript inference fails.
569748e
to
dd9633b
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Updated per feedback. Thanks @darosior!.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
utACK dd9633b
ACK dd9633b |
…criptor e.g. sh(addr(ADDR)) or sh(raw(HEX)) are invalid descriptors. Making sh and wsh top level functions to return addr/raw descriptors when the subscript inference fails. Github-Pull: bitcoin#28067 Rebased-From: cc781a2
Github-Pull: bitcoin#28067 Rebased-From: dd9633b
Backported 2/3 of the commits here in ##28067. |
494f1af depends: xcb-proto 1.15.2 (fanquake) 513ca0a test: wallet, add coverage for watch-only raw sh script migration (furszy) 6d5a510 descriptor: InferScript, do not return top-level only func as sub descriptor (furszy) 37d9cc6 test: wallet, add coverage for addressbook migration (furszy) 4b16650 wallet: migration bugfix, persist empty labels (furszy) 59b06b6 wallet: migration bugfix, clone 'send' record label to all wallets (furszy) Pull request description: Currently backports: * #28038 * #28067 2nd & 3rd commits. * #28097 ACKs for top commit: stickies-v: ACK 494f1af Tree-SHA512: cea134cfa72950d8428191adce79c0881302ca54488f64d3d4a5f9070bb2445d8074e58fa31a482481c4eabb74c852a025f53597540fc646dc20f33b21cf0a06
Linked to #28057.
Currently, the
InferScript
function returns an invalid descriptor when it tries to infer a p2sh-p2pkh script whose pubkey is not known by the wallet.This behavior occurs because the inference process bypasses the
pkh
subscript when the pubkey is not contained by the wallet (no pubkey provider), interpreting it as ash(addr(ADDR))
descriptor. Then, the failure arises because theaddr()
function is restricted to being used only at the top level.For reviewers, would recommend to start by examining the functional test to understand the context and the circumstances on which this can result in a fatal error (e.g. during the migration process).