descriptors: use the Miniscript compiler to create a descriptor
This commit is contained in:
parent
a0aa30470c
commit
55d86c62a5
@ -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"
|
||||
|
||||
@ -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"))
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -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:
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user