Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 1 addition & 4 deletions bitcoind-tests/tests/test_cpp.rs
Original file line number Diff line number Diff line change
Expand Up @@ -147,10 +147,7 @@ pub fn test_from_cpp_ms(cl: &Client, testdata: &TestData) {
for i in 0..psbts.len() {
let wsh_derived = desc_vec[i].derived_descriptor(&secp);
let ms = if let Descriptor::Wsh(wsh) = &wsh_derived {
match wsh.as_inner() {
miniscript::descriptor::WshInner::Ms(ms) => ms,
_ => unreachable!(),
}
wsh.as_inner()
} else {
unreachable!("Only Wsh descriptors are supported");
};
Expand Down
22 changes: 4 additions & 18 deletions bitcoind-tests/tests/test_desc.rs
Original file line number Diff line number Diff line change
Expand Up @@ -225,29 +225,15 @@ pub fn test_desc_satisfy(
Descriptor::Pkh(pk) => find_sk_single_key(*pk.as_inner(), testdata),
Descriptor::Wpkh(pk) => find_sk_single_key(*pk.as_inner(), testdata),
Descriptor::Sh(sh) => match sh.as_inner() {
miniscript::descriptor::ShInner::Wsh(wsh) => match wsh.as_inner() {
miniscript::descriptor::WshInner::SortedMulti(ref smv) => {
let ms = Miniscript::from_ast(smv.sorted_node()).unwrap();
Copy link
Copy Markdown
Contributor Author

@trevarj trevarj Apr 4, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The key point here was to call sorted_node() and now we don't have something so convenient. See the HACK in find_sks_ms

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Interesting. So, the issue is that we want to get all the keys in the descriptor, in the order they'd be needed, and we're using pk_iter for this. Since pk_iter can't sort the keys, we have to sort them after the fact.

This is a bit of a weird thing to desire -- does this test actually fail without this? It seems like we never use the ordering of sks_reqd. In fact, we lose the ordering because we iterate over it, produce signatures, then stick them into the BTreeMap psbt.inputs[0].partial_sigs.

Also, since #910 I think the logic in this example is actually wrong, or at least inconsistent -- it would sort sortedmulti but not sortedmulti_a. And your hack doesn't change this.

So can we just drop the sortedmulti logic here and treat sortedmulti like multi?

find_sks_ms(&ms, testdata)
}
miniscript::descriptor::WshInner::Ms(ref ms) => find_sks_ms(ms, testdata),
},
miniscript::descriptor::ShInner::Wsh(wsh) => {
find_sks_ms(wsh.as_inner(), testdata)
}
miniscript::descriptor::ShInner::Wpkh(pk) => {
find_sk_single_key(*pk.as_inner(), testdata)
}
miniscript::descriptor::ShInner::SortedMulti(smv) => {
let ms = Miniscript::from_ast(smv.sorted_node()).unwrap();
find_sks_ms(&ms, testdata)
}
miniscript::descriptor::ShInner::Ms(ms) => find_sks_ms(ms, testdata),
},
Descriptor::Wsh(wsh) => match wsh.as_inner() {
miniscript::descriptor::WshInner::SortedMulti(ref smv) => {
let ms = Miniscript::from_ast(smv.sorted_node()).unwrap();
find_sks_ms(&ms, testdata)
}
miniscript::descriptor::WshInner::Ms(ref ms) => find_sks_ms(ms, testdata),
},
Descriptor::Wsh(wsh) => find_sks_ms(wsh.as_inner(), testdata),
Descriptor::Tr(_tr) => unreachable!("Tr checked earlier"),
};
let msg = psbt
Expand Down
22 changes: 1 addition & 21 deletions src/descriptor/iter.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,6 @@ pub struct PkIter<'desc, Pk: MiniscriptKey> {
ms_iter_legacy: Option<miniscript::iter::PkIter<'desc, Pk, Legacy>>,
ms_iter_segwit: Option<miniscript::iter::PkIter<'desc, Pk, Segwitv0>>,
ms_iter_taproot: Option<miniscript::iter::PkIter<'desc, Pk, Tap>>,
sorted_multi: Option<core::slice::Iter<'desc, Pk>>,
}

impl<'desc, Pk: MiniscriptKey> PkIter<'desc, Pk> {
Expand All @@ -26,7 +25,6 @@ impl<'desc, Pk: MiniscriptKey> PkIter<'desc, Pk> {
ms_iter_legacy: None,
ms_iter_segwit: None,
ms_iter_taproot: None,
sorted_multi: None,
}
}

Expand All @@ -38,7 +36,6 @@ impl<'desc, Pk: MiniscriptKey> PkIter<'desc, Pk> {
ms_iter_legacy: None,
ms_iter_segwit: None,
ms_iter_taproot: None,
sorted_multi: None,
}
}

