bitcoin_primitives/script/
builder.rs1use core::fmt;
4
5use super::{PushBytes, Script, ScriptBuf};
6use crate::opcodes::Opcode;
7use crate::prelude::Vec;
8
9#[derive(PartialEq, Eq, Clone)]
16pub struct Builder<T>(ScriptBuf<T>);
17
18impl<T> Builder<T> {
19 #[inline]
21 pub const fn new() -> Self { Self(ScriptBuf::new()) }
22
23 #[must_use]
35 pub fn push_slice<D: AsRef<PushBytes>>(mut self, data: D) -> Self {
36 self.0.push_slice(data);
37 self
38 }
39
40 #[must_use]
46 pub fn push_slice_non_minimal<D: AsRef<PushBytes>>(mut self, data: D) -> Self {
47 self.0.push_slice_non_minimal(data);
48 self
49 }
50
51 #[must_use]
53 pub fn push_opcode(mut self, data: Opcode) -> Self {
54 self.0.push_opcode(data);
55 self
56 }
57
58 pub fn into_script(self) -> ScriptBuf<T> { self.0 }
60
61 pub fn as_script(&self) -> &Script<T> { &self.0 }
63}
64
65impl<T> Default for Builder<T> {
66 fn default() -> Self { Self::new() }
67}
68
69impl<T> From<Vec<u8>> for Builder<T> {
71 fn from(v: Vec<u8>) -> Self {
72 let script = ScriptBuf::from(v);
73 Self(script)
74 }
75}
76
77impl<T> fmt::Display for Builder<T> {
78 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { fmt::Display::fmt(&self.0, f) }
79}
80
81impl<T> fmt::Debug for Builder<T> {
82 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
83 f.debug_tuple("Builder").field(&self.0).finish()
84 }
85}
86
87#[cfg(test)]
88mod tests {
89 use alloc::{format, vec};
90
91 use super::Builder;
92 use crate::script::{PushBytes, ScriptSigTag as Tag};
93
94 #[test]
95 fn push_slice_minimal() {
96 let script = Builder::<Tag>::new().push_slice([0x81]).into_script();
97 assert_eq!(script.as_bytes(), &[0x4f]);
98
99 for n in 1u8..=16 {
100 let script = Builder::<Tag>::new().push_slice([n]).into_script();
101 assert_eq!(script.as_bytes(), &[0x50 + n]);
102 }
103
104 let script = Builder::<Tag>::new().push_slice([0u8]).into_script();
105 assert_eq!(script.as_bytes(), &[1, 0]);
106 let script = Builder::<Tag>::new().push_slice([17u8]).into_script();
107 assert_eq!(script.as_bytes(), &[1, 17]);
108 let script = Builder::<Tag>::new().push_slice(b"NRA4VR").into_script();
109 assert_eq!(script.as_bytes(), &[6, b'N', b'R', b'A', b'4', b'V', b'R']);
110 }
111
112 #[test]
113 fn push_slice_non_minimal() {
114 let script = Builder::<Tag>::new().push_slice_non_minimal([0x81]).into_script();
115 assert_eq!(script.as_bytes(), &[1, 0x81]);
116
117 let script = Builder::<Tag>::new().push_slice_non_minimal([1u8]).into_script();
118 assert_eq!(script.as_bytes(), &[1, 1]);
119 }
120
121 #[test]
122 fn push_slice_pushdata1_and_pushdata2() {
123 let script = Builder::<Tag>::new()
124 .push_slice(<&PushBytes>::try_from([0xab; 0x4b].as_slice()).unwrap())
125 .into_script();
126 assert_eq!(script.as_bytes()[0], 0x4b);
127 assert_eq!(script.len(), 1 + 0x4b);
128
129 let script = Builder::<Tag>::new()
130 .push_slice(<&PushBytes>::try_from([0xab; 0x4c].as_slice()).unwrap())
131 .into_script();
132 assert_eq!(&script.as_bytes()[..2], &[0x4c, 0x4c]);
133 assert_eq!(script.len(), 2 + 0x4c);
134
135 let script = Builder::<Tag>::new()
136 .push_slice(<&PushBytes>::try_from([0xab; 0xff].as_slice()).unwrap())
137 .into_script();
138 assert_eq!(&script.as_bytes()[..2], &[0x4c, 0xff]);
139 assert_eq!(script.len(), 2 + 0xff);
140
141 let script = Builder::<Tag>::new()
142 .push_slice(<&PushBytes>::try_from([0xab; 0x100].as_slice()).unwrap())
143 .into_script();
144 assert_eq!(&script.as_bytes()[..3], &[0x4d, 0x00, 0x01]);
145 assert_eq!(script.len(), 3 + 0x100);
146
147 let script = Builder::<Tag>::new()
148 .push_slice(<&PushBytes>::try_from([0xab; 0x102].as_slice()).unwrap())
149 .into_script();
150 assert_eq!(&script.as_bytes()[..3], &[0x4d, 0x02, 0x01]);
151 assert_eq!(script.len(), 3 + 0x102);
152
153 let script = Builder::<Tag>::new()
154 .push_slice(<&PushBytes>::try_from(vec![0xab; 0xffff].as_slice()).unwrap())
155 .into_script();
156 assert_eq!(&script.as_bytes()[..3], &[0x4d, 0xff, 0xff]);
157 assert_eq!(script.len(), 3 + 0xffff);
158 }
159
160 #[test]
161 #[cfg_attr(miri, ignore)]
162 fn push_slice_pushdata4_boundary() {
163 let script = Builder::<Tag>::new()
164 .push_slice(<&PushBytes>::try_from(vec![0u8; 0x10000].as_slice()).unwrap())
165 .into_script();
166 assert_eq!(&script.as_bytes()[..5], &[0x4e, 0x00, 0x00, 0x01, 0x00]);
167 assert_eq!(script.len(), 5 + 0x10000);
168 }
169
170 #[test]
171 #[cfg(target_pointer_width = "64")]
172 #[cfg_attr(miri, ignore)]
173 fn push_slice_pushdata4_length_bytes() {
174 let len = 0x0102_0304;
175 let script = Builder::<Tag>::new()
176 .push_slice(<&PushBytes>::try_from(vec![0u8; len].as_slice()).unwrap())
177 .into_script();
178 assert_eq!(&script.as_bytes()[..5], &[0x4e, 0x04, 0x03, 0x02, 0x01]);
179 assert_eq!(script.len(), 5 + len);
180 }
181
182 #[test]
183 fn from_vec() {
184 let script = Builder::<Tag>::from(vec![0xac, 0x51]).into_script();
185 assert_eq!(script.as_bytes(), &[0xac, 0x51]);
186 }
187
188 #[test]
189 fn display_delegates_to_script() {
190 let builder = Builder::<Tag>::from(vec![0x51, 0x52]);
191 let displayed = format!("{}", builder);
192 assert!(!displayed.is_empty());
193 assert_eq!(displayed, format!("{}", builder.as_script()));
194 }
195}