Skip to content

Commit 5ec50b5

Browse files
committed
Updated Bare descriptor to use Bare Ctx
Updated Bare descriptor to user Bare context for standardness checks instead of previously incorrect Legacy context Combine top level checks
1 parent c60a4e9 commit 5ec50b5

File tree

8 files changed

+111
-66
lines changed

8 files changed

+111
-66
lines changed

src/descriptor/create_descriptor.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ use bitcoin::blockdata::script::Instruction;
99
use descriptor::satisfied_constraints::Error as IntError;
1010
use descriptor::satisfied_constraints::{Stack, StackElement};
1111
use descriptor::Descriptor;
12-
use miniscript::{Legacy, Miniscript, Segwitv0};
12+
use miniscript::{Bare, Legacy, Miniscript, Segwitv0};
1313
use Error;
1414
use ToPublicKey;
1515

@@ -233,7 +233,7 @@ pub fn from_txin_with_witness_stack<'txin>(
233233
if !witness.is_empty() {
234234
return Err(Error::NonEmptyWitness);
235235
}
236-
let ms = Miniscript::<bitcoin::PublicKey, Legacy>::parse(script_pubkey)?;
236+
let ms = Miniscript::<bitcoin::PublicKey, Bare>::parse(script_pubkey)?;
237237
Ok((Descriptor::Bare(ms), Stack(stack?)))
238238
}
239239
}

src/descriptor/mod.rs

Lines changed: 26 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -37,13 +37,12 @@ use bitcoin::{self, Script};
3737
use serde::{de, ser};
3838

3939
use expression;
40-
use miniscript;
41-
use miniscript::context::ScriptContextError;
42-
use miniscript::{Legacy, Miniscript, Segwitv0};
40+
use miniscript::context::{ScriptContext, ScriptContextError};
4341
use Error;
4442
use MiniscriptKey;
4543
use Satisfier;
4644
use ToPublicKey;
45+
use {Bare, Legacy, Miniscript, Segwitv0};
4746