Expand All @@ -50,7 +47,6 @@ impl<'desc, Pk: MiniscriptKey> PkIter<'desc, Pk> {
ms_iter_legacy: Some(ms.iter_pk()),
ms_iter_segwit: None,
ms_iter_taproot: None,
sorted_multi: None,
}
}

Expand All @@ -62,19 +58,6 @@ impl<'desc, Pk: MiniscriptKey> PkIter<'desc, Pk> {
ms_iter_legacy: None,
ms_iter_segwit: Some(ms.iter_pk()),
ms_iter_taproot: None,
sorted_multi: None,
}
}

pub(super) fn from_sortedmulti(sm: &'desc [Pk]) -> Self {
Self {
single_key: None,
taptree_iter: None,
ms_iter_bare: None,
ms_iter_legacy: None,
ms_iter_segwit: None,
ms_iter_taproot: None,
sorted_multi: Some(sm.iter()),
}
}

Expand All @@ -86,7 +69,6 @@ impl<'desc, Pk: MiniscriptKey> PkIter<'desc, Pk> {
ms_iter_legacy: None,
ms_iter_segwit: None,
ms_iter_taproot: None,
sorted_multi: None,
}
}
}
Expand Down Expand Up @@ -118,9 +100,7 @@ impl<'desc, Pk: MiniscriptKey> Iterator for PkIter<'desc, Pk> {
// Finally run through the train of other iterators.
self.ms_iter_bare.as_mut().and_then(Iterator::next).or_else(
|| self.ms_iter_legacy.as_mut().and_then(Iterator::next).or_else(
|| self.ms_iter_segwit.as_mut().and_then(Iterator::next).or_else(
|| self.sorted_multi.as_mut().and_then(Iterator::next).cloned()
)
|| self.ms_iter_segwit.as_mut().and_then(Iterator::next)
)
)
}
Expand Down
71 changes: 48 additions & 23 deletions src/descriptor/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -36,15 +36,13 @@ mod bare;
mod iter;
mod segwitv0;
mod sh;
mod sortedmulti;
mod tr;

