bsv_rs/script/
script_num.rs1use crate::primitives::BigNumber;
15use crate::Result;
16
17pub struct ScriptNum;
22
23impl ScriptNum {
24 pub fn from_bytes(bytes: &[u8], require_minimal: bool) -> Result<BigNumber> {
35 if bytes.is_empty() {
37 return Ok(BigNumber::zero());
38 }
39
40 if require_minimal && !Self::is_minimally_encoded(bytes) {
42 return Err(crate::error::Error::ScriptExecutionError(
43 "Non-minimally encoded script number".to_string(),
44 ));
45 }
46
47 let last_byte = bytes[bytes.len() - 1];
49 let is_negative = (last_byte & 0x80) != 0;
50
51 let mut magnitude = bytes.to_vec();
53 if let Some(last) = magnitude.last_mut() {
54 *last &= 0x7f;
55 }
56
57 let bn = BigNumber::from_bytes_le(&magnitude);
59
60 if is_negative {
62 Ok(bn.neg())
63 } else {
64 Ok(bn)
65 }
66 }
67
68 pub fn to_bytes(value: &BigNumber) -> Vec<u8> {
78 if value.is_zero() {
80 return Vec::new();
81 }
82
83 let is_negative = value.is_negative();
84 let abs_value = value.abs();
85
86 let mut bytes = abs_value.to_bytes_le_min();
88
89 if let Some(&last) = bytes.last() {
91 if (last & 0x80) != 0 {
92 bytes.push(if is_negative { 0x80 } else { 0x00 });
93 } else if is_negative {
94 if let Some(last_mut) = bytes.last_mut() {
96 *last_mut |= 0x80;
97 }
98 }
99 } else if is_negative {
100 bytes.push(0x80);
102 }
103
104 bytes
105 }
106
107 pub fn is_minimally_encoded(bytes: &[u8]) -> bool {
114 if bytes.is_empty() {
115 return true;
116 }
117
118 let last_byte = bytes[bytes.len() - 1];
121 if (last_byte & 0x7f) != 0 {
122 return true;
123 }
124
125 if bytes.len() > 1 && (bytes[bytes.len() - 2] & 0x80) != 0 {
129 return true;
130 }
131
132 false
133 }
134
135 pub fn cast_to_bool(bytes: &[u8]) -> bool {
144 if bytes.is_empty() {
145 return false;
146 }
147
148 for (i, &byte) in bytes.iter().enumerate() {
149 if byte != 0 {
150 if i == bytes.len() - 1 && byte == 0x80 {
152 return false;
153 }
154 return true;
155 }
156 }
157
158 false
159 }
160
161 pub fn minimally_encode(bytes: &[u8]) -> Vec<u8> {
165 if bytes.is_empty() {
166 return Vec::new();
167 }
168
169 match Self::from_bytes(bytes, false) {
171 Ok(bn) => Self::to_bytes(&bn),
172 Err(_) => bytes.to_vec(),
173 }
174 }
175}
176
177#[cfg(test)]
178mod tests {
179 use super::*;
180
181 #[test]
182 fn test_zero() {
183 assert_eq!(ScriptNum::to_bytes(&BigNumber::zero()), Vec::<u8>::new());
185
186 let bn = ScriptNum::from_bytes(&[], true).unwrap();
188 assert!(bn.is_zero());
189 }
190
191 #[test]
192 fn test_positive_numbers() {
193 let bn = BigNumber::from_i64(1);
195 assert_eq!(ScriptNum::to_bytes(&bn), vec![0x01]);
196
197 let bn = BigNumber::from_i64(127);
199 assert_eq!(ScriptNum::to_bytes(&bn), vec![0x7f]);
200
201 let bn = BigNumber::from_i64(128);
203 assert_eq!(ScriptNum::to_bytes(&bn), vec![0x80, 0x00]);
204
205 let bn = BigNumber::from_i64(255);
207 assert_eq!(ScriptNum::to_bytes(&bn), vec![0xff, 0x00]);
208
209 let bn = BigNumber::from_i64(256);
211 assert_eq!(ScriptNum::to_bytes(&bn), vec![0x00, 0x01]);
212 }
213
214 #[test]
215 fn test_negative_numbers() {
216 let bn = BigNumber::from_i64(-1);
218 assert_eq!(ScriptNum::to_bytes(&bn), vec![0x81]);
219
220 let bn = BigNumber::from_i64(-127);
222 assert_eq!(ScriptNum::to_bytes(&bn), vec![0xff]);
223
224 let bn = BigNumber::from_i64(-128);
226 assert_eq!(ScriptNum::to_bytes(&bn), vec![0x80, 0x80]);
227
228 let bn = BigNumber::from_i64(-255);
230 assert_eq!(ScriptNum::to_bytes(&bn), vec![0xff, 0x80]);
231 }
232
233 #[test]
234 fn test_roundtrip() {
235 let test_values = [
236 0i64, 1, -1, 127, -127, 128, -128, 255, -255, 256, -256, 1000, -1000,
237 ];
238
239 for val in test_values {
240 let bn = BigNumber::from_i64(val);
241 let bytes = ScriptNum::to_bytes(&bn);
242 let decoded = ScriptNum::from_bytes(&bytes, true).unwrap();
243 assert_eq!(bn, decoded, "Roundtrip failed for {}", val);
244 }
245 }
246
247 #[test]
248 fn test_minimal_encoding() {
249 assert!(ScriptNum::is_minimally_encoded(&[]));
251 assert!(ScriptNum::is_minimally_encoded(&[0x01]));
252 assert!(ScriptNum::is_minimally_encoded(&[0x7f]));
253 assert!(ScriptNum::is_minimally_encoded(&[0x80, 0x00]));
254 assert!(ScriptNum::is_minimally_encoded(&[0x81]));
255
256 assert!(!ScriptNum::is_minimally_encoded(&[0x00])); assert!(!ScriptNum::is_minimally_encoded(&[0x80])); assert!(!ScriptNum::is_minimally_encoded(&[0x01, 0x00])); }
261
262 #[test]
263 fn test_cast_to_bool() {
264 assert!(!ScriptNum::cast_to_bool(&[]));
266 assert!(!ScriptNum::cast_to_bool(&[0x00]));
267 assert!(!ScriptNum::cast_to_bool(&[0x00, 0x00]));
268 assert!(!ScriptNum::cast_to_bool(&[0x80])); assert!(!ScriptNum::cast_to_bool(&[0x00, 0x80])); assert!(ScriptNum::cast_to_bool(&[0x01]));
273 assert!(ScriptNum::cast_to_bool(&[0x81])); assert!(ScriptNum::cast_to_bool(&[0x00, 0x01]));
275 assert!(ScriptNum::cast_to_bool(&[0x7f]));
276 }
277
278 #[test]
279 fn test_non_minimal_decoding() {
280 let result = ScriptNum::from_bytes(&[0x00], true);
282 assert!(result.is_err());
283
284 let bn = ScriptNum::from_bytes(&[0x00], false).unwrap();
286 assert!(bn.is_zero());
287 }
288}