4847
mod create_descriptor;
4948
mod satisfied_constraints;
@@ -58,7 +57,7 @@ pub use self::satisfied_constraints::Stack;
5857
#[derive(Clone, PartialEq, Eq, PartialOrd, Ord)]
5958
pub enum Descriptor<Pk: MiniscriptKey> {
6059
/// A raw scriptpubkey (including pay-to-pubkey) under Legacy context
61-
Bare(Miniscript<Pk, Legacy>),
60+
Bare(Miniscript<Pk, Bare>),
6261
/// Pay-to-Pubkey
6362
Pk(Pk),
6463
/// Pay-to-PubKey-Hash
@@ -747,11 +746,8 @@ where
747746
match (newtop.name, newtop.args.len()) {
748747
("wsh", 1) => {
749748
let sub = Miniscript::from_tree(&newtop.args[0])?;
750-
if sub.ty.corr.base != miniscript::types::Base::B {
751-
Err(Error::NonTopLevel(format!("{:?}", sub)))
752-
} else {
753-
Ok(Descriptor::ShWsh(sub))
754-
}
749+
Segwitv0::top_level_checks(&sub)?;
750+
Ok(Descriptor::ShWsh(sub))
755751
}
756752
("wpkh", 1) => {
757753
let wpkh = expression::terminal(&newtop.args[0], |pk| Pk::from_str(pk))?;
@@ -763,29 +759,20 @@ where
763759
}
764760
_ => {
765761
let sub = Miniscript::from_tree(&top.args[0])?;
766-
if sub.ty.corr.base != miniscript::types::Base::B {
767-
Err(Error::NonTopLevel(format!("{:?}", sub)))
768-
} else {
769-
Ok(Descriptor::Sh(sub))
770-
}
762+
Legacy::top_level_checks(&sub)?;
763+
Ok(Descriptor::Sh(sub))
771764
}
772765
}
773766
}
774767
("wsh", 1) => {
775768
let sub = Miniscript::from_tree(&top.args[0])?;
776-
if sub.ty.corr.base != miniscript::types::Base::B {
777-
Err(Error::NonTopLevel(format!("{:?}", sub)))
778-
} else {
779-
Ok(Descriptor::Wsh(sub))
780-
}
769+
Segwitv0::top_level_checks(&sub)?;
770+
Ok(Descriptor::Wsh(sub))
781771
}
782772
_ => {
783773
let sub = Miniscript::from_tree(&top)?;
784-
if sub.ty.corr.base != miniscript::types::Base::B {
785-
Err(Error::NonTopLevel(format!("{:?}", sub)))
786-
} else {
787-
Ok(Descriptor::Bare(sub))
788-
}
774+
Bare::top_level_checks(&sub)?;
775+
Ok(Descriptor::Bare(sub))
789776
}
790777
}
791778
}
@@ -855,6 +842,7 @@ mod tests {
855842
use bitcoin::util::bip32;
856843
use bitcoin::{self, secp256k1, PublicKey};
857844
use descriptor::{DescriptorPublicKey, DescriptorSinglePub, DescriptorXPub};
845+
use hex_script;
858846
use miniscript::satisfy::BitcoinSig;
859847
use std::collections::HashMap;
860848
use std::str::FromStr;
@@ -902,14 +890,25 @@ mod tests {
902890
StdDescriptor::from_str(&format!("sh(wpkh({}))", uncompressed_pk)).unwrap_err();
903891
StdDescriptor::from_str(&format!("wsh(pk{})", uncompressed_pk)).unwrap_err();
904892
StdDescriptor::from_str(&format!("sh(wsh(pk{}))", uncompressed_pk)).unwrap_err();
893+
StdDescriptor::from_str(&format!(
894+
"or_i(pk({}),pk({}))",
895+
uncompressed_pk, uncompressed_pk
896+
))
897+
.unwrap_err();
905898
}
906899

907900
#[test]
908901
pub fn script_pubkey() {
909-
let bare = StdDescriptor::from_str("older(1000)").unwrap();
902+
let bare = StdDescriptor::from_str(&format!(
903+
"multi(1,020000000000000000000000000000000000000000000000000000000000000002)"
904+
))
905+
.unwrap();
906+
println!("{:x}", bare.script_pubkey());
910907
assert_eq!(
911908
bare.script_pubkey(),
912-
bitcoin::Script::from(vec![0x02, 0xe8, 0x03, 0xb2])
909+
hex_script(
910+
"512102000000000000000000000000000000000000000000000000000000000000000251ae"
911+
)
913912
);
914913
assert_eq!(bare.address(bitcoin::Network::Bitcoin), None);
915914

@@ -1175,6 +1174,7 @@ mod tests {
11751174
.into_script()
11761175
);
11771176

1177+
let ms = ms_str!("c:pk_k({})", pk);
11781178
let sh = Descriptor::Sh(ms.clone());
11791179
sh.satisfy(&mut txin, &satisfier).expect("satisfaction");
11801180
assert_eq!(

src/descriptor/satisfied_constraints.rs

Lines changed: 26 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -309,21 +309,32 @@ where
309309
has_errored: false,
310310
}
311311
}
312-
&Descriptor::Sh(ref miniscript) | &Descriptor::Bare(ref miniscript) => {
313-
SatisfiedConstraints {
314-
verify_sig: verify_sig,
315-
public_key: None,
316-
state: vec![NodeEvaluationState {
317-
node: Any::from_legacy(miniscript),
318-
n_evaluated: 0,
319-
n_satisfied: 0,
320-
}],
321-
stack: stack,
322-
age,
323-
height,
324-
has_errored: false,
325-
}
326-
}
312+
&Descriptor::Sh(ref miniscript) => SatisfiedConstraints {
313+
verify_sig: verify_sig,
314+
public_key: None,
315+
state: vec![NodeEvaluationState {
316+
node: Any::from_legacy(miniscript),
317+
n_evaluated: 0,
318+
n_satisfied: 0,
319+
}],
320+
stack: stack,
321+
age,
322+
height,
323+
has_errored: false,
324+
},
325+
&Descriptor::Bare(ref miniscript) => SatisfiedConstraints {
326+
verify_sig: verify_sig,
327+
public_key: None,
328+
state: vec![NodeEvaluationState {
329+
node: Any::from_bare(miniscript),
330+
n_evaluated: 0,
331+
n_satisfied: 0,
332+
}],
333+
stack: stack,
334+
age,
335+
height,
336+
has_errored: false,
337+
},
327338
}
328339
}
329340
}

src/lib.rs

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -347,6 +347,9 @@ pub enum Error {
347347
MaxRecursiveDepthExceeded,
348348
/// Script size too large
349349
ScriptSizeTooLarge,
350+
/// Anything but c:pk(key) (P2PK), c:pk_h(key) (P2PKH), and thresh_m(k,...)
351+
/// up to n=3 is invalid by standardness (bare)
352+
NonStandardBareScript,
350353
}
351354

