Skip to content

Commit 5b40fc6

Browse files
committed
sortedmulti: use new Threshold type internally
Eliminates several redundant error checks, calls to errstr, and makes the logic more uniform. Also demonstrates the `Expression::to_null_threshold` and `Threshold::translate_by_index` methods, which may not have been clearly motivated in previous commits.
1 parent a7e18d9 commit 5b40fc6

File tree

2 files changed

+61
-57
lines changed

2 files changed

+61
-57
lines changed

src/descriptor/mod.rs

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1040,18 +1040,17 @@ mod tests {
10401040
StdDescriptor::from_str("(\u{7f}()3").unwrap_err();
10411041
StdDescriptor::from_str("pk()").unwrap_err();
10421042
StdDescriptor::from_str("nl:0").unwrap_err(); //issue 63
1043-
let compressed_pk = String::from("");
10441043
assert_eq!(
10451044
StdDescriptor::from_str("sh(sortedmulti)")
10461045
.unwrap_err()
10471046
.to_string(),
1048-
"unexpected «no arguments given for sortedmulti»"
1047+
"expected threshold, found terminal",
10491048
); //issue 202
10501049
assert_eq!(
1051-
StdDescriptor::from_str(&format!("sh(sortedmulti(2,{}))", compressed_pk))
1050+
StdDescriptor::from_str(&format!("sh(sortedmulti(2,{}))", &TEST_PK[3..69]))
10521051
.unwrap_err()
10531052
.to_string(),
1054-
"unexpected «higher threshold than there were keys in sortedmulti»"
1053+
"invalid threshold 2-of-1; cannot have k > n",
10551054
); //issue 202
10561055

10571056
StdDescriptor::from_str(TEST_PK).unwrap();

src/descriptor/sortedmulti.rs

Lines changed: 58 additions & 53 deletions
Original file line numberDiff line numberDiff line change
@@ -19,62 +19,55 @@ use crate::plan::AssetProvider;
1919
use crate::prelude::*;
2020
use crate::sync::Arc;
2121
use crate::{
22-
errstr, expression, policy, script_num_size, Error, ForEachKey, Miniscript, MiniscriptKey,
23-
Satisfier, ToPublicKey, TranslateErr, Translator,
22+
expression, policy, script_num_size, Error, ForEachKey, Miniscript, MiniscriptKey, Satisfier,
23+
Threshold, ToPublicKey, TranslateErr, Translator,
2424
};
2525

2626
/// Contents of a "sortedmulti" descriptor
2727
#[derive(Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
2828
pub struct SortedMultiVec<Pk: MiniscriptKey, Ctx: ScriptContext> {
29-
/// signatures required
30-
pub k: usize,
31-
/// public keys inside sorted Multi
32-
pub pks: Vec<Pk>,
29+
inner: Threshold<Pk, MAX_PUBKEYS_PER_MULTISIG>,
3330
/// The current ScriptContext for sortedmulti
34-
pub(crate) phantom: PhantomData<Ctx>,
31+
phantom: PhantomData<Ctx>,
3532
}
3633

3734
impl<Pk: MiniscriptKey, Ctx: ScriptContext> SortedMultiVec<Pk, Ctx> {
38-
/// Create a new instance of `SortedMultiVec` given a list of keys and the threshold
39-
///
40-
/// Internally checks all the applicable size limits and pubkey types limitations according to the current `Ctx`.
41-
pub fn new(k: usize, pks: Vec<Pk>) -> Result<Self, Error> {
42-
// A sortedmulti() is only defined for <= 20 keys (it maps to CHECKMULTISIG)
43-
if pks.len() > MAX_PUBKEYS_PER_MULTISIG {
44-
return Err(Error::BadDescriptor("Too many public keys".to_string()));
45-
}
46-
35+
fn constructor_check(&self) -> Result<(), Error> {
4736
// Check the limits before creating a new SortedMultiVec
4837
// For example, under p2sh context the scriptlen can only be
4938
// upto 520 bytes.
50-
let term: Terminal<Pk, Ctx> = Terminal::Multi(k, pks.clone());
39+
let term: Terminal<Pk, Ctx> = Terminal::Multi(self.inner.k(), self.inner.data().to_owned());
5140
let ms = Miniscript::from_ast(term)?;
52-
5341
// This would check all the consensus rules for p2sh/p2wsh and
5442
// even tapscript in future
55-
Ctx::check_local_validity(&ms)?;
43+
Ctx::check_local_validity(&ms).map_err(From::from)
44+
}
5645

57-
Ok(Self { k, pks, phantom: PhantomData })
46+
/// Create a new instance of `SortedMultiVec` given a list of keys and the threshold
47+
///
48+
/// Internally checks all the applicable size limits and pubkey types limitations according to the current `Ctx`.
49+
pub fn new(k: usize, pks: Vec<Pk>) -> Result<Self, Error> {
50+
let ret =
51+
Self { inner: Threshold::new(k, pks).map_err(Error::Threshold)?, phantom: PhantomData };
52+
ret.constructor_check()?;
53+
Ok(ret)
5854
}
55+
5956
/// Parse an expression tree into a SortedMultiVec
6057
pub fn from_tree(tree: &expression::Tree) -> Result<Self, Error>
6158
where
6259
Pk: FromStr,
6360
<Pk as FromStr>::Err: fmt::Display,
6461
{
65-
if tree.args.is_empty() {
66-
return Err(errstr("no arguments given for sortedmulti"));
67-
}
68-
let k = expression::parse_num(tree.args[0].name)?;
69-
if k > (tree.args.len() - 1) as u32 {
70-
return Err(errstr("higher threshold than there were keys in sortedmulti"));
71-
}
72-
let pks: Result<Vec<Pk>, _> = tree.args[1..]
73-
.iter()
74-
.map(|sub| expression::terminal(sub, Pk::from_str))
75-
.collect();
76-
77-
pks.map(|pks| SortedMultiVec::new(k as usize, pks))?
62+
let ret = Self {
63+
inner: tree
64+
.to_null_threshold()
65+
.map_err(Error::ParseThreshold)?
66+
.translate_by_index(|i| expression::terminal(&tree.args[i + 1], Pk::from_str))?,
67+
phantom: PhantomData,
68+
};
69+
ret.constructor_check()?;
70+
Ok(ret)
7871
}
7972

8073
/// This will panic if fpk returns an uncompressed key when
@@ -88,23 +81,39 @@ impl<Pk: MiniscriptKey, Ctx: ScriptContext> SortedMultiVec<Pk, Ctx> {
8881
T: Translator<Pk, Q, FuncError>,
8982
Q: MiniscriptKey,
9083
{
91-
let pks: Result<Vec<Q>, _> = self.pks.iter().map(|pk| t.pk(pk)).collect();
92-
let res = SortedMultiVec::new(self.k, pks?).map_err(TranslateErr::OuterError)?;
93-
Ok(res)
84+
let ret = SortedMultiVec {
85+
inner: self.inner.translate_ref(|pk| t.pk(pk))?,
86+
phantom: PhantomData,
87+
};
88+
ret.constructor_check().map_err(TranslateErr::OuterError)?;
89+
Ok(ret)
9490
}
91+
92+
/// The threshold value for the multisig.
93+
pub fn k(&self) -> usize { self.inner.k() }
94+
95+
/// The number of keys in the multisig.
96+
pub fn n(&self) -> usize { self.inner.n() }
97+
98+
/// Accessor for the public keys in the multisig.
99+
///
100+
/// The keys in this structure might **not** be sorted. In general, they cannot be
101+
/// sorted until they are converted to consensus-encoded public keys, which may not
102+
/// be possible (for example for BIP32 paths with unfilled wildcards).
103+
pub fn pks(&self) -> &[Pk] { self.inner.data() }
95104
}
96105

97106
impl<Pk: MiniscriptKey, Ctx: ScriptContext> ForEachKey<Pk> for SortedMultiVec<Pk, Ctx> {
98107
fn for_each_key<'a, F: FnMut(&'a Pk) -> bool>(&'a self, pred: F) -> bool {
99-
self.pks.iter().all(pred)
108+
self.pks().iter().all(pred)
100109
}
101110
}
102111

103112
impl<Pk: MiniscriptKey, Ctx: ScriptContext> SortedMultiVec<Pk, Ctx> {
104113
/// utility function to sanity a sorted multi vec
105114
pub fn sanity_check(&self) -> Result<(), Error> {
106115
let ms: Miniscript<Pk, Ctx> =
107-
Miniscript::from_ast(Terminal::Multi(self.k, self.pks.clone()))
116+
Miniscript::from_ast(Terminal::Multi(self.k(), self.pks().to_owned()))
108117
.expect("Must typecheck");
109118
// '?' for doing From conversion
110119
ms.sanity_check()?;
@@ -118,7 +127,7 @@ impl<Pk: MiniscriptKey, Ctx: ScriptContext> SortedMultiVec<Pk, Ctx> {
118127
where
119128
Pk: ToPublicKey,
120129
{
121-
let mut pks = self.pks.clone();
130+
let mut pks = self.pks().to_owned();
122131
// Sort pubkeys lexicographically according to BIP 67
123132
pks.sort_by(|a, b| {
124133
a.to_public_key()
@@ -127,7 +136,7 @@ impl<Pk: MiniscriptKey, Ctx: ScriptContext> SortedMultiVec<Pk, Ctx> {
127136
.partial_cmp(&b.to_public_key().inner.serialize())
128137
.unwrap()
129138
});
130-
Terminal::Multi(self.k, pks)
139+
Terminal::Multi(self.k(), pks)
131140
}
132141

133142
/// Encode as a Bitcoin script
@@ -169,10 +178,10 @@ impl<Pk: MiniscriptKey, Ctx: ScriptContext> SortedMultiVec<Pk, Ctx> {
169178
/// to instead call the corresponding function on a `Descriptor`, which
170179
/// will handle the segwit/non-segwit technicalities for you.
171180
pub fn script_size(&self) -> usize {
172-
script_num_size(self.k)
181+
script_num_size(self.k())
173182
+ 1
174-
+ script_num_size(self.pks.len())
175-
+ self.pks.iter().map(|pk| Ctx::pk_len(pk)).sum::<usize>()
183+
+ script_num_size(self.n())
184+
+ self.pks().iter().map(|pk| Ctx::pk_len(pk)).sum::<usize>()
176185
}
177186

178187
/// Maximum number of witness elements used to satisfy the Miniscript
@@ -183,7 +192,7 @@ impl<Pk: MiniscriptKey, Ctx: ScriptContext> SortedMultiVec<Pk, Ctx> {
183192
/// This function may panic on malformed `Miniscript` objects which do
184193
/// not correspond to semantically sane Scripts. (Such scripts should be
185194
/// rejected at parse time. Any exceptions are bugs.)
186-
pub fn max_satisfaction_witness_elements(&self) -> usize { 2 + self.k }
195+
pub fn max_satisfaction_witness_elements(&self) -> usize { 2 + self.k() }
187196

188197
/// Maximum size, in bytes, of a satisfying witness.
189198
/// In general, it is not recommended to use this function directly, but
@@ -193,14 +202,14 @@ impl<Pk: MiniscriptKey, Ctx: ScriptContext> SortedMultiVec<Pk, Ctx> {
193202
/// All signatures are assumed to be 73 bytes in size, including the
194203
/// length prefix (segwit) or push opcode (pre-segwit) and sighash
195204
/// postfix.
196-
pub fn max_satisfaction_size(&self) -> usize { 1 + 73 * self.k }
205+
pub fn max_satisfaction_size(&self) -> usize { 1 + 73 * self.k() }
197206
}
198207

199208
impl<Pk: MiniscriptKey, Ctx: ScriptContext> policy::Liftable<Pk> for SortedMultiVec<Pk, Ctx> {
200209
fn lift(&self) -> Result<policy::semantic::Policy<Pk>, Error> {
201210
let ret = policy::semantic::Policy::Thresh(
202-
self.k,
203-
self.pks
211+
self.k(),
212+
self.pks()
204213
.iter()
205214
.map(|k| Arc::new(policy::semantic::Policy::Key(k.clone())))
206215
.collect(),
@@ -215,11 +224,7 @@ impl<Pk: MiniscriptKey, Ctx: ScriptContext> fmt::Debug for SortedMultiVec<Pk, Ct
215224

216225
impl<Pk: MiniscriptKey, Ctx: ScriptContext> fmt::Display for SortedMultiVec<Pk, Ctx> {
217226
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
218-
write!(f, "sortedmulti({}", self.k)?;
219-
for k in &self.pks {
220-
write!(f, ",{}", k)?;
221-
}
222-
f.write_str(")")
227+
fmt::Display::fmt(&self.inner.display("sortedmulti", true), f)
223228
}
224229
}
225230

@@ -249,7 +254,7 @@ mod tests {
249254
let error = res.expect_err("constructor should err");
250255

251256
match error {
252-
Error::BadDescriptor(_) => {} // ok
257+
Error::Threshold(_) => {} // ok
253258
other => panic!("unexpected error: {:?}", other),
254259
}
255260
}

0 commit comments

Comments
 (0)