Skip to content

Commit abeda65

Browse files
Introduce max_weight_to_satisfy
This commit has two intentions: 1. Define `max_weight_to_satisfy` to be the difference in `TxIn` weight between "satisfied" and "unsatisfied" states. In an "unsatisfied" state, we still need to include the `scriptSigLen` varint, as well as the `witnessStackLen` (for txs with at least one segwit spend). 2. Deprecrate `max_satisfaction_weight` Comments, tests and examples have been updated to reflect the above intentions. Co-authored-by: Daniela Brozzoni <[email protected]>
1 parent a7bfff4 commit abeda65

File tree

11 files changed

+231
-18
lines changed

11 files changed

+231
-18
lines changed

embedded/src/main.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,7 @@ fn main() -> ! {
5353
assert!(desc.sanity_check().is_ok());
5454

5555
// Estimate the satisfaction cost
56-
assert_eq!(desc.max_satisfaction_weight().unwrap(), 293);
56+
assert_eq!(desc.max_weight_to_satisfy().unwrap(), 284);
5757
// end miniscript test
5858

5959
// exit QEMU

examples/psbt_sign_finalize.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ fn main() {
3333
);
3434
println!(
3535
"Weight for witness satisfaction cost {}",
36-
bridge_descriptor.max_satisfaction_weight().unwrap()
36+
bridge_descriptor.max_weight_to_satisfy().unwrap()
3737
);
3838

3939
let master_private_key_str = "cQhdvB3McbBJdx78VSSumqoHQiSXs75qwLptqwxSQBNBMDxafvaw";

examples/sign_multisig.rs

+3-3
Original file line numberDiff line numberDiff line change
@@ -30,9 +30,9 @@ fn main() {
3030
let descriptor = miniscript::Descriptor::<bitcoin::PublicKey>::from_str(&s).unwrap();
3131

3232
// Check weight for witness satisfaction cost ahead of time.
33-
// 4 (scriptSig length of 0) + 1 (witness stack size) + 106 (serialized witnessScript)
34-
// + 73*2 (signature length + signatures + sighash bytes) + 1 (dummy byte) = 258
35-
assert_eq!(descriptor.max_satisfaction_weight().unwrap(), 258);
33+
// 106 (serialized witnessScript)
34+
// + 73*2 (signature length + signatures + sighash bytes) + 1 (dummy byte) = 253
35+
assert_eq!(descriptor.max_weight_to_satisfy().unwrap(), 253);
3636

3737
// Sometimes it is necessary to have additional information to get the
3838
// `bitcoin::PublicKey` from the `MiniscriptKey` which can be supplied by

examples/taproot.rs

+5-5
Original file line numberDiff line numberDiff line change
@@ -100,11 +100,11 @@ fn main() {
100100

101101
// Max Satisfaction Weight for compilation, corresponding to the script-path spend
102102
// `multi_a(2,PUBKEY_1,PUBKEY_2) at taptree depth 1, having
103-
// Max Witness Size = scriptSig len + witnessStack len + varint(control_block_size) +
104-
// control_block size + varint(script_size) + script_size + max_satisfaction_size
105-
// = 4 + 1 + 1 + 65 + 1 + 70 + 132 = 274
106-
let max_sat_wt = real_desc.max_satisfaction_weight().unwrap();
107-
assert_eq!(max_sat_wt, 274);
103+
// Max Witness Size = varint(control_block_size) + control_block size +
104+
// varint(script_size) + script_size + max_satisfaction_size
105+
// = 1 + 65 + 1 + 70 + 132 = 269
106+
let max_sat_wt = real_desc.max_weight_to_satisfy().unwrap();
107+
assert_eq!(max_sat_wt, 269);
108108

109109
// Compute the bitcoin address and check if it matches
110110
let network = Network::Bitcoin;

src/descriptor/bare.rs

+30-2
Original file line numberDiff line numberDiff line change
@@ -66,15 +66,29 @@ impl<Pk: MiniscriptKey> Bare<Pk> {
6666
Ok(())
6767
}
6868

69+
/// Computes an upper bound on the difference in weight between a
70+
/// non-satisfied `TxIn` (with empty `scriptSig` and `witness` fields) and a
71+
/// satisfied `TxIn`.
72+
///
73+
/// Assumes all ECDSA signatures are 73 bytes, including push opcode and
74+
/// sighash suffix.
75+
pub fn max_weight_to_satisfy(&self) -> Result<usize, Error> {
76+
let scriptsig_size = self.ms.max_satisfaction_size()?;
77+
// scriptSig varint difference between non-satisfied (0) and satisfied
78+
let scriptsig_varint_diff = varint_len(scriptsig_size) - varint_len(0);
79+
Ok(4 * (scriptsig_varint_diff + scriptsig_size))
80+
}
81+
6982
/// Computes an upper bound on the weight of a satisfying witness to the
7083
/// transaction.
7184
///
72-
/// Assumes all ec-signatures are 73 bytes, including push opcode and
85+
/// Assumes all ECDSA signatures are 73 bytes, including push opcode and
7386
/// sighash suffix. Includes the weight of the VarInts encoding the
7487
/// scriptSig and witness stack length.
7588
///
7689
/// # Errors
7790
/// When the descriptor is impossible to safisfy (ex: sh(OP_FALSE)).
91+
#[deprecated(note = "use max_weight_to_satisfy instead")]
7892
pub fn max_satisfaction_weight(&self) -> Result<usize, Error> {
7993
let scriptsig_len = self.ms.max_satisfaction_size()?;
8094
Ok(4 * (varint_len(scriptsig_len) + scriptsig_len))
@@ -212,10 +226,24 @@ impl<Pk: MiniscriptKey> Pkh<Pk> {
212226
self.pk
213227
}
214228

229+
/// Computes an upper bound on the difference in weight between a
230+
/// non-satisfied `TxIn` (with empty `scriptSig` and `witness` fields) and a
231+
/// satisfied `TxIn`.
232+
///
233+
/// Assumes all ECDSA signatures are 73 bytes, including push opcode and
234+
/// sighash suffix.
235+
pub fn max_weight_to_satisfy(&self) -> usize {
236+
// OP_72 + <sig(71)+sigHash(1)> + OP_33 + <pubkey>
237+
let scriptsig_size = 73 + BareCtx::pk_len(&self.pk);
238+
// scriptSig varint different between non-satisfied (0) and satisfied
239+
let scriptsig_varint_diff = varint_len(scriptsig_size) - varint_len(0);
240+
4 * (scriptsig_varint_diff + scriptsig_size)
241+
}
242+
215243
/// Computes an upper bound on the weight of a satisfying witness to the
216244
/// transaction.
217245
///
218-
/// Assumes all ec-signatures are 73 bytes, including push opcode and
246+
/// Assumes all ECDSA signatures are 73 bytes, including push opcode and
219247
/// sighash suffix. Includes the weight of the VarInts encoding the
220248
/// scriptSig and witness stack length.
221249
pub fn max_satisfaction_weight(&self) -> usize {

src/descriptor/mod.rs

+36-1
Original file line numberDiff line numberDiff line change
@@ -317,15 +317,50 @@ impl<Pk: MiniscriptKey> Descriptor<Pk> {
317317
}
318318
}
319319

320+
/// Computes an upper bound on the difference in weight between a
321+
/// non-satisfied `TxIn` and a satisfied `TxIn`.
322+
///
323+
/// A non-satisfied `TxIn` contains the `scriptSigLen` varint recording the
324+
/// value 0 (which is 4WU) and also the `witnessStackLen` for transactions
325+
/// that have at least one witness spend, which will also record the value 0
326+
/// (which is 1WU).
327+
///
328+
/// Hence, "satisfaction weight" contains the additional varint weight of
329+
/// `scriptSigLen` and `witnessStackLen` (which occurs when the varint value
330+
/// increases over the 1byte threshold), and also the weights of `scriptSig`
331+
/// and the rest of the `witnessField` (which is the stack items and the
332+
/// stack-item-len varints of each item).
333+
///
334+
/// Assumes all ECDSA signatures are 73 bytes, including push opcode and
335+
/// sighash suffix.
336+
/// Assumes all Schnorr signatures are 66 bytes, including push opcode and
337+
/// sighash suffix.
338+
///
339+
/// # Errors
340+
/// When the descriptor is impossible to safisfy (ex: sh(OP_FALSE)).
341+
pub fn max_weight_to_satisfy(&self) -> Result<usize, Error> {
342+
let weight = match *self {
343+
Descriptor::Bare(ref bare) => bare.max_weight_to_satisfy()?,
344+
Descriptor::Pkh(ref pkh) => pkh.max_weight_to_satisfy(),
345+
Descriptor::Wpkh(ref wpkh) => wpkh.max_weight_to_satisfy(),
346+
Descriptor::Wsh(ref wsh) => wsh.max_weight_to_satisfy()?,
347+
Descriptor::Sh(ref sh) => sh.max_weight_to_satisfy()?,
348+
Descriptor::Tr(ref tr) => tr.max_weight_to_satisfy()?,
349+
};
350+
Ok(weight)
351+
}
352+
320353
/// Computes an upper bound on the weight of a satisfying witness to the
321354
/// transaction.
322355
///
323-
/// Assumes all ec-signatures are 73 bytes, including push opcode and
356+
/// Assumes all ECDSA signatures are 73 bytes, including push opcode and
324357
/// sighash suffix. Includes the weight of the VarInts encoding the
325358
/// scriptSig and witness stack length.
326359
///
327360
/// # Errors
328361
/// When the descriptor is impossible to safisfy (ex: sh(OP_FALSE)).
362+
#[deprecated(note = "use max_weight_to_satisfy instead")]
363+
#[allow(deprecated)]
329364
pub fn max_satisfaction_weight(&self) -> Result<usize, Error> {
330365
let weight = match *self {
331366
Descriptor::Bare(ref bare) => bare.max_satisfaction_weight()?,

src/descriptor/segwitv0.rs

+46-2
Original file line numberDiff line numberDiff line change
@@ -82,15 +82,45 @@ impl<Pk: MiniscriptKey> Wsh<Pk> {
8282
Ok(())
8383
}
8484

85+
/// Computes an upper bound on the difference in weight between a
86+
/// non-satisfied `TxIn` (with empty `scriptSig` and `witness` fields) and a
87+
/// satisfied `TxIn`.
88+
///
89+
/// Assumes all ECDSA signatures are 73 bytes, including push opcode and
90+
/// sighash suffix.
91+
///
92+
/// # Errors
93+
/// When the descriptor is impossible to safisfy (ex: sh(OP_FALSE)).
94+
pub fn max_weight_to_satisfy(&self) -> Result<usize, Error> {
95+
let (redeem_script_size, max_sat_elems, max_sat_size) = match self.inner {
96+
WshInner::SortedMulti(ref smv) => (
97+
smv.script_size(),
98+
smv.max_satisfaction_witness_elements(),
99+
smv.max_satisfaction_size(),
100+
),
101+
WshInner::Ms(ref ms) => (
102+
ms.script_size(),
103+
ms.max_satisfaction_witness_elements()?,
104+
ms.max_satisfaction_size()?,
105+
),
106+
};
107+
// stack size varint difference between non-satisfied (0) and satisfied
108+
// `max_sat_elems` is inclusive of the "witness script" (redeem script)
109+
let stack_varint_diff = varint_len(max_sat_elems) - varint_len(0);
110+
111+
Ok(stack_varint_diff + varint_len(redeem_script_size) + redeem_script_size + max_sat_size)
112+
}
113+
85114
/// Computes an upper bound on the weight of a satisfying witness to the
86115
/// transaction.
87116
///
88-
/// Assumes all ec-signatures are 73 bytes, including push opcode and
117+
/// Assumes all ECDSA signatures are 73 bytes, including push opcode and
89118
/// sighash suffix. Includes the weight of the VarInts encoding the
90119
/// scriptSig and witness stack length.
91120
///
92121
/// # Errors
93122
/// When the descriptor is impossible to safisfy (ex: sh(OP_FALSE)).
123+
#[deprecated(note = "use max_weight_to_satisfy instead")]
94124
pub fn max_satisfaction_weight(&self) -> Result<usize, Error> {
95125
let (script_size, max_sat_elems, max_sat_size) = match self.inner {
96126
WshInner::SortedMulti(ref smv) => (
@@ -325,10 +355,24 @@ impl<Pk: MiniscriptKey> Wpkh<Pk> {
325355
}
326356
}
327357

358+
/// Computes an upper bound on the difference in weight between a
359+
/// non-satisfied `TxIn` (with empty `scriptSig` and `witness` fields) and a
360+
/// satisfied `TxIn`.
361+
///
362+
/// Assumes all ECDSA signatures are 73 bytes, including push opcode and
363+
/// sighash suffix.
364+
pub fn max_weight_to_satisfy(&self) -> usize {
365+
// stack items: <varint(sig+sigHash)> <sig(71)+sigHash(1)> <varint(pubkey)> <pubkey>
366+
let stack_items_size = 73 + Segwitv0::pk_len(&self.pk);
367+
// stackLen varint difference between non-satisfied (0) and satisfied
368+
let stack_varint_diff = varint_len(2) - varint_len(0);
369+
stack_varint_diff + stack_items_size
370+
}
371+
328372
/// Computes an upper bound on the weight of a satisfying witness to the
329373
/// transaction.
330374
///
331-
/// Assumes all ec-signatures are 73 bytes, including push opcode and
375+
/// Assumes all ECDSA signatures are 73 bytes, including push opcode and
332376
/// sighash suffix. Includes the weight of the VarInts encoding the
333377
/// scriptSig and witness stack length.
334378
pub fn max_satisfaction_weight(&self) -> usize {

src/descriptor/sh.rs

+48-1
Original file line numberDiff line numberDiff line change
@@ -206,15 +206,62 @@ impl<Pk: MiniscriptKey> Sh<Pk> {
206206
}
207207
}
208208

209+
/// Computes an upper bound on the difference in weight between a
210+
/// non-satisfied `TxIn` (with empty `scriptSig` and `witness` fields) and a
211+
/// satisfied `TxIn`.
212+
///
213+
/// Assumes all ECDSA signatures are 73 bytes, including push opcode and
214+
/// sighash suffix.
215+
///
216+
/// # Errors
217+
/// When the descriptor is impossible to safisfy (ex: sh(OP_FALSE)).
218+
pub fn max_weight_to_satisfy(&self) -> Result<usize, Error> {
219+
let (scriptsig_size, witness_size) = match self.inner {
220+
// add weighted script sig, len byte stays the same
221+
ShInner::Wsh(ref wsh) => {
222+
// scriptSig: OP_34 <OP_0 OP_32 <32-byte-hash>>
223+
let scriptsig_size = 1 + 1 + 1 + 32;
224+
let witness_size = wsh.max_weight_to_satisfy()?;
225+
(scriptsig_size, witness_size)
226+
}
227+
ShInner::SortedMulti(ref smv) => {
228+
let ss = smv.script_size();
229+
let ps = push_opcode_size(ss);
230+
let scriptsig_size = ps + ss + smv.max_satisfaction_size();
231+
(scriptsig_size, 0)
232+
}
233+
// add weighted script sig, len byte stays the same
234+
ShInner::Wpkh(ref wpkh) => {
235+
// scriptSig: OP_22 <OP_0 OP_20 <20-byte-hash>>
236+
let scriptsig_size = 1 + 1 + 1 + 20;
237+
let witness_size = wpkh.max_weight_to_satisfy();
238+
(scriptsig_size, witness_size)
239+
}
240+
ShInner::Ms(ref ms) => {
241+
let ss = ms.script_size();
242+
let ps = push_opcode_size(ss);
243+
let scriptsig_size = ps + ss + ms.max_satisfaction_size()?;
244+
(scriptsig_size, 0)
245+
}
246+
};
247+
248+
// scriptSigLen varint difference between non-satisfied (0) and satisfied
249+
let scriptsig_varint_diff = varint_len(scriptsig_size) - varint_len(0);
250+
251+
Ok(4 * (scriptsig_varint_diff + scriptsig_size) + witness_size)
252+
}
253+
209254
/// Computes an upper bound on the weight of a satisfying witness to the
210255
/// transaction.
211256
///
212-
/// Assumes all ec-signatures are 73 bytes, including push opcode and
257+
/// Assumes all ECDSA signatures are 73 bytes, including push opcode and
213258
/// sighash suffix. Includes the weight of the VarInts encoding the
214259
/// scriptSig and witness stack length.
215260
///
216261
/// # Errors
217262
/// When the descriptor is impossible to safisfy (ex: sh(OP_FALSE)).
263+
#[deprecated(note = "use max_weight_to_satisfy instead")]
264+
#[allow(deprecated)]
218265
pub fn max_satisfaction_weight(&self) -> Result<usize, Error> {
219266
Ok(match self.inner {
220267
// add weighted script sig, len byte stays the same

src/descriptor/tr.rs

+52-1
Original file line numberDiff line numberDiff line change
@@ -246,15 +246,66 @@ impl<Pk: MiniscriptKey> Tr<Pk> {
246246
Ok(())
247247
}
248248

249+
/// Computes an upper bound on the difference in weight between a
250+
/// non-satisfied `TxIn` (with empty `scriptSig` and `witness` fields) and a
251+
/// satisfied `TxIn`.
252+
///
253+
/// Assumes all Schnorr signatures are 66 bytes, including push opcode and
254+
/// sighash suffix.
255+
///
256+
/// # Errors
257+
/// When the descriptor is impossible to safisfy (ex: sh(OP_FALSE)).
258+
pub fn max_weight_to_satisfy(&self) -> Result<usize, Error> {
259+
let tree = match self.taptree() {
260+
None => {
261+
// key spend path
262+
// item: varint(sig+sigHash) + <sig(64)+sigHash(1)>
263+
let item_sig_size = 1 + 65;
264+
// 1 stack item
265+
let stack_varint_diff = varint_len(1) - varint_len(0);
266+
267+
return Ok(stack_varint_diff + item_sig_size);
268+
}
269+
// script path spend..
270+
Some(tree) => tree,
271+
};
272+
273+
tree.iter()
274+
.filter_map(|(depth, ms)| {
275+
let script_size = ms.script_size();
276+
let max_sat_elems = ms.max_satisfaction_witness_elements().ok()?;
277+
let max_sat_size = ms.max_satisfaction_size().ok()?;
278+
let control_block_size = control_block_len(depth);
279+
280+
// stack varint difference (+1 for ctrl block, witness script already included)
281+
let stack_varint_diff = varint_len(max_sat_elems + 1) - varint_len(0);
282+
283+
Some(
284+
stack_varint_diff +
285+
// size of elements to satisfy script
286+
max_sat_size +
287+
// second to last element: script
288+
varint_len(script_size) +
289+
script_size +
290+
// last element: control block
291+
varint_len(control_block_size) +
292+
control_block_size,
293+
)
294+
})
295+
.max()
296+
.ok_or(Error::ImpossibleSatisfaction)
297+
}
298+
249299
/// Computes an upper bound on the weight of a satisfying witness to the
250300
/// transaction.
251301
///
252-
/// Assumes all ec-signatures are 73 bytes, including push opcode and
302+
/// Assumes all ECDSA signatures are 73 bytes, including push opcode and
253303
/// sighash suffix. Includes the weight of the VarInts encoding the
254304
/// scriptSig and witness stack length.
255305
///
256306
/// # Errors
257307
/// When the descriptor is impossible to safisfy (ex: sh(OP_FALSE)).
308+
#[deprecated(note = "use max_weight_to_satisfy instead")]
258309
pub fn max_satisfaction_weight(&self) -> Result<usize, Error> {
259310
let tree = match self.taptree() {
260311
// key spend path:

src/lib.rs

+8-1
Original file line numberDiff line numberDiff line change
@@ -74,7 +74,14 @@
7474
//! assert!(desc.sanity_check().is_ok());
7575
//!
7676
//! // Estimate the satisfaction cost
77-
//! assert_eq!(desc.max_satisfaction_weight().unwrap(), 293);
77+
//! // scriptSig:
78+
//! // * OP_PUSH34 <OP_0 OP_32 <32-byte-hash>>
79+
//! // = (1 + 1 + 1 + 32) * 4 = 140 WU
80+
//! // redeemScript: varint <OP_33 <pk1> OP_CHECKSIG OP_IFDUP OP_NOTIF OP_33 <pk2> OP_CHECKSIG OP_ENDIF>
81+
//! // = 1 + (1 + 33 + 1 + 1 + 1 + 1 + 33 + 1 + 1) = 74 WU
82+
//! // stackItem[Sig]: varint <sig+sighash> = 1 + 73 = 74 WU
83+
//! // Expected satisfaction weight: 140 + 74 + 74 = 288
84+
//! assert_eq!(desc.max_weight_to_satisfy().unwrap(), 288);
7885
//! ```
7986
//!
8087

src/miniscript/context.rs

+1
Original file line numberDiff line numberDiff line change
@@ -222,6 +222,7 @@ where
222222

223223
/// Depending on script context, the size of a satifaction witness may slightly differ.
224224
fn max_satisfaction_size<Pk: MiniscriptKey>(ms: &Miniscript<Pk, Self>) -> Option<usize>;
225+
225226
/// Depending on script Context, some of the Terminals might not
226227
/// be valid under the current consensus rules.
227228
/// Or some of the script resource limits may have been exceeded.

0 commit comments

Comments
 (0)