Skip to content

Commit aff41d6

Browse files
committed
Merge #730: Update compiler example to a Policy example
97b6fb0 Add a policy example (rajarshimaitra) da76708 Update compiler example. (rajarshimaitra) Pull request description: ### Description Fixes #729. There is an "unmaintained" warning in a old version of clap, which triggered the issue. Ideally, we should not have clap in bdk's dependency. It was only used for the `compiler.rs` example, which was a very tiny clap app compiling miniscript policies, and it wasn't really an example for bdk. This PR rewrites the example as a `policy.rs` which demos the BDK's Policy module and policy structures. - Use a `wsh(multi(2, Privkey, Pubkey))` descriptor, which has only one part private and other part public. - use `into_wallet_descriptor()` to turn that into a `Descriptor` and `KeyMap`. - Use the `KeyMap` to create a custom signer. - Extract the descriptor `Policy` structure from the given keymap. I am not very sure on how much this example is helpful. I still find it hard to read the Policy structure visually. But if Policy is something we want the user to know about descriptors and bdk wallets, this shows how to extract it for a simple multisig condition. Note: There is no use of `bdk::wallet` in the example. BDK uses the Policy extraction internally while transaction creation. But all these are exposed publicly, so can be used independently too. ### Questions: - Should we still have a `minscript::policy::compile()` example? Which IIUC is very different from `bdk::policy:Policy`. I didn't include it in this PR, because I am not sure if it fits inside bdk example categories. - Should we expose `extract_policy` as an wallet API? All though its possible to get policy without creating a wallet, why not let the wallet also spit one out for itself, if its useful? ### Checklists #### All Submissions: * [x] I've signed all my commits * [x] I followed the [contribution guidelines](https://github.com/bitcoindevkit/bdk/blob/master/CONTRIBUTING.md) * [x] I ran `cargo fmt` and `cargo clippy` before committing ACKs for top commit: afilini: ACK 97b6fb0 Tree-SHA512: 8e3719fdad308a347d22377050b2f29e02a884ff7d4e57a05a06d078de0709b5bf70bbcb1a696d1e1cdfe02cdb470e5af643da7c775b67fe318046bd6b80f440
2 parents c3faf05 + 97b6fb0 commit aff41d6

File tree

3 files changed

+101
-61
lines changed

3 files changed

+101
-61
lines changed

Cargo.toml

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -100,7 +100,6 @@ test-hardware-signer = ["hardware-signer"]
100100
[dev-dependencies]
101101
lazy_static = "1.4"
102102
env_logger = "0.7"
103-
clap = "2.33"
104103
electrsd = "0.20"
105104

106105
[[example]]
@@ -114,6 +113,10 @@ name = "miniscriptc"
114113
path = "examples/compiler.rs"
115114
required-features = ["compiler"]
116115

116+
[[example]]
117+
name = "policy"
118+
path = "examples/policy.rs"
119+
117120
[[example]]
118121
name = "rpcwallet"
119122
path = "examples/rpcwallet.rs"

examples/compiler.rs

Lines changed: 31 additions & 60 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,6 @@
1111

1212
extern crate bdk;
1313
extern crate bitcoin;
14-
extern crate clap;
1514
extern crate log;
1615
extern crate miniscript;
1716
extern crate serde_json;
@@ -21,8 +20,6 @@ use std::str::FromStr;
2120

2221
use log::info;
2322

24-
use clap::{App, Arg};
25-
2623
use bitcoin::Network;
2724
use miniscript::policy::Concrete;
2825
use miniscript::Descriptor;
@@ -31,75 +28,49 @@ use bdk::database::memory::MemoryDatabase;
3128
use bdk::wallet::AddressIndex::New;
3229
use bdk::{KeychainKind, Wallet};
3330