// Descriptor Exports
pub use self::bare::{Bare, Pkh};
pub use self::iter::PkIter;
pub use self::segwitv0::{Wpkh, Wsh, WshInner};
pub use self::segwitv0::{Wpkh, Wsh};
pub use self::sh::{Sh, ShInner};
pub use self::sortedmulti::SortedMultiVec;
pub use self::tr::{
TapTree, TapTreeDepthError, TapTreeIter, TapTreeIterItem, Tr, TrSpendInfo, TrSpendInfoIter,
TrSpendInfoIterItem,
Expand Down Expand Up @@ -256,18 +254,11 @@ impl<Pk: MiniscriptKey> Descriptor<Pk> {
Descriptor::Pkh(ref pk) => PkIter::from_key(pk.as_inner().clone()),
Descriptor::Wpkh(ref pk) => PkIter::from_key(pk.as_inner().clone()),
Descriptor::Sh(ref sh) => match *sh.as_inner() {
ShInner::Wsh(ref wsh) => match wsh.as_inner() {
WshInner::SortedMulti(ref sorted) => PkIter::from_sortedmulti(sorted.pks()),
WshInner::Ms(ref ms) => PkIter::from_miniscript_segwit(ms),
},
ShInner::Wsh(ref wsh) => PkIter::from_miniscript_segwit(wsh.as_inner()),
ShInner::Wpkh(ref pk) => PkIter::from_key(pk.as_inner().clone()),
ShInner::SortedMulti(ref sorted) => PkIter::from_sortedmulti(sorted.pks()),
ShInner::Ms(ref ms) => PkIter::from_miniscript_legacy(ms),
},
Descriptor::Wsh(ref wsh) => match wsh.as_inner() {
WshInner::SortedMulti(ref sorted) => PkIter::from_sortedmulti(sorted.pks()),
WshInner::Ms(ref ms) => PkIter::from_miniscript_segwit(ms),
},
Descriptor::Wsh(ref wsh) => PkIter::from_miniscript_segwit(wsh.as_inner()),
Descriptor::Tr(ref tr) => PkIter::from_tr(tr),
}
}
Expand Down Expand Up @@ -313,18 +304,29 @@ impl<Pk: MiniscriptKey> Descriptor<Pk> {
Descriptor::Pkh(ref _pkh) => DescriptorType::Pkh,
Descriptor::Wpkh(ref _wpkh) => DescriptorType::Wpkh,
Descriptor::Sh(ref sh) => match sh.as_inner() {
ShInner::Wsh(ref wsh) => match wsh.as_inner() {
WshInner::SortedMulti(ref _smv) => DescriptorType::ShWshSortedMulti,
WshInner::Ms(ref _ms) => DescriptorType::ShWsh,
},
ShInner::Wsh(ref wsh) => {
if let Terminal::SortedMulti(..) = wsh.as_inner().node {
DescriptorType::ShWshSortedMulti
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In a65deea:

As a followup let's maybe delete this from DescriptorType since it's no longer a distinct kind of descriptor. But let's not do it here since it'd be an API-breaking change and I'm not certain that we want to do it.

} else {
DescriptorType::ShWsh
}
}
ShInner::Wpkh(ref _wpkh) => DescriptorType::ShWpkh,
ShInner::SortedMulti(ref _smv) => DescriptorType::ShSortedMulti,
ShInner::Ms(ref _ms) => DescriptorType::Sh,
},
Descriptor::Wsh(ref wsh) => match wsh.as_inner() {
WshInner::SortedMulti(ref _smv) => DescriptorType::WshSortedMulti,
WshInner::Ms(ref _ms) => DescriptorType::Wsh,
ShInner::Ms(ref ms) => {
if let Terminal::SortedMulti(..) = ms.node {
DescriptorType::ShSortedMulti
} else {
DescriptorType::Sh
}
}
},
Descriptor::Wsh(ref wsh) => {
if let Terminal::SortedMulti(..) = wsh.as_inner().node {
DescriptorType::WshSortedMulti
} else {
DescriptorType::Wsh
}
}
Descriptor::Tr(ref _tr) => DescriptorType::Tr,
}
}
Expand Down Expand Up @@ -1208,6 +1210,7 @@ mod tests {

use super::{checksum, *};
use crate::hex_script;
use crate::miniscript::context::ScriptContextError;
#[cfg(feature = "compiler")]
use crate::policy;

Expand Down Expand Up @@ -1257,7 +1260,7 @@ mod tests {
StdDescriptor::from_str("sh(sortedmulti)")
.unwrap_err()
.to_string(),
"sortedmulti must have at least 1 children, but found 0"
"expected threshold, found terminal"
); //issue 202
assert_eq!(
StdDescriptor::from_str(&format!("sh(sortedmulti(2,{}))", &TEST_PK[3..69]))
Expand Down Expand Up @@ -2884,4 +2887,26 @@ pk(03f28773c2d975288bc7d1d205c3748651b075fbc6610e58cddeeddf8f19405aa8))";
let definite: Result<Descriptor<DefiniteDescriptorKey>, _> = desc.try_into();
assert!(matches!(definite, Err(NonDefiniteKeyError::Wildcard)));
}

#[test]
fn too_many_pubkeys_for_p2sh() {
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

FYI: migrated from deleted sortedmulti.rs file.

// Arbitrary 65-byte public key (66 with length prefix).
let pk = PublicKey::from_str(
"0400232a2acfc9b43fa89f1b4f608fde335d330d7114f70ea42bfb4a41db368a3e3be6934a4097dd25728438ef73debb1f2ffdb07fec0f18049df13bdc5285dc5b",
)
.unwrap();

// This is legal for CHECKMULTISIG, but the 8 keys consume the whole 520 bytes
// allowed by P2SH, meaning that the full script goes over the limit.
let thresh = Threshold::new(2, vec![pk; 8]).expect("the thresh is ok..");
let script = Miniscript::<_, Legacy>::sortedmulti(thresh).encode();
let res = Miniscript::<_, Legacy>::decode(&script);

let error = res.expect_err("decoding should err");

match error {
Error::ContextError(ScriptContextError::MaxRedeemScriptSizeExceeded { .. }) => {} // ok
other => panic!("unexpected error: {:?}", other),
}
}
}
Loading
Loading