descriptors: use the Miniscript compiler to create a descriptor

This commit is contained in:
Antoine Poinsot 2023-03-24 18:46:25 +01:00
parent a0aa30470c
commit 55d86c62a5
No known key found for this signature in database
GPG Key ID: E13FC145CD3F4304
3 changed files with 25 additions and 59 deletions

View File

@ -25,7 +25,7 @@ daemon = ["libc"]
[dependencies]
# For managing transactions (it re-exports the bitcoin crate)
miniscript = { git = "https://github.com/darosior/rust-miniscript", branch = "multipath_descriptors_on_9.0", features = ["serde"] }
miniscript = { git = "https://github.com/darosior/rust-miniscript", branch = "multipath_descriptors_on_9.0", features = ["serde", "compiler"] }
# Don't reinvent the wheel
dirs = "5.0"

View File

@ -1,14 +1,14 @@
use miniscript::{
bitcoin::{util::bip32, Sequence},
descriptor,
policy::{Liftable, Semantic as SemanticPolicy},
Miniscript, ScriptContext, Terminal,
policy::{Concrete as ConcretePolicy, Liftable, Semantic as SemanticPolicy},
ScriptContext,
};
use std::{
collections::{HashMap, HashSet},
convert::TryFrom,
error, fmt, sync,
error, fmt,
};
#[derive(Debug)]
@ -288,33 +288,14 @@ impl PathInfo {
}
}
/// Returns `None` if it is a multisig that does not fit inside a CHECKMULTISIG.
pub fn into_miniscript(
self,
as_hash: bool,
) -> Option<Miniscript<descriptor::DescriptorPublicKey, miniscript::Segwitv0>> {
/// Get a Miniscript Policy for this path.
pub fn into_ms_policy(self) -> ConcretePolicy<descriptor::DescriptorPublicKey> {
match self {
PathInfo::Single(key) => Some(
Miniscript::from_ast(Terminal::Check(sync::Arc::from(
Miniscript::from_ast(if as_hash {
Terminal::PkH(key)
} else {
Terminal::PkK(key)
})
.expect("pk_k is a valid Miniscript"),
)))
.expect("Well typed"),
PathInfo::Single(key) => ConcretePolicy::Key(key),
PathInfo::Multi(thresh, keys) => ConcretePolicy::Threshold(
thresh,
keys.into_iter().map(ConcretePolicy::Key).collect(),
),
PathInfo::Multi(thresh, keys) => {
if thresh < 1 || keys.len() > 20 || thresh > keys.len() {
None
} else {
Some(
Miniscript::from_ast(Terminal::Multi(thresh, keys))
.expect("multi is a valid Miniscript"),
)
}
}
}
}
}
@ -484,36 +465,21 @@ impl LianaPolicy {
recovery_path: (timelock, recovery_path),
} = self;
// Create the timelocked spending path. If there is a single key we make it a pk_h() in
// order to save on the script size (since we assume the timelocked recovery path will
// seldom be used).
let recovery_timelock = Terminal::Older(Sequence::from_height(timelock));
let recovery_keys = recovery_path
.into_miniscript(true)
.expect("We check the multisig never overflows in our constructors.");
let recovery_branch = Miniscript::from_ast(Terminal::AndV(
Miniscript::from_ast(Terminal::Verify(recovery_keys.into()))
.expect("Well typed")
.into(),
Miniscript::from_ast(recovery_timelock)
.expect("Well typed")
.into(),
))
.expect("Well typed");
// Create the timelocked recovery spending path.
let recovery_timelock = ConcretePolicy::Older(Sequence::from_height(timelock));
let recovery_keys = recovery_path.into_ms_policy();
let recovery_branch = ConcretePolicy::And(vec![recovery_keys, recovery_timelock]);
// Combine the timelocked spending path with the simple "primary" path. For the primary key
// we don't use a pkh since it's the one that will likely always be used.
let primary_keys = primary_path
.into_miniscript(false)
.expect("We check the multisig never overflows in our constructors.");
let tl_miniscript =
Miniscript::from_ast(Terminal::OrD(primary_keys.into(), recovery_branch.into()))
.expect("Well typed");
miniscript::Segwitv0::check_local_validity(&tl_miniscript)
.expect("Miniscript must be sane");
descriptor::Descriptor::Wsh(
descriptor::Wsh::new(tl_miniscript).expect("Must pass sanity checks"),
)
// Create the primary spending path and combine both, assuming the recovery path will
// seldom be used.
let primary_keys = primary_path.into_ms_policy();
let tl_policy = ConcretePolicy::Or(vec![(99, primary_keys), (1, recovery_branch)]);
let ms = tl_policy
.compile::<miniscript::Segwitv0>()
.expect("Compilation must never fail, nothing overflows.");
miniscript::Segwitv0::check_local_validity(&ms).expect("Miniscript must be sane");
descriptor::Descriptor::Wsh(descriptor::Wsh::new(ms).expect("Must pass sanity checks"))
}
}

