Skip to content
Merged
Show file tree
Hide file tree
Changes from 5 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
6 changes: 3 additions & 3 deletions src/descriptor/tr/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -383,9 +383,9 @@ impl<Pk: FromStrKey> crate::expression::FromTree for Tr<Pk> {
} else {
let script = Miniscript::from_tree(node)?;
// FIXME hack for https://github.com/rust-bitcoin/rust-miniscript/issues/734
if script.ty.corr.base != crate::miniscript::types::Base::B {
return Err(Error::NonTopLevel(format!("{:?}", script)));
};
script
.validate(&Tap::CONSENSUS)
.map_err(Error::Validation)?;

tree_builder.push_leaf(script);
tap_tree_iter.skip_descendants();
Expand Down
7 changes: 2 additions & 5 deletions src/interpreter/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1059,7 +1059,6 @@ mod tests {

use super::inner::ToNoChecks;
use super::*;
use crate::miniscript::analyzable::ExtParams;

#[allow(clippy::type_complexity)]
fn setup_keys_sigs(
Expand Down Expand Up @@ -1562,14 +1561,12 @@ mod tests {
// because it does not implement FromStr
fn no_checks_ms(ms: &str) -> Miniscript<BitcoinKey, NoChecks> {
// Parsing should allow raw hashes in the interpreter
let elem: Miniscript<bitcoin::PublicKey, NoChecks> =
Miniscript::from_str_ext(ms, &ExtParams::allow_all()).unwrap();
let elem: Miniscript<bitcoin::PublicKey, NoChecks> = ms.parse().unwrap();
elem.to_no_checks_ms()
}

fn x_only_no_checks_ms(ms: &str) -> Miniscript<BitcoinKey, NoChecks> {
let elem: Miniscript<bitcoin::key::XOnlyPublicKey, NoChecks> =
Miniscript::from_str_ext(ms, &ExtParams::allow_all()).unwrap();
let elem: Miniscript<bitcoin::XOnlyPublicKey, NoChecks> = ms.parse().unwrap();
elem.to_no_checks_ms()
}
}
10 changes: 6 additions & 4 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -124,6 +124,7 @@ pub mod plan;
pub mod policy;
mod primitives;
pub mod psbt;
mod validation;

#[cfg(test)]
mod test_utils;
Expand All @@ -147,6 +148,7 @@ use crate::prelude::*;
pub use crate::primitives::absolute_locktime::{AbsLockTime, AbsLockTimeError};
pub use crate::primitives::relative_locktime::{RelLockTime, RelLockTimeError};
pub use crate::primitives::threshold::{Threshold, ThresholdError};
pub use crate::validation::{Error as ValidationError, ValidationParams};

/// Trait representing a key which can be converted to a hash type.
pub trait MiniscriptKey: Clone + Eq + Ord + fmt::Debug + fmt::Display + hash::Hash {
Expand Down Expand Up @@ -441,8 +443,6 @@ pub enum Error {
Unexpected(String),
/// Encountered a wrapping character that we don't recognize
UnknownWrapper(char),
/// Parsed a miniscript and the result was not of type T
NonTopLevel(String),
/// Parsed a miniscript but there were more script opcodes after it
Trailing(String),
/// Could not satisfy a script (fragment) because of a missing signature
Expand Down Expand Up @@ -492,6 +492,8 @@ pub enum Error {
ParseThreshold(ParseThresholdError),
/// Invalid expression tree.
Parse(ParseError),
/// Validation of a script failed.
Validation(ValidationError),
}

#[doc(hidden)] // will be removed when we remove Error
Expand All @@ -511,7 +513,6 @@ impl fmt::Display for Error {
Error::UnexpectedStart => f.write_str("unexpected start of script"),
Error::Unexpected(ref s) => write!(f, "unexpected «{}»", s),
Error::UnknownWrapper(ch) => write!(f, "unknown wrapper «{}:»", ch),
Error::NonTopLevel(ref s) => write!(f, "non-T miniscript: {}", s),
Error::Trailing(ref s) => write!(f, "trailing tokens: {}", s),
Error::MissingSig(ref pk) => write!(f, "missing signature for key {:?}", pk),
Error::CouldNotSatisfy => f.write_str("could not satisfy"),
Expand Down Expand Up @@ -547,6 +548,7 @@ impl fmt::Display for Error {
Error::Threshold(ref e) => e.fmt(f),
Error::ParseThreshold(ref e) => e.fmt(f),
Error::Parse(ref e) => e.fmt(f),
Error::Validation(ref e) => e.fmt(f),
}
}
}
Expand All @@ -560,7 +562,6 @@ impl std::error::Error for Error {
UnexpectedStart
| Unexpected(_)
| UnknownWrapper(_)
| NonTopLevel(_)
| Trailing(_)
| MissingSig(_)
| CouldNotSatisfy
Expand Down Expand Up @@ -588,6 +589,7 @@ impl std::error::Error for Error {
Threshold(e) => Some(e),
ParseThreshold(e) => Some(e),
Parse(e) => Some(e),
Validation(e) => Some(e),
}
}
}
Expand Down
2 changes: 1 addition & 1 deletion src/macros.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
/// `ms_str!("c:or_i(pk({}),pk({}))", pk1, pk2)`
#[cfg(test)]
macro_rules! ms_str {
($($arg:tt)*) => (Miniscript::from_str_ext(&format!($($arg)*), &$crate::ExtParams::allow_all()).unwrap())
($($arg:tt)*) => (Miniscript::from_str_with_validation_params(&format!($($arg)*), &$crate::ValidationParams::MAX).unwrap())
}

/// Allows tests to create a concrete policy directly from string as
Expand Down
87 changes: 78 additions & 9 deletions src/miniscript/context.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,9 +13,8 @@ use crate::miniscript::limits::{
MAX_OPS_PER_SCRIPT, MAX_SCRIPTSIG_SIZE, MAX_SCRIPT_ELEMENT_SIZE, MAX_SCRIPT_SIZE,
MAX_STACK_SIZE, MAX_STANDARD_P2WSH_SCRIPT_SIZE, MAX_STANDARD_P2WSH_STACK_ITEMS,
};
use crate::miniscript::types;
use crate::prelude::*;
use crate::{hash256, Error, ForEachKey, Miniscript, MiniscriptKey, Terminal};
use crate::{hash256, Error, ForEachKey, Miniscript, MiniscriptKey, Terminal, ValidationParams};

/// Error for Script Context
#[derive(Clone, PartialEq, Eq, Debug)]
Expand Down Expand Up @@ -176,6 +175,18 @@ where
{
/// The consensus key associated with the type. Must be a parseable key
type Key: ParseableKey;

/// The validation parameters enforcing consensus limits in this context, and
/// nothing further.
const CONSENSUS: ValidationParams;

/// Sensible validation parameters for this context. Unless you have a good reason
/// to choose otherwise, these are the validation parameters you want.
///
/// They are also the validation parameters used throughout this library when no
/// explicit choice of parameters is made.
const SANE: ValidationParams;

/// Depending on ScriptContext, fragments can be malleable. For Example,
/// under Legacy context, PkH is malleable because it is possible to
/// estimate the cost of satisfaction because of compressed keys
Expand Down Expand Up @@ -271,9 +282,6 @@ where

/// Check whether the top-level is type B
fn top_level_type_check<Pk: MiniscriptKey>(ms: &Miniscript<Pk, Self>) -> Result<(), Error> {
if ms.ty.corr.base != types::Base::B {
return Err(Error::NonTopLevel(format!("{:?}", ms)));
}
Comment on lines 284 to -288

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

So if I understand correctly, this will be fixed later to once again check if it's type B?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

No, this function will be totally deleted. We now check for type B in Miniscript::validate. The commit message describes this.

// (Ab)use `for_each_key` to record the number of derivation paths a multipath key has.
#[derive(PartialEq)]
enum MultipathLenChecker {
Expand Down Expand Up @@ -359,6 +367,20 @@ pub enum Legacy {}

impl ScriptContext for Legacy {
type Key = bitcoin::PublicKey;

const CONSENSUS: ValidationParams = ValidationParams {
allow_compressed_keys: true,
allow_dup_if: false,
allow_uncompressed_keys: true,
allow_multi_a: false,
allow_or_i: false,
allow_x_only_keys: false,
max_opcode_count: MAX_OPS_PER_SCRIPT,
max_script_size: MAX_SCRIPT_ELEMENT_SIZE,
..ValidationParams::CONSENSUS
};
const SANE: ValidationParams = Self::CONSENSUS.intersect(&ValidationParams::SANE);

fn check_terminal_non_malleable<Pk: MiniscriptKey>(
frag: &Terminal<Pk, Self>,
) -> Result<(), ScriptContextError> {
Expand Down Expand Up @@ -467,6 +489,22 @@ pub enum Segwitv0 {}

impl ScriptContext for Segwitv0 {
type Key = bitcoin::PublicKey;

const CONSENSUS: ValidationParams = ValidationParams {
allow_compressed_keys: true,
allow_uncompressed_keys: false,
allow_multi_a: false,
allow_x_only_keys: false,
max_opcode_count: MAX_OPS_PER_SCRIPT,
max_exec_stack_size: MAX_STACK_SIZE,
..ValidationParams::CONSENSUS
};
const SANE: ValidationParams = ValidationParams {
max_script_size: MAX_STANDARD_P2WSH_SCRIPT_SIZE,
max_witness_items: MAX_STANDARD_P2WSH_STACK_ITEMS,
..Self::CONSENSUS.intersect(&ValidationParams::SANE)
};

fn check_terminal_non_malleable<Pk: MiniscriptKey>(
_frag: &Terminal<Pk, Self>,
) -> Result<(), ScriptContextError> {
Expand Down Expand Up @@ -580,6 +618,22 @@ pub enum Tap {}

impl ScriptContext for Tap {
type Key = bitcoin::secp256k1::XOnlyPublicKey;

const CONSENSUS: ValidationParams = ValidationParams {
allow_compressed_keys: false,
allow_uncompressed_keys: false,
allow_multi: false,
allow_x_only_keys: true,
..ValidationParams::CONSENSUS
};
const SANE: ValidationParams = ValidationParams {
// Segwit runtime stack item number applies, but no script size limit (though maybe we should
// enforce a 4mb limit?) and no policy limit on number of initial stack items.
// https://github.com/bitcoin/bips/blob/master/bip-0342.mediawiki#user-content-Resource_limits
max_exec_stack_size: MAX_STACK_SIZE,
..Self::CONSENSUS.intersect(&ValidationParams::SANE)
};

fn check_terminal_non_malleable<Pk: MiniscriptKey>(
_frag: &Terminal<Pk, Self>,
) -> Result<(), ScriptContextError> {
Expand Down Expand Up @@ -691,6 +745,20 @@ pub enum BareCtx {}

impl ScriptContext for BareCtx {
type Key = bitcoin::PublicKey;

const CONSENSUS: ValidationParams = ValidationParams {
allow_compressed_keys: true,
allow_dup_if: false,
allow_uncompressed_keys: true,
allow_multi_a: false,
allow_or_i: false,
allow_x_only_keys: false,
max_opcode_count: MAX_OPS_PER_SCRIPT,
max_script_size: MAX_SCRIPT_SIZE,
..ValidationParams::CONSENSUS
};
const SANE: ValidationParams = Self::CONSENSUS.intersect(&ValidationParams::SANE);

fn check_terminal_non_malleable<Pk: MiniscriptKey>(
_frag: &Terminal<Pk, Self>,
) -> Result<(), ScriptContextError> {
Expand Down Expand Up @@ -801,6 +869,10 @@ pub enum NoChecks {}
impl ScriptContext for NoChecks {
// todo: When adding support for interpreter, we need a enum with all supported keys here
type Key = bitcoin::PublicKey;

const CONSENSUS: ValidationParams = ValidationParams::MAX;
const SANE: ValidationParams = ValidationParams::MAX;

fn check_terminal_non_malleable<Pk: MiniscriptKey>(
_frag: &Terminal<Pk, Self>,
) -> Result<(), ScriptContextError> {
Expand Down Expand Up @@ -865,10 +937,7 @@ impl ScriptContext for NoChecks {
Ok(())
}

fn top_level_type_check<Pk: MiniscriptKey>(ms: &Miniscript<Pk, Self>) -> Result<(), Error> {
if ms.ty.corr.base != types::Base::B {
return Err(Error::NonTopLevel(format!("{:?}", ms)));
}
fn top_level_type_check<Pk: MiniscriptKey>(_: &Miniscript<Pk, Self>) -> Result<(), Error> {
Ok(())
}

Expand Down
Loading