-
Notifications
You must be signed in to change notification settings - Fork 13
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
descriptor_string: crash due to resource limits #73
Comments
Innteresting. I'd be ok to just copy Core's behavior here. I'm not sure how to think about this in general. It seems to me that we should be able to compute |
cc @sanket1729 if you wanna try to think through this. |
Hmm, I cannot reproduce this on my end. I tested this with latest master (13.0.0) and 12.2.0 (the version used in bitcoinfuzz) #[test]
fn test_btc_fuzz_issue_73() {
let secp = secp256k1::Secp256k1::new();
let desc = Descriptor::parse_descriptor(&secp, "wsh(thresh(1,pk(xprv9s21ZrQH143K31xYSDQpPDxsXRTUcvj2iNHm5NUtrGiGG5e2DtALGdso3pGz6ssrdK4PFmM8NSpSBHNqPqm55Qn3LqFtT2emdEXVYsCzC2U/840/94709400'),slnnnnnnnnnnnnnntvtvtvtvtvtvtvtvnn:0))").unwrap();
dbg!(&desc);
match &desc.0 {
Descriptor::Bare(bare) => todo!(),
Descriptor::Pkh(pkh) => todo!(),
Descriptor::Wpkh(wpkh) => todo!(),
Descriptor::Sh(sh) => todo!(),
Descriptor::Wsh(wsh) => {
match wsh.as_inner() {
WshInner::SortedMulti(sorted_multi_vec) => todo!(),
WshInner::Ms(miniscript) => {
dbg!(&miniscript.ext);
},
}
},
Descriptor::Tr(tr) => todo!(),
}
} The descriptor successfully parses and computes the OpCounts correctly.
|
@sanket1729 I think you need to call |
Ah, nice. I can reproduce it. Looking into it |
@apoelstra, rust-miniscript differs from Bitcoin Core by analyzing if script elements are satisfiable. In this case 0 (OP_FALSE) in second thresh branch is not sensible as it's inherently unsatisfiable, so our implementation fails these cases for sanity checks. But I think this uncovered another issue in rust-miniscript. #[test]
fn test_2() {
let desc = Descriptor::<String>::from_str("wsh(thresh(1,pk(A),s:pk(B)))").unwrap();
let desc = Descriptor::<String>::from_str("wsh(thresh(1,pk(A),s:pk(A)))").unwrap(); // This should also ERR
// But miniscript APIs are fine.
let ms = Miniscript::<String, Segwitv0>::from_str("thresh(1,pk(A),s:pk(B))").unwrap();
let ms = Miniscript::<String, Segwitv0>::from_str("thresh(1,pk(A),s:pk(A))").expect_err("This is not a valid miniscript");
} |
I do not see why Bitcoin Core does not fail for this case. Maybe diff --git a/src/script/miniscript.h b/src/script/miniscript.h
index 75f978457c..0ec60e286e 100644
--- a/src/script/miniscript.h
+++ b/src/script/miniscript.h
@@ -1492,7 +1492,7 @@ public:
bool CheckOpsLimit() const {
if (IsTapscript(m_script_ctx)) return true;
if (const auto ops = GetOps()) return *ops <= MAX_OPS_PER_SCRIPT;
- return true;
+ return false;
} |
Oh, yeah, this script is actually unspendable isn't it? I missed that on first reading. But I guess what's happening is: in Core it interprets "cannot be satisfied" as "within resource limits", and its separate satisfiability check is broken? |
I don't think core has a notion of 'cannot be satisfied.' The branch is within resource limits, the total opcodes are 31 (< 200), but it cannot be satisfied. Our error reporting conflates the 'unsatisfied sub-child in thresh' with resource limit checks. This is because of how opcode counting is implemented using the Option, where we store 0 as None and propagate it upwards the tree. match ms.ext.ops.op_count() {
None => Err(ScriptContextError::MaxOpCountExceeded), // Change this different here that shows branch is not satisfiable.
Some(op_count) if op_count > MAX_OPS_PER_SCRIPT => {
Err(ScriptContextError::MaxOpCountExceeded)
}
_ => Ok(()),
}
In rust-miniscript, sanity checks we require both. Maybe this is the bug! |
It is spendable, by providing a signature for the public key and a
No, i don't think an unsatisfiable fragment should be considered as bumping against the ops limit. |
Make sense. |
Honestly I can't figure out what is going on in our op-counting function for |
@apoelstra, the core issue is that rust-miniscript won't allow thresh(1,pk(A),s:0), but bitcoin core will allow this. The Opcounts for satisfaction returns |
Descriptor:
"wsh(thresh(1,pk(xprv9s21ZrQH143K31xYSDQpPDxsXRTUcvj2iNHm5NUtrGiGG5e2DtALGdso3pGz6ssrdK4PFmM8NSpSBHNqPqm55Qn3LqFtT2emdEXVYsCzC2U/840/94709400'),slnnnnnnnnnnnnnntvtvtvtvtvtvtvtvnn:0))".
Bitcoin Core successfully parses it but rust-miniscript returns the following error: "at least one spend path exceeds the resource limits". I noticed that rust-miniscript is throwing an "impossible satisfaction" error from:
In the case of Bitcoin Core,
CheckOpsLimit
does not return false when it cannot get the maximum number of ops needed to satisfy the script because non-malleable satisfactions are guaranteed to be valid ifValidSatisfactions()
(this function checks whether the node is valid at all, then check ops limit and stack size):The text was updated successfully, but these errors were encountered: