1use crate::error::{AvmError, AvmResult};
4use crate::types::StackValue;
5use crate::vm::EvalContext;
6use base64::Engine;
7use num_traits::Zero;
8
9const MAX_BYTE_MATH_SIZE: usize = 64;
10
11fn bytes_to_uint(bytes: &[u8]) -> AvmResult<num_bigint::BigUint> {
13 if bytes.len() > MAX_BYTE_MATH_SIZE {
14 return Err(AvmError::invalid_program(format!(
15 "Byte array too large for math: {} > {}",
16 bytes.len(),
17 MAX_BYTE_MATH_SIZE
18 )));
19 }
20 Ok(num_bigint::BigUint::from_bytes_be(bytes))
21}
22
23fn uint_to_bytes(val: &num_bigint::BigUint, min_len: usize) -> Vec<u8> {
25 let bytes = val.to_bytes_be();
26 if bytes.len() >= min_len {
27 bytes
28 } else {
29 let mut result = vec![0u8; min_len - bytes.len()];
30 result.extend_from_slice(&bytes);
31 result
32 }
33}
34
35pub fn op_b_plus(ctx: &mut EvalContext) -> AvmResult<()> {
37 ctx.advance_pc(1)?;
38 let b = ctx.pop()?;
39 let a = ctx.pop()?;
40
41 let a_bytes = a.as_bytes()?;
42 let b_bytes = b.as_bytes()?;
43
44 let a_num = bytes_to_uint(a_bytes)?;
45 let b_num = bytes_to_uint(b_bytes)?;
46
47 let result = a_num + b_num;
48 let max_len = std::cmp::max(a_bytes.len(), b_bytes.len());
49 let result_bytes = uint_to_bytes(&result, max_len);
50
51 ctx.push(StackValue::Bytes(result_bytes))?;
52 Ok(())
53}
54
55pub fn op_b_minus(ctx: &mut EvalContext) -> AvmResult<()> {
57 ctx.advance_pc(1)?;
58 let b = ctx.pop()?;
59 let a = ctx.pop()?;
60
61 let a_bytes = a.as_bytes()?;
62 let b_bytes = b.as_bytes()?;
63
64 let a_num = bytes_to_uint(a_bytes)?;
65 let b_num = bytes_to_uint(b_bytes)?;
66
67 if a_num < b_num {
68 return Err(AvmError::IntegerUnderflow);
69 }
70
71 let result = a_num - b_num;
72 let max_len = std::cmp::max(a_bytes.len(), b_bytes.len());
73 let result_bytes = uint_to_bytes(&result, max_len);
74
75 ctx.push(StackValue::Bytes(result_bytes))?;
76 Ok(())
77}
78
79pub fn op_b_div(ctx: &mut EvalContext) -> AvmResult<()> {
81 ctx.advance_pc(1)?;
82 let b = ctx.pop()?;
83 let a = ctx.pop()?;
84
85 let a_bytes = a.as_bytes()?;
86 let b_bytes = b.as_bytes()?;
87
88 let a_num = bytes_to_uint(a_bytes)?;
89 let b_num = bytes_to_uint(b_bytes)?;
90
91 if b_num.is_zero() {
92 return Err(AvmError::DivisionByZero);
93 }
94
95 let result = a_num / b_num;
96 let max_len = std::cmp::max(a_bytes.len(), b_bytes.len());
97 let result_bytes = uint_to_bytes(&result, max_len);
98
99 ctx.push(StackValue::Bytes(result_bytes))?;
100 Ok(())
101}
102
103pub fn op_b_mul(ctx: &mut EvalContext) -> AvmResult<()> {
105 ctx.advance_pc(1)?;
106 let b = ctx.pop()?;
107 let a = ctx.pop()?;
108
109 let a_bytes = a.as_bytes()?;
110 let b_bytes = b.as_bytes()?;
111
112 let a_num = bytes_to_uint(a_bytes)?;
113 let b_num = bytes_to_uint(b_bytes)?;
114
115 let result = a_num * b_num;
116 let max_len = std::cmp::max(a_bytes.len(), b_bytes.len());
117 let result_bytes = uint_to_bytes(&result, max_len);
118
119 ctx.push(StackValue::Bytes(result_bytes))?;
120 Ok(())
121}
122
123pub fn op_b_lt(ctx: &mut EvalContext) -> AvmResult<()> {
125 ctx.advance_pc(1)?;
126 let b = ctx.pop()?;
127 let a = ctx.pop()?;
128
129 let a_bytes = a.as_bytes()?;
130 let b_bytes = b.as_bytes()?;
131
132 let a_num = bytes_to_uint(a_bytes)?;
133 let b_num = bytes_to_uint(b_bytes)?;
134
135 let result = if a_num < b_num { 1 } else { 0 };
136 ctx.push(StackValue::Uint(result))?;
137 Ok(())
138}
139
140pub fn op_b_gt(ctx: &mut EvalContext) -> AvmResult<()> {
142 ctx.advance_pc(1)?;
143 let b = ctx.pop()?;
144 let a = ctx.pop()?;
145
146 let a_bytes = a.as_bytes()?;
147 let b_bytes = b.as_bytes()?;
148
149 let a_num = bytes_to_uint(a_bytes)?;
150 let b_num = bytes_to_uint(b_bytes)?;
151
152 let result = if a_num > b_num { 1 } else { 0 };
153 ctx.push(StackValue::Uint(result))?;
154 Ok(())
155}
156
157pub fn op_b_le(ctx: &mut EvalContext) -> AvmResult<()> {
159 ctx.advance_pc(1)?;
160 let b = ctx.pop()?;
161 let a = ctx.pop()?;
162
163 let a_bytes = a.as_bytes()?;
164 let b_bytes = b.as_bytes()?;
165
166 let a_num = bytes_to_uint(a_bytes)?;
167 let b_num = bytes_to_uint(b_bytes)?;
168
169 let result = if a_num <= b_num { 1 } else { 0 };
170 ctx.push(StackValue::Uint(result))?;
171 Ok(())
172}
173
174pub fn op_b_ge(ctx: &mut EvalContext) -> AvmResult<()> {
176 ctx.advance_pc(1)?;
177 let b = ctx.pop()?;
178 let a = ctx.pop()?;
179
180 let a_bytes = a.as_bytes()?;
181 let b_bytes = b.as_bytes()?;
182
183 let a_num = bytes_to_uint(a_bytes)?;
184 let b_num = bytes_to_uint(b_bytes)?;
185
186 let result = if a_num >= b_num { 1 } else { 0 };
187 ctx.push(StackValue::Uint(result))?;
188 Ok(())
189}
190
191pub fn op_b_eq(ctx: &mut EvalContext) -> AvmResult<()> {
193 ctx.advance_pc(1)?;
194 let b = ctx.pop()?;
195 let a = ctx.pop()?;
196
197 let a_bytes = a.as_bytes()?;
198 let b_bytes = b.as_bytes()?;
199
200 let result = if a_bytes == b_bytes { 1 } else { 0 };
201 ctx.push(StackValue::Uint(result))?;
202 Ok(())
203}
204
205pub fn op_b_ne(ctx: &mut EvalContext) -> AvmResult<()> {
207 ctx.advance_pc(1)?;
208 let b = ctx.pop()?;
209 let a = ctx.pop()?;
210
211 let a_bytes = a.as_bytes()?;
212 let b_bytes = b.as_bytes()?;
213
214 let result = if a_bytes != b_bytes { 1 } else { 0 };
215 ctx.push(StackValue::Uint(result))?;
216 Ok(())
217}
218
219pub fn op_b_mod(ctx: &mut EvalContext) -> AvmResult<()> {
221 ctx.advance_pc(1)?;
222 let b = ctx.pop()?;
223 let a = ctx.pop()?;
224
225 let a_bytes = a.as_bytes()?;
226 let b_bytes = b.as_bytes()?;
227
228 let a_num = bytes_to_uint(a_bytes)?;
229 let b_num = bytes_to_uint(b_bytes)?;
230
231 if b_num.is_zero() {
232 return Err(AvmError::DivisionByZero);
233 }
234
235 let result = a_num % b_num;
236 let max_len = std::cmp::max(a_bytes.len(), b_bytes.len());
237 let result_bytes = uint_to_bytes(&result, max_len);
238
239 ctx.push(StackValue::Bytes(result_bytes))?;
240 Ok(())
241}
242
243pub fn op_b_or(ctx: &mut EvalContext) -> AvmResult<()> {
245 ctx.advance_pc(1)?;
246 let b = ctx.pop()?;
247 let a = ctx.pop()?;
248
249 let a_bytes = a.as_bytes()?;
250 let b_bytes = b.as_bytes()?;
251
252 let max_len = std::cmp::max(a_bytes.len(), b_bytes.len());
254 let mut a_padded = vec![0u8; max_len];
255 let mut b_padded = vec![0u8; max_len];
256
257 a_padded[max_len - a_bytes.len()..].copy_from_slice(a_bytes);
258 b_padded[max_len - b_bytes.len()..].copy_from_slice(b_bytes);
259
260 let result: Vec<u8> = a_padded
261 .iter()
262 .zip(b_padded.iter())
263 .map(|(a, b)| a | b)
264 .collect();
265
266 ctx.push(StackValue::Bytes(result))?;
267 Ok(())
268}
269
270pub fn op_b_and(ctx: &mut EvalContext) -> AvmResult<()> {
272 ctx.advance_pc(1)?;
273 let b = ctx.pop()?;
274 let a = ctx.pop()?;
275
276 let a_bytes = a.as_bytes()?;
277 let b_bytes = b.as_bytes()?;
278
279 let max_len = std::cmp::max(a_bytes.len(), b_bytes.len());
281 let mut a_padded = vec![0u8; max_len];
282 let mut b_padded = vec![0u8; max_len];
283
284 a_padded[max_len - a_bytes.len()..].copy_from_slice(a_bytes);
285 b_padded[max_len - b_bytes.len()..].copy_from_slice(b_bytes);
286
287 let result: Vec<u8> = a_padded
288 .iter()
289 .zip(b_padded.iter())
290 .map(|(a, b)| a & b)
291 .collect();
292
293 ctx.push(StackValue::Bytes(result))?;
294 Ok(())
295}
296
297pub fn op_b_xor(ctx: &mut EvalContext) -> AvmResult<()> {
299 ctx.advance_pc(1)?;
300 let b = ctx.pop()?;
301 let a = ctx.pop()?;
302
303 let a_bytes = a.as_bytes()?;
304 let b_bytes = b.as_bytes()?;
305
306 let max_len = std::cmp::max(a_bytes.len(), b_bytes.len());
308 let mut a_padded = vec![0u8; max_len];
309 let mut b_padded = vec![0u8; max_len];
310
311 a_padded[max_len - a_bytes.len()..].copy_from_slice(a_bytes);
312 b_padded[max_len - b_bytes.len()..].copy_from_slice(b_bytes);
313
314 let result: Vec<u8> = a_padded
315 .iter()
316 .zip(b_padded.iter())
317 .map(|(a, b)| a ^ b)
318 .collect();
319
320 ctx.push(StackValue::Bytes(result))?;
321 Ok(())
322}
323
324pub fn op_b_not(ctx: &mut EvalContext) -> AvmResult<()> {
326 ctx.advance_pc(1)?;
327 let a = ctx.pop()?;
328
329 let a_bytes = a.as_bytes()?;
330 let result: Vec<u8> = a_bytes.iter().map(|b| !b).collect();
331
332 ctx.push(StackValue::Bytes(result))?;
333 Ok(())
334}
335
336pub fn op_getbit(ctx: &mut EvalContext) -> AvmResult<()> {
339 ctx.advance_pc(1)?;
340 let bit_index = ctx.pop()?;
341 let value = ctx.pop()?;
342
343 let bytes = value.as_bytes()?;
344 let bit_idx = bit_index.as_uint()? as usize;
345
346 let total_bits = bytes.len() * 8;
347 if bit_idx >= total_bits {
348 return Err(AvmError::invalid_program(format!(
349 "Bit index {bit_idx} out of bounds for {total_bits} bits"
350 )));
351 }
352
353 let byte_idx = bit_idx / 8;
354 let bit_offset = 7 - (bit_idx % 8); let bit_value = (bytes[byte_idx] >> bit_offset) & 1;
356
357 ctx.push(StackValue::Uint(bit_value as u64))?;
358 Ok(())
359}
360
361pub fn op_setbit(ctx: &mut EvalContext) -> AvmResult<()> {
363 ctx.advance_pc(1)?;
364 let new_bit = ctx.pop()?;
365 let bit_index = ctx.pop()?;
366 let value = ctx.pop()?;
367
368 let bytes = value.as_bytes()?;
369 let bit_idx = bit_index.as_uint()? as usize;
370 let bit_val = new_bit.as_uint()? & 1;
371
372 let total_bits = bytes.len() * 8;
373 if bit_idx >= total_bits {
374 return Err(AvmError::invalid_program(format!(
375 "Bit index {bit_idx} out of bounds for {total_bits} bits"
376 )));
377 }
378
379 let mut result = bytes.to_vec();
380 let byte_idx = bit_idx / 8;
381 let bit_offset = 7 - (bit_idx % 8); if bit_val == 1 {
384 result[byte_idx] |= 1 << bit_offset;
385 } else {
386 result[byte_idx] &= !(1 << bit_offset);
387 }
388
389 ctx.push(StackValue::Bytes(result))?;
390 Ok(())
391}
392
393pub fn op_getbyte(ctx: &mut EvalContext) -> AvmResult<()> {
395 ctx.advance_pc(1)?;
396 let byte_index = ctx.pop()?;
397 let value = ctx.pop()?;
398
399 let bytes = value.as_bytes()?;
400 let byte_idx = byte_index.as_uint()? as usize;
401
402 if byte_idx >= bytes.len() {
403 return Err(AvmError::invalid_program(format!(
404 "Byte index {} out of bounds for {} bytes",
405 byte_idx,
406 bytes.len()
407 )));
408 }
409
410 ctx.push(StackValue::Uint(bytes[byte_idx] as u64))?;
411 Ok(())
412}
413
414pub fn op_setbyte(ctx: &mut EvalContext) -> AvmResult<()> {
416 ctx.advance_pc(1)?;
417 let new_byte = ctx.pop()?;
418 let byte_index = ctx.pop()?;
419 let value = ctx.pop()?;
420
421 let bytes = value.as_bytes()?;
422 let byte_idx = byte_index.as_uint()? as usize;
423 let byte_val = new_byte.as_uint()? as u8;
424
425 if byte_idx >= bytes.len() {
426 return Err(AvmError::invalid_program(format!(
427 "Byte index {} out of bounds for {} bytes",
428 byte_idx,
429 bytes.len()
430 )));
431 }
432
433 let mut result = bytes.to_vec();
434 result[byte_idx] = byte_val;
435
436 ctx.push(StackValue::Bytes(result))?;
437 Ok(())
438}
439
440pub fn op_extract(ctx: &mut EvalContext) -> AvmResult<()> {
442 ctx.advance_pc(1)?;
443 let start = ctx.read_bytes(1)?[0] as usize;
444 ctx.advance_pc(1)?;
445 let length = ctx.read_bytes(1)?[0] as usize;
446 ctx.advance_pc(1)?;
447
448 let value = ctx.pop()?;
449 let bytes = value.as_bytes()?;
450
451 if start >= bytes.len() || start + length > bytes.len() {
452 return Err(AvmError::invalid_program(format!(
453 "Extract bounds [{}, {}) out of range for {} bytes",
454 start,
455 start + length,
456 bytes.len()
457 )));
458 }
459
460 let result = bytes[start..start + length].to_vec();
461 ctx.push(StackValue::Bytes(result))?;
462 Ok(())
463}
464
465pub fn op_extract3(ctx: &mut EvalContext) -> AvmResult<()> {
467 ctx.advance_pc(1)?;
468 let length = ctx.pop()?;
469 let start = ctx.pop()?;
470 let value = ctx.pop()?;
471
472 let bytes = value.as_bytes()?;
473 let start_idx = start.as_uint()? as usize;
474 let len = length.as_uint()? as usize;
475
476 if start_idx >= bytes.len() || start_idx + len > bytes.len() {
477 return Err(AvmError::invalid_program(format!(
478 "Extract bounds [{}, {}) out of range for {} bytes",
479 start_idx,
480 start_idx + len,
481 bytes.len()
482 )));
483 }
484
485 let result = bytes[start_idx..start_idx + len].to_vec();
486 ctx.push(StackValue::Bytes(result))?;
487 Ok(())
488}
489
490pub fn op_extract_uint16(ctx: &mut EvalContext) -> AvmResult<()> {
492 ctx.advance_pc(1)?;
493 let start = ctx.pop()?;
494 let value = ctx.pop()?;
495
496 let bytes = value.as_bytes()?;
497 let start_idx = start.as_uint()? as usize;
498
499 if start_idx + 2 > bytes.len() {
500 return Err(AvmError::invalid_program(format!(
501 "Extract uint16 bounds [{}, {}) out of range for {} bytes",
502 start_idx,
503 start_idx + 2,
504 bytes.len()
505 )));
506 }
507
508 let result = u16::from_be_bytes([bytes[start_idx], bytes[start_idx + 1]]) as u64;
509 ctx.push(StackValue::Uint(result))?;
510 Ok(())
511}
512
513pub fn op_extract_uint32(ctx: &mut EvalContext) -> AvmResult<()> {
515 ctx.advance_pc(1)?;
516 let start = ctx.pop()?;
517 let value = ctx.pop()?;
518
519 let bytes = value.as_bytes()?;
520 let start_idx = start.as_uint()? as usize;
521
522 if start_idx + 4 > bytes.len() {
523 return Err(AvmError::invalid_program(format!(
524 "Extract uint32 bounds [{}, {}) out of range for {} bytes",
525 start_idx,
526 start_idx + 4,
527 bytes.len()
528 )));
529 }
530
531 let result = u32::from_be_bytes([
532 bytes[start_idx],
533 bytes[start_idx + 1],
534 bytes[start_idx + 2],
535 bytes[start_idx + 3],
536 ]) as u64;
537 ctx.push(StackValue::Uint(result))?;
538 Ok(())
539}
540
541pub fn op_extract_uint64(ctx: &mut EvalContext) -> AvmResult<()> {
543 ctx.advance_pc(1)?;
544 let start = ctx.pop()?;
545 let value = ctx.pop()?;
546
547 let bytes = value.as_bytes()?;
548 let start_idx = start.as_uint()? as usize;
549
550 if start_idx + 8 > bytes.len() {
551 return Err(AvmError::invalid_program(format!(
552 "Extract uint64 bounds [{}, {}) out of range for {} bytes",
553 start_idx,
554 start_idx + 8,
555 bytes.len()
556 )));
557 }
558
559 let result = u64::from_be_bytes([
560 bytes[start_idx],
561 bytes[start_idx + 1],
562 bytes[start_idx + 2],
563 bytes[start_idx + 3],
564 bytes[start_idx + 4],
565 bytes[start_idx + 5],
566 bytes[start_idx + 6],
567 bytes[start_idx + 7],
568 ]);
569 ctx.push(StackValue::Uint(result))?;
570 Ok(())
571}
572
573pub fn op_replace2(ctx: &mut EvalContext) -> AvmResult<()> {
575 ctx.advance_pc(1)?;
576 let start = ctx.read_bytes(1)?[0] as usize;
577 ctx.advance_pc(1)?;
578
579 let replacement = ctx.pop()?;
580 let value = ctx.pop()?;
581
582 let bytes = value.as_bytes()?;
583 let repl_bytes = replacement.as_bytes()?;
584
585 if start >= bytes.len() || start + repl_bytes.len() > bytes.len() {
586 return Err(AvmError::invalid_program(format!(
587 "Replace bounds [{}, {}) out of range for {} bytes",
588 start,
589 start + repl_bytes.len(),
590 bytes.len()
591 )));
592 }
593
594 let mut result = bytes.to_vec();
595 result[start..start + repl_bytes.len()].copy_from_slice(repl_bytes);
596
597 ctx.push(StackValue::Bytes(result))?;
598 Ok(())
599}
600
601pub fn op_replace3(ctx: &mut EvalContext) -> AvmResult<()> {
603 ctx.advance_pc(1)?;
604 let replacement = ctx.pop()?;
605 let start = ctx.pop()?;
606 let value = ctx.pop()?;
607
608 let bytes = value.as_bytes()?;
609 let repl_bytes = replacement.as_bytes()?;
610 let start_idx = start.as_uint()? as usize;
611
612 if start_idx >= bytes.len() || start_idx + repl_bytes.len() > bytes.len() {
613 return Err(AvmError::invalid_program(format!(
614 "Replace bounds [{}, {}) out of range for {} bytes",
615 start_idx,
616 start_idx + repl_bytes.len(),
617 bytes.len()
618 )));
619 }
620
621 let mut result = bytes.to_vec();
622 result[start_idx..start_idx + repl_bytes.len()].copy_from_slice(repl_bytes);
623
624 ctx.push(StackValue::Bytes(result))?;
625 Ok(())
626}
627
628pub fn op_base64_decode(ctx: &mut EvalContext) -> AvmResult<()> {
630 ctx.advance_pc(1)?;
631 let encoding = ctx.read_bytes(1)?[0]; ctx.advance_pc(1)?;
633
634 let value = ctx.pop()?;
635 let input = value.as_bytes()?;
636
637 let input_str = std::str::from_utf8(input)
638 .map_err(|_| AvmError::invalid_program("Invalid UTF-8 in base64 input"))?;
639
640 let result = match encoding {
641 0 => base64::engine::general_purpose::URL_SAFE_NO_PAD.decode(input_str),
642 1 => base64::engine::general_purpose::STANDARD.decode(input_str),
643 _ => return Err(AvmError::invalid_program("Invalid base64 encoding type")),
644 };
645
646 match result {
647 Ok(decoded) => ctx.push(StackValue::Bytes(decoded))?,
648 Err(_) => return Err(AvmError::invalid_program("Invalid base64 input")),
649 }
650
651 Ok(())
652}
653
654pub fn op_json_ref(ctx: &mut EvalContext) -> AvmResult<()> {
656 ctx.advance_pc(1)?;
657 let return_type = ctx.read_bytes(1)?[0]; ctx.advance_pc(1)?;
659
660 let key = ctx.pop()?;
661 let json_data = ctx.pop()?;
662
663 let json_bytes = json_data.as_bytes()?;
664 let key_bytes = key.as_bytes()?;
665
666 let json_str = std::str::from_utf8(json_bytes)
667 .map_err(|_| AvmError::invalid_program("Invalid UTF-8 in JSON data"))?;
668 let key_str = std::str::from_utf8(key_bytes)
669 .map_err(|_| AvmError::invalid_program("Invalid UTF-8 in JSON key"))?;
670
671 let parsed_json: serde_json::Value = serde_json::from_str(json_str)
673 .map_err(|_| AvmError::invalid_program("Invalid JSON data"))?;
674
675 let field_value = parsed_json.get(key_str);
677
678 match return_type {
679 0 => {
680 match field_value {
682 Some(serde_json::Value::String(s)) => {
683 ctx.push(StackValue::Bytes(s.as_bytes().to_vec()))?;
684 }
685 Some(value) => {
686 let s = value.to_string();
688 ctx.push(StackValue::Bytes(s.into_bytes()))?;
689 }
690 None => {
691 ctx.push(StackValue::Bytes(Vec::new()))?;
693 }
694 }
695 }
696 1 => {
697 match field_value {
699 Some(serde_json::Value::Number(n)) => {
700 if let Some(u) = n.as_u64() {
701 ctx.push(StackValue::Uint(u))?;
702 } else {
703 ctx.push(StackValue::Uint(0))?;
705 }
706 }
707 _ => {
708 ctx.push(StackValue::Uint(0))?;
710 }
711 }
712 }
713 2 => {
714 match field_value {
716 Some(serde_json::Value::Object(_)) | Some(serde_json::Value::Array(_)) => {
717 let serialized = serde_json::to_string(field_value.unwrap()).map_err(|_| {
718 AvmError::invalid_program("Failed to serialize JSON object")
719 })?;
720 ctx.push(StackValue::Bytes(serialized.into_bytes()))?;
721 }
722 Some(value) => {
723 let serialized = serde_json::to_string(value)
725 .map_err(|_| AvmError::invalid_program("Failed to serialize JSON value"))?;
726 ctx.push(StackValue::Bytes(serialized.into_bytes()))?;
727 }
728 None => {
729 ctx.push(StackValue::Bytes(b"null".to_vec()))?;
731 }
732 }
733 }
734 _ => return Err(AvmError::invalid_program("Invalid JSON return type")),
735 }
736
737 Ok(())
738}