Skip to content
This repository was archived by the owner on Oct 22, 2026. It is now read-only.
Open
Show file tree
Hide file tree
Changes from all 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
12 changes: 12 additions & 0 deletions src/descriptor/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2369,11 +2369,17 @@ pk(03f28773c2d975288bc7d1d205c3748651b075fbc6610e58cddeeddf8f19405aa8))";
Desc::from_str(&format!("sh(pk({}))", comp_key)).unwrap();
Desc::from_str(&format!("sh(pk({}))", uncomp_key)).unwrap();
Desc::from_str(&format!("sh(pk({}))", x_only_key)).unwrap_err();
Desc::from_str(&format!("sh(pkh({}))", comp_key)).unwrap();
Desc::from_str(&format!("sh(pkh({}))", uncomp_key)).unwrap();
Desc::from_str(&format!("sh(pkh({}))", x_only_key)).unwrap_err();

// bare tests, x-only keys not supported
Desc::from_str(&format!("pk({})", comp_key)).unwrap();
Desc::from_str(&format!("pk({})", uncomp_key)).unwrap();
Desc::from_str(&format!("pk({})", x_only_key)).unwrap_err();
Desc::from_str(&format!("c:pk_h({})", comp_key)).unwrap();
Desc::from_str(&format!("c:pk_h({})", uncomp_key)).unwrap();
Desc::from_str(&format!("c:pk_h({})", x_only_key)).unwrap_err();

// pkh tests, x-only keys not supported
Desc::from_str(&format!("pkh({})", comp_key)).unwrap();
Expand All @@ -2389,6 +2395,9 @@ pk(03f28773c2d975288bc7d1d205c3748651b075fbc6610e58cddeeddf8f19405aa8))";
Desc::from_str(&format!("wsh(pk({}))", comp_key)).unwrap();
Desc::from_str(&format!("wsh(pk({}))", uncomp_key)).unwrap_err();
Desc::from_str(&format!("wsh(pk({}))", x_only_key)).unwrap_err();
Desc::from_str(&format!("wsh(pkh({}))", comp_key)).unwrap();
Desc::from_str(&format!("wsh(pkh({}))", uncomp_key)).unwrap_err();
Desc::from_str(&format!("wsh(pkh({}))", x_only_key)).unwrap_err();

// Tap tests, key path
Desc::from_str(&format!("tr({})", comp_key)).unwrap();
Expand All @@ -2399,6 +2408,9 @@ pk(03f28773c2d975288bc7d1d205c3748651b075fbc6610e58cddeeddf8f19405aa8))";
Desc::from_str(&format!("tr({},pk({}))", x_only_key, comp_key)).unwrap();
Desc::from_str(&format!("tr({},pk({}))", x_only_key, uncomp_key)).unwrap_err();
Desc::from_str(&format!("tr({},pk({}))", x_only_key, x_only_key)).unwrap();
Desc::from_str(&format!("tr({},pkh({}))", x_only_key, comp_key)).unwrap();
Desc::from_str(&format!("tr({},pkh({}))", x_only_key, uncomp_key)).unwrap_err();
Desc::from_str(&format!("tr({},pkh({}))", x_only_key, x_only_key)).unwrap();
}

