bitcoin/blockdata/script/
builder.rs

1// SPDX-License-Identifier: CC0-1.0
2
3use core::fmt;
4
5use secp256k1::XOnlyPublicKey;
6
7use crate::blockdata::locktime::absolute;
8use crate::blockdata::opcodes::all::*;
9use crate::blockdata::opcodes::{self, Opcode};
10use crate::blockdata::script::{opcode_to_verify, write_scriptint, PushBytes, Script, ScriptBuf};
11use crate::blockdata::transaction::Sequence;
12use crate::key::PublicKey;
13use crate::prelude::*;
14
15/// An Object which can be used to construct a script piece by piece.
16#[derive(PartialEq, Eq, Clone)]
17pub struct Builder(ScriptBuf, Option<Opcode>);
18
19impl Builder {
20    /// Creates a new empty script.
21    #[inline]
22    pub const fn new() -> Self { Builder(ScriptBuf::new(), None) }
23
24    /// Returns the length in bytes of the script.
25    pub fn len(&self) -> usize { self.0.len() }
26
27    /// Checks whether the script is the empty script.
28    pub fn is_empty(&self) -> bool { self.0.is_empty() }
29
30    /// Adds instructions to push an integer onto the stack.
31    ///
32    /// Integers are encoded as little-endian signed-magnitude numbers, but there are dedicated
33    /// opcodes to push some small integers.
34    pub fn push_int(self, data: i64) -> Builder {
35        // We can special-case -1, 1-16
36        if data == -1 || (1..=16).contains(&data) {
37            let opcode = Opcode::from((data - 1 + opcodes::OP_TRUE.to_u8() as i64) as u8);
38            self.push_opcode(opcode)
39        }
40        // We can also special-case zero
41        else if data == 0 {
42            self.push_opcode(opcodes::OP_0)
43        }
44        // Otherwise encode it as data
45        else {
46            self.push_int_non_minimal(data)
47        }
48    }
49
50    /// Adds instructions to push an integer onto the stack without optimization.
51    ///
52    /// This uses the explicit encoding regardless of the availability of dedicated opcodes.
53    pub(in crate::blockdata) fn push_int_non_minimal(self, data: i64) -> Builder {
54        let mut buf = [0u8; 8];
55        let len = write_scriptint(&mut buf, data);
56        self.push_slice(&<&PushBytes>::from(&buf)[..len])
57    }
58
59    /// Adds instructions to push some arbitrary data onto the stack.
60    pub fn push_slice<T: AsRef<PushBytes>>(mut self, data: T) -> Builder {
61        self.0.push_slice(data);
62        self.1 = None;
63        self
64    }
65
66    /// Adds instructions to push a public key onto the stack.
67    pub fn push_key(self, key: &PublicKey) -> Builder {
68        if key.compressed {
69            self.push_slice(key.inner.serialize())
70        } else {
71            self.push_slice(key.inner.serialize_uncompressed())
72        }
73    }
74
75    /// Adds instructions to push an XOnly public key onto the stack.
76    pub fn push_x_only_key(self, x_only_key: &XOnlyPublicKey) -> Builder {
77        self.push_slice(x_only_key.serialize())
78    }
79
80    /// Adds a single opcode to the script.
81    pub fn push_opcode(mut self, data: Opcode) -> Builder {
82        self.0.push_opcode(data);
83        self.1 = Some(data);
84        self
85    }
86
87    /// Adds an `OP_VERIFY` to the script or replaces the last opcode with VERIFY form.
88    ///
89    /// Some opcodes such as `OP_CHECKSIG` have a verify variant that works as if `VERIFY` was
90    /// in the script right after. To save space this function appends `VERIFY` only if
91    /// the most-recently-added opcode *does not* have an alternate `VERIFY` form. If it does
92    /// the last opcode is replaced. E.g., `OP_CHECKSIG` will become `OP_CHECKSIGVERIFY`.
93    ///
94    /// Note that existing `OP_*VERIFY` opcodes do not lead to the instruction being ignored
95    /// because `OP_VERIFY` consumes an item from the stack so ignoring them would change the
96    /// semantics.
97    pub fn push_verify(mut self) -> Builder {
98        // "duplicated code" because we need to update `1` field
99        match opcode_to_verify(self.1) {
100            Some(opcode) => {
101                (self.0).0.pop();
102                self.push_opcode(opcode)
103            }
104            None => self.push_opcode(OP_VERIFY),
105        }
106    }
107
108    /// Adds instructions to push an absolute lock time onto the stack.
109    pub fn push_lock_time(self, lock_time: absolute::LockTime) -> Builder {
110        self.push_int(lock_time.to_consensus_u32().into())
111    }
112
113    /// Adds instructions to push a sequence number onto the stack.
114    pub fn push_sequence(self, sequence: Sequence) -> Builder {
115        self.push_int(sequence.to_consensus_u32().into())
116    }
117
118    /// Converts the `Builder` into `ScriptBuf`.
119    pub fn into_script(self) -> ScriptBuf { self.0 }
120
121    /// Converts the `Builder` into script bytes
122    pub fn into_bytes(self) -> Vec<u8> { self.0.into() }
123
124    /// Returns the internal script
125    pub fn as_script(&self) -> &Script { &self.0 }
126
127    /// Returns script bytes
128    pub fn as_bytes(&self) -> &[u8] { self.0.as_bytes() }
129}
130
131impl Default for Builder {
132    fn default() -> Builder { Builder::new() }
133}
134
135/// Creates a new builder from an existing vector.
136impl From<Vec<u8>> for Builder {
137    fn from(v: Vec<u8>) -> Builder {
138        let script = ScriptBuf::from(v);
139        let last_op = script.last_opcode();
140        Builder(script, last_op)
141    }
142}
143
144impl fmt::Display for Builder {
145    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { self.0.fmt_asm(f) }
146}
147
148internals::debug_from_display!(Builder);