Skip to content

Commit 3665ea0

Browse files
committed
Merge #387: Clean up the examples
f0501da examples: Refactor htlc (Tobin C. Harding) c79235a examples: Refactor parse (Tobin C. Harding) b809710 examples: Refactor sign_multisig (Tobin C. Harding) 5ad6339 examples: Refactor verify_tx (Tobin C. Harding) 1198be7 examples: Refactor xpub_descriptors (Tobin C. Harding) Pull request description: In an effort to make the examples more digestible to newcomers do various clean ups and refactorings. Each example is done as a separate patch to assist review. The diffs get smaller as you go down the list :) ACKs for top commit: apoelstra: ACK f0501da sanket1729: ACK f0501da Tree-SHA512: 385c0e88603aa2b611a26b01341ddfa4165f532bc81c782cdfeabfca1419c83a11be8b742e0db41638b8897bb08f1c54d54818db97fa508eece287a45ce32f24
2 parents 9f1290e + f0501da commit 3665ea0

File tree

5 files changed

+259
-238
lines changed

5 files changed

+259
-238
lines changed

examples/htlc.rs

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ use miniscript::DescriptorTrait;
2424
use std::str::FromStr;
2525

2626
fn main() {
27-
//HTLC policy with 10:1 odds for happy(co-operative) case compared to uncooperative case
27+
// HTLC policy with 10:1 odds for happy (co-operative) case compared to uncooperative case.
2828
let htlc_policy = Concrete::<bitcoin::PublicKey>::from_str(&format!("or(10@and(sha256({secret_hash}),pk({redeem_identity})),1@and(older({expiry}),pk({refund_identity})))",
2929
secret_hash = "1111111111111111111111111111111111111111111111111111111111111111",
3030
redeem_identity = "022222222222222222222222222222222222222222222222222222222222222222",
@@ -39,31 +39,34 @@ fn main() {
3939
)
4040
.expect("Resource limits");
4141

42-
// Check whether the descriptor is safe
43-
// This checks whether all spend paths are accessible in bitcoin network.
44-
// It maybe possible that some of the spend require more than 100 elements in Wsh scripts
45-
// Or they contain a combination of timelock and heightlock.
42+
// Check whether the descriptor is safe. This checks whether all spend paths are accessible in
43+
// the Bitcoin network. It may be possible that some of the spend paths require more than 100
44+
// elements in Wsh scripts or they contain a combination of timelock and heightlock.
4645
assert!(htlc_descriptor.sanity_check().is_ok());
4746
assert_eq!(
4847
format!("{}", htlc_descriptor),
4948
"wsh(andor(pk(022222222222222222222222222222222222222222222222222222222222222222),sha256(1111111111111111111111111111111111111111111111111111111111111111),and_v(v:pkh(51814f108670aced2d77c1805ddd6634bc9d4731),older(4444))))#s0qq76ng"
5049
);
5150

51+
// Lift the descriptor into an abstract policy.
5252
assert_eq!(
5353
format!("{}", htlc_descriptor.lift().unwrap()),
5454
"or(and(pkh(4377a5acd66dc5cb67148a24818d1e51fa183bd2),sha256(1111111111111111111111111111111111111111111111111111111111111111)),and(pkh(51814f108670aced2d77c1805ddd6634bc9d4731),older(4444)))"
5555
);
5656

57+
// Get the scriptPpubkey for this Wsh descriptor.
5758
assert_eq!(
5859
format!("{:x}", htlc_descriptor.spk()),
5960
"0020d853877af928a8d2a569c9c0ed14bd16f6a80ce9cccaf8a6150fd8f7f8867ae2"
6061
);
6162

63+
// Encode the Wsh descriptor into a Bitcoin script.
6264
assert_eq!(
6365
format!("{:x}", htlc_descriptor.inner_script()),
6466
"21022222222222222222222222222222222222222222222222222222222222222222ac6476a91451814f108670aced2d77c1805ddd6634bc9d473188ad025c11b26782012088a82011111111111111111111111111111111111111111111111111111111111111118768"
6567
);
6668

69+
// Get the address for this Wsh descriptor.v
6770
assert_eq!(
6871
format!("{}", htlc_descriptor.address(Network::Bitcoin).unwrap()),
6972
"bc1qmpfcw7he9z5d9ftfe8qw699azmm2sr8fen903fs4plv007yx0t3qxfmqv5"

examples/parse.rs

Lines changed: 16 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@
1212
// If not, see <http://creativecommons.org/publicdomain/zero/1.0/>.
1313
//
1414

15-
//! Example: Parsing a descriptor from a string
15+
//! Example: Parsing a descriptor from a string.
1616
1717
use bitcoin;
1818
use miniscript;
@@ -21,54 +21,49 @@ use miniscript::{descriptor::DescriptorType, Descriptor, DescriptorTrait};
2121
use std::str::FromStr;
2222

2323
fn main() {
24-
let my_descriptor = miniscript::Descriptor::<bitcoin::PublicKey>::from_str(
24+
let desc = miniscript::Descriptor::<bitcoin::PublicKey>::from_str(
2525
"wsh(c:pk_k(020202020202020202020202020202020202020202020202020202020202020202))",
2626
)
2727
.unwrap();
2828

29-
// Check whether the descriptor is safe
30-
// This checks whether all spend paths are accessible in bitcoin network.
31-
// It maybe possible that some of the spend require more than 100 elements in Wsh scripts
32-
// Or they contain a combination of timelock and heightlock.
33-
assert!(my_descriptor.sanity_check().is_ok());
29+
// Check whether the descriptor is safe. This checks whether all spend paths are accessible in
30+
// the Bitcoin network. It may be possible that some of the spend paths require more than 100
31+
// elements in Wsh scripts or they contain a combination of timelock and heightlock.
32+
assert!(desc.sanity_check().is_ok());
3433

3534
// Compute the script pubkey. As mentioned in the documentation, script_pubkey only fails
36-
// for Tr descriptors that don't have some pre-computed data
35+
// for Tr descriptors that don't have some pre-computed data.
3736
assert_eq!(
38-
format!("{:x}", my_descriptor.script_pubkey()),
37+
format!("{:x}", desc.script_pubkey()),
3938
"0020daef16dd7c946a3e735a6e43310cb2ce33dfd14a04f76bf8241a16654cb2f0f9"
4039
);
4140

42-
// Another way to compute script pubkey
43-
// We can also compute the type of descriptor
44-
let desc_type = my_descriptor.desc_type();
41+
// As another way to compute script pubkey; we can also compute the type of the descriptor.
42+
let desc_type = desc.desc_type();
4543
assert_eq!(desc_type, DescriptorType::Wsh);
46-
// Since we know the type of descriptor, we can get the Wsh struct from Descriptor
47-
// This allows us to call infallible methods for getting script pubkey
48-
if let Descriptor::Wsh(wsh) = &my_descriptor {
44+
// Since we know the type of descriptor, we can get the Wsh struct from Descriptor. This allows
45+
// us to call infallible methods for getting script pubkey.
46+
if let Descriptor::Wsh(wsh) = &desc {
4947
assert_eq!(
5048
format!("{:x}", wsh.spk()),
5149
"0020daef16dd7c946a3e735a6e43310cb2ce33dfd14a04f76bf8241a16654cb2f0f9"
5250
);
53-
} else {
54-
// We checked for the descriptor type earlier
5551
}
5652

57-
// Get the inner script inside the descriptor
53+
// Get the inner script inside the descriptor.
5854
assert_eq!(
5955
format!(
6056
"{:x}",
61-
my_descriptor
62-
.explicit_script()
57+
desc.explicit_script()
6358
.expect("Wsh descriptors have inner scripts")
6459
),
6560
"21020202020202020202020202020202020202020202020202020202020202020202ac"
6661
);
6762

63+
// In a similar fashion we can parse a wrapped segwit script.
6864
let desc = miniscript::Descriptor::<bitcoin::PublicKey>::from_str(
6965
"sh(wsh(c:pk_k(020202020202020202020202020202020202020202020202020202020202020202)))",
7066
)
7167
.unwrap();
72-
7368
assert!(desc.desc_type() == DescriptorType::ShWsh);
7469
}

examples/sign_multisig.rs

Lines changed: 82 additions & 78 deletions
Original file line numberDiff line numberDiff line change
@@ -12,23 +12,87 @@
1212
// If not, see <http://creativecommons.org/publicdomain/zero/1.0/>.
1313
//
1414

15-
//! Example: Signing a 2-of-3 multisignature
15+
//! Example: Signing a 2-of-3 multisignature.
1616
1717
use bitcoin;
1818
use miniscript;
1919

2020
use bitcoin::blockdata::witness::Witness;
21-
use bitcoin::secp256k1; // secp256k1 re-exported from rust-bitcoin
21+
use bitcoin::secp256k1;
2222
use miniscript::DescriptorTrait;
2323
use std::collections::HashMap;
2424
use std::str::FromStr;
2525

2626
fn main() {
27-
// Avoid repeatedly typing a pretty-common descriptor type
28-
type BitcoinDescriptor = miniscript::Descriptor<bitcoin::PublicKey>;
27+
let mut tx = spending_transaction();
28+
let pks = list_of_three_arbitrary_public_keys();
29+
let sig = random_signature_from_the_blockchain();
2930

30-
// Transaction which spends some output
31-
let mut tx = bitcoin::Transaction {
31+
// Descriptor for the output being spent.
32+
let s = format!("wsh(multi(2,{},{},{}))", pks[0], pks[1], pks[2],);
33+
let descriptor = miniscript::Descriptor::<bitcoin::PublicKey>::from_str(&s).unwrap();
34+
35+
// Check weight for witness satisfaction cost ahead of time.
36+
// 4 (scriptSig length of 0) + 1 (witness stack size) + 106 (serialized witnessScript)
37+
// + 73*2 (signature length + signatures + sighash bytes) + 1 (dummy byte) = 258
38+
assert_eq!(descriptor.max_satisfaction_weight().unwrap(), 258);
39+
40+
// Sometimes it is necessary to have additional information to get the
41+
// `bitcoin::PublicKey` from the `MiniscriptKey` which can be supplied by
42+
// the `to_pk_ctx` parameter. For example, when calculating the script
43+
// pubkey of a descriptor with xpubs, the secp context and child information
44+
// maybe required.
45+
46+
// Observe the script properties, just for fun.
47+
assert_eq!(
48+
format!("{:x}", descriptor.script_pubkey()),
49+
"00200ed49b334a12c37f3df8a2974ad91ff95029215a2b53f78155be737907f06163"
50+
);
51+
52+
assert_eq!(
53+
format!(
54+
"{:x}",
55+
descriptor
56+
.explicit_script()
57+
.expect("wsh descriptors have unique inner script")
58+
),
59+
"52\
60+
21020202020202020202020202020202020202020202020202020202020202020202\
61+
21020102030405060708010203040506070801020304050607080000000000000000\
62+
21030102030405060708010203040506070801020304050607080000000000000000\
63+
53ae"
64+
);
65+
66+
// Attempt to satisfy at age 0, height 0.
67+
let original_txin = tx.input[0].clone();
68+
69+
let mut sigs = HashMap::<bitcoin::PublicKey, miniscript::bitcoin::EcdsaSig>::new();
70+
71+
// Doesn't work with no signatures.
72+
assert!(descriptor.satisfy(&mut tx.input[0], &sigs).is_err());
73+
assert_eq!(tx.input[0], original_txin);
74+
75+
// ...or one signature...
76+
sigs.insert(pks[1], sig);
77+
assert!(descriptor.satisfy(&mut tx.input[0], &sigs).is_err());
78+
assert_eq!(tx.input[0], original_txin);
79+
80+
// ...but two signatures is ok.
81+
sigs.insert(pks[2], sig);
82+
assert!(descriptor.satisfy(&mut tx.input[0], &sigs).is_ok());
83+
assert_ne!(tx.input[0], original_txin);
84+
assert_eq!(tx.input[0].witness.len(), 4); // 0, sig, sig, witness script
85+
86+
// ...and even if we give it a third signature, only two are used.
87+
sigs.insert(pks[0], sig);
88+
assert!(descriptor.satisfy(&mut tx.input[0], &sigs).is_ok());
89+
assert_ne!(tx.input[0], original_txin);
90+
assert_eq!(tx.input[0].witness.len(), 4); // 0, sig, sig, witness script
91+
}
92+
93+
// Transaction which spends some output.
94+
fn spending_transaction() -> bitcoin::Transaction {
95+
bitcoin::Transaction {
3296
version: 2,
3397
lock_time: 0,
3498
input: vec![bitcoin::TxIn {
@@ -41,10 +105,12 @@ fn main() {
41105
script_pubkey: bitcoin::Script::new(),
42106
value: 100_000_000,
43107
}],
44-
};
108+
}
109+
}
45110

111+
fn list_of_three_arbitrary_public_keys() -> Vec<bitcoin::PublicKey> {
46112
#[cfg_attr(feature="cargo-fmt", rustfmt_skip)]
47-
let public_keys = vec![
113+
vec![
48114
bitcoin::PublicKey::from_slice(&[2; 33]).expect("key 1"),
49115
bitcoin::PublicKey::from_slice(&[
50116
0x02,
@@ -60,10 +126,13 @@ fn main() {
60126
0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08,
61127
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
62128
]).expect("key 3"),
63-
];
64-
let bitcoin_sig = bitcoin::EcdsaSig {
65-
// copied at random off the blockchain; this is not actually a valid
66-
// signature for this transaction; Miniscript does not verify
129+
]
130+
}
131+
132+
// Returns a signature copied at random off the blockchain; this is not actually
133+
// a valid signature for this transaction; Miniscript does not verify the validity.
134+
fn random_signature_from_the_blockchain() -> bitcoin::EcdsaSig {
135+
bitcoin::EcdsaSig {
67136
sig: secp256k1::ecdsa::Signature::from_str(
68137
"3045\
69138
0221\
@@ -73,70 +142,5 @@ fn main() {
73142
)
74143
.unwrap(),
75144
hash_ty: bitcoin::EcdsaSighashType::All,
76-
};
77-
78-
let descriptor_str = format!(
79-
"wsh(multi(2,{},{},{}))",
80-
public_keys[0], public_keys[1], public_keys[2],
81-
);
82-
83-
// Descriptor for the output being spent
84-
let my_descriptor =
85-
BitcoinDescriptor::from_str(&descriptor_str[..]).expect("parse descriptor string");
86-
87-
// Check weight for witness satisfaction cost ahead of time.
88-
// 4(scriptSig length of 0) + 1(witness stack size) + 106(serialized witnessScript)
89-
// + 73*2(signature length + signatures + sighash bytes) + 1(dummy byte) = 258
90-
assert_eq!(my_descriptor.max_satisfaction_weight().unwrap(), 258);
91-
92-
// Sometimes it is necessary to have additional information to get the bitcoin::PublicKey
93-
// from the MiniscriptKey which can supplied by `to_pk_ctx` parameter. For example,
94-
// when calculating the script pubkey of a descriptor with xpubs, the secp context and
95-
// child information maybe required.
96-
97-
// Observe the script properties, just for fun
98-
assert_eq!(
99-
format!("{:x}", my_descriptor.script_pubkey()),
100-
"00200ed49b334a12c37f3df8a2974ad91ff95029215a2b53f78155be737907f06163"
101-
);
102-
103-
assert_eq!(
104-
format!(
105-
"{:x}",
106-
my_descriptor
107-
.explicit_script()
108-
.expect("wsh descriptors have unique inner script")
109-
),
110-
"52\
111-
21020202020202020202020202020202020202020202020202020202020202020202\
112-
21020102030405060708010203040506070801020304050607080000000000000000\
113-
21030102030405060708010203040506070801020304050607080000000000000000\
114-
53ae"
115-
);
116-
117-
// Attempt to satisfy at age 0, height 0
118-
let original_txin = tx.input[0].clone();
119-
120-
let mut sigs = HashMap::<bitcoin::PublicKey, miniscript::bitcoin::EcdsaSig>::new();
121-
122-
// Doesn't work with no signatures
123-
assert!(my_descriptor.satisfy(&mut tx.input[0], &sigs).is_err());
124-
assert_eq!(tx.input[0], original_txin);
125-
126-
// ...or one signature...
127-
sigs.insert(public_keys[1], bitcoin_sig);
128-
assert!(my_descriptor.satisfy(&mut tx.input[0], &sigs).is_err());
129-
assert_eq!(tx.input[0], original_txin);
130-
131-
// ...but two signatures is ok
132-
sigs.insert(public_keys[2], bitcoin_sig);
133-
assert!(my_descriptor.satisfy(&mut tx.input[0], &sigs).is_ok());
134-
assert_ne!(tx.input[0], original_txin);
135-
assert_eq!(tx.input[0].witness.len(), 4); // 0, sig, sig, witness script
136-
137-
// ...and even if we give it a third signature, only two are used
138-
sigs.insert(public_keys[0], bitcoin_sig);
139-
assert!(my_descriptor.satisfy(&mut tx.input[0], &sigs).is_ok());
140-
assert_ne!(tx.input[0], original_txin);
141-
assert_eq!(tx.input[0].witness.len(), 4); // 0, sig, sig, witness script
145+
}
142146
}

0 commit comments

Comments
 (0)