From 18d317b9cf2f859206e4b3ca939ed799e7aef294 Mon Sep 17 00:00:00 2001 From: Robin Salen Date: Wed, 5 Apr 2023 06:35:52 -0400 Subject: [PATCH 1/7] Implement sdiv in interpreter. --- evm/src/cpu/kernel/interpreter.rs | 108 +++++++++++++++++++++++++++++- 1 file changed, 107 insertions(+), 1 deletion(-) diff --git a/evm/src/cpu/kernel/interpreter.rs b/evm/src/cpu/kernel/interpreter.rs index 3f699260..49ff2222 100644 --- a/evm/src/cpu/kernel/interpreter.rs +++ b/evm/src/cpu/kernel/interpreter.rs @@ -1,5 +1,6 @@ //! An EVM interpreter for testing and debugging purposes. +use core::cmp::Ordering; use std::collections::HashMap; use std::ops::Range; @@ -305,7 +306,7 @@ impl<'a> Interpreter<'a> { 0x02 => self.run_mul(), // "MUL", 0x03 => self.run_sub(), // "SUB", 0x04 => self.run_div(), // "DIV", - 0x05 => todo!(), // "SDIV", + 0x05 => self.run_sdiv(), // "SDIV", 0x06 => self.run_mod(), // "MOD", 0x07 => todo!(), // "SMOD", 0x08 => self.run_addmod(), // "ADDMOD", @@ -467,6 +468,41 @@ impl<'a> Interpreter<'a> { self.push(if y.is_zero() { U256::zero() } else { x / y }); } + fn run_sdiv(&mut self) { + let mut x = self.pop(); + let mut y = self.pop(); + + let y_is_zero = y.is_zero(); + + if y_is_zero { + self.push(U256::zero()); + } else if y.eq(&MINUS_ONE) && x.eq(&MIN_VALUE) { + self.push(MIN_VALUE); + } else { + let x_is_pos = x.eq(&(x & SIGN_MASK)); + let y_is_pos = y.eq(&(y & SIGN_MASK)); + + // We compute the absolute quotient first, + // then adapt its sign based on the operands. + if !x_is_pos { + x = two_complement(x); + } + if !y_is_pos { + y = two_complement(y); + } + let div = x / y; + if div.eq(&U256::zero()) { + self.push(U256::zero()); + } + + self.push(if x_is_pos == y_is_pos { + div + } else { + two_complement(div) + }); + } + } + fn run_mod(&mut self) { let x = self.pop(); let y = self.pop(); @@ -837,6 +873,76 @@ impl<'a> Interpreter<'a> { } } +// Computes the two's complement of the given integer. +fn two_complement(x: U256) -> U256 { + let flipped_bits = x ^ MINUS_ONE; + flipped_bits.overflowing_add(U256::one()).0 +} + +fn signed_cmp(x: U256, y: U256) -> Ordering { + let x_is_zero = x.is_zero(); + let y_is_zero = y.is_zero(); + + if x_is_zero && y_is_zero { + return Ordering::Equal; + } + + let x_is_pos = x.eq(&(x & SIGN_MASK)); + let y_is_pos = y.eq(&(y & SIGN_MASK)); + + if x_is_zero { + if y_is_pos { + return Ordering::Less; + } else { + return Ordering::Greater; + } + }; + + if y_is_zero { + if x_is_pos { + return Ordering::Greater; + } else { + return Ordering::Less; + } + }; + + match (x_is_pos, y_is_pos) { + (true, true) => x.cmp(&y), + (true, false) => Ordering::Greater, + (false, true) => Ordering::Less, + (false, false) => x.cmp(&y).reverse(), + } +} + +const MINUS_ZERO: U256 = U256([ + 0x0000000000000001, + 0x0000000000000000, + 0x0000000000000000, + 0x8000000000000000, +]); + +const MINUS_ONE: U256 = U256([ + 0xffffffffffffffff, + 0xffffffffffffffff, + 0xffffffffffffffff, + 0xffffffffffffffff, +]); + +/// -2^255 in two's complement representation consists in the MSB set to 1. +const MIN_VALUE: U256 = U256([ + 0x0000000000000000, + 0x0000000000000000, + 0x0000000000000000, + 0x8000000000000000, +]); + +const SIGN_MASK: U256 = U256([ + 0xffffffffffffffff, + 0xffffffffffffffff, + 0xffffffffffffffff, + 0x7fffffffffffffff, +]); + /// Return the (ordered) JUMPDEST offsets in the code. fn find_jumpdests(code: &[u8]) -> Vec { let mut offset = 0; From 232832e34dccf6d2781307244cf6e620a766fd39 Mon Sep 17 00:00:00 2001 From: Robin Salen Date: Thu, 6 Apr 2023 16:46:10 -0400 Subject: [PATCH 2/7] Implement smod in interpreter --- evm/src/cpu/kernel/interpreter.rs | 30 +++++++++++++++++++++++++++++- 1 file changed, 29 insertions(+), 1 deletion(-) diff --git a/evm/src/cpu/kernel/interpreter.rs b/evm/src/cpu/kernel/interpreter.rs index 49ff2222..ede6aece 100644 --- a/evm/src/cpu/kernel/interpreter.rs +++ b/evm/src/cpu/kernel/interpreter.rs @@ -308,7 +308,7 @@ impl<'a> Interpreter<'a> { 0x04 => self.run_div(), // "DIV", 0x05 => self.run_sdiv(), // "SDIV", 0x06 => self.run_mod(), // "MOD", - 0x07 => todo!(), // "SMOD", + 0x07 => self.run_smod(), // "SMOD", 0x08 => self.run_addmod(), // "ADDMOD", 0x09 => self.run_mulmod(), // "MULMOD", 0x0a => self.run_exp(), // "EXP", @@ -509,6 +509,34 @@ impl<'a> Interpreter<'a> { self.push(if y.is_zero() { U256::zero() } else { x % y }); } + fn run_smod(&mut self) { + let mut x = self.pop(); + let mut y = self.pop(); + + if y.is_zero() { + self.push(U256::zero()); + } else { + let x_is_pos = x.eq(&(x & SIGN_MASK)); + let y_is_pos = y.eq(&(y & SIGN_MASK)); + + // We compute the absolute remainder first, + // then adapt its sign based on the operands. + if !x_is_pos { + x = two_complement(x); + } + if !y_is_pos { + y = two_complement(y); + } + let rem = x % y; + if rem.eq(&U256::zero()) { + self.push(U256::zero()); + } + + // Remainder always has the same sign as the dividend. + self.push(if x_is_pos { rem } else { two_complement(rem) }); + } + } + fn run_addmod(&mut self) { let x = U512::from(self.pop()); let y = U512::from(self.pop()); From ac2ccc1eb9f821bccc5222d8eafe8fbe6cc8a8b2 Mon Sep 17 00:00:00 2001 From: Robin Salen Date: Thu, 6 Apr 2023 16:58:21 -0400 Subject: [PATCH 3/7] Implement slt in interpreter --- evm/src/cpu/kernel/interpreter.rs | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/evm/src/cpu/kernel/interpreter.rs b/evm/src/cpu/kernel/interpreter.rs index ede6aece..64d2d0b8 100644 --- a/evm/src/cpu/kernel/interpreter.rs +++ b/evm/src/cpu/kernel/interpreter.rs @@ -318,7 +318,7 @@ impl<'a> Interpreter<'a> { 0x0e => self.run_subfp254(), // "SUBFP254", 0x10 => self.run_lt(), // "LT", 0x11 => self.run_gt(), // "GT", - 0x12 => todo!(), // "SLT", + 0x12 => self.run_slt(), // "SLT", 0x13 => todo!(), // "SGT", 0x14 => self.run_eq(), // "EQ", 0x15 => self.run_iszero(), // "ISZERO", @@ -577,6 +577,12 @@ impl<'a> Interpreter<'a> { self.push_bool(x > y); } + fn run_slt(&mut self) { + let x = self.pop(); + let y = self.pop(); + self.push_bool(signed_cmp(x, y) == Ordering::Less); + } + fn run_eq(&mut self) { let x = self.pop(); let y = self.pop(); @@ -942,13 +948,7 @@ fn signed_cmp(x: U256, y: U256) -> Ordering { } } -const MINUS_ZERO: U256 = U256([ - 0x0000000000000001, - 0x0000000000000000, - 0x0000000000000000, - 0x8000000000000000, -]); - +/// -1 in two's complement representation consists in all bits set to 1. const MINUS_ONE: U256 = U256([ 0xffffffffffffffff, 0xffffffffffffffff, From 4db004417cfc1417f6984633347929aa7f6407a4 Mon Sep 17 00:00:00 2001 From: Robin Salen Date: Thu, 6 Apr 2023 16:59:19 -0400 Subject: [PATCH 4/7] Implement sgt in interpreter --- evm/src/cpu/kernel/interpreter.rs | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/evm/src/cpu/kernel/interpreter.rs b/evm/src/cpu/kernel/interpreter.rs index 64d2d0b8..108ed133 100644 --- a/evm/src/cpu/kernel/interpreter.rs +++ b/evm/src/cpu/kernel/interpreter.rs @@ -319,7 +319,7 @@ impl<'a> Interpreter<'a> { 0x10 => self.run_lt(), // "LT", 0x11 => self.run_gt(), // "GT", 0x12 => self.run_slt(), // "SLT", - 0x13 => todo!(), // "SGT", + 0x13 => self.run_sgt(), // "SGT", 0x14 => self.run_eq(), // "EQ", 0x15 => self.run_iszero(), // "ISZERO", 0x16 => self.run_and(), // "AND", @@ -583,6 +583,12 @@ impl<'a> Interpreter<'a> { self.push_bool(signed_cmp(x, y) == Ordering::Less); } + fn run_sgt(&mut self) { + let x = self.pop(); + let y = self.pop(); + self.push_bool(signed_cmp(x, y) == Ordering::Greater); + } + fn run_eq(&mut self) { let x = self.pop(); let y = self.pop(); From b943ddb0d20e7fba50f101fbae22d97573bee9ab Mon Sep 17 00:00:00 2001 From: Robin Salen Date: Thu, 6 Apr 2023 17:11:44 -0400 Subject: [PATCH 5/7] Implement signextend in interpreter --- evm/src/cpu/kernel/interpreter.rs | 27 ++++++++++++++++++++++++++- 1 file changed, 26 insertions(+), 1 deletion(-) diff --git a/evm/src/cpu/kernel/interpreter.rs b/evm/src/cpu/kernel/interpreter.rs index 108ed133..1506ba1e 100644 --- a/evm/src/cpu/kernel/interpreter.rs +++ b/evm/src/cpu/kernel/interpreter.rs @@ -312,7 +312,7 @@ impl<'a> Interpreter<'a> { 0x08 => self.run_addmod(), // "ADDMOD", 0x09 => self.run_mulmod(), // "MULMOD", 0x0a => self.run_exp(), // "EXP", - 0x0b => todo!(), // "SIGNEXTEND", + 0x0b => self.run_signextend(), // "SIGNEXTEND", 0x0c => self.run_addfp254(), // "ADDFP254", 0x0d => self.run_mulfp254(), // "MULFP254", 0x0e => self.run_subfp254(), // "SUBFP254", @@ -589,6 +589,31 @@ impl<'a> Interpreter<'a> { self.push_bool(signed_cmp(x, y) == Ordering::Greater); } + fn run_signextend(&mut self) { + let n = self.pop(); + let x = self.pop(); + if n > U256::from(31) { + self.push(x); + } else { + let n = n.low_u64() as usize; + let num_bytes_prepend = 31 - n; + + let mut x_bytes = [0u8; 32]; + x.to_big_endian(&mut x_bytes); + let mut x_bytes = x_bytes[num_bytes_prepend..].to_vec(); + let sign_bit = x_bytes[0] >> 7; + + self.push(if sign_bit == 0 { + x_bytes.extend_from_slice(&vec![0; num_bytes_prepend]); + U256::from_big_endian(&x_bytes) + } else { + let mut tmp = vec![0xff; num_bytes_prepend]; + tmp.extend_from_slice(&x_bytes); + U256::from_big_endian(&tmp) + }); + } + } + fn run_eq(&mut self) { let x = self.pop(); let y = self.pop(); From 3da8efa6ba59681d5696f2eb21cc26c9d45dfbe7 Mon Sep 17 00:00:00 2001 From: Robin Salen Date: Thu, 6 Apr 2023 17:21:18 -0400 Subject: [PATCH 6/7] Implement sar in interpreter --- evm/src/cpu/kernel/interpreter.rs | 26 +++++++++++++++++++++++++- 1 file changed, 25 insertions(+), 1 deletion(-) diff --git a/evm/src/cpu/kernel/interpreter.rs b/evm/src/cpu/kernel/interpreter.rs index 1506ba1e..5dd346ec 100644 --- a/evm/src/cpu/kernel/interpreter.rs +++ b/evm/src/cpu/kernel/interpreter.rs @@ -329,7 +329,7 @@ impl<'a> Interpreter<'a> { 0x1a => self.run_byte(), // "BYTE", 0x1b => self.run_shl(), // "SHL", 0x1c => self.run_shr(), // "SHR", - 0x1d => todo!(), // "SAR", + 0x1d => self.run_sar(), // "SAR", 0x20 => self.run_keccak256(), // "KECCAK256", 0x21 => self.run_keccak_general(), // "KECCAK_GENERAL", 0x30 => todo!(), // "ADDRESS", @@ -675,6 +675,30 @@ impl<'a> Interpreter<'a> { self.push(value >> shift); } + fn run_sar(&mut self) { + let shift = self.pop(); + let value = self.pop(); + let value_is_neg = !value.eq(&(value & SIGN_MASK)); + + if shift < U256::from(256usize) { + let shift = shift.low_u64() as usize; + let mask = !(MINUS_ONE >> shift); + let value_shifted = value >> shift; + + if value_is_neg { + self.push(value_shifted | mask); + } else { + self.push(value_shifted); + }; + } else { + self.push(if value_is_neg { + MINUS_ONE + } else { + U256::zero() + }); + } + } + fn run_keccak256(&mut self) { let offset = self.pop().as_usize(); let size = self.pop().as_usize(); From 011ea8e49bc0ce6015bb230c093ebe7ca887385b Mon Sep 17 00:00:00 2001 From: Robin Salen Date: Tue, 11 Apr 2023 07:41:33 -0400 Subject: [PATCH 7/7] Fix from review --- evm/src/cpu/kernel/interpreter.rs | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/evm/src/cpu/kernel/interpreter.rs b/evm/src/cpu/kernel/interpreter.rs index 5dd346ec..fa3788cf 100644 --- a/evm/src/cpu/kernel/interpreter.rs +++ b/evm/src/cpu/kernel/interpreter.rs @@ -600,17 +600,17 @@ impl<'a> Interpreter<'a> { let mut x_bytes = [0u8; 32]; x.to_big_endian(&mut x_bytes); - let mut x_bytes = x_bytes[num_bytes_prepend..].to_vec(); + let x_bytes = x_bytes[num_bytes_prepend..].to_vec(); let sign_bit = x_bytes[0] >> 7; - self.push(if sign_bit == 0 { - x_bytes.extend_from_slice(&vec![0; num_bytes_prepend]); - U256::from_big_endian(&x_bytes) + let mut bytes = if sign_bit == 0 { + vec![0; num_bytes_prepend] } else { - let mut tmp = vec![0xff; num_bytes_prepend]; - tmp.extend_from_slice(&x_bytes); - U256::from_big_endian(&tmp) - }); + vec![0xff; num_bytes_prepend] + }; + bytes.extend_from_slice(&x_bytes); + + self.push(U256::from_big_endian(&bytes)); } }