1use std::iter::once;
2
3use crate::{
4 data_stack::OpcodeData,
5 opcodes::{codes::*, OP_1_NEGATE_VAL, OP_DATA_MAX_VAL, OP_DATA_MIN_VAL, OP_SMALL_INT_MAX_VAL},
6 MAX_SCRIPTS_SIZE, MAX_SCRIPT_ELEMENT_SIZE,
7};
8use hexplay::{HexView, HexViewBuilder};
9use thiserror::Error;
10
11const DEFAULT_SCRIPT_ALLOC: usize = 512;
17
18#[derive(Error, PartialEq, Eq, Debug, Clone, Copy)]
19pub enum ScriptBuilderError {
20 #[error("adding opcode {0} would exceed the maximum allowed canonical script length of {MAX_SCRIPTS_SIZE}")]
21 OpCodeRejected(u8),
22
23 #[error("adding {0} opcodes would exceed the maximum allowed canonical script length of {MAX_SCRIPTS_SIZE}")]
24 OpCodesRejected(usize),
25
26 #[error("adding {0} bytes of data would exceed the maximum allowed canonical script length of {MAX_SCRIPTS_SIZE}")]
27 DataRejected(usize),
28
29 #[error("adding a data element of {0} bytes exceed the maximum allowed script element size of {MAX_SCRIPT_ELEMENT_SIZE}")]
30 ElementExceedsMaxSize(usize),
31
32 #[error("adding integer {0} would exceed the maximum allowed canonical script length of {MAX_SCRIPTS_SIZE}")]
33 IntegerRejected(i64),
34}
35pub type ScriptBuilderResult<T> = std::result::Result<T, ScriptBuilderError>;
36
37pub struct ScriptBuilder {
61 script: Vec<u8>,
62}
63
64impl ScriptBuilder {
65 pub fn new() -> Self {
66 Self { script: Vec::with_capacity(DEFAULT_SCRIPT_ALLOC) }
67 }
68
69 pub fn script(&self) -> &[u8] {
70 &self.script
71 }
72
73 #[cfg(any(test, target_arch = "wasm32"))]
74 pub fn extend(&mut self, data: &[u8]) {
75 self.script.extend(data);
76 }
77
78 pub fn drain(&mut self) -> Vec<u8> {
79 std::mem::take(&mut self.script)
84 }
85
86 pub fn add_op(&mut self, opcode: u8) -> ScriptBuilderResult<&mut Self> {
90 if self.script.len() >= MAX_SCRIPTS_SIZE {
93 return Err(ScriptBuilderError::OpCodeRejected(opcode));
94 }
95
96 self.script.push(opcode);
97 Ok(self)
98 }
99
100 pub fn add_ops(&mut self, opcodes: &[u8]) -> ScriptBuilderResult<&mut Self> {
101 if self.script.len() + opcodes.len() > MAX_SCRIPTS_SIZE {
104 return Err(ScriptBuilderError::OpCodesRejected(opcodes.len()));
105 }
106
107 self.script.extend_from_slice(opcodes);
108 Ok(self)
109 }
110
111 pub fn canonical_data_size(data: &[u8]) -> usize {
113 let data_len = data.len();
114
115 if data_len == 0 || (data_len == 1 && (data[0] <= OP_SMALL_INT_MAX_VAL || data[0] == OP_1_NEGATE_VAL)) {
119 return 1;
120 }
121
122 data_len
123 + if data_len <= OP_DATA_MAX_VAL as usize {
124 1 } else if data_len <= u8::MAX as usize {
126 2 } else if data_len <= u16::MAX as usize {
128 3 } else {
130 5 }
132 }
133
134 fn add_raw_data(&mut self, data: &[u8]) -> &mut Self {
139 let data_len = data.len();
140
141 if data_len == 0 || (data_len == 1 && data[0] == 0) {
145 self.script.push(Op0);
146 return self;
147 } else if data_len == 1 && data[0] <= OP_SMALL_INT_MAX_VAL {
148 self.script.push((Op1 - 1) + data[0]);
149 return self;
150 } else if data_len == 1 && data[0] == OP_1_NEGATE_VAL {
151 self.script.push(Op1Negate);
152 return self;
153 }
154
155 if data_len <= OP_DATA_MAX_VAL as usize {
160 self.script.push((OP_DATA_MIN_VAL - 1) + data_len as u8);
161 } else if data_len <= u8::MAX as usize {
162 self.script.extend(once(OpPushData1).chain(once(data_len as u8)));
163 } else if data_len <= u16::MAX as usize {
164 self.script.extend(once(OpPushData2).chain((data_len as u16).to_le_bytes()));
165 } else {
166 self.script.extend(once(OpPushData4).chain((data_len as u32).to_le_bytes()));
167 }
168
169 self.script.extend(data);
171 self
172 }
173
174 #[cfg(test)]
182 pub fn add_data_unchecked(&mut self, data: &[u8]) -> &mut Self {
183 self.add_raw_data(data)
184 }
185
186 pub fn add_data(&mut self, data: &[u8]) -> ScriptBuilderResult<&mut Self> {
196 let data_size = Self::canonical_data_size(data);
199
200 if self.script.len() + data_size > MAX_SCRIPTS_SIZE {
201 return Err(ScriptBuilderError::DataRejected(data_size));
202 }
203
204 let data_len = data.len();
207 if data_len > MAX_SCRIPT_ELEMENT_SIZE {
208 return Err(ScriptBuilderError::ElementExceedsMaxSize(data_len));
209 }
210
211 Ok(self.add_raw_data(data))
212 }
213
214 pub fn add_i64(&mut self, val: i64) -> ScriptBuilderResult<&mut Self> {
215 if self.script.len() + 1 > MAX_SCRIPTS_SIZE {
218 return Err(ScriptBuilderError::IntegerRejected(val));
219 }
220
221 if val == 0 {
223 self.script.push(Op0);
224 return Ok(self);
225 }
226 if val == -1 || (1..=16).contains(&val) {
227 self.script.push(((Op1 as i64 - 1) + val) as u8);
228 return Ok(self);
229 }
230
231 let bytes: Vec<_> = OpcodeData::serialize(&val);
232 self.add_data(&bytes)
233 }
234
235 pub fn add_lock_time(&mut self, lock_time: u64) -> ScriptBuilderResult<&mut Self> {
237 self.add_u64(lock_time)
238 }
239
240 pub fn add_sequence(&mut self, sequence: u64) -> ScriptBuilderResult<&mut Self> {
242 self.add_u64(sequence)
243 }
244
245 fn add_u64(&mut self, val: u64) -> ScriptBuilderResult<&mut Self> {
247 let buffer: [u8; 8] = val.to_le_bytes();
248 let trimmed_size = 8 - buffer.iter().rev().position(|x| *x != 0u8).unwrap_or(8);
249 let trimmed = &buffer[0..trimmed_size];
250 self.add_data(trimmed)
251 }
252
253 pub fn hex_view_builder(&self) -> HexViewBuilder<'_> {
255 HexViewBuilder::new(&self.script)
256 }
257
258 pub fn hex_view(&self, offset: usize, width: usize) -> HexView<'_> {
260 HexViewBuilder::new(&self.script).address_offset(offset).row_width(width).finish()
261 }
262}
263
264impl Default for ScriptBuilder {
265 fn default() -> Self {
266 Self::new()
267 }
268}
269
270#[cfg(test)]
271mod tests {
272 use super::*;
273 use std::iter::{once, repeat};
274
275 #[test]
277 fn test_add_op() {
278 struct Test {
279 name: &'static str,
280 opcodes: Vec<u8>,
281 expected: Vec<u8>,
282 }
283
284 let tests = vec![
285 Test { name: "push OP_FALSE", opcodes: vec![OpFalse], expected: vec![OpFalse] },
286 Test { name: "push OP_TRUE", opcodes: vec![OpTrue], expected: vec![OpTrue] },
287 Test { name: "push OP_0", opcodes: vec![Op0], expected: vec![Op0] },
288 Test { name: "push OP_1 OP_2", opcodes: vec![Op1, Op2], expected: vec![Op1, Op2] },
289 Test { name: "push OP_BLAKE2B OP_EQUAL", opcodes: vec![OpBlake2b, OpEqual], expected: vec![OpBlake2b, OpEqual] },
290 ];
291
292 for test in tests.iter() {
294 let mut builder = ScriptBuilder::new();
295 test.opcodes.iter().for_each(|opcode| {
296 builder.add_op(*opcode).expect("the script is canonical");
297 });
298 let result = builder.script();
299 assert_eq!(result, &test.expected, "{} wrong result using add_op", test.name);
300 }
301
302 for test in tests.iter() {
304 let mut builder = ScriptBuilder::new();
305 let result = builder.add_ops(&test.opcodes).expect("the script is canonical").script();
306 assert_eq!(result, &test.expected, "{} wrong result using add_ops", test.name);
307 }
308 }
309
310 #[test]
312 fn test_add_i64() {
313 struct Test {
314 name: &'static str,
315 val: i64,
316 expected: Vec<u8>,
317 }
318
319 let tests = vec![
320 Test { name: "push -1", val: -1, expected: vec![Op1Negate] },
321 Test { name: "push small int 0", val: 0, expected: vec![Op0] },
322 Test { name: "push small int 1", val: 1, expected: vec![Op1] },
323 Test { name: "push small int 2", val: 2, expected: vec![Op2] },
324 Test { name: "push small int 3", val: 3, expected: vec![Op3] },
325 Test { name: "push small int 4", val: 4, expected: vec![Op4] },
326 Test { name: "push small int 5", val: 5, expected: vec![Op5] },
327 Test { name: "push small int 6", val: 6, expected: vec![Op6] },
328 Test { name: "push small int 7", val: 7, expected: vec![Op7] },
329 Test { name: "push small int 8", val: 8, expected: vec![Op8] },
330 Test { name: "push small int 9", val: 9, expected: vec![Op9] },
331 Test { name: "push small int 10", val: 10, expected: vec![Op10] },
332 Test { name: "push small int 11", val: 11, expected: vec![Op11] },
333 Test { name: "push small int 12", val: 12, expected: vec![Op12] },
334 Test { name: "push small int 13", val: 13, expected: vec![Op13] },
335 Test { name: "push small int 14", val: 14, expected: vec![Op14] },
336 Test { name: "push small int 15", val: 15, expected: vec![Op15] },
337 Test { name: "push small int 16", val: 16, expected: vec![Op16] },
338 Test { name: "push 17", val: 17, expected: vec![OpData1, 0x11] },
339 Test { name: "push 65", val: 65, expected: vec![OpData1, 0x41] },
340 Test { name: "push 127", val: 127, expected: vec![OpData1, 0x7f] },
341 Test { name: "push 128", val: 128, expected: vec![OpData2, 0x80, 0] },
342 Test { name: "push 255", val: 255, expected: vec![OpData2, 0xff, 0] },
343 Test { name: "push 256", val: 256, expected: vec![OpData2, 0, 0x01] },
344 Test { name: "push 32767", val: 32767, expected: vec![OpData2, 0xff, 0x7f] },
345 Test { name: "push 32768", val: 32768, expected: vec![OpData3, 0, 0x80, 0] },
346 Test { name: "push -2", val: -2, expected: vec![OpData1, 0x82] },
347 Test { name: "push -3", val: -3, expected: vec![OpData1, 0x83] },
348 Test { name: "push -4", val: -4, expected: vec![OpData1, 0x84] },
349 Test { name: "push -5", val: -5, expected: vec![OpData1, 0x85] },
350 Test { name: "push -17", val: -17, expected: vec![OpData1, 0x91] },
351 Test { name: "push -65", val: -65, expected: vec![OpData1, 0xc1] },
352 Test { name: "push -127", val: -127, expected: vec![OpData1, 0xff] },
353 Test { name: "push -128", val: -128, expected: vec![OpData2, 0x80, 0x80] },
354 Test { name: "push -255", val: -255, expected: vec![OpData2, 0xff, 0x80] },
355 Test { name: "push -256", val: -256, expected: vec![OpData2, 0x00, 0x81] },
356 Test { name: "push -32767", val: -32767, expected: vec![OpData2, 0xff, 0xff] },
357 Test { name: "push -32768", val: -32768, expected: vec![OpData3, 0x00, 0x80, 0x80] },
358 ];
359
360 for test in tests {
361 let mut builder = ScriptBuilder::new();
362 let result = builder.add_i64(test.val).expect("the script is canonical").script();
363 assert_eq!(result, test.expected, "{} wrong result", test.name);
364 }
365 }
366
367 #[test]
369 fn test_add_data() {
370 struct Test {
371 name: &'static str,
372 data: Vec<u8>,
373 expected: ScriptBuilderResult<Vec<u8>>,
374 unchecked: bool,
376 }
377
378 let tests = vec![
379 Test { name: "push empty byte sequence", data: vec![], expected: Ok(vec![Op0]), unchecked: false },
381 Test { name: "push 1 byte 0x00", data: vec![0x00], expected: Ok(vec![Op0]), unchecked: false },
382 Test { name: "push 1 byte 0x01", data: vec![0x01], expected: Ok(vec![Op1]), unchecked: false },
384 Test { name: "push 1 byte 0x02", data: vec![0x02], expected: Ok(vec![Op2]), unchecked: false },
385 Test { name: "push 1 byte 0x03", data: vec![0x03], expected: Ok(vec![Op3]), unchecked: false },
386 Test { name: "push 1 byte 0x04", data: vec![0x04], expected: Ok(vec![Op4]), unchecked: false },
387 Test { name: "push 1 byte 0x05", data: vec![0x05], expected: Ok(vec![Op5]), unchecked: false },
388 Test { name: "push 1 byte 0x06", data: vec![0x06], expected: Ok(vec![Op6]), unchecked: false },
389 Test { name: "push 1 byte 0x07", data: vec![0x07], expected: Ok(vec![Op7]), unchecked: false },
390 Test { name: "push 1 byte 0x08", data: vec![0x08], expected: Ok(vec![Op8]), unchecked: false },
391 Test { name: "push 1 byte 0x09", data: vec![0x09], expected: Ok(vec![Op9]), unchecked: false },
392 Test { name: "push 1 byte 0x0a", data: vec![0x0a], expected: Ok(vec![Op10]), unchecked: false },
393 Test { name: "push 1 byte 0x0b", data: vec![0x0b], expected: Ok(vec![Op11]), unchecked: false },
394 Test { name: "push 1 byte 0x0c", data: vec![0x0c], expected: Ok(vec![Op12]), unchecked: false },
395 Test { name: "push 1 byte 0x0d", data: vec![0x0d], expected: Ok(vec![Op13]), unchecked: false },
396 Test { name: "push 1 byte 0x0e", data: vec![0x0e], expected: Ok(vec![Op14]), unchecked: false },
397 Test { name: "push 1 byte 0x0f", data: vec![0x0f], expected: Ok(vec![Op15]), unchecked: false },
398 Test { name: "push 1 byte 0x10", data: vec![0x10], expected: Ok(vec![Op16]), unchecked: false },
399 Test { name: "push 1 byte 0x81", data: vec![0x81], expected: Ok(vec![Op1Negate]), unchecked: false },
401 Test { name: "push 1 byte 0x11", data: vec![0x11], expected: Ok(vec![OpData1, 0x11]), unchecked: false },
405 Test { name: "push 1 byte 0x80", data: vec![0x80], expected: Ok(vec![OpData1, 0x80]), unchecked: false },
406 Test { name: "push 1 byte 0x82", data: vec![0x82], expected: Ok(vec![OpData1, 0x82]), unchecked: false },
407 Test { name: "push 1 byte 0xff", data: vec![0xff], expected: Ok(vec![OpData1, 0xff]), unchecked: false },
408 Test {
409 name: "push data len 17",
410 data: vec![0x49; 17],
411 expected: Ok(once(OpData17).chain(repeat(0x49).take(17)).collect()),
412 unchecked: false,
413 },
414 Test {
415 name: "push data len 75",
416 data: vec![0x49; 75],
417 expected: Ok(once(OpData75).chain(repeat(0x49).take(75)).collect()),
418 unchecked: false,
419 },
420 Test {
422 name: "push data len 76",
423 data: vec![0x49; 76],
424 expected: Ok(once(OpPushData1).chain(once(76)).chain(repeat(0x49).take(76)).collect()),
425 unchecked: false,
426 },
427 Test {
428 name: "push data len 255",
429 data: vec![0x49; 255],
430 expected: Ok(once(OpPushData1).chain(once(255)).chain(repeat(0x49).take(255)).collect()),
431 unchecked: false,
432 },
433 Test {
435 name: "push data len 256",
436 data: vec![0x49; 256],
437 expected: Ok(once(OpPushData2).chain([0, 1]).chain(repeat(0x49).take(256)).collect()),
438 unchecked: false,
439 },
440 Test {
441 name: "push data len 520",
442 data: vec![0x49; 520],
443 expected: Ok(once(OpPushData2).chain([8, 2]).chain(repeat(0x49).take(520)).collect()),
444 unchecked: false,
445 },
446 Test {
450 name: "push data len 521",
451 data: vec![0x49; 521],
452 expected: Err(ScriptBuilderError::ElementExceedsMaxSize(521)),
453 unchecked: false,
454 },
455 Test {
456 name: "push data len 32767 (canonical)",
457 data: vec![0x49; 32767],
458 expected: Err(ScriptBuilderError::DataRejected(32770)),
459 unchecked: false,
460 },
461 Test {
462 name: "push data len 65536 (canonical)",
463 data: vec![0x49; 65536],
464 expected: Err(ScriptBuilderError::DataRejected(65541)),
465 unchecked: false,
466 },
467 Test {
473 name: "push data len 32767 (non-canonical)",
474 data: vec![0x49; 32767],
475 expected: Ok(once(OpPushData2).chain([255, 127]).chain(repeat(0x49).take(32767)).collect()),
476 unchecked: true,
477 },
478 Test {
480 name: "push data len 65536 (non-canonical)",
481 data: vec![0x49; 65536],
482 expected: Ok(once(OpPushData4).chain([0, 0, 1, 0]).chain(repeat(0x49).take(65536)).collect()),
483 unchecked: true,
484 },
485 ];
486
487 for test in tests {
488 let mut builder = ScriptBuilder::new();
489 let result = match test.unchecked {
490 false => builder.add_data(&test.data).map(|x| x.drain()),
491 true => {
492 builder.add_data_unchecked(&test.data);
493 Ok(builder.drain())
494 }
495 };
496 assert_eq!(result, test.expected, "{} wrong result", test.name);
497 }
498 }
499
500 #[test]
501 fn test_u64() {
502 struct Test {
503 name: &'static str,
504 value: u64,
505 expected: Vec<u8>,
506 }
507
508 let tests = vec![
509 Test { name: "0x00", value: 0x00, expected: vec![Op0] },
510 Test { name: "0x01", value: 0x01, expected: vec![Op1] },
511 Test { name: "0xff", value: 0xff, expected: vec![OpData1, 0xff] },
512 Test { name: "0xffee", value: 0xffee, expected: vec![OpData2, 0xee, 0xff] },
513 Test { name: "0xffeedd", value: 0xffeedd, expected: vec![OpData3, 0xdd, 0xee, 0xff] },
514 Test { name: "0xffeeddcc", value: 0xffeeddcc, expected: vec![OpData4, 0xcc, 0xdd, 0xee, 0xff] },
515 Test { name: "0xffeeddccbb", value: 0xffeeddccbb, expected: vec![OpData5, 0xbb, 0xcc, 0xdd, 0xee, 0xff] },
516 Test { name: "0xffeeddccbbaa", value: 0xffeeddccbbaa, expected: vec![OpData6, 0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0xff] },
517 Test {
518 name: "0xffeeddccbbaa99",
519 value: 0xffeeddccbbaa99,
520 expected: vec![OpData7, 0x99, 0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0xff],
521 },
522 Test {
523 name: "0xffeeddccbbaa9988",
524 value: 0xffeeddccbbaa9988,
525 expected: vec![OpData8, 0x88, 0x99, 0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0xff],
526 },
527 Test { name: "0xffffffffffffffff", value: u64::MAX, expected: once(OpData8).chain(repeat(0xff).take(8)).collect() },
528 ];
529
530 for test in tests {
531 let result = ScriptBuilder::new().add_u64(test.value).expect("the script is canonical").drain();
532 assert_eq!(result, test.expected, "{} wrong result", test.name);
533 let result = ScriptBuilder::new().add_lock_time(test.value).expect("the script is canonical").drain();
534 assert_eq!(result, test.expected, "{} wrong lock time result", test.name);
535 let result = ScriptBuilder::new().add_sequence(test.value).expect("the script is canonical").drain();
536 assert_eq!(result, test.expected, "{} wrong sequence result", test.name);
537 }
538 }
539
540 #[test]
543 fn test_exceed_max_script_size() {
544 fn full_builder() -> ScriptBuilder {
545 let mut builder = ScriptBuilder::new();
546 builder.add_data_unchecked(&[0u8; MAX_SCRIPTS_SIZE - 3]);
547 builder
548 }
549 let mut builder = full_builder();
551 let original_result: Vec<u8> = Vec::from(builder.script());
552
553 let result = builder.add_data(&[0u8]).map(|_| ());
556 assert_eq!(
557 result,
558 Err(ScriptBuilderError::DataRejected(1)),
559 "adding data that would exceed the maximum size of the script must fail"
560 );
561 assert_eq!(builder.script(), &original_result, "unexpected modified script");
562
563 let result = builder.add_op(Op0).map(|_| ());
566 assert_eq!(
567 result,
568 Err(ScriptBuilderError::OpCodeRejected(Op0)),
569 "adding an opcode that would exceed the maximum size of the script must fail"
570 );
571 assert_eq!(builder.script(), &original_result, "unexpected modified script");
572
573 let result = builder.add_ops(&[OpCheckSig]).map(|_| ());
576 assert_eq!(
577 result,
578 Err(ScriptBuilderError::OpCodesRejected(1)),
579 "adding an opcode array that would exceed the maximum size of the script must fail"
580 );
581 assert_eq!(builder.script(), &original_result, "unexpected modified script");
582
583 let result = builder.add_i64(0).map(|_| ());
586 assert_eq!(
587 result,
588 Err(ScriptBuilderError::IntegerRejected(0)),
589 "adding an integer that would exceed the maximum size of the script must fail"
590 );
591 assert_eq!(builder.script(), &original_result, "unexpected modified script");
592
593 let result = builder.add_lock_time(0).map(|_| ());
596 assert_eq!(
597 result,
598 Err(ScriptBuilderError::DataRejected(1)),
599 "adding a lock time that would exceed the maximum size of the script must fail"
600 );
601 assert_eq!(builder.script(), &original_result, "unexpected modified script");
602
603 let result = builder.add_sequence(0).map(|_| ());
606 assert_eq!(
607 result,
608 Err(ScriptBuilderError::DataRejected(1)),
609 "adding a sequence that would exceed the maximum size of the script must fail"
610 );
611 assert_eq!(builder.script(), &original_result, "unexpected modified script");
612 }
613}