Skip to content

Commit 85f793b

Browse files
committed
Adding brief explaination
Signed-off-by: Harshil Jani <[email protected]>
1 parent 980664b commit 85f793b

File tree

2 files changed

+74
-32
lines changed

2 files changed

+74
-32
lines changed

examples/plan_spend.rs

+39-16
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,25 @@ fn main() {
2929
"023fc33527afab09fa97135f2180bcd22ce637b1d2fbcb2db748b1f2c33f45b2b4",
3030
];
3131

32-
// Defining the taproot descriptor
32+
// The taproot descriptor combines different spending paths and conditions, allowing the funds to be spent using
33+
// different methods depending on the desired conditions.
34+
35+
// tr({A},{{pkh({B}),{{multi_a(1,{C},{D}),and_v(v:pk({E}),after(10))}}}}) represents a taproot spending policy.
36+
// Let's break it down:
37+
//
38+
// * Key Spend Path
39+
// {A} represents the public key for the taproot key spending path.
40+
//
41+
// * Script Spend Paths
42+
// {B} represents the public key for the pay-to-pubkey-hash (P2PKH) spending path.
43+
// The multi_a(1,{C},{D}) construct represents a 1-of-2 multi-signature script condition.
44+
// It requires at least one signature from {C} and {D} to spend funds using the script spend path.
45+
// The and_v(v:pk({E}),after(10)) construct represents a combination of a P2PK script condition and a time lock.
46+
// It requires a valid signature from {E} and enforces a time lock of 10 blocks on spending funds.
47+
48+
// By constructing transactions using this taproot descriptor and signing them appropriately,
49+
// you can create flexible spending policies that enable different spending paths and conditions depending on the
50+
// transaction's inputs and outputs.
3351
let s = format!(
3452
"tr({},{{pkh({}),{{multi_a(1,{},{}),and_v(v:pk({}),after(10))}}}})",
3553
keys[0], keys[1], keys[2], keys[3], keys[4]
@@ -127,17 +145,24 @@ fn main() {
127145
value: amount * 4 / 5,
128146
});
129147

130-
// Plan the Transaction using available assets
148+
// Consider that out of all the keys required to sign the descriptor spend path we only have some handful of assets.
149+
// We can plan the PSBT with only few assets(keys or hashes) if that are enough for satisfying any policy.
150+
//
151+
// Here for example assume that we only have two keys available.
152+
// Key A and Key B (as seen from the descriptor above)
153+
// We have to add the keys to `Asset` and then obtain plan with only available signatures if the descriptor can be satisfied.
131154
let mut assets = Assets::new();
132-
assets = assets.add(DescriptorPublicKey::from_str(keys[0]).unwrap()); // Master Key for Key Spend Path
133-
assets = assets.add(DescriptorPublicKey::from_str(keys[1]).unwrap()); // Script Spend Path
155+
assets = assets.add(DescriptorPublicKey::from_str(keys[0]).unwrap()); // Master Key for Key Spend Path - Key A
156+
assets = assets.add(DescriptorPublicKey::from_str(keys[1]).unwrap()); // Script Spend Path - Key B
134157

135-
// Obtain the result of the plan based on provided assets
136-
let result = bridge_descriptor.clone().get_plan(&assets);
158+
// Obtain the Plan based on available Assets
159+
let plan = bridge_descriptor.clone().get_plan(&assets).unwrap();
137160

