From ff7daaf98da0ac47b0accebb7e04c26ba4ae726c Mon Sep 17 00:00:00 2001 From: enitrat Date: Thu, 26 Sep 2024 13:30:32 +0200 Subject: [PATCH 1/2] refactor: utils crate --- crates/contracts/src/cairo1_helpers.cairo | 2 +- .../contracts/src/kakarot_core/eth_rpc.cairo | 2 +- .../tests/test_account_contract.cairo | 10 +- .../contracts/tests/test_cairo1_helpers.cairo | 2 +- .../tests/test_execution_from_outside.cairo | 32 +- .../contracts/tests/test_kakarot_core.cairo | 29 +- crates/contracts/tests/test_ownable.cairo | 7 +- crates/evm/src/backend/starknet_backend.cairo | 4 +- crates/evm/src/backend/validation.cairo | 1 - crates/evm/src/errors.cairo | 2 +- crates/evm/src/gas.cairo | 2 +- .../environmental_information.cairo | 6 +- .../src/instructions/memory_operations.cairo | 2 +- .../src/instructions/push_operations.cairo | 3 +- crates/evm/src/instructions/sha3.cairo | 4 +- .../stop_and_arithmetic_operations.cairo | 2 +- .../src/instructions/system_operations.cairo | 2 +- crates/evm/src/interpreter.cairo | 3 +- crates/evm/src/memory.cairo | 7 +- crates/evm/src/model/account.cairo | 4 +- crates/evm/src/precompiles/blake2f.cairo | 4 +- .../precompiles/ec_operations/ec_add.cairo | 74 +- .../precompiles/ec_operations/ec_mul.cairo | 8 +- crates/evm/src/precompiles/ec_recover.cairo | 6 +- crates/evm/src/precompiles/modexp.cairo | 3 +- crates/evm/src/precompiles/p256verify.cairo | 4 +- crates/evm/src/precompiles/sha256.cairo | 5 +- crates/evm/src/state.cairo | 2 +- crates/utils/src/address.cairo | 3 +- crates/utils/src/crypto/modexp/arith.cairo | 14 +- crates/utils/src/crypto/modexp/lib.cairo | 2 +- crates/utils/src/crypto/modexp/mpnat.cairo | 6 +- crates/utils/src/eth_transaction.cairo | 5 +- .../src/eth_transaction/transaction.cairo | 2 +- crates/utils/src/felt_vec.cairo | 800 ++++++ crates/utils/src/helpers.cairo | 2435 +---------------- crates/utils/src/lib.cairo | 1 + crates/utils/src/rlp.cairo | 4 +- crates/utils/src/set.cairo | 2 +- crates/utils/src/traits.cairo | 3 + crates/utils/src/traits/array.cairo | 98 + crates/utils/src/traits/bytes.cairo | 815 ++++++ crates/utils/src/traits/integer.cairo | 671 +++++ 43 files changed, 2548 insertions(+), 2545 deletions(-) create mode 100644 crates/utils/src/felt_vec.cairo create mode 100644 crates/utils/src/traits/array.cairo create mode 100644 crates/utils/src/traits/bytes.cairo create mode 100644 crates/utils/src/traits/integer.cairo diff --git a/crates/contracts/src/cairo1_helpers.cairo b/crates/contracts/src/cairo1_helpers.cairo index 872e134e4..4bd3b89d2 100644 --- a/crates/contracts/src/cairo1_helpers.cairo +++ b/crates/contracts/src/cairo1_helpers.cairo @@ -123,7 +123,7 @@ pub mod embeddable_impls { use evm::precompiles::EcAdd; use evm::precompiles::EcMul; use evm::precompiles::Sha256; - use utils::helpers::U256Trait; + use utils::traits::integer::U256Trait; #[starknet::embeddable] diff --git a/crates/contracts/src/kakarot_core/eth_rpc.cairo b/crates/contracts/src/kakarot_core/eth_rpc.cairo index 9e7786353..c07468b75 100644 --- a/crates/contracts/src/kakarot_core/eth_rpc.cairo +++ b/crates/contracts/src/kakarot_core/eth_rpc.cairo @@ -9,7 +9,7 @@ use evm::backend::validation::validate_eth_tx; use evm::model::{TransactionResult, Address}; use evm::{EVMTrait}; use utils::constants::POW_2_53; -use utils::eth_transaction::transaction::{TransactionTrait, Transaction}; +use utils::eth_transaction::transaction::Transaction; #[starknet::interface] pub trait IEthRPC { diff --git a/crates/contracts/tests/test_account_contract.cairo b/crates/contracts/tests/test_account_contract.cairo index 663c62c79..101967435 100644 --- a/crates/contracts/tests/test_account_contract.cairo +++ b/crates/contracts/tests/test_account_contract.cairo @@ -6,8 +6,8 @@ use contracts::test_utils::{ use contracts::{IAccountDispatcher, IAccountDispatcherTrait}; use core::starknet::account::{Call}; use core::starknet::{EthAddress, ContractAddress}; -use evm::test_utils::{ca_address, native_token, eoa_address}; -use openzeppelin::token::erc20::interface::{IERC20CamelDispatcher, IERC20CamelDispatcherTrait}; +use evm::test_utils::{ca_address, eoa_address}; +use openzeppelin::token::erc20::interface::IERC20CamelDispatcherTrait; use snforge_std::{start_cheat_caller_address, stop_cheat_caller_address}; #[test] @@ -101,7 +101,7 @@ fn test_ca_external_starknet_call_native_token() { let (success, data) = contract_account.execute_starknet_call(call); stop_cheat_caller_address(ca_address.starknet); - assert(success == true, 'execute_starknet_call failed'); + assert(success, 'execute_starknet_call failed'); assert(data.len() == 2, 'wrong return data length'); let balance = native_token.balanceOf(ca_address.starknet); assert((*data[0], *data[1]) == (balance.low.into(), balance.high.into()), 'wrong return data'); @@ -122,7 +122,7 @@ fn test_ca_external_starknet_call_kakarot_get_starknet_address() { let (success, data) = contract_account.execute_starknet_call(call); stop_cheat_caller_address(ca_address.starknet); - assert(success == true, 'execute_starknet_call failed'); + assert(success, 'execute_starknet_call failed'); assert(data.len() == 1, 'wrong return data length'); assert(*data[0] == ca_address.starknet.try_into().unwrap(), 'wrong return data'); } @@ -142,7 +142,7 @@ fn test_ca_external_starknet_call_cannot_call_kakarot_other_selector() { let (success, data) = contract_account.execute_starknet_call(call); stop_cheat_caller_address(ca_address.starknet); - assert(success == false, 'execute_starknet_call failed'); + assert(!success, 'execute_starknet_call failed'); assert(data.len() == 19, 'wrong return data length'); assert(data == KAKAROT_REENTRANCY.span(), 'wrong return data'); } diff --git a/crates/contracts/tests/test_cairo1_helpers.cairo b/crates/contracts/tests/test_cairo1_helpers.cairo index 6c4654665..ace7677d7 100644 --- a/crates/contracts/tests/test_cairo1_helpers.cairo +++ b/crates/contracts/tests/test_cairo1_helpers.cairo @@ -1,5 +1,5 @@ use contracts::cairo1_helpers::Cairo1Helpers; -use utils::helpers::BytesUsedTrait; +use utils::traits::integer::BytesUsedTrait; #[test] fn test_keccak() { diff --git a/crates/contracts/tests/test_execution_from_outside.cairo b/crates/contracts/tests/test_execution_from_outside.cairo index 218fa5de8..69a323f3e 100644 --- a/crates/contracts/tests/test_execution_from_outside.cairo +++ b/crates/contracts/tests/test_execution_from_outside.cairo @@ -12,33 +12,23 @@ use contracts::test_utils::{ use core::num::traits::Bounded; use core::starknet::account::Call; use core::starknet::secp256_trait::Signature; -use core::starknet::{ - ContractAddress, ClassHash, VALIDATED, get_contract_address, contract_address_const, EthAddress, - get_tx_info, Event -}; -use evm::test_utils::{ - kakarot_address, other_evm_address, other_starknet_address, eoa_address, tx_gas_limit, - gas_price, VMBuilderTrait -}; -use evm::test_utils::{ca_address, chain_id}; -use openzeppelin::token::erc20::interface::{IERC20CamelDispatcher, IERC20CamelDispatcherTrait}; +use core::starknet::{ContractAddress, contract_address_const, EthAddress, Event}; +use evm::test_utils::chain_id; +use evm::test_utils::other_evm_address; +use openzeppelin::token::erc20::interface::IERC20CamelDispatcher; use snforge_std::{ - start_cheat_caller_address, stop_cheat_caller_address, start_cheat_signature, - stop_cheat_signature, start_cheat_chain_id, stop_cheat_chain_id, start_cheat_transaction_hash, - stop_cheat_transaction_hash, spy_events, EventSpyTrait, EventsFilterTrait, CheatSpan, - cheat_caller_address, stop_mock_call, stop_cheat_block_timestamp, start_cheat_block_timestamp, - start_cheat_chain_id_global, stop_cheat_chain_id_global, start_mock_call, + start_cheat_caller_address, stop_cheat_caller_address, start_cheat_transaction_hash, spy_events, + EventSpyTrait, CheatSpan, cheat_caller_address, stop_cheat_block_timestamp, + start_cheat_block_timestamp, start_cheat_chain_id_global, stop_cheat_chain_id_global, start_cheat_caller_address_global, stop_cheat_caller_address_global }; -use snforge_utils::snforge_utils::{ContractEvents, ContractEventsTrait, EventsFilterBuilderTrait}; -use utils::eth_transaction::transaction::{Transaction, TransactionTrait}; +use snforge_utils::snforge_utils::EventsFilterBuilderTrait; +use utils::eth_transaction::transaction::Transaction; use utils::eth_transaction::tx_type::TxType; -use utils::helpers::{U8SpanExTrait, u256_to_bytes_array}; +use utils::helpers::u256_to_bytes_array; use utils::serialization::{serialize_bytes, serialize_transaction_signature}; -use utils::test_data::{ - legacy_rlp_encoded_tx, eip_2930_encoded_tx, eip_1559_encoded_tx, legacy_rlp_encoded_deploy_tx -}; +use utils::test_data::{legacy_rlp_encoded_tx, eip_2930_encoded_tx, eip_1559_encoded_tx}; fn transaction_signer() -> EthAddress { 0x6Bd85F39321B00c6d603474C5B2fddEB9c92A466.try_into().unwrap() diff --git a/crates/contracts/tests/test_kakarot_core.cairo b/crates/contracts/tests/test_kakarot_core.cairo index f343964f9..b5816bc75 100644 --- a/crates/contracts/tests/test_kakarot_core.cairo +++ b/crates/contracts/tests/test_kakarot_core.cairo @@ -1,43 +1,30 @@ use contracts::UninitializedAccount; use contracts::account_contract::{IAccountDispatcher, IAccountDispatcherTrait}; -use contracts::kakarot_core::interface::{ - IExtendedKakarotCoreDispatcher, IExtendedKakarotCoreDispatcherTrait -}; +use contracts::kakarot_core::interface::IExtendedKakarotCoreDispatcherTrait; use contracts::kakarot_core::{KakarotCore}; use contracts::test_contracts::test_upgradeable::{ - MockContractUpgradeableV1, IMockContractUpgradeableDispatcher, - IMockContractUpgradeableDispatcherTrait + IMockContractUpgradeableDispatcher, IMockContractUpgradeableDispatcherTrait }; use contracts::test_data::{deploy_counter_calldata, counter_evm_bytecode}; use contracts::{test_utils as contract_utils,}; -use core::fmt::{Debug, Formatter, Error}; use core::num::traits::Zero; use core::ops::SnapshotDeref; use core::option::OptionTrait; use core::starknet::storage::StoragePathEntry; -use core::starknet::{testing, contract_address_const, ContractAddress, EthAddress, ClassHash}; +use core::starknet::{contract_address_const, ContractAddress, EthAddress, ClassHash}; use core::traits::TryInto; -use evm::model::{Address}; -use evm::test_utils::{sequencer_evm_address, chain_id}; +use evm::test_utils::chain_id; use evm::test_utils; use snforge_std::{ - declare, DeclareResultTrait, start_cheat_caller_address, stop_cheat_caller_address, - start_cheat_signature, stop_cheat_signature, start_cheat_chain_id_global, - stop_cheat_chain_id_global, start_cheat_transaction_hash, stop_cheat_transaction_hash, - spy_events, Event, EventSpyTrait, test_address, cheat_caller_address, CheatSpan, store, load, - EventSpyAssertionsTrait, start_mock_call, stop_mock_call -}; -use snforge_utils::snforge_utils::{ - EventsFilterBuilderTrait, ContractEvents, ContractEventsTrait, store_evm + declare, DeclareResultTrait, start_cheat_caller_address, spy_events, EventSpyTrait, + cheat_caller_address, CheatSpan, store }; +use snforge_utils::snforge_utils::{EventsFilterBuilderTrait, ContractEventsTrait}; use starknet::storage::StorageTrait; -use utils::constants::EMPTY_KECCAK; -use utils::eth_transaction::common::{TxKind, TxKindTrait}; use utils::eth_transaction::legacy::TxLegacy; -use utils::eth_transaction::transaction::{Transaction, TransactionTrait}; -use utils::helpers::U8SpanExTrait; +use utils::eth_transaction::transaction::Transaction; use utils::helpers::{EthAddressExTrait, u256_to_bytes_array}; #[test] diff --git a/crates/contracts/tests/test_ownable.cairo b/crates/contracts/tests/test_ownable.cairo index df9ba9a36..7b343b47f 100644 --- a/crates/contracts/tests/test_ownable.cairo +++ b/crates/contracts/tests/test_ownable.cairo @@ -3,14 +3,11 @@ use contracts::test_utils::constants::{ZERO, OWNER, OTHER}; use contracts::test_utils; use core::num::traits::Zero; use core::starknet::ContractAddress; -use core::starknet::testing; use ownable_component::{InternalImpl, OwnableImpl}; -use snforge_std::{ - start_cheat_caller_address, stop_cheat_caller_address, spy_events, test_address, EventSpyTrait -}; -use snforge_utils::snforge_utils::{EventsFilterBuilderTrait, ContractEvents, ContractEventsTrait}; +use snforge_std::{start_cheat_caller_address, spy_events, test_address, EventSpyTrait}; +use snforge_utils::snforge_utils::{EventsFilterBuilderTrait, ContractEventsTrait}; #[starknet::contract] diff --git a/crates/evm/src/backend/starknet_backend.cairo b/crates/evm/src/backend/starknet_backend.cairo index 92b0be448..4382d5525 100644 --- a/crates/evm/src/backend/starknet_backend.cairo +++ b/crates/evm/src/backend/starknet_backend.cairo @@ -7,7 +7,7 @@ use core::starknet::storage::StoragePointerReadAccess; use core::starknet::syscalls::{deploy_syscall}; use core::starknet::syscalls::{emit_event_syscall}; use core::starknet::{EthAddress, get_block_info, SyscallResultTrait}; -use evm::errors::{ensure, EVMError, EOA_EXISTS}; +use evm::errors::{ensure, EVMError}; use evm::model::{Address, AddressTrait, Environment, Account, AccountTrait}; use evm::model::{Transfer}; use evm::state::{State, StateTrait}; @@ -258,8 +258,8 @@ mod tests { use snforge_std::{test_address, start_mock_call, get_class_hash}; use snforge_utils::snforge_utils::{assert_not_called, assert_called}; use super::commit_storage; - use utils::helpers::U8SpanExTrait; use utils::helpers::compute_starknet_address; + use utils::traits::bytes::U8SpanExTrait; // Helper function to create a test account fn create_test_account(is_selfdestruct: bool, is_created: bool, id: felt252) -> Account { diff --git a/crates/evm/src/backend/validation.cairo b/crates/evm/src/backend/validation.cairo index d16dfb546..8384c6290 100644 --- a/crates/evm/src/backend/validation.cairo +++ b/crates/evm/src/backend/validation.cairo @@ -7,7 +7,6 @@ use core::starknet::{get_caller_address}; use evm::gas; use openzeppelin::token::erc20::interface::{IERC20CamelDispatcher, IERC20CamelDispatcherTrait}; use starknet::storage::StorageTrait; -use utils::constants::POW_2_32; use utils::eth_transaction::check_gas_fee; use utils::eth_transaction::transaction::{Transaction, TransactionTrait}; diff --git a/crates/evm/src/errors.cairo b/crates/evm/src/errors.cairo index fa4587ca7..b6d17eaa5 100644 --- a/crates/evm/src/errors.cairo +++ b/crates/evm/src/errors.cairo @@ -1,5 +1,5 @@ use core::fmt::{Debug, Formatter, Error, Display}; -use utils::helpers::ToBytes; +use utils::traits::bytes::ToBytes; // STACK diff --git a/crates/evm/src/gas.cairo b/crates/evm/src/gas.cairo index 77274be0b..8d257c9b0 100644 --- a/crates/evm/src/gas.cairo +++ b/crates/evm/src/gas.cairo @@ -283,7 +283,7 @@ mod tests { use utils::eth_transaction::eip2930::{AccessListItem, TxEip2930}; use utils::eth_transaction::legacy::TxLegacy; use utils::eth_transaction::transaction::Transaction; - use utils::helpers::ToBytes; + use utils::traits::bytes::ToBytes; #[test] fn test_calculate_intrinsic_gas_cost() { diff --git a/crates/evm/src/instructions/environmental_information.cairo b/crates/evm/src/instructions/environmental_information.cairo index bc209eec9..7936a6a8a 100644 --- a/crates/evm/src/instructions/environmental_information.cairo +++ b/crates/evm/src/instructions/environmental_information.cairo @@ -312,9 +312,9 @@ mod tests { use evm::test_utils::{VMBuilderTrait, origin, callvalue, gas_price}; use snforge_std::test_address; use utils::constants::EMPTY_KECCAK; - use utils::helpers::{ - u256_to_bytes_array, ArrayExtTrait, compute_starknet_address, U8SpanExTrait - }; + use utils::helpers::{u256_to_bytes_array, compute_starknet_address}; + use utils::traits::array::ArrayExtTrait; + use utils::traits::bytes::{U8SpanExTrait}; use utils::traits::{EthAddressIntoU256}; diff --git a/crates/evm/src/instructions/memory_operations.cairo b/crates/evm/src/instructions/memory_operations.cairo index 3597803d0..b506815fe 100644 --- a/crates/evm/src/instructions/memory_operations.cairo +++ b/crates/evm/src/instructions/memory_operations.cairo @@ -319,8 +319,8 @@ mod tests { native_token }; use snforge_std::{test_address, start_mock_call}; - use utils::helpers::U8SpanExTrait; use utils::helpers::compute_starknet_address; + use utils::traits::bytes::U8SpanExTrait; #[test] fn test_pc_basic() { diff --git a/crates/evm/src/instructions/push_operations.cairo b/crates/evm/src/instructions/push_operations.cairo index f5fa6b9bb..ef6bdd8e2 100644 --- a/crates/evm/src/instructions/push_operations.cairo +++ b/crates/evm/src/instructions/push_operations.cairo @@ -4,8 +4,7 @@ use evm::errors::EVMError; use evm::gas; use evm::model::vm::{VM, VMTrait}; use evm::stack::StackTrait; -use utils::helpers::FromBytes; -use utils::helpers::U8SpanExTrait; +use utils::traits::bytes::{FromBytes, U8SpanExTrait}; /// Place i bytes items on stack. #[inline(always)] diff --git a/crates/evm/src/instructions/sha3.cairo b/crates/evm/src/instructions/sha3.cairo index e85bc528c..c0f1e7041 100644 --- a/crates/evm/src/instructions/sha3.cairo +++ b/crates/evm/src/instructions/sha3.cairo @@ -8,7 +8,9 @@ use evm::gas; use evm::memory::MemoryTrait; use evm::model::vm::{VM, VMTrait}; use evm::stack::StackTrait; -use utils::helpers::{ArrayExtTrait, U256Trait, ceil32}; +use utils::helpers::ceil32; +use utils::traits::array::ArrayExtTrait; +use utils::traits::integer::U256Trait; #[generate_trait] pub impl Sha3Impl of Sha3Trait { diff --git a/crates/evm/src/instructions/stop_and_arithmetic_operations.cairo b/crates/evm/src/instructions/stop_and_arithmetic_operations.cairo index 08f91b7cc..42542ea51 100644 --- a/crates/evm/src/instructions/stop_and_arithmetic_operations.cairo +++ b/crates/evm/src/instructions/stop_and_arithmetic_operations.cairo @@ -5,9 +5,9 @@ use evm::errors::EVMError; use evm::gas; use evm::model::vm::{VM, VMTrait}; use evm::stack::StackTrait; -use utils::helpers::BytesUsedTrait; use utils::i256::i256; use utils::math::{Exponentiation, WrappingExponentiation, u256_wide_add}; +use utils::traits::integer::BytesUsedTrait; #[generate_trait] pub impl StopAndArithmeticOperations of StopAndArithmeticOperationsTrait { diff --git a/crates/evm/src/instructions/system_operations.cairo b/crates/evm/src/instructions/system_operations.cairo index c6acbf01f..2fd210559 100644 --- a/crates/evm/src/instructions/system_operations.cairo +++ b/crates/evm/src/instructions/system_operations.cairo @@ -386,9 +386,9 @@ mod tests { }; use snforge_std::{test_address, start_mock_call}; use utils::constants::EMPTY_KECCAK; - use utils::helpers::U8SpanExTrait; use utils::helpers::compute_starknet_address; use utils::helpers::load_word; + use utils::traits::bytes::U8SpanExTrait; use utils::traits::{EthAddressIntoU256}; diff --git a/crates/evm/src/interpreter.cairo b/crates/evm/src/interpreter.cairo index 59d11b6bb..3e97b988f 100644 --- a/crates/evm/src/interpreter.cairo +++ b/crates/evm/src/interpreter.cairo @@ -4,11 +4,10 @@ use contracts::kakarot_core::interface::IKakarotCore; use core::num::traits::{Bounded, Zero}; use core::ops::SnapshotDeref; use core::starknet::EthAddress; -use core::starknet::get_tx_info; use core::starknet::storage::{StoragePointerReadAccess}; use evm::backend::starknet_backend; use evm::create_helpers::CreateHelpers; -use evm::errors::{EVMError, EVMErrorTrait, CONTRACT_ACCOUNT_EXISTS}; +use evm::errors::{EVMError, EVMErrorTrait}; use evm::instructions::{ ExchangeOperationsTrait, LoggingOperationsTrait, StopAndArithmeticOperationsTrait, diff --git a/crates/evm/src/memory.cairo b/crates/evm/src/memory.cairo index 836750f32..40458c324 100644 --- a/crates/evm/src/memory.cairo +++ b/crates/evm/src/memory.cairo @@ -5,7 +5,8 @@ use utils::constants::{ POW_2_0, POW_2_8, POW_2_16, POW_2_24, POW_2_32, POW_2_40, POW_2_48, POW_2_56, POW_2_64, POW_2_72, POW_2_80, POW_2_88, POW_2_96, POW_2_104, POW_2_112, POW_2_120, POW_256_16 }; -use utils::{helpers, helpers::ArrayExtTrait, math::Bitshift}; +use utils::traits::array::ArrayExtTrait; +use utils::{helpers, math::Bitshift}; // 2**17 const MEMORY_SEGMENT_SIZE: usize = 131072; @@ -622,7 +623,9 @@ mod tests { use core::num::traits::Bounded; use evm::memory::{MemoryTrait, InternalMemoryTrait}; use utils::constants::{POW_2_8, POW_2_56, POW_2_64, POW_2_120}; - use utils::{math::Exponentiation, math::WrappingExponentiation, helpers, helpers::SpanExtTrait}; + use utils::{ + math::Exponentiation, math::WrappingExponentiation, helpers, traits::array::SpanExtTrait + }; fn load_should_load_an_element_from_the_memory_with_offset_stored_with_store_n( diff --git a/crates/evm/src/model/account.cairo b/crates/evm/src/model/account.cairo index 87a5b2fae..0d407a627 100644 --- a/crates/evm/src/model/account.cairo +++ b/crates/evm/src/model/account.cairo @@ -6,7 +6,7 @@ use core::starknet::{ContractAddress, EthAddress}; use evm::backend::starknet_backend::fetch_balance; use evm::model::Address; use utils::constants::EMPTY_KECCAK; -use utils::helpers::U8SpanExTrait; +use utils::traits::bytes::U8SpanExTrait; #[derive(Drop)] struct AccountBuilder { @@ -278,7 +278,7 @@ mod tests { mod test_has_code_or_nonce { use evm::model::account::{Account, AccountTrait, Address}; use utils::constants::EMPTY_KECCAK; - use utils::helpers::U8SpanExTrait; + use utils::traits::bytes::U8SpanExTrait; #[test] fn test_should_return_false_when_empty() { diff --git a/crates/evm/src/precompiles/blake2f.cairo b/crates/evm/src/precompiles/blake2f.cairo index bc91001a1..1de0b0914 100644 --- a/crates/evm/src/precompiles/blake2f.cairo +++ b/crates/evm/src/precompiles/blake2f.cairo @@ -5,7 +5,7 @@ use core::starknet::EthAddress; use evm::errors::{EVMError, ensure}; use evm::precompiles::Precompile; use utils::crypto::blake2_compress::compress; -use utils::helpers::{FromBytes, ToBytes}; +use utils::traits::bytes::{FromBytes, ToBytes}; const GF_ROUND: u64 = 1; const INPUT_LENGTH: usize = 213; @@ -97,7 +97,7 @@ mod tests { }; use evm::test_utils::{VMBuilderTrait, native_token, setup_test_environment}; use snforge_std::start_mock_call; - use utils::helpers::FromBytes; + use utils::traits::bytes::FromBytes; #[test] fn test_blake2_precompile_fail_empty_input() { diff --git a/crates/evm/src/precompiles/ec_operations/ec_add.cairo b/crates/evm/src/precompiles/ec_operations/ec_add.cairo index ab1ae8ecc..09e55b418 100644 --- a/crates/evm/src/precompiles/ec_operations/ec_add.cairo +++ b/crates/evm/src/precompiles/ec_operations/ec_add.cairo @@ -3,21 +3,19 @@ use core::circuit::CircuitInput as CI; use core::circuit::u96; use core::circuit::{ - u384, CircuitElement, CircuitInput, circuit_add, circuit_sub, circuit_mul, circuit_inverse, - EvalCircuitTrait, CircuitOutputsTrait, CircuitModulus, CircuitInputs + u384, circuit_sub, circuit_mul, circuit_inverse, EvalCircuitTrait, CircuitOutputsTrait, + CircuitModulus, CircuitInputs }; -use core::num::traits::Zero; use core::option::Option; use core::starknet::{EthAddress}; use crate::precompiles::ec_operations::{ - eq_mod_p, eq_neg_mod_p, is_on_curve, double_ec_point_unchecked, BN254_ORDER, BN254_PRIME_LIMBS, - BN254_PRIME + eq_mod_p, eq_neg_mod_p, is_on_curve, double_ec_point_unchecked, BN254_PRIME_LIMBS, BN254_PRIME }; use evm::errors::EVMError; use evm::precompiles::Precompile; use garaga::core::circuit::AddInputResultTrait2; -use utils::helpers::ToBytes; -use utils::helpers::{load_word, U8SpanExTrait}; +use utils::helpers::{load_word}; +use utils::traits::bytes::{ToBytes, U8SpanExTrait}; const BASE_COST: u64 = 150; @@ -86,41 +84,39 @@ fn ec_add(x1: u256, y1: u256, x2: u256, y2: u256) -> Option<(u256, u256)> { return Option::None; } } + } else if x2 == 0 && y2 == 0 { + // Only second point is at infinity. + let x1_u384: u384 = x1.into(); + let y1_u384: u384 = y1.into(); + if is_on_curve(x1_u384, y1_u384) { + // First point is on the curve, return it. + return Option::Some((x1, y1)); + } else { + // First point is not on the curve, return None (error). + return Option::None; + } } else { - if x2 == 0 && y2 == 0 { - // Only second point is at infinity. - let x1_u384: u384 = x1.into(); - let y1_u384: u384 = y1.into(); - if is_on_curve(x1_u384, y1_u384) { - // First point is on the curve, return it. - return Option::Some((x1, y1)); - } else { - // First point is not on the curve, return None (error). - return Option::None; + // None of the points are at infinity. + let x1_u384: u384 = x1.into(); + let x2_u384: u384 = x2.into(); + let y1_u384: u384 = y1.into(); + let y2_u384: u384 = y2.into(); + + if is_on_curve(x1_u384, y1_u384) && is_on_curve(x2_u384, y2_u384) { + match ec_safe_add(x1_u384, y1_u384, x2_u384, y2_u384) { + Option::Some(( + x, y + )) => Option::Some( + ( + TryInto::::try_into(x).unwrap(), + TryInto::::try_into(y).unwrap() + ) + ), + Option::None => Option::Some((0, 0)), } } else { - // None of the points are at infinity. - let x1_u384: u384 = x1.into(); - let x2_u384: u384 = x2.into(); - let y1_u384: u384 = y1.into(); - let y2_u384: u384 = y2.into(); - - if is_on_curve(x1_u384, y1_u384) && is_on_curve(x2_u384, y2_u384) { - match ec_safe_add(x1_u384, y1_u384, x2_u384, y2_u384) { - Option::Some(( - x, y - )) => Option::Some( - ( - TryInto::::try_into(x).unwrap(), - TryInto::::try_into(y).unwrap() - ) - ), - Option::None => Option::Some((0, 0)), - } - } else { - // None of the points are infinity and at least one of them is not on the curve. - return Option::None; - } + // None of the points are infinity and at least one of them is not on the curve. + return Option::None; } } } diff --git a/crates/evm/src/precompiles/ec_operations/ec_mul.cairo b/crates/evm/src/precompiles/ec_operations/ec_mul.cairo index ee611a9c6..89f3a3075 100644 --- a/crates/evm/src/precompiles/ec_operations/ec_mul.cairo +++ b/crates/evm/src/precompiles/ec_operations/ec_mul.cairo @@ -2,14 +2,12 @@ use core::circuit::u384; use core::option::Option; use core::starknet::{EthAddress}; use crate::precompiles::ec_operations::ec_add::ec_safe_add; +use crate::precompiles::ec_operations::{is_on_curve, double_ec_point_unchecked, BN254_PRIME}; -use crate::precompiles::ec_operations::{ - eq_mod_p, eq_neg_mod_p, is_on_curve, double_ec_point_unchecked, BN254_ORDER, BN254_PRIME_LIMBS, - BN254_PRIME -}; use evm::errors::EVMError; use evm::precompiles::Precompile; -use utils::helpers::{load_word, ToBytes, U8SpanExTrait}; +use utils::helpers::{load_word}; +use utils::traits::bytes::{ToBytes, U8SpanExTrait}; const BASE_COST: u64 = 6000; const U256_BYTES_LEN: usize = 32; diff --git a/crates/evm/src/precompiles/ec_recover.cairo b/crates/evm/src/precompiles/ec_recover.cairo index b18264568..646fafbfc 100644 --- a/crates/evm/src/precompiles/ec_recover.cairo +++ b/crates/evm/src/precompiles/ec_recover.cairo @@ -1,7 +1,6 @@ use core::starknet::secp256_trait::{ Signature, recover_public_key, Secp256PointTrait, is_valid_signature }; -use core::starknet::secp256_trait::{Secp256Trait}; use core::starknet::{ EthAddress, eth_signature::{public_key_point_to_eth_address}, secp256k1::{Secp256k1Point}, SyscallResultTrait @@ -9,9 +8,8 @@ use core::starknet::{ use core::traits::Into; use evm::errors::EVMError; use evm::precompiles::Precompile; -use utils::helpers::U8SpanExTrait; -use utils::helpers::{ToBytes, FromBytes}; use utils::traits::EthAddressIntoU256; +use utils::traits::bytes::{U8SpanExTrait, FromBytes, ToBytes}; use utils::traits::{NumericIntoBool, BoolIntoNumeric}; const EC_RECOVER_PRECOMPILE_GAS_COST: u64 = 3000; @@ -90,7 +88,7 @@ mod tests { use evm::test_utils::setup_test_environment; use evm::test_utils::{VMBuilderTrait, MemoryTestUtilsTrait, native_token}; use snforge_std::start_mock_call; - use utils::helpers::{ToBytes, FromBytes}; + use utils::traits::bytes::{ToBytes, FromBytes}; // source: diff --git a/crates/evm/src/precompiles/modexp.cairo b/crates/evm/src/precompiles/modexp.cairo index ae088c220..5afb6def2 100644 --- a/crates/evm/src/precompiles/modexp.cairo +++ b/crates/evm/src/precompiles/modexp.cairo @@ -13,7 +13,8 @@ use evm::errors::EVMError; use evm::precompiles::Precompile; use utils::crypto::modexp::lib::modexp; -use utils::helpers::{U8SpanExTrait, FromBytes, BitsUsed}; +use utils::traits::bytes::{U8SpanExTrait, FromBytes}; +use utils::traits::integer::BitsUsed; const HEADER_LENGTH: usize = 96; const MIN_GAS: u64 = 200; diff --git a/crates/evm/src/precompiles/p256verify.cairo b/crates/evm/src/precompiles/p256verify.cairo index 303597bb2..4fed39cc2 100644 --- a/crates/evm/src/precompiles/p256verify.cairo +++ b/crates/evm/src/precompiles/p256verify.cairo @@ -3,7 +3,7 @@ use core::starknet::secp256_trait::{Secp256Trait}; use core::starknet::{EthAddress, secp256r1::{Secp256r1Point}, secp256_trait::is_valid_signature}; use evm::errors::{EVMError}; use evm::precompiles::Precompile; -use utils::helpers::FromBytes; +use utils::traits::bytes::FromBytes; const P256VERIFY_PRECOMPILE_GAS_COST: u64 = 3450; @@ -113,7 +113,7 @@ mod tests { use evm::test_utils::{VMBuilderTrait}; use evm::test_utils::{setup_test_environment, native_token}; use snforge_std::start_mock_call; - use utils::helpers::{ToBytes, FromBytes}; + use utils::traits::bytes::{ToBytes, FromBytes}; // source: diff --git a/crates/evm/src/precompiles/sha256.cairo b/crates/evm/src/precompiles/sha256.cairo index c8a453786..f64f4cb50 100644 --- a/crates/evm/src/precompiles/sha256.cairo +++ b/crates/evm/src/precompiles/sha256.cairo @@ -2,8 +2,8 @@ use core::sha256::compute_sha256_u32_array; use core::starknet::EthAddress; use evm::errors::EVMError; use evm::precompiles::Precompile; -use utils::helpers::{FromBytes, ToBytes}; use utils::math::Bitshift; +use utils::traits::bytes::{FromBytes, ToBytes}; const BASE_COST: u64 = 60; const COST_PER_WORD: u64 = 12; @@ -60,8 +60,7 @@ mod tests { VMBuilderTrait, MemoryTestUtilsTrait, native_token, setup_test_environment }; use snforge_std::{start_mock_call}; - use utils::helpers::ToBytes; - use utils::helpers::{FromBytes}; + use utils::traits::bytes::{ToBytes, FromBytes}; //source: // diff --git a/crates/evm/src/state.cairo b/crates/evm/src/state.cairo index ed455d10a..26e3e9f34 100644 --- a/crates/evm/src/state.cairo +++ b/crates/evm/src/state.cairo @@ -313,9 +313,9 @@ mod tests { use evm::test_utils; use snforge_std::{test_address, start_mock_call}; use utils::constants::EMPTY_KECCAK; - use utils::helpers::U8SpanExTrait; use utils::helpers::compute_starknet_address; use utils::set::SetTrait; + use utils::traits::bytes::U8SpanExTrait; #[test] fn test_get_account_when_inexistant() { diff --git a/crates/utils/src/address.cairo b/crates/utils/src/address.cairo index 128414436..d5c1c7308 100644 --- a/crates/utils/src/address.cairo +++ b/crates/utils/src/address.cairo @@ -1,9 +1,10 @@ use core::array::ArrayTrait; use core::starknet::EthAddress; use core::traits::TryInto; +use crate::traits::bytes::{ToBytes, U8SpanExTrait}; use evm::errors::EVMError; -use utils::helpers::{U8SpanExTrait, EthAddressExTrait, ToBytes}; +use utils::helpers::{EthAddressExTrait}; use utils::rlp::{RLPTrait, RLPItem}; use utils::traits::{TryIntoResult}; diff --git a/crates/utils/src/crypto/modexp/arith.cairo b/crates/utils/src/crypto/modexp/arith.cairo index 513c5286e..ef6a7ea82 100644 --- a/crates/utils/src/crypto/modexp/arith.cairo +++ b/crates/utils/src/crypto/modexp/arith.cairo @@ -4,12 +4,13 @@ use alexandria_data_structures::vec::{Felt252Vec, Felt252VecImpl}; use core::num::traits::{WideMul, OverflowingAdd, OverflowingSub, WrappingMul}; use core::option::OptionTrait; +use crate::felt_vec::{Felt252VecTrait}; +use crate::helpers::{u128_split}; +use crate::math::WrappingBitshift; +use crate::math::{Bitshift}; +use crate::traits::BoolIntoNumeric; +use crate::traits::integer::U128Trait; use super::mpnat::{MPNat, Word, DoubleWord, WORD_BITS, BASE, DOUBLE_WORD_MAX, WORD_MAX}; -use utils::helpers::{u128_split, Felt252VecTrait, U128Trait}; -use utils::math::WrappingBitshift; -use utils::math::{Bitshift}; - -use utils::traits::BoolIntoNumeric; // Computes the "Montgomery Product" of two numbers. // See Coarsely Integrated Operand Scanning (CIOS) Method in @@ -593,6 +594,7 @@ mod tests { use core::num::traits::{WrappingSub, WrappingMul}; use core::result::ResultTrait; use core::traits::Into; + use crate::felt_vec::{Felt252VecTrait}; use utils::crypto::modexp::arith::{ mod_inv, monsq, monpro, compute_r_mod_n, in_place_shl, in_place_shr, big_wrapping_pow, @@ -602,8 +604,8 @@ mod tests { MPNat, MPNatTrait, WORD_MAX, DOUBLE_WORD_MAX, BASE, Word, WORD_BYTES }; use utils::crypto::modexp::mpnat::{mp_nat_to_u128}; - use utils::helpers::{Felt252VecTrait, ToBytes}; use utils::math::{WrappingBitshift, WrappingExponentiation}; + use utils::traits::bytes::ToBytes; // the tests are taken from // [aurora-engine](https://github.com/aurora-is-near/aurora-engine/blob/1213f2c7c035aa523601fced8f75bef61b4728ab/engine-modexp/src/arith.rs#L401) diff --git a/crates/utils/src/crypto/modexp/lib.cairo b/crates/utils/src/crypto/modexp/lib.cairo index 6abc1b985..5325a198e 100644 --- a/crates/utils/src/crypto/modexp/lib.cairo +++ b/crates/utils/src/crypto/modexp/lib.cairo @@ -1,8 +1,8 @@ // CREDITS: The implementation has been take from // [aurora-engine](https://github.com/aurora-is-near/aurora-engine/tree/develop/engine-modexp) +use crate::felt_vec::{Felt252VecTrait}; use utils::crypto::modexp::mpnat::MPNatTrait; -use utils::helpers::Felt252VecTrait; /// Computes `(base ^ exp) % modulus`, where all values are given as big-endian /// encoded bytes. diff --git a/crates/utils/src/crypto/modexp/mpnat.cairo b/crates/utils/src/crypto/modexp/mpnat.cairo index fbca2a900..3d13875dd 100644 --- a/crates/utils/src/crypto/modexp/mpnat.cairo +++ b/crates/utils/src/crypto/modexp/mpnat.cairo @@ -6,13 +6,15 @@ use core::array::SpanTrait; use core::num::traits::CheckedMul; use core::option::OptionTrait; use core::result::ResultTrait; +use crate::felt_vec::{Felt252VecTrait}; use super::arith::{ big_wrapping_pow, mod_inv, compute_r_mod_n, join_as_double, in_place_shl, in_place_shr, in_place_add, in_place_mul_sub, big_wrapping_mul, monsq, monpro, borrowing_sub, carrying_add }; -use utils::helpers::{FromBytes, U64Trait, Felt252VecTrait, U128Trait, BitsUsed, ByteSize}; use utils::math::Bitshift; +use utils::traits::bytes::FromBytes; +use utils::traits::integer::{U64Trait, U128Trait, BitsUsed, ByteSize}; pub type Word = u64; pub type DoubleWord = u128; @@ -698,8 +700,8 @@ mod tests { use alexandria_data_structures::vec::VecTrait; use super::mp_nat_to_u128; use utils::crypto::modexp::mpnat::MPNatTrait; - use utils::helpers::ToBytes; use utils::math::{Bitshift, WrappingBitshift}; + use utils::traits::bytes::ToBytes; // the tests are taken from // [aurora-engine](https://github.com/aurora-is-near/aurora-engine/blob/1213f2c7c035aa523601fced8f75bef61b4728ab/engine-modexp/src/mpnat.rs#L825) diff --git a/crates/utils/src/eth_transaction.cairo b/crates/utils/src/eth_transaction.cairo index 0331dc830..ffd623c84 100644 --- a/crates/utils/src/eth_transaction.cairo +++ b/crates/utils/src/eth_transaction.cairo @@ -8,9 +8,8 @@ use core::cmp::min; use core::num::traits::CheckedAdd; use core::option::OptionTrait; use core::starknet::{EthAddress, secp256_trait::Signature,}; -use utils::errors::{EthTransactionError, RLPErrorImpl}; - -use utils::helpers::ByteArrayExt; +use crate::errors::{EthTransactionError, RLPErrorImpl}; +use crate::traits::bytes::ByteArrayExt; #[derive(Drop)] pub struct TransactionMetadata { diff --git a/crates/utils/src/eth_transaction/transaction.cairo b/crates/utils/src/eth_transaction/transaction.cairo index b7aa07ee8..f301c8b4e 100644 --- a/crates/utils/src/eth_transaction/transaction.cairo +++ b/crates/utils/src/eth_transaction/transaction.cairo @@ -5,8 +5,8 @@ use crate::eth_transaction::eip1559::{TxEip1559, TxEip1559Trait}; use crate::eth_transaction::eip2930::{AccessListItem, TxEip2930, TxEip2930Trait}; use crate::eth_transaction::legacy::TxLegacy; use crate::eth_transaction::tx_type::{TxType}; -use crate::helpers::U8SpanExTrait; use crate::rlp::{RLPItem, RLPTrait, RLPHelpersTrait}; +use crate::traits::bytes::U8SpanExTrait; use crate::traits::{DefaultSignature}; diff --git a/crates/utils/src/felt_vec.cairo b/crates/utils/src/felt_vec.cairo new file mode 100644 index 000000000..82706e75d --- /dev/null +++ b/crates/utils/src/felt_vec.cairo @@ -0,0 +1,800 @@ +use alexandria_data_structures::vec::VecTrait; +use alexandria_data_structures::vec::{Felt252Vec, Felt252VecImpl}; +use core::num::traits::Zero; +use crate::math::Exponentiation; +use crate::traits::bytes::{ToBytes}; + + +#[derive(Drop, Debug, PartialEq)] +pub enum Felt252VecTraitErrors { + IndexOutOfBound, + Overflow, + LengthIsNotSame, + SizeLessThanCurrentLength +} + +#[generate_trait] +pub impl Felt252VecTraitImpl< + T, + +Drop, + +Copy, + +Felt252DictValue, + +Zero, + +Add, + +Sub, + +Div, + +Mul, + +Exponentiation, + +ToBytes, + +PartialOrd, + +Into, + +PartialEq, +> of Felt252VecTrait { + /// Returns Felt252Vec as a Span<8>, the returned Span is in big endian format + /// # Arguments + /// * `self` a ref Felt252Vec + /// # Returns + /// * A Span representing bytes conversion of `self` in big endian format + fn to_be_bytes(ref self: Felt252Vec) -> Span { + let mut res: Array = array![]; + self.remove_trailing_zeroes(); + + let mut i = self.len(); + + while i != 0 { + i -= 1; + res.append_span(self[i].to_be_bytes_padded()); + }; + + res.span() + } + + /// Returns Felt252Vec as a Span<8>, the returned Span is in little endian format + /// # Arguments + /// * `self` a ref Felt252Vec + /// # Returns + /// * A Span representing bytes conversion of `self` in little endian format + fn to_le_bytes(ref self: Felt252Vec) -> Span { + let mut res: Array = array![]; + let mut i = 0; + + while i != self.len() { + if self[i] == Zero::zero() { + res.append(Zero::zero()); + } else { + res.append_span(self[i].to_le_bytes()); + } + + i += 1; + }; + + res.span() + } + + /// Expands a Felt252Vec to a new length by appending zeroes + /// + /// This function will mutate the Felt252Vec in-place and will expand its length, + /// since the default value for Felt252Dict item is 0, all new elements will be set to 0. + /// If the new length is less than the current length, it will return an error. + /// + /// # Arguments + /// * `self` a ref Felt252Vec + /// * `new_length` the new length of the Felt252Vec + /// + /// # Returns + /// * Result::<(), Felt252VecTraitErrors> + /// + /// # Errors + /// * Felt252VecTraitErrors::SizeLessThanCurrentLength if the new length is less than the + /// current length + fn expand(ref self: Felt252Vec, new_length: usize) -> Result<(), Felt252VecTraitErrors> { + if (new_length < self.len) { + return Result::Err(Felt252VecTraitErrors::SizeLessThanCurrentLength); + }; + + self.len = new_length; + + Result::Ok(()) + } + + /// Sets all elements of the Felt252Vec to zero, mutates the Felt252Vec in-place + /// + /// # Arguments + /// self a ref Felt252Vec + fn reset(ref self: Felt252Vec) { + let mut new_vec: Felt252Vec = Default::default(); + new_vec.len = self.len; + self = new_vec; + } + + /// Returns the leading zeroes of a Felt252Vec + /// + /// # Arguments + /// * `self` a ref Felt252Vec + /// + /// # Returns + /// * The number of leading zeroes in `self`. + fn count_leading_zeroes(ref self: Felt252Vec) -> usize { + let mut i = 0; + while i != self.len() && self[i] == Zero::zero() { + i += 1; + }; + + i + } + + /// Resizes the Felt252Vec in-place so that len is equal to new_len. + /// + /// This function will mutate the Felt252Vec in-place and will resize its length to the new + /// length. + /// If new_len is greater than len, the Vec is extended by the difference, with each additional + /// slot filled with 0. If new_len is less than len, the Vec is simply truncated from the right. + /// + /// # Arguments + /// * `self` a ref Felt252Vec + /// * `new_len` the new length of the Felt252Vec + fn resize(ref self: Felt252Vec, new_len: usize) { + self.len = new_len; + } + + + /// Copies the elements from a Span into the Felt252Vec in little endian format, in case + /// of overflow or index being out of bounds, an error is returned + /// + /// # Arguments + /// * `self` a ref Felt252Vec + /// * `index` the index at `self` to start copying from + /// * `slice` a Span + /// + /// # Errors + /// * Felt252VecTraitErrors::IndexOutOfBound if the index is out of bounds + /// * Felt252VecTraitErrors::Overflow if the Span is too big to fit in the Felt252Vec + fn copy_from_bytes_le( + ref self: Felt252Vec, index: usize, mut slice: Span + ) -> Result<(), Felt252VecTraitErrors> { + if (index >= self.len) { + return Result::Err(Felt252VecTraitErrors::IndexOutOfBound); + } + + if ((slice.len() + index) > self.len()) { + return Result::Err(Felt252VecTraitErrors::Overflow); + } + + let mut i = index; + for val in slice { + // safe unwrap, as in case of none, we will never reach this branch + self.set(i, (*val).into()); + i += 1; + }; + + Result::Ok(()) + } + + /// Copies the elements from a Felt252Vec into the Felt252Vec in little endian format, If + /// length of both Felt252Vecs are not same, it will return an error + /// + /// # Arguments + /// * `self` a ref Felt252Vec + /// * `vec` a ref Felt252Vec + /// + /// # Errors + /// * Felt252VecTraitErrors::LengthIsNotSame if the length of both Felt252Vecs are not same + fn copy_from_vec_le( + ref self: Felt252Vec, ref vec: Felt252Vec + ) -> Result<(), Felt252VecTraitErrors> { + if (vec.len() != self.len) { + return Result::Err(Felt252VecTraitErrors::LengthIsNotSame); + } + + self = vec.duplicate(); + + Result::Ok(()) + } + + /// Insert elements of Felt252Vec into another Felt252Vec at a given index, in case of overflow + /// or index being out of bounds, an error is returned + /// + /// # Arguments + /// * `self` a ref Felt252Vec + /// * `idx` the index at `self` to start inserting from + /// * `vec` a ref Felt252Vec + /// + /// # Errors + /// * Felt252VecTraitErrors::IndexOutOfBound if the index is out of bounds + /// * Felt252VecTraitErrors::Overflow if the Felt252Vec is too big to fit in the Felt252Vec + fn insert_vec( + ref self: Felt252Vec, idx: usize, ref vec: Felt252Vec + ) -> Result<(), Felt252VecTraitErrors> { + if idx >= self.len() { + return Result::Err(Felt252VecTraitErrors::IndexOutOfBound); + } + + if (idx + vec.len > self.len) { + return Result::Err(Felt252VecTraitErrors::Overflow); + } + + let stop = idx + vec.len(); + let mut i = idx; + while i != stop { + self.set(i, vec[i - idx]); + i += 1; + }; + + Result::Ok(()) + } + + + /// Removes trailing zeroes from a Felt252Vec + /// + /// # Arguments + /// * `input` a ref Felt252Vec + fn remove_trailing_zeroes(ref self: Felt252Vec) { + let mut new_len = self.len; + while (new_len != 0) && (self[new_len - 1] == Zero::zero()) { + new_len -= 1; + }; + + self.len = new_len; + } + + /// Pops an element out of the vector, returns Option::None if the vector is empty + /// + /// # Arguments + /// * `self` a ref Felt252Vec + /// # Returns + /// + /// * Option::Some(T), returns the last element or Option::None if the vector is empty + fn pop(ref self: Felt252Vec) -> Option { + if (self.len) == 0 { + return Option::None; + } + + let popped_ele = self[self.len() - 1]; + self.len = self.len - 1; + Option::Some(popped_ele) + } + + /// takes a Felt252Vec and returns a new Felt252Vec with the same elements + /// + /// Note: this is an expensive operation, as it will create a new Felt252Vec + /// + /// # Arguments + /// * `self` a ref Felt252Vec + /// + /// # Returns + /// * A new Felt252Vec with the same elements + fn duplicate(ref self: Felt252Vec) -> Felt252Vec { + let mut new_vec = Default::default(); + + let mut i: u32 = 0; + + while i != self.len { + new_vec.push(self[i]); + i += 1; + }; + + new_vec + } + + /// Returns a new Felt252Vec with elements starting from `idx` to `idx + len` + /// + /// This function will start cloning from `idx` and will clone `len` elements, it will firstly + /// clone the elements and then return a new Felt252Vec + /// In case of overflow return Option::None + /// + /// # Arguments + /// * `self` a ref Felt252Vec + /// * `idx` the index to start cloning from + /// * `len` the length of the clone + /// + /// # Returns + /// * Felt252Vec + /// + /// # Panics + /// * If the index is out of bounds + /// + /// Note: this is an expensive operation, as it will create a new Felt252Vec + fn clone_slice(ref self: Felt252Vec, idx: usize, len: usize) -> Felt252Vec { + let mut new_vec = Default::default(); + + let mut i: u32 = 0; + + while i != len { + new_vec.push(self[idx + i]); + + i += 1; + }; + + new_vec + } + + /// Returns whether two Felt252Vec are equal after removing trailing_zeroes + /// + /// # Arguments + /// * `self` a ref Felt252Vec + /// * `rhs` a ref Felt252Vec + /// + /// # Returns + /// * bool, returns true if both Felt252Vecs are equal, false otherwise + /// TODO: if this utils is only used for testing, then refactor as a test util + fn equal_remove_trailing_zeroes(ref self: Felt252Vec, ref rhs: Felt252Vec) -> bool { + let mut lhs = self.duplicate(); + lhs.remove_trailing_zeroes(); + + let mut rhs = rhs.duplicate(); + rhs.remove_trailing_zeroes(); + + if lhs.len() != rhs.len() { + return false; + }; + + let mut i = 0; + let mut result = true; + while i != lhs.len() { + if lhs[i] != rhs[i] { + result = false; + break; + } + i += 1; + }; + result + } + + /// Fills a Felt252Vec with a given `value` starting from `start_idx` to `start_idx + len` + /// In case of index out of bounds or overflow, error is returned + /// + /// # Arguments + /// * `self` a ref Felt252Vec + /// * `start_idx` the index to start filling from + /// * `len` the length of the fill + /// * `value` the value to fill the Felt252Vec with + /// + /// # Returns + /// * Result::<(), Felt252VecTraitErrors> + /// + /// # Errors + /// * Felt252VecTraitErrors::IndexOutOfBound if the index is out of bounds + /// * Felt252VecTraitErrors::Overflow if the Felt252Vec is too big to fit in the Felt252Vec + fn fill( + ref self: Felt252Vec, start_idx: usize, len: usize, value: T + ) -> Result<(), Felt252VecTraitErrors> { + // Index out of bounds + if (start_idx >= self.len()) { + return Result::Err(Felt252VecTraitErrors::IndexOutOfBound); + } + + // Overflow + if (start_idx + len > self.len()) { + return Result::Err(Felt252VecTraitErrors::Overflow); + } + + let mut i = start_idx; + while i != start_idx + len { + self.set(i, value); + + i += 1; + }; + + Result::Ok(()) + } +} + +#[cfg(test)] +mod tests { + mod felt252_vec_u8_test { + use alexandria_data_structures::vec::{VecTrait, Felt252Vec}; + use crate::felt_vec::{Felt252VecTrait}; + + #[test] + fn test_felt252_vec_u8_to_bytes() { + let mut vec: Felt252Vec = Default::default(); + vec.push(0); + vec.push(1); + vec.push(2); + vec.push(3); + + let result = vec.to_le_bytes(); + let expected = [0, 1, 2, 3].span(); + + assert_eq!(result, expected); + } + } + + mod felt252_vec_u64_test { + use alexandria_data_structures::vec::{VecTrait, Felt252Vec}; + use crate::felt_vec::{Felt252VecTrait}; + + #[test] + fn test_felt252_vec_u64_words64_to_le_bytes() { + let mut vec: Felt252Vec = Default::default(); + vec.push(0); + vec.push(1); + vec.push(2); + vec.push(3); + + let result = vec.to_le_bytes(); + let expected = [0, 1, 2, 3].span(); + + assert_eq!(result, expected); + } + + #[test] + fn test_felt252_vec_u64_words64_to_be_bytes() { + let mut vec: Felt252Vec = Default::default(); + vec.push(0); + vec.push(1); + vec.push(2); + vec.push(3); + + let result = vec.to_be_bytes(); + let expected = [ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 3, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 2, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0 + ].span(); + + assert_eq!(result, expected); + } + } + + mod felt252_vec_test { + use alexandria_data_structures::vec::{VecTrait, Felt252Vec}; + use crate::felt_vec::{Felt252VecTrait, Felt252VecTraitErrors}; + + #[test] + fn test_felt252_vec_expand() { + let mut vec: Felt252Vec = Default::default(); + vec.push(0); + vec.push(1); + + vec.expand(4).unwrap(); + + assert_eq!(vec.len(), 4); + assert_eq!(vec.pop().unwrap(), 0); + assert_eq!(vec.pop().unwrap(), 0); + assert_eq!(vec.pop().unwrap(), 1); + assert_eq!(vec.pop().unwrap(), 0); + } + + #[test] + fn test_felt252_vec_expand_fail() { + let mut vec: Felt252Vec = Default::default(); + vec.push(0); + vec.push(1); + + let result = vec.expand(1); + assert_eq!(result, Result::Err(Felt252VecTraitErrors::SizeLessThanCurrentLength)); + } + + #[test] + fn test_felt252_vec_reset() { + let mut vec: Felt252Vec = Default::default(); + vec.push(0); + vec.push(1); + + vec.reset(); + + assert_eq!(vec.len(), 2); + assert_eq!(vec.pop().unwrap(), 0); + assert_eq!(vec.pop().unwrap(), 0); + } + + #[test] + fn test_felt252_vec_count_leading_zeroes() { + let mut vec: Felt252Vec = Default::default(); + vec.push(0); + vec.push(0); + vec.push(0); + vec.push(1); + + let result = vec.count_leading_zeroes(); + + assert_eq!(result, 3); + } + + + #[test] + fn test_felt252_vec_resize_len_greater_than_current_len() { + let mut vec: Felt252Vec = Default::default(); + vec.push(0); + vec.push(1); + + vec.expand(4).unwrap(); + + assert_eq!(vec.len(), 4); + assert_eq!(vec.pop().unwrap(), 0); + assert_eq!(vec.pop().unwrap(), 0); + assert_eq!(vec.pop().unwrap(), 1); + assert_eq!(vec.pop().unwrap(), 0); + } + + #[test] + fn test_felt252_vec_resize_len_less_than_current_len() { + let mut vec: Felt252Vec = Default::default(); + vec.push(0); + vec.push(1); + vec.push(0); + vec.push(0); + + vec.resize(2); + + assert_eq!(vec.len(), 2); + assert_eq!(vec.pop().unwrap(), 1); + assert_eq!(vec.pop().unwrap(), 0); + } + + #[test] + fn test_felt252_vec_len_0() { + let mut vec: Felt252Vec = Default::default(); + vec.push(0); + vec.push(1); + + vec.resize(0); + + assert_eq!(vec.len(), 0); + } + + #[test] + fn test_copy_from_bytes_le_size_equal_to_vec_size() { + let mut vec: Felt252Vec = Default::default(); + vec.expand(4).unwrap(); + + let bytes = [1, 2, 3, 4].span(); + vec.copy_from_bytes_le(0, bytes).unwrap(); + + assert_eq!(vec.len(), 4); + assert_eq!(vec.pop().unwrap(), 4); + assert_eq!(vec.pop().unwrap(), 3); + assert_eq!(vec.pop().unwrap(), 2); + assert_eq!(vec.pop().unwrap(), 1); + } + + #[test] + fn test_copy_from_bytes_le_size_less_than_vec_size() { + let mut vec: Felt252Vec = Default::default(); + vec.expand(4).unwrap(); + + let bytes = [1, 2].span(); + vec.copy_from_bytes_le(2, bytes).unwrap(); + + assert_eq!(vec.len(), 4); + assert_eq!(vec.pop().unwrap(), 2); + assert_eq!(vec.pop().unwrap(), 1); + assert_eq!(vec.pop().unwrap(), 0); + assert_eq!(vec.pop().unwrap(), 0); + } + + #[test] + fn test_copy_from_bytes_le_size_greater_than_vec_size() { + let mut vec: Felt252Vec = Default::default(); + vec.expand(4).unwrap(); + + let bytes = [1, 2, 3, 4].span(); + let result = vec.copy_from_bytes_le(2, bytes); + + assert_eq!(result, Result::Err(Felt252VecTraitErrors::Overflow)); + } + + #[test] + fn test_copy_from_bytes_index_out_of_bound() { + let mut vec: Felt252Vec = Default::default(); + vec.expand(4).unwrap(); + + let bytes = [1, 2].span(); + let result = vec.copy_from_bytes_le(4, bytes); + + assert_eq!(result, Result::Err(Felt252VecTraitErrors::IndexOutOfBound)); + } + + #[test] + fn test_copy_from_vec_le() { + let mut vec: Felt252Vec = Default::default(); + vec.expand(2).unwrap(); + + let mut vec2: Felt252Vec = Default::default(); + vec2.push(1); + vec2.push(2); + + vec.copy_from_vec_le(ref vec2).unwrap(); + + assert_eq!(vec.len, 2); + assert_eq!(vec.pop().unwrap(), 2); + assert_eq!(vec.pop().unwrap(), 1); + } + + #[test] + fn test_copy_from_vec_le_not_equal_lengths() { + let mut vec: Felt252Vec = Default::default(); + vec.expand(2).unwrap(); + + let mut vec2: Felt252Vec = Default::default(); + vec2.push(1); + + let result = vec.copy_from_vec_le(ref vec2); + + assert_eq!(result, Result::Err(Felt252VecTraitErrors::LengthIsNotSame)); + } + + + #[test] + fn test_insert_vec_size_equal_to_vec_size() { + let mut vec: Felt252Vec = Default::default(); + vec.expand(2).unwrap(); + + let mut vec2: Felt252Vec = Default::default(); + vec2.push(1); + vec2.push(2); + + vec.insert_vec(0, ref vec2).unwrap(); + + assert_eq!(vec.len(), 2); + assert_eq!(vec.pop().unwrap(), 2); + assert_eq!(vec.pop().unwrap(), 1); + } + + #[test] + fn test_insert_vec_size_less_than_vec_size() { + let mut vec: Felt252Vec = Default::default(); + vec.expand(4).unwrap(); + + let mut vec2: Felt252Vec = Default::default(); + vec2.push(1); + vec2.push(2); + + vec.insert_vec(2, ref vec2).unwrap(); + + assert_eq!(vec.len(), 4); + assert_eq!(vec.pop().unwrap(), 2); + assert_eq!(vec.pop().unwrap(), 1); + assert_eq!(vec.pop().unwrap(), 0); + assert_eq!(vec.pop().unwrap(), 0); + } + + #[test] + fn test_insert_vec_size_greater_than_vec_size() { + let mut vec: Felt252Vec = Default::default(); + vec.expand(2).unwrap(); + + let mut vec2: Felt252Vec = Default::default(); + vec2.push(1); + vec2.push(2); + vec2.push(3); + vec2.push(4); + + let result = vec.insert_vec(1, ref vec2); + assert_eq!(result, Result::Err(Felt252VecTraitErrors::Overflow)); + } + + #[test] + fn test_insert_vec_index_out_of_bound() { + let mut vec: Felt252Vec = Default::default(); + vec.expand(4).unwrap(); + + let mut vec2: Felt252Vec = Default::default(); + vec2.push(1); + vec2.push(2); + + let result = vec.insert_vec(4, ref vec2); + assert_eq!(result, Result::Err(Felt252VecTraitErrors::IndexOutOfBound)); + } + + #[test] + fn test_remove_trailing_zeroes_le() { + let mut vec: Felt252Vec = Default::default(); + vec.push(1); + vec.push(2); + vec.push(0); + vec.push(0); + + vec.remove_trailing_zeroes(); + + assert_eq!(vec.len(), 2); + assert_eq!(vec.pop().unwrap(), 2); + assert_eq!(vec.pop().unwrap(), 1); + } + + #[test] + fn test_pop() { + let mut vec: Felt252Vec = Default::default(); + vec.push(1); + vec.push(2); + + assert_eq!(vec.pop().unwrap(), 2); + assert_eq!(vec.pop().unwrap(), 1); + assert_eq!(vec.pop(), Option::::None); + } + + #[test] + fn test_duplicate() { + let mut vec: Felt252Vec = Default::default(); + vec.push(1); + vec.push(2); + + let mut vec2 = vec.duplicate(); + + assert_eq!(vec.len(), vec2.len()); + assert_eq!(vec.pop(), vec2.pop()); + assert_eq!(vec.pop(), vec2.pop()); + assert_eq!(vec.pop().is_none(), true); + assert_eq!(vec2.pop().is_none(), true); + } + + #[test] + fn test_clone_slice() { + let mut vec: Felt252Vec = Default::default(); + vec.push(1); + vec.push(2); + + let mut vec2 = vec.clone_slice(1, 1); + + assert_eq!(vec2.len(), 1); + assert_eq!(vec2.pop().unwrap(), 2); + } + + #[test] + fn test_equal() { + let mut vec: Felt252Vec = Default::default(); + vec.push(1); + vec.push(2); + + let mut vec2: Felt252Vec = Default::default(); + vec2.push(1); + vec2.push(2); + + assert!(vec.equal_remove_trailing_zeroes(ref vec2)); + vec2.pop().unwrap(); + assert!(!vec.equal_remove_trailing_zeroes(ref vec2)); + } + + #[test] + fn test_fill() { + let mut vec: Felt252Vec = Default::default(); + vec.expand(4).unwrap(); + + vec.fill(1, 3, 1).unwrap(); + + assert_eq!(vec.pop().unwrap(), 1); + assert_eq!(vec.pop().unwrap(), 1); + assert_eq!(vec.pop().unwrap(), 1); + assert_eq!(vec.pop().unwrap(), 0); + } + + #[test] + fn test_fill_overflow() { + let mut vec: Felt252Vec = Default::default(); + vec.expand(4).unwrap(); + + assert_eq!(vec.fill(4, 0, 1), Result::Err(Felt252VecTraitErrors::IndexOutOfBound)); + assert_eq!(vec.fill(2, 4, 1), Result::Err(Felt252VecTraitErrors::Overflow)); + } + } +} diff --git a/crates/utils/src/helpers.cairo b/crates/utils/src/helpers.cairo index 8db8e0253..c95c1a78f 100644 --- a/crates/utils/src/helpers.cairo +++ b/crates/utils/src/helpers.cairo @@ -1,26 +1,28 @@ -use alexandria_data_structures::vec::VecTrait; -use alexandria_data_structures::vec::{Felt252Vec, Felt252VecImpl}; use core::array::ArrayTrait; use core::array::SpanTrait; use core::cmp::min; use core::hash::{HashStateExTrait, HashStateTrait}; -use core::integer; -use core::keccak::{cairo_keccak}; -use core::num::traits::Bounded; -use core::num::traits::{SaturatingAdd}; -use core::num::traits::{Zero, One, BitSize}; + use core::panic_with_felt252; use core::pedersen::PedersenTrait; use core::starknet::{EthAddress, ContractAddress, ClassHash}; use core::traits::TryInto; -use core::traits::{DivRem, BitAnd}; -use utils::constants::{CONTRACT_ADDRESS_PREFIX, MAX_ADDRESS}; +use core::traits::{DivRem}; +use crate::traits::array::{ArrayExtTrait}; +use utils::constants::{CONTRACT_ADDRESS_PREFIX, MAX_ADDRESS}; use utils::constants::{POW_2, POW_256_1, POW_256_REV}; -use utils::math::{Bitshift, WrappingBitshift, Exponentiation}; +use utils::math::{Bitshift, WrappingBitshift}; use utils::traits::{U256TryIntoContractAddress, EthAddressIntoU256, BoolIntoNumeric}; - +/// Splits a u128 into two u64 parts, representing the high and low parts of the input. +/// +/// # Arguments +/// * `input` - The u128 value to be split. +/// +/// # Returns +/// A tuple containing two u64 values, where the first element is the high part of the input +/// and the second element is the low part of the input. pub fn u128_split(input: u128) -> (u64, u64) { let (high, low) = core::integer::u128_safe_divmod( input, 0x10000000000000000_u128.try_into().unwrap() @@ -62,7 +64,7 @@ pub fn pow256_rev(i: usize) -> u256 { *v } -// Computes 2**pow for 0 <= pow < 128. +/// Computes 2**pow for 0 <= pow < 128. pub fn pow2(pow: usize) -> u128 { if (pow > 127) { return panic_with_felt252('pow2: pow >= 128'); @@ -78,6 +80,12 @@ pub fn split_word(mut value: u256, mut len: usize, ref dst: Array) { ArrayExtTrait::concat(ref dst, word_be.span()); } +/// Splits a u128 into `len` bytes in little-endian order and appends them to the destination array. +/// +/// # Arguments +/// * `dest` - The destination array to append the bytes to +/// * `value` - The u128 value to split into bytes +/// * `len` - The number of bytes to split the value into pub fn split_u128_le(ref dest: Array, mut value: u128, mut len: usize) { while len != 0 { dest.append((value % 256).try_into().unwrap()); @@ -164,683 +172,6 @@ pub fn u256_to_bytes_array(mut value: u256) -> Array { bytes_arr_reversed } -#[generate_trait] -pub impl ArrayExtension> of ArrayExtTrait { - // Concatenates two arrays by adding the elements of arr2 to arr1. - fn concat<+Copy>(ref self: Array, mut arr2: Span) { - for elem in arr2 { - self.append(*elem); - }; - } - - /// Reverses an array - fn reverse<+Copy>(self: Span) -> Array { - let mut counter = self.len(); - let mut dst: Array = ArrayTrait::new(); - while counter != 0 { - dst.append(*self[counter - 1]); - counter -= 1; - }; - dst - } - - // Appends n time value to the Array - fn append_n<+Copy>(ref self: Array, value: T, mut n: usize) { - while n != 0 { - self.append(value); - - n -= 1; - }; - } - - // Appends an item only if it is not already in the array. - fn append_unique<+Copy, +PartialEq>(ref self: Array, value: T) { - if self.span().contains(value) { - return (); - } - self.append(value); - } - - // Concatenates two arrays by adding the elements of arr2 to arr1. - fn concat_unique<+Copy, +PartialEq>(ref self: Array, mut arr2: Span) { - for elem in arr2 { - self.append_unique(*elem) - }; - } -} - -#[generate_trait] -pub impl SpanExtension, +Drop> of SpanExtTrait { - // Returns true if the array contains an item. - fn contains<+PartialEq>(mut self: Span, value: T) -> bool { - let mut result = false; - for elem in self { - if *elem == value { - result = true; - } - }; - result - } - - // Returns the index of an item in the array. - fn index_of<+PartialEq>(mut self: Span, value: T) -> Option { - let mut i = 0; - let mut result = Option::None; - for elem in self { - if *elem == value { - result = Option::Some(i); - } - i += 1; - }; - return result; - } -} - -#[generate_trait] -pub impl U8SpanExImpl of U8SpanExTrait { - // keccack256 on a bytes message - fn compute_keccak256_hash(self: Span) -> u256 { - let (mut keccak_input, last_input_word, last_input_num_bytes) = self.to_u64_words(); - let hash = cairo_keccak(ref keccak_input, :last_input_word, :last_input_num_bytes) - .reverse_endianness(); - - hash - } - /// Transforms a Span into an Array of u64 full words, a pending u64 word and its length in - /// bytes - fn to_u64_words(self: Span) -> (Array, u64, usize) { - let nonzero_8: NonZero = 8_u32.try_into().unwrap(); - let (full_u64_word_count, last_input_num_bytes) = DivRem::div_rem(self.len(), nonzero_8); - - let mut u64_words: Array = Default::default(); - let mut byte_counter: u8 = 0; - let mut pending_word: u64 = 0; - let mut u64_word_counter: usize = 0; - - while u64_word_counter != full_u64_word_count { - if byte_counter == 8 { - u64_words.append(pending_word); - byte_counter = 0; - pending_word = 0; - u64_word_counter += 1; - } - pending_word += match self.get(u64_word_counter * 8 + byte_counter.into()) { - Option::Some(byte) => { - let byte: u64 = (*byte.unbox()).into(); - // Accumulate pending_word in a little endian manner - byte.shl(8_u64 * byte_counter.into()) - }, - Option::None => { break; }, - }; - byte_counter += 1; - }; - - // Fill the last input word - let mut last_input_word: u64 = 0; - let mut byte_counter: u8 = 0; - - // We enter a second loop for clarity. - // O(2n) should be okay - // We might want to regroup every computation into a single loop with appropriate `if` - // branching For optimisation - while byte_counter.into() != last_input_num_bytes { - last_input_word += match self.get(full_u64_word_count * 8 + byte_counter.into()) { - Option::Some(byte) => { - let byte: u64 = (*byte.unbox()).into(); - byte.shl(8_u64 * byte_counter.into()) - }, - Option::None => { break; }, - }; - byte_counter += 1; - }; - - (u64_words, last_input_word, last_input_num_bytes) - } - - /// Returns right padded slice of the span, starting from index offset - /// If offset is greater than the span length, returns an empty span - /// # Examples - /// - /// ``` - /// let span = [0x0, 0x01, 0x02, 0x03, 0x04, 0x05].span(); - /// let expected = [0x04, 0x05, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0].span(); - /// let result = span.slice_right_padded(4, 10); - /// assert_eq!(result, expected); - /// ``` - /// # Arguments - /// * `offset` - The offset to start the slice from - /// * `len` - The length of the slice - /// - /// # Returns - /// * A span of length `len` starting from `offset` right padded with 0s if `offset` is greater - /// than the span length, returns an empty span of length `len` if offset is grearter than the - /// span length - fn slice_right_padded(self: Span, offset: usize, len: usize) -> Span { - let start = if offset <= self.len() { - offset - } else { - self.len() - }; - - let end = min(start.saturating_add(len), self.len()); - - let slice = self.slice(start, end - start); - // Save appending to span for this case as it is more efficient to just return the slice - if slice.len() == len { - return slice; - } - - // Copy the span - let mut arr = array![]; - arr.append_span(slice); - - while arr.len() != len { - arr.append(0); - }; - - arr.span() - } - - /// Clones and pads the given span with 0s to the right to the given length, if data is more - /// than the given length, it is truncated from the right side - /// # Examples - /// ``` - /// let span = [0x01, 0x02, 0x03, 0x04, 0x05].span(); - /// let expected = [0x01, 0x02, 0x03, 0x04, 0x05, 0x0, 0x0, 0x0, 0x0, 0x0].span(); - /// let result = span.pad_right_with_zeroes(10); - /// - /// assert_eq!(result, expected); - /// - /// // Truncates the data if it is more than the given length - /// let span = [0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a].span(); - /// let expected = [0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09].span(); - /// let result = span.pad_right_with_zeroes(9); - /// - /// assert_eq!(result, expected); - /// ``` - /// # Arguments - /// * `len` - The length of the padded span - /// - /// # Returns - /// * A span of length `len` right padded with 0s if the span length is less than `len`, returns - /// a span of length `len` if the span length is greater than `len` then the data is truncated - /// from the right side - fn pad_right_with_zeroes(self: Span, len: usize) -> Span { - if self.len() >= len { - return self.slice(0, len); - } - - // Create a new array with the original data - let mut arr = array![]; - for i in self { - arr.append(*i); - }; - - // Pad with zeroes - while arr.len() != len { - arr.append(0); - }; - - arr.span() - } - - - /// Clones and pads the given span with 0s to the given length, if data is more than the given - /// length, it is truncated from the right side # Examples - /// ``` - /// let span = [0x0, 0x01, 0x02, 0x03, 0x04, 0x05].span(); - /// let expected = [0x0, 0x0, 0x0, 0x0, 0x0, 0x01, 0x02, 0x03, 0x04, 0x05].span(); - /// let result = span.pad_left_with_zeroes(10); - /// - /// assert_eq!(result, expected); - /// - /// // Truncates the data if it is more than the given length - /// let span = [0x0, 0x01, 0x02, 0x03, 0x04, 0x05, 0x6, 0x7, 0x8, 0x9].span(); - /// let expected = [0x0, 0x01, 0x02, 0x03, 0x04, 0x05, 0x6, 0x7, 0x8].span(); - /// let result = span.pad_left_with_zeroes(9); - /// - /// assert_eq!(result, expected); - /// ``` - /// # Arguments - /// * `len` - The length of the padded span - /// - /// # Returns - /// * A span of length `len` left padded with 0s if the span length is less than `len`, returns - /// a span of length `len` if the span length is greater than `len` then the data is truncated - /// from the right side - fn pad_left_with_zeroes(self: Span, len: usize) -> Span { - if self.len() >= len { - return self.slice(0, len); - } - - // left pad with 0 - let mut arr = array![]; - while arr.len() != (len - self.len()) { - arr.append(0); - }; - - // append the data - let mut i = 0; - while i != self.len() { - arr.append(*self[i]); - i += 1; - }; - - arr.span() - } -} - -#[generate_trait] -pub impl U64Impl of U64Trait { - /// Returns the number of trailing zeroes in the bit representation of `self`. - /// # Arguments - /// * `self` a `u64` value. - /// # Returns - /// * The number of trailing zeroes in the bit representation of `self`. - fn count_trailing_zeroes(self: u64) -> u8 { - let mut count = 0; - - if self == 0 { - return 64; // If n is 0, all 64 bits are zeros - }; - - let mut mask = 1; - - while (self & mask) == 0 { - count += 1; - mask *= 2; - }; - - count - } -} - - -#[generate_trait] -pub impl U128Impl of U128Trait { - /// Returns the Least signficant 64 bits of a u128 - fn as_u64(self: u128) -> u64 { - let (_, bottom_word) = u128_split(self); - bottom_word - } -} - -#[generate_trait] -pub impl U256Impl of U256Trait { - /// Splits an u256 into 4 little endian u64. - /// Returns ((high_high, high_low),(low_high, low_low)) - fn split_into_u64_le(self: u256) -> ((u64, u64), (u64, u64)) { - let low_le = integer::u128_byte_reverse(self.low); - let high_le = integer::u128_byte_reverse(self.high); - (u128_split(high_le), u128_split(low_le)) - } - - /// Reverse the endianness of an u256 - fn reverse_endianness(self: u256) -> u256 { - let new_low = integer::u128_byte_reverse(self.high); - let new_high = integer::u128_byte_reverse(self.low); - u256 { low: new_low, high: new_high } - } -} - -pub trait ToBytes { - /// Unpacks a type T into a span of big endian bytes - /// - /// # Arguments - /// * `self` a value of type T. - /// - /// # Returns - /// * The bytes representation of the value in big endian. - fn to_be_bytes(self: T) -> Span; - /// Unpacks a type T into a span of big endian bytes, padded to the byte size of T - /// - /// # Arguments - /// * `self` a value of type T. - /// - /// # Returns - /// * The bytesrepresentation of the value in big endian padded to the byte size of T. - fn to_be_bytes_padded(self: T) -> Span; - /// Unpacks a type T into a span of little endian bytes - /// - /// # Arguments - /// * `self` a value of type T. - /// - /// # Returns - /// * The bytes representation of the value in little endian. - fn to_le_bytes(self: T) -> Span; - /// Unpacks a type T into a span of little endian bytes, padded to the byte size of T - /// - /// # Arguments - /// * `self` a value of type T. - /// - /// # Returns - /// * The bytesrepresentation of the value in little endian padded to the byte size of T. - fn to_le_bytes_padded(self: T) -> Span; -} - -pub impl ToBytesImpl< - T, - +Zero, - +One, - +Add, - +Sub, - +Mul, - +BitAnd, - +Bitshift, - +BitSize, - +BytesUsedTrait, - +Into, - +TryInto, - +Copy, - +Drop, - +core::ops::AddAssign, - +PartialEq -> of ToBytes { - fn to_be_bytes(self: T) -> Span { - let bytes_used = self.bytes_used(); - - let one = One::::one(); - let two = one + one; - let eight = two * two * two; - - // 0xFF - let mask = Bounded::::MAX.into(); - - let mut bytes: Array = Default::default(); - let mut i: u8 = 0; - while i != bytes_used { - let val = Bitshift::::shr(self, eight * (bytes_used - i - 1).into()); - bytes.append((val & mask).try_into().unwrap()); - i += 1; - }; - - bytes.span() - } - - fn to_be_bytes_padded(mut self: T) -> Span { - let padding = (BitSize::::bits() / 8); - self.to_be_bytes().pad_left_with_zeroes(padding) - } - - fn to_le_bytes(mut self: T) -> Span { - let bytes_used = self.bytes_used(); - let one = One::::one(); - let two = one + one; - let eight = two * two * two; - - // 0xFF - let mask = Bounded::::MAX.into(); - - let mut bytes: Array = Default::default(); - - let mut i: u8 = 0; - while i != bytes_used { - let val = self.shr(eight * i.into()); - bytes.append((val & mask).try_into().unwrap()); - i += 1; - }; - - bytes.span() - } - - fn to_le_bytes_padded(mut self: T) -> Span { - let padding = (BitSize::::bits() / 8); - self.to_le_bytes().slice_right_padded(0, padding) - } -} - -pub trait FromBytes { - /// Parses a span of big endian bytes into a type T - /// - /// # Arguments - /// * `self` a span of big endian bytes. - /// - /// # Returns - /// * The Option::(value) represented by the bytes in big endian, Option::None if the span is - /// not the byte size of T. - fn from_be_bytes(self: Span) -> Option; - - /// Parses a span of big endian bytes into a type T, allowing for partial input - /// - /// # Arguments - /// * `self` a span of big endian bytes. - /// - /// # Returns - /// * The Option::(value) represented by the bytes in big endian, Option::None if the span is - /// longer than the byte size of T. - fn from_be_bytes_partial(self: Span) -> Option; - - - /// Parses a span of little endian bytes into a type T - /// - /// # Arguments - /// * `self` a span of little endian bytes. - /// - /// # Returns - /// * The Option::(value) represented by the bytes in little endian, Option::None if the span is - /// not the byte size of T. - fn from_le_bytes(self: Span) -> Option; - - /// Parses a span of little endian bytes into a type T, allowing for partial input - /// - /// # Arguments - /// * `self` a span of little endian bytes. - /// - /// # Returns - /// * The Option::(value) represented by the bytes in little endian, Option::None if the span is - /// longer than the byte size of T. - fn from_le_bytes_partial(self: Span) -> Option; -} - -pub impl FromBytesImpl< - T, - +Zero, - +One, - +Add, - +Sub, - +Mul, - +BitAnd, - +Bitshift, - +ByteSize, - +BytesUsedTrait, - +Into, - +Into, - +TryInto, - +Copy, - +Drop, - +core::ops::AddAssign, - +PartialEq -> of FromBytes { - fn from_be_bytes(self: Span) -> Option { - let byte_size = ByteSize::::byte_size(); - - if self.len() != byte_size { - return Option::None; - } - - let mut result: T = Zero::zero(); - for byte in self { - let tmp = result * 256_u16.into(); - result = tmp + (*byte).into(); - }; - Option::Some(result) - } - - fn from_be_bytes_partial(self: Span) -> Option { - let byte_size = ByteSize::::byte_size(); - - if self.len() > byte_size { - return Option::None; - } - - let mut result: T = Zero::zero(); - for byte in self { - let tmp = result * 256_u16.into(); - result = tmp + (*byte).into(); - }; - - Option::Some(result) - } - - fn from_le_bytes(self: Span) -> Option { - let byte_size = ByteSize::::byte_size(); - - if self.len() != byte_size { - return Option::None; - } - - let mut result: T = Zero::zero(); - let mut i = self.len(); - while i != 0 { - i -= 1; - let tmp = result * 256_u16.into(); - result = tmp + (*self[i]).into(); - }; - Option::Some(result) - } - - fn from_le_bytes_partial(self: Span) -> Option { - let byte_size = ByteSize::::byte_size(); - - if self.len() > byte_size { - return Option::None; - } - - let mut result: T = Zero::zero(); - let mut i = self.len(); - while i != 0 { - i -= 1; - let tmp = result * 256_u16.into(); - result = tmp + (*self[i]).into(); - }; - Option::Some(result) - } -} - - -#[generate_trait] -pub impl ByteArrayExt of ByteArrayExTrait { - fn append_span_bytes(ref self: ByteArray, mut bytes: Span) { - for val in bytes { - self.append_byte(*val); - }; - } - - fn from_bytes(mut bytes: Span) -> ByteArray { - let mut arr: ByteArray = Default::default(); - let (nb_full_words, pending_word_len) = DivRem::div_rem( - bytes.len(), 31_u32.try_into().unwrap() - ); - let mut i = 0; - while i != nb_full_words { - let mut word: felt252 = 0; - let mut j = 0; - while j != 31 { - word = word * POW_256_1.into() + (*bytes.pop_front().unwrap()).into(); - j += 1; - }; - arr.append_word(word.try_into().unwrap(), 31); - i += 1; - }; - - if pending_word_len == 0 { - return arr; - }; - - let mut pending_word: felt252 = 0; - let mut i = 0; - - while i != pending_word_len { - pending_word = pending_word * POW_256_1.into() + (*bytes.pop_front().unwrap()).into(); - i += 1; - }; - arr.append_word(pending_word.try_into().unwrap(), pending_word_len); - arr - } - - fn is_empty(self: @ByteArray) -> bool { - self.len() == 0 - } - - fn into_bytes(self: ByteArray) -> Span { - let mut output: Array = Default::default(); - let len = self.len(); - let mut i = 0; - while i != len { - output.append(self[i]); - i += 1; - }; - output.span() - } - - - /// Transforms a ByteArray into an Array of u64 full words, a pending u64 word and its length in - /// bytes - fn to_u64_words(self: ByteArray) -> (Array, u64, usize) { - // We pass it by value because we want to take ownership, but we snap it - // because `at` takes a snap and if this snap is automatically done by - // the compiler in the loop, it won't compile - let self = @self; - let nonzero_8: NonZero = 8_u32.try_into().unwrap(); - let (full_u64_word_count, last_input_num_bytes) = DivRem::div_rem(self.len(), nonzero_8); - - let mut u64_words: Array = Default::default(); - let mut byte_counter: u8 = 0; - let mut pending_word: u64 = 0; - let mut u64_word_counter: usize = 0; - - while u64_word_counter != full_u64_word_count { - if byte_counter == 8 { - u64_words.append(pending_word); - byte_counter = 0; - pending_word = 0; - u64_word_counter += 1; - } - pending_word += match self.at(u64_word_counter * 8 + byte_counter.into()) { - Option::Some(byte) => { - let byte: u64 = byte.into(); - // Accumulate pending_word in a little endian manner - byte.shl(8_u64 * byte_counter.into()) - }, - Option::None => { break; }, - }; - byte_counter += 1; - }; - - // Fill the last input word - let mut last_input_word: u64 = 0; - let mut byte_counter: u8 = 0; - - // We enter a second loop for clarity. - // O(2n) should be okay - // We might want to regroup every computation into a single loop with appropriate `if` - // branching For optimisation - while byte_counter.into() != last_input_num_bytes { - last_input_word += match self.at(full_u64_word_count * 8 + byte_counter.into()) { - Option::Some(byte) => { - let byte: u64 = byte.into(); - byte.shl(8_u64 * byte_counter.into()) - }, - Option::None => { break; }, - }; - byte_counter += 1; - }; - - (u64_words, last_input_word, last_input_num_bytes) - } -} - -#[generate_trait] -pub impl ResultExImpl, +Drop> of ResultExTrait { - /// Converts a Result to a Result - fn map_err>(self: Result, err: F) -> Result { - match self { - Result::Ok(val) => Result::Ok(val), - Result::Err(_) => Result::Err(err) - } - } -} - pub fn compute_starknet_address( kakarot_address: ContractAddress, evm_address: EthAddress, class_hash: ClassHash @@ -914,590 +245,29 @@ pub impl EthAddressExImpl of EthAddressExTrait { } -pub trait BytesUsedTrait { - /// Returns the number of bytes used to represent a `T` value. - /// # Arguments - /// * `self` - The value to check. - /// # Returns - /// The number of bytes used to represent the value. - fn bytes_used(self: T) -> u8; -} +#[cfg(test)] +mod tests { + use utils::helpers; -pub impl U8BytesUsedTraitImpl of BytesUsedTrait { - fn bytes_used(self: u8) -> u8 { - if self == 0 { - return 0; - } + #[test] + fn test_u256_to_bytes_array() { + let value: u256 = 256; - return 1; + let bytes_array = helpers::u256_to_bytes_array(value); + assert(1 == *bytes_array[30], 'wrong conversion'); } -} -pub impl USizeBytesUsedTraitImpl of BytesUsedTrait { - fn bytes_used(self: usize) -> u8 { - if self < 0x10000 { // 256^2 - if self < 0x100 { // 256^1 - if self == 0 { - return 0; - } else { - return 1; - }; - } - return 2; - } else { - if self < 0x1000000 { // 256^3 - return 3; - } - return 4; - } - } -} + #[test] + fn test_load_word() { + // No bytes to load + let res0 = helpers::load_word(0, ArrayTrait::new().span()); + assert(0 == res0, 'res0: wrong load'); -pub impl U64BytesUsedTraitImpl of BytesUsedTrait { - fn bytes_used(self: u64) -> u8 { - if self <= Bounded::::MAX.into() { // 256^4 - return BytesUsedTrait::::bytes_used(self.try_into().unwrap()); - } else { - if self < 0x1000000000000 { // 256^6 - if self < 0x10000000000 { - if self < 0x100000000 { - return 4; - } - return 5; - } - return 6; - } else { - if self < 0x100000000000000 { // 256^7 - return 7; - } else { - return 8; - } - } - } - } -} - -pub impl U128BytesTraitUsedImpl of BytesUsedTrait { - fn bytes_used(self: u128) -> u8 { - let (u64high, u64low) = u128_split(self); - if u64high == 0 { - return BytesUsedTrait::::bytes_used(u64low.try_into().unwrap()); - } else { - return BytesUsedTrait::::bytes_used(u64high.try_into().unwrap()) + 8; - } - } -} - -pub impl U256BytesUsedTraitImpl of BytesUsedTrait { - fn bytes_used(self: u256) -> u8 { - if self.high == 0 { - return BytesUsedTrait::::bytes_used(self.low.try_into().unwrap()); - } else { - return BytesUsedTrait::::bytes_used(self.high.try_into().unwrap()) + 16; - } - } -} - -pub trait ByteSize { - fn byte_size() -> usize; -} - -pub impl ByteSizeImpl> of ByteSize { - fn byte_size() -> usize { - BitSize::::bits() / 8 - } -} - -pub trait BitsUsed { - /// Returns the number of bits required to represent `self`, ignoring leading zeros. - /// # Arguments - /// `self` - The value to check. - /// # Returns - /// The number of bits used to represent the value, ignoring leading zeros. - fn bits_used(self: T) -> u32; - - /// Returns the number of leading zeroes in the bit representation of `self`. - /// # Arguments - /// `self` - The value to check. - /// # Returns - /// The number of leading zeroes in the bit representation of `self`. - fn count_leading_zeroes(self: T) -> u32; -} - -pub impl BitsUsedImpl< - T, - +Zero, - +One, - +Add, - +Sub, - +Mul, - +Bitshift, - +BitSize, - +BytesUsedTrait, - +Into, - +TryInto, - +Copy, - +Drop, - +PartialEq -> of BitsUsed { - fn bits_used(self: T) -> u32 { - if self == Zero::zero() { - return 0; - } - let two: T = One::one() + One::one(); - let eight: T = two * two * two; - - let bytes_used = self.bytes_used(); - let last_byte = self.shr(eight * (bytes_used.into() - One::one())); - - // safe unwrap since we know atmost 8 bits are used - let bits_used: u8 = bits_used_internal::bits_used_in_byte(last_byte.try_into().unwrap()); - - bits_used.into() + 8 * (bytes_used - 1).into() - } - - fn count_leading_zeroes(self: T) -> u32 { - BitSize::::bits() - self.bits_used() - } -} - -pub(crate) mod bits_used_internal { - /// Returns the number of bits used to represent the value in binary representation - /// # Arguments - /// * `self` - The value to compute the number of bits used - /// # Returns - /// * The number of bits used to represent the value in binary representation - pub(crate) fn bits_used_in_byte(self: u8) -> u8 { - if self < 0b100000 { - if self < 0b1000 { - if self < 0b100 { - if self < 0b10 { - if self == 0 { - return 0; - } else { - return 1; - }; - } - return 2; - } - - return 3; - } - - if self < 0b10000 { - return 4; - } - - return 5; - } else { - if self < 0b10000000 { - if self < 0b1000000 { - return 6; - } - return 7; - } - return 8; - } - } -} - -#[derive(Drop, Debug, PartialEq)] -pub enum Felt252VecTraitErrors { - IndexOutOfBound, - Overflow, - LengthIsNotSame, - SizeLessThanCurrentLength -} - -#[generate_trait] -pub impl Felt252VecTraitImpl< - T, - +Drop, - +Copy, - +Felt252DictValue, - +Zero, - +Add, - +Sub, - +Div, - +Mul, - +Exponentiation, - +ToBytes, - +PartialOrd, - +Into, - +PartialEq, -> of Felt252VecTrait { - /// Returns Felt252Vec as a Span<8>, the returned Span is in big endian format - /// # Arguments - /// * `self` a ref Felt252Vec - /// # Returns - /// * A Span representing bytes conversion of `self` in big endian format - fn to_be_bytes(ref self: Felt252Vec) -> Span { - let mut res: Array = array![]; - self.remove_trailing_zeroes(); - - let mut i = self.len(); - - while i != 0 { - i -= 1; - res.append_span(self[i].to_be_bytes_padded()); - }; - - res.span() - } - - /// Returns Felt252Vec as a Span<8>, the returned Span is in little endian format - /// # Arguments - /// * `self` a ref Felt252Vec - /// # Returns - /// * A Span representing bytes conversion of `self` in little endian format - fn to_le_bytes(ref self: Felt252Vec) -> Span { - let mut res: Array = array![]; - let mut i = 0; - - while i != self.len() { - if self[i] == Zero::zero() { - res.append(Zero::zero()); - } else { - res.append_span(self[i].to_le_bytes()); - } - - i += 1; - }; - - res.span() - } - - /// Expands a Felt252Vec to a new length by appending zeroes - /// - /// This function will mutate the Felt252Vec in-place and will expand its length, - /// since the default value for Felt252Dict item is 0, all new elements will be set to 0. - /// If the new length is less than the current length, it will return an error. - /// - /// # Arguments - /// * `self` a ref Felt252Vec - /// * `new_length` the new length of the Felt252Vec - /// - /// # Returns - /// * Result::<(), Felt252VecTraitErrors> - /// - /// # Errors - /// * Felt252VecTraitErrors::SizeLessThanCurrentLength if the new length is less than the - /// current length - fn expand(ref self: Felt252Vec, new_length: usize) -> Result<(), Felt252VecTraitErrors> { - if (new_length < self.len) { - return Result::Err(Felt252VecTraitErrors::SizeLessThanCurrentLength); - }; - - self.len = new_length; - - Result::Ok(()) - } - - /// Sets all elements of the Felt252Vec to zero, mutates the Felt252Vec in-place - /// - /// # Arguments - /// self a ref Felt252Vec - fn reset(ref self: Felt252Vec) { - let mut new_vec: Felt252Vec = Default::default(); - new_vec.len = self.len; - self = new_vec; - } - - /// Returns the leading zeroes of a Felt252Vec - /// - /// # Arguments - /// * `self` a ref Felt252Vec - /// - /// # Returns - /// * The number of leading zeroes in `self`. - fn count_leading_zeroes(ref self: Felt252Vec) -> usize { - let mut i = 0; - while i != self.len() && self[i] == Zero::zero() { - i += 1; - }; - - i - } - - /// Resizes the Felt252Vec in-place so that len is equal to new_len. - /// - /// This function will mutate the Felt252Vec in-place and will resize its length to the new - /// length. - /// If new_len is greater than len, the Vec is extended by the difference, with each additional - /// slot filled with 0. If new_len is less than len, the Vec is simply truncated from the right. - /// - /// # Arguments - /// * `self` a ref Felt252Vec - /// * `new_len` the new length of the Felt252Vec - fn resize(ref self: Felt252Vec, new_len: usize) { - self.len = new_len; - } - - - /// Copies the elements from a Span into the Felt252Vec in little endian format, in case - /// of overflow or index being out of bounds, an error is returned - /// - /// # Arguments - /// * `self` a ref Felt252Vec - /// * `index` the index at `self` to start copying from - /// * `slice` a Span - /// - /// # Errors - /// * Felt252VecTraitErrors::IndexOutOfBound if the index is out of bounds - /// * Felt252VecTraitErrors::Overflow if the Span is too big to fit in the Felt252Vec - fn copy_from_bytes_le( - ref self: Felt252Vec, index: usize, mut slice: Span - ) -> Result<(), Felt252VecTraitErrors> { - if (index >= self.len) { - return Result::Err(Felt252VecTraitErrors::IndexOutOfBound); - } - - if ((slice.len() + index) > self.len()) { - return Result::Err(Felt252VecTraitErrors::Overflow); - } - - let mut i = index; - for val in slice { - // safe unwrap, as in case of none, we will never reach this branch - self.set(i, (*val).into()); - i += 1; - }; - - Result::Ok(()) - } - - /// Copies the elements from a Felt252Vec into the Felt252Vec in little endian format, If - /// length of both Felt252Vecs are not same, it will return an error - /// - /// # Arguments - /// * `self` a ref Felt252Vec - /// * `vec` a ref Felt252Vec - /// - /// # Errors - /// * Felt252VecTraitErrors::LengthIsNotSame if the length of both Felt252Vecs are not same - fn copy_from_vec_le( - ref self: Felt252Vec, ref vec: Felt252Vec - ) -> Result<(), Felt252VecTraitErrors> { - if (vec.len() != self.len) { - return Result::Err(Felt252VecTraitErrors::LengthIsNotSame); - } - - self = vec.duplicate(); - - Result::Ok(()) - } - - /// Insert elements of Felt252Vec into another Felt252Vec at a given index, in case of overflow - /// or index being out of bounds, an error is returned - /// - /// # Arguments - /// * `self` a ref Felt252Vec - /// * `idx` the index at `self` to start inserting from - /// * `vec` a ref Felt252Vec - /// - /// # Errors - /// * Felt252VecTraitErrors::IndexOutOfBound if the index is out of bounds - /// * Felt252VecTraitErrors::Overflow if the Felt252Vec is too big to fit in the Felt252Vec - fn insert_vec( - ref self: Felt252Vec, idx: usize, ref vec: Felt252Vec - ) -> Result<(), Felt252VecTraitErrors> { - if idx >= self.len() { - return Result::Err(Felt252VecTraitErrors::IndexOutOfBound); - } - - if (idx + vec.len > self.len) { - return Result::Err(Felt252VecTraitErrors::Overflow); - } - - let stop = idx + vec.len(); - let mut i = idx; - while i != stop { - self.set(i, vec[i - idx]); - i += 1; - }; - - Result::Ok(()) - } - - - /// Removes trailing zeroes from a Felt252Vec - /// - /// # Arguments - /// * `input` a ref Felt252Vec - fn remove_trailing_zeroes(ref self: Felt252Vec) { - let mut new_len = self.len; - while (new_len != 0) && (self[new_len - 1] == Zero::zero()) { - new_len -= 1; - }; - - self.len = new_len; - } - - /// Pops an element out of the vector, returns Option::None if the vector is empty - /// - /// # Arguments - /// * `self` a ref Felt252Vec - /// # Returns - /// - /// * Option::Some(T), returns the last element or Option::None if the vector is empty - fn pop(ref self: Felt252Vec) -> Option { - if (self.len) == 0 { - return Option::None; - } - - let popped_ele = self[self.len() - 1]; - self.len = self.len - 1; - Option::Some(popped_ele) - } - - /// takes a Felt252Vec and returns a new Felt252Vec with the same elements - /// - /// Note: this is an expensive operation, as it will create a new Felt252Vec - /// - /// # Arguments - /// * `self` a ref Felt252Vec - /// - /// # Returns - /// * A new Felt252Vec with the same elements - fn duplicate(ref self: Felt252Vec) -> Felt252Vec { - let mut new_vec = Default::default(); - - let mut i: u32 = 0; - - while i != self.len { - new_vec.push(self[i]); - i += 1; - }; - - new_vec - } - - /// Returns a new Felt252Vec with elements starting from `idx` to `idx + len` - /// - /// This function will start cloning from `idx` and will clone `len` elements, it will firstly - /// clone the elements and then return a new Felt252Vec - /// In case of overflow return Option::None - /// - /// # Arguments - /// * `self` a ref Felt252Vec - /// * `idx` the index to start cloning from - /// * `len` the length of the clone - /// - /// # Returns - /// * Felt252Vec - /// - /// # Panics - /// * If the index is out of bounds - /// - /// Note: this is an expensive operation, as it will create a new Felt252Vec - fn clone_slice(ref self: Felt252Vec, idx: usize, len: usize) -> Felt252Vec { - let mut new_vec = Default::default(); - - let mut i: u32 = 0; - - while i != len { - new_vec.push(self[idx + i]); - - i += 1; - }; - - new_vec - } - - /// Returns whether two Felt252Vec are equal after removing trailing_zeroes - /// - /// # Arguments - /// * `self` a ref Felt252Vec - /// * `rhs` a ref Felt252Vec - /// - /// # Returns - /// * bool, returns true if both Felt252Vecs are equal, false otherwise - /// TODO: if this utils is only used for testing, then refactor as a test util - fn equal_remove_trailing_zeroes(ref self: Felt252Vec, ref rhs: Felt252Vec) -> bool { - let mut lhs = self.duplicate(); - lhs.remove_trailing_zeroes(); - - let mut rhs = rhs.duplicate(); - rhs.remove_trailing_zeroes(); - - if lhs.len() != rhs.len() { - return false; - }; - - let mut i = 0; - let mut result = true; - while i != lhs.len() { - if lhs[i] != rhs[i] { - result = false; - break; - } - i += 1; - }; - result - } - - /// Fills a Felt252Vec with a given `value` starting from `start_idx` to `start_idx + len` - /// In case of index out of bounds or overflow, error is returned - /// - /// # Arguments - /// * `self` a ref Felt252Vec - /// * `start_idx` the index to start filling from - /// * `len` the length of the fill - /// * `value` the value to fill the Felt252Vec with - /// - /// # Returns - /// * Result::<(), Felt252VecTraitErrors> - /// - /// # Errors - /// * Felt252VecTraitErrors::IndexOutOfBound if the index is out of bounds - /// * Felt252VecTraitErrors::Overflow if the Felt252Vec is too big to fit in the Felt252Vec - fn fill( - ref self: Felt252Vec, start_idx: usize, len: usize, value: T - ) -> Result<(), Felt252VecTraitErrors> { - // Index out of bounds - if (start_idx >= self.len()) { - return Result::Err(Felt252VecTraitErrors::IndexOutOfBound); - } - - // Overflow - if (start_idx + len > self.len()) { - return Result::Err(Felt252VecTraitErrors::Overflow); - } - - let mut i = start_idx; - while i != start_idx + len { - self.set(i, value); - - i += 1; - }; - - Result::Ok(()) - } -} - -#[cfg(test)] -mod tests { - use utils::helpers; - - #[test] - fn test_u256_to_bytes_array() { - let value: u256 = 256; - - let bytes_array = helpers::u256_to_bytes_array(value); - assert(1 == *bytes_array[30], 'wrong conversion'); - } - - #[test] - fn test_load_word() { - // No bytes to load - let res0 = helpers::load_word(0, ArrayTrait::new().span()); - assert(0 == res0, 'res0: wrong load'); - - // Single bytes value - let mut arr1 = ArrayTrait::new(); - arr1.append(0x01); - let res1 = helpers::load_word(1, arr1.span()); - assert(1 == res1, 'res1: wrong load'); + // Single bytes value + let mut arr1 = ArrayTrait::new(); + arr1.append(0x01); + let res1 = helpers::load_word(1, arr1.span()); + assert(1 == res1, 'res1: wrong load'); let mut arr2 = ArrayTrait::new(); arr2.append(0xff); @@ -1618,1133 +388,4 @@ mod tests { counter += 1; }; } - - mod test_array_ext { - use utils::helpers::{ArrayExtTrait}; - #[test] - fn test_append_n() { - // Given - let mut original: Array = array![1, 2, 3, 4]; - - // When - original.append_n(9, 3); - - // Then - assert(original == array![1, 2, 3, 4, 9, 9, 9], 'append_n failed'); - } - - #[test] - fn test_append_unique() { - let mut arr = array![1, 2, 3]; - arr.append_unique(4); - assert(arr == array![1, 2, 3, 4], 'should have appended'); - arr.append_unique(2); - assert(arr == array![1, 2, 3, 4], 'shouldnt have appended'); - } - } - - mod u8_test { - use utils::helpers::BitsUsed; - use utils::math::Bitshift; - - #[test] - fn test_bits_used() { - assert_eq!(0x00_u8.bits_used(), 0); - let mut value: u8 = 0xff; - let mut i = 8; - loop { - assert_eq!(value.bits_used(), i); - if i == 0 { - break; - }; - value = value.shr(1); - - i -= 1; - }; - } - } - - mod u32_test { - use utils::helpers::{BytesUsedTrait, ToBytes, FromBytes}; - use utils::math::Bitshift; - - #[test] - fn test_u32_from_be_bytes() { - let input: Array = array![0xf4, 0x32, 0x15, 0x62]; - let res: Option = input.span().from_be_bytes(); - - assert!(res.is_some()); - assert_eq!(res.unwrap(), 0xf4321562); - } - - #[test] - fn test_u32_from_be_bytes_too_big_should_return_none() { - let input: Array = array![0xf4, 0x32, 0x15, 0x62, 0x01]; - let res: Option = input.span().from_be_bytes(); - - assert!(res.is_none()); - } - - #[test] - fn test_u32_from_be_bytes_too_small_should_return_none() { - let input: Array = array![0xf4, 0x32, 0x15]; - let res: Option = input.span().from_be_bytes(); - - assert!(res.is_none()); - } - - #[test] - fn test_u32_from_be_bytes_partial_full() { - let input: Array = array![0xf4, 0x32, 0x15, 0x62]; - let res: Option = input.span().from_be_bytes_partial(); - - assert!(res.is_some()); - assert_eq!(res.unwrap(), 0xf4321562); - } - - #[test] - fn test_u32_from_be_bytes_partial_smaller_input() { - let input: Array = array![0xf4, 0x32, 0x15]; - let res: Option = input.span().from_be_bytes_partial(); - - assert!(res.is_some()); - assert_eq!(res.unwrap(), 0xf43215); - } - - #[test] - fn test_u32_from_be_bytes_partial_single_byte() { - let input: Array = array![0xf4]; - let res: Option = input.span().from_be_bytes_partial(); - - assert!(res.is_some()); - assert_eq!(res.unwrap(), 0xf4); - } - - #[test] - fn test_u32_from_be_bytes_partial_empty_input() { - let input: Array = array![]; - let res: Option = input.span().from_be_bytes_partial(); - - assert!(res.is_some()); - assert_eq!(res.unwrap(), 0); - } - - #[test] - fn test_u32_from_be_bytes_partial_too_big_input() { - let input: Array = array![0xf4, 0x32, 0x15, 0x62, 0x01]; - let res: Option = input.span().from_be_bytes_partial(); - - assert!(res.is_none()); - } - - #[test] - fn test_u32_from_le_bytes() { - let input: Array = array![0x62, 0x15, 0x32, 0xf4]; - let res: Option = input.span().from_le_bytes(); - - assert!(res.is_some()); - assert_eq!(res.unwrap(), 0xf4321562); - } - - #[test] - fn test_u32_from_le_bytes_too_big() { - let input: Array = array![0x62, 0x15, 0x32, 0xf4, 0x01]; - let res: Option = input.span().from_le_bytes(); - - assert!(res.is_none()); - } - - #[test] - fn test_u32_from_le_bytes_too_small() { - let input: Array = array![0x62, 0x15, 0x32]; - let res: Option = input.span().from_le_bytes(); - - assert!(res.is_none()); - } - - #[test] - fn test_u32_from_le_bytes_zero() { - let input: Array = array![0x00, 0x00, 0x00, 0x00]; - let res: Option = input.span().from_le_bytes(); - - assert!(res.is_some()); - assert_eq!(res.unwrap(), 0); - } - - #[test] - fn test_u32_from_le_bytes_max() { - let input: Array = array![0xff, 0xff, 0xff, 0xff]; - let res: Option = input.span().from_le_bytes(); - - assert!(res.is_some()); - assert_eq!(res.unwrap(), 0xffffffff); - } - - #[test] - fn test_u32_from_le_bytes_partial() { - let input: Array = array![0x62, 0x15, 0x32]; - let res: Option = input.span().from_le_bytes_partial(); - - assert!(res.is_some()); - assert_eq!(res.unwrap(), 0x321562); - } - - #[test] - fn test_u32_from_le_bytes_partial_full() { - let input: Array = array![0x62, 0x15, 0x32, 0xf4]; - let res: Option = input.span().from_le_bytes_partial(); - - assert!(res.is_some()); - assert_eq!(res.unwrap(), 0xf4321562); - } - - #[test] - fn test_u32_from_le_bytes_partial_too_big() { - let input: Array = array![0x62, 0x15, 0x32, 0xf4, 0x01]; - let res: Option = input.span().from_le_bytes_partial(); - - assert!(res.is_none()); - } - - #[test] - fn test_u32_from_le_bytes_partial_empty() { - let input: Array = array![]; - let res: Option = input.span().from_le_bytes_partial(); - - assert!(res.is_some()); - assert_eq!(res.unwrap(), 0); - } - - #[test] - fn test_u32_from_le_bytes_partial_single_byte() { - let input: Array = array![0xff]; - let res: Option = input.span().from_le_bytes_partial(); - - assert!(res.is_some()); - assert_eq!(res.unwrap(), 0xff); - } - - #[test] - fn test_u32_to_bytes_full() { - let input: u32 = 0xf4321562; - let res: Span = input.to_be_bytes(); - - assert_eq!(res.len(), 4); - assert_eq!(*res[0], 0xf4); - assert_eq!(*res[1], 0x32); - assert_eq!(*res[2], 0x15); - assert_eq!(*res[3], 0x62); - } - - #[test] - fn test_u32_to_bytes_partial() { - let input: u32 = 0xf43215; - let res: Span = input.to_be_bytes(); - - assert_eq!(res.len(), 3); - assert_eq!(*res[0], 0xf4); - assert_eq!(*res[1], 0x32); - assert_eq!(*res[2], 0x15); - } - - - #[test] - fn test_u32_to_bytes_leading_zeros() { - let input: u32 = 0x00f432; - let res: Span = input.to_be_bytes(); - - assert_eq!(res.len(), 2); - assert_eq!(*res[0], 0xf4); - assert_eq!(*res[1], 0x32); - } - - #[test] - fn test_u32_to_be_bytes_padded() { - let input: u32 = 7; - let result = input.to_be_bytes_padded(); - let expected = [0x0, 0x0, 0x0, 7].span(); - - assert_eq!(result, expected); - } - - - #[test] - fn test_u32_bytes_used() { - assert_eq!(0x00_u32.bytes_used(), 0); - let mut value: u32 = 0xff; - let mut i = 1; - loop { - assert_eq!(value.bytes_used(), i); - if i == 4 { - break; - }; - i += 1; - value = value.shl(8); - }; - } - - #[test] - fn test_u32_bytes_used_leading_zeroes() { - let len: u32 = 0x001234; - let bytes_count = len.bytes_used(); - - assert_eq!(bytes_count, 2); - } - } - - mod u64_test { - use utils::helpers::U64Trait; - use utils::helpers::{BitsUsed, BytesUsedTrait, ToBytes}; - use utils::math::Bitshift; - - - #[test] - fn test_u64_bytes_used() { - assert_eq!(0x00_u64.bytes_used(), 0); - let mut value: u64 = 0xff; - let mut i = 1; - loop { - assert_eq!(value.bytes_used(), i); - if i == 8 { - break; - }; - i += 1; - value = value.shl(8); - }; - } - - #[test] - fn test_u64_to_be_bytes_padded() { - let input: u64 = 7; - let result = input.to_be_bytes_padded(); - let expected = [0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 7].span(); - - assert_eq!(result, expected); - } - - #[test] - fn test_u64_trailing_zeroes() { - /// bit len is 3, and trailing zeroes are 2 - let input: u64 = 4; - let result = input.count_trailing_zeroes(); - let expected = 2; - - assert_eq!(result, expected); - } - - - #[test] - fn test_u64_leading_zeroes() { - /// bit len is 3, and leading zeroes are 64 - 3 = 61 - let input: u64 = 7; - let result = input.count_leading_zeroes(); - let expected = 61; - - assert_eq!(result, expected); - } - - #[test] - fn test_u64_bits_used() { - let input: u64 = 7; - let result = input.bits_used(); - let expected = 3; - - assert_eq!(result, expected); - } - } - - mod u128_test { - use core::num::traits::Bounded; - use utils::helpers::{BytesUsedTrait, ToBytes}; - use utils::math::Bitshift; - - #[test] - fn test_u128_bytes_used() { - assert_eq!(0x00_u128.bytes_used(), 0); - let mut value: u128 = 0xff; - let mut i = 1; - loop { - assert_eq!(value.bytes_used(), i); - if i == 16 { - break; - }; - i += 1; - value = value.shl(8); - }; - } - - #[test] - fn test_u128_to_bytes_full() { - let input: u128 = Bounded::MAX; - let result: Span = input.to_be_bytes(); - let expected = [ - 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 - ].span(); - - assert_eq!(result, expected); - } - - #[test] - fn test_u128_to_bytes_partial() { - let input: u128 = 0xf43215; - let result: Span = input.to_be_bytes(); - let expected = [0xf4, 0x32, 0x15].span(); - - assert_eq!(result, expected); - } - - #[test] - fn test_u128_to_bytes_padded() { - let input: u128 = 0xf43215; - let result: Span = input.to_be_bytes_padded(); - let expected = [ - 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xf4, 0x32, 0x15 - ].span(); - - assert_eq!(result, expected); - } - } - - mod u256_test { - use utils::helpers::U256Trait; - use utils::helpers::{BitsUsed, BytesUsedTrait}; - use utils::math::Bitshift; - - #[test] - fn test_reverse_bytes_u256() { - let value: u256 = 0xFAFFFFFF000000E500000077000000DEAD0000000004200000FADE0000450000; - let res = value.reverse_endianness(); - assert( - res == 0x0000450000DEFA0000200400000000ADDE00000077000000E5000000FFFFFFFA, - 'reverse mismatch' - ); - } - - #[test] - fn test_split_u256_into_u64_little() { - let value: u256 = 0xFAFFFFFF000000E500000077000000DEAD0000000004200000FADE0000450000; - let ((high_h, low_h), (high_l, low_l)) = value.split_into_u64_le(); - assert_eq!(high_h, 0xDE00000077000000); - assert_eq!(low_h, 0xE5000000FFFFFFFA); - assert_eq!(high_l, 0x0000450000DEFA00); - assert_eq!(low_l, 0x00200400000000AD); - } - - #[test] - fn test_u256_bytes_used() { - assert_eq!(0x00_u256.bytes_used(), 0); - let mut value: u256 = 0xff; - let mut i = 1; - loop { - assert_eq!(value.bytes_used(), i); - if i == 32 { - break; - }; - i += 1; - value = value.shl(8); - }; - } - - #[test] - fn test_u256_leading_zeroes() { - /// bit len is 3, and leading zeroes are 256 - 3 = 253 - let input: u256 = 7; - let result = input.count_leading_zeroes(); - let expected = 253; - - assert_eq!(result, expected); - } - - #[test] - fn test_u64_bits_used() { - let input: u256 = 7; - let result = input.bits_used(); - let expected = 3; - - assert_eq!(result, expected); - } - } - - - mod bytearray_test { - use utils::helpers::ByteArrayExTrait; - - - #[test] - fn test_pack_bytes_ge_bytes31() { - let mut arr = array![ - 0x01, - 0x02, - 0x03, - 0x04, - 0x05, - 0x06, - 0x07, - 0x08, - 0x09, - 0x0A, - 0x0B, - 0x0C, - 0x0D, - 0x0E, - 0x0F, - 0x10, - 0x11, - 0x12, - 0x13, - 0x14, - 0x15, - 0x16, - 0x17, - 0x18, - 0x19, - 0x1A, - 0x1B, - 0x1C, - 0x1D, - 0x1E, - 0x1F, - 0x20, - 0x21 // 33 elements - ]; - - let res = ByteArrayExTrait::from_bytes(arr.span()); - - // Ensure that the result is complete and keeps the same order - let mut i = 0; - while i != arr.len() { - assert(*arr[i] == res[i], 'byte mismatch'); - i += 1; - }; - } - - #[test] - fn test_bytearray_append_span_bytes() { - let bytes = array![0x01, 0x02, 0x03, 0x04]; - let mut byte_arr: ByteArray = Default::default(); - byte_arr.append_byte(0xFF); - byte_arr.append_byte(0xAA); - byte_arr.append_span_bytes(bytes.span()); - assert_eq!(byte_arr.len(), 6); - assert_eq!(byte_arr[0], 0xFF); - assert_eq!(byte_arr[1], 0xAA); - assert_eq!(byte_arr[2], 0x01); - assert_eq!(byte_arr[3], 0x02); - assert_eq!(byte_arr[4], 0x03); - assert_eq!(byte_arr[5], 0x04); - } - - #[test] - fn test_byte_array_into_bytes() { - let input = array![ - 0x01, - 0x02, - 0x03, - 0x04, - 0x05, - 0x06, - 0x07, - 0x08, - 0x09, - 0x0A, - 0x0B, - 0x0C, - 0x0D, - 0x0E, - 0x0F, - 0x10, - 0x11, - 0x12, - 0x13, - 0x14, - 0x15, - 0x16, - 0x17, - 0x18, - 0x19, - 0x1A, - 0x1B, - 0x1C, - 0x1D, - 0x1E, - 0x1F, - 0x20, - 0x21 // 33 elements - ]; - let byte_array = ByteArrayExTrait::from_bytes(input.span()); - let res = byte_array.into_bytes(); - - // Ensure that the elements are correct - assert(res == input.span(), 'bytes mismatch'); - } - - #[test] - fn test_pack_bytes_le_bytes31() { - let mut arr = array![0x11, 0x22, 0x33, 0x44]; - let res = ByteArrayExTrait::from_bytes(arr.span()); - - // Ensure that the result is complete and keeps the same order - let mut i = 0; - while i != arr.len() { - assert(*arr[i] == res[i], 'byte mismatch'); - i += 1; - }; - } - - - #[test] - fn test_bytearray_to_64_words_partial() { - let input = ByteArrayExTrait::from_bytes([0x01, 0x02, 0x03, 0x04, 0x05, 0x06].span()); - let (u64_words, pending_word, pending_word_len) = input.to_u64_words(); - assert(pending_word == 6618611909121, 'wrong pending word'); - assert(pending_word_len == 6, 'wrong pending word length'); - assert(u64_words.len() == 0, 'wrong u64 words length'); - } - - #[test] - fn test_bytearray_to_64_words_full() { - let input = ByteArrayExTrait::from_bytes( - [0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08].span() - ); - let (u64_words, pending_word, pending_word_len) = input.to_u64_words(); - - assert(pending_word == 0, 'wrong pending word'); - assert(pending_word_len == 0, 'wrong pending word length'); - assert(u64_words.len() == 1, 'wrong u64 words length'); - assert(*u64_words[0] == 578437695752307201, 'wrong u64 words length'); - } - } - - mod span_u8_test { - use utils::helpers::{U8SpanExTrait, ToBytes}; - - #[test] - fn test_span_u8_to_64_words_partial() { - let mut input: Span = [0x01, 0x02, 0x03, 0x04, 0x05, 0x06].span(); - let (u64_words, pending_word, pending_word_len) = input.to_u64_words(); - assert(pending_word == 6618611909121, 'wrong pending word'); - assert(pending_word_len == 6, 'wrong pending word length'); - assert(u64_words.len() == 0, 'wrong u64 words length'); - } - - #[test] - fn test_span_u8_to_64_words_full() { - let mut input: Span = [0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08].span(); - let (u64_words, pending_word, pending_word_len) = input.to_u64_words(); - - assert(pending_word == 0, 'wrong pending word'); - assert(pending_word_len == 0, 'wrong pending word length'); - assert(u64_words.len() == 1, 'wrong u64 words length'); - assert(*u64_words[0] == 578437695752307201, 'wrong u64 words length'); - } - - - #[test] - fn test_compute_msg_hash() { - let msg = 0xabcdef_u32.to_be_bytes(); - let expected_hash = 0x800d501693feda2226878e1ec7869eef8919dbc5bd10c2bcd031b94d73492860; - let hash = msg.compute_keccak256_hash(); - - assert_eq!(hash, expected_hash); - } - - #[test] - fn test_right_padded_span_offset_0() { - let span = [0x0, 0x01, 0x02, 0x03, 0x04, 0x05].span(); - let expected = [0x0, 0x01, 0x02, 0x03, 0x04, 0x05, 0x0, 0x0, 0x0, 0x0].span(); - let result = span.slice_right_padded(0, 10); - - assert_eq!(result, expected); - } - - #[test] - fn test_right_padded_span_offset_4() { - let span = [0x0, 0x01, 0x02, 0x03, 0x04, 0x05].span(); - let expected = [0x04, 0x05, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0].span(); - let result = span.slice_right_padded(4, 10); - - assert_eq!(result, expected); - } - - #[test] - fn test_right_padded_span_offset_greater_than_span_len() { - let span = [0x0, 0x01, 0x02, 0x03, 0x04, 0x05].span(); - let expected = [0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0].span(); - let result = span.slice_right_padded(6, 10); - - assert_eq!(result, expected); - } - - #[test] - fn test_pad_left_with_zeroes_len_10() { - let span = [0x0, 0x01, 0x02, 0x03, 0x04, 0x05].span(); - let expected = [0x0, 0x0, 0x0, 0x0, 0x0, 0x01, 0x02, 0x03, 0x04, 0x05].span(); - let result = span.pad_left_with_zeroes(10); - - assert_eq!(result, expected); - } - - #[test] - fn test_pad_left_with_zeroes_len_equal_than_data_len() { - let span = [0x0, 0x01, 0x02, 0x03, 0x04, 0x05, 0x6, 0x7, 0x8, 0x9].span(); - let expected = [0x0, 0x01, 0x02, 0x03, 0x04, 0x05, 0x6, 0x7, 0x8, 0x9].span(); - let result = span.pad_left_with_zeroes(10); - - assert_eq!(result, expected); - } - - #[test] - fn test_pad_left_with_zeroes_len_equal_than_smaller_len() { - let span = [0x0, 0x01, 0x02, 0x03, 0x04, 0x05, 0x6, 0x7, 0x8, 0x9].span(); - let expected = [0x0, 0x01, 0x02, 0x03, 0x04, 0x05, 0x6, 0x7, 0x8].span(); - let result = span.pad_left_with_zeroes(9); - - assert_eq!(result, expected); - } - - #[test] - fn test_pad_right_with_zeroes_len_10() { - let span = [0x01, 0x02, 0x03, 0x04, 0x05].span(); - let expected = [0x01, 0x02, 0x03, 0x04, 0x05, 0x0, 0x0, 0x0, 0x0, 0x0].span(); - let result = span.pad_right_with_zeroes(10); - - assert_eq!(result, expected); - } - - #[test] - fn test_pad_right_with_zeroes_truncate() { - let span = [0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a].span(); - let expected = [0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09].span(); - let result = span.pad_right_with_zeroes(9); - - assert_eq!(result, expected); - } - - #[test] - fn test_pad_right_with_zeroes_same_length() { - let span = [0x01, 0x02, 0x03, 0x04, 0x05].span(); - let expected = [0x01, 0x02, 0x03, 0x04, 0x05].span(); - let result = span.pad_right_with_zeroes(5); - - assert_eq!(result, expected); - } - } - - - mod felt252_vec_u8_test { - use alexandria_data_structures::vec::{VecTrait, Felt252Vec}; - use utils::helpers::{Felt252VecTrait}; - - #[test] - fn test_felt252_vec_u8_to_bytes() { - let mut vec: Felt252Vec = Default::default(); - vec.push(0); - vec.push(1); - vec.push(2); - vec.push(3); - - let result = vec.to_le_bytes(); - let expected = [0, 1, 2, 3].span(); - - assert_eq!(result, expected); - } - } - - mod felt252_vec_u64_test { - use alexandria_data_structures::vec::{VecTrait, Felt252Vec}; - use utils::helpers::{Felt252VecTrait}; - - #[test] - fn test_felt252_vec_u64_words64_to_le_bytes() { - let mut vec: Felt252Vec = Default::default(); - vec.push(0); - vec.push(1); - vec.push(2); - vec.push(3); - - let result = vec.to_le_bytes(); - let expected = [0, 1, 2, 3].span(); - - assert_eq!(result, expected); - } - - #[test] - fn test_felt252_vec_u64_words64_to_be_bytes() { - let mut vec: Felt252Vec = Default::default(); - vec.push(0); - vec.push(1); - vec.push(2); - vec.push(3); - - let result = vec.to_be_bytes(); - let expected = [ - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 3, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 2, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 1, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0 - ].span(); - - assert_eq!(result, expected); - } - } - - mod felt252_vec_test { - use alexandria_data_structures::vec::{VecTrait, Felt252Vec}; - use utils::helpers::{Felt252VecTrait, Felt252VecTraitErrors}; - - #[test] - fn test_felt252_vec_expand() { - let mut vec: Felt252Vec = Default::default(); - vec.push(0); - vec.push(1); - - vec.expand(4).unwrap(); - - assert_eq!(vec.len(), 4); - assert_eq!(vec.pop().unwrap(), 0); - assert_eq!(vec.pop().unwrap(), 0); - assert_eq!(vec.pop().unwrap(), 1); - assert_eq!(vec.pop().unwrap(), 0); - } - - #[test] - fn test_felt252_vec_expand_fail() { - let mut vec: Felt252Vec = Default::default(); - vec.push(0); - vec.push(1); - - let result = vec.expand(1); - assert_eq!(result, Result::Err(Felt252VecTraitErrors::SizeLessThanCurrentLength)); - } - - #[test] - fn test_felt252_vec_reset() { - let mut vec: Felt252Vec = Default::default(); - vec.push(0); - vec.push(1); - - vec.reset(); - - assert_eq!(vec.len(), 2); - assert_eq!(vec.pop().unwrap(), 0); - assert_eq!(vec.pop().unwrap(), 0); - } - - #[test] - fn test_felt252_vec_count_leading_zeroes() { - let mut vec: Felt252Vec = Default::default(); - vec.push(0); - vec.push(0); - vec.push(0); - vec.push(1); - - let result = vec.count_leading_zeroes(); - - assert_eq!(result, 3); - } - - - #[test] - fn test_felt252_vec_resize_len_greater_than_current_len() { - let mut vec: Felt252Vec = Default::default(); - vec.push(0); - vec.push(1); - - vec.expand(4).unwrap(); - - assert_eq!(vec.len(), 4); - assert_eq!(vec.pop().unwrap(), 0); - assert_eq!(vec.pop().unwrap(), 0); - assert_eq!(vec.pop().unwrap(), 1); - assert_eq!(vec.pop().unwrap(), 0); - } - - #[test] - fn test_felt252_vec_resize_len_less_than_current_len() { - let mut vec: Felt252Vec = Default::default(); - vec.push(0); - vec.push(1); - vec.push(0); - vec.push(0); - - vec.resize(2); - - assert_eq!(vec.len(), 2); - assert_eq!(vec.pop().unwrap(), 1); - assert_eq!(vec.pop().unwrap(), 0); - } - - #[test] - fn test_felt252_vec_len_0() { - let mut vec: Felt252Vec = Default::default(); - vec.push(0); - vec.push(1); - - vec.resize(0); - - assert_eq!(vec.len(), 0); - } - - #[test] - fn test_copy_from_bytes_le_size_equal_to_vec_size() { - let mut vec: Felt252Vec = Default::default(); - vec.expand(4).unwrap(); - - let bytes = [1, 2, 3, 4].span(); - vec.copy_from_bytes_le(0, bytes).unwrap(); - - assert_eq!(vec.len(), 4); - assert_eq!(vec.pop().unwrap(), 4); - assert_eq!(vec.pop().unwrap(), 3); - assert_eq!(vec.pop().unwrap(), 2); - assert_eq!(vec.pop().unwrap(), 1); - } - - #[test] - fn test_copy_from_bytes_le_size_less_than_vec_size() { - let mut vec: Felt252Vec = Default::default(); - vec.expand(4).unwrap(); - - let bytes = [1, 2].span(); - vec.copy_from_bytes_le(2, bytes).unwrap(); - - assert_eq!(vec.len(), 4); - assert_eq!(vec.pop().unwrap(), 2); - assert_eq!(vec.pop().unwrap(), 1); - assert_eq!(vec.pop().unwrap(), 0); - assert_eq!(vec.pop().unwrap(), 0); - } - - #[test] - fn test_copy_from_bytes_le_size_greater_than_vec_size() { - let mut vec: Felt252Vec = Default::default(); - vec.expand(4).unwrap(); - - let bytes = [1, 2, 3, 4].span(); - let result = vec.copy_from_bytes_le(2, bytes); - - assert_eq!(result, Result::Err(Felt252VecTraitErrors::Overflow)); - } - - #[test] - fn test_copy_from_bytes_index_out_of_bound() { - let mut vec: Felt252Vec = Default::default(); - vec.expand(4).unwrap(); - - let bytes = [1, 2].span(); - let result = vec.copy_from_bytes_le(4, bytes); - - assert_eq!(result, Result::Err(Felt252VecTraitErrors::IndexOutOfBound)); - } - - #[test] - fn test_copy_from_vec_le() { - let mut vec: Felt252Vec = Default::default(); - vec.expand(2).unwrap(); - - let mut vec2: Felt252Vec = Default::default(); - vec2.push(1); - vec2.push(2); - - vec.copy_from_vec_le(ref vec2).unwrap(); - - assert_eq!(vec.len, 2); - assert_eq!(vec.pop().unwrap(), 2); - assert_eq!(vec.pop().unwrap(), 1); - } - - #[test] - fn test_copy_from_vec_le_not_equal_lengths() { - let mut vec: Felt252Vec = Default::default(); - vec.expand(2).unwrap(); - - let mut vec2: Felt252Vec = Default::default(); - vec2.push(1); - - let result = vec.copy_from_vec_le(ref vec2); - - assert_eq!(result, Result::Err(Felt252VecTraitErrors::LengthIsNotSame)); - } - - - #[test] - fn test_insert_vec_size_equal_to_vec_size() { - let mut vec: Felt252Vec = Default::default(); - vec.expand(2).unwrap(); - - let mut vec2: Felt252Vec = Default::default(); - vec2.push(1); - vec2.push(2); - - vec.insert_vec(0, ref vec2).unwrap(); - - assert_eq!(vec.len(), 2); - assert_eq!(vec.pop().unwrap(), 2); - assert_eq!(vec.pop().unwrap(), 1); - } - - #[test] - fn test_insert_vec_size_less_than_vec_size() { - let mut vec: Felt252Vec = Default::default(); - vec.expand(4).unwrap(); - - let mut vec2: Felt252Vec = Default::default(); - vec2.push(1); - vec2.push(2); - - vec.insert_vec(2, ref vec2).unwrap(); - - assert_eq!(vec.len(), 4); - assert_eq!(vec.pop().unwrap(), 2); - assert_eq!(vec.pop().unwrap(), 1); - assert_eq!(vec.pop().unwrap(), 0); - assert_eq!(vec.pop().unwrap(), 0); - } - - #[test] - fn test_insert_vec_size_greater_than_vec_size() { - let mut vec: Felt252Vec = Default::default(); - vec.expand(2).unwrap(); - - let mut vec2: Felt252Vec = Default::default(); - vec2.push(1); - vec2.push(2); - vec2.push(3); - vec2.push(4); - - let result = vec.insert_vec(1, ref vec2); - assert_eq!(result, Result::Err(Felt252VecTraitErrors::Overflow)); - } - - #[test] - fn test_insert_vec_index_out_of_bound() { - let mut vec: Felt252Vec = Default::default(); - vec.expand(4).unwrap(); - - let mut vec2: Felt252Vec = Default::default(); - vec2.push(1); - vec2.push(2); - - let result = vec.insert_vec(4, ref vec2); - assert_eq!(result, Result::Err(Felt252VecTraitErrors::IndexOutOfBound)); - } - - #[test] - fn test_remove_trailing_zeroes_le() { - let mut vec: Felt252Vec = Default::default(); - vec.push(1); - vec.push(2); - vec.push(0); - vec.push(0); - - vec.remove_trailing_zeroes(); - - assert_eq!(vec.len(), 2); - assert_eq!(vec.pop().unwrap(), 2); - assert_eq!(vec.pop().unwrap(), 1); - } - - #[test] - fn test_pop() { - let mut vec: Felt252Vec = Default::default(); - vec.push(1); - vec.push(2); - - assert_eq!(vec.pop().unwrap(), 2); - assert_eq!(vec.pop().unwrap(), 1); - assert_eq!(vec.pop(), Option::::None); - } - - #[test] - fn test_duplicate() { - let mut vec: Felt252Vec = Default::default(); - vec.push(1); - vec.push(2); - - let mut vec2 = vec.duplicate(); - - assert_eq!(vec.len(), vec2.len()); - assert_eq!(vec.pop(), vec2.pop()); - assert_eq!(vec.pop(), vec2.pop()); - assert_eq!(vec.pop().is_none(), true); - assert_eq!(vec2.pop().is_none(), true); - } - - #[test] - fn test_clone_slice() { - let mut vec: Felt252Vec = Default::default(); - vec.push(1); - vec.push(2); - - let mut vec2 = vec.clone_slice(1, 1); - - assert_eq!(vec2.len(), 1); - assert_eq!(vec2.pop().unwrap(), 2); - } - - #[test] - fn test_equal() { - let mut vec: Felt252Vec = Default::default(); - vec.push(1); - vec.push(2); - - let mut vec2: Felt252Vec = Default::default(); - vec2.push(1); - vec2.push(2); - - assert!(vec.equal_remove_trailing_zeroes(ref vec2)); - vec2.pop().unwrap(); - assert!(!vec.equal_remove_trailing_zeroes(ref vec2)); - } - - #[test] - fn test_fill() { - let mut vec: Felt252Vec = Default::default(); - vec.expand(4).unwrap(); - - vec.fill(1, 3, 1).unwrap(); - - assert_eq!(vec.pop().unwrap(), 1); - assert_eq!(vec.pop().unwrap(), 1); - assert_eq!(vec.pop().unwrap(), 1); - assert_eq!(vec.pop().unwrap(), 0); - } - - #[test] - fn test_fill_overflow() { - let mut vec: Felt252Vec = Default::default(); - vec.expand(4).unwrap(); - - assert_eq!(vec.fill(4, 0, 1), Result::Err(Felt252VecTraitErrors::IndexOutOfBound)); - assert_eq!(vec.fill(2, 4, 1), Result::Err(Felt252VecTraitErrors::Overflow)); - } - } } diff --git a/crates/utils/src/lib.cairo b/crates/utils/src/lib.cairo index 2817428af..9fdfc6a1f 100644 --- a/crates/utils/src/lib.cairo +++ b/crates/utils/src/lib.cairo @@ -3,6 +3,7 @@ pub mod constants; pub mod crypto; pub mod errors; pub mod eth_transaction; +pub mod felt_vec; pub mod fmt; pub mod helpers; pub mod i256; diff --git a/crates/utils/src/rlp.cairo b/crates/utils/src/rlp.cairo index 4dd6256df..70e509048 100644 --- a/crates/utils/src/rlp.cairo +++ b/crates/utils/src/rlp.cairo @@ -5,7 +5,9 @@ use core::panic_with_felt252; use core::starknet::EthAddress; use crate::errors::{RLPError}; use crate::eth_transaction::eip2930::AccessListItem; -use crate::helpers::{EthAddressExTrait, ArrayExtension, ToBytes, FromBytes}; +use crate::helpers::{EthAddressExTrait}; +use crate::traits::array::ArrayExtension; +use crate::traits::bytes::{ToBytes, FromBytes}; // Possible RLP types #[derive(Drop, PartialEq, Debug)] diff --git a/crates/utils/src/set.cairo b/crates/utils/src/set.cairo index 1348bc40b..e6d2356a4 100644 --- a/crates/utils/src/set.cairo +++ b/crates/utils/src/set.cairo @@ -1,4 +1,4 @@ -use utils::helpers::{SpanExtTrait, ArrayExtTrait}; +use crate::traits::array::{SpanExtTrait, ArrayExtTrait}; #[derive(Drop, PartialEq)] pub struct Set { diff --git a/crates/utils/src/traits.cairo b/crates/utils/src/traits.cairo index e325f72cf..88ea21aed 100644 --- a/crates/utils/src/traits.cairo +++ b/crates/utils/src/traits.cairo @@ -1,3 +1,6 @@ +pub mod array; +pub mod bytes; +pub mod integer; use core::array::SpanTrait; use core::num::traits::{Zero, One}; use core::starknet::secp256_trait::{Signature}; diff --git a/crates/utils/src/traits/array.cairo b/crates/utils/src/traits/array.cairo new file mode 100644 index 000000000..4001bbd22 --- /dev/null +++ b/crates/utils/src/traits/array.cairo @@ -0,0 +1,98 @@ +#[generate_trait] +pub impl ArrayExtension> of ArrayExtTrait { + // Concatenates two arrays by adding the elements of arr2 to arr1. + fn concat<+Copy>(ref self: Array, mut arr2: Span) { + for elem in arr2 { + self.append(*elem); + }; + } + + /// Reverses an array + fn reverse<+Copy>(self: Span) -> Array { + let mut counter = self.len(); + let mut dst: Array = ArrayTrait::new(); + while counter != 0 { + dst.append(*self[counter - 1]); + counter -= 1; + }; + dst + } + + // Appends n time value to the Array + fn append_n<+Copy>(ref self: Array, value: T, mut n: usize) { + while n != 0 { + self.append(value); + + n -= 1; + }; + } + + // Appends an item only if it is not already in the array. + fn append_unique<+Copy, +PartialEq>(ref self: Array, value: T) { + if self.span().contains(value) { + return (); + } + self.append(value); + } + + // Concatenates two arrays by adding the elements of arr2 to arr1. + fn concat_unique<+Copy, +PartialEq>(ref self: Array, mut arr2: Span) { + for elem in arr2 { + self.append_unique(*elem) + }; + } +} + +#[generate_trait] +pub impl SpanExtension, +Drop> of SpanExtTrait { + // Returns true if the array contains an item. + fn contains<+PartialEq>(mut self: Span, value: T) -> bool { + let mut result = false; + for elem in self { + if *elem == value { + result = true; + } + }; + result + } + + // Returns the index of an item in the array. + fn index_of<+PartialEq>(mut self: Span, value: T) -> Option { + let mut i = 0; + let mut result = Option::None; + for elem in self { + if *elem == value { + result = Option::Some(i); + } + i += 1; + }; + return result; + } +} + +#[cfg(test)] +mod tests { + mod test_array_ext { + use super::super::{ArrayExtTrait}; + #[test] + fn test_append_n() { + // Given + let mut original: Array = array![1, 2, 3, 4]; + + // When + original.append_n(9, 3); + + // Then + assert(original == array![1, 2, 3, 4, 9, 9, 9], 'append_n failed'); + } + + #[test] + fn test_append_unique() { + let mut arr = array![1, 2, 3]; + arr.append_unique(4); + assert(arr == array![1, 2, 3, 4], 'should have appended'); + arr.append_unique(2); + assert(arr == array![1, 2, 3, 4], 'shouldnt have appended'); + } + } +} diff --git a/crates/utils/src/traits/bytes.cairo b/crates/utils/src/traits/bytes.cairo new file mode 100644 index 000000000..80dca60f5 --- /dev/null +++ b/crates/utils/src/traits/bytes.cairo @@ -0,0 +1,815 @@ +use core::cmp::min; +use core::keccak::{cairo_keccak}; +use core::num::traits::{Zero, One, Bounded, BitSize, SaturatingAdd}; +use core::traits::{BitAnd}; +use crate::math::{Bitshift}; +use crate::traits::integer::{BytesUsedTrait, ByteSize, U256Trait}; +use utils::constants::{POW_2, POW_256_1, POW_256_REV}; + +#[generate_trait] +pub impl U8SpanExImpl of U8SpanExTrait { + // keccack256 on a bytes message + fn compute_keccak256_hash(self: Span) -> u256 { + let (mut keccak_input, last_input_word, last_input_num_bytes) = self.to_u64_words(); + let hash = cairo_keccak(ref keccak_input, :last_input_word, :last_input_num_bytes) + .reverse_endianness(); + + hash + } + /// Transforms a Span into an Array of u64 full words, a pending u64 word and its length in + /// bytes + fn to_u64_words(self: Span) -> (Array, u64, usize) { + let nonzero_8: NonZero = 8_u32.try_into().unwrap(); + let (full_u64_word_count, last_input_num_bytes) = DivRem::div_rem(self.len(), nonzero_8); + + let mut u64_words: Array = Default::default(); + let mut byte_counter: u8 = 0; + let mut pending_word: u64 = 0; + let mut u64_word_counter: usize = 0; + + while u64_word_counter != full_u64_word_count { + if byte_counter == 8 { + u64_words.append(pending_word); + byte_counter = 0; + pending_word = 0; + u64_word_counter += 1; + } + pending_word += match self.get(u64_word_counter * 8 + byte_counter.into()) { + Option::Some(byte) => { + let byte: u64 = (*byte.unbox()).into(); + // Accumulate pending_word in a little endian manner + byte.shl(8_u64 * byte_counter.into()) + }, + Option::None => { break; }, + }; + byte_counter += 1; + }; + + // Fill the last input word + let mut last_input_word: u64 = 0; + let mut byte_counter: u8 = 0; + + // We enter a second loop for clarity. + // O(2n) should be okay + // We might want to regroup every computation into a single loop with appropriate `if` + // branching For optimisation + while byte_counter.into() != last_input_num_bytes { + last_input_word += match self.get(full_u64_word_count * 8 + byte_counter.into()) { + Option::Some(byte) => { + let byte: u64 = (*byte.unbox()).into(); + byte.shl(8_u64 * byte_counter.into()) + }, + Option::None => { break; }, + }; + byte_counter += 1; + }; + + (u64_words, last_input_word, last_input_num_bytes) + } + + /// Returns right padded slice of the span, starting from index offset + /// If offset is greater than the span length, returns an empty span + /// # Examples + /// + /// ``` + /// let span = [0x0, 0x01, 0x02, 0x03, 0x04, 0x05].span(); + /// let expected = [0x04, 0x05, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0].span(); + /// let result = span.slice_right_padded(4, 10); + /// assert_eq!(result, expected); + /// ``` + /// # Arguments + /// * `offset` - The offset to start the slice from + /// * `len` - The length of the slice + /// + /// # Returns + /// * A span of length `len` starting from `offset` right padded with 0s if `offset` is greater + /// than the span length, returns an empty span of length `len` if offset is grearter than the + /// span length + fn slice_right_padded(self: Span, offset: usize, len: usize) -> Span { + let start = if offset <= self.len() { + offset + } else { + self.len() + }; + + let end = min(start.saturating_add(len), self.len()); + + let slice = self.slice(start, end - start); + // Save appending to span for this case as it is more efficient to just return the slice + if slice.len() == len { + return slice; + } + + // Copy the span + let mut arr = array![]; + arr.append_span(slice); + + while arr.len() != len { + arr.append(0); + }; + + arr.span() + } + + /// Clones and pads the given span with 0s to the right to the given length, if data is more + /// than the given length, it is truncated from the right side + /// # Examples + /// ``` + /// let span = [0x01, 0x02, 0x03, 0x04, 0x05].span(); + /// let expected = [0x01, 0x02, 0x03, 0x04, 0x05, 0x0, 0x0, 0x0, 0x0, 0x0].span(); + /// let result = span.pad_right_with_zeroes(10); + /// + /// assert_eq!(result, expected); + /// + /// // Truncates the data if it is more than the given length + /// let span = [0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a].span(); + /// let expected = [0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09].span(); + /// let result = span.pad_right_with_zeroes(9); + /// + /// assert_eq!(result, expected); + /// ``` + /// # Arguments + /// * `len` - The length of the padded span + /// + /// # Returns + /// * A span of length `len` right padded with 0s if the span length is less than `len`, returns + /// a span of length `len` if the span length is greater than `len` then the data is truncated + /// from the right side + fn pad_right_with_zeroes(self: Span, len: usize) -> Span { + if self.len() >= len { + return self.slice(0, len); + } + + // Create a new array with the original data + let mut arr = array![]; + for i in self { + arr.append(*i); + }; + + // Pad with zeroes + while arr.len() != len { + arr.append(0); + }; + + arr.span() + } + + + /// Clones and pads the given span with 0s to the given length, if data is more than the given + /// length, it is truncated from the right side # Examples + /// ``` + /// let span = [0x0, 0x01, 0x02, 0x03, 0x04, 0x05].span(); + /// let expected = [0x0, 0x0, 0x0, 0x0, 0x0, 0x01, 0x02, 0x03, 0x04, 0x05].span(); + /// let result = span.pad_left_with_zeroes(10); + /// + /// assert_eq!(result, expected); + /// + /// // Truncates the data if it is more than the given length + /// let span = [0x0, 0x01, 0x02, 0x03, 0x04, 0x05, 0x6, 0x7, 0x8, 0x9].span(); + /// let expected = [0x0, 0x01, 0x02, 0x03, 0x04, 0x05, 0x6, 0x7, 0x8].span(); + /// let result = span.pad_left_with_zeroes(9); + /// + /// assert_eq!(result, expected); + /// ``` + /// # Arguments + /// * `len` - The length of the padded span + /// + /// # Returns + /// * A span of length `len` left padded with 0s if the span length is less than `len`, returns + /// a span of length `len` if the span length is greater than `len` then the data is truncated + /// from the right side + fn pad_left_with_zeroes(self: Span, len: usize) -> Span { + if self.len() >= len { + return self.slice(0, len); + } + + // left pad with 0 + let mut arr = array![]; + while arr.len() != (len - self.len()) { + arr.append(0); + }; + + // append the data + let mut i = 0; + while i != self.len() { + arr.append(*self[i]); + i += 1; + }; + + arr.span() + } +} + +pub trait ToBytes { + /// Unpacks a type T into a span of big endian bytes + /// + /// # Arguments + /// * `self` a value of type T. + /// + /// # Returns + /// * The bytes representation of the value in big endian. + fn to_be_bytes(self: T) -> Span; + /// Unpacks a type T into a span of big endian bytes, padded to the byte size of T + /// + /// # Arguments + /// * `self` a value of type T. + /// + /// # Returns + /// * The bytesrepresentation of the value in big endian padded to the byte size of T. + fn to_be_bytes_padded(self: T) -> Span; + /// Unpacks a type T into a span of little endian bytes + /// + /// # Arguments + /// * `self` a value of type T. + /// + /// # Returns + /// * The bytes representation of the value in little endian. + fn to_le_bytes(self: T) -> Span; + /// Unpacks a type T into a span of little endian bytes, padded to the byte size of T + /// + /// # Arguments + /// * `self` a value of type T. + /// + /// # Returns + /// * The bytesrepresentation of the value in little endian padded to the byte size of T. + fn to_le_bytes_padded(self: T) -> Span; +} + +pub impl ToBytesImpl< + T, + +Zero, + +One, + +Add, + +Sub, + +Mul, + +BitAnd, + +Bitshift, + +BitSize, + +BytesUsedTrait, + +Into, + +TryInto, + +Copy, + +Drop, + +core::ops::AddAssign, + +PartialEq +> of ToBytes { + fn to_be_bytes(self: T) -> Span { + let bytes_used = self.bytes_used(); + + let one = One::::one(); + let two = one + one; + let eight = two * two * two; + + // 0xFF + let mask = Bounded::::MAX.into(); + + let mut bytes: Array = Default::default(); + let mut i: u8 = 0; + while i != bytes_used { + let val = Bitshift::::shr(self, eight * (bytes_used - i - 1).into()); + bytes.append((val & mask).try_into().unwrap()); + i += 1; + }; + + bytes.span() + } + + fn to_be_bytes_padded(mut self: T) -> Span { + let padding = (BitSize::::bits() / 8); + self.to_be_bytes().pad_left_with_zeroes(padding) + } + + fn to_le_bytes(mut self: T) -> Span { + let bytes_used = self.bytes_used(); + let one = One::::one(); + let two = one + one; + let eight = two * two * two; + + // 0xFF + let mask = Bounded::::MAX.into(); + + let mut bytes: Array = Default::default(); + + let mut i: u8 = 0; + while i != bytes_used { + let val = self.shr(eight * i.into()); + bytes.append((val & mask).try_into().unwrap()); + i += 1; + }; + + bytes.span() + } + + fn to_le_bytes_padded(mut self: T) -> Span { + let padding = (BitSize::::bits() / 8); + self.to_le_bytes().slice_right_padded(0, padding) + } +} + +pub trait FromBytes { + /// Parses a span of big endian bytes into a type T + /// + /// # Arguments + /// * `self` a span of big endian bytes. + /// + /// # Returns + /// * The Option::(value) represented by the bytes in big endian, Option::None if the span is + /// not the byte size of T. + fn from_be_bytes(self: Span) -> Option; + + /// Parses a span of big endian bytes into a type T, allowing for partial input + /// + /// # Arguments + /// * `self` a span of big endian bytes. + /// + /// # Returns + /// * The Option::(value) represented by the bytes in big endian, Option::None if the span is + /// longer than the byte size of T. + fn from_be_bytes_partial(self: Span) -> Option; + + + /// Parses a span of little endian bytes into a type T + /// + /// # Arguments + /// * `self` a span of little endian bytes. + /// + /// # Returns + /// * The Option::(value) represented by the bytes in little endian, Option::None if the span is + /// not the byte size of T. + fn from_le_bytes(self: Span) -> Option; + + /// Parses a span of little endian bytes into a type T, allowing for partial input + /// + /// # Arguments + /// * `self` a span of little endian bytes. + /// + /// # Returns + /// * The Option::(value) represented by the bytes in little endian, Option::None if the span is + /// longer than the byte size of T. + fn from_le_bytes_partial(self: Span) -> Option; +} + +pub impl FromBytesImpl< + T, + +Zero, + +One, + +Add, + +Sub, + +Mul, + +BitAnd, + +Bitshift, + +ByteSize, + +BytesUsedTrait, + +Into, + +Into, + +TryInto, + +Copy, + +Drop, + +core::ops::AddAssign, + +PartialEq +> of FromBytes { + fn from_be_bytes(self: Span) -> Option { + let byte_size = ByteSize::::byte_size(); + + if self.len() != byte_size { + return Option::None; + } + + let mut result: T = Zero::zero(); + for byte in self { + let tmp = result * 256_u16.into(); + result = tmp + (*byte).into(); + }; + Option::Some(result) + } + + fn from_be_bytes_partial(self: Span) -> Option { + let byte_size = ByteSize::::byte_size(); + + if self.len() > byte_size { + return Option::None; + } + + let mut result: T = Zero::zero(); + for byte in self { + let tmp = result * 256_u16.into(); + result = tmp + (*byte).into(); + }; + + Option::Some(result) + } + + fn from_le_bytes(self: Span) -> Option { + let byte_size = ByteSize::::byte_size(); + + if self.len() != byte_size { + return Option::None; + } + + let mut result: T = Zero::zero(); + let mut i = self.len(); + while i != 0 { + i -= 1; + let tmp = result * 256_u16.into(); + result = tmp + (*self[i]).into(); + }; + Option::Some(result) + } + + fn from_le_bytes_partial(self: Span) -> Option { + let byte_size = ByteSize::::byte_size(); + + if self.len() > byte_size { + return Option::None; + } + + let mut result: T = Zero::zero(); + let mut i = self.len(); + while i != 0 { + i -= 1; + let tmp = result * 256_u16.into(); + result = tmp + (*self[i]).into(); + }; + Option::Some(result) + } +} + + +#[generate_trait] +pub impl ByteArrayExt of ByteArrayExTrait { + fn append_span_bytes(ref self: ByteArray, mut bytes: Span) { + for val in bytes { + self.append_byte(*val); + }; + } + + fn from_bytes(mut bytes: Span) -> ByteArray { + let mut arr: ByteArray = Default::default(); + let (nb_full_words, pending_word_len) = DivRem::div_rem( + bytes.len(), 31_u32.try_into().unwrap() + ); + let mut i = 0; + while i != nb_full_words { + let mut word: felt252 = 0; + let mut j = 0; + while j != 31 { + word = word * POW_256_1.into() + (*bytes.pop_front().unwrap()).into(); + j += 1; + }; + arr.append_word(word.try_into().unwrap(), 31); + i += 1; + }; + + if pending_word_len == 0 { + return arr; + }; + + let mut pending_word: felt252 = 0; + let mut i = 0; + + while i != pending_word_len { + pending_word = pending_word * POW_256_1.into() + (*bytes.pop_front().unwrap()).into(); + i += 1; + }; + arr.append_word(pending_word.try_into().unwrap(), pending_word_len); + arr + } + + fn is_empty(self: @ByteArray) -> bool { + self.len() == 0 + } + + fn into_bytes(self: ByteArray) -> Span { + let mut output: Array = Default::default(); + let len = self.len(); + let mut i = 0; + while i != len { + output.append(self[i]); + i += 1; + }; + output.span() + } + + + /// Transforms a ByteArray into an Array of u64 full words, a pending u64 word and its length in + /// bytes + fn to_u64_words(self: ByteArray) -> (Array, u64, usize) { + // We pass it by value because we want to take ownership, but we snap it + // because `at` takes a snap and if this snap is automatically done by + // the compiler in the loop, it won't compile + let self = @self; + let nonzero_8: NonZero = 8_u32.try_into().unwrap(); + let (full_u64_word_count, last_input_num_bytes) = DivRem::div_rem(self.len(), nonzero_8); + + let mut u64_words: Array = Default::default(); + let mut byte_counter: u8 = 0; + let mut pending_word: u64 = 0; + let mut u64_word_counter: usize = 0; + + while u64_word_counter != full_u64_word_count { + if byte_counter == 8 { + u64_words.append(pending_word); + byte_counter = 0; + pending_word = 0; + u64_word_counter += 1; + } + pending_word += match self.at(u64_word_counter * 8 + byte_counter.into()) { + Option::Some(byte) => { + let byte: u64 = byte.into(); + // Accumulate pending_word in a little endian manner + byte.shl(8_u64 * byte_counter.into()) + }, + Option::None => { break; }, + }; + byte_counter += 1; + }; + + // Fill the last input word + let mut last_input_word: u64 = 0; + let mut byte_counter: u8 = 0; + + // We enter a second loop for clarity. + // O(2n) should be okay + // We might want to regroup every computation into a single loop with appropriate `if` + // branching For optimisation + while byte_counter.into() != last_input_num_bytes { + last_input_word += match self.at(full_u64_word_count * 8 + byte_counter.into()) { + Option::Some(byte) => { + let byte: u64 = byte.into(); + byte.shl(8_u64 * byte_counter.into()) + }, + Option::None => { break; }, + }; + byte_counter += 1; + }; + + (u64_words, last_input_word, last_input_num_bytes) + } +} + + +#[cfg(test)] +mod tests { + mod bytearray_test { + use super::super::{ByteArrayExTrait}; + #[test] + fn test_pack_bytes_ge_bytes31() { + let mut arr = array![ + 0x01, + 0x02, + 0x03, + 0x04, + 0x05, + 0x06, + 0x07, + 0x08, + 0x09, + 0x0A, + 0x0B, + 0x0C, + 0x0D, + 0x0E, + 0x0F, + 0x10, + 0x11, + 0x12, + 0x13, + 0x14, + 0x15, + 0x16, + 0x17, + 0x18, + 0x19, + 0x1A, + 0x1B, + 0x1C, + 0x1D, + 0x1E, + 0x1F, + 0x20, + 0x21 // 33 elements + ]; + + let res = ByteArrayExTrait::from_bytes(arr.span()); + + // Ensure that the result is complete and keeps the same order + let mut i = 0; + while i != arr.len() { + assert(*arr[i] == res[i], 'byte mismatch'); + i += 1; + }; + } + + #[test] + fn test_bytearray_append_span_bytes() { + let bytes = array![0x01, 0x02, 0x03, 0x04]; + let mut byte_arr: ByteArray = Default::default(); + byte_arr.append_byte(0xFF); + byte_arr.append_byte(0xAA); + byte_arr.append_span_bytes(bytes.span()); + assert_eq!(byte_arr.len(), 6); + assert_eq!(byte_arr[0], 0xFF); + assert_eq!(byte_arr[1], 0xAA); + assert_eq!(byte_arr[2], 0x01); + assert_eq!(byte_arr[3], 0x02); + assert_eq!(byte_arr[4], 0x03); + assert_eq!(byte_arr[5], 0x04); + } + + #[test] + fn test_byte_array_into_bytes() { + let input = array![ + 0x01, + 0x02, + 0x03, + 0x04, + 0x05, + 0x06, + 0x07, + 0x08, + 0x09, + 0x0A, + 0x0B, + 0x0C, + 0x0D, + 0x0E, + 0x0F, + 0x10, + 0x11, + 0x12, + 0x13, + 0x14, + 0x15, + 0x16, + 0x17, + 0x18, + 0x19, + 0x1A, + 0x1B, + 0x1C, + 0x1D, + 0x1E, + 0x1F, + 0x20, + 0x21 // 33 elements + ]; + let byte_array = ByteArrayExTrait::from_bytes(input.span()); + let res = byte_array.into_bytes(); + + // Ensure that the elements are correct + assert(res == input.span(), 'bytes mismatch'); + } + + #[test] + fn test_pack_bytes_le_bytes31() { + let mut arr = array![0x11, 0x22, 0x33, 0x44]; + let res = ByteArrayExTrait::from_bytes(arr.span()); + + // Ensure that the result is complete and keeps the same order + let mut i = 0; + while i != arr.len() { + assert(*arr[i] == res[i], 'byte mismatch'); + i += 1; + }; + } + + + #[test] + fn test_bytearray_to_64_words_partial() { + let input = ByteArrayExTrait::from_bytes([0x01, 0x02, 0x03, 0x04, 0x05, 0x06].span()); + let (u64_words, pending_word, pending_word_len) = input.to_u64_words(); + assert(pending_word == 6618611909121, 'wrong pending word'); + assert(pending_word_len == 6, 'wrong pending word length'); + assert(u64_words.len() == 0, 'wrong u64 words length'); + } + + #[test] + fn test_bytearray_to_64_words_full() { + let input = ByteArrayExTrait::from_bytes( + [0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08].span() + ); + let (u64_words, pending_word, pending_word_len) = input.to_u64_words(); + + assert(pending_word == 0, 'wrong pending word'); + assert(pending_word_len == 0, 'wrong pending word length'); + assert(u64_words.len() == 1, 'wrong u64 words length'); + assert(*u64_words[0] == 578437695752307201, 'wrong u64 words length'); + } + } + + + mod span_u8_test { + use super::super::{U8SpanExTrait, ToBytes}; + + #[test] + fn test_span_u8_to_64_words_partial() { + let mut input: Span = [0x01, 0x02, 0x03, 0x04, 0x05, 0x06].span(); + let (u64_words, pending_word, pending_word_len) = input.to_u64_words(); + assert(pending_word == 6618611909121, 'wrong pending word'); + assert(pending_word_len == 6, 'wrong pending word length'); + assert(u64_words.len() == 0, 'wrong u64 words length'); + } + + #[test] + fn test_span_u8_to_64_words_full() { + let mut input: Span = [0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08].span(); + let (u64_words, pending_word, pending_word_len) = input.to_u64_words(); + + assert(pending_word == 0, 'wrong pending word'); + assert(pending_word_len == 0, 'wrong pending word length'); + assert(u64_words.len() == 1, 'wrong u64 words length'); + assert(*u64_words[0] == 578437695752307201, 'wrong u64 words length'); + } + + + #[test] + fn test_compute_msg_hash() { + let msg = 0xabcdef_u32.to_be_bytes(); + let expected_hash = 0x800d501693feda2226878e1ec7869eef8919dbc5bd10c2bcd031b94d73492860; + let hash = msg.compute_keccak256_hash(); + + assert_eq!(hash, expected_hash); + } + + #[test] + fn test_right_padded_span_offset_0() { + let span = [0x0, 0x01, 0x02, 0x03, 0x04, 0x05].span(); + let expected = [0x0, 0x01, 0x02, 0x03, 0x04, 0x05, 0x0, 0x0, 0x0, 0x0].span(); + let result = span.slice_right_padded(0, 10); + + assert_eq!(result, expected); + } + + #[test] + fn test_right_padded_span_offset_4() { + let span = [0x0, 0x01, 0x02, 0x03, 0x04, 0x05].span(); + let expected = [0x04, 0x05, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0].span(); + let result = span.slice_right_padded(4, 10); + + assert_eq!(result, expected); + } + + #[test] + fn test_right_padded_span_offset_greater_than_span_len() { + let span = [0x0, 0x01, 0x02, 0x03, 0x04, 0x05].span(); + let expected = [0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0].span(); + let result = span.slice_right_padded(6, 10); + + assert_eq!(result, expected); + } + + #[test] + fn test_pad_left_with_zeroes_len_10() { + let span = [0x0, 0x01, 0x02, 0x03, 0x04, 0x05].span(); + let expected = [0x0, 0x0, 0x0, 0x0, 0x0, 0x01, 0x02, 0x03, 0x04, 0x05].span(); + let result = span.pad_left_with_zeroes(10); + + assert_eq!(result, expected); + } + + #[test] + fn test_pad_left_with_zeroes_len_equal_than_data_len() { + let span = [0x0, 0x01, 0x02, 0x03, 0x04, 0x05, 0x6, 0x7, 0x8, 0x9].span(); + let expected = [0x0, 0x01, 0x02, 0x03, 0x04, 0x05, 0x6, 0x7, 0x8, 0x9].span(); + let result = span.pad_left_with_zeroes(10); + + assert_eq!(result, expected); + } + + #[test] + fn test_pad_left_with_zeroes_len_equal_than_smaller_len() { + let span = [0x0, 0x01, 0x02, 0x03, 0x04, 0x05, 0x6, 0x7, 0x8, 0x9].span(); + let expected = [0x0, 0x01, 0x02, 0x03, 0x04, 0x05, 0x6, 0x7, 0x8].span(); + let result = span.pad_left_with_zeroes(9); + + assert_eq!(result, expected); + } + + #[test] + fn test_pad_right_with_zeroes_len_10() { + let span = [0x01, 0x02, 0x03, 0x04, 0x05].span(); + let expected = [0x01, 0x02, 0x03, 0x04, 0x05, 0x0, 0x0, 0x0, 0x0, 0x0].span(); + let result = span.pad_right_with_zeroes(10); + + assert_eq!(result, expected); + } + + #[test] + fn test_pad_right_with_zeroes_truncate() { + let span = [0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a].span(); + let expected = [0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09].span(); + let result = span.pad_right_with_zeroes(9); + + assert_eq!(result, expected); + } + + #[test] + fn test_pad_right_with_zeroes_same_length() { + let span = [0x01, 0x02, 0x03, 0x04, 0x05].span(); + let expected = [0x01, 0x02, 0x03, 0x04, 0x05].span(); + let result = span.pad_right_with_zeroes(5); + + assert_eq!(result, expected); + } + } +} diff --git a/crates/utils/src/traits/integer.cairo b/crates/utils/src/traits/integer.cairo new file mode 100644 index 000000000..9ee504f46 --- /dev/null +++ b/crates/utils/src/traits/integer.cairo @@ -0,0 +1,671 @@ +use core::integer::{u128_byte_reverse}; +use core::num::traits::{Zero, One, Bounded, BitSize}; +use crate::helpers::{u128_split}; +use crate::math::{Bitshift}; + +#[generate_trait] +pub impl U64Impl of U64Trait { + /// Returns the number of trailing zeroes in the bit representation of `self`. + /// # Arguments + /// * `self` a `u64` value. + /// # Returns + /// * The number of trailing zeroes in the bit representation of `self`. + fn count_trailing_zeroes(self: u64) -> u8 { + let mut count = 0; + + if self == 0 { + return 64; // If n is 0, all 64 bits are zeros + }; + + let mut mask = 1; + + while (self & mask) == 0 { + count += 1; + mask *= 2; + }; + + count + } +} + + +#[generate_trait] +pub impl U128Impl of U128Trait { + /// Returns the Least significant 64 bits of a u128 + fn as_u64(self: u128) -> u64 { + let (_, bottom_word) = u128_split(self); + bottom_word + } +} + +#[generate_trait] +pub impl U256Impl of U256Trait { + /// Splits an u256 into 4 little endian u64. + /// Returns ((high_high, high_low),(low_high, low_low)) + fn split_into_u64_le(self: u256) -> ((u64, u64), (u64, u64)) { + let low_le = u128_byte_reverse(self.low); + let high_le = u128_byte_reverse(self.high); + (u128_split(high_le), u128_split(low_le)) + } + + /// Reverse the endianness of an u256 + fn reverse_endianness(self: u256) -> u256 { + let new_low = u128_byte_reverse(self.high); + let new_high = u128_byte_reverse(self.low); + u256 { low: new_low, high: new_high } + } +} + +pub trait BytesUsedTrait { + /// Returns the number of bytes used to represent a `T` value. + /// # Arguments + /// * `self` - The value to check. + /// # Returns + /// The number of bytes used to represent the value. + fn bytes_used(self: T) -> u8; +} + +pub impl U8BytesUsedTraitImpl of BytesUsedTrait { + fn bytes_used(self: u8) -> u8 { + if self == 0 { + return 0; + } + + return 1; + } +} + +pub impl USizeBytesUsedTraitImpl of BytesUsedTrait { + fn bytes_used(self: usize) -> u8 { + if self < 0x10000 { // 256^2 + if self < 0x100 { // 256^1 + if self == 0 { + return 0; + } else { + return 1; + }; + } + return 2; + } else { + if self < 0x1000000 { // 256^3 + return 3; + } + return 4; + } + } +} + +pub impl U64BytesUsedTraitImpl of BytesUsedTrait { + fn bytes_used(self: u64) -> u8 { + if self <= Bounded::::MAX.into() { // 256^4 + return BytesUsedTrait::::bytes_used(self.try_into().unwrap()); + } else { + if self < 0x1000000000000 { // 256^6 + if self < 0x10000000000 { + if self < 0x100000000 { + return 4; + } + return 5; + } + return 6; + } else { + if self < 0x100000000000000 { // 256^7 + return 7; + } else { + return 8; + } + } + } + } +} + +pub impl U128BytesTraitUsedImpl of BytesUsedTrait { + fn bytes_used(self: u128) -> u8 { + let (u64high, u64low) = u128_split(self); + if u64high == 0 { + return BytesUsedTrait::::bytes_used(u64low.try_into().unwrap()); + } else { + return BytesUsedTrait::::bytes_used(u64high.try_into().unwrap()) + 8; + } + } +} + +pub impl U256BytesUsedTraitImpl of BytesUsedTrait { + fn bytes_used(self: u256) -> u8 { + if self.high == 0 { + return BytesUsedTrait::::bytes_used(self.low.try_into().unwrap()); + } else { + return BytesUsedTrait::::bytes_used(self.high.try_into().unwrap()) + 16; + } + } +} + +pub trait ByteSize { + fn byte_size() -> usize; +} + +pub impl ByteSizeImpl> of ByteSize { + fn byte_size() -> usize { + BitSize::::bits() / 8 + } +} + +pub trait BitsUsed { + /// Returns the number of bits required to represent `self`, ignoring leading zeros. + /// # Arguments + /// `self` - The value to check. + /// # Returns + /// The number of bits used to represent the value, ignoring leading zeros. + fn bits_used(self: T) -> u32; + + /// Returns the number of leading zeroes in the bit representation of `self`. + /// # Arguments + /// `self` - The value to check. + /// # Returns + /// The number of leading zeroes in the bit representation of `self`. + fn count_leading_zeroes(self: T) -> u32; +} + +pub impl BitsUsedImpl< + T, + +Zero, + +One, + +Add, + +Sub, + +Mul, + +Bitshift, + +BitSize, + +BytesUsedTrait, + +Into, + +TryInto, + +Copy, + +Drop, + +PartialEq +> of BitsUsed { + fn bits_used(self: T) -> u32 { + if self == Zero::zero() { + return 0; + } + let two: T = One::one() + One::one(); + let eight: T = two * two * two; + + let bytes_used = self.bytes_used(); + let last_byte = self.shr(eight * (bytes_used.into() - One::one())); + + // safe unwrap since we know atmost 8 bits are used + let bits_used: u8 = bits_used_internal::bits_used_in_byte(last_byte.try_into().unwrap()); + + bits_used.into() + 8 * (bytes_used - 1).into() + } + + fn count_leading_zeroes(self: T) -> u32 { + BitSize::::bits() - self.bits_used() + } +} + +pub(crate) mod bits_used_internal { + /// Returns the number of bits used to represent the value in binary representation + /// # Arguments + /// * `self` - The value to compute the number of bits used + /// # Returns + /// * The number of bits used to represent the value in binary representation + pub(crate) fn bits_used_in_byte(self: u8) -> u8 { + if self < 0b100000 { + if self < 0b1000 { + if self < 0b100 { + if self < 0b10 { + if self == 0 { + return 0; + } else { + return 1; + }; + } + return 2; + } + + return 3; + } + + if self < 0b10000 { + return 4; + } + + return 5; + } else { + if self < 0b10000000 { + if self < 0b1000000 { + return 6; + } + return 7; + } + return 8; + } + } +} + +#[cfg(test)] +mod tests { + mod u8_test { + use crate::math::Bitshift; + use super::super::BitsUsed; + + #[test] + fn test_bits_used() { + assert_eq!(0x00_u8.bits_used(), 0); + let mut value: u8 = 0xff; + let mut i = 8; + loop { + assert_eq!(value.bits_used(), i); + if i == 0 { + break; + }; + value = value.shr(1); + + i -= 1; + }; + } + } + + mod u32_test { + use crate::math::Bitshift; + use crate::traits::bytes::{ToBytes, FromBytes}; + use super::super::{BytesUsedTrait}; + + #[test] + fn test_u32_from_be_bytes() { + let input: Array = array![0xf4, 0x32, 0x15, 0x62]; + let res: Option = input.span().from_be_bytes(); + + assert!(res.is_some()); + assert_eq!(res.unwrap(), 0xf4321562); + } + + #[test] + fn test_u32_from_be_bytes_too_big_should_return_none() { + let input: Array = array![0xf4, 0x32, 0x15, 0x62, 0x01]; + let res: Option = input.span().from_be_bytes(); + + assert!(res.is_none()); + } + + #[test] + fn test_u32_from_be_bytes_too_small_should_return_none() { + let input: Array = array![0xf4, 0x32, 0x15]; + let res: Option = input.span().from_be_bytes(); + + assert!(res.is_none()); + } + + #[test] + fn test_u32_from_be_bytes_partial_full() { + let input: Array = array![0xf4, 0x32, 0x15, 0x62]; + let res: Option = input.span().from_be_bytes_partial(); + + assert!(res.is_some()); + assert_eq!(res.unwrap(), 0xf4321562); + } + + #[test] + fn test_u32_from_be_bytes_partial_smaller_input() { + let input: Array = array![0xf4, 0x32, 0x15]; + let res: Option = input.span().from_be_bytes_partial(); + + assert!(res.is_some()); + assert_eq!(res.unwrap(), 0xf43215); + } + + #[test] + fn test_u32_from_be_bytes_partial_single_byte() { + let input: Array = array![0xf4]; + let res: Option = input.span().from_be_bytes_partial(); + + assert!(res.is_some()); + assert_eq!(res.unwrap(), 0xf4); + } + + #[test] + fn test_u32_from_be_bytes_partial_empty_input() { + let input: Array = array![]; + let res: Option = input.span().from_be_bytes_partial(); + + assert!(res.is_some()); + assert_eq!(res.unwrap(), 0); + } + + #[test] + fn test_u32_from_be_bytes_partial_too_big_input() { + let input: Array = array![0xf4, 0x32, 0x15, 0x62, 0x01]; + let res: Option = input.span().from_be_bytes_partial(); + + assert!(res.is_none()); + } + + #[test] + fn test_u32_from_le_bytes() { + let input: Array = array![0x62, 0x15, 0x32, 0xf4]; + let res: Option = input.span().from_le_bytes(); + + assert!(res.is_some()); + assert_eq!(res.unwrap(), 0xf4321562); + } + + #[test] + fn test_u32_from_le_bytes_too_big() { + let input: Array = array![0x62, 0x15, 0x32, 0xf4, 0x01]; + let res: Option = input.span().from_le_bytes(); + + assert!(res.is_none()); + } + + #[test] + fn test_u32_from_le_bytes_too_small() { + let input: Array = array![0x62, 0x15, 0x32]; + let res: Option = input.span().from_le_bytes(); + + assert!(res.is_none()); + } + + #[test] + fn test_u32_from_le_bytes_zero() { + let input: Array = array![0x00, 0x00, 0x00, 0x00]; + let res: Option = input.span().from_le_bytes(); + + assert!(res.is_some()); + assert_eq!(res.unwrap(), 0); + } + + #[test] + fn test_u32_from_le_bytes_max() { + let input: Array = array![0xff, 0xff, 0xff, 0xff]; + let res: Option = input.span().from_le_bytes(); + + assert!(res.is_some()); + assert_eq!(res.unwrap(), 0xffffffff); + } + + #[test] + fn test_u32_from_le_bytes_partial() { + let input: Array = array![0x62, 0x15, 0x32]; + let res: Option = input.span().from_le_bytes_partial(); + + assert!(res.is_some()); + assert_eq!(res.unwrap(), 0x321562); + } + + #[test] + fn test_u32_from_le_bytes_partial_full() { + let input: Array = array![0x62, 0x15, 0x32, 0xf4]; + let res: Option = input.span().from_le_bytes_partial(); + + assert!(res.is_some()); + assert_eq!(res.unwrap(), 0xf4321562); + } + + #[test] + fn test_u32_from_le_bytes_partial_too_big() { + let input: Array = array![0x62, 0x15, 0x32, 0xf4, 0x01]; + let res: Option = input.span().from_le_bytes_partial(); + + assert!(res.is_none()); + } + + #[test] + fn test_u32_from_le_bytes_partial_empty() { + let input: Array = array![]; + let res: Option = input.span().from_le_bytes_partial(); + + assert!(res.is_some()); + assert_eq!(res.unwrap(), 0); + } + + #[test] + fn test_u32_from_le_bytes_partial_single_byte() { + let input: Array = array![0xff]; + let res: Option = input.span().from_le_bytes_partial(); + + assert!(res.is_some()); + assert_eq!(res.unwrap(), 0xff); + } + + #[test] + fn test_u32_to_bytes_full() { + let input: u32 = 0xf4321562; + let res: Span = input.to_be_bytes(); + + assert_eq!(res.len(), 4); + assert_eq!(*res[0], 0xf4); + assert_eq!(*res[1], 0x32); + assert_eq!(*res[2], 0x15); + assert_eq!(*res[3], 0x62); + } + + #[test] + fn test_u32_to_bytes_partial() { + let input: u32 = 0xf43215; + let res: Span = input.to_be_bytes(); + + assert_eq!(res.len(), 3); + assert_eq!(*res[0], 0xf4); + assert_eq!(*res[1], 0x32); + assert_eq!(*res[2], 0x15); + } + + + #[test] + fn test_u32_to_bytes_leading_zeros() { + let input: u32 = 0x00f432; + let res: Span = input.to_be_bytes(); + + assert_eq!(res.len(), 2); + assert_eq!(*res[0], 0xf4); + assert_eq!(*res[1], 0x32); + } + + #[test] + fn test_u32_to_be_bytes_padded() { + let input: u32 = 7; + let result = input.to_be_bytes_padded(); + let expected = [0x0, 0x0, 0x0, 7].span(); + + assert_eq!(result, expected); + } + + + #[test] + fn test_u32_bytes_used() { + assert_eq!(0x00_u32.bytes_used(), 0); + let mut value: u32 = 0xff; + let mut i = 1; + loop { + assert_eq!(value.bytes_used(), i); + if i == 4 { + break; + }; + i += 1; + value = value.shl(8); + }; + } + + #[test] + fn test_u32_bytes_used_leading_zeroes() { + let len: u32 = 0x001234; + let bytes_count = len.bytes_used(); + + assert_eq!(bytes_count, 2); + } + } + + mod u64_test { + use crate::math::Bitshift; + use crate::traits::bytes::{ToBytes}; + use super::super::{BitsUsed, BytesUsedTrait, U64Trait}; + + + #[test] + fn test_u64_bytes_used() { + assert_eq!(0x00_u64.bytes_used(), 0); + let mut value: u64 = 0xff; + let mut i = 1; + loop { + assert_eq!(value.bytes_used(), i); + if i == 8 { + break; + }; + i += 1; + value = value.shl(8); + }; + } + + #[test] + fn test_u64_to_be_bytes_padded() { + let input: u64 = 7; + let result = input.to_be_bytes_padded(); + let expected = [0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 7].span(); + + assert_eq!(result, expected); + } + + #[test] + fn test_u64_trailing_zeroes() { + /// bit len is 3, and trailing zeroes are 2 + let input: u64 = 4; + let result = input.count_trailing_zeroes(); + let expected = 2; + + assert_eq!(result, expected); + } + + + #[test] + fn test_u64_leading_zeroes() { + /// bit len is 3, and leading zeroes are 64 - 3 = 61 + let input: u64 = 7; + let result = input.count_leading_zeroes(); + let expected = 61; + + assert_eq!(result, expected); + } + + #[test] + fn test_u64_bits_used() { + let input: u64 = 7; + let result = input.bits_used(); + let expected = 3; + + assert_eq!(result, expected); + } + } + + mod u128_test { + use core::num::traits::Bounded; + use crate::math::Bitshift; + use crate::traits::bytes::{ToBytes}; + use super::super::{BitsUsed, BytesUsedTrait}; + + #[test] + fn test_u128_bytes_used() { + assert_eq!(0x00_u128.bytes_used(), 0); + let mut value: u128 = 0xff; + let mut i = 1; + loop { + assert_eq!(value.bytes_used(), i); + if i == 16 { + break; + }; + i += 1; + value = value.shl(8); + }; + } + + #[test] + fn test_u128_to_bytes_full() { + let input: u128 = Bounded::MAX; + let result: Span = input.to_be_bytes(); + let expected = [ + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 + ].span(); + + assert_eq!(result, expected); + } + + #[test] + fn test_u128_to_bytes_partial() { + let input: u128 = 0xf43215; + let result: Span = input.to_be_bytes(); + let expected = [0xf4, 0x32, 0x15].span(); + + assert_eq!(result, expected); + } + + #[test] + fn test_u128_to_bytes_padded() { + let input: u128 = 0xf43215; + let result: Span = input.to_be_bytes_padded(); + let expected = [ + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xf4, 0x32, 0x15 + ].span(); + + assert_eq!(result, expected); + } + } + + mod u256_test { + use crate::math::Bitshift; + use crate::traits::integer::{U256Trait}; + use super::super::{BitsUsed, BytesUsedTrait}; + + #[test] + fn test_reverse_bytes_u256() { + let value: u256 = 0xFAFFFFFF000000E500000077000000DEAD0000000004200000FADE0000450000; + let res = value.reverse_endianness(); + assert( + res == 0x0000450000DEFA0000200400000000ADDE00000077000000E5000000FFFFFFFA, + 'reverse mismatch' + ); + } + + #[test] + fn test_split_u256_into_u64_little() { + let value: u256 = 0xFAFFFFFF000000E500000077000000DEAD0000000004200000FADE0000450000; + let ((high_h, low_h), (high_l, low_l)) = value.split_into_u64_le(); + assert_eq!(high_h, 0xDE00000077000000); + assert_eq!(low_h, 0xE5000000FFFFFFFA); + assert_eq!(high_l, 0x0000450000DEFA00); + assert_eq!(low_l, 0x00200400000000AD); + } + + #[test] + fn test_u256_bytes_used() { + assert_eq!(0x00_u256.bytes_used(), 0); + let mut value: u256 = 0xff; + let mut i = 1; + loop { + assert_eq!(value.bytes_used(), i); + if i == 32 { + break; + }; + i += 1; + value = value.shl(8); + }; + } + + #[test] + fn test_u256_leading_zeroes() { + /// bit len is 3, and leading zeroes are 256 - 3 = 253 + let input: u256 = 7; + let result = input.count_leading_zeroes(); + let expected = 253; + + assert_eq!(result, expected); + } + + #[test] + fn test_u64_bits_used() { + let input: u256 = 7; + let result = input.bits_used(); + let expected = 3; + + assert_eq!(result, expected); + } + } +} From a4dec6ecfda1d034a45de2ffeaf212feae779d28 Mon Sep 17 00:00:00 2001 From: enitrat Date: Thu, 26 Sep 2024 14:08:13 +0200 Subject: [PATCH 2/2] move ethaddress trait --- .../tests/test_account_contract.cairo | 2 +- .../tests/test_execution_from_outside.cairo | 4 +- .../contracts/tests/test_kakarot_core.cairo | 4 +- crates/contracts/tests/test_ownable.cairo | 1 - crates/evm/src/interpreter.cairo | 3 +- .../precompiles/ec_operations/ec_add.cairo | 1 - crates/evm/src/precompiles/ec_recover.cairo | 4 +- crates/utils/src/address.cairo | 2 +- crates/utils/src/helpers.cairo | 41 ------------------ crates/utils/src/rlp.cairo | 6 +-- crates/utils/src/traits.cairo | 2 + crates/utils/src/traits/eth_address.cairo | 43 +++++++++++++++++++ 12 files changed, 56 insertions(+), 57 deletions(-) create mode 100644 crates/utils/src/traits/eth_address.cairo diff --git a/crates/contracts/tests/test_account_contract.cairo b/crates/contracts/tests/test_account_contract.cairo index 101967435..430a5501c 100644 --- a/crates/contracts/tests/test_account_contract.cairo +++ b/crates/contracts/tests/test_account_contract.cairo @@ -4,8 +4,8 @@ use contracts::test_utils::{ setup_contracts_for_testing, deploy_contract_account, fund_account_with_native_token, deploy_eoa }; use contracts::{IAccountDispatcher, IAccountDispatcherTrait}; +use core::starknet::EthAddress; use core::starknet::account::{Call}; -use core::starknet::{EthAddress, ContractAddress}; use evm::test_utils::{ca_address, eoa_address}; use openzeppelin::token::erc20::interface::IERC20CamelDispatcherTrait; use snforge_std::{start_cheat_caller_address, stop_cheat_caller_address}; diff --git a/crates/contracts/tests/test_execution_from_outside.cairo b/crates/contracts/tests/test_execution_from_outside.cairo index 69a323f3e..9daff0fe4 100644 --- a/crates/contracts/tests/test_execution_from_outside.cairo +++ b/crates/contracts/tests/test_execution_from_outside.cairo @@ -6,8 +6,8 @@ use contracts::kakarot_core::interface::{ }; use contracts::test_data::{counter_evm_bytecode, eip_2930_rlp_encoded_counter_inc_tx,}; use contracts::test_utils::{ - setup_contracts_for_testing, deploy_eoa, deploy_contract_account, - fund_account_with_native_token, call_transaction + setup_contracts_for_testing, deploy_contract_account, fund_account_with_native_token, + call_transaction }; use core::num::traits::Bounded; use core::starknet::account::Call; diff --git a/crates/contracts/tests/test_kakarot_core.cairo b/crates/contracts/tests/test_kakarot_core.cairo index b5816bc75..89bcde981 100644 --- a/crates/contracts/tests/test_kakarot_core.cairo +++ b/crates/contracts/tests/test_kakarot_core.cairo @@ -1,4 +1,3 @@ -use contracts::UninitializedAccount; use contracts::account_contract::{IAccountDispatcher, IAccountDispatcherTrait}; use contracts::kakarot_core::interface::IExtendedKakarotCoreDispatcherTrait; use contracts::kakarot_core::{KakarotCore}; @@ -25,7 +24,8 @@ use snforge_utils::snforge_utils::{EventsFilterBuilderTrait, ContractEventsTrait use starknet::storage::StorageTrait; use utils::eth_transaction::legacy::TxLegacy; use utils::eth_transaction::transaction::Transaction; -use utils::helpers::{EthAddressExTrait, u256_to_bytes_array}; +use utils::helpers::{u256_to_bytes_array}; +use utils::traits::eth_address::EthAddressExTrait; #[test] fn test_kakarot_core_owner() { diff --git a/crates/contracts/tests/test_ownable.cairo b/crates/contracts/tests/test_ownable.cairo index 7b343b47f..d5ac7ca2a 100644 --- a/crates/contracts/tests/test_ownable.cairo +++ b/crates/contracts/tests/test_ownable.cairo @@ -1,6 +1,5 @@ use contracts::components::ownable::{ownable_component}; use contracts::test_utils::constants::{ZERO, OWNER, OTHER}; -use contracts::test_utils; use core::num::traits::Zero; use core::starknet::ContractAddress; diff --git a/crates/evm/src/interpreter.cairo b/crates/evm/src/interpreter.cairo index 3e97b988f..985079c72 100644 --- a/crates/evm/src/interpreter.cairo +++ b/crates/evm/src/interpreter.cairo @@ -1,4 +1,3 @@ -use contracts::account_contract::{IAccountDispatcher, IAccountDispatcherTrait}; use contracts::kakarot_core::KakarotCore; use contracts::kakarot_core::interface::IKakarotCore; use core::num::traits::{Bounded, Zero}; @@ -30,8 +29,8 @@ use utils::constants; use utils::eth_transaction::common::TxKind; use utils::eth_transaction::eip2930::{AccessListItem, AccessListItemTrait}; use utils::eth_transaction::transaction::{Transaction, TransactionTrait}; -use utils::helpers::EthAddressExTrait; use utils::set::{Set, SetTrait}; +use utils::traits::eth_address::EthAddressExTrait; #[generate_trait] pub impl EVMImpl of EVMTrait { diff --git a/crates/evm/src/precompiles/ec_operations/ec_add.cairo b/crates/evm/src/precompiles/ec_operations/ec_add.cairo index 09e55b418..e2dd6967c 100644 --- a/crates/evm/src/precompiles/ec_operations/ec_add.cairo +++ b/crates/evm/src/precompiles/ec_operations/ec_add.cairo @@ -1,6 +1,5 @@ use core::circuit::CircuitElement as CE; use core::circuit::CircuitInput as CI; -use core::circuit::u96; use core::circuit::{ u384, circuit_sub, circuit_mul, circuit_inverse, EvalCircuitTrait, CircuitOutputsTrait, diff --git a/crates/evm/src/precompiles/ec_recover.cairo b/crates/evm/src/precompiles/ec_recover.cairo index 646fafbfc..fe1a755b8 100644 --- a/crates/evm/src/precompiles/ec_recover.cairo +++ b/crates/evm/src/precompiles/ec_recover.cairo @@ -1,6 +1,4 @@ -use core::starknet::secp256_trait::{ - Signature, recover_public_key, Secp256PointTrait, is_valid_signature -}; +use core::starknet::secp256_trait::{Signature, recover_public_key, Secp256PointTrait}; use core::starknet::{ EthAddress, eth_signature::{public_key_point_to_eth_address}, secp256k1::{Secp256k1Point}, SyscallResultTrait diff --git a/crates/utils/src/address.cairo b/crates/utils/src/address.cairo index d5c1c7308..c2d0aba87 100644 --- a/crates/utils/src/address.cairo +++ b/crates/utils/src/address.cairo @@ -4,8 +4,8 @@ use core::traits::TryInto; use crate::traits::bytes::{ToBytes, U8SpanExTrait}; use evm::errors::EVMError; -use utils::helpers::{EthAddressExTrait}; use utils::rlp::{RLPTrait, RLPItem}; +use utils::traits::eth_address::EthAddressExTrait; use utils::traits::{TryIntoResult}; /// Computes the address of the new account that needs to be created. diff --git a/crates/utils/src/helpers.cairo b/crates/utils/src/helpers.cairo index c95c1a78f..d27ff89e9 100644 --- a/crates/utils/src/helpers.cairo +++ b/crates/utils/src/helpers.cairo @@ -204,47 +204,6 @@ pub fn compute_starknet_address( } -#[generate_trait] -pub impl EthAddressExImpl of EthAddressExTrait { - fn to_bytes(self: EthAddress) -> Array { - let bytes_used: u256 = 20; - let value: u256 = self.into(); - let mut bytes: Array = Default::default(); - let mut i = 0; - while i != bytes_used { - let val = value.wrapping_shr(8 * (bytes_used - i - 1)); - bytes.append((val & 0xFF).try_into().unwrap()); - i += 1; - }; - - bytes - } - - /// Packs 20 bytes into a EthAddress - /// # Arguments - /// * `input` a Span of len == 20 - /// # Returns - /// * Option::Some(EthAddress) if the operation succeeds - /// * Option::None otherwise - fn from_bytes(input: Span) -> EthAddress { - let len = input.len(); - if len != 20 { - panic_with_felt252('EthAddress::from_bytes != 20b') - } - let offset: u32 = len - 1; - let mut result: u256 = 0; - let mut i: u32 = 0; - while i != len { - let byte: u256 = (*input.at(i)).into(); - result += byte.shl((8 * (offset - i)).into()); - - i += 1; - }; - result.try_into().unwrap() - } -} - - #[cfg(test)] mod tests { use utils::helpers; diff --git a/crates/utils/src/rlp.cairo b/crates/utils/src/rlp.cairo index 70e509048..38e507ef0 100644 --- a/crates/utils/src/rlp.cairo +++ b/crates/utils/src/rlp.cairo @@ -5,9 +5,9 @@ use core::panic_with_felt252; use core::starknet::EthAddress; use crate::errors::{RLPError}; use crate::eth_transaction::eip2930::AccessListItem; -use crate::helpers::{EthAddressExTrait}; use crate::traits::array::ArrayExtension; use crate::traits::bytes::{ToBytes, FromBytes}; +use crate::traits::eth_address::EthAddressExTrait; // Possible RLP types #[derive(Drop, PartialEq, Debug)] @@ -239,8 +239,8 @@ pub impl RLPHelpersImpl of RLPHelpersTrait { return Result::Ok(Option::None); } if bytes.len() == 20 { - let value = EthAddressExTrait::from_bytes(bytes); - return Result::Ok(Option::Some(value)); + let maybe_value = EthAddressExTrait::from_bytes(bytes); + return Result::Ok(maybe_value); } return Result::Err(RLPError::FailedParsingAddress); }, diff --git a/crates/utils/src/traits.cairo b/crates/utils/src/traits.cairo index 88ea21aed..90827cb51 100644 --- a/crates/utils/src/traits.cairo +++ b/crates/utils/src/traits.cairo @@ -1,6 +1,8 @@ pub mod array; pub mod bytes; +pub mod eth_address; pub mod integer; + use core::array::SpanTrait; use core::num::traits::{Zero, One}; use core::starknet::secp256_trait::{Signature}; diff --git a/crates/utils/src/traits/eth_address.cairo b/crates/utils/src/traits/eth_address.cairo new file mode 100644 index 000000000..07f85d47e --- /dev/null +++ b/crates/utils/src/traits/eth_address.cairo @@ -0,0 +1,43 @@ +use core::starknet::EthAddress; +use crate::math::Bitshift; +use crate::traits::EthAddressIntoU256; + +#[generate_trait] +pub impl EthAddressExImpl of EthAddressExTrait { + fn to_bytes(self: EthAddress) -> Array { + let bytes_used: u256 = 20; + let value: u256 = self.into(); + let mut bytes: Array = Default::default(); + let mut i = 0; + while i != bytes_used { + let val = value.shr(8 * (bytes_used - i - 1)); + bytes.append((val & 0xFF).try_into().unwrap()); + i += 1; + }; + + bytes + } + + /// Packs 20 bytes into a EthAddress + /// # Arguments + /// * `input` a Span of len == 20 + /// # Returns + /// * Option::Some(EthAddress) if the operation succeeds + /// * Option::None otherwise + fn from_bytes(input: Span) -> Option { + let len = input.len(); + if len != 20 { + return Option::None; + } + let offset: u32 = len - 1; + let mut result: u256 = 0; + let mut i: u32 = 0; + while i != len { + let byte: u256 = (*input.at(i)).into(); + result += byte.shl((8 * (offset - i)).into()); + + i += 1; + }; + result.try_into() + } +}