Skip to content

Commit b809710

Browse files
committed
examples: Refactor sign_multisig
In an effort to make the example more clear; refactor the `sign_multisig` example. Refactor only, no logic changes.
1 parent 5ad6339 commit b809710

File tree

1 file changed

+82
-78
lines changed

1 file changed

+82
-78
lines changed

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)