use crate::{
Call, Int,
evm::{Context, Evm, EvmResult, EvmYield, HaltReason},
state::State,
};
#[inline]
pub fn push(evm: &mut Evm, _: &Context, _: &Call, _: &mut dyn State) -> EvmResult<()> {
let op = evm.code[evm.pc];
let len = match op {
0x60..=0x7F => op as usize - 0x5F, _ => 0, };
if len == 0 {
evm.gas_charge(2)?;
evm.push(Int::ZERO)?;
} else {
evm.gas_charge(3)?;
let lo = evm.pc + 1;
let available = len.min(evm.code.len().saturating_sub(lo));
let mut buf = [0u8; 32];
let dst = 32 - len;
buf[dst..dst + available].copy_from_slice(&evm.code[lo..lo + available]);
evm.push(Int::from(&buf[..]))?;
evm.pc += len;
}
Ok(())
}
#[inline]
pub fn dup(evm: &mut Evm, _: &Context, _: &Call, _: &mut dyn State) -> EvmResult<()> {
evm.gas_charge(3)?;
let op = evm.code[evm.pc];
let n = idx(op) - 1;
let Some(int) = evm.stack.iter().rev().nth(n).copied() else {
return Err(EvmYield::Halt(HaltReason::StackUnderflow));
};
evm.push(int)?;
Ok(())
}
pub fn swap(evm: &mut Evm, _: &Context, _: &Call, _: &mut dyn State) -> EvmResult<()> {
evm.gas_charge(3)?;
let op = evm.code[evm.pc];
let n = idx(op); if evm.stack.len() <= n {
return Err(EvmYield::Halt(HaltReason::StackUnderflow));
}
let i = evm.stack.len() - 1;
let j = i - n;
evm.stack.swap(i, j);
Ok(())
}
fn idx(op: u8) -> usize {
match op {
0x60..0x80 => op as usize - 0x60 + 1, 0x80..0x90 => op as usize - 0x80 + 1, 0x90..0xA0 => op as usize - 0x90 + 1, _ => 0,
}
}
#[cfg(test)]
mod tests {
use super::super::tests::{call, ctx, state};
use super::*;
use crate::call::Head;
use crate::{
Int,
evm::{Evm, HaltReason},
};
fn int(val: u64) -> Int {
Int::from(val)
}
fn is_halt(result: EvmResult<()>, expected: HaltReason) -> bool {
match (result, expected) {
(Err(EvmYield::Halt(HaltReason::StackUnderflow)), HaltReason::StackUnderflow) => true,
(Err(EvmYield::Halt(HaltReason::BadOpcode(a))), HaltReason::BadOpcode(b)) => a == b,
_ => false,
}
}
#[test]
fn test_push0() {
let head = Head::default();
let mut evm = Evm::new(head, vec![0x5F], 1000, Int::ONE, Int::ONE, vec![]); push(&mut evm, &ctx(), &call(), &mut state()).unwrap();
evm.apply(&mut state());
assert_eq!(evm.stack, vec![Int::ZERO]);
assert_eq!(evm.pc, 0);
}
#[test]
fn test_push1() {
let head = Head::default();
let mut evm = Evm::new(head, vec![0x60, 0x42], 1000, Int::ONE, Int::ONE, vec![]); push(&mut evm, &ctx(), &call(), &mut state()).unwrap();
evm.apply(&mut state());
assert_eq!(evm.stack, vec![int(0x42)]);
assert_eq!(evm.pc, 1);
}
#[test]
fn test_push2() {
let head = Head::default();
let mut evm = Evm::new(
head,
vec![0x61, 0x01, 0x02],
1000,
Int::ONE,
Int::ONE,
vec![],
); push(&mut evm, &ctx(), &call(), &mut state()).unwrap();
evm.apply(&mut state());
assert_eq!(evm.stack, vec![int(0x0102)]);
assert_eq!(evm.pc, 2);
}
#[test]
fn test_push32() {
let mut code = vec![0x7F]; code.extend(1u8..=32);
let head = Head::default();
let mut evm = Evm::new(head, code, 1000, Int::ONE, Int::ONE, vec![]);
push(&mut evm, &ctx(), &call(), &mut state()).unwrap();
evm.apply(&mut state());
assert_eq!(evm.stack.len(), 1);
assert_eq!(evm.pc, 32);
let expected: Vec<u8> = (1u8..=32).collect();
assert_eq!(evm.stack[0], Int::from(expected.as_slice()));
}
#[test]
fn test_push_truncated() {
let head = Head::default();
let mut evm = Evm::new(head, vec![0x61, 0xFF], 1000, Int::ONE, Int::ONE, vec![]);
push(&mut evm, &ctx(), &call(), &mut state()).unwrap();
evm.apply(&mut state());
assert_eq!(evm.stack.len(), 1);
assert_eq!(evm.pc, 2);
assert_eq!(evm.stack[0], Int::from(0xFF00));
}
#[test]
fn test_dup1() {
let a = int(1);
let head = Head::default();
let mut evm = Evm::new(head, vec![0x80], 1000, Int::ONE, Int::ONE, vec![]); evm.stack.push(a);
dup(&mut evm, &ctx(), &call(), &mut state()).unwrap();
evm.apply(&mut state());
assert_eq!(evm.stack, vec![a, a]);
assert_eq!(evm.pc, 0);
}
#[test]
fn test_dup2() {
let (a, b) = (int(1), int(2));
let head = Head::default();
let mut evm = Evm::new(head, vec![0x81], 1000, Int::ONE, Int::ONE, vec![]); evm.stack.extend([a, b]);
dup(&mut evm, &ctx(), &call(), &mut state()).unwrap();
evm.apply(&mut state());
assert_eq!(evm.stack, vec![a, b, a]); assert_eq!(evm.pc, 0);
}
#[test]
fn test_dup16() {
let vals: Vec<Int> = (1..=16).map(int).collect();
let head = Head::default();
let mut evm = Evm::new(head, vec![0x8F], 1000, Int::ONE, Int::ONE, vec![]); evm.stack.extend(vals.iter().copied());
dup(&mut evm, &ctx(), &call(), &mut state()).unwrap();
evm.apply(&mut state());
assert_eq!(evm.stack.len(), 17);
assert_eq!(*evm.stack.last().unwrap(), int(1)); }
#[test]
fn test_dup_underflow() {
let head = Head::default();
let mut evm = Evm::new(head, vec![0x81], 1000, Int::ONE, Int::ONE, vec![]); evm.stack.push(int(1));
let result = dup(&mut evm, &ctx(), &call(), &mut state());
assert!(is_halt(result, HaltReason::StackUnderflow));
}
#[test]
fn test_dup_empty() {
let head = Head::default();
let mut evm = Evm::new(head, vec![0x80], 1000, Int::ONE, Int::ONE, vec![]); let result = dup(&mut evm, &ctx(), &call(), &mut state());
assert!(is_halt(result, HaltReason::StackUnderflow));
}
#[test]
fn test_swap1() {
let (a, b) = (int(1), int(2));
let head = Head::default();
let mut evm = Evm::new(head, vec![0x90], 1000, Int::ONE, Int::ONE, vec![]); evm.stack.extend([a, b]);
swap(&mut evm, &ctx(), &call(), &mut state()).unwrap();
evm.apply(&mut state());
assert_eq!(evm.stack, vec![b, a]); assert_eq!(evm.pc, 0);
}
#[test]
fn test_swap2() {
let (a, b, c) = (int(1), int(2), int(3));
let head = Head::default();
let mut evm = Evm::new(head, vec![0x91], 1000, Int::ONE, Int::ONE, vec![]); evm.stack.extend([a, b, c]);
swap(&mut evm, &ctx(), &call(), &mut state()).unwrap();
evm.apply(&mut state());
assert_eq!(evm.stack, vec![c, b, a]); assert_eq!(evm.pc, 0);
}
#[test]
fn test_swap16() {
let vals: Vec<Int> = (1..=17).map(int).collect(); let head = Head::default();
let mut evm = Evm::new(head, vec![0x9F], 1000, Int::ONE, Int::ONE, vec![]); evm.stack.extend(vals.iter().copied());
swap(&mut evm, &ctx(), &call(), &mut state()).unwrap();
evm.apply(&mut state());
assert_eq!(*evm.stack.last().unwrap(), int(1)); assert_eq!(evm.stack[0], int(17)); }
#[test]
fn test_swap1_underflow_empty() {
let head = Head::default();
let mut evm = Evm::new(head, vec![0x90], 1000, Int::ONE, Int::ONE, vec![]); let result = swap(&mut evm, &ctx(), &call(), &mut state());
assert!(is_halt(result, HaltReason::StackUnderflow));
}
#[test]
fn test_swap1_underflow_one() {
let head = Head::default();
let mut evm = Evm::new(head, vec![0x90], 1000, Int::ONE, Int::ONE, vec![]); evm.stack.push(int(1));
let result = swap(&mut evm, &ctx(), &call(), &mut state());
eprintln!("RESULT: {result:?}");
assert!(is_halt(result, HaltReason::StackUnderflow));
}
#[test]
fn test_swap2_underflow() {
let head = Head::default();
let mut evm = Evm::new(head, vec![0x91], 1000, Int::ONE, Int::ONE, vec![]); evm.stack.extend([int(1), int(2)]);
let result = swap(&mut evm, &ctx(), &call(), &mut state());
assert!(is_halt(result, HaltReason::StackUnderflow));
}
}