138161
// Creating PSBT Input
139162
let mut input = psbt::Input::default();
140-
result.unwrap().update_psbt_input(&mut input);
163+
plan.update_psbt_input(&mut input);
164+
165+
// Update the PSBT input from the result which we have obtained from the Plan.
141166
input
142167
.update_with_descriptor_unchecked(&bridge_descriptor)
143168
.unwrap();
@@ -148,15 +173,13 @@ fn main() {
148173
psbt.outputs.push(psbt::Output::default());
149174

150175
// Use private keys to sign
151-
let sk1 = master_private_key.inner;
152-
let sk2 = backup1_private.inner;
153-
154-
// In the following example we have signed the descriptor with master key
155-
// which will allow the transaction to be key spend type.
156-
// Any other key apart from master key is part of script policies and it
157-
// will sign for script spend path if it satisfies.
158-
sign_taproot_psbt(&sk1, &mut psbt, &secp256k1); // Key Spend
159-
sign_taproot_psbt(&sk2, &mut psbt, &secp256k1); // Script Spend
176+
let key_a = master_private_key.inner;
177+
let key_b = backup1_private.inner;
178+
179+
// Taproot script can be signed when we have either Key spend or Script spend or both.
180+
// Here you can try to verify by commenting one of the spend path or try signing with both.
181+
sign_taproot_psbt(&key_a, &mut psbt, &secp256k1); // Key Spend - With Key A
182+
sign_taproot_psbt(&key_b, &mut psbt, &secp256k1); // Script Spend - With Key B
160183

161184
// Serializing and finalizing the PSBT Transaction
162185
let serialized = psbt.serialize();

examples/psbt_sign_finalize.rs

+35-16
Original file line numberDiff line numberDiff line change
@@ -15,9 +15,29 @@ use miniscript::psbt::{PsbtExt, PsbtInputExt};
1515
use miniscript::{Descriptor, DescriptorPublicKey};
1616

1717
fn main() {
18-
// Defining the descriptor
18+
// Defining the descriptor keys
1919
let secp256k1 = secp256k1::Secp256k1::new();
20-
let s = "wsh(t:or_c(pk(027a3565454fe1b749bccaef22aff72843a9c3efefd7b16ac54537a0c23f0ec0de),v:thresh(1,pkh(032d672a1a91cc39d154d366cd231983661b0785c7f27bc338447565844f4a6813),a:pkh(03417129311ed34c242c012cd0a3e0b9bca0065f742d0dfb63c78083ea6a02d4d9),a:pkh(025a687659658baeabdfc415164528065be7bcaade19342241941e556557f01e28))))#7hut9ukn";
20+
let keys = vec![
21+
"027a3565454fe1b749bccaef22aff72843a9c3efefd7b16ac54537a0c23f0ec0de",
22+
"032d672a1a91cc39d154d366cd231983661b0785c7f27bc338447565844f4a6813",
23+
"03417129311ed34c242c012cd0a3e0b9bca0065f742d0dfb63c78083ea6a02d4d9",
24+
"025a687659658baeabdfc415164528065be7bcaade19342241941e556557f01e28",
25+
];
26+
// The wsh descriptor indicates a Witness Script Hash, meaning the descriptor is for a SegWit script.
27+
// wsh(or(pk(A),thresh(1,pkh(B),pkh(C),pkh(D))))
28+
29+
// Let's break it down:
30+
// t:or_c specifies an "or" construct, which means the script can be satisfied by one of the given conditions:
31+
// pk(A) OR thresh(1,pkh(B),pkh(C),pkh(D))
32+
// Inside threshold condition atleast 1 out of all given conditions should satisfy.
33+
34+
// By constructing transactions using this wsh descriptor and signing them appropriately,
35+
// you can create flexible spending policies that enable different spending paths and conditions depending on the
36+
// transaction's inputs and outputs.
37+
let s = format!(
38+
"wsh(t:or_c(pk({}),v:thresh(1,pkh({}),a:pkh({}),a:pkh({}))))#7hut9ukn",
39+
keys[0], keys[1], keys[2], keys[3]
40+
);
2141
let bridge_descriptor = Descriptor::from_str(&s).expect("parse descriptor string");
2242

2343
assert!(bridge_descriptor.sanity_check().is_ok());
@@ -116,10 +136,12 @@ fn main() {
116136
value: amount * 4 / 5,
117137
});
118138

119-
// Plan the Transaction using available assets
120-
// The descriptor is : or(pk(A),thresh(1,pkh(B),pkh(C),pkh(D)))
121-
// For the context of planning in this example, We will only provide the key A as an asset
122-
// This will satisfy the pk(A) and since we have an OR, This should be sufficient to satisfy the given policy.
139+
// Consider that out of all the keys required to sign the descriptor, we only have some handful of assets.
140+
// We can plan the PSBT with only few assets(keys or hashes) if that are enough for satisfying any policy.
141+
//
142+
// Here for example assume that we only have one key available i.e Key A(as seen from the descriptor above)
143+
// Key A is enough to satisfy the given descriptor because it is OR.
144+
// We have to add the key to `Asset` and then obtain plan with only available signature if the descriptor can be satisfied.
123145
let mut assets = Assets::new();
124146
assets = assets.add(
125147
DescriptorPublicKey::from_str(
@@ -128,12 +150,14 @@ fn main() {
128150
.unwrap(),
129151
);
130152

131-
// Obtain the result of the plan based on provided assets
132-
let result = bridge_descriptor.clone().get_plan(&assets);
153+
// Obtain the Plan based on available Assets
154+
let plan = bridge_descriptor.clone().get_plan(&assets).unwrap();
133155

134156
// Creating a PSBT Input
135157
let mut input = psbt::Input::default();
136-
result.unwrap().update_psbt_input(&mut input);
158+
159+
// Update the PSBT input from the result which we have obtained from the Plan.
160+
plan.update_psbt_input(&mut input);
137161
input
138162
.update_with_descriptor_unchecked(&bridge_descriptor)
139163
.unwrap();
@@ -158,16 +182,11 @@ fn main() {
158182

159183
// Finally construct the signature and add to psbt
160184
let sig1 = secp256k1.sign_ecdsa(&msg, &sk1);
161-
let pk1 = backup1_private.public_key(&secp256k1);
185+
let key_a = backup1_private.public_key(&secp256k1);
162186
assert!(secp256k1.verify_ecdsa(&msg, &sig1, &pk1.inner).is_ok());
163187

164-
// Second key just in case
165-
let sig2 = secp256k1.sign_ecdsa(&msg, &sk2);
166-
let pk2 = backup2_private.public_key(&secp256k1);
167-
assert!(secp256k1.verify_ecdsa(&msg, &sig2, &pk2.inner).is_ok());
168-
169188
psbt.inputs[0].partial_sigs.insert(
170-
pk1,
189+
key_a,
171190
bitcoin::ecdsa::Signature {
172191
sig: sig1,
173192
hash_ty: hash_ty,

0 commit comments

Comments
 (0)