Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: implement 0x3E - RETURNDATACOPY Opcode #203

Merged
merged 23 commits into from
Sep 5, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
e5e945c
returndatacopy opcode
khaeljy Aug 27, 2023
056844b
Merge branch 'main' into RETURNDATACOPY
khaeljy Aug 28, 2023
bf87cfc
RETURNDATACOPY Opcode
khaeljy Aug 28, 2023
5332cbf
Merge branch 'main' into RETURNDATACOPY
khaeljy Aug 29, 2023
c880cde
Merge branch 'main' into RETURNDATACOPY
khaeljy Aug 29, 2023
2354bb0
Refactoring
khaeljy Aug 29, 2023
fa51662
RETURNDATACOPY
khaeljy Aug 29, 2023
13a439f
RETURNDATACOPY
khaeljy Aug 29, 2023
a70c090
Merge branch 'kkrt-labs:main' into RETURNDATACOPY
khaeljy Aug 30, 2023
9fc2ee9
Add out of bounds and overflowing add tests
khaeljy Aug 30, 2023
1659af7
Merge branch 'main' into RETURNDATACOPY
khaeljy Aug 31, 2023
f55269d
Merge branch 'main' into RETURNDATACOPY
khaeljy Sep 1, 2023
5813fe8
Merge branch 'main' into RETURNDATACOPY
khaeljy Sep 1, 2023
b1abae7
Update crates/evm/src/tests/test_instructions/test_environment_inform…
khaeljy Sep 4, 2023
26a69cb
Update crates/evm/src/tests/test_instructions/test_environment_inform…
khaeljy Sep 4, 2023
7bef8dd
Merge branch 'main' into RETURNDATACOPY
khaeljy Sep 4, 2023
dffade3
Add line breaks
khaeljy Sep 4, 2023
e5c7c24
Update crates/evm/src/tests/test_instructions/test_environment_inform…
khaeljy Sep 4, 2023
ab7b478
Remove unused function
khaeljy Sep 4, 2023
8e09c03
Merge branch 'RETURNDATACOPY' of https://github.com/khaeljy/kakarot-s…
khaeljy Sep 4, 2023
8b1ef54
Remove useless asserts
khaeljy Sep 4, 2023
e7c55b1
Test refactoring
khaeljy Sep 4, 2023
f34aa88
Merge branch 'main' into RETURNDATACOPY
khaeljy Sep 5, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 6 additions & 1 deletion crates/evm/src/errors.cairo
Original file line number Diff line number Diff line change
Expand Up @@ -11,11 +11,15 @@ const PC_OUT_OF_BOUNDS: felt252 = 'Kakarot: pc >= bytecode length';
// TYPE CONVERSION
const TYPE_CONVERSION_ERROR: felt252 = 'Kakarot: type conversion error';

// RETURNDATA
const RETURNDATA_OUT_OF_BOUNDS_ERROR: felt252 = 'Kakarot: ReturnDataOutOfBounds';

#[derive(Drop, Copy, PartialEq)]
enum EVMError {
StackError: felt252,
InvalidProgramCounter: felt252,
TypeConversionError: felt252
TypeConversionError: felt252,
ReturnDataError: felt252
}


Expand All @@ -25,6 +29,7 @@ impl EVMErrorIntoU256 of Into<EVMError, u256> {
EVMError::StackError(error_message) => error_message.into(),
EVMError::InvalidProgramCounter(error_message) => error_message.into(),
EVMError::TypeConversionError(error_message) => error_message.into(),
EVMError::ReturnDataError(error_message) => error_message.into(),
}
}
}
26 changes: 24 additions & 2 deletions crates/evm/src/instructions/environmental_information.cairo
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,14 @@
use starknet::{EthAddressIntoFelt252};
use result::ResultTrait;
use evm::stack::StackTrait;
use evm::errors::{EVMError, RETURNDATA_OUT_OF_BOUNDS_ERROR};
use evm::helpers::U256IntoResultU32;
use evm::context::{
ExecutionContext, ExecutionContextTrait, BoxDynamicExecutionContextDestruct, CallContextTrait
};
use evm::errors::EVMError;
use utils::helpers::EthAddressIntoU256;
use evm::helpers::U256IntoResultU32;
use evm::memory::MemoryTrait;
use integer::u32_overflowing_add;

