Skip to content

Commit 8331d55

Browse files
committed
Merge #849: several improvements and bugfixes to uncompressed key handling and sortedmulti
b5db340 sortedmulti: take Thresh as input to constructors (Andrew Poelstra) 276cf94 sortedmulti: simplify constructor and improve unit test (Andrew Poelstra) 3250e40 miniscript: take pubkey size into account in multi() type extdata (Andrew Poelstra) 8874640 miniscript: fix ExtData to correctly account for uncompressed keys (Andrew Poelstra) 040c29d miniscript: remove MalleablePkH script context rule (Andrew Poelstra) a5c477b clippy: fix all nightly lints (Andrew Poelstra) Pull request description: Removes the weird and incorrect `MalleablePkH` script context rule; fixes several bugs related to key size estimation in pkh and multisig fragments; improves tests of sortedmulti; uses the `Thresh` type in sortedmulti constructors. These bugs are obscure and only visible when using uncompressed pubkeys and I don't think they're worth backporting. This PR is starting to make "real" changes toward unifying our validation parameters. In particular we separate out validation errors in the `sortedmulti` constructors from threshold-construction errors (which the user is forced to deal with before calling a `sortedmulti` constructor). The PR is bigger than you might think because when testing this separation I found some bugs. This PR also includes a clippy fix which should unstick #805 (update nightly version) which has been stalled for months because the weekly retries don't trigger Github notifications and I didn't notice it. ACKs for top commit: sanket1729: utACK b5db340 Tree-SHA512: 4c221f4efe1014001d4bbb99fd89728f5b56348c09d34df9163ff01766cd11e5d13834ecb3bc7ceb49ae89a99d6149587d83b0d901f2b73c64cc91c9a47684fc
2 parents bce485b + b5db340 commit 8331d55

File tree

14 files changed

+94
-86
lines changed

14 files changed

+94
-86
lines changed

src/descriptor/key.rs

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1203,9 +1203,7 @@ impl<K: InnerXKey> DescriptorXKey<K> {
12031203
};
12041204

