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},
};
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
.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))
}
_ => {
.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();