Skip to content

Commit 97dcf4f

Browse files
committed
fix(wallet/descriptor+wallet): expect Threshold type, and handle it
internally
1 parent 108fb3f commit 97dcf4f

File tree

3 files changed

+100
-36
lines changed

3 files changed

+100
-36
lines changed

crates/wallet/src/descriptor/dsl.rs

Lines changed: 16 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -703,10 +703,10 @@ macro_rules! fragment {
703703
$crate::keys::make_pkh($key, &secp)
704704
});
705705
( after ( $value:expr ) ) => ({
706-
$crate::impl_leaf_opcode_value!(After, $crate::miniscript::AbsLockTime::from_consensus($value))
706+
$crate::impl_leaf_opcode_value!(After, $crate::miniscript::AbsLockTime::from_consensus($value).expect("valid `AbsLockTime`"))
707707
});
708708
( older ( $value:expr ) ) => ({
709-
$crate::impl_leaf_opcode_value!(Older, $crate::bitcoin::Sequence($value)) // TODO!!
709+
$crate::impl_leaf_opcode_value!(Older, $crate::miniscript::RelLockTime::from_consensus($value).expect("valid `RelLockTime`")) // TODO!!
710710
});
711711
( sha256 ( $hash:expr ) ) => ({
712712
$crate::impl_leaf_opcode_value!(Sha256, $hash)
@@ -757,7 +757,8 @@ macro_rules! fragment {
757757
(keys_acc, net_acc)
758758
});
759759

760-
$crate::impl_leaf_opcode_value_two!(Thresh, $thresh, items)
760+
let thresh = $crate::miniscript::Threshold::new($thresh, items).expect("valid threshold and pks collection");
761+
$crate::impl_leaf_opcode_value!(Thresh, thresh)
761762
.map(|(minisc, _, _)| (minisc, key_maps, valid_networks))
762763
});
763764
( thresh ( $thresh:expr, $( $inner:tt )* ) ) => ({
@@ -769,7 +770,12 @@ macro_rules! fragment {
769770
( multi_vec ( $thresh:expr, $keys:expr ) ) => ({
770771
let secp = $crate::bitcoin::secp256k1::Secp256k1::new();
771772

772-
$crate::keys::make_multi($thresh, $crate::miniscript::Terminal::Multi, $keys, &secp)
773+
let fun = |k, pks| {
774+
let thresh = $crate::miniscript::Threshold::new(k, pks).expect("valid threshold and pks collection");
775+
$crate::miniscript::Terminal::Multi(thresh)
776+
};
777+
778+
$crate::keys::make_multi($thresh, fun, $keys, &secp)
773779
});
774780
( multi ( $thresh:expr $(, $key:expr )+ ) ) => ({
775781
$crate::group_multi_keys!( $( $key ),* )
@@ -778,7 +784,12 @@ macro_rules! fragment {
778784
( multi_a_vec ( $thresh:expr, $keys:expr ) ) => ({
779785
let secp = $crate::bitcoin::secp256k1::Secp256k1::new();
780786

781-
$crate::keys::make_multi($thresh, $crate::miniscript::Terminal::MultiA, $keys, &secp)
787+
let fun = |k, pks| {
788+
let thresh = $crate::miniscript::Threshold::new(k, pks).expect("valid threshold and pks collection");
789+
$crate::miniscript::Terminal::MultiA(thresh)
790+
};
791+
792+
$crate::keys::make_multi($thresh, fun, $keys, &secp)
782793
});
783794
( multi_a ( $thresh:expr $(, $key:expr )+ ) ) => ({
784795
$crate::group_multi_keys!( $( $key ),* )

crates/wallet/src/descriptor/policy.rs

Lines changed: 83 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@ use crate::collections::{BTreeMap, HashSet, VecDeque};
4040
use alloc::string::String;
4141
use alloc::vec::Vec;
4242
use core::cmp::max;
43+
use miniscript::miniscript::limits::{MAX_PUBKEYS_IN_CHECKSIGADD, MAX_PUBKEYS_PER_MULTISIG};
4344

4445
use core::fmt;
4546

@@ -53,7 +54,7 @@ use bitcoin::{absolute, key::XOnlyPublicKey, PublicKey, Sequence};
5354
use miniscript::descriptor::{
5455
DescriptorPublicKey, ShInner, SinglePub, SinglePubKey, SortedMultiVec, WshInner,
5556
};
56-
use miniscript::hash256;
57+
use miniscript::{hash256, Threshold};
5758
use miniscript::{
5859
Descriptor, Miniscript, Satisfier, ScriptContext, SigType, Terminal, ToPublicKey,
5960
};
@@ -586,30 +587,25 @@ impl Policy {
586587
Ok(Some(policy))
587588
}
588589

589-
fn make_multisig<Ctx: ScriptContext + 'static>(
590-
keys: &[DescriptorPublicKey],
590+
fn make_multi<Ctx: ScriptContext + 'static>(
591+
threshold: &Threshold<DescriptorPublicKey, MAX_PUBKEYS_PER_MULTISIG>,
591592
signers: &SignersContainer,
592593
build_sat: BuildSatisfaction,
593-
threshold: usize,
594594
sorted: bool,
595595
secp: &SecpCtx,
596596
) -> Result<Option<Policy>, PolicyError> {
597-
if threshold == 0 {
598-
return Ok(None);
599-
}
600-
601-
let parsed_keys = keys.iter().map(|k| PkOrF::from_key(k, secp)).collect();
597+
let parsed_keys = threshold.iter().map(|k| PkOrF::from_key(k, secp)).collect();
602598

603599
let mut contribution = Satisfaction::Partial {
604-
n: keys.len(),
605-
m: threshold,
600+
n: threshold.n(),
601+
m: threshold.k(),
606602
items: vec![],
607603
conditions: Default::default(),
608604
sorted: Some(sorted),
609605
};
610606
let mut satisfaction = contribution.clone();
611607

612-
for (index, key) in keys.iter().enumerate() {
608+
for (index, key) in threshold.iter().enumerate() {
613609
if signers.find(signer_id(key, secp)).is_some() {
614610
contribution.add(
615611
&Satisfaction::Complete {
@@ -635,7 +631,7 @@ impl Policy {
635631

636632
let mut policy: Policy = SatisfiableItem::Multisig {
637633
keys: parsed_keys,
638-
threshold,
634+
threshold: threshold.k(),
639635
}
640636
.into();
641637
policy.contribution = contribution;
@@ -644,6 +640,57 @@ impl Policy {
644640
Ok(Some(policy))
645641
}
646642

643+
fn make_multi_a<Ctx: ScriptContext + 'static>(
644+
threshold: &Threshold<DescriptorPublicKey, MAX_PUBKEYS_IN_CHECKSIGADD>,
645+
signers: &SignersContainer,
646+
build_sat: BuildSatisfaction,
647+
sorted: bool,
648+
secp: &SecpCtx,
649+
) -> Result<Option<Policy>, PolicyError> {
650+
let parsed_keys = threshold.iter().map(|k| PkOrF::from_key(k, secp)).collect();
651+
652+
let mut contribution = Satisfaction::Partial {
653+
n: threshold.n(),
654+
m: threshold.k(),
655+
items: vec![],
656+
conditions: Default::default(),
657+
sorted: Some(sorted),
658+
};
659+
let mut satisfaction = contribution.clone();
660+
661+
for (index, key) in threshold.iter().enumerate() {
662+
if signers.find(signer_id(key, secp)).is_some() {
663+
contribution.add(
664+
&Satisfaction::Complete {
665+
condition: Default::default(),
666+
},
667+
index,
668+
)?;
669+
}
670+
if let Some(psbt) = build_sat.psbt() {
671+
if Ctx::find_signature(psbt, key, secp) {
672+
satisfaction.add(
673+
&Satisfaction::Complete {
674+
condition: Default::default(),
675+
},
676+
index,
677+
)?;
678+
}
679+
}
680+
}
681+
satisfaction.finalize();
682+
contribution.finalize();
683+
684+
let mut policy: Policy = SatisfiableItem::Multisig {
685+
keys: parsed_keys,
686+
threshold: threshold.k(),
687+
}
688+
.into();
689+
policy.contribution = contribution;
690+
policy.satisfaction = satisfaction;
691+
Ok(Some(policy))
692+
}
693+
647694
/// Return whether or not a specific path in the policy tree is required to unambiguously
648695
/// create a transaction
649696
///
@@ -952,7 +999,10 @@ impl<Ctx: ScriptContext + 'static> ExtractPolicy for Miniscript<DescriptorPublic
952999
Some(policy)
9531000
}
9541001
Terminal::Older(value) => {
955-
let mut policy: Policy = SatisfiableItem::RelativeTimelock { value: (*value).into() }.into();
1002+
let mut policy: Policy = SatisfiableItem::RelativeTimelock {
1003+
value: (*value).into(),
1004+
}
1005+
.into();
9561006
policy.contribution = Satisfaction::Complete {
9571007
condition: Condition {
9581008
timelock: None,
@@ -966,9 +1016,11 @@ impl<Ctx: ScriptContext + 'static> ExtractPolicy for Miniscript<DescriptorPublic
9661016
} = build_sat
9671017
{
9681018
let older = Older::new(Some(current_height), Some(input_max_height), false);
969-
let older_sat = Satisfier::<bitcoin::PublicKey>::check_older(&older, (*value).into());
970-
let inputs_sat = psbt_inputs_sat(psbt)
971-
.all(|sat| Satisfier::<bitcoin::PublicKey>::check_older(&sat, (*value).into()));
1019+
let older_sat =
1020+
Satisfier::<bitcoin::PublicKey>::check_older(&older, (*value).into());
1021+
let inputs_sat = psbt_inputs_sat(psbt).all(|sat| {
1022+
Satisfier::<bitcoin::PublicKey>::check_older(&sat, (*value).into())
1023+
});
9721024
if older_sat && inputs_sat {
9731025
policy.satisfaction = policy.contribution.clone();
9741026
}
@@ -986,8 +1038,11 @@ impl<Ctx: ScriptContext + 'static> ExtractPolicy for Miniscript<DescriptorPublic
9861038
Terminal::Hash160(hash) => {
9871039
Some(SatisfiableItem::Hash160Preimage { hash: *hash }.into())
9881040
}
989-
Terminal::Multi(k, pks) | Terminal::MultiA(k, pks) => {
990-
Policy::make_multisig::<Ctx>(pks, signers, build_sat, *k, false, secp)?
1041+
Terminal::Multi(threshold) => {
1042+
Policy::make_multi::<Ctx>(threshold, signers, build_sat, false, secp)?
1043+
}
1044+
Terminal::MultiA(threshold) => {
1045+
Policy::make_multi_a::<Ctx>(threshold, signers, build_sat, false, secp)?
9911046
}
9921047
// Identities
9931048
Terminal::Alt(inner)
@@ -1016,8 +1071,9 @@ impl<Ctx: ScriptContext + 'static> ExtractPolicy for Miniscript<DescriptorPublic
10161071
a.extract_policy(signers, build_sat, secp)?,
10171072
b.extract_policy(signers, build_sat, secp)?,
10181073
)?,
1019-
Terminal::Thresh(k, nodes) => {
1020-
let mut threshold = *k;
1074+
Terminal::Thresh(threshold) => {
1075+
let mut k = threshold.k();
1076+
let nodes = threshold.data();
10211077
let mapped: Vec<_> = nodes
10221078
.iter()
10231079
.map(|n| n.extract_policy(signers, build_sat, secp))
@@ -1027,13 +1083,13 @@ impl<Ctx: ScriptContext + 'static> ExtractPolicy for Miniscript<DescriptorPublic
10271083
.collect();
10281084

10291085
if mapped.len() < nodes.len() {
1030-
threshold = match threshold.checked_sub(nodes.len() - mapped.len()) {
1086+
k = match k.checked_sub(nodes.len() - mapped.len()) {
10311087
None => return Ok(None),
10321088
Some(x) => x,
10331089
};
10341090
}
10351091

1036-
Policy::make_thresh(mapped, threshold)?
1092+
Policy::make_thresh(mapped, k)?
10371093
}
10381094

10391095
// Unsupported
@@ -1087,13 +1143,10 @@ impl ExtractPolicy for Descriptor<DescriptorPublicKey> {
10871143
build_sat: BuildSatisfaction,
10881144
secp: &SecpCtx,
10891145
) -> Result<Option<Policy>, Error> {
1090-
Ok(Policy::make_multisig::<Ctx>(
1091-
keys.pks.as_ref(),
1092-
signers,
1093-
build_sat,
1094-
keys.k,
1095-
true,
1096-
secp,
1146+
let threshold = Threshold::new(keys.k(), keys.pks().to_vec())
1147+
.expect("valid threshold and pks collection");
1148+
Ok(Policy::make_multi::<Ctx>(
1149+
&threshold, signers, build_sat, true, secp,
10971150
)?)
10981151
}
10991152

crates/wallet/src/wallet/export.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -166,7 +166,7 @@ impl FullyNodedExport {
166166
fn check_ms<Ctx: ScriptContext>(
167167
terminal: &Terminal<String, Ctx>,
168168
) -> Result<(), &'static str> {
169-
if let Terminal::Multi(_, _) = terminal {
169+
if let Terminal::Multi(_) = terminal {
170170
Ok(())
171171
} else {
172172
Err("The descriptor contains operators not supported by Bitcoin Core")

0 commit comments

Comments
 (0)