diff --git a/Cargo.toml b/Cargo.toml index 9a47f3f2f..fef5e62cb 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -100,7 +100,6 @@ test-hardware-signer = ["hardware-signer"] [dev-dependencies] lazy_static = "1.4" env_logger = "0.7" -clap = "2.33" electrsd = "0.20" [[example]] @@ -114,6 +113,10 @@ name = "miniscriptc" path = "examples/compiler.rs" required-features = ["compiler"] +[[example]] +name = "policy" +path = "examples/policy.rs" + [[example]] name = "rpcwallet" path = "examples/rpcwallet.rs" diff --git a/examples/compiler.rs b/examples/compiler.rs index 8be87e812..2ddabffd3 100644 --- a/examples/compiler.rs +++ b/examples/compiler.rs @@ -11,7 +11,6 @@ extern crate bdk; extern crate bitcoin; -extern crate clap; extern crate log; extern crate miniscript; extern crate serde_json; @@ -21,8 +20,6 @@ use std::str::FromStr; use log::info; -use clap::{App, Arg}; - use bitcoin::Network; use miniscript::policy::Concrete; use miniscript::Descriptor; @@ -31,75 +28,49 @@ use bdk::database::memory::MemoryDatabase; use bdk::wallet::AddressIndex::New; use bdk::{KeychainKind, Wallet}; +/// Miniscript policy is a high level abstraction of spending conditions. Defined in the +/// rust-miscript library here https://docs.rs/miniscript/7.0.0/miniscript/policy/index.html +/// rust-miniscript provides a `compile()` function that can be used to compile any miniscript policy +/// into a descriptor. This descriptor then in turn can be used in bdk a fully functioning wallet +/// can be derived from the policy. +/// +/// This example demonstrates the interaction between a bdk wallet and miniscript policy. + fn main() -> Result<(), Box> { env_logger::init_from_env( env_logger::Env::default().filter_or(env_logger::DEFAULT_FILTER_ENV, "info"), ); - let matches = App::new("Miniscript Compiler") - .arg( - Arg::with_name("POLICY") - .help("Sets the spending policy to compile") - .required(true) - .index(1), - ) - .arg( - Arg::with_name("TYPE") - .help("Sets the script type used to embed the compiled policy") - .required(true) - .index(2) - .possible_values(&["sh", "wsh", "sh-wsh"]), - ) - .arg( - Arg::with_name("parsed_policy") - .long("parsed_policy") - .short("p") - .help("Also return the parsed spending policy in JSON format"), - ) - .arg( - Arg::with_name("network") - .short("n") - .long("network") - .help("Sets the network") - .takes_value(true) - .default_value("testnet") - .possible_values(&["testnet", "regtest", "bitcoin", "signet"]), - ) - .get_matches(); - - let policy_str = matches.value_of("POLICY").unwrap(); - info!("Compiling policy: {}", policy_str); + // We start with a generic miniscript policy string + let policy_str = "or(10@thresh(4,pk(029ffbe722b147f3035c87cb1c60b9a5947dd49c774cc31e94773478711a929ac0),pk(025f05815e3a1a8a83bfbb03ce016c9a2ee31066b98f567f6227df1d76ec4bd143),pk(025625f41e4a065efc06d5019cbbd56fe8c07595af1231e7cbc03fafb87ebb71ec),pk(02a27c8b850a00f67da3499b60562673dcf5fdfb82b7e17652a7ac54416812aefd),pk(03e618ec5f384d6e19ca9ebdb8e2119e5bef978285076828ce054e55c4daf473e2)),1@and(older(4209713),thresh(2,pk(03deae92101c790b12653231439f27b8897264125ecb2f46f48278603102573165),pk(033841045a531e1adf9910a6ec279589a90b3b8a904ee64ffd692bd08a8996c1aa),pk(02aebf2d10b040eb936a6f02f44ee82f8b34f5c1ccb20ff3949c2b28206b7c1068))))"; + info!("Compiling policy: \n{}", policy_str); + // Parse the string as a [`Concrete`] type miniscript policy. let policy = Concrete::::from_str(policy_str)?; - let descriptor = match matches.value_of("TYPE").unwrap() { - "sh" => Descriptor::new_sh(policy.compile()?)?, - "wsh" => Descriptor::new_wsh(policy.compile()?)?, - "sh-wsh" => Descriptor::new_sh_wsh(policy.compile()?)?, - _ => panic!("Invalid type"), - }; + // Create a `wsh` type descriptor from the policy. + // `policy.compile()` returns the resulting miniscript from the policy. + let descriptor = Descriptor::new_wsh(policy.compile()?)?; - info!("... Descriptor: {}", descriptor); + info!("Compiled into following Descriptor: \n{}", descriptor); let database = MemoryDatabase::new(); - let network = matches - .value_of("network") - .map(Network::from_str) - .transpose() - .unwrap() - .unwrap_or(Network::Testnet); - let wallet = Wallet::new(&format!("{}", descriptor), None, network, database)?; - - info!("... First address: {}", wallet.get_address(New)?); - - if matches.is_present("parsed_policy") { - let spending_policy = wallet.policies(KeychainKind::External)?; - info!( - "... Spending policy:\n{}", - serde_json::to_string_pretty(&spending_policy)? - ); - } + // Create a new wallet from this descriptor + let wallet = Wallet::new(&format!("{}", descriptor), None, Network::Regtest, database)?; + + info!( + "First derived address from the descriptor: \n{}", + wallet.get_address(New)? + ); + + // BDK also has it's own `Policy` structure to represent the spending condition in a more + // human readable json format. + let spending_policy = wallet.policies(KeychainKind::External)?; + info!( + "The BDK spending policy: \n{}", + serde_json::to_string_pretty(&spending_policy)? + ); Ok(()) } diff --git a/examples/policy.rs b/examples/policy.rs new file mode 100644 index 000000000..64e17825b --- /dev/null +++ b/examples/policy.rs @@ -0,0 +1,66 @@ +// Bitcoin Dev Kit +// Written in 2020 by Alekos Filini +// +// Copyright (c) 2020-2021 Bitcoin Dev Kit Developers +// +// This file is licensed under the Apache License, Version 2.0 or the MIT license +// , at your option. +// You may not use this file except in accordance with one or both of these +// licenses. + +extern crate bdk; +extern crate env_logger; +extern crate log; +use std::error::Error; + +use bdk::bitcoin::Network; +use bdk::descriptor::{policy::BuildSatisfaction, ExtractPolicy, IntoWalletDescriptor}; +use bdk::wallet::signer::SignersContainer; + +/// This example describes the use of the BDK's [`bdk::descriptor::policy`] module. +/// +/// Policy is higher abstraction representation of the wallet descriptor spending condition. +/// This is useful to express complex miniscript spending conditions into more human readable form. +/// The resulting `Policy` structure can be used to derive spending conditions the wallet is capable +/// to spend from. +/// +/// This example demos a Policy output for a 2of2 multisig between between 2 parties, where the wallet holds +/// one of the Extend Private key. + +fn main() -> Result<(), Box> { + env_logger::init_from_env( + env_logger::Env::default().filter_or(env_logger::DEFAULT_FILTER_ENV, "info"), + ); + + let secp = bitcoin::secp256k1::Secp256k1::new(); + + // The descriptor used in the example + // The form is "wsh(multi(2, , ))" + let desc = "wsh(multi(2,tprv8ZgxMBicQKsPdpkqS7Eair4YxjcuuvDPNYmKX3sCniCf16tHEVrjjiSXEkFRnUH77yXc6ZcwHHcLNfjdi5qUvw3VDfgYiH5mNsj5izuiu2N/1/*,tpubD6NzVbkrYhZ4XHndKkuB8FifXm8r5FQHwrN6oZuWCz13qb93rtgKvD4PQsqC4HP4yhV3tA2fqr2RbY5mNXfM7RxXUoeABoDtsFUq2zJq6YK/1/*))"; + + // Use the descriptor string to derive the full descriptor and a keymap. + // The wallet descriptor can be used to create a new bdk::wallet. + // While the `keymap` can be used to create a `SignerContainer`. + // + // The `SignerContainer` can sign for `PSBT`s. + // a bdk::wallet internally uses these to handle transaction signing. + // But they can be used as independent tools also. + let (wallet_desc, keymap) = desc.into_wallet_descriptor(&secp, Network::Testnet)?; + + log::info!("Example Descriptor for policy analysis : {}", wallet_desc); + + // Create the signer with the keymap and descriptor. + let signers_container = SignersContainer::build(keymap, &wallet_desc, &secp); + + // Extract the Policy from the given descriptor and signer. + // Note that Policy is a wallet specific structure. It depends on the the descriptor, and + // what the concerned wallet with a given signer can sign for. + let policy = wallet_desc + .extract_policy(&signers_container, BuildSatisfaction::None, &secp)? + .expect("We expect a policy"); + + log::info!("Derived Policy for the descriptor {:#?}", policy); + + Ok(()) +}