bitcoin_embed/
envelope.rs1use {
6 bitcoin::{
7 Script,
8 blockdata::{
9 constants::MAX_SCRIPT_ELEMENT_SIZE,
10 opcodes,
11 script::{
12 Instruction::{self, Op, PushBytes},
13 Instructions,
14 },
15 },
16 script::{Builder, Error, PushBytes as ScriptPushBytes},
17 },
18 std::iter::Peekable,
19};
20
21type Result<T> = std::result::Result<T, Error>;
22
23pub type Envelope = Vec<Vec<u8>>;
25
26pub fn append_to_builder(envelope: Envelope, mut builder: Builder) -> Builder {
28 builder = builder
29 .push_opcode(opcodes::OP_FALSE)
30 .push_opcode(opcodes::all::OP_IF);
31
32 for bytes in envelope {
33 for chunk in bytes.chunks(MAX_SCRIPT_ELEMENT_SIZE) {
34 builder = builder.push_slice::<&ScriptPushBytes>(chunk.try_into().unwrap());
35 }
36 }
37
38 builder.push_opcode(opcodes::all::OP_ENDIF)
39}
40
41pub fn append_bytes_to_builder(bytes: &[u8], builder: Builder) -> Builder {
43 append_to_builder(vec![bytes.to_vec()], builder)
44}
45
46pub fn from_script(script: &Script) -> Vec<Envelope> {
48 let mut envelopes = Vec::new();
49
50 let mut instructions = script.instructions().peekable();
51
52 while let Ok(Some(instruction)) = instructions.next().transpose() {
53 if instruction == PushBytes((&[]).into()) {
54 if let Ok(Some(envelope)) = from_instructions(&mut instructions) {
55 envelopes.push(envelope);
56 }
57 }
58 }
59
60 envelopes
61}
62
63fn accept(instructions: &mut Peekable<Instructions>, instruction: Instruction) -> Result<bool> {
64 if instructions.peek() == Some(&Ok(instruction)) {
65 instructions.next().transpose()?;
66 Ok(true)
67 } else {
68 Ok(false)
69 }
70}
71
72fn from_instructions(instructions: &mut Peekable<Instructions>) -> Result<Option<Envelope>> {
73 if !accept(instructions, Op(opcodes::all::OP_IF))? {
74 return Ok(None);
75 }
76
77 let mut payload = Vec::new();
78
79 loop {
80 match instructions.next().transpose()? {
81 None => return Ok(None),
82 Some(Op(opcodes::all::OP_ENDIF)) => {
83 return Ok(Some(payload));
84 }
85 Some(Op(opcodes::all::OP_PUSHNUM_NEG1)) => {
86 payload.push(vec![0x81]);
87 }
88 Some(Op(opcodes::all::OP_PUSHNUM_1)) => {
89 payload.push(vec![1]);
90 }
91 Some(Op(opcodes::all::OP_PUSHNUM_2)) => {
92 payload.push(vec![2]);
93 }
94 Some(Op(opcodes::all::OP_PUSHNUM_3)) => {
95 payload.push(vec![3]);
96 }
97 Some(Op(opcodes::all::OP_PUSHNUM_4)) => {
98 payload.push(vec![4]);
99 }
100 Some(Op(opcodes::all::OP_PUSHNUM_5)) => {
101 payload.push(vec![5]);
102 }
103 Some(Op(opcodes::all::OP_PUSHNUM_6)) => {
104 payload.push(vec![6]);
105 }
106 Some(Op(opcodes::all::OP_PUSHNUM_7)) => {
107 payload.push(vec![7]);
108 }
109 Some(Op(opcodes::all::OP_PUSHNUM_8)) => {
110 payload.push(vec![8]);
111 }
112 Some(Op(opcodes::all::OP_PUSHNUM_9)) => {
113 payload.push(vec![9]);
114 }
115 Some(Op(opcodes::all::OP_PUSHNUM_10)) => {
116 payload.push(vec![10]);
117 }
118 Some(Op(opcodes::all::OP_PUSHNUM_11)) => {
119 payload.push(vec![11]);
120 }
121 Some(Op(opcodes::all::OP_PUSHNUM_12)) => {
122 payload.push(vec![12]);
123 }
124 Some(Op(opcodes::all::OP_PUSHNUM_13)) => {
125 payload.push(vec![13]);
126 }
127 Some(Op(opcodes::all::OP_PUSHNUM_14)) => {
128 payload.push(vec![14]);
129 }
130 Some(Op(opcodes::all::OP_PUSHNUM_15)) => {
131 payload.push(vec![15]);
132 }
133 Some(Op(opcodes::all::OP_PUSHNUM_16)) => {
134 payload.push(vec![16]);
135 }
136 Some(PushBytes(push)) => {
137 payload.push(push.as_bytes().to_vec());
138 }
139 Some(_) => return Ok(None),
140 }
141 }
142}
143
144#[cfg(test)]
145mod tests {
146 use super::*;
147
148 #[test]
149 fn test_empty_script() {
150 let script = Script::new();
151 assert_eq!(from_script(script), Vec::<Envelope>::new());
152 }
153
154 #[test]
155 fn test_script_without_envelopes() {
156 let script = Builder::new()
157 .push_opcode(opcodes::OP_FALSE)
158 .push_opcode(opcodes::all::OP_CHECKSIG)
159 .into_script();
160
161 assert_eq!(from_script(&script), Vec::<Envelope>::new());
162 }
163
164 #[test]
165 fn test_single_empty_envelope() {
166 let script = Builder::new()
167 .push_opcode(opcodes::OP_FALSE)
168 .push_opcode(opcodes::all::OP_IF)
169 .push_opcode(opcodes::all::OP_ENDIF)
170 .into_script();
171
172 assert_eq!(from_script(&script), vec![Vec::<Vec<u8>>::new()]);
173 }
174
175 #[test]
176 fn test_envelope_with_single_push() {
177 let data = b"test data";
178
179 let script = Builder::new()
180 .push_opcode(opcodes::OP_FALSE)
181 .push_opcode(opcodes::all::OP_IF)
182 .push_slice(data)
183 .push_opcode(opcodes::all::OP_ENDIF)
184 .into_script();
185
186 assert_eq!(from_script(&script), vec![vec![data.to_vec()]]);
187 }
188
189 #[test]
190 fn test_envelope_with_multiple_pushes() {
191 let data1 = b"first";
192 let data2 = b"second";
193
194 let script = Builder::new()
195 .push_opcode(opcodes::OP_FALSE)
196 .push_opcode(opcodes::all::OP_IF)
197 .push_slice(data1)
198 .push_slice(data2)
199 .push_opcode(opcodes::all::OP_ENDIF)
200 .into_script();
201
202 assert_eq!(
203 from_script(&script),
204 vec![vec![data1.to_vec(), data2.to_vec()]]
205 );
206 }
207
208 #[test]
209 fn test_multiple_envelopes() {
210 let data1 = b"envelope1";
211 let data2 = b"envelope2";
212
213 let mut builder = Builder::new();
214
215 builder = builder
216 .push_opcode(opcodes::OP_FALSE)
218 .push_opcode(opcodes::all::OP_IF)
219 .push_slice(data1)
220 .push_opcode(opcodes::all::OP_ENDIF)
221 .push_opcode(opcodes::OP_FALSE)
223 .push_opcode(opcodes::all::OP_IF)
224 .push_slice(data2)
225 .push_opcode(opcodes::all::OP_ENDIF);
226
227 let script = builder.into_script();
228
229 assert_eq!(
230 from_script(&script),
231 vec![vec![data1.to_vec()], vec![data2.to_vec()]]
232 );
233 }
234
235 #[test]
236 fn test_pushnum_opcodes() {
237 let pushnums = [
238 (opcodes::all::OP_PUSHNUM_NEG1, vec![0x81]),
239 (opcodes::all::OP_PUSHNUM_1, vec![1]),
240 (opcodes::all::OP_PUSHNUM_2, vec![2]),
241 (opcodes::all::OP_PUSHNUM_16, vec![16]),
242 ];
243
244 for (opcode, expected) in pushnums {
245 let script = Builder::new()
246 .push_opcode(opcodes::OP_FALSE)
247 .push_opcode(opcodes::all::OP_IF)
248 .push_opcode(opcode)
249 .push_opcode(opcodes::all::OP_ENDIF)
250 .into_script();
251
252 assert_eq!(from_script(&script), vec![vec![expected]]);
253 }
254 }
255
256 #[test]
257 fn test_large_data_chunking() {
258 let large_data = vec![0xaa; 100_000];
259
260 let builder = Builder::new();
261 let builder = append_bytes_to_builder(&large_data, builder);
262 let script = builder.into_script();
263
264 let extracted = from_script(&script);
265 assert_eq!(extracted.len(), 1);
266
267 let flattened: Vec<u8> = extracted[0].iter().flatten().cloned().collect();
268 assert_eq!(flattened, large_data);
269 }
270
271 #[test]
272 fn test_append_to_builder() {
273 let envelope = vec![vec![1, 2, 3], vec![4, 5, 6]];
274
275 let builder = Builder::new();
276 let builder = append_to_builder(envelope.clone(), builder);
277 let script = builder.into_script();
278
279 assert_eq!(from_script(&script), vec![envelope]);
280 }
281
282 #[test]
283 fn test_append_bytes_to_builder() {
284 let data = b"test data";
285
286 let builder = Builder::new();
287 let builder = append_bytes_to_builder(data, builder);
288 let script = builder.into_script();
289
290 assert_eq!(from_script(&script), vec![vec![data.to_vec()]]);
291 }
292
293 #[test]
294 fn test_nested_invalid_instructions() {
295 let script = Builder::new()
296 .push_opcode(opcodes::OP_FALSE)
297 .push_opcode(opcodes::all::OP_IF)
298 .push_slice([0x01]) .push_opcode(opcodes::all::OP_IF) .push_opcode(opcodes::all::OP_ENDIF)
301 .into_script();
302
303 assert_eq!(from_script(&script), Vec::<Envelope>::new());
304 }
305
306 #[test]
307 fn test_incomplete_envelope() {
308 let script = Builder::new()
309 .push_opcode(opcodes::OP_FALSE)
310 .push_opcode(opcodes::all::OP_IF)
311 .push_slice(b"data")
312 .into_script();
314
315 assert_eq!(from_script(&script), Vec::<Envelope>::new());
316 }
317
318 #[test]
319 fn test_surrounding_opcodes() {
320 let data = b"test data";
321
322 let script = Builder::new()
323 .push_opcode(opcodes::all::OP_DUP)
324 .push_opcode(opcodes::OP_FALSE)
325 .push_opcode(opcodes::all::OP_IF)
326 .push_slice(data)
327 .push_opcode(opcodes::all::OP_ENDIF)
328 .push_opcode(opcodes::all::OP_EQUALVERIFY)
329 .into_script();
330
331 assert_eq!(from_script(&script), vec![vec![data.to_vec()]]);
332 }
333
334 #[test]
335 fn test_roundtrip() {
336 let original_data = vec![vec![1, 2, 3], vec![4, 5, 6], vec![7, 8, 9]];
337
338 let builder = Builder::new();
339 let builder = append_to_builder(original_data.clone(), builder);
340 let script = builder.into_script();
341
342 let extracted = from_script(&script);
343 assert_eq!(extracted, vec![original_data]);
344 }
345}