Skip to content

Commit ad40e69

Browse files
committed
Merge rust-bitcoin#4626: Saturate iwp constructors to u32 max
3355400 docs: document IWP function return limit and panic case (yancy) 8559a49 Do not bound Arbitrary parameters passed to InputWeightPrediction (yancy) e4c3d1e Use saturating add in IWP constructors (yancy) 8552534 Use u32 for struct and member variables in IWP, saturating to u32::MAX (yancy) Pull request description: Use u32 for struct and member variables in InputWeightPrediction (saturating to u32::MAX). To avoid panics during construction and while using auxiliary methods such as `total_size()`, support saturating operations. closes: rust-bitcoin#4547 ACKs for top commit: apoelstra: ACK 3355400; successfully ran local tests; looks great! tcharding: ACK 3355400 Tree-SHA512: 8e4af86914152b4c159749ba71f1a2a45682b7c16ba7b35a0c4fd4e8c7c162d3999f4280dffd71b231c7e24f0b4a18907465bd99d8ef958fb7bc81f519059f63
2 parents f0ec103 + 3355400 commit ad40e69

File tree

1 file changed

+52
-32
lines changed

1 file changed

+52
-32
lines changed

bitcoin/src/blockdata/transaction.rs

Lines changed: 52 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -926,8 +926,8 @@ pub const fn predict_weight_from_slices(
926926
/// associated constants/methods.
927927
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
928928
pub struct InputWeightPrediction {
929-
script_size: usize,
930-
witness_size: usize,
929+
script_size: u32,
930+
witness_size: u32,
931931
}
932932

933933
impl InputWeightPrediction {
@@ -990,6 +990,23 @@ impl InputWeightPrediction {
990990
/// [`InputWeightPrediction::new`].
991991
pub const P2TR_KEY_NON_DEFAULT_SIGHASH: Self = InputWeightPrediction::from_slice(0, &[65]);
992992

993+
const fn saturate_to_u32(x: usize) -> u32 {
994+
if x > u32::MAX as usize {
995+
u32::MAX
996+
} else {
997+
x as u32 //cast ok, condition prevents larger than u32::MAX.
998+
}
999+
}
1000+
1001+
const fn encoded_size(value: usize) -> u32 {
1002+
match value {
1003+
0..=0xFC => 1,
1004+
0xFD..=0xFFFF => 3,
1005+
0x10000..=0xFFFFFFFF => 5,
1006+
_ => 9,
1007+
}
1008+
}
1009+
9931010
/// Input weight prediction corresponding to spending of P2WPKH output using [signature
9941011
/// grinding].
9951012
///
@@ -1059,16 +1076,17 @@ impl InputWeightPrediction {
10591076
T::Item: Borrow<usize>,
10601077
{
10611078
let (count, total_size) = witness_element_lengths.into_iter().fold(
1062-
(0usize, 0),
1079+
(0usize, 0u32),
10631080
|(count, total_size), elem_len| {
10641081
let elem_len = *elem_len.borrow();
1065-
let elem_size = elem_len + compact_size::encoded_size(elem_len);
1066-
(count + 1, total_size + elem_size)
1082+
let elem_size =
1083+
Self::saturate_to_u32(elem_len).saturating_add(Self::encoded_size(elem_len));
1084+
(count + 1, total_size.saturating_add(elem_size))
10671085
},
10681086
);
1069-
let witness_size =
1070-
if count > 0 { total_size + compact_size::encoded_size(count) } else { 0 };
1071-
let script_size = input_script_len + compact_size::encoded_size(input_script_len);
1087+
let witness_size = if count > 0 { total_size + Self::encoded_size(count) } else { 0 };
1088+
let script_size =
1089+
Self::saturate_to_u32(input_script_len) + Self::encoded_size(input_script_len);
10721090

10731091
InputWeightPrediction { script_size, witness_size }
10741092
}
@@ -1080,33 +1098,40 @@ impl InputWeightPrediction {
10801098
/// `new` and thus is intended to be only used in `const` context.
10811099
pub const fn from_slice(input_script_len: usize, witness_element_lengths: &[usize]) -> Self {
10821100
let mut i = 0;
1083-
let mut total_size = 0;
1101+
let mut total_size: u32 = 0;
10841102
// for loops not supported in const fn
10851103
while i < witness_element_lengths.len() {
10861104
let elem_len = witness_element_lengths[i];
1087-
let elem_size = elem_len + compact_size::encoded_size_const(elem_len as u64);
1088-
total_size += elem_size;
1105+
let elem_size =
1106+
Self::saturate_to_u32(elem_len).saturating_add(Self::encoded_size(elem_len));
1107+
total_size = total_size.saturating_add(elem_size);
10891108
i += 1;
10901109
}
10911110
let witness_size = if !witness_element_lengths.is_empty() {
1092-
total_size + compact_size::encoded_size_const(witness_element_lengths.len() as u64)
1111+
total_size.saturating_add(Self::encoded_size(witness_element_lengths.len()))
10931112
} else {
10941113
0
10951114
};
1096-
let script_size =
1097-
input_script_len + compact_size::encoded_size_const(input_script_len as u64);
1115+
let script_size = Self::saturate_to_u32(input_script_len)
1116+
.saturating_add(Self::encoded_size(input_script_len));
10981117

10991118
InputWeightPrediction { script_size, witness_size }
11001119
}
11011120

11021121
/// Computes the **signature weight** added to a transaction by an input with this weight prediction,
11031122
/// not counting the prevout (txid, index), sequence, potential witness flag bytes or the witness count varint.
1123+
///
1124+
/// This function's internal arithmetic saturates at u32::MAX, so the return value of this
1125+
/// function may be inaccurate for extremely large witness predictions.
11041126
#[deprecated(since = "TBD", note = "use `InputWeightPrediction::witness_weight()` instead")]
11051127
pub const fn weight(&self) -> Weight { Self::witness_weight(self) }
11061128

11071129
/// Computes the signature, prevout (txid, index), and sequence weights of this weight
11081130
/// prediction.
11091131
///
1132+
/// This function's internal arithmetic saturates at u32::MAX, so the return value of this
1133+
/// function may be inaccurate for extremely large witness predictions.
1134+
///
11101135
/// See also [`InputWeightPrediction::witness_weight`]
11111136
pub const fn total_weight(&self) -> Weight {
11121137
// `impl const Trait` is currently unavailable: rust/issues/67792
@@ -1117,6 +1142,9 @@ impl InputWeightPrediction {
11171142

11181143
/// Computes the **signature weight** added to a transaction by an input with this weight prediction,
11191144
/// not counting the prevout (txid, index), sequence, potential witness flag bytes or the witness count varint.
1145+
///
1146+
/// This function's internal arithmetic saturates at u32::MAX, so the return value of this
1147+
/// function may be inaccurate for extremely large witness predictions.
11201148
///
11211149
/// See also [`InputWeightPrediction::total_weight`]
11221150
pub const fn witness_weight(&self) -> Weight {
@@ -1140,31 +1168,23 @@ mod sealed {
11401168
#[cfg(feature = "arbitrary")]
11411169
impl<'a> Arbitrary<'a> for InputWeightPrediction {
11421170
fn arbitrary(u: &mut Unstructured<'a>) -> arbitrary::Result<Self> {
1143-
// limit script size to 4Mwu block size.
1144-
let max_block = Weight::MAX_BLOCK.to_wu() as usize;
1145-
let input_script_len = u.int_in_range(0..=max_block)?;
1146-
let remaining = max_block - input_script_len;
1147-
1148-
// create witness data if there is remaining space.
1149-
let mut witness_length = u.int_in_range(0..=remaining)?;
1150-
let mut witness_element_lengths = Vec::new();
1151-
1152-
// build vec of random witness element lengths.
1153-
while witness_length > 0 {
1154-
let elem = u.int_in_range(1..=witness_length)?;
1155-
witness_element_lengths.push(elem);
1156-
witness_length -= elem;
1157-
}
1158-
11591171
match u.int_in_range(0..=7)? {
11601172
0 => Ok(InputWeightPrediction::P2WPKH_MAX),
11611173
1 => Ok(InputWeightPrediction::NESTED_P2WPKH_MAX),
11621174
2 => Ok(InputWeightPrediction::P2PKH_COMPRESSED_MAX),
11631175
3 => Ok(InputWeightPrediction::P2PKH_UNCOMPRESSED_MAX),
11641176
4 => Ok(InputWeightPrediction::P2TR_KEY_DEFAULT_SIGHASH),
11651177
5 => Ok(InputWeightPrediction::P2TR_KEY_NON_DEFAULT_SIGHASH),
1166-
6 => Ok(InputWeightPrediction::new(input_script_len, witness_element_lengths)),
1167-
_ => Ok(InputWeightPrediction::from_slice(input_script_len, &witness_element_lengths)),
1178+
6 => {
1179+
let input_script_len = usize::arbitrary(u)?;
1180+
let witness_element_lengths: Vec<usize> = Vec::arbitrary(u)?;
1181+
Ok(InputWeightPrediction::new(input_script_len, witness_element_lengths))
1182+
}
1183+
_ => {
1184+
let input_script_len = usize::arbitrary(u)?;
1185+
let witness_element_lengths: Vec<usize> = Vec::arbitrary(u)?;
1186+
Ok(InputWeightPrediction::from_slice(input_script_len, &witness_element_lengths))
1187+
}
11681188
}
11691189
}
11701190
}

0 commit comments

Comments
 (0)