Skip to main content

neo_vm_core/
engine.rs

1//! Neo VM Execution Engine
2//!
3//! Neo VM Engine
4//!
5//! Core execution engine for Neo zkVM.
6
7use 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
57// SAFETY: ExecutionContext is designed for single-threaded use within NeoVM.
58unsafe impl Send for ExecutionContext {}
59unsafe impl Sync for ExecutionContext {}
60
61/// Built-in syscall IDs (Neo N3 compatible)
62pub 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
71/// Gas cost lookup table for O(1) opcode cost retrieval
72/// Uses u16 to support CHECKSIG's high gas cost (32768)
73const GAS_COSTS: [u16; 256] = [
74    // 0x00-0x0F (PUSHINT8-PUSHM1)
75    1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 0x10-0x1F (PUSH0-PUSH16)
76    1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 0x20-0x2F
77    1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, // 0x30-0x3F (flow control)
78    2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
79    // 0x40-0x4F (RET, DEPTH, CLEAR, stack ops)
80    2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, // 0x50-0x5F (stack ops)
81    2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, // 0x60-0x6F (slot ops)
82    2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, // 0x70-0x7F (slot ops)
83    2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, // 0x80-0x8F (splice/buffer ops)
84    2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, // 0x90-0x9F (bitwise/invert/equality)
85    8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, // 0xA0-0xAF (arithmetic)
86    8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, // 0xB0-0xBF (comparison/min/max/within)
87    8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, // 0xC0-0xCF (compound types)
88    8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, // 0xD0-0xDF (compound types)
89    2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, // 0xE0-0xEF (reserved)
90    1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
91    // 0xF0-0xFF (crypto: SHA256, RIPEMD160, CHECKSIG)
92    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
100/// Maximum script size in bytes (1MB)
101pub const MAX_SCRIPT_SIZE: usize = 1024 * 1024;
102
103/// Default maximum stack depth
104pub const DEFAULT_MAX_STACK_DEPTH: usize = 2048;
105
106/// Default maximum invocation depth
107pub const DEFAULT_MAX_INVOCATION_DEPTH: usize = 1024;
108
109/// Execution trace step for proof generation
110#[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/// Full execution trace
119#[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    // Slot support for Neo VM compatibility
139    pub local_slots: Vec<StackItem>,
140    pub argument_slots: Vec<StackItem>,
141    pub static_slots: Vec<StackItem>,
142}
143
144impl NeoVM {
145    /// Default stack capacity for pre-allocation
146    const DEFAULT_STACK_CAPACITY: usize = 64;
147    /// Default invocation depth capacity
148    const DEFAULT_INVOCATION_CAPACITY: usize = 8;
149
150    /// Create a new VM with default limits
151    #[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    /// Create a new VM with custom limits
161    #[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    /// Run the VM until halt or fault
186    #[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    /// Push an item to the eval stack with depth checking
270    #[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    /// Check if pushing to the invocation stack would exceed the limit
280    #[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        // Gas metering
318        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        // Record trace step
326        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            // PUSHDATA1 - Push data with 1-byte length prefix
353            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            // PUSHDATA2 - Push data with 2-byte length prefix
367            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            // PUSHINT8
381            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            // PUSHINT16
390            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            // ADD
410            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            // SUB
425            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            // MUL
440            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            // DIV
455            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            // MOD
473            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            // POW
491            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            // SHL
509            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            // SHR
529            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            // MIN
549            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            // MAX
563            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            // WITHIN (a <= x < b)
577            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            // SIGN
596            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            // ABS
612            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            // NEGATE
622            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            // INC
632            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            // DEC
642            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            // LT
652            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            // LE
666            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            // GT
680            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            // GE
694            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            // EQUAL
708            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            // NOTEQUAL
714            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            // ISNULL
720            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            // NZ - Not zero
726            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            // NUMEQUAL
735            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            // NUMNOTEQUAL
749            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            // INVERT (bitwise NOT)
763            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            // AND (bitwise)
772            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            // OR (bitwise)
786            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            // XOR (bitwise)
800            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            // NOT (logical)
814            0xAA => {
815                let a = self.eval_stack.pop().ok_or(VMError::StackUnderflow)?;
816                self.push(StackItem::Boolean(!a.to_bool()))?;
817            }
818            // BOOLAND
819            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            // BOOLOR
826            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            // SWAP
833            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            // ROT
841            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            // PICK
850            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            // ROLL
860            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            // OVER
870            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            // DEPTH
879            0x43 => {
880                let depth = self.eval_stack.len() as i128;
881                self.push(StackItem::Integer(depth))?;
882            }
883            // NIP - Remove second-to-top item
884            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            // XDROP - Remove item at index n
892            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            // CLEAR - Clear the stack
901            0x49 => {
902                self.eval_stack.clear();
903            }
904            // TUCK - Copy top item and insert before second-to-top
905            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            // REVERSE3 - Reverse top 3 items
914            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            // REVERSE4 - Reverse top 4 items
922            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            // REVERSEN - Reverse top n items
931            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            // INITSLOT - Initialize local and argument slots
941            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                // Pop arguments from stack into argument slots
950                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            // LDLOC0-LDLOC6 - Load local variable 0-6
958            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            // LDLOC_S - Load local variable (short form)
968            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            // STLOC0-STLOC6 - Store local variable 0-6
982            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            // STLOC_S - Store local variable (short form)
991            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            // LDARG0-LDARG6 - Load argument 0-6
1004            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            // LDARG - Load argument
1014            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            // NOP
1028            0x21 => {}
1029            // ASSERT
1030            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            // JMP (1-byte offset)
1038            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            // JMPIF (1-byte offset)
1048            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            // JMPIFNOT (1-byte offset)
1061            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            // JMPEQ - Jump if equal
1074            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            // JMPNE - Jump if not equal
1096            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            // JMPGT - Jump if greater than
1118            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            // JMPGE - Jump if greater or equal
1140            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            // JMPLT - Jump if less than
1162            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            // JMPLE - Jump if less or equal
1184            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            // CALL (1-byte offset)
1206            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                // Store return address (simplified)
1225                self.push(StackItem::Pointer(return_ip as u32))?;
1226            }
1227            // SHA256
1228            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            // RIPEMD160
1241            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            // SHA256 + RIPEMD160 (Hash160)
1254            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            // CHECKSIG (ECDSA secp256k1)
1266            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            // SYSCALL
1294            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            // NEWARRAY0 - Create empty array
1303            0xC2 => {
1304                self.push(StackItem::Array(Vec::new()))?;
1305            }
1306            // NEWARRAY - Create array with n elements
1307            0xC3 => {
1308                let n = self.pop_usize_nonneg()?;
1309                let arr = vec![StackItem::Null; n];
1310                self.push(StackItem::Array(arr))?;
1311            }
1312            // NEWSTRUCT0 - Create empty struct
1313            0xC5 => {
1314                self.push(StackItem::Struct(Vec::new()))?;
1315            }
1316            // NEWSTRUCT - Create struct with n elements
1317            0xC6 => {
1318                let n = self.pop_usize_nonneg()?;
1319                let s = vec![StackItem::Null; n];
1320                self.push(StackItem::Struct(s))?;
1321            }
1322            // NEWMAP - Create empty map
1323            0xC8 => {
1324                self.push(StackItem::Map(Vec::new()))?;
1325            }
1326            // SIZE - Get size of array/map/string
1327            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            // PICKITEM - Get item from array/map
1338            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            // SETITEM - Set item in array/map
1360            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            // APPEND - Append to array
1383            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            // REMOVE - Remove from array/map
1392            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            // RET
1410            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                // Return a mock timestamp for zkVM
1441                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}