View File

@ -421,7 +421,7 @@ mod tests {
],
);
let policy = LianaPolicy::new(primary_keys, recovery_keys, 26352).unwrap();
assert_eq!(LianaDescriptor::new(policy).to_string(), "wsh(or_d(multi(3,[abcdef01]xpub6Eze7yAT3Y1wGrnzedCNVYDXUqa9NmHVWck5emBaTbXtURbe1NWZbK9bsz1TiVE7Cz341PMTfYgFw1KdLWdzcM1UMFTcdQfCYhhXZ2HJvTW/<0;1>/*,[aabb0011/10/4893]xpub6Bw79HbNSeS2xXw1sngPE3ehnk1U3iSPCgLYzC9LpN8m9nDuaKLZvkg8QXxL5pDmEmQtYscmUD8B9MkAAZbh6vxPzNXMaLfGQ9Sb3z85qhR/<0;1>/*,[abcdef01]xpub67zuTXF9Ln4731avKTBSawoVVNRuMfmRvkL7kLUaLBRqma9ZqdHBJg9qx8cPUm3oNQMiXT4TmGovXNoQPuwg17RFcVJ8YrnbcooN7pxVJqC/<0;1>/*),and_v(v:multi(2,[abcdef01]xpub69cP4Y7S9TWcbSNxmk6CEDBsoaqr3ZEdjHuZcHxEFFKGh569RsJNr2V27XGhsbH9FXgWUEmKXRN7c5wQfq2VPjt31xP9VsYnVUyU8HcVevm/<0;1>/*,[abcdef01]xpub6AA2N8RALRYgLD6jT1iXYCEDkndTeZndMtWPbtNX6sY5dPiLtf2T88ahdxrGXMUPoNadgR86sFhBXWQVgifPzDYbY9ZtwK4gqzx4y5Da1DW/<0;1>/*,[aabb0011/10/4893]xpub6AyxexvxizZJffF153evmfqHcE9MV88fCNCAtP3jQjXJHwrAKri71Tq9jWUkPxj9pja4u6AkCPHY7atgxzSEa2HtDwJfrRWKK4fsfQg4o77/<0;1>/*),older(26352))))#s0zsa6uc");
assert_eq!(LianaDescriptor::new(policy).to_string(), "wsh(or_d(multi(3,[abcdef01]xpub6Eze7yAT3Y1wGrnzedCNVYDXUqa9NmHVWck5emBaTbXtURbe1NWZbK9bsz1TiVE7Cz341PMTfYgFw1KdLWdzcM1UMFTcdQfCYhhXZ2HJvTW/<0;1>/*,[aabb0011/10/4893]xpub6Bw79HbNSeS2xXw1sngPE3ehnk1U3iSPCgLYzC9LpN8m9nDuaKLZvkg8QXxL5pDmEmQtYscmUD8B9MkAAZbh6vxPzNXMaLfGQ9Sb3z85qhR/<0;1>/*,[abcdef01]xpub67zuTXF9Ln4731avKTBSawoVVNRuMfmRvkL7kLUaLBRqma9ZqdHBJg9qx8cPUm3oNQMiXT4TmGovXNoQPuwg17RFcVJ8YrnbcooN7pxVJqC/<0;1>/*),and_v(v:thresh(2,pkh([abcdef01]xpub69cP4Y7S9TWcbSNxmk6CEDBsoaqr3ZEdjHuZcHxEFFKGh569RsJNr2V27XGhsbH9FXgWUEmKXRN7c5wQfq2VPjt31xP9VsYnVUyU8HcVevm/<0;1>/*),a:pkh([abcdef01]xpub6AA2N8RALRYgLD6jT1iXYCEDkndTeZndMtWPbtNX6sY5dPiLtf2T88ahdxrGXMUPoNadgR86sFhBXWQVgifPzDYbY9ZtwK4gqzx4y5Da1DW/<0;1>/*),a:pkh([aabb0011/10/4893]xpub6AyxexvxizZJffF153evmfqHcE9MV88fCNCAtP3jQjXJHwrAKri71Tq9jWUkPxj9pja4u6AkCPHY7atgxzSEa2HtDwJfrRWKK4fsfQg4o77/<0;1>/*)),older(26352))))#hmsqemgr");
// We prevent footguns with timelocks by requiring a u16. Note how the following wouldn't
// compile: