Skip to content

Commit

Permalink
fix(levm): smod non-compliance (#1305)
Browse files Browse the repository at this point in the history
Resolves #1291
  • Loading branch information
ilitteri authored Nov 27, 2024
1 parent d2ea924 commit 9881520
Show file tree
Hide file tree
Showing 3 changed files with 45 additions and 27 deletions.
53 changes: 28 additions & 25 deletions crates/vm/levm/src/opcode_handlers/arithmetic.rs
Original file line number Diff line number Diff line change
Expand Up @@ -134,28 +134,32 @@ impl VM {
) -> Result<OpcodeSuccess, VMError> {
self.increase_consumed_gas(current_call_frame, gas_cost::SMOD)?;

let dividend = current_call_frame.stack.pop()?;
let divisor = current_call_frame.stack.pop()?;
if divisor.is_zero() {
let unchecked_dividend = current_call_frame.stack.pop()?;
let unchecked_divisor = current_call_frame.stack.pop()?;

if unchecked_divisor.is_zero() {
current_call_frame.stack.push(U256::zero())?;
} else {
let normalized_dividend = abs(dividend);
let normalized_divisor = abs(divisor);

let mut remainder =
normalized_dividend
.checked_rem(normalized_divisor)
.ok_or(VMError::Internal(
InternalError::ArithmeticOperationDividedByZero,
))?; // Cannot be zero bc if above;

// The remainder should have the same sign as the dividend
if is_negative(dividend) {
remainder = negate(remainder);
return Ok(OpcodeSuccess::Continue);
}

let divisor = abs(unchecked_divisor);
let dividend = abs(unchecked_dividend);

let unchecked_remainder = match dividend.checked_rem(divisor) {
Some(remainder) => remainder,
None => {
current_call_frame.stack.push(U256::zero())?;
return Ok(OpcodeSuccess::Continue);
}
};

current_call_frame.stack.push(remainder)?;
}
let remainder = if is_negative(unchecked_dividend) {
negate(unchecked_remainder)
} else {
unchecked_remainder
};

current_call_frame.stack.push(remainder)?;

Ok(OpcodeSuccess::Continue)
}
Expand Down Expand Up @@ -294,16 +298,15 @@ fn is_negative(value: U256) -> bool {
}

/// Negates a number in two's complement
fn negate(value: U256) -> U256 {
let (dividend, _overflowed) = (!value).overflowing_add(U256::one());
dividend
}

fn abs(value: U256) -> U256 {
if is_negative(value) {
negate(value)
} else {
value
}
}

/// Negates a number in two's complement
fn negate(value: U256) -> U256 {
let inverted = !value;
inverted.saturating_add(U256::one())
}
12 changes: 12 additions & 0 deletions crates/vm/levm/tests/edge_case_tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -225,3 +225,15 @@ fn test_non_compliance_codecopy() {
&U256::zero()
);
}

#[test]
fn test_non_compliance_smod() {
let mut vm =
new_vm_with_bytecode(Bytes::copy_from_slice(&[0x60, 1, 0x60, 1, 0x19, 0x07])).unwrap();
let mut current_call_frame = vm.call_frames.pop().unwrap();
vm.execute(&mut current_call_frame);
assert_eq!(
current_call_frame.stack.stack.first().unwrap(),
&U256::zero()
);
}
7 changes: 5 additions & 2 deletions crates/vm/levm/tests/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -176,7 +176,10 @@ fn smod_op() {
let mut current_call_frame = vm.call_frames.pop().unwrap();
vm.execute(&mut current_call_frame);

assert!(vm.current_call_frame_mut().unwrap().stack.pop().unwrap() == U256::from(1));
assert_eq!(
vm.current_call_frame_mut().unwrap().stack.pop().unwrap(),
U256::one()
);

// Second Example
// Example taken from evm.codes
Expand Down Expand Up @@ -209,7 +212,7 @@ fn smod_op() {
)
.unwrap();

assert!(vm.current_call_frame_mut().unwrap().stack.pop().unwrap() == c);
assert_eq!(vm.current_call_frame_mut().unwrap().stack.pop().unwrap(), c);
}

#[test]
Expand Down

0 comments on commit 9881520

Please sign in to comment.