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 0x20 - SHA3 Opcode #281

Merged
merged 31 commits into from
Sep 25, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
31 commits
Select commit Hold shift + click to select a range
e4c725c
testing herodotus fct
Quentash Sep 4, 2023
de98b54
preclean
Quentash Sep 5, 2023
ad153f7
cleaned code and tests
Quentash Sep 5, 2023
d0f3ef3
Merge branch 'main' into feat/sha3
Quentash Sep 5, 2023
098b586
merge main
Quentash Sep 5, 2023
39cc37a
forgot to change gas_limit back to 1k
Quentash Sep 5, 2023
5675256
review correction
Quentash Sep 8, 2023
8236936
Merge branch 'main' into feat/sha3
Quentash Sep 8, 2023
daa9d42
added helpers
Quentash Sep 12, 2023
ddcae3f
Merge branch 'main' into feat/sha3
Quentash Sep 12, 2023
66f36a1
soluce2 + check on memory load
Quentash Sep 12, 2023
c0fd0a5
typos
Quentash Sep 12, 2023
4deaf22
added internal sha3 tests
Quentash Sep 13, 2023
ed2a4d1
added helpers tests
Quentash Sep 13, 2023
3af3926
wrong error message
Quentash Sep 13, 2023
479dfaf
Merge branch 'main' into feat/sha3
Quentash Sep 13, 2023
c81a09f
used mod internal instead of trait
Quentash Sep 13, 2023
ecfc4e0
functions and comments rework
Quentash Sep 14, 2023
c4d1ff9
Merge branch 'main' into feat/sha3
Quentash Sep 14, 2023
02f1740
bug correction due to merge
Quentash Sep 14, 2023
dc95762
removed unnecessary computation
Quentash Sep 14, 2023
d9f6bc6
quick fix review
Quentash Sep 14, 2023
04bd9a1
corrected tests names
Quentash Sep 15, 2023
a0fddb5
Merge branch 'main' into feat/sha3
Quentash Sep 15, 2023
e321a0a
docs: added fn docs
enitrat Sep 18, 2023
cd224ff
made compute_words more verbose
Quentash Sep 20, 2023
b229e63
Made helper fcts on u256trait
Quentash Sep 21, 2023
45f4b18
modified comments
Quentash Sep 21, 2023
cfdf5b4
Ensured memory lenght
Quentash Sep 22, 2023
d323bf9
added memory size verification to tests
Quentash Sep 22, 2023
9e5b0b9
Merge branch 'main' into feat/sha3
Quentash Sep 22, 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
1 change: 1 addition & 0 deletions crates/evm/src/instructions.cairo
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ mod push_operations;
use push_operations::PushOperationsTrait;

mod sha3;
use sha3::Sha3Trait;

mod stop_and_arithmetic_operations;
use stop_and_arithmetic_operations::StopAndArithmeticOperationsTrait;
Expand Down
153 changes: 148 additions & 5 deletions crates/evm/src/instructions/sha3.cairo
Original file line number Diff line number Diff line change
@@ -1,9 +1,152 @@
//! SHA3.

// Internal imports
use evm::context::{ExecutionContextTrait, ExecutionContext};
use evm::context::{ExecutionContext, ExecutionContextTrait, BoxDynamicExecutionContextDestruct};
use evm::stack::StackTrait;
use evm::memory::MemoryTrait;
use evm::errors::EVMError;
use evm::helpers::U256IntoResultU32;
use keccak::{cairo_keccak, u128_split};
use utils::helpers::{ArrayExtensionTrait, U256Trait};

/// SHA3 operation.
/// Hashes n bytes in memory at a given offset in memory.
/// # Specification: https://www.evm.codes/#20?fork=shanghai
fn exec_sha3(ref context: ExecutionContext) {}
use array::ArrayTrait;

