Introduce two extensions traits for ScriptBuf

In preparation for moving the `ScritpBuf` type to `primitives` add a
public and private extension trait for the functions we want to leave
here in `bitcoin`.

Note, includes a change to the `difine_extension_trait` metavariable
used on `$gent` from `ident` to `path` to support the generic
`AsRef<PushBytes>`.
This commit is contained in:
Tobin C. Harding 2024-08-19 10:37:45 +10:00
parent ae0a5bd64a
commit 2bb90b8203
No known key found for this signature in database
GPG Key ID: 40BF9E4C269D6607
14 changed files with 34 additions and 25 deletions

View File

@ -37,6 +37,7 @@ use bitcoin::bip32::{ChildNumber, DerivationPath, Fingerprint, IntoDerivationPat
use bitcoin::consensus::encode; use bitcoin::consensus::encode;
use bitcoin::locktime::absolute; use bitcoin::locktime::absolute;
use bitcoin::psbt::{self, Input, Psbt, PsbtSighashType}; use bitcoin::psbt::{self, Input, Psbt, PsbtSighashType};
use bitcoin::script::ScriptBufExt as _;
use bitcoin::secp256k1::{Secp256k1, Signing, Verification}; use bitcoin::secp256k1::{Secp256k1, Signing, Verification};
use bitcoin::{ use bitcoin::{
transaction, Address, Amount, CompressedPublicKey, Network, OutPoint, ScriptBuf, Sequence, transaction, Address, Amount, CompressedPublicKey, Network, OutPoint, ScriptBuf, Sequence,

View File

@ -84,7 +84,7 @@ use bitcoin::consensus::encode;
use bitcoin::key::{TapTweak, XOnlyPublicKey}; use bitcoin::key::{TapTweak, XOnlyPublicKey};
use bitcoin::opcodes::all::{OP_CHECKSIG, OP_CLTV, OP_DROP}; use bitcoin::opcodes::all::{OP_CHECKSIG, OP_CLTV, OP_DROP};
use bitcoin::psbt::{self, Input, Output, Psbt, PsbtSighashType}; use bitcoin::psbt::{self, Input, Output, Psbt, PsbtSighashType};
use bitcoin::script::ScriptExt as _; use bitcoin::script::{ScriptBufExt as _, ScriptExt as _};
use bitcoin::secp256k1::Secp256k1; use bitcoin::secp256k1::Secp256k1;
use bitcoin::sighash::{self, SighashCache, TapSighash, TapSighashType}; use bitcoin::sighash::{self, SighashCache, TapSighash, TapSighashType};
use bitcoin::taproot::{self, LeafVersion, TapLeafHash, TaprootBuilder, TaprootSpendInfo}; use bitcoin::taproot::{self, LeafVersion, TapLeafHash, TaprootBuilder, TaprootSpendInfo};

View File

@ -894,6 +894,7 @@ mod tests {
use super::*; use super::*;
use crate::network::params; use crate::network::params;
use crate::network::Network::{Bitcoin, Testnet}; use crate::network::Network::{Bitcoin, Testnet};
use crate::script::ScriptBufExt as _;
fn roundtrips(addr: &Address, network: Network) { fn roundtrips(addr: &Address, network: Network) {
assert_eq!( assert_eq!(

View File

@ -7,7 +7,7 @@ use crate::locktime::absolute;
use crate::opcodes::all::*; use crate::opcodes::all::*;
use crate::opcodes::{self, Opcode}; use crate::opcodes::{self, Opcode};
use crate::prelude::Vec; use crate::prelude::Vec;
use crate::script::{ScriptExt as _, ScriptExtPriv as _}; use crate::script::{ScriptBufExt as _, ScriptExt as _, ScriptExtPriv as _};
use crate::Sequence; use crate::Sequence;
/// An Object which can be used to construct a script piece by piece. /// An Object which can be used to construct a script piece by piece.

View File

@ -1,6 +1,8 @@
// SPDX-License-Identifier: CC0-1.0 // SPDX-License-Identifier: CC0-1.0
use super::{read_uint_iter, Error, PushBytes, Script, ScriptBuf, UintError}; use super::{
read_uint_iter, Error, PushBytes, Script, ScriptBuf, ScriptBufExtPriv as _, UintError,
};
use crate::opcodes::{self, Opcode}; use crate::opcodes::{self, Opcode};
/// A "parsed opcode" which allows iterating over a [`Script`] in a more sensible way. /// A "parsed opcode" which allows iterating over a [`Script`] in a more sensible way.

View File

@ -89,30 +89,30 @@ impl ScriptBuf {
} }
} }
mod tmp_pub { crate::internal_macros::define_extension_trait! {
use super::*; /// Extension functionality for the [`ScriptBuf`] type.
impl ScriptBuf { pub trait ScriptBufExt impl for ScriptBuf {
/// Creates a new script builder /// Creates a new script builder
pub fn builder() -> Builder { Builder::new() } fn builder() -> Builder { Builder::new() }
/// Generates OP_RETURN-type of scriptPubkey for the given data. /// Generates OP_RETURN-type of scriptPubkey for the given data.
pub fn new_op_return<T: AsRef<PushBytes>>(data: T) -> Self { fn new_op_return<T: AsRef<PushBytes>>(data: T) -> Self {
Builder::new().push_opcode(OP_RETURN).push_slice(data).into_script() Builder::new().push_opcode(OP_RETURN).push_slice(data).into_script()
} }
/// Creates a [`ScriptBuf`] from a hex string. /// Creates a [`ScriptBuf`] from a hex string.
pub fn from_hex(s: &str) -> Result<Self, hex::HexToBytesError> { fn from_hex(s: &str) -> Result<ScriptBuf, hex::HexToBytesError> {
let v = Vec::from_hex(s)?; let v = Vec::from_hex(s)?;
Ok(ScriptBuf::from_bytes(v)) Ok(ScriptBuf::from_bytes(v))
} }
/// Adds a single opcode to the script. /// Adds a single opcode to the script.
pub fn push_opcode(&mut self, data: Opcode) { self.as_byte_vec().push(data.to_u8()); } fn push_opcode(&mut self, data: Opcode) { self.as_byte_vec().push(data.to_u8()); }
/// Adds instructions to push some arbitrary data onto the stack. /// Adds instructions to push some arbitrary data onto the stack.
pub fn push_slice<T: AsRef<PushBytes>>(&mut self, data: T) { fn push_slice<T: AsRef<PushBytes>>(&mut self, data: T) {
let data = data.as_ref(); let data = data.as_ref();
self.reserve(Self::reserved_len_for_slice(data.len())); self.reserve(ScriptBuf::reserved_len_for_slice(data.len()));
self.push_slice_no_opt(data); self.push_slice_no_opt(data);
} }
@ -122,7 +122,7 @@ mod tmp_pub {
/// ///
/// The method panics if the instruction is a data push with length greater or equal to /// The method panics if the instruction is a data push with length greater or equal to
/// 0x100000000. /// 0x100000000.
pub fn push_instruction(&mut self, instruction: Instruction<'_>) { fn push_instruction(&mut self, instruction: Instruction<'_>) {
match instruction { match instruction {
Instruction::Op(opcode) => self.push_opcode(opcode), Instruction::Op(opcode) => self.push_opcode(opcode),
Instruction::PushBytes(bytes) => self.push_slice(bytes), Instruction::PushBytes(bytes) => self.push_slice(bytes),
@ -130,7 +130,7 @@ mod tmp_pub {
} }
/// Like push_instruction, but avoids calling `reserve` to not re-check the length. /// Like push_instruction, but avoids calling `reserve` to not re-check the length.
pub fn push_instruction_no_opt(&mut self, instruction: Instruction<'_>) { fn push_instruction_no_opt(&mut self, instruction: Instruction<'_>) {
match instruction { match instruction {
Instruction::Op(opcode) => self.push_opcode(opcode), Instruction::Op(opcode) => self.push_opcode(opcode),
Instruction::PushBytes(bytes) => self.push_slice_no_opt(bytes), Instruction::PushBytes(bytes) => self.push_slice_no_opt(bytes),
@ -151,23 +151,22 @@ mod tmp_pub {
/// This function needs to iterate over the script to find the last instruction. Prefer /// This function needs to iterate over the script to find the last instruction. Prefer
/// `Builder` if you're creating the script from scratch or if you want to push `OP_VERIFY` /// `Builder` if you're creating the script from scratch or if you want to push `OP_VERIFY`
/// multiple times. /// multiple times.
pub fn scan_and_push_verify(&mut self) { self.push_verify(self.last_opcode()); } fn scan_and_push_verify(&mut self) { self.push_verify(self.last_opcode()); }
} }
} }
mod tmp_priv { crate::internal_macros::define_extension_trait! {
use super::*; pub(crate) trait ScriptBufExtPriv impl for ScriptBuf {
impl ScriptBuf {
/// Pretends to convert `&mut ScriptBuf` to `&mut Vec<u8>` so that it can be modified. /// Pretends to convert `&mut ScriptBuf` to `&mut Vec<u8>` so that it can be modified.
/// ///
/// Note: if the returned value leaks the original `ScriptBuf` will become empty. /// Note: if the returned value leaks the original `ScriptBuf` will become empty.
pub(crate) fn as_byte_vec(&mut self) -> ScriptBufAsVec<'_> { fn as_byte_vec(&mut self) -> ScriptBufAsVec<'_> {
let vec = core::mem::take(self).into_bytes(); let vec = core::mem::take(self).into_bytes();
ScriptBufAsVec(self, vec) ScriptBufAsVec(self, vec)
} }
/// Pushes the slice without reserving /// Pushes the slice without reserving
pub(crate) fn push_slice_no_opt(&mut self, data: &PushBytes) { fn push_slice_no_opt(&mut self, data: &PushBytes) {
let mut this = self.as_byte_vec(); let mut this = self.as_byte_vec();
// Start with a PUSH opcode // Start with a PUSH opcode
match data.len().to_u64() { match data.len().to_u64() {
@ -197,7 +196,7 @@ mod tmp_priv {
} }
/// Computes the sum of `len` and the length of an appropriate push opcode. /// Computes the sum of `len` and the length of an appropriate push opcode.
pub(crate) fn reserved_len_for_slice(len: usize) -> usize { fn reserved_len_for_slice(len: usize) -> usize {
len + match len { len + match len {
0..=0x4b => 1, 0..=0x4b => 1,
0x4c..=0xff => 2, 0x4c..=0xff => 2,
@ -211,7 +210,7 @@ mod tmp_priv {
/// alternative. /// alternative.
/// ///
/// See the public fn [`Self::scan_and_push_verify`] to learn more. /// See the public fn [`Self::scan_and_push_verify`] to learn more.
pub(crate) fn push_verify(&mut self, last_opcode: Option<Opcode>) { fn push_verify(&mut self, last_opcode: Option<Opcode>) {
match opcode_to_verify(last_opcode) { match opcode_to_verify(last_opcode) {
Some(opcode) => { Some(opcode) => {
self.as_byte_vec().pop(); self.as_byte_vec().pop();

View File

@ -13,6 +13,8 @@ use io::Write;
use crate::prelude::{DisplayHex, Vec}; use crate::prelude::{DisplayHex, Vec};
use crate::script::PushBytes; use crate::script::PushBytes;
#[cfg(doc)]
use crate::script::ScriptBufExt as _;
use crate::sighash::{EcdsaSighashType, NonStandardSighashTypeError}; use crate::sighash::{EcdsaSighashType, NonStandardSighashTypeError};
const MAX_SIG_LEN: usize = 73; const MAX_SIG_LEN: usize = 73;

View File

@ -1470,6 +1470,7 @@ mod tests {
use super::*; use super::*;
use crate::consensus::deserialize; use crate::consensus::deserialize;
use crate::locktime::absolute; use crate::locktime::absolute;
use crate::script::ScriptBufExt as _;
extern crate serde_json; extern crate serde_json;

View File

@ -251,7 +251,7 @@ macro_rules! define_extension_trait {
($(#[$($trait_attrs:tt)*])* $trait_vis:vis trait $trait_name:ident impl for $ty:ident { ($(#[$($trait_attrs:tt)*])* $trait_vis:vis trait $trait_name:ident impl for $ty:ident {
$( $(
$(#[$($fn_attrs:tt)*])* $(#[$($fn_attrs:tt)*])*
fn $fn:ident$(<$($gen:ident: $gent:ident),*>)?($($params:tt)*) $( -> $ret:ty )? $body:block fn $fn:ident$(<$($gen:ident: $gent:path),*>)?($($params:tt)*) $( -> $ret:ty )? $body:block
)* )*
}) => { }) => {
$(#[$($trait_attrs)*])* $trait_vis trait $trait_name { $(#[$($trait_attrs)*])* $trait_vis trait $trait_name {

View File

@ -1216,7 +1216,7 @@ mod tests {
use crate::locktime::absolute; use crate::locktime::absolute;
use crate::network::NetworkKind; use crate::network::NetworkKind;
use crate::psbt::serialize::{Deserialize, Serialize}; use crate::psbt::serialize::{Deserialize, Serialize};
use crate::script::ScriptBuf; use crate::script::{ScriptBuf, ScriptBufExt as _};
use crate::transaction::{self, OutPoint, TxIn}; use crate::transaction::{self, OutPoint, TxIn};
use crate::witness::Witness; use crate::witness::Witness;
use crate::Sequence; use crate::Sequence;

View File

@ -383,6 +383,7 @@ fn key_source_len(key_source: &KeySource) -> usize { 4 + 4 * (key_source.1).as_r
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use super::*; use super::*;
use crate::script::ScriptBufExt as _;
// Composes tree matching a given depth map, filled with dumb script leafs, // Composes tree matching a given depth map, filled with dumb script leafs,
// each of which consists of a single push-int op code, with int value // each of which consists of a single push-int op code, with int value

View File

@ -1544,6 +1544,7 @@ mod test {
use secp256k1::VerifyOnly; use secp256k1::VerifyOnly;
use super::*; use super::*;
use crate::script::ScriptBufExt as _;
use crate::sighash::{TapSighash, TapSighashTag}; use crate::sighash::{TapSighash, TapSighashTag};
use crate::{Address, KnownHrp}; use crate::{Address, KnownHrp};
extern crate serde_json; extern crate serde_json;

View File

@ -9,7 +9,7 @@ use bitcoin::consensus::encode::{deserialize, serialize_hex};
use bitcoin::hex::FromHex; use bitcoin::hex::FromHex;
use bitcoin::opcodes::OP_0; use bitcoin::opcodes::OP_0;
use bitcoin::psbt::{Psbt, PsbtSighashType}; use bitcoin::psbt::{Psbt, PsbtSighashType};
use bitcoin::script::PushBytes; use bitcoin::script::{PushBytes, ScriptBufExt as _};
use bitcoin::secp256k1::Secp256k1; use bitcoin::secp256k1::Secp256k1;
use bitcoin::{ use bitcoin::{
absolute, script, transaction, Amount, Denomination, NetworkKind, OutPoint, PrivateKey, absolute, script, transaction, Amount, Denomination, NetworkKind, OutPoint, PrivateKey,

View File

@ -33,6 +33,7 @@ use bitcoin::hex::FromHex;
use bitcoin::locktime::{absolute, relative}; use bitcoin::locktime::{absolute, relative};
use bitcoin::psbt::raw::{self, Key, Pair, ProprietaryKey}; use bitcoin::psbt::raw::{self, Key, Pair, ProprietaryKey};
use bitcoin::psbt::{Input, Output, Psbt, PsbtSighashType}; use bitcoin::psbt::{Input, Output, Psbt, PsbtSighashType};
use bitcoin::script::ScriptBufExt as _;
use bitcoin::sighash::{EcdsaSighashType, TapSighashType}; use bitcoin::sighash::{EcdsaSighashType, TapSighashType};
use bitcoin::taproot::{self, ControlBlock, LeafVersion, TapTree, TaprootBuilder}; use bitcoin::taproot::{self, ControlBlock, LeafVersion, TapTree, TaprootBuilder};
use bitcoin::witness::Witness; use bitcoin::witness::Witness;