diff --git a/bitcoin/src/blockdata/script/owned.rs b/bitcoin/src/blockdata/script/owned.rs index 1bce8a28d..abfcc2529 100644 --- a/bitcoin/src/blockdata/script/owned.rs +++ b/bitcoin/src/blockdata/script/owned.rs @@ -105,7 +105,7 @@ impl ScriptBuf { } /// Adds a single opcode to the script. - pub fn push_opcode(&mut self, data: Opcode) { self.0.push(data.to_u8()); } + pub 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. pub fn push_slice>(&mut self, data: T) { @@ -153,33 +153,42 @@ impl ScriptBuf { } impl ScriptBuf { + /// Pretends to convert `&mut ScriptBuf` to `&mut Vec` so that it can be modified. + /// + /// Note: if the returned value leaks the original `ScriptBuf` will become empty. + pub(crate) fn as_byte_vec(&mut self) -> ScriptBufAsVec<'_> { + let vec = core::mem::take(self).into_bytes(); + ScriptBufAsVec(self, vec) + } + /// Pushes the slice without reserving pub(crate) fn push_slice_no_opt(&mut self, data: &PushBytes) { + let mut this = self.as_byte_vec(); // Start with a PUSH opcode match data.len().to_u64() { n if n < opcodes::Ordinary::OP_PUSHDATA1 as u64 => { - self.0.push(n as u8); + this.push(n as u8); } n if n < 0x100 => { - self.0.push(opcodes::Ordinary::OP_PUSHDATA1.to_u8()); - self.0.push(n as u8); + this.push(opcodes::Ordinary::OP_PUSHDATA1.to_u8()); + this.push(n as u8); } n if n < 0x10000 => { - self.0.push(opcodes::Ordinary::OP_PUSHDATA2.to_u8()); - self.0.push((n % 0x100) as u8); - self.0.push((n / 0x100) as u8); + this.push(opcodes::Ordinary::OP_PUSHDATA2.to_u8()); + this.push((n % 0x100) as u8); + this.push((n / 0x100) as u8); } // `PushBytes` enforces len < 0x100000000 n => { - self.0.push(opcodes::Ordinary::OP_PUSHDATA4.to_u8()); - self.0.push((n % 0x100) as u8); - self.0.push(((n / 0x100) % 0x100) as u8); - self.0.push(((n / 0x10000) % 0x100) as u8); - self.0.push((n / 0x1000000) as u8); + this.push(opcodes::Ordinary::OP_PUSHDATA4.to_u8()); + this.push((n % 0x100) as u8); + this.push(((n / 0x100) % 0x100) as u8); + this.push(((n / 0x10000) % 0x100) as u8); + this.push((n / 0x1000000) as u8); } } // Then push the raw bytes - self.0.extend_from_slice(data.as_bytes()); + this.extend_from_slice(data.as_bytes()); } /// Computes the sum of `len` and the length of an appropriate push opcode. @@ -200,7 +209,7 @@ impl ScriptBuf { pub(crate) fn push_verify(&mut self, last_opcode: Option) { match opcode_to_verify(last_opcode) { Some(opcode) => { - self.0.pop(); + self.as_byte_vec().pop(); self.push_opcode(opcode); } None => self.push_opcode(OP_VERIFY), @@ -254,3 +263,31 @@ impl<'a> Extend> for ScriptBuf { } } } + +/// Pretends that this is a mutable reference to [`ScriptBuf`]'s internal buffer. +/// +/// In reality the backing `Vec` is swapped with an empty one and this is holding both the +/// reference and the vec. The vec is put back when this drops so it also covers paics. (But not +/// leaks, which is OK since we never leak.) +pub(crate) struct ScriptBufAsVec<'a>(&'a mut ScriptBuf, Vec); + +impl<'a> core::ops::Deref for ScriptBufAsVec<'a> { + type Target = Vec; + + fn deref(&self) -> &Self::Target { + &self.1 + } +} + +impl<'a> core::ops::DerefMut for ScriptBufAsVec<'a> { + fn deref_mut(&mut self) -> &mut Self::Target { + &mut self.1 + } +} + +impl<'a> Drop for ScriptBufAsVec<'a> { + fn drop(&mut self) { + let vec = core::mem::take(&mut self.1); + *(self.0) = ScriptBuf::from_bytes(vec); + } +}