From 947640a19062814c276030d89dc5be10e59dda8b Mon Sep 17 00:00:00 2001 From: Elias Tazartes Date: Fri, 28 Jul 2023 14:28:17 +0200 Subject: [PATCH] feat: create first version of skeleton for kakarot ssj --- src/contract_account/contract_account.cairo | 6 ++ src/eoa/eth_transaction.cairo | 61 ++++++++++++++++ src/eoa/externally_owned_account.cairo | 2 + src/eoa/rlp.cairo | 3 + src/instructions.cairo | 4 +- src/memory.cairo | 36 +++++----- src/tests/contract_account.cairo | 3 + .../test_contract_account.cairo | 2 + src/tests/eoa.cairo | 3 + src/tests/eoa/test_eth_transaction.cairo | 2 + .../eoa/test_externally_owned_account.cairo | 2 + src/tests/eoa/test_rlp.cairo | 2 + src/tests/instructions.cairo | 3 + .../test_stop_and_arithmetic_operations.cairo | 2 + src/tests/test_memory.cairo | 14 ++-- src/utils.cairo | 72 ------------------- src/utils/helpers.cairo | 31 ++++++++ 17 files changed, 151 insertions(+), 97 deletions(-) create mode 100644 src/contract_account/contract_account.cairo create mode 100644 src/eoa/eth_transaction.cairo create mode 100644 src/eoa/externally_owned_account.cairo create mode 100644 src/eoa/rlp.cairo create mode 100644 src/tests/contract_account.cairo create mode 100644 src/tests/contract_account/test_contract_account.cairo create mode 100644 src/tests/eoa.cairo create mode 100644 src/tests/eoa/test_eth_transaction.cairo create mode 100644 src/tests/eoa/test_externally_owned_account.cairo create mode 100644 src/tests/eoa/test_rlp.cairo create mode 100644 src/tests/instructions.cairo create mode 100644 src/tests/instructions/test_stop_and_arithmetic_operations.cairo diff --git a/src/contract_account/contract_account.cairo b/src/contract_account/contract_account.cairo new file mode 100644 index 000000000..78668b06d --- /dev/null +++ b/src/contract_account/contract_account.cairo @@ -0,0 +1,6 @@ +// Migrate https://github.com/kkrt-labs/kakarot/blob/7ec7a96074394ddb592a2b6fbea279c6c5cb25a6/src/kakarot/accounts/contract/contract_account.cairo#L4 +// Note that we don't need proxies anymore with the new idiomatic way to replace implementations. +// For now, as discussed with Shahar Papini, we can still use storage slots to store bytecode. +// That being said, we can modify the way we store it with new mappings and storage outlays to optimize steps. +// Use Traits, impls blocks and idiomatic Cairo 1.0 + diff --git a/src/eoa/eth_transaction.cairo b/src/eoa/eth_transaction.cairo new file mode 100644 index 000000000..f40304c17 --- /dev/null +++ b/src/eoa/eth_transaction.cairo @@ -0,0 +1,61 @@ +use array::ArrayTrait; + +struct EthereumTransaction { + nonce: felt252, + gas_price: felt252, + gas_limit: felt252, + destination: felt252, + amount: felt252, + payload: @Array, + tx_hash: u256, + v: felt252, + r: u256, + s: u256, +} + +trait EthTransaction { + /// Decode a legacy Ethereum transaction + /// This function decodes a legacy Ethereum transaction in accordance with EIP-155. + /// It returns transaction details including nonce, gas price, gas limit, destination address, amount, payload, + /// transaction hash, and signature (v, r, s). The transaction hash is computed by keccak hashing the signed + /// transaction data, which includes the chain ID in accordance with EIP-155. + /// # Arguments + /// tx_data The raw transaction data + fn decode_legacy_tx(tx_data: @Array) -> EthereumTransaction; + + /// Decode a modern Ethereum transaction + /// This function decodes a modern Ethereum transaction in accordance with EIP-2718. + /// It returns transaction details including nonce, gas price, gas limit, destination address, amount, payload, + /// transaction hash, and signature (v, r, s). The transaction hash is computed by keccak hashing the signed + /// transaction data, which includes the chain ID as part of the transaction data itself. + /// # Arguments + /// tx_data The raw transaction data + fn decode_tx(tx_data: @Array) -> EthereumTransaction; + + /// Check if a raw transaction is a legacy Ethereum transaction + /// This function checks if a raw transaction is a legacy Ethereum transaction by checking the transaction type + /// according to EIP-2718. If the transaction type is less than or equal to 0xc0, it's a legacy transaction. + /// # Arguments + /// - `tx_data` The raw transaction data + fn is_legacy_tx(tx_data: @Array) -> bool; + + /// Decode a raw Ethereum transaction + /// This function decodes a raw Ethereum transaction. It checks if the transaction + /// is a legacy transaction or a modern transaction, and calls the appropriate decode function + /// resp. `decode_legacy_tx` or `decode_tx` based on the result. + /// # Arguments + /// - `tx_data` The raw transaction data + fn decode(tx_data: @Array) -> EthereumTransaction; + + /// Validate an Ethereum transaction + /// This function validates an Ethereum transaction by checking if the transaction + /// is correctly signed by the given address, and if the nonce in the transaction + /// matches the nonce of the account. + /// It decodes the transaction using the decode function, + /// and then verifies the Ethereum signature on the transaction hash. + /// # Arguments + /// - `address` The ethereum address that is supposed to have signed the transaction + /// - `account_nonce` The nonce of the account + /// - `param tx_data` The raw transaction data + fn validate_eth_tx(address: felt252, account_nonce: felt252, tx_data: @Array) -> bool; +} diff --git a/src/eoa/externally_owned_account.cairo b/src/eoa/externally_owned_account.cairo new file mode 100644 index 000000000..5fe9db18d --- /dev/null +++ b/src/eoa/externally_owned_account.cairo @@ -0,0 +1,2 @@ +// Migrate https://github.com/kkrt-labs/kakarot/blob/7ec7a96074394ddb592a2b6fbea279c6c5cb25a6/src/kakarot/accounts/eoa/externally_owned_account.cairo#L4 + diff --git a/src/eoa/rlp.cairo b/src/eoa/rlp.cairo new file mode 100644 index 000000000..73ae680ac --- /dev/null +++ b/src/eoa/rlp.cairo @@ -0,0 +1,3 @@ +// Reproduce the original rlp.cairo from https://github.com/kkrt-labs/kakarot/blob/7ec7a96074394ddb592a2b6fbea279c6c5cb25a6/src/utils/rlp.cairo#L18 +// By using new Cairo idiomatic ways: Traits, Struct and impl blocks. + diff --git a/src/instructions.cairo b/src/instructions.cairo index b3e52cf66..2c4e002c0 100644 --- a/src/instructions.cairo +++ b/src/instructions.cairo @@ -7,7 +7,7 @@ use traits::Into; use kakarot::context::ExecutionContext; use kakarot::context::CallContextTrait; use kakarot::context::ExecutionSummary; -use kakarot::utils; +use kakarot::utils::helpers; /// Sub modules. mod block_information; @@ -69,7 +69,7 @@ impl EVMInstructionsImpl of EVMInstructionsTrait { // Check if PC is not out of bounds. if pc >= bytecode_len { - utils::panic_with_code(0); + helpers::panic_with_code(0); } let opcode: u8 = *bytecode.at(pc); diff --git a/src/memory.cairo b/src/memory.cairo index fbceee73d..6b47fdd64 100644 --- a/src/memory.cairo +++ b/src/memory.cairo @@ -144,7 +144,7 @@ impl MemoryImpl of MemoryTrait { // |-- mask = 256 ** offset_in_chunk let mask: u256 = helpers::pow256_rev(offset_in_chunk); - let mask_c: u256 = utils::pow(256, 16).into() / mask; + let mask_c: u256 = helpers::pow(256, 16).into() / mask; // Split the 2 input bytes16 chunks at offset_in_chunk. @@ -256,22 +256,22 @@ impl MemoryImpl of MemoryTrait { break (); } - let current: felt252 = ((*elements[0]).into() * utils::pow(256, 15) - + (*elements[1]).into() * utils::pow(256, 14) - + (*elements[2]).into() * utils::pow(256, 13) - + (*elements[3]).into() * utils::pow(256, 12) - + (*elements[4]).into() * utils::pow(256, 11) - + (*elements[5]).into() * utils::pow(256, 10) - + (*elements[6]).into() * utils::pow(256, 9) - + (*elements[7]).into() * utils::pow(256, 8) - + (*elements[8]).into() * utils::pow(256, 7) - + (*elements[9]).into() * utils::pow(256, 6) - + (*elements[10]).into() * utils::pow(256, 5) - + (*elements[11]).into() * utils::pow(256, 4) - + (*elements[12]).into() * utils::pow(256, 3) - + (*elements[13]).into() * utils::pow(256, 2) - + (*elements[14]).into() * utils::pow(256, 1) - + (*elements[15]).into() * utils::pow(256, 0)); + let current: felt252 = ((*elements[0]).into() * helpers::pow(256, 15) + + (*elements[1]).into() * helpers::pow(256, 14) + + (*elements[2]).into() * helpers::pow(256, 13) + + (*elements[3]).into() * helpers::pow(256, 12) + + (*elements[4]).into() * helpers::pow(256, 11) + + (*elements[5]).into() * helpers::pow(256, 10) + + (*elements[6]).into() * helpers::pow(256, 9) + + (*elements[7]).into() * helpers::pow(256, 8) + + (*elements[8]).into() * helpers::pow(256, 7) + + (*elements[9]).into() * helpers::pow(256, 6) + + (*elements[10]).into() * helpers::pow(256, 5) + + (*elements[11]).into() * helpers::pow(256, 4) + + (*elements[12]).into() * helpers::pow(256, 3) + + (*elements[13]).into() * helpers::pow(256, 2) + + (*elements[14]).into() * helpers::pow(256, 1) + + (*elements[15]).into() * helpers::pow(256, 0)); self.items.insert(chunk_index.into(), current.try_into().unwrap()); chunk_index += 1; @@ -302,7 +302,7 @@ impl MemoryImpl of MemoryTrait { // Compute mask. let mask: u256 = helpers::pow256_rev(offset_in_chunk); - let mask_c: u256 = utils::pow(2, 128).into() / mask; + let mask_c: u256 = helpers::pow(2, 128).into() / mask; // Read the words at chunk_index, +1, +2. let w0: u128 = self.items.get(chunk_index.into()); diff --git a/src/tests/contract_account.cairo b/src/tests/contract_account.cairo new file mode 100644 index 000000000..508770bd2 --- /dev/null +++ b/src/tests/contract_account.cairo @@ -0,0 +1,3 @@ +// import modules tests/contract_account folder + + diff --git a/src/tests/contract_account/test_contract_account.cairo b/src/tests/contract_account/test_contract_account.cairo new file mode 100644 index 000000000..33b88ad19 --- /dev/null +++ b/src/tests/contract_account/test_contract_account.cairo @@ -0,0 +1,2 @@ +// tests go here + diff --git a/src/tests/eoa.cairo b/src/tests/eoa.cairo new file mode 100644 index 000000000..5d4804f4a --- /dev/null +++ b/src/tests/eoa.cairo @@ -0,0 +1,3 @@ +// import modules from tests/eoa folder here + + diff --git a/src/tests/eoa/test_eth_transaction.cairo b/src/tests/eoa/test_eth_transaction.cairo new file mode 100644 index 000000000..33b88ad19 --- /dev/null +++ b/src/tests/eoa/test_eth_transaction.cairo @@ -0,0 +1,2 @@ +// tests go here + diff --git a/src/tests/eoa/test_externally_owned_account.cairo b/src/tests/eoa/test_externally_owned_account.cairo new file mode 100644 index 000000000..33b88ad19 --- /dev/null +++ b/src/tests/eoa/test_externally_owned_account.cairo @@ -0,0 +1,2 @@ +// tests go here + diff --git a/src/tests/eoa/test_rlp.cairo b/src/tests/eoa/test_rlp.cairo new file mode 100644 index 000000000..33b88ad19 --- /dev/null +++ b/src/tests/eoa/test_rlp.cairo @@ -0,0 +1,2 @@ +// tests go here + diff --git a/src/tests/instructions.cairo b/src/tests/instructions.cairo new file mode 100644 index 000000000..351a680f8 --- /dev/null +++ b/src/tests/instructions.cairo @@ -0,0 +1,3 @@ +// import modules from tests/instructions folder here + + diff --git a/src/tests/instructions/test_stop_and_arithmetic_operations.cairo b/src/tests/instructions/test_stop_and_arithmetic_operations.cairo new file mode 100644 index 000000000..8b1cd6212 --- /dev/null +++ b/src/tests/instructions/test_stop_and_arithmetic_operations.cairo @@ -0,0 +1,2 @@ +// tests for stop and arithmetic operations go here + diff --git a/src/tests/test_memory.cairo b/src/tests/test_memory.cairo index b8f3d6164..96401c3ed 100644 --- a/src/tests/test_memory.cairo +++ b/src/tests/test_memory.cairo @@ -120,21 +120,23 @@ fn _load_should_load_an_element_from_the_memory_with_offset(offset: usize, low: #[available_gas(200000000)] fn test__load__should_load_an_element_from_the_memory_with_offset_1() { _load_should_load_an_element_from_the_memory_with_offset( - 8, 2 * utils::pow(256, 8).try_into().unwrap(), utils::pow(256, 8).try_into().unwrap() + 8, 2 * helpers::pow(256, 8).try_into().unwrap(), helpers::pow(256, 8).try_into().unwrap() ); } #[test] #[available_gas(200000000)] fn test__load__should_load_an_element_from_the_memory_with_offset_2() { _load_should_load_an_element_from_the_memory_with_offset( - 7, 2 * utils::pow(256, 7).try_into().unwrap(), utils::pow(256, 7).try_into().unwrap() + 7, 2 * helpers::pow(256, 7).try_into().unwrap(), helpers::pow(256, 7).try_into().unwrap() ); } #[test] #[available_gas(200000000)] fn test__load__should_load_an_element_from_the_memory_with_offset_3() { _load_should_load_an_element_from_the_memory_with_offset( - 23, 3 * utils::pow(256, 7).try_into().unwrap(), 2 * utils::pow(256, 7).try_into().unwrap() + 23, + 3 * helpers::pow(256, 7).try_into().unwrap(), + 2 * helpers::pow(256, 7).try_into().unwrap() ); } @@ -142,14 +144,16 @@ fn test__load__should_load_an_element_from_the_memory_with_offset_3() { #[available_gas(200000000)] fn test__load__should_load_an_element_from_the_memory_with_offset_4() { _load_should_load_an_element_from_the_memory_with_offset( - 33, 4 * utils::pow(256, 1).try_into().unwrap(), 3 * utils::pow(256, 1).try_into().unwrap() + 33, + 4 * helpers::pow(256, 1).try_into().unwrap(), + 3 * helpers::pow(256, 1).try_into().unwrap() ); } #[test] #[available_gas(200000000)] fn test__load__should_load_an_element_from_the_memory_with_offset_5() { _load_should_load_an_element_from_the_memory_with_offset( - 63, 0, 4 * utils::pow(256, 15).try_into().unwrap() + 63, 0, 4 * helpers::pow(256, 15).try_into().unwrap() ); } diff --git a/src/utils.cairo b/src/utils.cairo index 2c88e7dbf..30c659faa 100644 --- a/src/utils.cairo +++ b/src/utils.cairo @@ -5,75 +5,3 @@ use option::OptionTrait; mod helpers; mod constants; -/// Panic with a custom message. -/// # Arguments -/// * `msg` - The message to panic with. Must be a short string to fit in a felt252. -fn panic_with(err: felt252) { - let mut data = ArrayTrait::new(); - data.append(err); - panic(data); -} - -/// Convert a `felt252` to a `NonZero` type. -/// # Arguments -/// * `felt252` - The `felt252` to convert. -/// # Returns -/// * `Option::>` - The `felt252` as a `NonZero` type. -/// * `Option::>::None` - If `felt252` is 0. -fn to_non_zero(felt252: felt252) -> Option::> { - let res = felt252_is_zero(felt252); - match res { - zeroable::IsZeroResult::Zero(()) => Option::>::None(()), - zeroable::IsZeroResult::NonZero(val) => Option::>::Some(val), - } -} - - -/// Force conversion from `felt252` to `u128`. -fn unsafe_felt252_to_u128(a: felt252) -> u128 { - let res = integer::u128_try_from_felt252(a); - res.unwrap() -} - -/// Perform euclidean division on `felt252` types. -fn unsafe_euclidean_div_no_remainder(a: felt252, b: felt252) -> felt252 { - let a_u128 = unsafe_felt252_to_u128(a); - let b_u128 = unsafe_felt252_to_u128(b); - integer::u128_to_felt252(a_u128 / b_u128) -} - -fn unsafe_euclidean_div(a: felt252, b: felt252) -> (felt252, felt252) { - let a_u128 = unsafe_felt252_to_u128(a); - let b_u128 = unsafe_felt252_to_u128(b); - (integer::u128_to_felt252(a_u128 / b_u128), integer::u128_to_felt252(a_u128 % b_u128)) -} - -fn max(a: usize, b: usize) -> usize { - if a > b { - return a; - } else { - return b; - } -} - -// Raise a number to a power. -/// * `base` - The number to raise. -/// * `exp` - The exponent. -/// # Returns -/// * `felt252` - The result of base raised to the power of exp. -fn pow(base: felt252, exp: felt252) -> felt252 { - if exp == 0 { - return 1; - } else { - return base * pow(base, exp - 1); - } -} - -/// Panic with a custom code. -/// # Arguments -/// * `code` - The code to panic with. Must be a short string to fit in a felt252. -fn panic_with_code(code: felt252) { - let mut data = ArrayTrait::new(); - data.append(code); - panic(data); -} diff --git a/src/utils/helpers.cairo b/src/utils/helpers.cairo index e30562b98..11c4ef926 100644 --- a/src/utils/helpers.cairo +++ b/src/utils/helpers.cairo @@ -240,3 +240,34 @@ impl U256TryIntoU128 of TryInto { Option::Some(self.low) } } + + +fn max(a: usize, b: usize) -> usize { + if a > b { + return a; + } else { + return b; + } +} + +// Raise a number to a power. +/// * `base` - The number to raise. +/// * `exp` - The exponent. +/// # Returns +/// * `felt252` - The result of base raised to the power of exp. +fn pow(base: felt252, exp: felt252) -> felt252 { + if exp == 0 { + return 1; + } else { + return base * pow(base, exp - 1); + } +} + +/// Panic with a custom code. +/// # Arguments +/// * `code` - The code to panic with. Must be a short string to fit in a felt252. +fn panic_with_code(code: felt252) { + let mut data = ArrayTrait::new(); + data.append(code); + panic(data); +}