Skip to content

Commit

Permalink
feat: update stack (#375)
Browse files Browse the repository at this point in the history
* feat: update stack

* refactor: narrow type passed to compute_active_segment_index

* Fix percentage in snapshot

* fix: fix comments

---------

Co-authored-by: Clément Walter <[email protected]>
Co-authored-by: Mathieu <[email protected]>
  • Loading branch information
3 people authored Sep 28, 2023
1 parent a544d67 commit 2c17d56
Show file tree
Hide file tree
Showing 5 changed files with 287 additions and 39 deletions.
66 changes: 49 additions & 17 deletions crates/evm/src/stack.cairo
Original file line number Diff line number Diff line change
Expand Up @@ -24,14 +24,17 @@ use evm::helpers::U256IntoResultU32;
use starknet::EthAddress;
use utils::i256::i256;


#[derive(Destruct, Default)]
struct Stack {
active_segment: usize,
items: Felt252Dict<Nullable<u256>>,
len: usize,
len: Felt252Dict<usize>,
}

trait StackTrait {
fn new() -> Stack;
fn set_active_segment(ref self: Stack, active_segment: usize);
fn push(ref self: Stack, item: u256) -> Result<(), EVMError>;
fn pop(ref self: Stack) -> Result<u256, EVMError>;
fn pop_usize(ref self: Stack) -> Result<usize, EVMError>;
Expand All @@ -41,8 +44,10 @@ trait StackTrait {
fn peek(ref self: Stack) -> Option<u256>;
fn peek_at(ref self: Stack, index: usize) -> Result<u256, EVMError>;
fn swap_i(ref self: Stack, index: usize) -> Result<(), EVMError>;
fn len(self: @Stack) -> usize;
fn is_empty(self: @Stack) -> bool;
fn len(ref self: Stack) -> usize;
fn is_empty(ref self: Stack) -> bool;
fn active_segment(self: @Stack) -> usize;
fn compute_active_segment_index(self: @Stack, index: usize) -> felt252;
}

impl StackImpl of StackTrait {
Expand All @@ -51,19 +56,32 @@ impl StackImpl of StackTrait {
Default::default()
}

#[inline(always)]
fn set_active_segment(ref self: Stack, active_segment: usize) {
self.active_segment = active_segment;
}

/// Pushes a new bytes32 word onto the stack.
///
/// When pushing an item to the stack, we will compute
/// an index which corresponds to the index in the dict the item will be stored at.
/// The internal index is computed as follows:
///
/// index = len(Stack_i) + i * STACK_SEGMENT_SIZE
///
/// # Errors
///
/// If the stack is full, returns with a StackOverflow error.
#[inline(always)]
fn push(ref self: Stack, item: u256) -> Result<(), EVMError> {
let length = self.len();
// we can store at most 1024 256-bits words
if self.len() == constants::STACK_MAX_DEPTH {
if length == constants::STACK_MAX_DEPTH {
return Result::Err(EVMError::StackError(STACK_OVERFLOW));
}
self.items.insert(self.len.into(), NullableTrait::new(item));
self.len += 1;
let index = self.compute_active_segment_index(length);
self.items.insert(index, NullableTrait::new(item));
self.len.insert(self.active_segment().into(), length + 1);
Result::Ok(())
}

Expand All @@ -74,12 +92,13 @@ impl StackImpl of StackTrait {
/// If the stack is empty, returns with a StackOverflow error.
#[inline(always)]
fn pop(ref self: Stack) -> Result<u256, EVMError> {
if self.len() == 0 {
let length = self.len();
if length == 0 {
return Result::Err(EVMError::StackError(STACK_UNDERFLOW));
}
let last_index = self.len() - 1;
self.len -= 1;
let item = self.items.get(last_index.into());
self.len.insert(self.active_segment().into(), length - 1);
let internal_index = self.compute_active_segment_index(self.len());
let item = self.items.get(internal_index);
Result::Ok(item.deref())
}

Expand Down Expand Up @@ -156,7 +175,7 @@ impl StackImpl of StackTrait {
if self.len() == 0 {
Option::None(())
} else {
let last_index = self.len() - 1;
let last_index = self.compute_active_segment_index(self.len() - 1);
let item = self.items.get(last_index.into());
Option::Some(item.deref())
}
Expand All @@ -174,7 +193,7 @@ impl StackImpl of StackTrait {
return Result::Err(EVMError::StackError(STACK_UNDERFLOW));
}

let position = self.len() - 1 - index;
let position = self.compute_active_segment_index(self.len() - 1 - index);
let item = self.items.get(position.into());

Result::Ok(item.deref())
Expand All @@ -187,7 +206,7 @@ impl StackImpl of StackTrait {
if index >= self.len() {
return Result::Err(EVMError::StackError(STACK_UNDERFLOW));
}
let position_0: felt252 = (self.len() - 1).into();
let position_0: felt252 = self.compute_active_segment_index(self.len() - 1);
let position_item: felt252 = position_0 - index.into();
let top_item = self.items.get(position_0);
let swapped_item = self.items.get(position_item);
Expand All @@ -198,13 +217,26 @@ impl StackImpl of StackTrait {

/// Returns the length of the stack.
#[inline(always)]
fn len(self: @Stack) -> usize {
*self.len
fn len(ref self: Stack) -> usize {
self.len.get(self.active_segment.into())
}

/// Returns true if the stack is empty.
#[inline(always)]
fn is_empty(self: @Stack) -> bool {
*self.len == 0
fn is_empty(ref self: Stack) -> bool {
self.len() == 0
}

/// Returns the current active segment, i.e. the current active execution context's id
#[inline(always)]
fn active_segment(self: @Stack) -> usize {
*self.active_segment
}

/// Computes the internal index to access the Stack of the current execution context
#[inline(always)]
fn compute_active_segment_index(self: @Stack, index: usize) -> felt252 {
let internal_index = index + self.active_segment() * constants::STACK_MAX_DEPTH;
internal_index.into()
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -346,7 +346,7 @@ fn test_exec_gt_false_equal() {
}

#[test]
#[available_gas(22000000)]
#[available_gas(220000000)]
fn test_exec_slt() {
// https://github.com/ethereum/go-ethereum/blob/master/core/vm/testdata/testcases_slt.json
assert_slt(0x0, 0x0, 0);
Expand Down Expand Up @@ -591,7 +591,7 @@ fn assert_slt(b: u256, a: u256, expected: u256) {
}

#[test]
#[available_gas(22000000)]
#[available_gas(220000000)]
fn test_exec_sgt() {
// https://github.com/ethereum/go-ethereum/blob/master/core/vm/testdata/testcases_sgt.json
assert_sgt(0x0, 0x0, 0);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ fn test_exec_add() {
ctx.exec_add();

// Then
assert(ctx.stack.len == 2, 'stack should have two elems');
assert(ctx.stack.len() == 2, 'stack should have two elems');
assert(ctx.stack.peek().unwrap() == 5, 'stack top should be 3+2');
assert(ctx.stack.peek_at(1).unwrap() == 1, 'stack[1] should be 1');
}
Expand Down
Loading

0 comments on commit 2c17d56

Please sign in to comment.