Merge rust-bitcoin/rust-bitcoin#2896: Move `PushBytes::read_scriptint` outside of the `private` module

aebf216619 Use 100 column width for rustdoc (Tobin C. Harding)
c71ae9ac16 Move PushBytes::read_scriptint (Tobin C. Harding)

Pull request description:

  The `push_bytes` module has a `private` module that exists solely to protect the invariant on the `PushBytes` inner byte slice. There is a `PushBytes` impl block outside the private module for functions that can not and do not violate the length invariant.

  Recently we move the `read_scriptint` method to be on the `PushBytes` but we put it inside the `private` module, since the method only reads off of the slice it cannot invalidate the invariant and does not need to be inside the `private` module.

  Move the `read_scriptint` method outside of the `private` module to keep that module as small as possible, helping with its stated aim of being the only place that requires auditing.

  Patch 2 is whitespace only.

ACKs for top commit:
  Kixunil:
    ACK aebf216619
  apoelstra:
    ACK aebf216619

Tree-SHA512: f41eb691e7118a74767a2582a637a176a46e4a8d15259f1c1f3cef67bc57dd8dce4974407c5a5e31acd0b4665f722b20acad4e381747c9d496f18158a0ef9def
This commit is contained in:
Andrew Poelstra 2024-06-23 16:33:30 +00:00
commit ae8b0b1610
No known key found for this signature in database
GPG Key ID: C588D63CE41B97C1
1 changed files with 42 additions and 45 deletions

View File

@ -4,6 +4,8 @@
use core::ops::{Deref, DerefMut};
use crate::script;
#[allow(unused)]
use crate::prelude::*;
@ -21,7 +23,6 @@ mod primitive {
use super::PushBytesError;
#[allow(unused)]
use crate::prelude::*;
use crate::script::{scriptint_parse, Error};
#[cfg(any(target_pointer_width = "16", target_pointer_width = "32"))]
fn check_limit(_: usize) -> Result<(), PushBytesError> { Ok(()) }
@ -74,50 +75,6 @@ mod primitive {
/// Returns the underlying mutbale bytes.
pub fn as_mut_bytes(&mut self) -> &mut [u8] { &mut self.0 }
/// Decodes an integer in script(minimal CScriptNum) format.
///
/// Notice that this fails on overflow: the result is the same as in
/// bitcoind, that only 4-byte signed-magnitude values may be read as
/// numbers. They can be added or subtracted (and a long time ago,
/// multiplied and divided), and this may result in numbers which
/// can't be written out in 4 bytes or less. This is ok! The number
/// just can't be read as a number again.
/// This is a bit crazy and subtle, but it makes sense: you can load
/// 32-bit numbers and do anything with them, which back when mult/div
/// was allowed, could result in up to a 64-bit number. We don't want
/// overflow since that's surprising --- and we don't want numbers that
/// don't fit in 64 bits (for efficiency on modern processors) so we
/// simply say, anything in excess of 32 bits is no longer a number.
/// This is basically a ranged type implementation.
///
/// This code is based on the `CScriptNum` constructor in Bitcoin Core (see `script.h`).
pub fn read_scriptint(&self) -> Result<i64, Error> {
let last = match self.as_bytes().last() {
Some(last) => last,
None => return Ok(0),
};
if self.len() > 4 {
return Err(Error::NumericOverflow);
}
// Comment and code copied from Bitcoin Core:
// https://github.com/bitcoin/bitcoin/blob/447f50e4aed9a8b1d80e1891cda85801aeb80b4e/src/script/script.h#L247-L262
// If the most-significant-byte - excluding the sign bit - is zero
// then we're not minimal. Note how this test also rejects the
// negative-zero encoding, 0x80.
if (*last & 0x7f) == 0 {
// One exception: if there's more than one byte and the most
// significant bit of the second-most-significant-byte is set
// it would conflict with the sign bit. An example of this case
// is +-255, which encode to 0xff00 and 0xff80 respectively.
// (big-endian).
if self.len() <= 1 || (self[self.len() - 2] & 0x80) == 0 {
return Err(Error::NonMinimalPush);
}
}
Ok(scriptint_parse(self.as_bytes()))
}
}
macro_rules! delegate_index {
@ -335,6 +292,46 @@ impl PushBytes {
/// Returns true if the buffer contains zero bytes.
pub fn is_empty(&self) -> bool { self.as_bytes().is_empty() }
/// Decodes an integer in script(minimal CScriptNum) format.
///
/// Notice that this fails on overflow: the result is the same as in bitcoind, that only 4-byte
/// signed-magnitude values may be read as numbers. They can be added or subtracted (and a long
/// time ago, multiplied and divided), and this may result in numbers which can't be written out
/// in 4 bytes or less. This is ok! The number just can't be read as a number again. This is a
/// bit crazy and subtle, but it makes sense: you can load 32-bit numbers and do anything with
/// them, which back when mult/div was allowed, could result in up to a 64-bit number. We don't
/// want overflow since that's surprising --- and we don't want numbers that don't fit in 64
/// bits (for efficiency on modern processors) so we simply say, anything in excess of 32 bits
/// is no longer a number. This is basically a ranged type implementation.
///
/// This code is based on the `CScriptNum` constructor in Bitcoin Core (see `script.h`).
pub fn read_scriptint(&self) -> Result<i64, script::Error> {
let last = match self.as_bytes().last() {
Some(last) => last,
None => return Ok(0),
};
if self.len() > 4 {
return Err(script::Error::NumericOverflow);
}
// Comment and code copied from Bitcoin Core:
// https://github.com/bitcoin/bitcoin/blob/447f50e4aed9a8b1d80e1891cda85801aeb80b4e/src/script/script.h#L247-L262
// If the most-significant-byte - excluding the sign bit - is zero
// then we're not minimal. Note how this test also rejects the
// negative-zero encoding, 0x80.
if (*last & 0x7f) == 0 {
// One exception: if there's more than one byte and the most
// significant bit of the second-most-significant-byte is set
// it would conflict with the sign bit. An example of this case
// is +-255, which encode to 0xff00 and 0xff80 respectively.
// (big-endian).
if self.len() <= 1 || (self[self.len() - 2] & 0x80) == 0 {
return Err(script::Error::NonMinimalPush);
}
}
Ok(script::scriptint_parse(self.as_bytes()))
}
}
impl PushBytesBuf {