Add a newtype for script-pushable slice

The code previously contained undocumented panic when trying to push
slice. This change solves it by adding a newtype that guarantees limited
length.
This commit is contained in:
Martin Habovstiak 2023-02-18 12:43:13 +01:00
parent 8fedbcbf13
commit bcd95fa036
15 changed files with 604 additions and 89 deletions

View File

@ -22,7 +22,7 @@ fn do_test(data: &[u8]) {
// reserialized as numbers. (For -1 through 16, this will use special ops; for
// others it'll just reserialize them as pushes.)
if bytes.len() == 1 && bytes[0] != 0x80 && bytes[0] != 0x00 {
if let Ok(num) = script::read_scriptint(bytes) {
if let Ok(num) = script::read_scriptint(bytes.as_bytes()) {
b = b.push_int(num);
} else {
b = b.push_slice(bytes);

View File

@ -27,7 +27,7 @@
//! bitcoin = { version = "...", features = ["rand-std"] }
//! ```
use core::convert::TryFrom;
use core::convert::{TryFrom, TryInto};
use core::fmt;
use core::marker::PhantomData;
use core::str::FromStr;
@ -43,7 +43,7 @@ use crate::blockdata::constants::{
};
use crate::blockdata::opcodes;
use crate::blockdata::opcodes::all::*;
use crate::blockdata::script::{self, Instruction, Script, ScriptBuf};
use crate::blockdata::script::{self, Instruction, Script, ScriptBuf, PushBytes, PushBytesBuf, PushBytesErrorReport};
use crate::crypto::key::PublicKey;
use crate::crypto::schnorr::{TapTweak, TweakedPublicKey, UntweakedPublicKey};
use crate::error::ParseIntError;
@ -402,12 +402,14 @@ pub struct WitnessProgram {
/// The witness program version.
version: WitnessVersion,
/// The witness program. (Between 2 and 40 bytes)
program: Vec<u8>,
program: PushBytesBuf,
}
impl WitnessProgram {
/// Creates a new witness program.
pub fn new(version: WitnessVersion, program: Vec<u8>) -> Result<Self, Error> {
pub fn new<P>(version: WitnessVersion, program: P) -> Result<Self, Error> where P: TryInto<PushBytesBuf>, <P as TryInto<PushBytesBuf>>::Error: PushBytesErrorReport {
let program = program.try_into()
.map_err(|error| Error::InvalidWitnessProgramLength(error.input_len()))?;
if program.len() < 2 || program.len() > 40 {
return Err(Error::InvalidWitnessProgramLength(program.len()));
}
@ -425,7 +427,7 @@ impl WitnessProgram {
}
/// Returns the witness program.
pub fn program(&self) -> &[u8] {
pub fn program(&self) -> &PushBytes {
&self.program
}
}
@ -444,9 +446,13 @@ impl Payload {
} else if script.is_witness_program() {
let opcode = script.first_opcode().expect("witness_version guarantees len() > 4");
let witness_program = script
.as_bytes()[2..]
.to_vec();
let witness_program = WitnessProgram::new(
WitnessVersion::try_from(opcode)?,
script.as_bytes()[2..].to_vec(),
witness_program,
)?;
Payload::WitnessProgram(witness_program)
} else {
@ -481,7 +487,7 @@ impl Payload {
pub fn p2wpkh(pk: &PublicKey) -> Result<Payload, Error> {
let prog = WitnessProgram::new(
WitnessVersion::V0,
pk.wpubkey_hash().ok_or(Error::UncompressedPubkey)?.into_inner().to_vec()
pk.wpubkey_hash().ok_or(Error::UncompressedPubkey)?
)?;
Ok(Payload::WitnessProgram(prog))
}
@ -490,7 +496,7 @@ impl Payload {
pub fn p2shwpkh(pk: &PublicKey) -> Result<Payload, Error> {
let builder = script::Builder::new()
.push_int(0)
.push_slice(pk.wpubkey_hash().ok_or(Error::UncompressedPubkey)?.as_ref());
.push_slice(pk.wpubkey_hash().ok_or(Error::UncompressedPubkey)?);
Ok(Payload::ScriptHash(builder.into_script().script_hash()))
}
@ -499,7 +505,7 @@ impl Payload {
pub fn p2wsh(script: &Script) -> Payload {
let prog = WitnessProgram::new(
WitnessVersion::V0,
script.wscript_hash().as_inner().to_vec()
script.wscript_hash()
).expect("wscript_hash has len 32 compatible with segwitv0");
Payload::WitnessProgram(prog)
}
@ -507,7 +513,7 @@ impl Payload {
/// Create a pay to script payload that embeds a witness pay to script hash address
pub fn p2shwsh(script: &Script) -> Payload {
let ws =
script::Builder::new().push_int(0).push_slice(script.wscript_hash().as_ref()).into_script();
script::Builder::new().push_int(0).push_slice(script.wscript_hash()).into_script();
Payload::ScriptHash(ws.script_hash())
}
@ -521,7 +527,7 @@ impl Payload {
let (output_key, _parity) = internal_key.tap_tweak(secp, merkle_root);
let prog = WitnessProgram::new(
WitnessVersion::V1,
output_key.to_inner().serialize().to_vec()
output_key.to_inner().serialize()
).expect("taproot output key has len 32 <= 40");
Payload::WitnessProgram(prog)
}
@ -532,7 +538,7 @@ impl Payload {
pub fn p2tr_tweaked(output_key: TweakedPublicKey) -> Payload {
let prog = WitnessProgram::new(
WitnessVersion::V1,
output_key.to_inner().serialize().to_vec()
output_key.to_inner().serialize()
).expect("taproot output key has len 32 <= 40");
Payload::WitnessProgram(prog)
}
@ -543,7 +549,7 @@ impl Payload {
match self {
Payload::ScriptHash(hash) => hash.as_ref(),
Payload::PubkeyHash(hash) => hash.as_ref(),
Payload::WitnessProgram(prog) => prog.program(),
Payload::WitnessProgram(prog) => prog.program().as_bytes(),
}
}
}
@ -588,7 +594,7 @@ impl<'a> fmt::Display for AddressEncoding<'a> {
let mut bech32_writer =
bech32::Bech32Writer::new(self.bech32_hrp, version.bech32_variant(), writer)?;
bech32::WriteBase32::write_u5(&mut bech32_writer, version.into())?;
bech32::ToBase32::write_base32(&prog, &mut bech32_writer)
bech32::ToBase32::write_base32(&prog.as_bytes(), &mut bech32_writer)
}
}
}

View File

@ -334,7 +334,7 @@ impl Block {
match push.map_err(|_| Bip34Error::NotPresent)? {
script::Instruction::PushBytes(b) => {
// Check that the number is encoded in the minimal way.
let h = script::read_scriptint(b).map_err(|_e| Bip34Error::UnexpectedPush(b.to_vec()))?;
let h = script::read_scriptint(b.as_bytes()).map_err(|_e| Bip34Error::UnexpectedPush(b.as_bytes().to_vec()))?;
if h < 0 {
Err(Bip34Error::NegativeHeight)
} else {

View File

@ -85,7 +85,7 @@ fn bitcoin_genesis_tx() -> Transaction {
// Outputs
let script_bytes = hex!("04678afdb0fe5548271967f1a67130b7105cd6a828e03909a67962e0ea1f61deb649f6bc3f4cef38c4f35504e51ec112de5c384df7ba0b8d578a4c702b6bf11d5f");
let out_script = script::Builder::new()
.push_slice(&script_bytes)
.push_slice(script_bytes)
.push_opcode(OP_CHECKSIG)
.into_script();
ret.output.push(TxOut {

View File

@ -1,7 +1,7 @@
// Written in 2014 by Andrew Poelstra <apoelstra@wpsoftware.net>
// SPDX-License-Identifier: CC0-1.0
use core::convert::TryFrom;
use core::convert::{TryFrom, TryInto};
use core::fmt;
#[cfg(rust_v_1_53)]
use core::ops::Bound;
@ -277,6 +277,14 @@ impl Script {
&& self.0[1] == OP_PUSHBYTES_20.to_u8()
}
pub(crate) fn v0_p2wpkh(&self) -> Option<&[u8; 20]> {
if self.is_v0_p2wpkh() {
Some(self.0[2..].try_into().expect("is_v0_p2wpkh checks the length"))
} else {
None
}
}
/// Checks whether a script pubkey is a P2TR output.
#[inline]
pub fn is_v1_p2tr(&self) -> bool {

View File

@ -9,7 +9,7 @@ use secp256k1::XOnlyPublicKey;
use crate::blockdata::locktime::absolute;
use crate::blockdata::opcodes::{self, all::*};
use crate::blockdata::script::{write_scriptint, opcode_to_verify, Script, ScriptBuf};
use crate::blockdata::script::{write_scriptint, opcode_to_verify, Script, ScriptBuf, PushBytes};
use crate::blockdata::transaction::Sequence;
use crate::key::PublicKey;
use crate::prelude::*;
@ -56,11 +56,11 @@ impl Builder {
pub(in crate::blockdata) fn push_int_non_minimal(self, data: i64) -> Builder {
let mut buf = [0u8; 8];
let len = write_scriptint(&mut buf, data);
self.push_slice(&buf[..len])
self.push_slice(&<&PushBytes>::from(&buf)[..len])
}
/// Adds instructions to push some arbitrary data onto the stack.
pub fn push_slice(mut self, data: &[u8]) -> Builder {
pub fn push_slice<T: AsRef<PushBytes>>(mut self, data: T) -> Builder {
self.0.push_slice(data);
self.1 = None;
self
@ -69,15 +69,15 @@ impl Builder {
/// Adds instructions to push a public key onto the stack.
pub fn push_key(self, key: &PublicKey) -> Builder {
if key.compressed {
self.push_slice(&key.inner.serialize()[..])
self.push_slice(key.inner.serialize())
} else {
self.push_slice(&key.inner.serialize_uncompressed()[..])
self.push_slice(key.inner.serialize_uncompressed())
}
}
/// Adds instructions to push an XOnly public key onto the stack.
pub fn push_x_only_key(self, x_only_key: &XOnlyPublicKey) -> Builder {
self.push_slice(&x_only_key.serialize())
self.push_slice(x_only_key.serialize())
}
/// Adds a single opcode to the script.

View File

@ -1,14 +1,15 @@
// Written in 2014 by Andrew Poelstra <apoelstra@wpsoftware.net>
// SPDX-License-Identifier: CC0-1.0
use core::convert::TryInto;
use crate::blockdata::opcodes;
use crate::blockdata::script::{read_uint_iter, Error, Script, ScriptBuf, UintError};
use crate::blockdata::script::{read_uint_iter, Error, Script, ScriptBuf, UintError, PushBytes};
/// A "parsed opcode" which allows iterating over a [`Script`] in a more sensible way.
#[derive(Debug, PartialEq, Eq, Copy, Clone)]
pub enum Instruction<'a> {
/// Push a bunch of data.
PushBytes(&'a [u8]),
PushBytes(&'a PushBytes),
/// Some non-push opcode.
Op(opcodes::All),
}
@ -22,8 +23,8 @@ impl<'a> Instruction<'a> {
}
}
/// Returns the opcode if the instruction is not a data push.
pub fn push_bytes(&self) -> Option<&[u8]> {
/// Returns the pushed bytes if the instruction is a data push.
pub fn push_bytes(&self) -> Option<&PushBytes> {
match self {
Instruction::Op(_) => None,
Instruction::PushBytes(bytes) => Some(bytes),
@ -64,14 +65,15 @@ impl<'a> Instructions<'a> {
///
/// If the iterator is not long enough [`Error::EarlyEndOfScript`] is returned and the iterator
/// is killed to avoid returning an infinite stream of errors.
pub(super) fn take_slice_or_kill(&mut self, len: usize) -> Result<&'a [u8], Error> {
pub(super) fn take_slice_or_kill(&mut self, len: u32) -> Result<&'a PushBytes, Error> {
let len = len as usize;
if self.data.len() >= len {
let slice = &self.data.as_slice()[..len];
if len > 0 {
self.data.nth(len - 1);
}
Ok(slice)
Ok(slice.try_into().expect("len was created from u32, so can't happen"))
} else {
self.kill();
Err(Error::EarlyEndOfScript)
@ -94,7 +96,12 @@ impl<'a> Instructions<'a> {
self.kill();
return Some(Err(Error::NonMinimalPush));
}
Some(self.take_slice_or_kill(n).map(Instruction::PushBytes))
let result = n
.try_into()
.map_err(|_| Error::NumericOverflow)
.and_then(|n| self.take_slice_or_kill(n))
.map(Instruction::PushBytes);
Some(result)
}
}
@ -119,8 +126,6 @@ impl<'a> Iterator for Instructions<'a> {
opcodes::Class::PushBytes(n) => {
// make sure safety argument holds across refactorings
let n: u32 = n;
// casting is safe because we don't support 16-bit architectures
let n = n as usize;
let op_byte = self.data.as_slice().first();
match (self.enforce_minimal, op_byte, n) {
@ -131,7 +136,7 @@ impl<'a> Iterator for Instructions<'a> {
(_, None, 0) => {
// the iterator is already empty, may as well use this information to avoid
// whole take_slice_or_kill function
Some(Ok(Instruction::PushBytes(&[])))
Some(Ok(Instruction::PushBytes(PushBytes::empty())))
},
_ => {
Some(self.take_slice_or_kill(n).map(Instruction::PushBytes))

View File

@ -71,11 +71,13 @@ mod instruction;
mod owned;
#[cfg(test)]
mod tests;
mod push_bytes;
pub use self::borrowed::*;
pub use self::builder::*;
pub use self::instruction::*;
pub use self::owned::*;
pub use self::push_bytes::*;
/// Encodes an integer in script(minimal CScriptNum) format.
///

View File

@ -8,7 +8,7 @@ use secp256k1::{Secp256k1, Verification};
use crate::address::{WitnessVersion, WitnessProgram};
use crate::blockdata::opcodes::{self, all::*};
use crate::blockdata::script::{opcode_to_verify, Builder, Instruction, Script};
use crate::blockdata::script::{opcode_to_verify, Builder, Instruction, Script, PushBytes};
use crate::hashes::hex;
use crate::hash_types::{PubkeyHash, WPubkeyHash, ScriptHash, WScriptHash};
use crate::key::PublicKey;
@ -98,7 +98,7 @@ impl ScriptBuf {
Builder::new()
.push_opcode(OP_DUP)
.push_opcode(OP_HASH160)
.push_slice(&pubkey_hash[..])
.push_slice(pubkey_hash)
.push_opcode(OP_EQUALVERIFY)
.push_opcode(OP_CHECKSIG)
.into_script()
@ -108,7 +108,7 @@ impl ScriptBuf {
pub fn new_p2sh(script_hash: &ScriptHash) -> Self {
Builder::new()
.push_opcode(OP_HASH160)
.push_slice(&script_hash[..])
.push_slice(script_hash)
.push_opcode(OP_EQUAL)
.into_script()
}
@ -116,13 +116,13 @@ impl ScriptBuf {
/// Generates P2WPKH-type of scriptPubkey.
pub fn new_v0_p2wpkh(pubkey_hash: &WPubkeyHash) -> Self {
// pubkey hash is 20 bytes long, so it's safe to use `new_witness_program_unchecked` (Segwitv0)
ScriptBuf::new_witness_program_unchecked(WitnessVersion::V0, &pubkey_hash[..])
ScriptBuf::new_witness_program_unchecked(WitnessVersion::V0, pubkey_hash)
}
/// Generates P2WSH-type of scriptPubkey with a given hash of the redeem script.
pub fn new_v0_p2wsh(script_hash: &WScriptHash) -> Self {
// script hash is 32 bytes long, so it's safe to use `new_witness_program_unchecked` (Segwitv0)
ScriptBuf::new_witness_program_unchecked(WitnessVersion::V0, &script_hash[..])
ScriptBuf::new_witness_program_unchecked(WitnessVersion::V0, script_hash)
}
/// Generates P2TR for script spending path using an internal public key and some optional
@ -130,13 +130,13 @@ impl ScriptBuf {
pub fn new_v1_p2tr<C: Verification>(secp: &Secp256k1<C>, internal_key: UntweakedPublicKey, merkle_root: Option<TapNodeHash>) -> Self {
let (output_key, _) = internal_key.tap_tweak(secp, merkle_root);
// output key is 32 bytes long, so it's safe to use `new_witness_program_unchecked` (Segwitv1)
ScriptBuf::new_witness_program_unchecked(WitnessVersion::V1, &output_key.serialize())
ScriptBuf::new_witness_program_unchecked(WitnessVersion::V1, output_key.serialize())
}
/// Generates P2TR for key spending path for a known [`TweakedPublicKey`].
pub fn new_v1_p2tr_tweaked(output_key: TweakedPublicKey) -> Self {
// output key is 32 bytes long, so it's safe to use `new_witness_program_unchecked` (Segwitv1)
ScriptBuf::new_witness_program_unchecked(WitnessVersion::V1, &output_key.serialize())
ScriptBuf::new_witness_program_unchecked(WitnessVersion::V1, output_key.serialize())
}
/// Generates P2WSH-type of scriptPubkey with a given [`WitnessProgram`].
@ -152,7 +152,8 @@ impl ScriptBuf {
///
/// Convenience method used by `new_v0_p2wpkh`, `new_v0_p2wsh`, `new_v1_p2tr`, and
/// `new_v1_p2tr_tweaked`.
fn new_witness_program_unchecked(version: WitnessVersion, program: &[u8]) -> Self {
fn new_witness_program_unchecked<T: AsRef<PushBytes>>(version: WitnessVersion, program: T) -> Self {
let program = program.as_ref();
debug_assert!(program.len() >= 2 && program.len() <= 40);
// In segwit v0, the program must be 20 or 32 bytes long.
debug_assert!(version != WitnessVersion::V0 || program.len() == 20 || program.len() == 32);
@ -163,7 +164,7 @@ impl ScriptBuf {
}
/// Generates OP_RETURN-type of scriptPubkey for the given data.
pub fn new_op_return(data: &[u8]) -> Self {
pub fn new_op_return<T: AsRef<PushBytes>>(data: &T) -> Self {
Builder::new()
.push_opcode(OP_RETURN)
.push_slice(data)
@ -200,18 +201,16 @@ impl ScriptBuf {
///
/// [BIP143]: <https://github.com/bitcoin/bips/blob/99701f68a88ce33b2d0838eb84e115cef505b4c2/bip-0143.mediawiki>
pub fn p2wpkh_script_code(&self) -> Option<ScriptBuf> {
if !self.is_v0_p2wpkh() {
return None
}
let script = Builder::new()
.push_opcode(OP_DUP)
.push_opcode(OP_HASH160)
.push_slice(&self.as_bytes()[2..]) // The `self` script is 0x00, 0x14, <pubkey_hash>
.push_opcode(OP_EQUALVERIFY)
.push_opcode(OP_CHECKSIG)
.into_script();
Some(script)
self.v0_p2wpkh().map(|wpkh| {
Builder::new()
.push_opcode(OP_DUP)
.push_opcode(OP_HASH160)
// The `self` script is 0x00, 0x14, <pubkey_hash>
.push_slice(wpkh)
.push_opcode(OP_EQUALVERIFY)
.push_opcode(OP_CHECKSIG)
.into_script()
})
}
/// Adds a single opcode to the script.
@ -220,17 +219,14 @@ impl ScriptBuf {
}
/// Adds instructions to push some arbitrary data onto the stack.
///
/// ## Panics
///
/// The method panics if `data` length is greater or equal to 0x100000000.
pub fn push_slice(&mut self, data: &[u8]) {
pub fn push_slice<T: AsRef<PushBytes>>(&mut self, data: T) {
let data = data.as_ref();
self.reserve(Self::reserved_len_for_slice(data.len()));
self.push_slice_no_opt(data);
}
/// Pushes the slice without reserving
fn push_slice_no_opt(&mut self, data: &[u8]) {
fn push_slice_no_opt(&mut self, data: &PushBytes) {
// Start with a PUSH opcode
match data.len() as u64 {
n if n < opcodes::Ordinary::OP_PUSHDATA1 as u64 => { self.0.push(n as u8); },
@ -253,7 +249,7 @@ impl ScriptBuf {
_ => panic!("tried to put a 4bn+ sized object into a script!")
}
// Then push the raw bytes
self.0.extend_from_slice(data);
self.0.extend_from_slice(data.as_bytes());
}
/// Computes the sum of `len` and the lenght of an appropriate push opcode.

View File

@ -0,0 +1,463 @@
//! Contains `PushBytes` & co
use core::ops::{Deref, DerefMut};
use core::borrow::{Borrow, BorrowMut};
#[allow(unused)]
use crate::prelude::*;
pub use primitive::*;
/// This module only contains required operations so that outside functions wouldn't accidentally
/// break invariants. Therefore auditing this module should be sufficient.
mod primitive {
#[allow(unused)]
use crate::prelude::*;
use super::PushBytesError;
use core::convert::{TryFrom, TryInto};
use core::ops::{Index, Range, RangeFull, RangeFrom, RangeTo, RangeInclusive, RangeToInclusive};
#[cfg(feature = "rust_v_1_53")]
use core::ops::Bound;
#[cfg(any(target_pointer_width = "16", target_pointer_width = "32"))]
fn check_limit(len: usize) -> Result<(), PushBytesError> {
Ok(())
}
#[cfg(not(any(target_pointer_width = "16", target_pointer_width = "32")))]
fn check_limit(len: usize) -> Result<(), PushBytesError> {
if len < 0x100000000 {
Ok(())
} else {
Err(PushBytesError { len })
}
}
/// Byte slices that can be in Bitcoin script.
///
/// The encoding of Bitcoin script restricts data pushes to be less than 2^32 bytes long.
/// This type represents slices that are guaranteed to be within the limit so they can be put in
/// the script safely.
#[derive(Debug, Eq, PartialEq, Ord, PartialOrd, Hash)]
#[repr(transparent)]
pub struct PushBytes([u8]);
impl PushBytes {
/// Creates `&Self` without checking the length.
///
/// ## Safety
///
/// The caller is responsible for checking that the length is less than the [`LIMIT`].
unsafe fn from_slice_unchecked(bytes: &[u8]) -> &Self {
&*(bytes as *const [u8] as *const PushBytes)
}
/// Creates `&mut Self` without checking the length.
///
/// ## Safety
///
/// The caller is responsible for checking that the length is less than the [`LIMIT`].
unsafe fn from_mut_slice_unchecked(bytes: &mut [u8]) -> &mut Self {
&mut *(bytes as *mut [u8] as *mut PushBytes)
}
/// Creates an empty `PushBytes`.
pub fn empty() -> &'static Self {
// 0 < LIMIT
unsafe { Self::from_slice_unchecked(&[]) }
}
/// Returns the underlying bytes.
pub fn as_bytes(&self) -> &[u8] {
&self.0
}
/// Returns the underlying mutbale bytes.
pub fn as_mut_bytes(&mut self) -> &mut [u8] {
&mut self.0
}
}
macro_rules! delegate_index {
($($type:ty),* $(,)?) => {
$(
/// Script subslicing operation - read [slicing safety](#slicing-safety)!
impl Index<$type> for PushBytes {
type Output = Self;
#[inline]
#[cfg_attr(rust_v_1_46, track_caller)]
fn index(&self, index: $type) -> &Self::Output {
// Slicing can not make slices longer
unsafe {
Self::from_slice_unchecked(&self.0[index])
}
}
}
)*
}
}
delegate_index!(Range<usize>, RangeFrom<usize>, RangeTo<usize>, RangeFull, RangeInclusive<usize>, RangeToInclusive<usize>);
#[cfg(feature = "rust_v_1_53")]
#[cfg_attr(docsrs, doc(cfg(feature = "rust_v_1_53")))]
delegate_index!((Bound<usize>, Bound<usize>));
impl Index<usize> for PushBytes {
type Output = u8;
#[inline]
#[cfg_attr(rust_v_1_46, track_caller)]
fn index(&self, index: usize) -> &Self::Output {
&self.0[index]
}
}
impl<'a> TryFrom<&'a [u8]> for &'a PushBytes {
type Error = PushBytesError;
fn try_from(bytes: &'a [u8]) -> Result<Self, Self::Error> {
check_limit(bytes.len())?;
// We've just checked the length
Ok(unsafe { PushBytes::from_slice_unchecked(bytes) })
}
}
impl<'a> TryFrom<&'a mut [u8]> for &'a mut PushBytes {
type Error = PushBytesError;
fn try_from(bytes: &'a mut [u8]) -> Result<Self, Self::Error> {
check_limit(bytes.len())?;
// We've just checked the length
Ok(unsafe { PushBytes::from_mut_slice_unchecked(bytes) })
}
}
macro_rules! from_array {
($($len:literal),* $(,)?) => {
$(
impl<'a> From<&'a [u8; $len]> for &'a PushBytes {
fn from(bytes: &'a [u8; $len]) -> Self {
// Check that the macro wasn't called with a wrong number.
const _: () = [(); 1][($len >= 0x100000000u64) as usize];
// We know the size of array statically and we checked macro input.
unsafe { PushBytes::from_slice_unchecked(bytes) }
}
}
impl<'a> From<&'a mut [u8; $len]> for &'a mut PushBytes {
fn from(bytes: &'a mut [u8; $len]) -> Self {
// Macro check already above, no need to duplicate.
// We know the size of array statically and we checked macro input.
unsafe { PushBytes::from_mut_slice_unchecked(bytes) }
}
}
impl AsRef<PushBytes> for [u8; $len] {
fn as_ref(&self) -> &PushBytes {
self.into()
}
}
impl AsMut<PushBytes> for [u8; $len] {
fn as_mut(&mut self) -> &mut PushBytes {
self.into()
}
}
impl From<[u8; $len]> for PushBytesBuf {
fn from(bytes: [u8; $len]) -> Self {
PushBytesBuf(Vec::from(&bytes as &[_]))
}
}
impl<'a> From<&'a [u8; $len]> for PushBytesBuf {
fn from(bytes: &'a [u8; $len]) -> Self {
PushBytesBuf(Vec::from(bytes as &[_]))
}
}
)*
}
}
// Sizes up to 73 to support all pubkey and signature sizes
from_array! {
0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24,
25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47,
48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70,
71, 72, 73,
}
/// Owned, growable counterpart to `PushBytes`.
#[derive(Default, Debug, Clone, Eq, PartialEq, Ord, PartialOrd, Hash)]
pub struct PushBytesBuf(Vec<u8>);
impl PushBytesBuf {
/// Creates a new empty `PushBytesBuf`.
pub fn new() -> Self {
PushBytesBuf(Vec::new())
}
/// Creates a new empty `PushBytesBuf` with reserved capacity.
pub fn with_capacity(capacity: usize) -> Self {
PushBytesBuf(Vec::with_capacity(capacity))
}
/// Reserve capacity for `additional_capacity` bytes.
pub fn reserve(&mut self, additional_capacity: usize) {
self.0.reserve(additional_capacity)
}
/// Try pushing a single byte.
///
/// ## Errors
///
/// This method fails if `self` would exceed the limit.
#[allow(deprecated)]
pub fn push(&mut self, byte: u8) -> Result<(), PushBytesError> {
// This is OK on 32 bit archs since vec has its own check and this check is pointless.
check_limit(self.0.len().saturating_add(1))?;
self.0.push(byte);
Ok(())
}
/// Try appending a slice to `PushBytesBuf`
///
/// ## Errors
///
/// This method fails if `self` would exceed the limit.
pub fn extend_from_slice(&mut self, bytes: &[u8]) -> Result<(), PushBytesError> {
let len = self.0.len().saturating_add(bytes.len());
check_limit(len)?;
self.0.extend_from_slice(bytes);
Ok(())
}
/// Remove the last byte from buffer if any.
pub fn pop(&mut self) -> Option<u8> {
self.0.pop()
}
/// Remove the byte at `index` and return it.
///
/// ## Panics
///
/// This method panics if `index` is out of bounds.
#[cfg_attr(rust_v_1_46, track_caller)]
pub fn remove(&mut self, index: usize) -> u8 {
self.0.remove(index)
}
/// Remove all bytes from buffer without affecting capacity.
pub fn clear(&mut self) {
self.0.clear()
}
/// Remove bytes from buffer past `len`.
pub fn truncate(&mut self, len: usize) {
self.0.truncate(len)
}
/// Extracts `PushBytes` slice
pub fn as_push_bytes(&self) -> &PushBytes {
// length guaranteed by our invariant
unsafe { PushBytes::from_slice_unchecked(&self.0) }
}
/// Extracts mutable `PushBytes` slice
pub fn as_mut_push_bytes(&mut self) -> &mut PushBytes {
// length guaranteed by our invariant
unsafe { PushBytes::from_mut_slice_unchecked(&mut self.0) }
}
/// Accesses inner `Vec` - provided for `super` to impl other methods.
pub(super) fn inner(&self) -> &Vec<u8> {
&self.0
}
}
impl From<PushBytesBuf> for Vec<u8> {
fn from(value: PushBytesBuf) -> Self {
value.0
}
}
impl TryFrom<Vec<u8>> for PushBytesBuf {
type Error = PushBytesError;
fn try_from(vec: Vec<u8>) -> Result<Self, Self::Error> {
// check len
let _: &PushBytes = vec.as_slice().try_into()?;
Ok(PushBytesBuf(vec))
}
}
impl ToOwned for PushBytes {
type Owned = PushBytesBuf;
fn to_owned(&self) -> Self::Owned {
PushBytesBuf(self.0.to_owned())
}
}
}
impl PushBytes {
/// Returns the number of bytes in buffer.
pub fn len(&self) -> usize {
self.as_bytes().len()
}
/// Returns true if the buffer contains zero bytes.
pub fn is_empty(&self) -> bool {
self.as_bytes().is_empty()
}
}
impl PushBytesBuf {
/// Returns the number of bytes in buffer.
pub fn len(&self) -> usize {
self.inner().len()
}
/// Returns the number of bytes the buffer can contain without reallocating.
pub fn capacity(&self) -> usize {
self.inner().capacity()
}
/// Returns true if the buffer contains zero bytes.
pub fn is_empty(&self) -> bool {
self.inner().is_empty()
}
}
impl AsRef<[u8]> for PushBytes {
fn as_ref(&self) -> &[u8] {
self.as_bytes()
}
}
impl AsMut<[u8]> for PushBytes {
fn as_mut(&mut self) -> &mut [u8] {
self.as_mut_bytes()
}
}
impl Deref for PushBytesBuf {
type Target = PushBytes;
fn deref(&self) -> &Self::Target {
self.as_push_bytes()
}
}
impl DerefMut for PushBytesBuf {
fn deref_mut(&mut self) -> &mut Self::Target {
self.as_mut_push_bytes()
}
}
impl AsRef<PushBytes> for PushBytes {
fn as_ref(&self) -> &PushBytes {
self
}
}
impl AsMut<PushBytes> for PushBytes {
fn as_mut(&mut self) -> &mut PushBytes {
self
}
}
impl AsRef<PushBytes> for PushBytesBuf {
fn as_ref(&self) -> &PushBytes {
self.as_push_bytes()
}
}
impl AsMut<PushBytes> for PushBytesBuf {
fn as_mut(&mut self) -> &mut PushBytes {
self.as_mut_push_bytes()
}
}
impl Borrow<PushBytes> for PushBytesBuf {
fn borrow(&self) -> &PushBytes {
self.as_push_bytes()
}
}
impl BorrowMut<PushBytes> for PushBytesBuf {
fn borrow_mut(&mut self) -> &mut PushBytes {
self.as_mut_push_bytes()
}
}
/// Reports information about failed conversion into `PushBytes`.
///
/// This should not be needed by general public, except as an additional bound on `TryFrom` when
/// converting to `WitnessProgram`.
pub trait PushBytesErrorReport {
/// How many bytes the input had.
fn input_len(&self) -> usize;
}
impl PushBytesErrorReport for core::convert::Infallible {
#[inline]
fn input_len(&self) -> usize {
match *self {}
}
}
pub use error::*;
#[cfg(any(target_pointer_width = "16", target_pointer_width = "32"))]
mod error {
use core::fmt;
/// Error returned on attempt to create too large `PushBytes`.
#[allow(unused)]
#[derive(Debug, Clone)]
pub struct PushBytesError {
never: core::convert::Infallible,
}
impl super::PushBytesErrorReport for PushBytesError {
#[inline]
fn input_len(&self) -> usize {
match self.never {}
}
}
impl fmt::Display for PushBytesError {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self.never {}
}
}
}
#[cfg(not(any(target_pointer_width = "16", target_pointer_width = "32")))]
mod error {
use core::fmt;
/// Error returned on attempt to create too large `PushBytes`.
#[derive(Debug, Clone)]
pub struct PushBytesError {
/// How long the input was.
pub(super) len: usize
}
impl super::PushBytesErrorReport for PushBytesError {
#[inline]
fn input_len(&self) -> usize {
self.len
}
}
impl fmt::Display for PushBytesError {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "attempt to prepare {} bytes to be pushed into script but the limit is 2^32-1", self.len)
}
}
}
crate::error::impl_std_error!(PushBytesError);

View File

@ -97,9 +97,9 @@ fn p2pk_pubkey_bytes_incorrect_key_size_returns_none() {
#[test]
fn p2pk_pubkey_bytes_invalid_key_returns_some() {
let malformed_key = "21032e58afe51f9ed8ad3cc7897f634d881fdbe49816429ded8156bebd2ffd1ux";
let malformed_key = b"21032e58afe51f9ed8ad3cc7897f634d881fdbe49816429ded8156bebd2ffd1ux";
let invalid_key_script = Script::builder()
.push_slice(malformed_key.as_bytes())
.push_slice(malformed_key)
.push_opcode(OP_CHECKSIG)
.into_script();
assert!(invalid_key_script.p2pk_pubkey_bytes().is_some());
@ -197,7 +197,7 @@ fn script_builder() {
// from txid 3bb5e6434c11fb93f64574af5d116736510717f2c595eb45b52c28e31622dfff which was in my mempool when I wrote the test
let script = Builder::new().push_opcode(OP_DUP)
.push_opcode(OP_HASH160)
.push_slice(&hex!("16e1ae70ff0fa102905d4af297f6912bda6cce19"))
.push_slice(hex!("16e1ae70ff0fa102905d4af297f6912bda6cce19"))
.push_opcode(OP_EQUALVERIFY)
.push_opcode(OP_CHECKSIG)
.into_script();
@ -298,7 +298,7 @@ fn script_builder_verify() {
assert_eq!(checkmultisig2.to_hex_string(), "af");
let trick_slice = Builder::new()
.push_slice(&[0xae]) // OP_CHECKMULTISIG
.push_slice([0xae]) // OP_CHECKMULTISIG
.push_verify()
.into_script();
assert_eq!(trick_slice.to_hex_string(), "01ae69");
@ -498,19 +498,19 @@ fn test_iterator() {
unwrap_all!(v_zero, v_zeropush, v_min, v_nonmin_alt, slop_v_min, slop_v_nonmin, slop_v_nonmin_alt);
assert_eq!(v_zero, vec![(0, Instruction::PushBytes(&[]))]);
assert_eq!(v_zeropush, vec![(0, Instruction::PushBytes(&[0]))]);
assert_eq!(v_zero, vec![(0, Instruction::PushBytes(PushBytes::empty()))]);
assert_eq!(v_zeropush, vec![(0, Instruction::PushBytes([0].as_ref()))]);
assert_eq!(
v_min,
vec![(0, Instruction::PushBytes(&[105])), (2, Instruction::Op(opcodes::OP_NOP3))]
vec![(0, Instruction::PushBytes([105].as_ref())), (2, Instruction::Op(opcodes::OP_NOP3))]
);
assert_eq!(v_nonmin.unwrap_err(), Error::NonMinimalPush);
assert_eq!(
v_nonmin_alt,
vec![(0, Instruction::PushBytes(&[105, 0])), (3, Instruction::Op(opcodes::OP_NOP3))]
vec![(0, Instruction::PushBytes([105, 0].as_ref())), (3, Instruction::Op(opcodes::OP_NOP3))]
);
assert_eq!(v_min, slop_v_min);
@ -525,7 +525,7 @@ fn test_iterator() {
#[test]
fn script_ord() {
let script_1 = Builder::new().push_slice(&[1, 2, 3, 4]).into_script();
let script_1 = Builder::new().push_slice([1, 2, 3, 4]).into_script();
let script_2 = Builder::new().push_int(10).into_script();
let script_3 = Builder::new().push_int(15).into_script();
let script_4 = Builder::new().push_opcode(OP_RETURN).into_script();
@ -556,14 +556,14 @@ fn test_bitcoinconsensus () {
fn defult_dust_value_tests() {
// Check that our dust_value() calculator correctly calculates the dust limit on common
// well-known scriptPubKey types.
let script_p2wpkh = Builder::new().push_int(0).push_slice(&[42; 20]).into_script();
let script_p2wpkh = Builder::new().push_int(0).push_slice([42; 20]).into_script();
assert!(script_p2wpkh.is_v0_p2wpkh());
assert_eq!(script_p2wpkh.dust_value(), crate::Amount::from_sat(294));
let script_p2pkh = Builder::new()
.push_opcode(OP_DUP)
.push_opcode(OP_HASH160)
.push_slice(&[42; 20])
.push_slice([42; 20])
.push_opcode(OP_EQUALVERIFY)
.push_opcode(OP_CHECKSIG)
.into_script();
@ -611,7 +611,7 @@ fn script_extend() {
let script_5_items = [
Instruction::Op(OP_DUP),
Instruction::Op(OP_HASH160),
Instruction::PushBytes(&[42; 20]),
Instruction::PushBytes([42; 20].as_ref()),
Instruction::Op(OP_EQUALVERIFY),
Instruction::Op(OP_CHECKSIG),
];
@ -621,7 +621,7 @@ fn script_extend() {
let script_6_items = [
Instruction::Op(OP_DUP),
Instruction::Op(OP_HASH160),
Instruction::PushBytes(&[42; 20]),
Instruction::PushBytes([42; 20].as_ref()),
Instruction::Op(OP_EQUALVERIFY),
Instruction::Op(OP_CHECKSIG),
Instruction::Op(OP_NOP),
@ -632,7 +632,7 @@ fn script_extend() {
let script_7_items = [
Instruction::Op(OP_DUP),
Instruction::Op(OP_HASH160),
Instruction::PushBytes(&[42; 20]),
Instruction::PushBytes([42; 20].as_ref()),
Instruction::Op(OP_EQUALVERIFY),
Instruction::Op(OP_CHECKSIG),
Instruction::Op(OP_NOP),

View File

@ -15,6 +15,7 @@ use secp256k1;
use crate::prelude::*;
use crate::hashes::hex::{self, FromHex};
use crate::sighash::{EcdsaSighashType, NonStandardSighashType};
use crate::script::PushBytes;
const MAX_SIG_LEN: usize = 73;
@ -102,6 +103,8 @@ impl FromStr for Signature {
/// This avoids allocation and allows proving maximum size of the signature (73 bytes).
/// The type can be used largely as a byte slice. It implements all standard traits one would
/// expect and has familiar methods.
/// However, the usual use case is to push it into a script. This can be done directly passing it
/// into [`push_slice`](crate::script::ScriptBuf::push_slice).
#[derive(Copy, Clone)]
pub struct SerializedSignature {
data: [u8; MAX_SIG_LEN],
@ -146,6 +149,13 @@ impl AsMut<[u8]> for SerializedSignature {
}
}
impl AsRef<PushBytes> for SerializedSignature {
#[inline]
fn as_ref(&self) -> &PushBytes {
&<&PushBytes>::from(&self.data)[..self.len()]
}
}
impl core::borrow::Borrow<[u8]> for SerializedSignature {
#[inline]
fn borrow(&self) -> &[u8] {

View File

@ -27,6 +27,27 @@ macro_rules! impl_hashencode {
};
}
#[rustfmt::skip]
macro_rules! impl_asref_push_bytes {
($($hashtype:ident),*) => {
$(
impl AsRef<$crate::blockdata::script::PushBytes> for $hashtype {
fn as_ref(&self) -> &$crate::blockdata::script::PushBytes {
use $crate::hashes::Hash;
self.as_inner().as_ref()
}
}
impl From<$hashtype> for $crate::blockdata::script::PushBytesBuf {
fn from(hash: $hashtype) -> Self {
use $crate::hashes::Hash;
hash.as_inner().into()
}
}
)*
};
}
// newtypes module is solely here so we can rustfmt::skip.
pub use newtypes::*;
@ -75,4 +96,6 @@ See [`hashes::Hash::DISPLAY_BACKWARD`] for more details.
impl_hashencode!(FilterHash);
impl_hashencode!(FilterHeader);
impl_asref_push_bytes!(PubkeyHash, ScriptHash, WPubkeyHash, WScriptHash);
}

View File

@ -1641,15 +1641,15 @@ mod tests {
let cache = cache.segwit_cache();
// Parse hex into Vec because BIP143 test vector displays forwards but our sha256d::Hash displays backwards.
assert_eq!(
cache.prevouts.into_inner().as_ref(),
cache.prevouts.into_inner(),
&Vec::from_hex("96b827c8483d4e9b96712b6713a7b68d6e8003a781feba36c31143470b4efd37").unwrap()[..],
);
assert_eq!(
cache.sequences.into_inner().as_ref(),
cache.sequences.into_inner(),
&Vec::from_hex("52b0a642eea2fb7ae638c36f6252b6750293dbe574a806984b8e4d8548339a3b").unwrap()[..],
);
assert_eq!(
cache.outputs.into_inner().as_ref(),
cache.outputs.into_inner(),
&Vec::from_hex("863ef3e1a92afbfdb97f31ad0fc7683ee943e9abcf2501590ff8f6551f47e5e5").unwrap()[..],
);
}
@ -1677,15 +1677,15 @@ mod tests {
let cache = cache.segwit_cache();
// Parse hex into Vec because BIP143 test vector displays forwards but our sha256d::Hash displays backwards.
assert_eq!(
cache.prevouts.into_inner().as_ref(),
cache.prevouts.into_inner(),
&Vec::from_hex("b0287b4a252ac05af83d2dcef00ba313af78a3e9c329afa216eb3aa2a7b4613a").unwrap()[..],
);
assert_eq!(
cache.sequences.into_inner().as_ref(),
cache.sequences.into_inner(),
&Vec::from_hex("18606b350cd8bf565266bc352f0caddcf01e8fa789dd8a15386327cf8cabe198").unwrap()[..],
);
assert_eq!(
cache.outputs.into_inner().as_ref(),
cache.outputs.into_inner(),
&Vec::from_hex("de984f44532e2173ca0d64314fcefe6d30da6f8cf27bafa706da61df8a226c83").unwrap()[..],
);
}
@ -1718,15 +1718,15 @@ mod tests {
let cache = cache.segwit_cache();
// Parse hex into Vec because BIP143 test vector displays forwards but our sha256d::Hash displays backwards.
assert_eq!(
cache.prevouts.into_inner().as_ref(),
cache.prevouts.into_inner(),
&Vec::from_hex("74afdc312af5183c4198a40ca3c1a275b485496dd3929bca388c4b5e31f7aaa0").unwrap()[..],
);
assert_eq!(
cache.sequences.into_inner().as_ref(),
cache.sequences.into_inner(),
&Vec::from_hex("3bb13029ce7b1f559ef5e747fcac439f1455a2ec7c5f09b72290795e70665044").unwrap()[..],
);
assert_eq!(
cache.outputs.into_inner().as_ref(),
cache.outputs.into_inner(),
&Vec::from_hex("bc4d309071414bed932f98832b27b4d76dad7e6c1346f487a8fdbb8eb90307cc").unwrap()[..],
);
}

View File

@ -3,6 +3,7 @@
use std::collections::BTreeMap;
use std::str::FromStr;
use core::convert::TryFrom;
use bitcoin::bip32::{ExtendedPrivKey, ExtendedPubKey, Fingerprint, IntoDerivationPath, KeySource};
use bitcoin::blockdata::opcodes::OP_0;
@ -15,6 +16,7 @@ use bitcoin::{
absolute, Amount, Denomination, Network, OutPoint, PrivateKey, PublicKey, ScriptBuf, Sequence,
Transaction, TxIn, TxOut, Witness,
};
use bitcoin::script::PushBytes;
const NETWORK: Network = Network::Testnet;
@ -422,9 +424,9 @@ fn finalize_psbt(mut psbt: Psbt) -> Psbt {
let sigs: Vec<_> = psbt.inputs[0].partial_sigs.values().collect();
let script_sig = script::Builder::new()
.push_opcode(OP_0) // OP_CHECKMULTISIG bug pops +1 value when evaluating so push OP_0.
.push_slice(&sigs[0].to_vec())
.push_slice(&sigs[1].to_vec())
.push_slice(psbt.inputs[0].redeem_script.clone().unwrap().as_bytes())
.push_slice(sigs[0].serialize())
.push_slice(sigs[1].serialize())
.push_slice(<&PushBytes>::try_from(psbt.inputs[0].redeem_script.as_ref().unwrap().as_bytes()).unwrap())
.into_script();
psbt.inputs[0].final_script_sig = Some(script_sig);
@ -437,7 +439,7 @@ fn finalize_psbt(mut psbt: Psbt) -> Psbt {
// Input 1: SegWit UTXO
let script_sig = script::Builder::new()
.push_slice(psbt.inputs[1].redeem_script.as_ref().unwrap().as_bytes())
.push_slice(<&PushBytes>::try_from(psbt.inputs[1].redeem_script.as_ref().unwrap().as_bytes()).unwrap())
.into_script();
psbt.inputs[1].final_script_sig = Some(script_sig);