Skip to main content

bsv_script/interpreter/
scriptnum.rs

1//! Script number arithmetic with Bitcoin consensus rules.
2//!
3//! All numbers on the Bitcoin script stack are encoded as little-endian
4//! byte arrays with a sign bit in the most significant bit of the last byte.
5//! Numeric opcodes operate on 4-byte integers in [-2^31+1, 2^31-1] but
6//! results may overflow and remain valid as long as they are not reinterpreted
7//! as numbers.
8
9use num_bigint::BigInt;
10use num_traits::{One, Signed, ToPrimitive, Zero};
11
12use super::error::{InterpreterError, InterpreterErrorCode};
13
14/// A script number using big integer arithmetic for overflow safety.
15#[derive(Debug, Clone)]
16pub struct ScriptNumber {
17    /// The numeric value stored as a big integer.
18    pub val: BigInt,
19    /// Whether post-genesis rules are active (affects serialization clamping).
20    pub after_genesis: bool,
21}
22
23impl ScriptNumber {
24    /// Create a new ScriptNumber from an i64 value.
25    pub fn new(val: i64, after_genesis: bool) -> Self {
26        ScriptNumber {
27            val: BigInt::from(val),
28            after_genesis,
29        }
30    }
31
32    /// Parse a byte array into a ScriptNumber.
33    ///
34    /// `script_num_len` is the max allowed byte length.
35    /// `require_minimal` enforces minimal encoding.
36    /// `after_genesis` enables post-genesis rules.
37    pub fn from_bytes(
38        bb: &[u8],
39        script_num_len: usize,
40        require_minimal: bool,
41        after_genesis: bool,
42    ) -> Result<Self, InterpreterError> {
43        if bb.len() > script_num_len {
44            return Err(InterpreterError::new(
45                InterpreterErrorCode::NumberTooBig,
46                format!(
47                    "numeric value encoded as {:02x?} is {} bytes which exceeds the max allowed of {}",
48                    bb, bb.len(), script_num_len
49                ),
50            ));
51        }
52
53        if require_minimal {
54            check_minimal_data_encoding(bb)?;
55        }
56
57        if bb.is_empty() {
58            return Ok(ScriptNumber {
59                val: BigInt::zero(),
60                after_genesis,
61            });
62        }
63
64        // Decode from little endian with sign bit
65        let mut v = BigInt::zero();
66        for (i, &b) in bb.iter().enumerate() {
67            v |= BigInt::from(b) << (8 * i);
68        }
69
70        // If the most significant byte has the sign bit set, the number is negative
71        if bb[bb.len() - 1] & 0x80 != 0 {
72            // Remove the sign bit and negate
73            let mask = !(BigInt::from(0x80_i64) << (8 * (bb.len() - 1)));
74            v &= mask;
75            v = -v;
76        }
77
78        Ok(ScriptNumber {
79            val: v,
80            after_genesis,
81        })
82    }
83
84    /// Serialize the number to bytes in little-endian with sign bit.
85    pub fn to_bytes(&self) -> Vec<u8> {
86        if self.val.is_zero() {
87            return vec![];
88        }
89
90        let is_negative = self.val.is_negative();
91        let abs_val = if is_negative {
92            -self.val.clone()
93        } else {
94            self.val.clone()
95        };
96
97        // For pre-genesis, clamp to i32 range for serialization
98        let working_val = if !self.after_genesis {
99            let v = self
100                .val
101                .to_i64()
102                .unwrap_or(if is_negative { i64::MIN } else { i64::MAX });
103            if v > i32::MAX as i64 {
104                BigInt::from(i32::MAX)
105            } else if v < i32::MIN as i64 {
106                BigInt::from(i32::MIN).abs()
107            } else {
108                abs_val.clone()
109            }
110        } else {
111            abs_val.clone()
112        };
113
114        let _ = working_val; // we use abs_val below
115
116        // Convert absolute value to little-endian bytes
117        let mut result: Vec<u8> = Vec::new();
118        let mut cpy = abs_val;
119        while cpy > BigInt::zero() {
120            result.push((&cpy & BigInt::from(0xff_u8)).to_u8().unwrap_or(0));
121            cpy >>= 8;
122        }
123
124        if result.is_empty() {
125            return vec![];
126        }
127
128        // Handle sign bit
129        if result[result.len() - 1] & 0x80 != 0 {
130            // Need an extra byte for the sign
131            result.push(if is_negative { 0x80 } else { 0x00 });
132        } else if is_negative {
133            let last = result.len() - 1;
134            result[last] |= 0x80;
135        }
136
137        result
138    }
139
140    // Arithmetic operations (mutating, return self for chaining like Go)
141
142    /// Add another script number to this one and return self for chaining.
143    pub fn add(&mut self, other: &ScriptNumber) -> &mut Self {
144        self.val = &self.val + &other.val;
145        self
146    }
147
148    /// Subtract another script number from this one and return self for chaining.
149    pub fn sub(&mut self, other: &ScriptNumber) -> &mut Self {
150        self.val = &self.val - &other.val;
151        self
152    }
153
154    /// Multiply this script number by another and return self for chaining.
155    pub fn mul(&mut self, other: &ScriptNumber) -> &mut Self {
156        self.val = &self.val * &other.val;
157        self
158    }
159
160    /// Divide this script number by another (truncated toward zero) and return self for chaining.
161    pub fn div(&mut self, other: &ScriptNumber) -> &mut Self {
162        // Truncation towards zero (like Go's Quo)
163        use num_integer::Integer;
164        let (q, r) = self.val.div_rem(&other.val);
165        // Go's Quo truncates toward zero. BigInt div_rem might differ for negatives.
166        // num_integer div_rem uses truncated division, which matches Go's Quo.
167        let _ = r;
168        self.val = q;
169        self
170    }
171
172    /// Compute the truncated remainder of dividing by another and return self for chaining.
173    pub fn modulo(&mut self, other: &ScriptNumber) -> &mut Self {
174        // Go's Rem: truncated remainder
175        use num_integer::Integer;
176        let (_, r) = self.val.div_rem(&other.val);
177        self.val = r;
178        self
179    }
180
181    /// Increment this number by one and return self for chaining.
182    pub fn incr(&mut self) -> &mut Self {
183        self.val = &self.val + BigInt::one();
184        self
185    }
186
187    /// Decrement this number by one and return self for chaining.
188    pub fn decr(&mut self) -> &mut Self {
189        self.val = &self.val - BigInt::one();
190        self
191    }
192
193    /// Negate this number and return self for chaining.
194    pub fn neg(&mut self) -> &mut Self {
195        self.val = -self.val.clone();
196        self
197    }
198
199    /// Replace this number with its absolute value and return self for chaining.
200    pub fn abs(&mut self) -> &mut Self {
201        if self.val.is_negative() {
202            self.val = -self.val.clone();
203        }
204        self
205    }
206
207    /// Set this number to the given i64 value and return self for chaining.
208    pub fn set(&mut self, i: i64) -> &mut Self {
209        self.val = BigInt::from(i);
210        self
211    }
212
213    // Comparison operations
214
215    /// Return true if this number is zero.
216    pub fn is_zero(&self) -> bool {
217        self.val.is_zero()
218    }
219
220    /// Return true if this number is less than `other`.
221    pub fn less_than(&self, other: &ScriptNumber) -> bool {
222        self.val < other.val
223    }
224
225    /// Return true if this number is less than the given i64 value.
226    pub fn less_than_int(&self, i: i64) -> bool {
227        self.val < BigInt::from(i)
228    }
229
230    /// Return true if this number is less than or equal to `other`.
231    pub fn less_than_or_equal(&self, other: &ScriptNumber) -> bool {
232        self.val <= other.val
233    }
234
235    /// Return true if this number is greater than `other`.
236    pub fn greater_than(&self, other: &ScriptNumber) -> bool {
237        self.val > other.val
238    }
239
240    /// Return true if this number is greater than the given i64 value.
241    pub fn greater_than_int(&self, i: i64) -> bool {
242        self.val > BigInt::from(i)
243    }
244
245    /// Return true if this number is greater than or equal to `other`.
246    pub fn greater_than_or_equal(&self, other: &ScriptNumber) -> bool {
247        self.val >= other.val
248    }
249
250    /// Return true if this number is equal to `other`.
251    pub fn equal(&self, other: &ScriptNumber) -> bool {
252        self.val == other.val
253    }
254
255    /// Return true if this number is equal to the given i64 value.
256    pub fn equal_int(&self, i: i64) -> bool {
257        self.val == BigInt::from(i)
258    }
259
260    // Conversion
261
262    /// Convert to i32, clamping to [i32::MIN, i32::MAX] on overflow.
263    pub fn to_i32(&self) -> i32 {
264        match self.val.to_i64() {
265            Some(v) => {
266                if v > i32::MAX as i64 {
267                    i32::MAX
268                } else if v < i32::MIN as i64 {
269                    i32::MIN
270                } else {
271                    v as i32
272                }
273            }
274            None => {
275                if self.val.is_positive() {
276                    i32::MAX
277                } else {
278                    i32::MIN
279                }
280            }
281        }
282    }
283
284    /// Convert to i64, clamping to [i64::MIN, i64::MAX] on overflow.
285    pub fn to_i64(&self) -> i64 {
286        if self.greater_than_int(i64::MAX) {
287            return i64::MAX;
288        }
289        if self.less_than_int(i64::MIN) {
290            return i64::MIN;
291        }
292        self.val.to_i64().unwrap_or(0)
293    }
294
295    /// Convert to i64, returning 0 if the value does not fit.
296    pub fn to_int(&self) -> i64 {
297        self.val.to_i64().unwrap_or(0)
298    }
299}
300
301/// Minimally encode a byte array (used by OP_BIN2NUM).
302pub fn minimally_encode(data: &[u8]) -> Vec<u8> {
303    if data.is_empty() {
304        return vec![];
305    }
306
307    let mut data = data.to_vec();
308    let last = data[data.len() - 1];
309
310    if last & 0x7f != 0 {
311        return data;
312    }
313
314    if data.len() == 1 {
315        return vec![];
316    }
317
318    if data[data.len() - 2] & 0x80 != 0 {
319        return data;
320    }
321
322    let mut i = data.len() - 1;
323    while i > 0 {
324        if data[i - 1] != 0 {
325            if data[i - 1] & 0x80 != 0 {
326                data[i] = last;
327                return data[..=i].to_vec();
328            } else {
329                data[i - 1] |= last;
330                return data[..i].to_vec();
331            }
332        }
333        i -= 1;
334    }
335
336    vec![]
337}
338
339/// Check that a byte array uses minimal data encoding.
340pub fn check_minimal_data_encoding(v: &[u8]) -> Result<(), InterpreterError> {
341    if v.is_empty() {
342        return Ok(());
343    }
344
345    if v[v.len() - 1] & 0x7f == 0 {
346        if v.len() == 1 || v[v.len() - 2] & 0x80 == 0 {
347            return Err(InterpreterError::new(
348                InterpreterErrorCode::MinimalData,
349                format!(
350                    "numeric value encoded as {:02x?} is not minimally encoded",
351                    v
352                ),
353            ));
354        }
355    }
356
357    Ok(())
358}
359
360#[cfg(test)]
361mod tests {
362    use super::*;
363
364    fn hex_to_bytes(s: &str) -> Vec<u8> {
365        hex::decode(s).unwrap()
366    }
367
368    #[test]
369    fn test_script_num_bytes() {
370        let tests: Vec<(i64, Vec<u8>)> = vec![
371            (0, vec![]),
372            (1, hex_to_bytes("01")),
373            (-1, hex_to_bytes("81")),
374            (127, hex_to_bytes("7f")),
375            (-127, hex_to_bytes("ff")),
376            (128, hex_to_bytes("8000")),
377            (-128, hex_to_bytes("8080")),
378            (129, hex_to_bytes("8100")),
379            (-129, hex_to_bytes("8180")),
380            (256, hex_to_bytes("0001")),
381            (-256, hex_to_bytes("0081")),
382            (32767, hex_to_bytes("ff7f")),
383            (-32767, hex_to_bytes("ffff")),
384            (32768, hex_to_bytes("008000")),
385            (-32768, hex_to_bytes("008080")),
386            (65535, hex_to_bytes("ffff00")),
387            (-65535, hex_to_bytes("ffff80")),
388            (524288, hex_to_bytes("000008")),
389            (-524288, hex_to_bytes("000088")),
390            (7340032, hex_to_bytes("000070")),
391            (-7340032, hex_to_bytes("0000f0")),
392            (8388608, hex_to_bytes("00008000")),
393            (-8388608, hex_to_bytes("00008080")),
394            (2147483647, hex_to_bytes("ffffff7f")),
395            (-2147483647, hex_to_bytes("ffffffff")),
396            // Out of range values (still valid for results)
397            (2147483648, hex_to_bytes("0000008000")),
398            (-2147483648, hex_to_bytes("0000008080")),
399            (2415919104, hex_to_bytes("0000009000")),
400            (-2415919104, hex_to_bytes("0000009080")),
401            (4294967295, hex_to_bytes("ffffffff00")),
402            (-4294967295, hex_to_bytes("ffffffff80")),
403            (4294967296, hex_to_bytes("0000000001")),
404            (-4294967296, hex_to_bytes("0000000081")),
405            (281474976710655, hex_to_bytes("ffffffffffff00")),
406            (-281474976710655, hex_to_bytes("ffffffffffff80")),
407            (72057594037927935, hex_to_bytes("ffffffffffffff00")),
408            (-72057594037927935, hex_to_bytes("ffffffffffffff80")),
409            (9223372036854775807, hex_to_bytes("ffffffffffffff7f")),
410            (-9223372036854775807, hex_to_bytes("ffffffffffffffff")),
411        ];
412
413        for (num, expected) in &tests {
414            let sn = ScriptNumber {
415                val: BigInt::from(*num),
416                after_genesis: true, // after_genesis doesn't clamp
417            };
418            let got = sn.to_bytes();
419            assert_eq!(
420                &got, expected,
421                "Bytes: num={}, got={:02x?}, want={:02x?}",
422                num, got, expected
423            );
424        }
425    }
426
427    #[test]
428    fn test_make_script_num() {
429        struct Test {
430            serialized: Vec<u8>,
431            num: i64,
432            num_len: usize,
433            minimal_encoding: bool,
434            expect_err: bool,
435        }
436
437        let tests = vec![
438            // Minimal encoding rejects negative 0
439            Test {
440                serialized: hex_to_bytes("80"),
441                num: 0,
442                num_len: 4,
443                minimal_encoding: true,
444                expect_err: true,
445            },
446            // Valid minimally encoded
447            Test {
448                serialized: vec![],
449                num: 0,
450                num_len: 4,
451                minimal_encoding: true,
452                expect_err: false,
453            },
454            Test {
455                serialized: hex_to_bytes("01"),
456                num: 1,
457                num_len: 4,
458                minimal_encoding: true,
459                expect_err: false,
460            },
461            Test {
462                serialized: hex_to_bytes("81"),
463                num: -1,
464                num_len: 4,
465                minimal_encoding: true,
466                expect_err: false,
467            },
468            Test {
469                serialized: hex_to_bytes("7f"),
470                num: 127,
471                num_len: 4,
472                minimal_encoding: true,
473                expect_err: false,
474            },
475            Test {
476                serialized: hex_to_bytes("ff"),
477                num: -127,
478                num_len: 4,
479                minimal_encoding: true,
480                expect_err: false,
481            },
482            Test {
483                serialized: hex_to_bytes("8000"),
484                num: 128,
485                num_len: 4,
486                minimal_encoding: true,
487                expect_err: false,
488            },
489            Test {
490                serialized: hex_to_bytes("8080"),
491                num: -128,
492                num_len: 4,
493                minimal_encoding: true,
494                expect_err: false,
495            },
496            Test {
497                serialized: hex_to_bytes("8100"),
498                num: 129,
499                num_len: 4,
500                minimal_encoding: true,
501                expect_err: false,
502            },
503            Test {
504                serialized: hex_to_bytes("8180"),
505                num: -129,
506                num_len: 4,
507                minimal_encoding: true,
508                expect_err: false,
509            },
510            Test {
511                serialized: hex_to_bytes("0001"),
512                num: 256,
513                num_len: 4,
514                minimal_encoding: true,
515                expect_err: false,
516            },
517            Test {
518                serialized: hex_to_bytes("0081"),
519                num: -256,
520                num_len: 4,
521                minimal_encoding: true,
522                expect_err: false,
523            },
524            Test {
525                serialized: hex_to_bytes("ff7f"),
526                num: 32767,
527                num_len: 4,
528                minimal_encoding: true,
529                expect_err: false,
530            },
531            Test {
532                serialized: hex_to_bytes("ffff"),
533                num: -32767,
534                num_len: 4,
535                minimal_encoding: true,
536                expect_err: false,
537            },
538            Test {
539                serialized: hex_to_bytes("008000"),
540                num: 32768,
541                num_len: 4,
542                minimal_encoding: true,
543                expect_err: false,
544            },
545            Test {
546                serialized: hex_to_bytes("008080"),
547                num: -32768,
548                num_len: 4,
549                minimal_encoding: true,
550                expect_err: false,
551            },
552            Test {
553                serialized: hex_to_bytes("ffffff7f"),
554                num: 2147483647,
555                num_len: 4,
556                minimal_encoding: true,
557                expect_err: false,
558            },
559            Test {
560                serialized: hex_to_bytes("ffffffff"),
561                num: -2147483647,
562                num_len: 4,
563                minimal_encoding: true,
564                expect_err: false,
565            },
566            // 5-byte numbers
567            Test {
568                serialized: hex_to_bytes("ffffffff7f"),
569                num: 549755813887,
570                num_len: 5,
571                minimal_encoding: true,
572                expect_err: false,
573            },
574            Test {
575                serialized: hex_to_bytes("ffffffffff"),
576                num: -549755813887,
577                num_len: 5,
578                minimal_encoding: true,
579                expect_err: false,
580            },
581            // Out of range for 4-byte
582            Test {
583                serialized: hex_to_bytes("0000008000"),
584                num: 0,
585                num_len: 4,
586                minimal_encoding: true,
587                expect_err: true,
588            },
589            // Non-minimally encoded with flag
590            Test {
591                serialized: hex_to_bytes("00"),
592                num: 0,
593                num_len: 4,
594                minimal_encoding: true,
595                expect_err: true,
596            },
597            Test {
598                serialized: hex_to_bytes("0100"),
599                num: 0,
600                num_len: 4,
601                minimal_encoding: true,
602                expect_err: true,
603            },
604            // Non-minimally encoded without flag (OK)
605            Test {
606                serialized: hex_to_bytes("00"),
607                num: 0,
608                num_len: 4,
609                minimal_encoding: false,
610                expect_err: false,
611            },
612            Test {
613                serialized: hex_to_bytes("0100"),
614                num: 1,
615                num_len: 4,
616                minimal_encoding: false,
617                expect_err: false,
618            },
619        ];
620
621        for test in &tests {
622            let result = ScriptNumber::from_bytes(
623                &test.serialized,
624                test.num_len,
625                test.minimal_encoding,
626                true,
627            );
628            match result {
629                Ok(sn) => {
630                    assert!(
631                        !test.expect_err,
632                        "from_bytes({:02x?}): expected error",
633                        test.serialized
634                    );
635                    assert_eq!(
636                        sn.to_int(),
637                        test.num,
638                        "from_bytes({:02x?}): got {}, want {}",
639                        test.serialized,
640                        sn.to_int(),
641                        test.num
642                    );
643                }
644                Err(_) => {
645                    assert!(
646                        test.expect_err,
647                        "from_bytes({:02x?}): unexpected error",
648                        test.serialized
649                    );
650                }
651            }
652        }
653    }
654
655    #[test]
656    fn test_script_num_int32() {
657        let tests: Vec<(i64, i32)> = vec![
658            (0, 0),
659            (1, 1),
660            (-1, -1),
661            (2147483647, 2147483647),
662            (-2147483647, -2147483647),
663            (-2147483648, -2147483648),
664            // Clamped values
665            (2147483648, 2147483647),
666            (-2147483649, -2147483648),
667            (9223372036854775807, 2147483647),
668            (-9223372036854775808, -2147483648),
669        ];
670
671        for (input, want) in &tests {
672            let sn = ScriptNumber {
673                val: BigInt::from(*input),
674                after_genesis: false,
675            };
676            assert_eq!(
677                sn.to_i32(),
678                *want,
679                "Int32({}): got {}, want {}",
680                input,
681                sn.to_i32(),
682                want
683            );
684        }
685    }
686
687    #[test]
688    fn test_minimally_encode() {
689        // Empty stays empty
690        assert_eq!(minimally_encode(&[]), Vec::<u8>::new());
691        // Already minimal
692        assert_eq!(minimally_encode(&[0x7f]), vec![0x7f]);
693        // Single zero byte becomes empty
694        assert_eq!(minimally_encode(&[0x00]), Vec::<u8>::new());
695        // Negative zero becomes empty
696        assert_eq!(minimally_encode(&[0x80]), Vec::<u8>::new());
697    }
698}