#[test]
Expand Down
8 changes: 4 additions & 4 deletions src/miniscript/context.rs
Original file line number Diff line number Diff line change
Expand Up @@ -405,7 +405,7 @@ impl ScriptContext for Legacy {
) -> Result<(), ScriptContextError> {
// 1. Check the node first, throw an error on the language itself
let node_checked = match ms.node {
Terminal::PkK(ref pk) => Self::check_pk(pk),
Terminal::PkK(ref pk) | Terminal::PkH(ref pk) => Self::check_pk(pk),
Terminal::Multi(ref thresh) | Terminal::SortedMulti(ref thresh) => {
for pk in thresh.iter() {
Self::check_pk(pk)?;
Expand Down Expand Up @@ -527,7 +527,7 @@ impl ScriptContext for Segwitv0 {
) -> Result<(), ScriptContextError> {
// 1. Check the node first, throw an error on the language itself
let node_checked = match ms.node {
Terminal::PkK(ref pk) => Self::check_pk(pk),
Terminal::PkK(ref pk) | Terminal::PkH(ref pk) => Self::check_pk(pk),
Terminal::Multi(ref thresh) | Terminal::SortedMulti(ref thresh) => {
for pk in thresh.iter() {
Self::check_pk(pk)?;
Expand Down Expand Up @@ -656,7 +656,7 @@ impl ScriptContext for Tap {
) -> Result<(), ScriptContextError> {
// 1. Check the node first, throw an error on the language itself
let node_checked = match ms.node {
Terminal::PkK(ref pk) => Self::check_pk(pk),
Terminal::PkK(ref pk) | Terminal::PkH(ref pk) => Self::check_pk(pk),
Terminal::MultiA(ref thresh) | Terminal::SortedMultiA(ref thresh) => {
for pk in thresh.iter() {
Self::check_pk(pk)?;
Expand Down Expand Up @@ -783,7 +783,7 @@ impl ScriptContext for BareCtx {
) -> Result<(), ScriptContextError> {
// 1. Check the node first, throw an error on the language itself
let node_checked = match ms.node {
Terminal::PkK(ref key) => Self::check_pk(key),
Terminal::PkK(ref key) | Terminal::PkH(ref key) => Self::check_pk(key),
Terminal::Multi(ref thresh) | Terminal::SortedMulti(ref thresh) => {
for pk in thresh.iter() {
Self::check_pk(pk)?;
Expand Down
62 changes: 59 additions & 3 deletions src/policy/concrete.rs
Original file line number Diff line number Diff line change
Expand Up @@ -423,14 +423,14 @@ impl<Pk: MiniscriptKey> Policy<Pk> {
match policy {
Self::Trivial => None,
policy => {
let leaf_compilations: Vec<_> = policy
let leaf_compilations = policy
.enumerate_policy_tree(PositiveF64::ONE)
.into_iter()
.filter(|x| x.1 != Arc::new(Self::Unsatisfiable))
.map(|(prob, pol)| {
(prob, compiler::best_compilation(pol.as_ref()).unwrap())
Ok((prob, compiler::best_compilation(pol.as_ref())?))
})
.collect();
.collect::<Result<Vec<_>, CompilerError>>()?;

if !leaf_compilations.is_empty() {
let tap_tree = with_huffman_tree::<Pk>(leaf_compilations)?;
Expand Down Expand Up @@ -1276,6 +1276,7 @@ mod compiler_tests {

use super::*;
use crate::policy::Concrete;
use crate::{BareCtx, Legacy, Segwitv0};

fn nested_equal_odds_policy(n_keys: usize) -> Policy<String> {
assert!(n_keys >= 2);
Expand Down Expand Up @@ -1357,6 +1358,61 @@ mod compiler_tests {
));
}

#[test]
fn invalid_context_key_error() {
// A key that the context doesn't allow in pk_k() must not compile to pk_h() instead.
let comp_key = bitcoin::PublicKey::from_str(
"02c2fd50ceae468857bb7eb32ae9cd4083e6c7e42fbbec179d81134b3e3830586c",
)
.unwrap();
let uncomp_key = bitcoin::PublicKey::from_str(
"0479be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798\
483ada7726a3c4655da4fbfc0e1108a8fd17b448a68554199c47d08ffb10d4b8",
)
.unwrap();
let x_only_key = bitcoin::key::XOnlyPublicKey::from_str(
"79be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798",
)
.unwrap();

let x_only = Policy::Key(x_only_key);
assert!(matches!(
x_only.compile_to_descriptor::<Legacy>(DescriptorCtx::Sh),
Err(Error::CompilerError(CompilerError::LimitsExceeded))
));
assert!(matches!(
x_only.compile_to_descriptor::<Segwitv0>(DescriptorCtx::Wsh),
Err(Error::CompilerError(CompilerError::LimitsExceeded))
));
assert!(matches!(
x_only.compile_to_descriptor::<BareCtx>(DescriptorCtx::Bare),
Err(Error::CompilerError(CompilerError::LimitsExceeded))
));

let uncomp = Policy::Key(uncomp_key);
uncomp
.compile_to_descriptor::<Legacy>(DescriptorCtx::Sh)
.unwrap();
assert!(matches!(
uncomp.compile_to_descriptor::<Segwitv0>(DescriptorCtx::Wsh),
Err(Error::CompilerError(CompilerError::LimitsExceeded))
));
assert!(matches!(
uncomp.compile_to_descriptor::<Segwitv0>(DescriptorCtx::ShWsh),
Err(Error::CompilerError(CompilerError::LimitsExceeded))
));

// In Taproot, the uncompressed key can't be a leaf key either.
let tap: Policy<bitcoin::PublicKey> =
Policy::from_str(&format!("or(99@pk({}),1@pk({}))", comp_key, uncomp_key)).unwrap();
assert!(matches!(tap.compile_tr(None), Err(CompilerError::LimitsExceeded)));
assert!(matches!(tap.compile_tr_native(None, 128), Err(CompilerError::LimitsExceeded)));
assert!(matches!(
tap.compile_tr_private_experimental(None),
Err(Error::CompilerError(CompilerError::LimitsExceeded))
));
}

#[test]
fn huffman_depth_error() {
// Nesting produces geometrically decreasing Tapleaf probabilities without exceeding the
Expand Down
Loading