31+
/// Miniscript policy is a high level abstraction of spending conditions. Defined in the
32+
/// rust-miscript library here https://docs.rs/miniscript/7.0.0/miniscript/policy/index.html
33+
/// rust-miniscript provides a `compile()` function that can be used to compile any miniscript policy
34+
/// into a descriptor. This descriptor then in turn can be used in bdk a fully functioning wallet
35+
/// can be derived from the policy.
36+
///
37+
/// This example demonstrates the interaction between a bdk wallet and miniscript policy.
38+
3439
fn main() -> Result<(), Box<dyn Error>> {
3540
env_logger::init_from_env(
3641
env_logger::Env::default().filter_or(env_logger::DEFAULT_FILTER_ENV, "info"),
3742
);
3843

39-
let matches = App::new("Miniscript Compiler")
40-
.arg(
41-
Arg::with_name("POLICY")
42-
.help("Sets the spending policy to compile")
43-
.required(true)
44-
.index(1),
45-
)
46-
.arg(
47-
Arg::with_name("TYPE")
48-
.help("Sets the script type used to embed the compiled policy")
49-
.required(true)
50-
.index(2)
51-
.possible_values(&["sh", "wsh", "sh-wsh"]),
52-
)
53-
.arg(
54-
Arg::with_name("parsed_policy")
55-
.long("parsed_policy")
56-
.short("p")
57-
.help("Also return the parsed spending policy in JSON format"),
58-
)
59-
.arg(
60-
Arg::with_name("network")
61-
.short("n")
62-
.long("network")
63-
.help("Sets the network")
64-
.takes_value(true)
65-
.default_value("testnet")
66-
.possible_values(&["testnet", "regtest", "bitcoin", "signet"]),
67-
)
68-
.get_matches();
69-
70-
let policy_str = matches.value_of("POLICY").unwrap();
71-
info!("Compiling policy: {}", policy_str);
44+
// We start with a generic miniscript policy string
45+
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))))";
46+
info!("Compiling policy: \n{}", policy_str);
7247

48+
// Parse the string as a [`Concrete`] type miniscript policy.
7349
let policy = Concrete::<String>::from_str(policy_str)?;
7450

75-
let descriptor = match matches.value_of("TYPE").unwrap() {
76-
"sh" => Descriptor::new_sh(policy.compile()?)?,
77-
"wsh" => Descriptor::new_wsh(policy.compile()?)?,
78-
"sh-wsh" => Descriptor::new_sh_wsh(policy.compile()?)?,
79-
_ => panic!("Invalid type"),
80-
};
51+
// Create a `wsh` type descriptor from the policy.
52+
// `policy.compile()` returns the resulting miniscript from the policy.
53+
let descriptor = Descriptor::new_wsh(policy.compile()?)?;
8154

82-
info!("... Descriptor: {}", descriptor);
55+
info!("Compiled into following Descriptor: \n{}", descriptor);
8356

8457
let database = MemoryDatabase::new();
8558

86-
let network = matches
87-
.value_of("network")
88-
.map(Network::from_str)
89-
.transpose()
90-
.unwrap()
91-
.unwrap_or(Network::Testnet);
92-
let wallet = Wallet::new(&format!("{}", descriptor), None, network, database)?;
93-
94-
info!("... First address: {}", wallet.get_address(New)?);
95-
96-
if matches.is_present("parsed_policy") {
97-
let spending_policy = wallet.policies(KeychainKind::External)?;
98-
info!(
99-
"... Spending policy:\n{}",
100-
serde_json::to_string_pretty(&spending_policy)?
101-
);
102-
}
59+
// Create a new wallet from this descriptor
60+
let wallet = Wallet::new(&format!("{}", descriptor), None, Network::Regtest, database)?;
61+
62+
info!(
63+
"First derived address from the descriptor: \n{}",
64+
wallet.get_address(New)?
65+
);
66+
67+
// BDK also has it's own `Policy` structure to represent the spending condition in a more
68+
// human readable json format.
69+
let spending_policy = wallet.policies(KeychainKind::External)?;
70+
info!(
71+
"The BDK spending policy: \n{}",
72+
serde_json::to_string_pretty(&spending_policy)?
73+
);
10374

