Skip to content
Merged
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
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ page. See [DEVELOPMENT_CYCLE.md](DEVELOPMENT_CYCLE.md) for more details.
- Fixed routing electrum and esplora traffic through configured socks5 proxy
- Routed compact filter (cbf) traffic through the configured SOCKS5 proxy
- Rejected `--proxy` on the `rpc` backend, and unsupported proxy options (`--proxy_auth`, `--timeout`) on the `rpc` and `cbf` backends, instead of silently ignoring them
- Fixed `compile` rejecting policies that are valid for the requested script type

## [4.0.0]

Expand Down
22 changes: 7 additions & 15 deletions src/handlers/descriptor.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ use {
key::{Parity, rand},
secp256k1::{PublicKey, Scalar, Secp256k1, SecretKey},
},
miniscript::{Descriptor, Miniscript, descriptor::TapTree, policy::Concrete},
miniscript::{Descriptor, descriptor::TapTree, policy::Concrete},
},
std::{str::FromStr, sync::Arc},
};
Expand Down Expand Up @@ -83,22 +83,14 @@ impl AppCommand<AppContext<Init>> for CompileCommand {
let policy: Concrete<String> = Concrete::from_str(&self.policy)
.map_err(|e| Error::Generic(format!("Invalid policy: {e}")))?;

let legacy_policy: Miniscript<String, bdk_wallet::miniscript::Legacy> = policy
.compile()
.map_err(|e| Error::Generic(e.to_string()))?;
let segwit_policy: Miniscript<String, bdk_wallet::miniscript::Segwitv0> = policy
.compile()
.map_err(|e| Error::Generic(e.to_string()))?;
let taproot_policy: Miniscript<String, bdk_wallet::miniscript::Tap> = policy
.compile()
.map_err(|e| Error::Generic(e.to_string()))?;

let mut r = None;

// Compile per branch, not once up front: the contexts have different script
// limits, and the narrowest one would reject policies valid for the requested type.
let descriptor = match self.script_type.as_str() {
"sh" => Descriptor::new_sh(legacy_policy),
"wsh" => Descriptor::new_wsh(segwit_policy),
"sh-wsh" => Descriptor::new_sh_wsh(segwit_policy),
"sh" => Descriptor::new_sh(policy.compile()?),
"wsh" => Descriptor::new_wsh(policy.compile()?),
"sh-wsh" => Descriptor::new_sh_wsh(policy.compile()?),
"tr" => {
// Use a randomized unspendable internal key (H + rG) instead of a fixed NUMS
// point. This improves privacy by preventing observers from determining whether
Expand All @@ -118,7 +110,7 @@ impl AppCommand<AppContext<Init>> for CompileCommand {
.map_err(|e| Error::Generic(format!("Failed to tweak NUMS key: {e}")))?;
let (xonly_internal_key, _) = internal_key_point.x_only_public_key();

let tree = TapTree::Leaf(Arc::new(taproot_policy));
let tree = TapTree::Leaf(Arc::new(policy.compile()?));
Descriptor::new_tr(xonly_internal_key.to_string(), Some(tree))
}
_ => {
Expand Down
26 changes: 26 additions & 0 deletions tests/integration/init.rs
Original file line number Diff line number Diff line change
Expand Up @@ -197,6 +197,32 @@ mod test_compile {
.stdout(predicate::str::contains("wsh("));
}

/// A policy can be valid for tr or wsh type and still exceed the limits of the
/// legacy context, whose 520-byte redeemScript cap does not apply to it.
#[test]
fn test_compile_policy_beyond_legacy_limits() {
let temp_dir = TempDir::new().unwrap();
let cli = BdkCli::new("testnet", Some(temp_dir.path().to_path_buf()));

let keys = (1..=20)
.map(|i| format!("pk(K{i:02})"))
.collect::<Vec<_>>()
.join(",");
let policy = format!("thresh(2,{keys})");

// compile tr
cli.cmd("compile", &[&policy, "--type", "tr"])
.assert()
.success()
.stdout(predicate::str::contains("tr("));

// compile wsh
cli.cmd("compile", &[&policy, "--type", "wsh"])
.assert()
.success()
.stdout(predicate::str::contains("wsh("));
}

#[test]
fn test_compile_invalid_policy() {
let temp_dir = TempDir::new().unwrap();
Expand Down
Loading