12051205
if &compare_fingerprint == fingerprint
1206-
&& compare_path
1207-
.into_iter()
1208-
.eq(path_excluding_wildcard.into_iter())
1206+
&& compare_path.into_iter().eq(&path_excluding_wildcard)
12091207
{
12101208
Some(path_excluding_wildcard)
12111209
} else {

src/descriptor/mod.rs

Lines changed: 15 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -23,12 +23,13 @@ use sync::Arc;
2323

2424
use crate::expression::FromTree as _;
2525
use crate::miniscript::decode::Terminal;
26+
use crate::miniscript::limits::MAX_PUBKEYS_PER_MULTISIG;
2627
use crate::miniscript::{satisfy, Legacy, Miniscript, Segwitv0};
2728
use crate::plan::{AssetProvider, Plan};
2829
use crate::prelude::*;
2930
use crate::{
3031
expression, hash256, BareCtx, Error, ForEachKey, FromStrKey, MiniscriptKey, ParseError,
31-
Satisfier, ToPublicKey, TranslateErr, Translator,
32+
Satisfier, Threshold, ToPublicKey, TranslateErr, Translator,
3233
};
3334

3435
mod bare;
@@ -217,21 +218,27 @@ impl<Pk: MiniscriptKey> Descriptor<Pk> {
217218
/// Create a new sh sortedmulti descriptor with threshold `k`
218219
/// and Vec of `pks`.
219220
/// Errors when miniscript exceeds resource limits under p2sh context
220-
pub fn new_sh_sortedmulti(k: usize, pks: Vec<Pk>) -> Result<Self, Error> {
221-
Ok(Descriptor::Sh(Sh::new_sortedmulti(k, pks)?))
221+
pub fn new_sh_sortedmulti(
222+
thresh: Threshold<Pk, MAX_PUBKEYS_PER_MULTISIG>,
223+
) -> Result<Self, Error> {
224+
Ok(Descriptor::Sh(Sh::new_sortedmulti(thresh)?))
222225
}
223226

224227
/// Create a new sh wrapped wsh sortedmulti descriptor from threshold
225228
/// `k` and Vec of `pks`
226229
/// Errors when miniscript exceeds resource limits under segwit context
227-
pub fn new_sh_wsh_sortedmulti(k: usize, pks: Vec<Pk>) -> Result<Self, Error> {
228-
Ok(Descriptor::Sh(Sh::new_wsh_sortedmulti(k, pks)?))
230+
pub fn new_sh_wsh_sortedmulti(
231+
thresh: Threshold<Pk, MAX_PUBKEYS_PER_MULTISIG>,
232+
) -> Result<Self, Error> {
233+
Ok(Descriptor::Sh(Sh::new_wsh_sortedmulti(thresh)?))
229234
}
230235

231236
/// Create a new wsh sorted multi descriptor
232237
/// Errors when miniscript exceeds resource limits under p2sh context
233-
pub fn new_wsh_sortedmulti(k: usize, pks: Vec<Pk>) -> Result<Self, Error> {
234-
Ok(Descriptor::Wsh(Wsh::new_sortedmulti(k, pks)?))
238+
pub fn new_wsh_sortedmulti(
239+
thresh: Threshold<Pk, MAX_PUBKEYS_PER_MULTISIG>,
240+
) -> Result<Self, Error> {
241+
Ok(Descriptor::Wsh(Wsh::new_sortedmulti(thresh)?))
235242
}
236243

237244
/// Create new tr descriptor
@@ -288,7 +295,7 @@ impl<Pk: MiniscriptKey> Descriptor<Pk> {
288295
///
289296
/// If the descriptor is not a Taproot descriptor, **or** if the descriptor is a
290297
/// Taproot descriptor containing only a keyspend, returns an empty iterator.
291-
pub fn tap_tree_iter(&self) -> tr::TapTreeIter<Pk> {
298+
pub fn tap_tree_iter(&self) -> tr::TapTreeIter<'_, Pk> {
292299
if let Descriptor::Tr(ref tr) = self {
293300
if let Some(tree) = tr.tap_tree() {
294301
return tree.leaves();

src/descriptor/segwitv0.rs

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -14,14 +14,15 @@ use super::SortedMultiVec;
1414
use crate::descriptor::{write_descriptor, DefiniteDescriptorKey};
1515
use crate::expression::{self, FromTree};
1616
use crate::miniscript::context::{ScriptContext, ScriptContextError};
17+
use crate::miniscript::limits::MAX_PUBKEYS_PER_MULTISIG;
1718
use crate::miniscript::satisfy::{Placeholder, Satisfaction, Witness};
1819
use crate::plan::AssetProvider;
1920
use crate::policy::{semantic, Liftable};
2021
use crate::prelude::*;
2122
use crate::util::varint_len;
2223
use crate::{
23-
Error, ForEachKey, FromStrKey, Miniscript, MiniscriptKey, Satisfier, Segwitv0, ToPublicKey,
24-
TranslateErr, Translator,
24+
Error, ForEachKey, FromStrKey, Miniscript, MiniscriptKey, Satisfier, Segwitv0, Threshold,
25+
ToPublicKey, TranslateErr, Translator,
2526
};
2627
/// A Segwitv0 wsh descriptor
2728
#[derive(Clone, Ord, PartialOrd, Eq, PartialEq, Hash)]
@@ -45,10 +46,10 @@ impl<Pk: MiniscriptKey> Wsh<Pk> {
4546
}
4647

4748
/// Create a new sortedmulti wsh descriptor
48-
pub fn new_sortedmulti(k: usize, pks: Vec<Pk>) -> Result<Self, Error> {
49+
pub fn new_sortedmulti(thresh: Threshold<Pk, MAX_PUBKEYS_PER_MULTISIG>) -> Result<Self, Error> {
4950
// The context checks will be carried out inside new function for
5051
// sortedMultiVec
51-
Ok(Self { inner: WshInner::SortedMulti(SortedMultiVec::new(k, pks)?) })
52+
Ok(Self { inner: WshInner::SortedMulti(SortedMultiVec::new(thresh)?) })
5253
}
5354

5455
/// Get the descriptor without the checksum

src/descriptor/sh.rs

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -17,14 +17,15 @@ use super::{SortedMultiVec, Wpkh, Wsh};
1717
use crate::descriptor::{write_descriptor, DefiniteDescriptorKey};
1818
use crate::expression::{self, FromTree};
1919
use crate::miniscript::context::ScriptContext;
20+
use crate::miniscript::limits::MAX_PUBKEYS_PER_MULTISIG;
2021
use crate::miniscript::satisfy::{Placeholder, Satisfaction};
2122
use crate::plan::AssetProvider;
2223
use crate::policy::{semantic, Liftable};
2324
use crate::prelude::*;
2425
use crate::util::{varint_len, witness_to_scriptsig};
2526
use crate::{
2627
push_opcode_size, Error, ForEachKey, FromStrKey, Legacy, Miniscript, MiniscriptKey, Satisfier,
27-
Segwitv0, ToPublicKey, TranslateErr, Translator,
28+
Segwitv0, Threshold, ToPublicKey, TranslateErr, Translator,
2829
};
2930

3031
/// A Legacy p2sh Descriptor
@@ -125,10 +126,10 @@ impl<Pk: MiniscriptKey> Sh<Pk> {
125126

126127
/// Create a new p2sh sortedmulti descriptor with threshold `k`
127128
/// and Vec of `pks`.
128-
pub fn new_sortedmulti(k: usize, pks: Vec<Pk>) -> Result<Self, Error> {
129+
pub fn new_sortedmulti(thresh: Threshold<Pk, MAX_PUBKEYS_PER_MULTISIG>) -> Result<Self, Error> {
129130
// The context checks will be carried out inside new function for
130131
// sortedMultiVec
131-
Ok(Self { inner: ShInner::SortedMulti(SortedMultiVec::new(k, pks)?) })
132+
Ok(Self { inner: ShInner::SortedMulti(SortedMultiVec::new(thresh)?) })
132133
}
133134

134135
/// Create a new p2sh wrapped wsh descriptor with the raw miniscript
@@ -152,10 +153,12 @@ impl<Pk: MiniscriptKey> Sh<Pk> {
152153

153154
/// Create a new p2sh wrapped wsh sortedmulti descriptor from threshold
154155
/// `k` and Vec of `pks`
155-
pub fn new_wsh_sortedmulti(k: usize, pks: Vec<Pk>) -> Result<Self, Error> {
156+
pub fn new_wsh_sortedmulti(
157+
thresh: Threshold<Pk, MAX_PUBKEYS_PER_MULTISIG>,
158+
) -> Result<Self, Error> {
156159
// The context checks will be carried out inside new function for
157160
// sortedMultiVec
158-
Ok(Self { inner: ShInner::Wsh(Wsh::new_sortedmulti(k, pks)?) })
161+
Ok(Self { inner: ShInner::Wsh(Wsh::new_sortedmulti(thresh)?) })
159162
}
160163

161164
/// Create a new p2sh wrapped wpkh from `Pk`

src/descriptor/sortedmulti.rs

Lines changed: 13 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -33,13 +33,10 @@ pub struct SortedMultiVec<Pk: MiniscriptKey, Ctx: ScriptContext> {
3333

3434
impl<Pk: MiniscriptKey, Ctx: ScriptContext> SortedMultiVec<Pk, Ctx> {
3535
fn constructor_check(mut self) -> Result<Self, Error> {
36+
let ms = Miniscript::<Pk, Ctx>::multi(self.inner);
3637
// Check the limits before creating a new SortedMultiVec
3738
// For example, under p2sh context the scriptlen can only be
3839
// upto 520 bytes.
39-
let term: Terminal<Pk, Ctx> = Terminal::Multi(self.inner);
40-
let ms = Miniscript::from_ast(term)?;
41-
// This would check all the consensus rules for p2sh/p2wsh and
42-
// even tapscript in future
4340
Ctx::check_local_validity(&ms)?;
4441
if let Terminal::Multi(inner) = ms.node {
4542
self.inner = inner;
@@ -52,9 +49,8 @@ impl<Pk: MiniscriptKey, Ctx: ScriptContext> SortedMultiVec<Pk, Ctx> {
5249
/// Create a new instance of `SortedMultiVec` given a list of keys and the threshold
5350
///
5451
/// Internally checks all the applicable size limits and pubkey types limitations according to the current `Ctx`.
55-
pub fn new(k: usize, pks: Vec<Pk>) -> Result<Self, Error> {
56-
let ret =
57-
Self { inner: Threshold::new(k, pks).map_err(Error::Threshold)?, phantom: PhantomData };
52+
pub fn new(thresh: Threshold<Pk, MAX_PUBKEYS_PER_MULTISIG>) -> Result<Self, Error> {
53+
let ret = Self { inner: thresh, phantom: PhantomData };
5854
ret.constructor_check()
5955
}
6056

@@ -229,31 +225,27 @@ impl<Pk: MiniscriptKey, Ctx: ScriptContext> fmt::Display for SortedMultiVec<Pk,
229225
mod tests {
230226
use core::str::FromStr as _;
231227

232-
use bitcoin::secp256k1::PublicKey;
228+
use bitcoin::PublicKey;
233229

234230
use super::*;
235-
use crate::miniscript::context::Legacy;
231+
use crate::miniscript::context::{Legacy, ScriptContextError};
236232

237233
#[test]
238-
fn too_many_pubkeys() {
239-
// Arbitrary pubic key.
234+
fn too_many_pubkeys_for_p2sh() {
235+
// Arbitrary 65-byte public key (66 with length prefix).
240236
let pk = PublicKey::from_str(
241-
"02e6642fd69bd211f93f7f1f36ca51a26a5290eb2dd1b0d8279a87bb0d480c8443",
237+
"0400232a2acfc9b43fa89f1b4f608fde335d330d7114f70ea42bfb4a41db368a3e3be6934a4097dd25728438ef73debb1f2ffdb07fec0f18049df13bdc5285dc5b",
242238
)
243239
.unwrap();
244240

245-
let over = 1 + MAX_PUBKEYS_PER_MULTISIG;
246-
247-
let mut pks = Vec::new();
248-
for _ in 0..over {
249-
pks.push(pk);
250-
}
251-
252-
let res: Result<SortedMultiVec<PublicKey, Legacy>, Error> = SortedMultiVec::new(0, pks);
241+
// This is legal for CHECKMULTISIG, but the 8 keys consume the whole 520 bytes
242+
// allowed by P2SH, meaning that the full script goes over the limit.
243+
let thresh = Threshold::new(2, vec![pk; 8]).expect("the thresh is ok..");
244+
let res: Result<SortedMultiVec<PublicKey, Legacy>, Error> = SortedMultiVec::new(thresh);
253245
let error = res.expect_err("constructor should err");
254246

255247
match error {
256-
Error::Threshold(_) => {} // ok
248+
Error::ContextError(ScriptContextError::MaxRedeemScriptSizeExceeded { .. }) => {} // ok
257249
other => panic!("unexpected error: {:?}", other),
258250
}
259251
}

src/descriptor/tr/mod.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -108,7 +108,7 @@ impl<Pk: MiniscriptKey> Tr<Pk> {
108108
///
109109
/// The yielded elements include the Miniscript for each leave as well as its depth
110110
/// in the tree, which is the data required by PSBT (BIP 371).
111-
pub fn leaves(&self) -> TapTreeIter<Pk> {
111+
pub fn leaves(&self) -> TapTreeIter<'_, Pk> {
112112
match self.tree {
113113
Some(ref t) => t.leaves(),
114114
None => TapTreeIter::empty(),

src/descriptor/tr/spend_info.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -181,7 +181,7 @@ impl<Pk: ToPublicKey> TrSpendInfo<Pk> {
181181
/// This yields the same leaves in the same order as [`super::Tr::leaves`] on the original
182182
/// [`super::Tr`]. However, in addition to yielding the leaves and their depths, it also
183183
/// yields their scripts, leafhashes, and control blocks.
184-
pub fn leaves(&self) -> TrSpendInfoIter<Pk> {
184+
pub fn leaves(&self) -> TrSpendInfoIter<'_, Pk> {
185185
TrSpendInfoIter {
186186
spend_info: self,
187187
index: 0,

src/descriptor/tr/taptree.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,7 @@ impl<Pk: MiniscriptKey> TapTree<Pk> {
5353
///
5454
/// The yielded elements include the Miniscript for each leave as well as its depth
5555
/// in the tree, which is the data required by PSBT (BIP 371).
56-
pub fn leaves(&self) -> TapTreeIter<Pk> { TapTreeIter::from_tree(self) }
56+
pub fn leaves(&self) -> TapTreeIter<'_, Pk> { TapTreeIter::from_tree(self) }
5757

5858
/// Converts keys from one type of public key to another.
5959
pub fn translate_pk<T>(

src/miniscript/context.rs

Lines changed: 1 addition & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -21,10 +21,6 @@ use crate::{hash256, Error, ForEachKey, Miniscript, MiniscriptKey, Terminal};
2121
/// Error for Script Context
2222
#[derive(Clone, PartialEq, Eq, Debug)]
2323
pub enum ScriptContextError {
24-
/// Script Context does not permit PkH for non-malleability
25-
/// It is not possible to estimate the pubkey size at the creation
26-
/// time because of uncompressed pubkeys
27-
MalleablePkH,
2824
/// Script Context does not permit OrI for non-malleability
2925
/// Legacy fragments allow non-minimal IF which results in malleability
3026
MalleableOrI,
@@ -74,8 +70,7 @@ impl error::Error for ScriptContextError {
7470
use self::ScriptContextError::*;
7571

7672
match self {
77-
MalleablePkH
78-
| MalleableOrI
73+
MalleableOrI
7974
| MalleableDupIf
8075
| CompressedOnly(_)
8176
| XOnlyKeysNotAllowed(_, _)
@@ -97,7 +92,6 @@ impl error::Error for ScriptContextError {
9792
impl fmt::Display for ScriptContextError {
9893
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
9994
match *self {
100-
ScriptContextError::MalleablePkH => write!(f, "PkH is malleable under Legacy rules"),
10195
ScriptContextError::MalleableOrI => write!(f, "OrI is malleable under Legacy rules"),
10296
ScriptContextError::MalleableDupIf => {
10397
write!(f, "DupIf is malleable under Legacy rules")
@@ -380,8 +374,6 @@ impl ScriptContext for Legacy {
380374
frag: &Terminal<Pk, Self>,
381375
) -> Result<(), ScriptContextError> {
382376
match *frag {
383-
Terminal::PkH(ref _pkh) => Err(ScriptContextError::MalleablePkH),
384-
Terminal::RawPkH(ref _pk) => Err(ScriptContextError::MalleablePkH),
385377
Terminal::OrI(ref _a, ref _b) => Err(ScriptContextError::MalleableOrI),
386378
Terminal::DupIf(ref _ms) => Err(ScriptContextError::MalleableDupIf),
387379
_ => Ok(()),

src/miniscript/iter.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,12 +18,12 @@ impl<Pk: MiniscriptKey, Ctx: ScriptContext> Miniscript<Pk, Ctx> {
1818
/// Creates a new [Iter] iterator that will iterate over all [Miniscript] items within
1919
/// AST by traversing its branches. For the specific algorithm please see
2020
/// [Iter::next] function.
21-
pub fn iter(&self) -> Iter<Pk, Ctx> { Iter::new(self) }
21+
pub fn iter(&self) -> Iter<'_, Pk, Ctx> { Iter::new(self) }
2222

2323
/// Creates a new [PkIter] iterator that will iterate over all plain public keys (and not
2424
/// key hash values) present in [Miniscript] items within AST by traversing all its branches.
2525
/// For the specific algorithm please see [PkIter::next] function.
26-
pub fn iter_pk(&self) -> PkIter<Pk, Ctx> { PkIter::new(self) }
26+
pub fn iter_pk(&self) -> PkIter<'_, Pk, Ctx> { PkIter::new(self) }
2727

2828
/// Enumerates all child nodes of the current AST node (`self`) and returns a `Vec` referencing
2929
/// them.

0 commit comments

Comments
 (0)