Skip to content

Commit c061d93

Browse files
committed
Merge rust-bitcoin#3164: Extension traits for ScriptBuf
2bb90b8 Introduce two extensions traits for ScriptBuf (Tobin C. Harding) ae0a5bd Run cargo fmt (Tobin C. Harding) 3fdc574 Add temporary script buf modules (Tobin C. Harding) 4ff5d68 Add private ScriptBufAsVec type (Tobin C. Harding) c81fb93 Make push_slice_no_opt pub(crate) (Tobin C. Harding) 1001a33 Add second ScriptBuf impl block (Tobin C. Harding) 3625d74 Make pub in crate functions pub crate (Tobin C. Harding) b368384 Separate ScriptBuf POD methods (Tobin C. Harding) Pull request description: Similar to rust-bitcoin#3155 but for `ScriptBuf`, however it is a little more involved. Note: - the change to use `impl` syntax (and addition of rust-bitcoin#3179) - mad trickery of `ScriptBufAsVec` (props to Kix) - widening of scope of private functions Onward and upward! ACKs for top commit: Kixunil: ACK 2bb90b8 apoelstra: ACK 2bb90b8 successfully ran local tests Tree-SHA512: 7209d8dc436e52b23e1dbfd9db8432df225ebdb701f465e4d1b55328e22988c98a0f28efdf2a8b3edbafc754354d718ab36bd2f5b1621d12e061b2dadaf49a05
2 parents 95a7805 + 2bb90b8 commit c061d93

File tree

14 files changed

+170
-118
lines changed

14 files changed

+170
-118
lines changed

bitcoin/examples/ecdsa-psbt.rs