352355
#[doc(hidden)]
@@ -458,6 +461,12 @@ impl fmt::Display for Error {
458461
"Standardness rules imply bitcoin than {} bytes",
459462
MAX_SCRIPT_SIZE
460463
),
464+
Error::NonStandardBareScript => write!(
465+
f,
466+
"Anything but c:pk(key) (P2PK), c:pk_h(key) (P2PKH), and thresh_m(k,...) \
467+
up to n=3 is invalid by standardness (bare).
468+
"
469+
),
461470
}
462471
}
463472
}

src/miniscript/context.rs

Lines changed: 43 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -12,13 +12,14 @@
1212
// If not, see <http://creativecommons.org/publicdomain/zero/1.0/>.
1313
//
1414

15+
use miniscript::types;
1516
use miniscript::types::extra_props::{
1617
MAX_OPS_PER_SCRIPT, MAX_SCRIPTSIG_SIZE, MAX_SCRIPT_ELEMENT_SIZE, MAX_SCRIPT_SIZE,
1718
MAX_STANDARD_P2WSH_SCRIPT_SIZE, MAX_STANDARD_P2WSH_STACK_ITEMS,
1819
};
1920
use std::fmt;
21+
use Error;
2022
use {Miniscript, MiniscriptKey, Terminal};
21-
2223
/// Error for Script Context
2324
#[derive(Copy, Clone, PartialEq, Eq, Debug)]
2425
pub enum ScriptContextError {
@@ -49,9 +50,6 @@ pub enum ScriptContextError {
4950
MaxRedeemScriptSizeExceeded,
5051
/// The policy rules of bitcoin core only permit Script size upto 1650 bytes
5152
MaxScriptSigSizeExceeded,
52-
/// Anything but c:pk(key) (P2PK), c:pk_h(key) (P2PKH), and thresh_m(k,...)
53-
/// up to n=3 is invalid by standardness (bare)
54-
NonStandardBareScript,
5553
}
5654

5755
impl fmt::Display for ScriptContextError {
@@ -90,12 +88,6 @@ impl fmt::Display for ScriptContextError {
9088
"Atleast one satisfaction in Miniscript would be larger than \
9189
MAX_SCRIPTSIG_SIZE scriptsig"
9290
),
93-
ScriptContextError::NonStandardBareScript => write!(
94-
f,
95-
"Anything but c:pk(key) (P2PK), c:pk_h(key) (P2PKH), and thresh_m(k,...) \
96-
up to n=3 is invalid by standardness (bare).
97-
"
98-
),
9991
}
10092
}
10193
}
@@ -137,8 +129,6 @@ pub trait ScriptContext:
137129
/// it is safe to discard them. (unless explicity disabled by non-standard flag)
138130
/// For example, in Segwit Context with MiniscriptKey as bitcoin::PublicKey
139131
/// scripts over 3600 bytes are invalid.
140-
/// In Bare context, only c:pk(key) (P2PK),
141-
/// c:pk_h(key) (P2PKH), and thresh_m(k,...) up to n=3 are allowed
142132
/// Post Tapscript upgrade, this would have to consider other nodes.
143133
/// This does *NOT* recursively check the miniscript fragments.
144134
fn check_non_satisfaction_policy_rules<Pk: MiniscriptKey, Ctx: ScriptContext>(
@@ -189,6 +179,39 @@ pub trait ScriptContext:
189179
Self::check_satisfaction_policy_rules(ms)?;
190180
Ok(())
191181
}
182+
183+
/// Check whether the top-level is type B
184+
fn top_level_type_check<Pk: MiniscriptKey, Ctx: ScriptContext>(
185+
ms: &Miniscript<Pk, Ctx>,
186+
) -> Result<(), Error> {
187+
if ms.ty.corr.base != types::Base::B {
188+
return Err(Error::NonTopLevel(format!("{:?}", ms)));
189+
}
190+
Ok(())
191+
}
192+
193+
/// Other top level checks that are context specific
194+
fn other_top_level_checks<Pk: MiniscriptKey, Ctx: ScriptContext>(
195+
_ms: &Miniscript<Pk, Ctx>,
196+
) -> Result<(), Error> {
197+
Ok(())
198+
}
199+
200+
/// Check top level consensus rules.
201+
// All the preivous check_ were applied at each fragment while parsing script
202+
// Because if any of sub-miniscripts failed the reource level check, the entire
203+
// miniscript would also be invalid. However, there are certain checks like
204+
// in Bare context, only c:pk(key) (P2PK),
205+
// c:pk_h(key) (P2PKH), and thresh_m(k,...) up to n=3 are allowed
206+
// that are only applicable at the top-level
207+
// We can also combine the top-level check for Base::B here
208+
// even though it does not depend on context, but helps in cleaner code
209+
fn top_level_checks<Pk: MiniscriptKey, Ctx: ScriptContext>(
210+
ms: &Miniscript<Pk, Ctx>,
211+
) -> Result<(), Error> {
212+
Self::top_level_type_check(ms)?;
213+
Self::other_top_level_checks(ms)
214+
}
192215
}
193216

