// SPDX-License-Identifier: CC0-1.0 use core::fmt; use super::{opcode_to_verify, write_scriptint, Error, PushBytes, Script, ScriptBuf}; use crate::locktime::absolute; use crate::opcodes::all::*; use crate::opcodes::Opcode; use crate::prelude::Vec; use crate::script::{ScriptBufExt as _, ScriptBufExtPriv as _, ScriptExtPriv as _}; use crate::Sequence; /// An Object which can be used to construct a script piece by piece. #[derive(PartialEq, Eq, Clone)] pub struct Builder(ScriptBuf, Option); impl Builder { /// Constructs a new empty script. #[inline] pub const fn new() -> Self { Builder(ScriptBuf::new(), None) } /// Returns the length in bytes of the script. pub fn len(&self) -> usize { self.0.len() } /// Checks whether the script is the empty script. pub fn is_empty(&self) -> bool { self.0.is_empty() } /// Adds instructions to push an integer onto the stack. /// /// Integers are encoded as little-endian signed-magnitude numbers, but there are dedicated /// opcodes to push some small integers. /// /// # Errors /// /// Only errors if `data == i32::MIN` (CScriptNum cannot have value -2^31). pub fn push_int(self, n: i32) -> Result { if n == i32::MIN { // ref: https://github.com/bitcoin/bitcoin/blob/cac846c2fbf6fc69bfc288fd387aa3f68d84d584/src/script/script.h#L230 Err(Error::NumericOverflow) } else { Ok(self.push_int_unchecked(n.into())) } } /// Adds instructions to push an unchecked integer onto the stack. /// /// Integers are encoded as little-endian signed-magnitude numbers, but there are dedicated /// opcodes to push some small integers. /// /// This function implements `CScript::push_int64` from Core `script.h`. /// /// > Numeric opcodes (OP_1ADD, etc) are restricted to operating on 4-byte integers. /// > The semantics are subtle, though: operands must be in the range [-2^31 +1...2^31 -1], /// > but results may overflow (and are valid as long as they are not used in a subsequent /// > numeric operation). CScriptNum enforces those semantics by storing results as /// > an int64 and allowing out-of-range values to be returned as a vector of bytes but /// > throwing an exception if arithmetic is done or the result is interpreted as an integer. /// /// Does not check whether `n` is in the range of [-2^31 +1...2^31 -1]. pub fn push_int_unchecked(self, n: i64) -> Builder { match n { -1 => self.push_opcode(OP_PUSHNUM_NEG1), 0 => self.push_opcode(OP_PUSHBYTES_0), 1..=16 => self.push_opcode(Opcode::from(n as u8 + (OP_PUSHNUM_1.to_u8() - 1))), _ => self.push_int_non_minimal(n), } } /// Adds instructions to push an integer onto the stack without optimization. /// /// This uses the explicit encoding regardless of the availability of dedicated opcodes. 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(&<&PushBytes>::from(&buf)[..len]) } /// Adds instructions to push some arbitrary data onto the stack. pub fn push_slice>(mut self, data: T) -> Builder { self.0.push_slice(data); self.1 = None; self } /// Adds a single opcode to the script. pub fn push_opcode(mut self, data: Opcode) -> Builder { self.0.push_opcode(data); self.1 = Some(data); self } /// Adds an `OP_VERIFY` to the script or replaces the last opcode with VERIFY form. /// /// Some opcodes such as `OP_CHECKSIG` have a verify variant that works as if `VERIFY` was /// in the script right after. To save space this function appends `VERIFY` only if /// the most-recently-added opcode *does not* have an alternate `VERIFY` form. If it does /// the last opcode is replaced. E.g., `OP_CHECKSIG` will become `OP_CHECKSIGVERIFY`. /// /// Note that existing `OP_*VERIFY` opcodes do not lead to the instruction being ignored /// because `OP_VERIFY` consumes an item from the stack so ignoring them would change the /// semantics. pub fn push_verify(mut self) -> Builder { // "duplicated code" because we need to update `1` field match opcode_to_verify(self.1) { Some(opcode) => { (self.0).as_byte_vec().pop(); self.push_opcode(opcode) } None => self.push_opcode(OP_VERIFY), } } /// Adds instructions to push an absolute lock time onto the stack. pub fn push_lock_time(self, lock_time: absolute::LockTime) -> Builder { self.push_int_unchecked(lock_time.to_consensus_u32().into()) } /// Adds instructions to push a sequence number onto the stack. pub fn push_sequence(self, sequence: Sequence) -> Builder { self.push_int_unchecked(sequence.to_consensus_u32().into()) } /// Converts the `Builder` into `ScriptBuf`. pub fn into_script(self) -> ScriptBuf { self.0 } /// Converts the `Builder` into script bytes pub fn into_bytes(self) -> Vec { self.0.into() } /// Returns the internal script pub fn as_script(&self) -> &Script { &self.0 } /// Returns script bytes pub fn as_bytes(&self) -> &[u8] { self.0.as_bytes() } } impl Default for Builder { fn default() -> Builder { Builder::new() } } /// Constructs a new builder from an existing vector. impl From> for Builder { fn from(v: Vec) -> Builder { let script = ScriptBuf::from(v); let last_op = script.last_opcode(); Builder(script, last_op) } } impl fmt::Display for Builder { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { fmt::Display::fmt(&self.0, f) } } impl fmt::Debug for Builder { fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> { fmt::Display::fmt(self, f) } }