10475
Ok(())
10576
}

examples/policy.rs

Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
// Bitcoin Dev Kit
2+
// Written in 2020 by Alekos Filini <alekos.filini@gmail.com>
3+
//
4+
// Copyright (c) 2020-2021 Bitcoin Dev Kit Developers
5+
//
6+
// This file is licensed under the Apache License, Version 2.0 <LICENSE-APACHE
7+
// or http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
8+
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your option.
9+
// You may not use this file except in accordance with one or both of these
10+
// licenses.
11+
12+
extern crate bdk;
13+
extern crate env_logger;
14+
extern crate log;
15+
use std::error::Error;
16+
17+
use bdk::bitcoin::Network;
18+
use bdk::descriptor::{policy::BuildSatisfaction, ExtractPolicy, IntoWalletDescriptor};
19+
use bdk::wallet::signer::SignersContainer;
20+
21+
/// This example describes the use of the BDK's [`bdk::descriptor::policy`] module.
22+
///
23+
/// Policy is higher abstraction representation of the wallet descriptor spending condition.
24+
/// This is useful to express complex miniscript spending conditions into more human readable form.
25+
/// The resulting `Policy` structure can be used to derive spending conditions the wallet is capable
26+
/// to spend from.
27+
///
28+
/// This example demos a Policy output for a 2of2 multisig between between 2 parties, where the wallet holds
29+
/// one of the Extend Private key.
30+
31+
fn main() -> Result<(), Box<dyn Error>> {
32+
env_logger::init_from_env(
33+
env_logger::Env::default().filter_or(env_logger::DEFAULT_FILTER_ENV, "info"),
34+
);
35+
36+
let secp = bitcoin::secp256k1::Secp256k1::new();
37+
38+
// The descriptor used in the example
39+
// The form is "wsh(multi(2, <privkey>, <pubkey>))"
40+
let desc = "wsh(multi(2,tprv8ZgxMBicQKsPdpkqS7Eair4YxjcuuvDPNYmKX3sCniCf16tHEVrjjiSXEkFRnUH77yXc6ZcwHHcLNfjdi5qUvw3VDfgYiH5mNsj5izuiu2N/1/*,tpubD6NzVbkrYhZ4XHndKkuB8FifXm8r5FQHwrN6oZuWCz13qb93rtgKvD4PQsqC4HP4yhV3tA2fqr2RbY5mNXfM7RxXUoeABoDtsFUq2zJq6YK/1/*))";
41+
42+
// Use the descriptor string to derive the full descriptor and a keymap.
43+
// The wallet descriptor can be used to create a new bdk::wallet.
44+
// While the `keymap` can be used to create a `SignerContainer`.
45+
//
46+
// The `SignerContainer` can sign for `PSBT`s.
47+
// a bdk::wallet internally uses these to handle transaction signing.
48+
// But they can be used as independent tools also.
49+
let (wallet_desc, keymap) = desc.into_wallet_descriptor(&secp, Network::Testnet)?;
50+
51+
log::info!("Example Descriptor for policy analysis : {}", wallet_desc);
52+
53+
// Create the signer with the keymap and descriptor.
54+
let signers_container = SignersContainer::build(keymap, &wallet_desc, &secp);
55+
56+
// Extract the Policy from the given descriptor and signer.
57+
// Note that Policy is a wallet specific structure. It depends on the the descriptor, and
58+
// what the concerned wallet with a given signer can sign for.
59+
let policy = wallet_desc
60+
.extract_policy(&signers_container, BuildSatisfaction::None, &secp)?
61+
.expect("We expect a policy");
62+
63+
log::info!("Derived Policy for the descriptor {:#?}", policy);
64+
65+
Ok(())
66+
}

0 commit comments

Comments
 (0)