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:
parent
8fedbcbf13
commit
bcd95fa036
|
@ -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);
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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))
|
||||
|
|
|
@ -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.
|
||||
///
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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);
|
|
@ -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),
|
||||
|
|
|
@ -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] {
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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()[..],
|
||||
);
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
|
Loading…
Reference in New Issue