1use crate::stack_item::StackItem;
8use k256::ecdsa::{signature::Verifier, Signature, VerifyingKey};
9use ripemd::Ripemd160;
10use sha2::{Digest, Sha256};
11use thiserror::Error;
12
13#[derive(Error, Debug)]
14pub enum VMError {
15 #[error("Stack underflow")]
16 StackUnderflow,
17 #[error("Stack overflow: max depth {0} exceeded")]
18 StackOverflow(usize),
19 #[error("Invalid opcode: {0}")]
20 InvalidOpcode(u8),
21 #[error("Out of gas")]
22 OutOfGas,
23 #[error("Division by zero")]
24 DivisionByZero,
25 #[error("Invalid type")]
26 InvalidType,
27 #[error("Unknown syscall: {0}")]
28 UnknownSyscall(u32),
29 #[error("Invalid operation")]
30 InvalidOperation,
31 #[error("Invalid script")]
32 InvalidScript,
33 #[error("Invalid public key format for CHECKSIG")]
34 InvalidPublicKey,
35 #[error("Invalid signature format for CHECKSIG")]
36 InvalidSignature,
37 #[error("Signature verification failed")]
38 SignatureVerificationFailed,
39 #[error("Invocation depth exceeded: max {0}")]
40 InvocationDepthExceeded(usize),
41}
42
43#[derive(Debug, Clone)]
44pub enum VMState {
45 None,
46 Halt,
47 Fault,
48 Break,
49}
50
51#[derive(Debug, Clone)]
52pub struct ExecutionContext {
53 pub script: Vec<u8>,
54 pub ip: usize,
55}
56
57unsafe impl Send for ExecutionContext {}
59unsafe impl Sync for ExecutionContext {}
60
61pub mod syscall {
63 pub const SYSTEM_RUNTIME_LOG: u32 = 0x01;
64 pub const SYSTEM_RUNTIME_NOTIFY: u32 = 0x02;
65 pub const SYSTEM_RUNTIME_GETTIME: u32 = 0x03;
66 pub const SYSTEM_STORAGE_GET: u32 = 0x10;
67 pub const SYSTEM_STORAGE_PUT: u32 = 0x11;
68 pub const SYSTEM_STORAGE_DELETE: u32 = 0x12;
69}
70
71const GAS_COSTS: [u16; 256] = [
74 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
79 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
91 512, 512, 512, 32768, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
93];
94
95#[inline]
96fn get_gas_cost(op: u8) -> u64 {
97 GAS_COSTS[op as usize] as u64
98}
99
100pub const MAX_SCRIPT_SIZE: usize = 1024 * 1024;
102
103pub const DEFAULT_MAX_STACK_DEPTH: usize = 2048;
105
106pub const DEFAULT_MAX_INVOCATION_DEPTH: usize = 1024;
108
109#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
111pub struct TraceStep {
112 pub ip: usize,
113 pub opcode: u8,
114 pub stack_hash: [u8; 32],
115 pub gas_consumed: u64,
116}
117
118#[derive(Debug, Clone, Default, serde::Serialize, serde::Deserialize)]
120pub struct ExecutionTrace {
121 pub steps: Vec<TraceStep>,
122 pub initial_state_hash: [u8; 32],
123 pub final_state_hash: [u8; 32],
124}
125
126pub struct NeoVM {
127 pub state: VMState,
128 pub eval_stack: Vec<StackItem>,
129 pub invocation_stack: Vec<ExecutionContext>,
130 pub gas_consumed: u64,
131 pub gas_limit: u64,
132 pub max_stack_depth: usize,
133 pub max_invocation_depth: usize,
134 pub notifications: Vec<StackItem>,
135 pub logs: Vec<String>,
136 pub trace: ExecutionTrace,
137 pub tracing_enabled: bool,
138 pub local_slots: Vec<StackItem>,
140 pub argument_slots: Vec<StackItem>,
141 pub static_slots: Vec<StackItem>,
142}
143
144impl NeoVM {
145 const DEFAULT_STACK_CAPACITY: usize = 64;
147 const DEFAULT_INVOCATION_CAPACITY: usize = 8;
149
150 #[inline]
152 pub fn new(gas_limit: u64) -> Self {
153 Self::with_limits(
154 gas_limit,
155 DEFAULT_MAX_STACK_DEPTH,
156 DEFAULT_MAX_INVOCATION_DEPTH,
157 )
158 }
159
160 #[inline]
162 pub fn with_limits(
163 gas_limit: u64,
164 max_stack_depth: usize,
165 max_invocation_depth: usize,
166 ) -> Self {
167 Self {
168 state: VMState::None,
169 eval_stack: Vec::with_capacity(Self::DEFAULT_STACK_CAPACITY),
170 invocation_stack: Vec::with_capacity(Self::DEFAULT_INVOCATION_CAPACITY),
171 gas_consumed: 0,
172 gas_limit,
173 max_stack_depth,
174 max_invocation_depth,
175 notifications: Vec::new(),
176 logs: Vec::new(),
177 trace: ExecutionTrace::default(),
178 tracing_enabled: false,
179 local_slots: Vec::with_capacity(Self::DEFAULT_STACK_CAPACITY),
180 argument_slots: Vec::with_capacity(Self::DEFAULT_STACK_CAPACITY),
181 static_slots: Vec::with_capacity(Self::DEFAULT_STACK_CAPACITY),
182 }
183 }
184
185 #[inline]
187 pub fn run(&mut self) {
188 while !matches!(self.state, VMState::Halt | VMState::Fault) {
189 if self.execute_next().is_err() {
190 self.state = VMState::Fault;
191 break;
192 }
193 }
194 }
195
196 #[inline]
197 pub fn enable_tracing(&mut self) {
198 self.tracing_enabled = true;
199 self.trace.initial_state_hash = self.compute_state_hash();
200 }
201
202 #[inline]
203 fn compute_state_hash(&self) -> [u8; 32] {
204 use sha2::Digest;
205 let mut hasher = Sha256::new();
206 for item in &self.eval_stack {
207 hasher.update(format!("{:?}", item).as_bytes());
208 }
209 hasher.update(self.gas_consumed.to_le_bytes());
210 hasher.finalize().into()
211 }
212
213 fn read_u8(ctx: &mut ExecutionContext) -> Result<u8, VMError> {
214 if ctx.ip >= ctx.script.len() {
215 return Err(VMError::InvalidScript);
216 }
217 let byte = ctx.script[ctx.ip];
218 ctx.ip += 1;
219 Ok(byte)
220 }
221
222 fn read_i8(ctx: &mut ExecutionContext) -> Result<i8, VMError> {
223 Ok(Self::read_u8(ctx)? as i8)
224 }
225
226 fn read_u16_le(ctx: &mut ExecutionContext) -> Result<u16, VMError> {
227 if ctx.ip + 1 >= ctx.script.len() {
228 return Err(VMError::InvalidScript);
229 }
230 let val = u16::from_le_bytes([ctx.script[ctx.ip], ctx.script[ctx.ip + 1]]);
231 ctx.ip += 2;
232 Ok(val)
233 }
234
235 fn read_u32_le(ctx: &mut ExecutionContext) -> Result<u32, VMError> {
236 if ctx.ip + 3 >= ctx.script.len() {
237 return Err(VMError::InvalidScript);
238 }
239 let val = u32::from_le_bytes([
240 ctx.script[ctx.ip],
241 ctx.script[ctx.ip + 1],
242 ctx.script[ctx.ip + 2],
243 ctx.script[ctx.ip + 3],
244 ]);
245 ctx.ip += 4;
246 Ok(val)
247 }
248
249 fn pop_usize_nonneg(&mut self) -> Result<usize, VMError> {
250 let value = self
251 .eval_stack
252 .pop()
253 .and_then(|x| x.to_integer())
254 .ok_or(VMError::StackUnderflow)?;
255 if value < 0 {
256 return Err(VMError::InvalidOperation);
257 }
258 Ok(value as usize)
259 }
260
261 fn relative_target(base_ip: usize, offset: i8, script_len: usize) -> Result<usize, VMError> {
262 let target = base_ip as isize + offset as isize;
263 if target < 0 || target as usize > script_len {
264 return Err(VMError::InvalidScript);
265 }
266 Ok(target as usize)
267 }
268
269 #[inline]
271 fn push(&mut self, item: StackItem) -> Result<(), VMError> {
272 if self.eval_stack.len() >= self.max_stack_depth {
273 return Err(VMError::StackOverflow(self.max_stack_depth));
274 }
275 self.eval_stack.push(item);
276 Ok(())
277 }
278
279 #[inline]
281 fn check_invocation_depth(&self) -> Result<(), VMError> {
282 if self.invocation_stack.len() >= self.max_invocation_depth {
283 return Err(VMError::InvocationDepthExceeded(self.max_invocation_depth));
284 }
285 Ok(())
286 }
287
288 #[inline]
289 pub fn load_script(&mut self, script: Vec<u8>) -> Result<(), VMError> {
290 if script.len() > MAX_SCRIPT_SIZE {
291 return Err(VMError::InvalidScript);
292 }
293 self.check_invocation_depth()?;
294 self.invocation_stack
295 .push(ExecutionContext { script, ip: 0 });
296 Ok(())
297 }
298
299 pub fn execute_next(&mut self) -> Result<(), VMError> {
300 let ctx = self
301 .invocation_stack
302 .last_mut()
303 .ok_or(VMError::StackUnderflow)?;
304
305 if ctx.ip >= ctx.script.len() {
306 self.state = VMState::Halt;
307 if self.tracing_enabled {
308 self.trace.final_state_hash = self.compute_state_hash();
309 }
310 return Ok(());
311 }
312
313 let ip = ctx.ip;
314 let op = ctx.script[ctx.ip];
315 ctx.ip += 1;
316
317 let gas_cost = get_gas_cost(op);
319 self.gas_consumed += gas_cost;
320 if self.gas_consumed > self.gas_limit {
321 self.state = VMState::Fault;
322 return Err(VMError::OutOfGas);
323 }
324
325 if self.tracing_enabled {
327 let step = TraceStep {
328 ip,
329 opcode: op,
330 stack_hash: self.compute_state_hash(),
331 gas_consumed: self.gas_consumed,
332 };
333 self.trace.steps.push(step);
334 }
335
336 if let Err(e) = self.execute_op(op) {
337 self.state = VMState::Fault;
338 return Err(e);
339 }
340 Ok(())
341 }
342
343 fn execute_op(&mut self, op: u8) -> Result<(), VMError> {
344 match op {
345 0x10 => self.push(StackItem::Integer(0))?,
346 0x11..=0x20 => {
347 let n = (op - 0x10) as i128;
348 self.push(StackItem::Integer(n))?;
349 }
350 0x0F => self.push(StackItem::Integer(-1))?,
351 0x0B => self.push(StackItem::Null)?,
352 0x0C => {
354 let ctx = self
355 .invocation_stack
356 .last_mut()
357 .ok_or(VMError::StackUnderflow)?;
358 let len = Self::read_u8(ctx)? as usize;
359 if ctx.ip + len > ctx.script.len() {
360 return Err(VMError::InvalidScript);
361 }
362 let data = ctx.script[ctx.ip..ctx.ip + len].to_vec();
363 ctx.ip += len;
364 self.push(StackItem::ByteString(data))?;
365 }
366 0x0D => {
368 let ctx = self
369 .invocation_stack
370 .last_mut()
371 .ok_or(VMError::StackUnderflow)?;
372 let len = Self::read_u16_le(ctx)? as usize;
373 if ctx.ip + len > ctx.script.len() {
374 return Err(VMError::InvalidScript);
375 }
376 let data = ctx.script[ctx.ip..ctx.ip + len].to_vec();
377 ctx.ip += len;
378 self.push(StackItem::ByteString(data))?;
379 }
380 0x00 => {
382 let ctx = self
383 .invocation_stack
384 .last_mut()
385 .ok_or(VMError::StackUnderflow)?;
386 let val = Self::read_u8(ctx)? as i8 as i128;
387 self.push(StackItem::Integer(val))?;
388 }
389 0x01 => {
391 let ctx = self
392 .invocation_stack
393 .last_mut()
394 .ok_or(VMError::StackUnderflow)?;
395 let val = i16::from_le_bytes(Self::read_u16_le(ctx)?.to_le_bytes()) as i128;
396 self.push(StackItem::Integer(val))?;
397 }
398 0x45 => {
399 self.eval_stack.pop().ok_or(VMError::StackUnderflow)?;
400 }
401 0x4A => {
402 let item = self
403 .eval_stack
404 .last()
405 .ok_or(VMError::StackUnderflow)?
406 .clone();
407 self.push(item)?;
408 }
409 0x9E => {
411 let b = self
412 .eval_stack
413 .pop()
414 .and_then(|x| x.to_integer())
415 .ok_or(VMError::StackUnderflow)?;
416 let a = self
417 .eval_stack
418 .pop()
419 .and_then(|x| x.to_integer())
420 .ok_or(VMError::StackUnderflow)?;
421 let result = a.checked_add(b).ok_or(VMError::InvalidOperation)?;
422 self.push(StackItem::Integer(result))?;
423 }
424 0x9F => {
426 let b = self
427 .eval_stack
428 .pop()
429 .and_then(|x| x.to_integer())
430 .ok_or(VMError::StackUnderflow)?;
431 let a = self
432 .eval_stack
433 .pop()
434 .and_then(|x| x.to_integer())
435 .ok_or(VMError::StackUnderflow)?;
436 let result = a.checked_sub(b).ok_or(VMError::InvalidOperation)?;
437 self.push(StackItem::Integer(result))?;
438 }
439 0xA0 => {
441 let b = self
442 .eval_stack
443 .pop()
444 .and_then(|x| x.to_integer())
445 .ok_or(VMError::StackUnderflow)?;
446 let a = self
447 .eval_stack
448 .pop()
449 .and_then(|x| x.to_integer())
450 .ok_or(VMError::StackUnderflow)?;
451 let result = a.checked_mul(b).ok_or(VMError::InvalidOperation)?;
452 self.push(StackItem::Integer(result))?;
453 }
454 0xA1 => {
456 let b = self
457 .eval_stack
458 .pop()
459 .and_then(|x| x.to_integer())
460 .ok_or(VMError::StackUnderflow)?;
461 let a = self
462 .eval_stack
463 .pop()
464 .and_then(|x| x.to_integer())
465 .ok_or(VMError::StackUnderflow)?;
466 if b == 0 {
467 return Err(VMError::DivisionByZero);
468 }
469 let result = a.checked_div(b).ok_or(VMError::InvalidOperation)?;
470 self.push(StackItem::Integer(result))?;
471 }
472 0xA2 => {
474 let b = self
475 .eval_stack
476 .pop()
477 .and_then(|x| x.to_integer())
478 .ok_or(VMError::StackUnderflow)?;
479 let a = self
480 .eval_stack
481 .pop()
482 .and_then(|x| x.to_integer())
483 .ok_or(VMError::StackUnderflow)?;
484 if b == 0 {
485 return Err(VMError::DivisionByZero);
486 }
487 let result = a.checked_rem(b).ok_or(VMError::InvalidOperation)?;
488 self.push(StackItem::Integer(result))?;
489 }
490 0xA3 => {
492 let exp = self
493 .eval_stack
494 .pop()
495 .and_then(|x| x.to_integer())
496 .ok_or(VMError::StackUnderflow)?;
497 let base = self
498 .eval_stack
499 .pop()
500 .and_then(|x| x.to_integer())
501 .ok_or(VMError::StackUnderflow)?;
502 if exp < 0 {
503 return Err(VMError::InvalidOperation);
504 }
505 let result = base.pow(exp as u32);
506 self.push(StackItem::Integer(result))?;
507 }
508 0xA8 => {
510 let shift = self
511 .eval_stack
512 .pop()
513 .and_then(|x| x.to_integer())
514 .ok_or(VMError::StackUnderflow)?;
515 let value = self
516 .eval_stack
517 .pop()
518 .and_then(|x| x.to_integer())
519 .ok_or(VMError::StackUnderflow)?;
520 if !(0..=256).contains(&shift) {
521 return Err(VMError::InvalidOperation);
522 }
523 let result = value
524 .checked_shl(shift as u32)
525 .ok_or(VMError::InvalidOperation)?;
526 self.push(StackItem::Integer(result))?;
527 }
528 0xA9 => {
530 let shift = self
531 .eval_stack
532 .pop()
533 .and_then(|x| x.to_integer())
534 .ok_or(VMError::StackUnderflow)?;
535 let value = self
536 .eval_stack
537 .pop()
538 .and_then(|x| x.to_integer())
539 .ok_or(VMError::StackUnderflow)?;
540 if !(0..=256).contains(&shift) {
541 return Err(VMError::InvalidOperation);
542 }
543 let result = value
544 .checked_shr(shift as u32)
545 .ok_or(VMError::InvalidOperation)?;
546 self.push(StackItem::Integer(result))?;
547 }
548 0xB9 => {
550 let b = self
551 .eval_stack
552 .pop()
553 .and_then(|x| x.to_integer())
554 .ok_or(VMError::StackUnderflow)?;
555 let a = self
556 .eval_stack
557 .pop()
558 .and_then(|x| x.to_integer())
559 .ok_or(VMError::StackUnderflow)?;
560 self.push(StackItem::Integer(a.min(b)))?;
561 }
562 0xBA => {
564 let b = self
565 .eval_stack
566 .pop()
567 .and_then(|x| x.to_integer())
568 .ok_or(VMError::StackUnderflow)?;
569 let a = self
570 .eval_stack
571 .pop()
572 .and_then(|x| x.to_integer())
573 .ok_or(VMError::StackUnderflow)?;
574 self.push(StackItem::Integer(a.max(b)))?;
575 }
576 0xBB => {
578 let b = self
579 .eval_stack
580 .pop()
581 .and_then(|x| x.to_integer())
582 .ok_or(VMError::StackUnderflow)?;
583 let a = self
584 .eval_stack
585 .pop()
586 .and_then(|x| x.to_integer())
587 .ok_or(VMError::StackUnderflow)?;
588 let x = self
589 .eval_stack
590 .pop()
591 .and_then(|x| x.to_integer())
592 .ok_or(VMError::StackUnderflow)?;
593 self.push(StackItem::Boolean(a <= x && x < b))?;
594 }
595 0x99 => {
597 let a = self
598 .eval_stack
599 .pop()
600 .and_then(|x| x.to_integer())
601 .ok_or(VMError::StackUnderflow)?;
602 let sign = if a > 0 {
603 1
604 } else if a < 0 {
605 -1
606 } else {
607 0
608 };
609 self.push(StackItem::Integer(sign))?;
610 }
611 0x9A => {
613 let a = self
614 .eval_stack
615 .pop()
616 .and_then(|x| x.to_integer())
617 .ok_or(VMError::StackUnderflow)?;
618 let result = a.checked_abs().ok_or(VMError::InvalidOperation)?;
619 self.push(StackItem::Integer(result))?;
620 }
621 0x9B => {
623 let a = self
624 .eval_stack
625 .pop()
626 .and_then(|x| x.to_integer())
627 .ok_or(VMError::StackUnderflow)?;
628 let result = a.checked_neg().ok_or(VMError::InvalidOperation)?;
629 self.push(StackItem::Integer(result))?;
630 }
631 0x9C => {
633 let a = self
634 .eval_stack
635 .pop()
636 .and_then(|x| x.to_integer())
637 .ok_or(VMError::StackUnderflow)?;
638 let result = a.checked_add(1).ok_or(VMError::InvalidOperation)?;
639 self.push(StackItem::Integer(result))?;
640 }
641 0x9D => {
643 let a = self
644 .eval_stack
645 .pop()
646 .and_then(|x| x.to_integer())
647 .ok_or(VMError::StackUnderflow)?;
648 let result = a.checked_sub(1).ok_or(VMError::InvalidOperation)?;
649 self.push(StackItem::Integer(result))?;
650 }
651 0xB5 => {
653 let b = self
654 .eval_stack
655 .pop()
656 .and_then(|x| x.to_integer())
657 .ok_or(VMError::StackUnderflow)?;
658 let a = self
659 .eval_stack
660 .pop()
661 .and_then(|x| x.to_integer())
662 .ok_or(VMError::StackUnderflow)?;
663 self.push(StackItem::Boolean(a < b))?;
664 }
665 0xB6 => {
667 let b = self
668 .eval_stack
669 .pop()
670 .and_then(|x| x.to_integer())
671 .ok_or(VMError::StackUnderflow)?;
672 let a = self
673 .eval_stack
674 .pop()
675 .and_then(|x| x.to_integer())
676 .ok_or(VMError::StackUnderflow)?;
677 self.push(StackItem::Boolean(a <= b))?;
678 }
679 0xB7 => {
681 let b = self
682 .eval_stack
683 .pop()
684 .and_then(|x| x.to_integer())
685 .ok_or(VMError::StackUnderflow)?;
686 let a = self
687 .eval_stack
688 .pop()
689 .and_then(|x| x.to_integer())
690 .ok_or(VMError::StackUnderflow)?;
691 self.push(StackItem::Boolean(a > b))?;
692 }
693 0xB8 => {
695 let b = self
696 .eval_stack
697 .pop()
698 .and_then(|x| x.to_integer())
699 .ok_or(VMError::StackUnderflow)?;
700 let a = self
701 .eval_stack
702 .pop()
703 .and_then(|x| x.to_integer())
704 .ok_or(VMError::StackUnderflow)?;
705 self.push(StackItem::Boolean(a >= b))?;
706 }
707 0x97 => {
709 let b = self.eval_stack.pop().ok_or(VMError::StackUnderflow)?;
710 let a = self.eval_stack.pop().ok_or(VMError::StackUnderflow)?;
711 self.push(StackItem::Boolean(a == b))?;
712 }
713 0x98 => {
715 let b = self.eval_stack.pop().ok_or(VMError::StackUnderflow)?;
716 let a = self.eval_stack.pop().ok_or(VMError::StackUnderflow)?;
717 self.push(StackItem::Boolean(a != b))?;
718 }
719 0xD8 => {
721 let item = self.eval_stack.pop().ok_or(VMError::StackUnderflow)?;
722 self.eval_stack
723 .push(StackItem::Boolean(matches!(item, StackItem::Null)));
724 }
725 0xB1 => {
727 let a = self
728 .eval_stack
729 .pop()
730 .and_then(|x| x.to_integer())
731 .ok_or(VMError::StackUnderflow)?;
732 self.push(StackItem::Boolean(a != 0))?;
733 }
734 0xB3 => {
736 let b = self
737 .eval_stack
738 .pop()
739 .and_then(|x| x.to_integer())
740 .ok_or(VMError::StackUnderflow)?;
741 let a = self
742 .eval_stack
743 .pop()
744 .and_then(|x| x.to_integer())
745 .ok_or(VMError::StackUnderflow)?;
746 self.push(StackItem::Boolean(a == b))?;
747 }
748 0xB4 => {
750 let b = self
751 .eval_stack
752 .pop()
753 .and_then(|x| x.to_integer())
754 .ok_or(VMError::StackUnderflow)?;
755 let a = self
756 .eval_stack
757 .pop()
758 .and_then(|x| x.to_integer())
759 .ok_or(VMError::StackUnderflow)?;
760 self.push(StackItem::Boolean(a != b))?;
761 }
762 0x90 => {
764 let a = self
765 .eval_stack
766 .pop()
767 .and_then(|x| x.to_integer())
768 .ok_or(VMError::StackUnderflow)?;
769 self.push(StackItem::Integer(!a))?;
770 }
771 0x91 => {
773 let b = self
774 .eval_stack
775 .pop()
776 .and_then(|x| x.to_integer())
777 .ok_or(VMError::StackUnderflow)?;
778 let a = self
779 .eval_stack
780 .pop()
781 .and_then(|x| x.to_integer())
782 .ok_or(VMError::StackUnderflow)?;
783 self.push(StackItem::Integer(a & b))?;
784 }
785 0x92 => {
787 let b = self
788 .eval_stack
789 .pop()
790 .and_then(|x| x.to_integer())
791 .ok_or(VMError::StackUnderflow)?;
792 let a = self
793 .eval_stack
794 .pop()
795 .and_then(|x| x.to_integer())
796 .ok_or(VMError::StackUnderflow)?;
797 self.push(StackItem::Integer(a | b))?;
798 }
799 0x93 => {
801 let b = self
802 .eval_stack
803 .pop()
804 .and_then(|x| x.to_integer())
805 .ok_or(VMError::StackUnderflow)?;
806 let a = self
807 .eval_stack
808 .pop()
809 .and_then(|x| x.to_integer())
810 .ok_or(VMError::StackUnderflow)?;
811 self.push(StackItem::Integer(a ^ b))?;
812 }
813 0xAA => {
815 let a = self.eval_stack.pop().ok_or(VMError::StackUnderflow)?;
816 self.push(StackItem::Boolean(!a.to_bool()))?;
817 }
818 0xAB => {
820 let b = self.eval_stack.pop().ok_or(VMError::StackUnderflow)?;
821 let a = self.eval_stack.pop().ok_or(VMError::StackUnderflow)?;
822 self.eval_stack
823 .push(StackItem::Boolean(a.to_bool() && b.to_bool()));
824 }
825 0xAC => {
827 let b = self.eval_stack.pop().ok_or(VMError::StackUnderflow)?;
828 let a = self.eval_stack.pop().ok_or(VMError::StackUnderflow)?;
829 self.eval_stack
830 .push(StackItem::Boolean(a.to_bool() || b.to_bool()));
831 }
832 0x50 => {
834 let len = self.eval_stack.len();
835 if len < 2 {
836 return Err(VMError::StackUnderflow);
837 }
838 self.eval_stack.swap(len - 1, len - 2);
839 }
840 0x51 => {
842 let len = self.eval_stack.len();
843 if len < 3 {
844 return Err(VMError::StackUnderflow);
845 }
846 let item = self.eval_stack.remove(len - 3);
847 self.push(item)?;
848 }
849 0x4D => {
851 let n = self.pop_usize_nonneg()?;
852 let len = self.eval_stack.len();
853 if n >= len {
854 return Err(VMError::StackUnderflow);
855 }
856 let item = self.eval_stack[len - 1 - n].clone();
857 self.push(item)?;
858 }
859 0x52 => {
861 let n = self.pop_usize_nonneg()?;
862 let len = self.eval_stack.len();
863 if n >= len {
864 return Err(VMError::StackUnderflow);
865 }
866 let item = self.eval_stack.remove(len - 1 - n);
867 self.push(item)?;
868 }
869 0x4B => {
871 let len = self.eval_stack.len();
872 if len < 2 {
873 return Err(VMError::StackUnderflow);
874 }
875 let item = self.eval_stack[len - 2].clone();
876 self.push(item)?;
877 }
878 0x43 => {
880 let depth = self.eval_stack.len() as i128;
881 self.push(StackItem::Integer(depth))?;
882 }
883 0x46 => {
885 let len = self.eval_stack.len();
886 if len < 2 {
887 return Err(VMError::StackUnderflow);
888 }
889 self.eval_stack.remove(len - 2);
890 }
891 0x48 => {
893 let n = self.pop_usize_nonneg()?;
894 let len = self.eval_stack.len();
895 if n >= len {
896 return Err(VMError::StackUnderflow);
897 }
898 self.eval_stack.remove(len - 1 - n);
899 }
900 0x49 => {
902 self.eval_stack.clear();
903 }
904 0x4E => {
906 let len = self.eval_stack.len();
907 if len < 2 {
908 return Err(VMError::StackUnderflow);
909 }
910 let item = self.eval_stack[len - 1].clone();
911 self.eval_stack.insert(len - 2, item);
912 }
913 0x53 => {
915 let len = self.eval_stack.len();
916 if len < 3 {
917 return Err(VMError::StackUnderflow);
918 }
919 self.eval_stack.swap(len - 1, len - 3);
920 }
921 0x54 => {
923 let len = self.eval_stack.len();
924 if len < 4 {
925 return Err(VMError::StackUnderflow);
926 }
927 self.eval_stack.swap(len - 1, len - 4);
928 self.eval_stack.swap(len - 2, len - 3);
929 }
930 0x55 => {
932 let n = self.pop_usize_nonneg()?;
933 let len = self.eval_stack.len();
934 if n > len {
935 return Err(VMError::StackUnderflow);
936 }
937 let start = len - n;
938 self.eval_stack[start..].reverse();
939 }
940 0x57 => {
942 let ctx = self
943 .invocation_stack
944 .last_mut()
945 .ok_or(VMError::StackUnderflow)?;
946 let local_count = Self::read_u8(ctx)? as usize;
947 let arg_count = Self::read_u8(ctx)? as usize;
948 self.local_slots = vec![StackItem::Null; local_count];
949 self.argument_slots = Vec::with_capacity(arg_count);
951 for _ in 0..arg_count {
952 let arg = self.eval_stack.pop().ok_or(VMError::StackUnderflow)?;
953 self.argument_slots.push(arg);
954 }
955 self.argument_slots.reverse();
956 }
957 0x66..=0x6C => {
959 let idx = (op - 0x66) as usize;
960 let item = self
961 .local_slots
962 .get(idx)
963 .cloned()
964 .ok_or(VMError::InvalidOperation)?;
965 self.push(item)?;
966 }
967 0x6D => {
969 let ctx = self
970 .invocation_stack
971 .last_mut()
972 .ok_or(VMError::StackUnderflow)?;
973 let idx = Self::read_u8(ctx)? as usize;
974 let item = self
975 .local_slots
976 .get(idx)
977 .cloned()
978 .ok_or(VMError::InvalidOperation)?;
979 self.push(item)?;
980 }
981 0x6E..=0x72 => {
983 let val = self.eval_stack.pop().ok_or(VMError::StackUnderflow)?;
984 let idx = (op - 0x6E) as usize;
985 if idx >= self.local_slots.len() {
986 self.local_slots.resize(idx + 1, StackItem::Null);
987 }
988 self.local_slots[idx] = val;
989 }
990 0x73 => {
992 let ctx = self
993 .invocation_stack
994 .last_mut()
995 .ok_or(VMError::StackUnderflow)?;
996 let idx = Self::read_u8(ctx)? as usize;
997 let item = self.eval_stack.pop().ok_or(VMError::StackUnderflow)?;
998 if idx >= self.local_slots.len() {
999 return Err(VMError::InvalidOperation);
1000 }
1001 self.local_slots[idx] = item;
1002 }
1003 0x74..=0x79 => {
1005 let idx = (op - 0x74) as usize;
1006 let item = self
1007 .argument_slots
1008 .get(idx)
1009 .cloned()
1010 .ok_or(VMError::InvalidOperation)?;
1011 self.push(item)?;
1012 }
1013 0x7A => {
1015 let ctx = self
1016 .invocation_stack
1017 .last_mut()
1018 .ok_or(VMError::StackUnderflow)?;
1019 let idx = Self::read_u8(ctx)? as usize;
1020 let item = self
1021 .argument_slots
1022 .get(idx)
1023 .cloned()
1024 .ok_or(VMError::InvalidOperation)?;
1025 self.push(item)?;
1026 }
1027 0x21 => {}
1029 0x39 => {
1031 let cond = self.eval_stack.pop().ok_or(VMError::StackUnderflow)?;
1032 if !cond.to_bool() {
1033 self.state = VMState::Fault;
1034 return Err(VMError::InvalidOperation);
1035 }
1036 }
1037 0x22 => {
1039 let ctx = self
1040 .invocation_stack
1041 .last_mut()
1042 .ok_or(VMError::StackUnderflow)?;
1043 let base_ip = ctx.ip.checked_sub(1).ok_or(VMError::InvalidScript)?;
1044 let offset = Self::read_i8(ctx)?;
1045 ctx.ip = Self::relative_target(base_ip, offset, ctx.script.len())?;
1046 }
1047 0x24 => {
1049 let ctx = self
1050 .invocation_stack
1051 .last_mut()
1052 .ok_or(VMError::StackUnderflow)?;
1053 let base_ip = ctx.ip.checked_sub(1).ok_or(VMError::InvalidScript)?;
1054 let offset = Self::read_i8(ctx)?;
1055 let cond = self.eval_stack.pop().ok_or(VMError::StackUnderflow)?;
1056 if cond.to_bool() {
1057 ctx.ip = Self::relative_target(base_ip, offset, ctx.script.len())?;
1058 }
1059 }
1060 0x26 => {
1062 let ctx = self
1063 .invocation_stack
1064 .last_mut()
1065 .ok_or(VMError::StackUnderflow)?;
1066 let base_ip = ctx.ip.checked_sub(1).ok_or(VMError::InvalidScript)?;
1067 let offset = Self::read_i8(ctx)?;
1068 let cond = self.eval_stack.pop().ok_or(VMError::StackUnderflow)?;
1069 if !cond.to_bool() {
1070 ctx.ip = Self::relative_target(base_ip, offset, ctx.script.len())?;
1071 }
1072 }
1073 0x28 => {
1075 let ctx = self
1076 .invocation_stack
1077 .last_mut()
1078 .ok_or(VMError::StackUnderflow)?;
1079 let base_ip = ctx.ip.checked_sub(1).ok_or(VMError::InvalidScript)?;
1080 let offset = Self::read_i8(ctx)?;
1081 let b = self
1082 .eval_stack
1083 .pop()
1084 .and_then(|x| x.to_integer())
1085 .ok_or(VMError::StackUnderflow)?;
1086 let a = self
1087 .eval_stack
1088 .pop()
1089 .and_then(|x| x.to_integer())
1090 .ok_or(VMError::StackUnderflow)?;
1091 if a == b {
1092 ctx.ip = Self::relative_target(base_ip, offset, ctx.script.len())?;
1093 }
1094 }
1095 0x2A => {
1097 let ctx = self
1098 .invocation_stack
1099 .last_mut()
1100 .ok_or(VMError::StackUnderflow)?;
1101 let base_ip = ctx.ip.checked_sub(1).ok_or(VMError::InvalidScript)?;
1102 let offset = Self::read_i8(ctx)?;
1103 let b = self
1104 .eval_stack
1105 .pop()
1106 .and_then(|x| x.to_integer())
1107 .ok_or(VMError::StackUnderflow)?;
1108 let a = self
1109 .eval_stack
1110 .pop()
1111 .and_then(|x| x.to_integer())
1112 .ok_or(VMError::StackUnderflow)?;
1113 if a != b {
1114 ctx.ip = Self::relative_target(base_ip, offset, ctx.script.len())?;
1115 }
1116 }
1117 0x2C => {
1119 let ctx = self
1120 .invocation_stack
1121 .last_mut()
1122 .ok_or(VMError::StackUnderflow)?;
1123 let base_ip = ctx.ip.checked_sub(1).ok_or(VMError::InvalidScript)?;
1124 let offset = Self::read_i8(ctx)?;
1125 let b = self
1126 .eval_stack
1127 .pop()
1128 .and_then(|x| x.to_integer())
1129 .ok_or(VMError::StackUnderflow)?;
1130 let a = self
1131 .eval_stack
1132 .pop()
1133 .and_then(|x| x.to_integer())
1134 .ok_or(VMError::StackUnderflow)?;
1135 if a > b {
1136 ctx.ip = Self::relative_target(base_ip, offset, ctx.script.len())?;
1137 }
1138 }
1139 0x2E => {
1141 let ctx = self
1142 .invocation_stack
1143 .last_mut()
1144 .ok_or(VMError::StackUnderflow)?;
1145 let base_ip = ctx.ip.checked_sub(1).ok_or(VMError::InvalidScript)?;
1146 let offset = Self::read_i8(ctx)?;
1147 let b = self
1148 .eval_stack
1149 .pop()
1150 .and_then(|x| x.to_integer())
1151 .ok_or(VMError::StackUnderflow)?;
1152 let a = self
1153 .eval_stack
1154 .pop()
1155 .and_then(|x| x.to_integer())
1156 .ok_or(VMError::StackUnderflow)?;
1157 if a >= b {
1158 ctx.ip = Self::relative_target(base_ip, offset, ctx.script.len())?;
1159 }
1160 }
1161 0x30 => {
1163 let ctx = self
1164 .invocation_stack
1165 .last_mut()
1166 .ok_or(VMError::StackUnderflow)?;
1167 let base_ip = ctx.ip.checked_sub(1).ok_or(VMError::InvalidScript)?;
1168 let offset = Self::read_i8(ctx)?;
1169 let b = self
1170 .eval_stack
1171 .pop()
1172 .and_then(|x| x.to_integer())
1173 .ok_or(VMError::StackUnderflow)?;
1174 let a = self
1175 .eval_stack
1176 .pop()
1177 .and_then(|x| x.to_integer())
1178 .ok_or(VMError::StackUnderflow)?;
1179 if a < b {
1180 ctx.ip = Self::relative_target(base_ip, offset, ctx.script.len())?;
1181 }
1182 }
1183 0x32 => {
1185 let ctx = self
1186 .invocation_stack
1187 .last_mut()
1188 .ok_or(VMError::StackUnderflow)?;
1189 let base_ip = ctx.ip.checked_sub(1).ok_or(VMError::InvalidScript)?;
1190 let offset = Self::read_i8(ctx)?;
1191 let b = self
1192 .eval_stack
1193 .pop()
1194 .and_then(|x| x.to_integer())
1195 .ok_or(VMError::StackUnderflow)?;
1196 let a = self
1197 .eval_stack
1198 .pop()
1199 .and_then(|x| x.to_integer())
1200 .ok_or(VMError::StackUnderflow)?;
1201 if a <= b {
1202 ctx.ip = Self::relative_target(base_ip, offset, ctx.script.len())?;
1203 }
1204 }
1205 0x34 => {
1207 self.check_invocation_depth()?;
1208 let (return_ip, target_ip, script) = {
1209 let ctx = self
1210 .invocation_stack
1211 .last_mut()
1212 .ok_or(VMError::StackUnderflow)?;
1213 let base_ip = ctx.ip.checked_sub(1).ok_or(VMError::InvalidScript)?;
1214 let offset = Self::read_i8(ctx)?;
1215 let return_ip = ctx.ip;
1216 let target_ip = Self::relative_target(base_ip, offset, ctx.script.len())?;
1217 let script = ctx.script.clone();
1218 (return_ip, target_ip, script)
1219 };
1220 self.invocation_stack.push(ExecutionContext {
1221 script,
1222 ip: target_ip,
1223 });
1224 self.push(StackItem::Pointer(return_ip as u32))?;
1226 }
1227 0xF0 => {
1229 let data = self.eval_stack.pop().ok_or(VMError::StackUnderflow)?;
1230 let bytes = match data {
1231 StackItem::ByteString(b) | StackItem::Buffer(b) => b,
1232 StackItem::Integer(i) => i.to_le_bytes().to_vec(),
1233 _ => return Err(VMError::InvalidType),
1234 };
1235 let mut hasher = Sha256::new();
1236 hasher.update(&bytes);
1237 let result = hasher.finalize().to_vec();
1238 self.push(StackItem::ByteString(result))?;
1239 }
1240 0xF1 => {
1242 let data = self.eval_stack.pop().ok_or(VMError::StackUnderflow)?;
1243 let bytes = match data {
1244 StackItem::ByteString(b) | StackItem::Buffer(b) => b,
1245 StackItem::Integer(i) => i.to_le_bytes().to_vec(),
1246 _ => return Err(VMError::InvalidType),
1247 };
1248 let mut hasher = Ripemd160::new();
1249 hasher.update(&bytes);
1250 let result = hasher.finalize().to_vec();
1251 self.push(StackItem::ByteString(result))?;
1252 }
1253 0xF2 => {
1255 let data = self.eval_stack.pop().ok_or(VMError::StackUnderflow)?;
1256 let bytes = match data {
1257 StackItem::ByteString(b) | StackItem::Buffer(b) => b,
1258 StackItem::Integer(i) => i.to_le_bytes().to_vec(),
1259 _ => return Err(VMError::InvalidType),
1260 };
1261 let sha_result = Sha256::digest(&bytes);
1262 let result = Ripemd160::digest(sha_result).to_vec();
1263 self.push(StackItem::ByteString(result))?;
1264 }
1265 0xF3 => {
1267 let pubkey = self.eval_stack.pop().ok_or(VMError::StackUnderflow)?;
1268 let sig = self.eval_stack.pop().ok_or(VMError::StackUnderflow)?;
1269 let msg = self.eval_stack.pop().ok_or(VMError::StackUnderflow)?;
1270
1271 let pubkey_bytes = match pubkey {
1272 StackItem::ByteString(b) | StackItem::Buffer(b) => b,
1273 _ => return Err(VMError::InvalidType),
1274 };
1275 let sig_bytes = match sig {
1276 StackItem::ByteString(b) | StackItem::Buffer(b) => b,
1277 _ => return Err(VMError::InvalidType),
1278 };
1279 let msg_bytes = match msg {
1280 StackItem::ByteString(b) | StackItem::Buffer(b) => b,
1281 _ => return Err(VMError::InvalidType),
1282 };
1283
1284 let result = VerifyingKey::from_sec1_bytes(&pubkey_bytes)
1285 .map_err(|_| VMError::InvalidPublicKey)?;
1286 let signature =
1287 Signature::from_slice(&sig_bytes).map_err(|_| VMError::InvalidSignature)?;
1288 let msg_hash = Sha256::digest(&msg_bytes);
1289
1290 let verified = result.verify(&msg_hash, &signature).is_ok();
1291 self.push(StackItem::Boolean(verified))?;
1292 }
1293 0x41 => {
1295 let ctx = self
1296 .invocation_stack
1297 .last_mut()
1298 .ok_or(VMError::StackUnderflow)?;
1299 let id = Self::read_u32_le(ctx)?;
1300 self.execute_syscall(id)?;
1301 }
1302 0xC2 => {
1304 self.push(StackItem::Array(Vec::new()))?;
1305 }
1306 0xC3 => {
1308 let n = self.pop_usize_nonneg()?;
1309 let arr = vec![StackItem::Null; n];
1310 self.push(StackItem::Array(arr))?;
1311 }
1312 0xC5 => {
1314 self.push(StackItem::Struct(Vec::new()))?;
1315 }
1316 0xC6 => {
1318 let n = self.pop_usize_nonneg()?;
1319 let s = vec![StackItem::Null; n];
1320 self.push(StackItem::Struct(s))?;
1321 }
1322 0xC8 => {
1324 self.push(StackItem::Map(Vec::new()))?;
1325 }
1326 0xCA => {
1328 let item = self.eval_stack.pop().ok_or(VMError::StackUnderflow)?;
1329 let size = match &item {
1330 StackItem::Array(a) | StackItem::Struct(a) => a.len(),
1331 StackItem::Map(m) => m.len(),
1332 StackItem::ByteString(b) | StackItem::Buffer(b) => b.len(),
1333 _ => return Err(VMError::InvalidType),
1334 };
1335 self.push(StackItem::Integer(size as i128))?;
1336 }
1337 0xCE => {
1339 let key = self.eval_stack.pop().ok_or(VMError::StackUnderflow)?;
1340 let container = self.eval_stack.pop().ok_or(VMError::StackUnderflow)?;
1341 let item = match (container, key) {
1342 (StackItem::Array(a), StackItem::Integer(i)) => a
1343 .get(i as usize)
1344 .cloned()
1345 .ok_or(VMError::InvalidOperation)?,
1346 (StackItem::Struct(s), StackItem::Integer(i)) => s
1347 .get(i as usize)
1348 .cloned()
1349 .ok_or(VMError::InvalidOperation)?,
1350 (StackItem::Map(m), k) => m
1351 .iter()
1352 .find(|(mk, _)| *mk == k)
1353 .map(|(_, v)| v.clone())
1354 .ok_or(VMError::InvalidOperation)?,
1355 _ => return Err(VMError::InvalidType),
1356 };
1357 self.push(item)?;
1358 }
1359 0xD0 => {
1361 let value = self.eval_stack.pop().ok_or(VMError::StackUnderflow)?;
1362 let key = self.eval_stack.pop().ok_or(VMError::StackUnderflow)?;
1363 let container = self.eval_stack.last_mut().ok_or(VMError::StackUnderflow)?;
1364 match (container, key) {
1365 (StackItem::Array(a), StackItem::Integer(i)) => {
1366 let idx = i as usize;
1367 if idx >= a.len() {
1368 return Err(VMError::InvalidOperation);
1369 }
1370 a[idx] = value;
1371 }
1372 (StackItem::Map(m), k) => {
1373 if let Some(entry) = m.iter_mut().find(|(mk, _)| *mk == k) {
1374 entry.1 = value;
1375 } else {
1376 m.push((k, value));
1377 }
1378 }
1379 _ => return Err(VMError::InvalidType),
1380 }
1381 }
1382 0xCF => {
1384 let item = self.eval_stack.pop().ok_or(VMError::StackUnderflow)?;
1385 let container = self.eval_stack.last_mut().ok_or(VMError::StackUnderflow)?;
1386 match container {
1387 StackItem::Array(a) => a.push(item),
1388 _ => return Err(VMError::InvalidType),
1389 }
1390 }
1391 0xD2 => {
1393 let key = self.eval_stack.pop().ok_or(VMError::StackUnderflow)?;
1394 let container = self.eval_stack.last_mut().ok_or(VMError::StackUnderflow)?;
1395 match (container, key) {
1396 (StackItem::Array(a), StackItem::Integer(i)) => {
1397 let idx = i as usize;
1398 if idx >= a.len() {
1399 return Err(VMError::InvalidOperation);
1400 }
1401 a.remove(idx);
1402 }
1403 (StackItem::Map(m), k) => {
1404 m.retain(|(mk, _)| *mk != k);
1405 }
1406 _ => return Err(VMError::InvalidType),
1407 }
1408 }
1409 0x40 => {
1411 self.invocation_stack
1412 .pop()
1413 .ok_or(VMError::InvalidOperation)?;
1414 if self.invocation_stack.is_empty() {
1415 self.state = VMState::Halt;
1416 }
1417 }
1418 _ => return Err(VMError::InvalidOpcode(op)),
1419 }
1420 Ok(())
1421 }
1422
1423 fn execute_syscall(&mut self, id: u32) -> Result<(), VMError> {
1424 match id {
1425 syscall::SYSTEM_RUNTIME_LOG => {
1426 let msg = self.eval_stack.pop().ok_or(VMError::StackUnderflow)?;
1427 if let StackItem::ByteString(b) = msg {
1428 if let Ok(s) = String::from_utf8(b) {
1429 self.logs.push(s);
1430 }
1431 }
1432 Ok(())
1433 }
1434 syscall::SYSTEM_RUNTIME_NOTIFY => {
1435 let item = self.eval_stack.pop().ok_or(VMError::StackUnderflow)?;
1436 self.notifications.push(item);
1437 Ok(())
1438 }
1439 syscall::SYSTEM_RUNTIME_GETTIME => {
1440 self.push(StackItem::Integer(0))?;
1442 Ok(())
1443 }
1444 _ => Err(VMError::UnknownSyscall(id)),
1445 }
1446 }
1447}
1448
1449#[cfg(test)]
1450mod tests {
1451 use super::*;
1452
1453 #[test]
1454 fn test_push_operations() {
1455 let mut vm = NeoVM::new(1_000_000);
1456 let _ = vm.load_script(vec![0x11, 0x12, 0x13, 0x40]);
1457
1458 while !matches!(vm.state, VMState::Halt | VMState::Fault) {
1459 vm.execute_next().unwrap();
1460 }
1461
1462 assert!(matches!(vm.state, VMState::Halt));
1463 assert_eq!(vm.eval_stack.len(), 3);
1464 }
1465
1466 #[test]
1467 fn test_add_operation() {
1468 let mut vm = NeoVM::new(1_000_000);
1469 let _ = vm.load_script(vec![0x12, 0x13, 0x9E, 0x40]);
1470
1471 while !matches!(vm.state, VMState::Halt | VMState::Fault) {
1472 vm.execute_next().unwrap();
1473 }
1474
1475 assert_eq!(vm.eval_stack.pop(), Some(StackItem::Integer(5)));
1476 }
1477
1478 #[test]
1479 fn test_sub_operation() {
1480 let mut vm = NeoVM::new(1_000_000);
1481 let _ = vm.load_script(vec![0x15, 0x12, 0x9F, 0x40]);
1482
1483 while !matches!(vm.state, VMState::Halt | VMState::Fault) {
1484 vm.execute_next().unwrap();
1485 }
1486
1487 assert_eq!(vm.eval_stack.pop(), Some(StackItem::Integer(3)));
1488 }
1489
1490 #[test]
1491 fn test_mul_operation() {
1492 let mut vm = NeoVM::new(1_000_000);
1493 let _ = vm.load_script(vec![0x13, 0x14, 0xA0, 0x40]);
1494
1495 while !matches!(vm.state, VMState::Halt | VMState::Fault) {
1496 vm.execute_next().unwrap();
1497 }
1498
1499 assert_eq!(vm.eval_stack.pop(), Some(StackItem::Integer(12)));
1500 }
1501
1502 #[test]
1503 fn test_comparison_lt() {
1504 let mut vm = NeoVM::new(1_000_000);
1505 let _ = vm.load_script(vec![0x12, 0x15, 0xB5, 0x40]);
1506
1507 while !matches!(vm.state, VMState::Halt | VMState::Fault) {
1508 vm.execute_next().unwrap();
1509 }
1510
1511 assert_eq!(vm.eval_stack.pop(), Some(StackItem::Boolean(true)));
1512 }
1513}