#[generate_trait]
impl EnvironmentInformationImpl of EnvironmentInformationTrait {
Expand Down Expand Up @@ -149,6 +150,27 @@ impl EnvironmentInformationImpl of EnvironmentInformationTrait {
/// Save word to memory.
/// # Specification: https://www.evm.codes/#3e?fork=shanghai
fn exec_returndatacopy(ref self: ExecutionContext) -> Result<(), EVMError> {
let popped = self.stack.pop_n(3)?;
let dest_offset: u32 = Into::<u256, Result<u32, EVMError>>::into((*popped[0]))?;
let offset: u32 = Into::<u256, Result<u32, EVMError>>::into((*popped[1]))?;
let size: u32 = Into::<u256, Result<u32, EVMError>>::into((*popped[2]))?;

let return_data: Span<u8> = self.return_data();

match u32_overflowing_add(offset, size) {
Result::Ok(x) => {
if (x > return_data.len()) {
return Result::Err(EVMError::ReturnDataError(RETURNDATA_OUT_OF_BOUNDS_ERROR));
}
},
Result::Err(x) => {
return Result::Err(EVMError::ReturnDataError(RETURNDATA_OUT_OF_BOUNDS_ERROR));
}
}

let data_to_copy: Span<u8> = return_data.slice(offset, size);
self.memory.store_n(data_to_copy, dest_offset);

Result::Ok(())
}

Expand Down
2 changes: 1 addition & 1 deletion crates/evm/src/memory.cairo
Original file line number Diff line number Diff line change
Expand Up @@ -262,7 +262,7 @@ impl InternalMemoryMethods of InternalMemoryTrait {

self.items.insert(chunk_index.into(), current.try_into().unwrap());
chunk_index += 1;
elements = elements.slice(0, 16);
elements = elements.slice(16, elements.len() - 16);
}
}

Expand Down
Original file line number Diff line number Diff line change
@@ -1,16 +1,22 @@
use evm::instructions::EnvironmentInformationTrait;
use evm::memory::{InternalMemoryTrait, MemoryTrait};
use evm::tests::test_utils::{
setup_execution_context, setup_execution_context_with_bytecode, evm_address, callvalue
};
use evm::stack::StackTrait;
use evm::memory::{InternalMemoryTrait, MemoryTrait};
use option::OptionTrait;
use starknet::EthAddressIntoFelt252;
use utils::helpers::{EthAddressIntoU256, u256_to_bytes_array};
use evm::errors::{EVMError, TYPE_CONVERSION_ERROR};
use evm::errors::{EVMError, TYPE_CONVERSION_ERROR, RETURNDATA_OUT_OF_BOUNDS_ERROR};
use evm::context::{
ExecutionContext, ExecutionContextTrait, BoxDynamicExecutionContextDestruct, CallContextTrait
};
use utils::helpers::{ArrayExtension, ArrayExtensionTrait};
use integer::u32_overflowing_add;

// *************************************************************************
// 0x30: ADDRESS
// *************************************************************************

#[test]
#[available_gas(20000000)]
Expand All @@ -34,6 +40,10 @@ fn test_address_nested_call() { // A (EOA) -(calls)-> B (smart contract) -(calls
// ref: https://github.com/kkrt-labs/kakarot-ssj/issues/183
}

// *************************************************************************
// 0x34: CALLVALUE
// *************************************************************************

#[test]
#[available_gas(120000)]
fn test__exec_callvalue() {
Expand All @@ -48,19 +58,9 @@ fn test__exec_callvalue() {
assert(ctx.stack.pop().unwrap() == callvalue(), 'should be `123456789');
}

#[test]
#[available_gas(20000000)]
fn test_gasprice() {
// Given
let mut ctx = setup_execution_context();

// When
ctx.exec_gasprice();

// Then
assert(ctx.stack.len() == 1, 'stack should have one element');
assert(ctx.stack.peek().unwrap() == 10, 'stack top should be 10');
}
// *************************************************************************
// 0x36: CALLDATASIZE
// *************************************************************************

#[test]
#[available_gas(20000000)]
Expand All @@ -77,6 +77,10 @@ fn test_calldata_size() {
assert(ctx.stack.peek().unwrap() == calldata.len().into(), 'stack top is not calldatasize');
}

// *************************************************************************
// 0x38: CODESIZE
// *************************************************************************

#[test]
#[available_gas(20000000)]
fn test_codesize() {
Expand All @@ -92,6 +96,10 @@ fn test_codesize() {
assert(ctx.stack.pop().unwrap() == bytecode.len().into(), 'wrong codesize');
}

// *************************************************************************
// 0x39: CODECOPY
// *************************************************************************

#[test]
#[available_gas(20000000)]
fn test_codecopy_type_conversion_error() {
Expand Down Expand Up @@ -186,6 +194,28 @@ fn test_codecopy(dest_offset: u32, offset: u32, mut size: u32) {
};
}

// *************************************************************************
// 0x3A: GASPRICE
// *************************************************************************

#[test]
#[available_gas(20000000)]
fn test_gasprice() {
// Given
let mut ctx = setup_execution_context();

// When
ctx.exec_gasprice();

// Then
assert(ctx.stack.len() == 1, 'stack should have one element');
assert(ctx.stack.peek().unwrap() == 10, 'stack top should be 10');
}

// *************************************************************************
// 0x3D: RETURNDATASIZE
// *************************************************************************

#[test]
#[available_gas(20000000)]
fn test_returndatasize() {
Expand All @@ -202,3 +232,160 @@ fn test_returndatasize() {
assert(ctx.stack.len() == 1, 'stack should have one element');
assert(ctx.stack.pop().unwrap() == size.into(), 'wrong returndatasize');
}

// *************************************************************************
// 0x3E: RETURNDATACOPY
// *************************************************************************

#[test]
#[available_gas(20000000)]
fn test_returndata_copy_type_conversion_error() {
// Given
let mut ctx = setup_execution_context();

ctx.stack.push(0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF);
ctx.stack.push(0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF);
ctx.stack.push(0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF);

// When
let res = ctx.exec_returndatacopy();

// Then
khaeljy marked this conversation as resolved.
Show resolved Hide resolved
assert(
res.unwrap_err() == EVMError::TypeConversionError(TYPE_CONVERSION_ERROR),
'should return ConversionError'
);
}

#[test]
#[available_gas(20000000)]
fn test_returndata_copy_overflowing_add_error() {
test_returndata_copy(0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF);
}

#[test]
#[available_gas(20000000)]
fn test_returndata_copy_basic() {
test_returndata_copy(32, 0, 0);
}

#[test]
#[available_gas(20000000)]
fn test_returndata_copy_with_offset() {
test_returndata_copy(32, 2, 0);
}

#[test]
#[available_gas(20000000)]
fn test_returndata_copy_with_out_of_bound_bytes() {
test_returndata_copy(32, 30, 10);
}

#[test]
#[available_gas(20000000)]
fn test_returndata_copy_with_multiple_words() {
test_returndata_copy(32, 0, 33);
}

fn test_returndata_copy(dest_offset: u32, offset: u32, mut size: u32) {
// Given
let mut ctx = setup_execution_context();
ctx
.set_return_data(
array![
1,
2,
3,
4,
5,
6,
7,
8,
9,
10,
11,
12,
13,
14,
15,
16,
17,
18,
19,
20,
21,
22,
23,
24,
25,
26,
27,
28,
29,
30,
31,
32,
33,
34,
35,
36
]
);

let return_data: Span<u8> = ctx.return_data();

if (size == 0) {
size = return_data.len() - offset;
}

ctx.stack.push(size.into());
ctx.stack.push(offset.into());
ctx.stack.push(dest_offset.into());

// When
let res = ctx.exec_returndatacopy();

// Then
assert(ctx.stack.is_empty(), 'stack should be empty');

match u32_overflowing_add(offset, size) {
Result::Ok(x) => {
if (x > return_data.len()) {
assert(
res.unwrap_err() == EVMError::ReturnDataError(RETURNDATA_OUT_OF_BOUNDS_ERROR),
'should return out of bounds'
);
return;
}
},
Result::Err(x) => {
assert(
res.unwrap_err() == EVMError::ReturnDataError(RETURNDATA_OUT_OF_BOUNDS_ERROR),
'should return out of bounds'
);
return;
}
}

let result: u256 = ctx.memory.load_internal(dest_offset).into();
let mut results: Array<u8> = ArrayTrait::new();

let mut i = 0;
loop {
if i == (size / 32) + 1 {
break;
}

let result: u256 = ctx.memory.load_internal(dest_offset + (i * 32)).into();
let result_span = u256_to_bytes_array(result).span();

if ((i + 1) * 32 > size) {
ArrayExtensionTrait::concat(ref results, result_span.slice(0, size - (i * 32)));
} else {
ArrayExtensionTrait::concat(ref results, result_span);
}

i += 1;
};
assert(results.span() == return_data.slice(offset, size), 'wrong data value');
}
1 change: 1 addition & 0 deletions crates/utils/src/helpers.cairo
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ use starknet::{EthAddress, EthAddressIntoFelt252};
use cmp::min;
use utils::constants;


/// Ceils a number of bits to the next word (32 bytes)
///
/// # Arguments
Expand Down
Loading