+1
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@ use bitcoin::bip32::{ChildNumber, DerivationPath, Fingerprint, IntoDerivationPat
3737
use bitcoin::consensus::encode;
3838
use bitcoin::locktime::absolute;
3939
use bitcoin::psbt::{self, Input, Psbt, PsbtSighashType};
40+
use bitcoin::script::ScriptBufExt as _;
4041
use bitcoin::secp256k1::{Secp256k1, Signing, Verification};
4142
use bitcoin::{
4243
transaction, Address, Amount, CompressedPublicKey, Network, OutPoint, ScriptBuf, Sequence,

bitcoin/examples/taproot-psbt.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -84,7 +84,7 @@ use bitcoin::consensus::encode;
8484
use bitcoin::key::{TapTweak, XOnlyPublicKey};
8585
use bitcoin::opcodes::all::{OP_CHECKSIG, OP_CLTV, OP_DROP};
8686
use bitcoin::psbt::{self, Input, Output, Psbt, PsbtSighashType};
87-
use bitcoin::script::ScriptExt as _;
87+
use bitcoin::script::{ScriptBufExt as _, ScriptExt as _};
8888
use bitcoin::secp256k1::Secp256k1;
8989
use bitcoin::sighash::{self, SighashCache, TapSighash, TapSighashType};
9090
use bitcoin::taproot::{self, LeafVersion, TapLeafHash, TaprootBuilder, TaprootSpendInfo};

bitcoin/src/address/mod.rs

+1
Original file line numberDiff line numberDiff line change
@@ -894,6 +894,7 @@ mod tests {
894894
use super::*;
895895
use crate::network::params;
896896
use crate::network::Network::{Bitcoin, Testnet};
897+
use crate::script::ScriptBufExt as _;
897898

898899
fn roundtrips(addr: &Address, network: Network) {
899900
assert_eq!(

bitcoin/src/blockdata/script/builder.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ use crate::locktime::absolute;
77
use crate::opcodes::all::*;
88
use crate::opcodes::{self, Opcode};
99
use crate::prelude::Vec;
10-
use crate::script::{ScriptExt as _, ScriptExtPriv as _};
10+
use crate::script::{ScriptBufExt as _, ScriptExt as _, ScriptExtPriv as _};
1111
use crate::Sequence;
1212

1313
/// An Object which can be used to construct a script piece by piece.

bitcoin/src/blockdata/script/instruction.rs

+3-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
// SPDX-License-Identifier: CC0-1.0
22

3-
use super::{read_uint_iter, Error, PushBytes, Script, ScriptBuf, UintError};
3+
use super::{
4+
read_uint_iter, Error, PushBytes, Script, ScriptBuf, ScriptBufExtPriv as _, UintError,
5+
};
46
use crate::opcodes::{self, Opcode};
57

68
/// A "parsed opcode" which allows iterating over a [`Script`] in a more sensible way.

bitcoin/src/blockdata/script/owned.rs

+154-112
Original file line numberDiff line numberDiff line change
@@ -64,20 +64,6 @@ impl ScriptBuf {
6464
/// Returns a mutable reference to unsized script.
6565
pub fn as_mut_script(&mut self) -> &mut Script { Script::from_bytes_mut(&mut self.0) }
6666

67-
/// Creates a new script builder
68-
pub fn builder() -> Builder { Builder::new() }
69-
70-
/// Generates OP_RETURN-type of scriptPubkey for the given data.
71-
pub fn new_op_return<T: AsRef<PushBytes>>(data: T) -> Self {
72-
Builder::new().push_opcode(OP_RETURN).push_slice(data).into_script()
73-
}
74-
75-
/// Creates a [`ScriptBuf`] from a hex string.
76-
pub fn from_hex(s: &str) -> Result<Self, hex::HexToBytesError> {
77-
let v = Vec::from_hex(s)?;
78-
Ok(ScriptBuf::from_bytes(v))
79-
}
80-
8167
/// Converts byte vector into script.
8268
///
8369
/// This method doesn't (re)allocate.
@@ -88,119 +74,151 @@ impl ScriptBuf {
8874
/// This method doesn't (re)allocate.
8975
pub fn into_bytes(self) -> Vec<u8> { self.0 }
9076

91-
/// Adds a single opcode to the script.
92-
pub fn push_opcode(&mut self, data: Opcode) { self.0.push(data.to_u8()); }
93-
94-
/// Adds instructions to push some arbitrary data onto the stack.
95-
pub fn push_slice<T: AsRef<PushBytes>>(&mut self, data: T) {
96-
let data = data.as_ref();
97-
self.reserve(Self::reserved_len_for_slice(data.len()));
98-
self.push_slice_no_opt(data);
77+
/// Converts this `ScriptBuf` into a [boxed](Box) [`Script`].
78+
///
79+
/// This method reallocates if the capacity is greater than length of the script but should not
80+
/// when they are equal. If you know beforehand that you need to create a script of exact size
81+
/// use [`reserve_exact`](Self::reserve_exact) before adding data to the script so that the
82+
/// reallocation can be avoided.
83+
#[must_use = "`self` will be dropped if the result is not used"]
84+
#[inline]
85+
pub fn into_boxed_script(self) -> Box<Script> {
86+
// Copied from PathBuf::into_boxed_path
87+
let rw = Box::into_raw(self.0.into_boxed_slice()) as *mut Script;
88+
unsafe { Box::from_raw(rw) }
9989
}
90+
}
10091

101-
/// Pushes the slice without reserving
102-
fn push_slice_no_opt(&mut self, data: &PushBytes) {
103-
// Start with a PUSH opcode
104-
match data.len().to_u64() {
105-
n if n < opcodes::Ordinary::OP_PUSHDATA1 as u64 => {
106-
self.0.push(n as u8);
107-
}
108-
n if n < 0x100 => {
109-
self.0.push(opcodes::Ordinary::OP_PUSHDATA1.to_u8());
110-
self.0.push(n as u8);
111-
}
112-
n if n < 0x10000 => {
113-
self.0.push(opcodes::Ordinary::OP_PUSHDATA2.to_u8());
114-
self.0.push((n % 0x100) as u8);
115-
self.0.push((n / 0x100) as u8);
116-
}
117-
// `PushBytes` enforces len < 0x100000000
118-
n => {
119-
self.0.push(opcodes::Ordinary::OP_PUSHDATA4.to_u8());
120-
self.0.push((n % 0x100) as u8);
121-
self.0.push(((n / 0x100) % 0x100) as u8);
122-
self.0.push(((n / 0x10000) % 0x100) as u8);
123-
self.0.push((n / 0x1000000) as u8);
124-
}
92+
crate::internal_macros::define_extension_trait! {
93+
/// Extension functionality for the [`ScriptBuf`] type.
94+
pub trait ScriptBufExt impl for ScriptBuf {
95+
/// Creates a new script builder
96+
fn builder() -> Builder { Builder::new() }
97+
98+
/// Generates OP_RETURN-type of scriptPubkey for the given data.
99+
fn new_op_return<T: AsRef<PushBytes>>(data: T) -> Self {
100+
Builder::new().push_opcode(OP_RETURN).push_slice(data).into_script()
125101
}
126-
// Then push the raw bytes
127-
self.0.extend_from_slice(data.as_bytes());
128-
}
129102

130-
/// Computes the sum of `len` and the length of an appropriate push opcode.
131-
pub(in crate::blockdata::script) fn reserved_len_for_slice(len: usize) -> usize {
132-
len + match len {
133-
0..=0x4b => 1,
134-
0x4c..=0xff => 2,
135-
0x100..=0xffff => 3,
136-
// we don't care about oversized, the other fn will panic anyway
137-
_ => 5,
103+
/// Creates a [`ScriptBuf`] from a hex string.
104+
fn from_hex(s: &str) -> Result<ScriptBuf, hex::HexToBytesError> {
105+
let v = Vec::from_hex(s)?;
106+
Ok(ScriptBuf::from_bytes(v))
138107
}
139-
}
140108

141-
/// Add a single instruction to the script.
142-
///
143-
/// # Panics
144-
///
145-
/// The method panics if the instruction is a data push with length greater or equal to
146-
/// 0x100000000.
147-
pub fn push_instruction(&mut self, instruction: Instruction<'_>) {
148-
match instruction {
149-
Instruction::Op(opcode) => self.push_opcode(opcode),
150-
Instruction::PushBytes(bytes) => self.push_slice(bytes),
109+
/// Adds a single opcode to the script.
110+
fn push_opcode(&mut self, data: Opcode) { self.as_byte_vec().push(data.to_u8()); }
111+
112+
/// Adds instructions to push some arbitrary data onto the stack.
113+
fn push_slice<T: AsRef<PushBytes>>(&mut self, data: T) {
114+
let data = data.as_ref();
115+
self.reserve(ScriptBuf::reserved_len_for_slice(data.len()));
116+
self.push_slice_no_opt(data);
151117
}
152-
}
153118

154-
/// Like push_instruction, but avoids calling `reserve` to not re-check the length.
155-
pub fn push_instruction_no_opt(&mut self, instruction: Instruction<'_>) {
156-
match instruction {
157-
Instruction::Op(opcode) => self.push_opcode(opcode),
158-
Instruction::PushBytes(bytes) => self.push_slice_no_opt(bytes),
119+
/// Add a single instruction to the script.
120+
///
121+
/// # Panics
122+
///
123+
/// The method panics if the instruction is a data push with length greater or equal to
124+
/// 0x100000000.
125+
fn push_instruction(&mut self, instruction: Instruction<'_>) {
126+
match instruction {
127+
Instruction::Op(opcode) => self.push_opcode(opcode),
128+
Instruction::PushBytes(bytes) => self.push_slice(bytes),
129+
}
130+
}
131+
132+
/// Like push_instruction, but avoids calling `reserve` to not re-check the length.
133+
fn push_instruction_no_opt(&mut self, instruction: Instruction<'_>) {
134+
match instruction {
135+
Instruction::Op(opcode) => self.push_opcode(opcode),
136+
Instruction::PushBytes(bytes) => self.push_slice_no_opt(bytes),
137+
}
159138
}
139+
140+
/// Adds an `OP_VERIFY` to the script or replaces the last opcode with VERIFY form.
141+
///
142+
/// Some opcodes such as `OP_CHECKSIG` have a verify variant that works as if `VERIFY` was
143+
/// in the script right after. To save space this function appends `VERIFY` only if
144+
/// the most-recently-added opcode *does not* have an alternate `VERIFY` form. If it does
145+
/// the last opcode is replaced. E.g., `OP_CHECKSIG` will become `OP_CHECKSIGVERIFY`.
146+
///
147+
/// Note that existing `OP_*VERIFY` opcodes do not lead to the instruction being ignored
148+
/// because `OP_VERIFY` consumes an item from the stack so ignoring them would change the
149+
/// semantics.
150+
///
151+
/// This function needs to iterate over the script to find the last instruction. Prefer
152+
/// `Builder` if you're creating the script from scratch or if you want to push `OP_VERIFY`
153+
/// multiple times.
154+
fn scan_and_push_verify(&mut self) { self.push_verify(self.last_opcode()); }
160155
}
156+
}
161157

162-
/// Adds an `OP_VERIFY` to the script or replaces the last opcode with VERIFY form.
163-
///
164-
/// Some opcodes such as `OP_CHECKSIG` have a verify variant that works as if `VERIFY` was
165-
/// in the script right after. To save space this function appends `VERIFY` only if
166-
/// the most-recently-added opcode *does not* have an alternate `VERIFY` form. If it does
167-
/// the last opcode is replaced. E.g., `OP_CHECKSIG` will become `OP_CHECKSIGVERIFY`.
168-
///
169-
/// Note that existing `OP_*VERIFY` opcodes do not lead to the instruction being ignored
170-
/// because `OP_VERIFY` consumes an item from the stack so ignoring them would change the
171-
/// semantics.
172-
///
173-
/// This function needs to iterate over the script to find the last instruction. Prefer
174-
/// `Builder` if you're creating the script from scratch or if you want to push `OP_VERIFY`
175-
/// multiple times.
176-
pub fn scan_and_push_verify(&mut self) { self.push_verify(self.last_opcode()); }
158+
crate::internal_macros::define_extension_trait! {
159+
pub(crate) trait ScriptBufExtPriv impl for ScriptBuf {
160+
/// Pretends to convert `&mut ScriptBuf` to `&mut Vec<u8>` so that it can be modified.
161+
///
162+
/// Note: if the returned value leaks the original `ScriptBuf` will become empty.
163+
fn as_byte_vec(&mut self) -> ScriptBufAsVec<'_> {
164+
let vec = core::mem::take(self).into_bytes();
165+
ScriptBufAsVec(self, vec)
166+
}
177167

178-
/// Adds an `OP_VERIFY` to the script or changes the most-recently-added opcode to `VERIFY`
179-
/// alternative.
180-
///
181-
/// See the public fn [`Self::scan_and_push_verify`] to learn more.
182-
pub(in crate::blockdata::script) fn push_verify(&mut self, last_opcode: Option<Opcode>) {
183-
match opcode_to_verify(last_opcode) {
184-
Some(opcode) => {
185-
self.0.pop();
186-
self.push_opcode(opcode);
168+
/// Pushes the slice without reserving
169+
fn push_slice_no_opt(&mut self, data: &PushBytes) {
170+
let mut this = self.as_byte_vec();
171+
// Start with a PUSH opcode
172+
match data.len().to_u64() {
173+
n if n < opcodes::Ordinary::OP_PUSHDATA1 as u64 => {
174+
this.push(n as u8);
175+
}
176+
n if n < 0x100 => {
177+
this.push(opcodes::Ordinary::OP_PUSHDATA1.to_u8());
178+
this.push(n as u8);
179+
}
180+
n if n < 0x10000 => {
181+
this.push(opcodes::Ordinary::OP_PUSHDATA2.to_u8());
182+
this.push((n % 0x100) as u8);
183+
this.push((n / 0x100) as u8);
184+
}
185+
// `PushBytes` enforces len < 0x100000000
186+
n => {
187+
this.push(opcodes::Ordinary::OP_PUSHDATA4.to_u8());
188+
this.push((n % 0x100) as u8);
189+
this.push(((n / 0x100) % 0x100) as u8);
190+
this.push(((n / 0x10000) % 0x100) as u8);
191+
this.push((n / 0x1000000) as u8);
192+
}
187193
}
188-
None => self.push_opcode(OP_VERIFY),
194+
// Then push the raw bytes
195+
this.extend_from_slice(data.as_bytes());
189196
}
190-
}
191197

192-
/// Converts this `ScriptBuf` into a [boxed](Box) [`Script`].
193-
///
194-
/// This method reallocates if the capacity is greater than length of the script but should not
195-
/// when they are equal. If you know beforehand that you need to create a script of exact size
196-
/// use [`reserve_exact`](Self::reserve_exact) before adding data to the script so that the
197-
/// reallocation can be avoided.
198-
#[must_use = "`self` will be dropped if the result is not used"]
199-
#[inline]
200-
pub fn into_boxed_script(self) -> Box<Script> {
201-
// Copied from PathBuf::into_boxed_path
202-
let rw = Box::into_raw(self.0.into_boxed_slice()) as *mut Script;
203-
unsafe { Box::from_raw(rw) }
198+
/// Computes the sum of `len` and the length of an appropriate push opcode.
199+
fn reserved_len_for_slice(len: usize) -> usize {
200+
len + match len {
201+
0..=0x4b => 1,
202+
0x4c..=0xff => 2,
203+
0x100..=0xffff => 3,
204+
// we don't care about oversized, the other fn will panic anyway
205+
_ => 5,
206+
}
207+
}
208+
209+
/// Adds an `OP_VERIFY` to the script or changes the most-recently-added opcode to `VERIFY`
210+
/// alternative.
211+
///
212+
/// See the public fn [`Self::scan_and_push_verify`] to learn more.
213+
fn push_verify(&mut self, last_opcode: Option<Opcode>) {
214+
match opcode_to_verify(last_opcode) {
215+
Some(opcode) => {
216+
self.as_byte_vec().pop();
217+
self.push_opcode(opcode);
218+
}
219+
None => self.push_opcode(OP_VERIFY),
220+
}
221+
}
204222
}
205223
}
206224

@@ -250,3 +268,27 @@ impl<'a> Extend<Instruction<'a>> for ScriptBuf {
250268
}
251269
}
252270
}
271+
272+
/// Pretends that this is a mutable reference to [`ScriptBuf`]'s internal buffer.
273+
///
274+
/// In reality the backing `Vec<u8>` is swapped with an empty one and this is holding both the
275+
/// reference and the vec. The vec is put back when this drops so it also covers paics. (But not
276+
/// leaks, which is OK since we never leak.)
277+
pub(crate) struct ScriptBufAsVec<'a>(&'a mut ScriptBuf, Vec<u8>);
278+
279+
impl<'a> core::ops::Deref for ScriptBufAsVec<'a> {
280+
type Target = Vec<u8>;
281+
282+
fn deref(&self) -> &Self::Target { &self.1 }
283+
}
284+
285+
impl<'a> core::ops::DerefMut for ScriptBufAsVec<'a> {
286+
fn deref_mut(&mut self) -> &mut Self::Target { &mut self.1 }
287+
}
288+
289+
impl<'a> Drop for ScriptBufAsVec<'a> {
290+
fn drop(&mut self) {
291+
let vec = core::mem::take(&mut self.1);
292+
*(self.0) = ScriptBuf::from_bytes(vec);
293+
}
294+
}

bitcoin/src/crypto/ecdsa.rs

+2
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,8 @@ use io::Write;
1313

1414
use crate::prelude::{DisplayHex, Vec};
1515
use crate::script::PushBytes;
16+
#[cfg(doc)]
17+
use crate::script::ScriptBufExt as _;
1618
use crate::sighash::{EcdsaSighashType, NonStandardSighashTypeError};
1719

1820
const MAX_SIG_LEN: usize = 73;

bitcoin/src/crypto/sighash.rs

+1
Original file line numberDiff line numberDiff line change
@@ -1470,6 +1470,7 @@ mod tests {
14701470
use super::*;
14711471
use crate::consensus::deserialize;
14721472
use crate::locktime::absolute;
1473+
use crate::script::ScriptBufExt as _;
14731474

14741475
extern crate serde_json;
14751476

bitcoin/src/internal_macros.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -251,7 +251,7 @@ macro_rules! define_extension_trait {
251251
($(#[$($trait_attrs:tt)*])* $trait_vis:vis trait $trait_name:ident impl for $ty:ident {
252252
$(
253253
$(#[$($fn_attrs:tt)*])*
254-
fn $fn:ident$(<$($gen:ident: $gent:ident),*>)?($($params:tt)*) $( -> $ret:ty )? $body:block
254+
fn $fn:ident$(<$($gen:ident: $gent:path),*>)?($($params:tt)*) $( -> $ret:ty )? $body:block
255255
)*
256256
}) => {
257257
$(#[$($trait_attrs)*])* $trait_vis trait $trait_name {

bitcoin/src/psbt/mod.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -1216,7 +1216,7 @@ mod tests {
12161216
use crate::locktime::absolute;
12171217
use crate::network::NetworkKind;
12181218
use crate::psbt::serialize::{Deserialize, Serialize};
1219-
use crate::script::ScriptBuf;
1219+
use crate::script::{ScriptBuf, ScriptBufExt as _};
12201220
use crate::transaction::{self, OutPoint, TxIn};
12211221
use crate::witness::Witness;
12221222
use crate::Sequence;

bitcoin/src/psbt/serialize.rs

+1
Original file line numberDiff line numberDiff line change
@@ -383,6 +383,7 @@ fn key_source_len(key_source: &KeySource) -> usize { 4 + 4 * (key_source.1).as_r
383383
#[cfg(test)]
384384
mod tests {
385385
use super::*;
386+
use crate::script::ScriptBufExt as _;
386387

387388
// Composes tree matching a given depth map, filled with dumb script leafs,
388389
// each of which consists of a single push-int op code, with int value

bitcoin/src/taproot/mod.rs

+1
Original file line numberDiff line numberDiff line change
@@ -1544,6 +1544,7 @@ mod test {
15441544
use secp256k1::VerifyOnly;
15451545

15461546
use super::*;
1547+
use crate::script::ScriptBufExt as _;
15471548
use crate::sighash::{TapSighash, TapSighashTag};
15481549
use crate::{Address, KnownHrp};
15491550
extern crate serde_json;

0 commit comments

Comments
 (0)