194217
/// Legacy ScriptContext
@@ -345,14 +368,17 @@ impl ScriptContext for Bare {
345368
Ok(())
346369
}
347370

348-
fn check_non_satisfaction_policy_rules<Pk: MiniscriptKey, Ctx: ScriptContext>(
371+
fn other_top_level_checks<Pk: MiniscriptKey, Ctx: ScriptContext>(
349372
ms: &Miniscript<Pk, Ctx>,
350-
) -> Result<(), ScriptContextError> {
373+
) -> Result<(), Error> {
351374
match &ms.node {
352-
Terminal::PkH(_pkh) => Ok(()),
353-
Terminal::PkK(_pk) => Ok(()),
375+
Terminal::Check(ref ms) => match &ms.node {
376+
Terminal::PkH(_pkh) => Ok(()),
377+
Terminal::PkK(_pk) => Ok(()),
378+
_ => Err(Error::NonStandardBareScript),
379+
},
354380
Terminal::Multi(_k, subs) if subs.len() <= 3 => Ok(()),
355-
_ => Err(ScriptContextError::NonStandardBareScript),
381+
_ => Err(Error::NonStandardBareScript),
356382
}
357383
}
358384
}

src/miniscript/mod.rs

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -30,8 +30,7 @@ use std::{fmt, str};
3030
use bitcoin;
3131
use bitcoin::blockdata::script;
3232

33-
pub use self::context::Legacy;
34-
pub use self::context::Segwitv0;
33+
pub use self::context::{Bare, Legacy, Segwitv0};
3534

3635
pub mod astelem;
3736
pub(crate) mod context;

src/policy/mod.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -119,7 +119,8 @@ impl<Pk: MiniscriptKey, Ctx: ScriptContext> Liftable<Pk> for Terminal<Pk, Ctx> {
119119
impl<Pk: MiniscriptKey> Liftable<Pk> for Descriptor<Pk> {
120120
fn lift(&self) -> Result<Semantic<Pk>, Error> {
121121
Ok(match *self {
122-
Descriptor::Bare(ref d) | Descriptor::Sh(ref d) => d.node.lift()?,
122+
Descriptor::Bare(ref d) => d.node.lift()?,
123+
Descriptor::Sh(ref d) => d.node.lift()?,
123124
Descriptor::Wsh(ref d) | Descriptor::ShWsh(ref d) => d.node.lift()?,
124125
Descriptor::Pk(ref p)
125126
| Descriptor::Pkh(ref p)

src/psbt/finalizer.rs

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -27,9 +27,8 @@ use bitcoin::util::bip143::SigHashCache;
2727
use bitcoin::{self, PublicKey, Script, SigHashType};
2828
use descriptor::{from_txin_with_witness_stack, SatisfiedConstraints};
2929
use Descriptor;
30-
use Legacy;
3130
use Miniscript;
32-
use Segwitv0;
31+
use {Bare, Legacy, Segwitv0};
3332
// Get the scriptpubkey for the psbt input
3433
fn get_scriptpubkey(psbt: &Psbt, index: usize) -> Result<&Script, InputError> {
3534
let script_pubkey;
@@ -188,7 +187,7 @@ fn get_descriptor(psbt: &Psbt, index: usize) -> Result<Descriptor<PublicKey>, In
188187
if inp.redeem_script.is_some() {
189188
return Err(InputError::NonEmptyRedeemScript);
190189
}
191-
let ms = Miniscript::<bitcoin::PublicKey, Legacy>::parse(script_pubkey)?;
190+
let ms = Miniscript::<bitcoin::PublicKey, Bare>::parse(script_pubkey)?;
192191
Ok(Descriptor::Bare(ms))
193192
}
194193
}

0 commit comments

Comments
 (0)