#[generate_trait]
impl Sha3Impl of Sha3Trait {
/// SHA3 operation : Hashes n bytes in memory at a given offset in memory
/// and push the hash result to the stack.
///
/// # Inputs
/// * `offset` - The offset in memory where to read the datas
/// * `size` - The amount of bytes to read
///
/// # Specification: https://www.evm.codes/#20?fork=shanghai
fn exec_sha3(ref self: ExecutionContext) -> Result<(), EVMError> {
let offset: usize = self.stack.pop_usize()?;
let mut size: usize = self.stack.pop_usize()?;

let mut to_hash: Array<u64> = Default::default();
Eikix marked this conversation as resolved.
Show resolved Hide resolved

let (nb_words, nb_zeroes) = internal::compute_memory_words_amount(
size, offset, self.memory.bytes_len
);
let mut last_input_offset = internal::fill_array_with_memory_words(
ref self, ref to_hash, offset, nb_words
);
// Fill array to hash with zeroes for bytes out of memory bound
Eikix marked this conversation as resolved.
Show resolved Hide resolved
// which is faster than reading them from memory
to_hash.append_n(0, 4 * nb_zeroes);
Eikix marked this conversation as resolved.
Show resolved Hide resolved

// For cases where the size of bytes to hash isn't a multiple of 8,
// prepare the last bytes to hash into last_input instead of appending
// it to to_hash.
let last_input: u64 = if (size % 32 != 0) {
Eikix marked this conversation as resolved.
Show resolved Hide resolved
let loaded = self.memory.load(last_input_offset);
internal::prepare_last_input(ref to_hash, loaded, size % 32)
} else {
0
};
// Properly set the memory length in case we skipped reading zeroes
self.memory.ensure_length(size + offset);

let mut hash = cairo_keccak(ref to_hash, last_input, size % 8);
self.stack.push(hash.reverse_endianness())
}
}


mod internal {
use evm::context::{ExecutionContext, ExecutionContextTrait, BoxDynamicExecutionContextDestruct};
use evm::stack::StackTrait;
use evm::memory::MemoryTrait;
use utils::helpers::U256Trait;

/// Computes how many words are read from the memory
/// and how many words must be filled with zeroes
/// given a target size, a memory offset and the length of the memory.
///
/// # Arguments
///
/// * `size` - The amount of bytes to hash
/// * `offset` - Offset in memory
/// * `mem_len` - Size of the memory
/// Returns : (nb_words, nb_zeroes)
fn compute_memory_words_amount(size: u32, offset: u32, mem_len: u32) -> (u32, u32) {
// Bytes to hash are less than a word size
if size < 32 {
return (0, 0);
}
// Bytes out of memory bound are zeroes
if offset > mem_len {
return (0, size / 32);
}
// The only word to read from memory is less than 32 bytes
if mem_len - offset < 32 {
return (1, (size / 32) - 1);
}

let bytes_to_read = cmp::min(mem_len - offset, size);
let nb_words = bytes_to_read / 32;
(nb_words, (size / 32) - nb_words)
}

/// Fills the `to_hash` array with little endian u64s
/// by splitting words read from the memory and
/// returns the next offset to read from.
///
/// # Arguments
///
/// * `self` - The context in which the memory is read
/// * `to_hash` - A reference to the array to fill
/// * `offset` - Offset in memory to start reading from
/// * `amount` - The amount of words to read from memory
/// Return the new offset
fn fill_array_with_memory_words(
ref self: ExecutionContext, ref to_hash: Array<u64>, mut offset: u32, mut amount: u32
) -> u32 {
loop {
if amount == 0 {
break;
}
let loaded = self.memory.load(offset);
let ((high_h, low_h), (high_l, low_l)) = loaded.split_into_u64_le();
to_hash.append(low_h);
to_hash.append(high_h);
to_hash.append(low_l);
to_hash.append(high_l);

offset += 32;
amount -= 1;
};
offset
}

/// Fills the `to_hash` array with the n-1 remaining little endian u64
/// depending on size from a word and returns
/// the u64 containing the last 8 bytes word to hash.
///
/// # Arguments
///
/// * `to_hash` - A reference to the array to fill
/// * `value` - The word to split in u64 words
/// * `size` - The amount of bytes still required to hash
/// Returns the last u64 word that isn't 8 Bytes long.
fn prepare_last_input(ref to_hash: Array<u64>, value: u256, size: u32) -> u64 {
let ((high_h, low_h), (high_l, low_l)) = value.split_into_u64_le();
if size < 8 {
return low_h;
} else if size < 16 {
to_hash.append(low_h);
return high_h;
} else if size < 24 {
to_hash.append(low_h);
to_hash.append(high_h);
return low_l;
} else {
to_hash.append(low_h);
to_hash.append(high_h);
to_hash.append(low_l);
return high_l;
}
}
}
1 change: 1 addition & 0 deletions crates/evm/src/tests/test_instructions.cairo
Original file line number Diff line number Diff line change
Expand Up @@ -7,3 +7,4 @@ mod test_push_operations;
mod test_memory_operations;
mod test_exchange_operations;
mod test_system_operations;
mod test_sha3;
Loading
Loading