Merge rust-bitcoin/rust-bitcoin#4554: Sane InputWeightPredition Arbitrary Type

8dd24cb67b Add Arbitrary type for InputWeightPrediction (yancy)

Pull request description:

  This type creates sane Arbitrary InputWeightPrediction types that do not panic.  To this end, when a custom type is created by calling new() or from_slice() constructor, only use values that would no greater than block size 4 MB of witness data or 1 MB of non-witness data.  This idea is from the discussion here  https://github.com/rust-bitcoin/rust-bitcoin/issues/4547

  I'd be up for a future PR that limits the constructors to no greater than 1MB or 4MB witness data, although I feel like this Arbitrary type will be less controversial.

  Furthermore, I did test this locally against my application and no panics where produced either through the constructor nor auxiliary methods `total_weight` or `witness_weight`.

ACKs for top commit:
  tcharding:
    ACK 8dd24cb67b
  apoelstra:
    ACK 8dd24cb67b948e619038b387d6c3bd79252a5de1; successfully ran local tests

Tree-SHA512: ff33f1a6ced4f68c4d236e362b1d03da405a8b9701cda0405405421ca21a563fa288c8065a9309e542fec0c8bc850119bcf93a2dc5e27677c42b35b1e5e52722
This commit is contained in:
merge-script 2025-05-26 14:01:51 +00:00
commit 9371018cbf
No known key found for this signature in database
GPG Key ID: C588D63CE41B97C1
1 changed files with 34 additions and 0 deletions

View File

@ -12,6 +12,8 @@
use core::fmt;
#[cfg(feature = "arbitrary")]
use arbitrary::{Arbitrary, Unstructured};
use hashes::sha256d;
use internals::{compact_size, write_err, ToU64};
use io::{BufRead, Write};
@ -1141,6 +1143,38 @@ mod sealed {
impl Sealed for super::Version {}
}
#[cfg(feature = "arbitrary")]
impl<'a> Arbitrary<'a> for InputWeightPrediction {
fn arbitrary(u: &mut Unstructured<'a>) -> arbitrary::Result<Self> {
// limit script size to 4Mwu block size.
let max_block = Weight::MAX_BLOCK.to_wu() as usize;
let input_script_len = u.int_in_range(0..=max_block)?;
let remaining = max_block - input_script_len;
// create witness data if there is remaining space.
let mut witness_length = u.int_in_range(0..=remaining)?;
let mut witness_element_lengths = Vec::new();
// build vec of random witness element lengths.
while witness_length > 0 {
let elem = u.int_in_range(1..=witness_length)?;
witness_element_lengths.push(elem);
witness_length -= elem;
}
match u.int_in_range(0..=7)? {
0 => Ok(InputWeightPrediction::P2WPKH_MAX),
1 => Ok(InputWeightPrediction::NESTED_P2WPKH_MAX),
2 => Ok(InputWeightPrediction::P2PKH_COMPRESSED_MAX),
3 => Ok(InputWeightPrediction::P2PKH_UNCOMPRESSED_MAX),
4 => Ok(InputWeightPrediction::P2TR_KEY_DEFAULT_SIGHASH),
5 => Ok(InputWeightPrediction::P2TR_KEY_NON_DEFAULT_SIGHASH),
6 => Ok(InputWeightPrediction::new(input_script_len, witness_element_lengths)),
_ => Ok(InputWeightPrediction::from_slice(input_script_len, &witness_element_lengths)),
}
}
}
#[cfg(test)]
mod tests {
use hex::FromHex;