From 83b312a861b42f2ba629eee2e3b00938bc095fcf Mon Sep 17 00:00:00 2001 From: Quentash Date: Thu, 5 Oct 2023 11:47:53 +0200 Subject: [PATCH 01/12] first commit --- crates/utils/Scarb.toml | 1 + crates/utils/src/eth_transaction.cairo | 59 ++++++++++++++++--- crates/utils/src/tests.cairo | 1 + .../src/tests/test_eth_transaction.cairo | 43 ++++++++++++++ 4 files changed, 97 insertions(+), 7 deletions(-) create mode 100644 crates/utils/src/tests/test_eth_transaction.cairo diff --git a/crates/utils/Scarb.toml b/crates/utils/Scarb.toml index d784eea29..14878d0ff 100644 --- a/crates/utils/Scarb.toml +++ b/crates/utils/Scarb.toml @@ -5,3 +5,4 @@ version = "0.1.0" # See more keys and their definitions at https://docs.swmansion.com/scarb/docs/reference/manifest.html [dependencies] +cairo_lib = { git = "https://github.com/Quentash/cairo-lib.git" } \ No newline at end of file diff --git a/crates/utils/src/eth_transaction.cairo b/crates/utils/src/eth_transaction.cairo index 69cfee447..7a90f7abf 100644 --- a/crates/utils/src/eth_transaction.cairo +++ b/crates/utils/src/eth_transaction.cairo @@ -1,19 +1,27 @@ use starknet::EthAddress; +use utils::traits::{SpanDefault, EthAddressDefault, ContractAddressDefault}; +use cairo_lib::encoding::rlp; +use cairo_lib::utils::types::bytes::{Bytes, BytesTryIntoU256}; +use cairo_lib::utils::types::byte::Byte; +use traits::{Into, TryInto}; +use debug::PrintTrait; +#[derive(Drop, Copy, Default)] struct EthereumTransaction { nonce: u128, gas_price: u128, gas_limit: u128, destination: EthAddress, amount: u256, - payload: Span, + payload: Span, tx_hash: u256, v: u128, r: u256, s: u256, } -trait EthTransaction { +#[generate_trait] +impl EthTransaction of EthTransactionTrait { /// Decode a legacy Ethereum transaction /// This function decodes a legacy Ethereum transaction in accordance with EIP-155. /// It returns transaction details including nonce, gas price, gas limit, destination address, amount, payload, @@ -21,7 +29,10 @@ trait EthTransaction { /// transaction data, which includes the chain ID in accordance with EIP-155. /// # Arguments /// tx_data The raw transaction data - fn decode_legacy_tx(tx_data: Span) -> EthereumTransaction; + fn decode_legacy_tx(tx_data: Span) -> EthereumTransaction { + let eth_transaction = Default::default(); + eth_transaction + } /// Decode a modern Ethereum transaction /// This function decodes a modern Ethereum transaction in accordance with EIP-2718. @@ -30,14 +41,42 @@ trait EthTransaction { /// transaction data, which includes the chain ID as part of the transaction data itself. /// # Arguments /// tx_data The raw transaction data - fn decode_tx(tx_data: Span) -> EthereumTransaction; + fn decode_tx(tx_data: Span) -> EthereumTransaction { + let mut eth_transaction: EthereumTransaction = Default::default(); + + let (items, size) = rlp::rlp_decode(tx_data).unwrap(); + + match items { + rlp::RLPItem::Bytes(value) => {}, + rlp::RLPItem::List(list) => { + let nonce: u256 = (*list[0]).try_into().unwrap(); + eth_transaction.nonce = nonce.try_into().unwrap(); + + let gas_price: u256 = (*list[1]).try_into().unwrap(); + eth_transaction.gas_price = gas_price.try_into().unwrap(); + + let gas_limit: u256 = (*list[2]).try_into().unwrap(); + eth_transaction.gas_limit = gas_limit.try_into().unwrap(); + + let destination: u256 = (*list[3]).try_into().unwrap(); + eth_transaction.destination = destination.into(); + + let amount: u256 = (*list[4]).try_into().unwrap(); + eth_transaction.amount = amount.into(); + + eth_transaction.payload = (*list[5]).try_into().unwrap(); + }, + } + + eth_transaction + } /// Check if a raw transaction is a legacy Ethereum transaction /// This function checks if a raw transaction is a legacy Ethereum transaction by checking the transaction type /// according to EIP-2718. If the transaction type is less than or equal to 0xc0, it's a legacy transaction. /// # Arguments /// - `tx_data` The raw transaction data - fn is_legacy_tx(tx_data: Span) -> bool; + //fn is_legacy_tx(tx_data: Span) -> bool; /// Decode a raw Ethereum transaction /// This function decodes a raw Ethereum transaction. It checks if the transaction @@ -45,7 +84,11 @@ trait EthTransaction { /// resp. `decode_legacy_tx` or `decode_tx` based on the result. /// # Arguments /// - `tx_data` The raw transaction data - fn decode(tx_data: Span) -> EthereumTransaction; + fn decode(tx_data: Span, r: u256, s: u256, v: u128) -> EthereumTransaction { + let mut eth_transaction: EthereumTransaction = EthTransaction::decode_tx(tx_data); + + eth_transaction + } /// Validate an Ethereum transaction /// This function validates an Ethereum transaction by checking if the transaction @@ -57,5 +100,7 @@ trait EthTransaction { /// - `address` The ethereum address that is supposed to have signed the transaction /// - `account_nonce` The nonce of the account /// - `param tx_data` The raw transaction data - fn validate_eth_tx(address: EthAddress, account_nonce: u128, tx_data: Span) -> bool; + fn validate_eth_tx(address: EthAddress, account_nonce: u128, tx_data: Span) -> bool { + true + } } diff --git a/crates/utils/src/tests.cairo b/crates/utils/src/tests.cairo index 8d80965de..79db7f80d 100644 --- a/crates/utils/src/tests.cairo +++ b/crates/utils/src/tests.cairo @@ -1,3 +1,4 @@ mod test_helpers; mod test_math; mod test_i256; +mod test_eth_transaction; diff --git a/crates/utils/src/tests/test_eth_transaction.cairo b/crates/utils/src/tests/test_eth_transaction.cairo new file mode 100644 index 000000000..0738dde20 --- /dev/null +++ b/crates/utils/src/tests/test_eth_transaction.cairo @@ -0,0 +1,43 @@ +use utils::helpers; +use utils::helpers::{ + SpanExtension, SpanExtensionTrait, ArrayExtension, ArrayExtensionTrait, U256Trait +}; +use utils::eth_transaction::{EthTransactionTrait, EthereumTransaction}; +use debug::PrintTrait; + +#[test] +#[available_gas(2000000000)] +fn test_decode() { + let mut a = helpers::u256_to_bytes_array( + 0xec098504a817c800825208943535353535353535353535353535353535353535 + ); + let b = helpers::u256_to_bytes_array(0x880de0b6b3a764000080018080); + ArrayExtension::concat(ref a, b.span().slice(32 - 13, 13)); + + let r = 18515461264373351373200002665853028612451056578545711640558177340181847433846; + let s = 46948507304638947509940763649030358759909902576025900602547168820602576006531; + let v = 37; + + let eth_transaction = EthTransactionTrait::decode(a.span(), r, s, v); + + 'lol'.print(); + eth_transaction.nonce.print(); + eth_transaction.gas_price.print(); + eth_transaction.destination.print(); + eth_transaction.amount.print(); +} +/// chainId : 1 +/// nonce : 9 +/// gasprice : 20000000000 +/// startgas : 21000 +/// to : 0x3535353535353535353535353535353535353535 +/// value : 1000000000000000000 +/// data : [] +/// r = 18515461264373351373200002665853028612451056578545711640558177340181847433846; +/// s = 46948507304638947509940763649030358759909902576025900602547168820602576006531; +/// v = 37; +///privkey = 0x4646464646464646464646464646464646464646464646464646464646464646 +/// signing data : 0xec098504a817c800825208943535353535353535353535353535353535353535880de0b6b3a764000080018080 +/// signing hash : 0xdaf5a779ae972f972197303d7b574746c7ef83eadac0f2791ad23db92e4c8e53 +/// signed tx : 0xf86c098504a817c800825208943535353535353535353535353535353535353535880de0b6b3a76400008025a028ef61340bd939bc2195fe537567866003e1a15d3c71ff63e1590620aa636276a067cbe9d8997f761aecb703304b3800ccf555c9f3dc64214b297fb1966a3b6d83 + From 0fb5e61d50ec119e52cb3884e1555a841958dec9 Mon Sep 17 00:00:00 2001 From: Quentash Date: Thu, 5 Oct 2023 13:19:51 +0200 Subject: [PATCH 02/12] rlp import --- crates/utils/Scarb.toml | 1 - crates/utils/src/bitwise.cairo | 78 + crates/utils/src/eth_transaction.cairo | 7 +- crates/utils/src/lib.cairo | 2 + crates/utils/src/math.cairo | 26 + crates/utils/src/rlp.cairo | 160 +- crates/utils/src/tests.cairo | 2 + crates/utils/src/tests/test_bitwise.cairo | 34 + .../src/tests/test_eth_transaction.cairo | 18 +- crates/utils/src/tests/test_math.cairo | 2 +- crates/utils/src/tests/test_rlp.cairo | 1584 +++++++++++++++++ crates/utils/src/types.cairo | 2 + crates/utils/src/types/byte.cairo | 16 + crates/utils/src/types/bytes.cairo | 104 ++ 14 files changed, 2021 insertions(+), 15 deletions(-) create mode 100644 crates/utils/src/bitwise.cairo create mode 100644 crates/utils/src/tests/test_bitwise.cairo create mode 100644 crates/utils/src/tests/test_rlp.cairo create mode 100644 crates/utils/src/types.cairo create mode 100644 crates/utils/src/types/byte.cairo create mode 100644 crates/utils/src/types/bytes.cairo diff --git a/crates/utils/Scarb.toml b/crates/utils/Scarb.toml index 14878d0ff..d784eea29 100644 --- a/crates/utils/Scarb.toml +++ b/crates/utils/Scarb.toml @@ -5,4 +5,3 @@ version = "0.1.0" # See more keys and their definitions at https://docs.swmansion.com/scarb/docs/reference/manifest.html [dependencies] -cairo_lib = { git = "https://github.com/Quentash/cairo-lib.git" } \ No newline at end of file diff --git a/crates/utils/src/bitwise.cairo b/crates/utils/src/bitwise.cairo new file mode 100644 index 000000000..81c6ae311 --- /dev/null +++ b/crates/utils/src/bitwise.cairo @@ -0,0 +1,78 @@ +use utils::math::{pow, pow_felt252}; +use math::Oneable; +use zeroable::Zeroable; + +// @notice Bitwise left shift +// @param num The number to be shifted +// @param shift The number of bits to shift +// @return The left shifted number +fn left_shift< + T, + impl TZeroable: Zeroable, + impl TAdd: Add, + impl TSub: Sub, + impl TMul: Mul, + impl TOneable: Oneable, + impl TCopy: Copy, + impl TDrop: Drop +// TODO refactor shift type from T to usize +>( + num: T, shift: T +) -> T { + let two = TOneable::one() + TOneable::one(); + num * pow(two, shift) +} + +fn left_shift_felt252(num: felt252, shift: felt252) -> felt252 { + num * pow_felt252(2, shift) +} + +// @notice Bitwise right shift +// @param num The number to be shifted +// @param shift The number of bits to shift +// @return The right shifted number +fn right_shift< + T, + impl TZeroable: Zeroable, + impl TAdd: Add, + impl TSub: Sub, + impl TMul: Mul, + impl TDiv: Div, + impl TOneable: Oneable, + impl TCopy: Copy, + impl TDrop: Drop +// TODO refactor shift type from T to usize +>( + num: T, shift: T +) -> T { + let two = TOneable::one() + TOneable::one(); + num / pow(two, shift) +} + +// @notice Bit length of a number +// @param num The number to be measured +// @return The number of bits in the number +fn bit_length< + T, + impl TZeroable: Zeroable, + impl TPartialOrd: PartialOrd, + impl TAddImpl: Add, + impl TSub: Sub, + impl TMul: Mul, + impl TOneable: Oneable, + impl TCopy: Copy, + impl TDrop: Drop +>( + num: T +) -> T { + let mut bit_position = TZeroable::zero(); + let mut cur_n = TOneable::one(); + loop { + if cur_n > num { + break (); + }; + bit_position = bit_position + TOneable::one(); + cur_n = left_shift(cur_n, TOneable::one()); + }; + bit_position +} diff --git a/crates/utils/src/eth_transaction.cairo b/crates/utils/src/eth_transaction.cairo index 7a90f7abf..0bd9cecb9 100644 --- a/crates/utils/src/eth_transaction.cairo +++ b/crates/utils/src/eth_transaction.cairo @@ -1,8 +1,9 @@ use starknet::EthAddress; use utils::traits::{SpanDefault, EthAddressDefault, ContractAddressDefault}; -use cairo_lib::encoding::rlp; -use cairo_lib::utils::types::bytes::{Bytes, BytesTryIntoU256}; -use cairo_lib::utils::types::byte::Byte; +use utils::rlp; +use utils::types::bytes::{Bytes, BytesTryIntoU256}; +use utils::types::byte::Byte; +use utils::helpers::SpanExtensionTrait; use traits::{Into, TryInto}; use debug::PrintTrait; diff --git a/crates/utils/src/lib.cairo b/crates/utils/src/lib.cairo index f43891b42..59092406a 100644 --- a/crates/utils/src/lib.cairo +++ b/crates/utils/src/lib.cairo @@ -7,6 +7,8 @@ mod eth_transaction; mod rlp; mod traits; mod i256; +mod types; +mod bitwise; #[cfg(test)] mod tests; diff --git a/crates/utils/src/math.cairo b/crates/utils/src/math.cairo index a148b8b50..d1d9f7576 100644 --- a/crates/utils/src/math.cairo +++ b/crates/utils/src/math.cairo @@ -1,6 +1,7 @@ use integer::{ u256, u256_overflow_mul, u256_overflowing_add, u512, BoundedInt, u128_overflowing_mul }; +use math::Oneable; trait Exponentiation { /// Raise a number to a power. @@ -231,3 +232,28 @@ impl U128WrappingBitshiftImpl of WrappingBitshift { } } +fn pow< + T, + impl TZeroable: Zeroable, + impl TSub: Sub, + impl TMul: Mul, + impl TOneable: Oneable, + impl TCopy: Copy, + impl TDrop: Drop +>( + base: T, mut exp: T +) -> T { + if exp.is_zero() { + TOneable::one() + } else { + base * pow(base, exp - TOneable::one()) + } +} + +fn pow_felt252(base: felt252, exp: felt252) -> felt252 { + if exp == 0 { + 1 + } else { + base * pow_felt252(base, exp - 1) + } +} diff --git a/crates/utils/src/rlp.cairo b/crates/utils/src/rlp.cairo index ce42d975c..6e723c0df 100644 --- a/crates/utils/src/rlp.cairo +++ b/crates/utils/src/rlp.cairo @@ -1,7 +1,159 @@ -// Reproduce the original rlp.cairo from https://github.com/kkrt-labs/kakarot/blob/7ec7a96074394ddb592a2b6fbea279c6c5cb25a6/src/utils/rlp.cairo#L18 -// By using new Cairo idiomatic ways: Traits, Struct and impl blocks. +use result::ResultTrait; +use option::OptionTrait; +use array::{Array, ArrayTrait, Span, SpanTrait}; +use clone::Clone; +use traits::{Into, TryInto}; +use utils::types::bytes::{Bytes, BytesTryIntoU256}; +use utils::types::byte::Byte; -// We can reuse https://github.com/HerodotusDev/cairo-lib/blob/main/src/encoding/rlp.cairo -// Check before reusing that it works soundly +// @notice Enum with all possible RLP types +#[derive(Drop, PartialEq)] +enum RLPType { + String: (), + StringShort: (), + StringLong: (), + ListShort: (), + ListLong: (), +} +#[generate_trait] +impl RLPTypeImpl of RLPTypeTrait { + // @notice Returns RLPType from the leading byte + // @param byte Leading byte + // @return Result with RLPType + fn from_byte(byte: Byte) -> Result { + if byte <= 0x7f { + Result::Ok(RLPType::String(())) + } else if byte <= 0xb7 { + Result::Ok(RLPType::StringShort(())) + } else if byte <= 0xbf { + Result::Ok(RLPType::StringLong(())) + } else if byte <= 0xf7 { + Result::Ok(RLPType::ListShort(())) + } else if byte <= 0xff { + Result::Ok(RLPType::ListLong(())) + } else { + Result::Err('Invalid byte') + } + } +} +// @notice Represent a RLP item +#[derive(Drop)] +enum RLPItem { + Bytes: Bytes, + // Should be Span to allow for any depth/recursion, not yet supported by the compiler + List: Span +} + +// @notice RLP decodes a rlp encoded byte array +// @param input RLP encoded bytes +// @return Result with RLPItem and size of the decoded item +fn rlp_decode(input: Bytes) -> Result<(RLPItem, usize), felt252> { + let prefix = *input.at(0); + + // Unwrap is impossible to panic here + let rlp_type = RLPTypeTrait::from_byte(prefix).unwrap(); + match rlp_type { + RLPType::String(()) => { + let mut arr = array![prefix]; + Result::Ok((RLPItem::Bytes(arr.span()), 1)) + }, + RLPType::StringShort(()) => { + let len = prefix.into() - 0x80; + let res = input.slice(1, len); + + Result::Ok((RLPItem::Bytes(res), 1 + len)) + }, + RLPType::StringLong(()) => { + let len_len = prefix.into() - 0xb7; + let len_span = input.slice(1, len_len); + + // Bytes => u256 => u32 + let len256: u256 = len_span.try_into().unwrap(); + let len: u32 = len256.try_into().unwrap(); + let res = input.slice(1 + len_len, len); + + Result::Ok((RLPItem::Bytes(res), 1 + len_len + len)) + }, + RLPType::ListShort(()) => { + let len = prefix.into() - 0xc0; + let mut in = input.slice(1, len); + let res = rlp_decode_list(ref in); + Result::Ok((RLPItem::List(res), 1 + len)) + }, + RLPType::ListLong(()) => { + let len_len = prefix.into() - 0xf7; + let len_span = input.slice(1, len_len); + + // Bytes => u256 => u32 + let len256: u256 = len_span.try_into().unwrap(); + let len: u32 = len256.try_into().unwrap(); + let mut in = input.slice(1 + len_len, len); + let res = rlp_decode_list(ref in); + Result::Ok((RLPItem::List(res), 1 + len_len + len)) + } + } +} + +fn rlp_decode_list(ref input: Bytes) -> Span { + let mut i = 0; + let len = input.len(); + let mut output = ArrayTrait::new(); + + loop { + if i >= len { + break (); + } + + let (decoded, decoded_len) = rlp_decode(input).unwrap(); + match decoded { + RLPItem::Bytes(b) => { + output.append(b); + input = input.slice(decoded_len, input.len() - decoded_len); + }, + RLPItem::List(_) => { panic_with_felt252('Recursive list not supported'); } + } + i += decoded_len; + }; + output.span() +} + +impl RLPItemPartialEq of PartialEq { + fn eq(lhs: @RLPItem, rhs: @RLPItem) -> bool { + match lhs { + RLPItem::Bytes(b) => { + match rhs { + RLPItem::Bytes(b2) => { b == b2 }, + RLPItem::List(_) => false + } + }, + RLPItem::List(l) => { + match rhs { + RLPItem::Bytes(_) => false, + RLPItem::List(l2) => { + let len_l = (*l).len(); + if len_l != (*l2).len() { + return false; + } + let mut i: usize = 0; + loop { + if i >= len_l { + break true; + } + if (*l).at(i) != (*l2).at(i) { + break false; + } + i += 1; + } + } + } + } + } + } + + fn ne(lhs: @RLPItem, rhs: @RLPItem) -> bool { + // TODO optimize + !(lhs == rhs) + } +} diff --git a/crates/utils/src/tests.cairo b/crates/utils/src/tests.cairo index 79db7f80d..c97875f94 100644 --- a/crates/utils/src/tests.cairo +++ b/crates/utils/src/tests.cairo @@ -2,3 +2,5 @@ mod test_helpers; mod test_math; mod test_i256; mod test_eth_transaction; +mod test_bitwise; +mod test_rlp; diff --git a/crates/utils/src/tests/test_bitwise.cairo b/crates/utils/src/tests/test_bitwise.cairo new file mode 100644 index 000000000..34bf18e39 --- /dev/null +++ b/crates/utils/src/tests/test_bitwise.cairo @@ -0,0 +1,34 @@ +use utils::bitwise::{left_shift, right_shift, bit_length}; + +#[test] +#[available_gas(999999)] +fn test_left_shift() { + assert(left_shift(1_u32, 0_u32) == 1, '1 << 0'); + assert(left_shift(1_u32, 1_u32) == 2, '1 << 1'); + assert(left_shift(1_u32, 2_u32) == 4, '1 << 2'); + assert(left_shift(1_u32, 8_u32) == 256, '1 << 8'); + assert(left_shift(2_u32, 8_u32) == 512, '2 << 8'); + assert(left_shift(255_u32, 8_u32) == 65280, '255 << 8'); +} + +#[test] +#[available_gas(999999)] +fn test_right_shift() { + assert(right_shift(1_u32, 0_u32) == 1, '1 >> 0'); + assert(right_shift(2_u32, 1_u32) == 1, '2 >> 1'); + assert(right_shift(4_u32, 2_u32) == 1, '4 >> 2'); + assert(right_shift(256_u32, 8_u32) == 1, '256 >> 8'); + assert(right_shift(512_u32, 8_u32) == 2, '512 >> 8'); + assert(right_shift(65280_u32, 8_u32) == 255, '65280 >> 8'); +} + +#[test] +#[available_gas(999999)] +fn test_bit_length() { + assert(bit_length(0_u32) == 0, 'bit length of 0 is 0'); + assert(bit_length(1_u32) == 1, 'bit length of 1 is 1'); + assert(bit_length(2_u128) == 2, 'bit length of 2 is 2'); + assert(bit_length(5_u8) == 3, 'bit length of 5 is 3'); + assert(bit_length(7_u128) == 3, 'bit length of 7 is 3'); + assert(bit_length(8_u32) == 4, 'bit length of 8 is 4'); +} diff --git a/crates/utils/src/tests/test_eth_transaction.cairo b/crates/utils/src/tests/test_eth_transaction.cairo index 0738dde20..99a604fb1 100644 --- a/crates/utils/src/tests/test_eth_transaction.cairo +++ b/crates/utils/src/tests/test_eth_transaction.cairo @@ -20,12 +20,17 @@ fn test_decode() { let eth_transaction = EthTransactionTrait::decode(a.span(), r, s, v); - 'lol'.print(); - eth_transaction.nonce.print(); - eth_transaction.gas_price.print(); - eth_transaction.destination.print(); - eth_transaction.amount.print(); + assert(eth_transaction.nonce == 9, 'wrong nonce'); + assert(eth_transaction.gas_price == 20000000000, 'wrong gas_price'); + assert(eth_transaction.gas_limit == 21000, 'wrong gas_limit'); + assert( + eth_transaction.destination.into() == 0x3535353535353535353535353535353535353535, + 'wrong destination' + ); + assert(eth_transaction.amount == 1000000000000000000, 'wrong amount'); + assert(eth_transaction.payload.len() == 0, 'wrong payload size'); } +/// example taken from : https://eips.ethereum.org/EIPS/eip-155 /// chainId : 1 /// nonce : 9 /// gasprice : 20000000000 @@ -36,8 +41,9 @@ fn test_decode() { /// r = 18515461264373351373200002665853028612451056578545711640558177340181847433846; /// s = 46948507304638947509940763649030358759909902576025900602547168820602576006531; /// v = 37; -///privkey = 0x4646464646464646464646464646464646464646464646464646464646464646 +/// privkey = 0x4646464646464646464646464646464646464646464646464646464646464646 /// signing data : 0xec098504a817c800825208943535353535353535353535353535353535353535880de0b6b3a764000080018080 /// signing hash : 0xdaf5a779ae972f972197303d7b574746c7ef83eadac0f2791ad23db92e4c8e53 /// signed tx : 0xf86c098504a817c800825208943535353535353535353535353535353535353535880de0b6b3a76400008025a028ef61340bd939bc2195fe537567866003e1a15d3c71ff63e1590620aa636276a067cbe9d8997f761aecb703304b3800ccf555c9f3dc64214b297fb1966a3b6d83 + diff --git a/crates/utils/src/tests/test_math.cairo b/crates/utils/src/tests/test_math.cairo index 375b65117..9a9bf8fea 100644 --- a/crates/utils/src/tests/test_math.cairo +++ b/crates/utils/src/tests/test_math.cairo @@ -1,6 +1,6 @@ use integer::{u256_overflowing_add, BoundedInt, u512}; use utils::math::{ - Exponentiation, WrappingExponentiation, u256_wide_add, Bitshift, WrappingBitshift + Exponentiation, WrappingExponentiation, u256_wide_add, Bitshift, WrappingBitshift, pow }; #[test] diff --git a/crates/utils/src/tests/test_rlp.cairo b/crates/utils/src/tests/test_rlp.cairo new file mode 100644 index 000000000..fabf9e556 --- /dev/null +++ b/crates/utils/src/tests/test_rlp.cairo @@ -0,0 +1,1584 @@ +use utils::rlp::{rlp_decode, RLPType, RLPTypeTrait, RLPItem}; +use utils::types::bytes::Bytes; +use array::{ArrayTrait, SpanTrait}; +use result::ResultTrait; + +#[test] +#[available_gas(9999999)] +fn test_rlp_types() { + let mut i = 0; + loop { + if i <= 0x7f { + assert(RLPTypeTrait::from_byte(i).unwrap() == RLPType::String(()), 'Parse type String'); + } else if i <= 0xb7 { + assert( + RLPTypeTrait::from_byte(i).unwrap() == RLPType::StringShort(()), + 'Parse type StringShort' + ); + } else if i <= 0xbf { + assert( + RLPTypeTrait::from_byte(i).unwrap() == RLPType::StringLong(()), + 'Parse type StringLong' + ); + } else if i <= 0xf7 { + assert( + RLPTypeTrait::from_byte(i).unwrap() == RLPType::ListShort(()), + 'Parse type ListShort' + ); + } else if i <= 0xff { + assert( + RLPTypeTrait::from_byte(i).unwrap() == RLPType::ListLong(()), 'Parse type ListLong' + ); + } + + if i == 0xff { + break (); + } + i += 1; + }; +} + +#[test] +#[available_gas(99999999)] +fn test_rlp_decode_string() { + let mut i = 0; + loop { + if i == 0x80 { + break (); + } + let mut arr = ArrayTrait::new(); + arr.append(i); + + let (res, len) = rlp_decode(arr.span()).unwrap(); + assert(len == 1, 'Wrong len'); + assert(res == RLPItem::Bytes(arr.span()), 'Wrong value'); + + i += 1; + }; +} + +#[test] +#[available_gas(99999999)] +fn test_rlp_decode_short_string() { + let mut arr = array![ + 0x9b, + 0x5a, + 0x80, + 0x6c, + 0xf6, + 0x34, + 0xc0, + 0x39, + 0x8d, + 0x8f, + 0x2d, + 0x89, + 0xfd, + 0x49, + 0xa9, + 0x1e, + 0xf3, + 0x3d, + 0xa4, + 0x74, + 0xcd, + 0x84, + 0x94, + 0xbb, + 0xa8, + 0xda, + 0x3b, + 0xf7 + ]; + + let (res, len) = rlp_decode(arr.span()).unwrap(); + assert(len == 1 + (0x9b - 0x80), 'Wrong len'); + + arr.pop_front(); + let expected_item = RLPItem::Bytes(arr.span()); + + assert(res == expected_item, 'Wrong value'); +} + +#[test] +#[available_gas(99999999)] +fn test_rlp_decode_long_string_len_of_len_1() { + let mut arr = array![ + 0xb8, + 0x3c, + 0xf7, + 0xa1, + 0x7e, + 0xf9, + 0x59, + 0xd4, + 0x88, + 0x38, + 0x8d, + 0xdc, + 0x34, + 0x7b, + 0x3a, + 0x10, + 0xdd, + 0x85, + 0x43, + 0x1d, + 0x0c, + 0x37, + 0x98, + 0x6a, + 0x63, + 0xbd, + 0x18, + 0xba, + 0xa3, + 0x8d, + 0xb1, + 0xa4, + 0x81, + 0x6f, + 0x24, + 0xde, + 0xc3, + 0xec, + 0x16, + 0x6e, + 0xb3, + 0xb2, + 0xac, + 0xc4, + 0xc4, + 0xf7, + 0x79, + 0x04, + 0xba, + 0x76, + 0x3c, + 0x67, + 0xc6, + 0xd0, + 0x53, + 0xda, + 0xea, + 0x10, + 0x86, + 0x19, + 0x7d, + 0xd9 + ]; + + let (res, len) = rlp_decode(arr.span()).unwrap(); + assert(len == 1 + (0xb8 - 0xb7) + 0x3c, 'Wrong len'); + + arr.pop_front(); + arr.pop_front(); + let expected_item = RLPItem::Bytes(arr.span()); + + assert(res == expected_item, 'Wrong value'); +} + +#[test] +#[available_gas(99999999)] +fn test_rlp_decode_long_string_len_of_len_2() { + let mut arr = array![ + 0xb9, + 0x01, + 0x02, + 0xf7, + 0xa1, + 0x7e, + 0xf9, + 0x59, + 0xd4, + 0x88, + 0x38, + 0x8d, + 0xdc, + 0x34, + 0x7b, + 0x3a, + 0x10, + 0xdd, + 0x85, + 0x43, + 0x1d, + 0x0c, + 0x37, + 0x98, + 0x6a, + 0x63, + 0xbd, + 0x18, + 0xba, + 0xa3, + 0x8d, + 0xb1, + 0xa4, + 0x81, + 0x6f, + 0x24, + 0xde, + 0xc3, + 0xec, + 0x16, + 0x6e, + 0xb3, + 0xb2, + 0xac, + 0xc4, + 0xc4, + 0xf7, + 0x79, + 0x04, + 0xba, + 0x76, + 0x3c, + 0x67, + 0xc6, + 0xd0, + 0x53, + 0xda, + 0xea, + 0x10, + 0x86, + 0x19, + 0x7d, + 0xd9, + 0x33, + 0x58, + 0x47, + 0x69, + 0x34, + 0x76, + 0x89, + 0x43, + 0x67, + 0x93, + 0x45, + 0x76, + 0x87, + 0x34, + 0x95, + 0x67, + 0x89, + 0x34, + 0x36, + 0x43, + 0x86, + 0x79, + 0x43, + 0x63, + 0x34, + 0x78, + 0x63, + 0x49, + 0x58, + 0x67, + 0x83, + 0x59, + 0x64, + 0x56, + 0x37, + 0x93, + 0x74, + 0x58, + 0x69, + 0x69, + 0x43, + 0x67, + 0x39, + 0x48, + 0x67, + 0x98, + 0x45, + 0x63, + 0x89, + 0x45, + 0x67, + 0x94, + 0x37, + 0x63, + 0x04, + 0x56, + 0x40, + 0x39, + 0x68, + 0x43, + 0x08, + 0x68, + 0x40, + 0x65, + 0x03, + 0x46, + 0x80, + 0x93, + 0x48, + 0x64, + 0x95, + 0x36, + 0x87, + 0x39, + 0x84, + 0x56, + 0x73, + 0x76, + 0x89, + 0x34, + 0x95, + 0x86, + 0x73, + 0x65, + 0x40, + 0x93, + 0x60, + 0x98, + 0x34, + 0x56, + 0x83, + 0x04, + 0x56, + 0x80, + 0x36, + 0x08, + 0x59, + 0x68, + 0x45, + 0x06, + 0x83, + 0x06, + 0x68, + 0x40, + 0x59, + 0x68, + 0x40, + 0x65, + 0x84, + 0x03, + 0x68, + 0x30, + 0x48, + 0x65, + 0x03, + 0x46, + 0x83, + 0x49, + 0x57, + 0x68, + 0x95, + 0x79, + 0x68, + 0x34, + 0x76, + 0x83, + 0x74, + 0x69, + 0x87, + 0x43, + 0x59, + 0x63, + 0x84, + 0x75, + 0x63, + 0x98, + 0x47, + 0x56, + 0x34, + 0x86, + 0x73, + 0x94, + 0x87, + 0x65, + 0x43, + 0x98, + 0x67, + 0x34, + 0x96, + 0x79, + 0x34, + 0x86, + 0x57, + 0x93, + 0x48, + 0x57, + 0x69, + 0x34, + 0x87, + 0x56, + 0x89, + 0x34, + 0x57, + 0x68, + 0x73, + 0x49, + 0x56, + 0x53, + 0x79, + 0x43, + 0x95, + 0x67, + 0x34, + 0x96, + 0x79, + 0x38, + 0x47, + 0x63, + 0x94, + 0x65, + 0x37, + 0x89, + 0x63, + 0x53, + 0x45, + 0x68, + 0x79, + 0x88, + 0x97, + 0x68, + 0x87, + 0x68, + 0x68, + 0x68, + 0x76, + 0x99, + 0x87, + 0x60 + ]; + + let (res, len) = rlp_decode(arr.span()).unwrap(); + assert(len == 1 + (0xb9 - 0xb7) + 0x0102, 'Wrong len'); + + arr.pop_front(); + arr.pop_front(); + arr.pop_front(); + let expected_item = RLPItem::Bytes(arr.span()); + + assert(res == expected_item, 'Wrong value'); +} + +#[test] +#[available_gas(99999999999)] +fn test_rlp_decode_short_list() { + let mut arr = array![0xc9, 0x83, 0x35, 0x35, 0x89, 0x42, 0x83, 0x45, 0x38, 0x92]; + let (res, len) = rlp_decode(arr.span()).unwrap(); + assert(len == 1 + (0xc9 - 0xc0), 'Wrong len'); + + let mut expected_0 = array![0x35, 0x35, 0x89]; + let mut expected_1 = array![0x42]; + let mut expected_2 = array![0x45, 0x38, 0x92]; + + let expected = array![expected_0.span(), expected_1.span(), expected_2.span()]; + let expected_item = RLPItem::List(expected.span()); + + assert(res == expected_item, 'Wrong value'); +} + +#[test] +#[available_gas(99999999999)] +fn test_rlp_decode_long_list() { + let mut arr = array![ + 0xf9, + 0x02, + 0x11, + 0xa0, + 0x77, + 0x70, + 0xcf, + 0x09, + 0xb5, + 0x06, + 0x7a, + 0x1b, + 0x35, + 0xdf, + 0x62, + 0xa9, + 0x24, + 0x89, + 0x81, + 0x75, + 0xce, + 0xae, + 0xec, + 0xad, + 0x1f, + 0x68, + 0xcd, + 0xb4, + 0xa8, + 0x44, + 0x40, + 0x0c, + 0x73, + 0xc1, + 0x4a, + 0xf4, + 0xa0, + 0x1e, + 0xa3, + 0x85, + 0xd0, + 0x5a, + 0xb2, + 0x61, + 0x46, + 0x6d, + 0x5c, + 0x04, + 0x87, + 0xfe, + 0x68, + 0x45, + 0x34, + 0xc1, + 0x9f, + 0x1a, + 0x4b, + 0x5c, + 0x4b, + 0x18, + 0xdc, + 0x1a, + 0x36, + 0x35, + 0x60, + 0x02, + 0x50, + 0x71, + 0xb4, + 0xa0, + 0x2c, + 0x4c, + 0x04, + 0xce, + 0x35, + 0x40, + 0xd3, + 0xd1, + 0x46, + 0x18, + 0x72, + 0x30, + 0x3c, + 0x53, + 0xa5, + 0xe5, + 0x66, + 0x83, + 0xc1, + 0x30, + 0x4f, + 0x8d, + 0x36, + 0xa8, + 0x80, + 0x0c, + 0x6a, + 0xf5, + 0xfa, + 0x3f, + 0xcd, + 0xee, + 0xa0, + 0xa9, + 0xdc, + 0x77, + 0x8d, + 0xc5, + 0x4b, + 0x7d, + 0xd3, + 0xc4, + 0x82, + 0x22, + 0xe7, + 0x39, + 0xd1, + 0x61, + 0xfe, + 0xb0, + 0xc0, + 0xee, + 0xce, + 0xb2, + 0xdc, + 0xd5, + 0x17, + 0x37, + 0xf0, + 0x5b, + 0x8e, + 0x37, + 0xa6, + 0x38, + 0x51, + 0xa0, + 0xa9, + 0x5f, + 0x4d, + 0x55, + 0x56, + 0xdf, + 0x62, + 0xdd, + 0xc2, + 0x62, + 0x99, + 0x04, + 0x97, + 0xae, + 0x56, + 0x9b, + 0xcd, + 0x8e, + 0xfd, + 0xda, + 0x7b, + 0x20, + 0x07, + 0x93, + 0xf8, + 0xd3, + 0xde, + 0x4c, + 0xdb, + 0x97, + 0x18, + 0xd7, + 0xa0, + 0x39, + 0xd4, + 0x06, + 0x6d, + 0x14, + 0x38, + 0x22, + 0x6e, + 0xaf, + 0x4a, + 0xc9, + 0xe9, + 0x43, + 0xa8, + 0x74, + 0xa9, + 0xa9, + 0xc2, + 0x5f, + 0xb0, + 0xd8, + 0x1d, + 0xb9, + 0x86, + 0x1d, + 0x8c, + 0x13, + 0x36, + 0xb3, + 0xe2, + 0x03, + 0x4c, + 0xa0, + 0x7a, + 0xcc, + 0x7c, + 0x63, + 0xb4, + 0x6a, + 0xa4, + 0x18, + 0xb3, + 0xc9, + 0xa0, + 0x41, + 0xa1, + 0x25, + 0x6b, + 0xcb, + 0x73, + 0x61, + 0x31, + 0x6b, + 0x39, + 0x7a, + 0xda, + 0x5a, + 0x88, + 0x67, + 0x49, + 0x1b, + 0xbb, + 0x13, + 0x01, + 0x30, + 0xa0, + 0x15, + 0x35, + 0x8a, + 0x81, + 0x25, + 0x2e, + 0xc4, + 0x93, + 0x71, + 0x13, + 0xfe, + 0x36, + 0xc7, + 0x80, + 0x46, + 0xb7, + 0x11, + 0xfb, + 0xa1, + 0x97, + 0x34, + 0x91, + 0xbb, + 0x29, + 0x18, + 0x7a, + 0x00, + 0x78, + 0x5f, + 0xf8, + 0x52, + 0xae, + 0xa0, + 0x68, + 0x91, + 0x42, + 0xd3, + 0x16, + 0xab, + 0xfa, + 0xa7, + 0x1c, + 0x8b, + 0xce, + 0xdf, + 0x49, + 0x20, + 0x1d, + 0xdb, + 0xb2, + 0x10, + 0x4e, + 0x25, + 0x0a, + 0xdc, + 0x90, + 0xc4, + 0xe8, + 0x56, + 0x22, + 0x1f, + 0x53, + 0x4a, + 0x96, + 0x58, + 0xa0, + 0xdc, + 0x36, + 0x50, + 0x99, + 0x25, + 0x34, + 0xfd, + 0xa8, + 0xa3, + 0x14, + 0xa7, + 0xdb, + 0xb0, + 0xae, + 0x3b, + 0xa8, + 0xc7, + 0x9d, + 0xb5, + 0x55, + 0x0c, + 0x69, + 0xce, + 0x2a, + 0x24, + 0x60, + 0xc0, + 0x07, + 0xad, + 0xc4, + 0xc1, + 0xa3, + 0xa0, + 0x20, + 0xb0, + 0x68, + 0x3b, + 0x66, + 0x55, + 0xb0, + 0x05, + 0x9e, + 0xe1, + 0x03, + 0xd0, + 0x4e, + 0x4b, + 0x50, + 0x6b, + 0xcb, + 0xc1, + 0x39, + 0x00, + 0x63, + 0x92, + 0xb7, + 0xda, + 0xb1, + 0x11, + 0x78, + 0xc2, + 0x66, + 0x03, + 0x42, + 0xe7, + 0xa0, + 0x8e, + 0xed, + 0xeb, + 0x45, + 0xfb, + 0x63, + 0x0f, + 0x1c, + 0xd9, + 0x97, + 0x36, + 0xeb, + 0x18, + 0x57, + 0x22, + 0x17, + 0xcb, + 0xc6, + 0xd5, + 0xf3, + 0x15, + 0xb7, + 0x1b, + 0xe2, + 0x03, + 0xb0, + 0x3c, + 0xe8, + 0xd9, + 0x9b, + 0x26, + 0x14, + 0xa0, + 0x79, + 0x23, + 0xa3, + 0x3d, + 0xf6, + 0x5a, + 0x98, + 0x6f, + 0xd5, + 0xe7, + 0xf9, + 0xe6, + 0xe4, + 0xc2, + 0xb9, + 0x69, + 0x73, + 0x6b, + 0x08, + 0x94, + 0x4e, + 0xbe, + 0x99, + 0x39, + 0x4a, + 0x86, + 0x14, + 0x61, + 0x2f, + 0xe6, + 0x09, + 0xf3, + 0xa0, + 0x65, + 0x34, + 0xd7, + 0xd0, + 0x1a, + 0x20, + 0x71, + 0x4a, + 0xa4, + 0xfb, + 0x2a, + 0x55, + 0xb9, + 0x46, + 0xce, + 0x64, + 0xc3, + 0x22, + 0x2d, + 0xff, + 0xad, + 0x2a, + 0xa2, + 0xd1, + 0x8a, + 0x92, + 0x34, + 0x73, + 0xc9, + 0x2a, + 0xb1, + 0xfd, + 0xa0, + 0xbf, + 0xf9, + 0xc2, + 0x8b, + 0xfe, + 0xb8, + 0xbf, + 0x2d, + 0xa9, + 0xb6, + 0x18, + 0xc8, + 0xc3, + 0xb0, + 0x6f, + 0xe8, + 0x0c, + 0xb1, + 0xc0, + 0xbd, + 0x14, + 0x47, + 0x38, + 0xf7, + 0xc4, + 0x21, + 0x61, + 0xff, + 0x29, + 0xe2, + 0x50, + 0x2f, + 0xa0, + 0x7f, + 0x14, + 0x61, + 0x69, + 0x3c, + 0x70, + 0x4e, + 0xa5, + 0x02, + 0x1b, + 0xbb, + 0xa3, + 0x5e, + 0x72, + 0xc5, + 0x02, + 0xf6, + 0x43, + 0x9e, + 0x45, + 0x8f, + 0x98, + 0x24, + 0x2e, + 0xd0, + 0x37, + 0x48, + 0xea, + 0x8f, + 0xe2, + 0xb3, + 0x5f, + 0x80 + ]; + let (res, len) = rlp_decode(arr.span()).unwrap(); + assert(len == 1 + (0xf9 - 0xf7) + 0x0211, 'Wrong len'); + + let mut expected_0 = array![ + 0x77, + 0x70, + 0xcf, + 0x09, + 0xb5, + 0x06, + 0x7a, + 0x1b, + 0x35, + 0xdf, + 0x62, + 0xa9, + 0x24, + 0x89, + 0x81, + 0x75, + 0xce, + 0xae, + 0xec, + 0xad, + 0x1f, + 0x68, + 0xcd, + 0xb4, + 0xa8, + 0x44, + 0x40, + 0x0c, + 0x73, + 0xc1, + 0x4a, + 0xf4 + ]; + let mut expected_1 = array![ + 0x1e, + 0xa3, + 0x85, + 0xd0, + 0x5a, + 0xb2, + 0x61, + 0x46, + 0x6d, + 0x5c, + 0x04, + 0x87, + 0xfe, + 0x68, + 0x45, + 0x34, + 0xc1, + 0x9f, + 0x1a, + 0x4b, + 0x5c, + 0x4b, + 0x18, + 0xdc, + 0x1a, + 0x36, + 0x35, + 0x60, + 0x02, + 0x50, + 0x71, + 0xb4 + ]; + let mut expected_2 = array![ + 0x2c, + 0x4c, + 0x04, + 0xce, + 0x35, + 0x40, + 0xd3, + 0xd1, + 0x46, + 0x18, + 0x72, + 0x30, + 0x3c, + 0x53, + 0xa5, + 0xe5, + 0x66, + 0x83, + 0xc1, + 0x30, + 0x4f, + 0x8d, + 0x36, + 0xa8, + 0x80, + 0x0c, + 0x6a, + 0xf5, + 0xfa, + 0x3f, + 0xcd, + 0xee + ]; + let mut expected_3 = array![ + 0xa9, + 0xdc, + 0x77, + 0x8d, + 0xc5, + 0x4b, + 0x7d, + 0xd3, + 0xc4, + 0x82, + 0x22, + 0xe7, + 0x39, + 0xd1, + 0x61, + 0xfe, + 0xb0, + 0xc0, + 0xee, + 0xce, + 0xb2, + 0xdc, + 0xd5, + 0x17, + 0x37, + 0xf0, + 0x5b, + 0x8e, + 0x37, + 0xa6, + 0x38, + 0x51 + ]; + let mut expected_4 = array![ + 0xa9, + 0x5f, + 0x4d, + 0x55, + 0x56, + 0xdf, + 0x62, + 0xdd, + 0xc2, + 0x62, + 0x99, + 0x04, + 0x97, + 0xae, + 0x56, + 0x9b, + 0xcd, + 0x8e, + 0xfd, + 0xda, + 0x7b, + 0x20, + 0x07, + 0x93, + 0xf8, + 0xd3, + 0xde, + 0x4c, + 0xdb, + 0x97, + 0x18, + 0xd7 + ]; + let mut expected_5 = array![ + 0x39, + 0xd4, + 0x06, + 0x6d, + 0x14, + 0x38, + 0x22, + 0x6e, + 0xaf, + 0x4a, + 0xc9, + 0xe9, + 0x43, + 0xa8, + 0x74, + 0xa9, + 0xa9, + 0xc2, + 0x5f, + 0xb0, + 0xd8, + 0x1d, + 0xb9, + 0x86, + 0x1d, + 0x8c, + 0x13, + 0x36, + 0xb3, + 0xe2, + 0x03, + 0x4c + ]; + let mut expected_6 = array![ + 0x7a, + 0xcc, + 0x7c, + 0x63, + 0xb4, + 0x6a, + 0xa4, + 0x18, + 0xb3, + 0xc9, + 0xa0, + 0x41, + 0xa1, + 0x25, + 0x6b, + 0xcb, + 0x73, + 0x61, + 0x31, + 0x6b, + 0x39, + 0x7a, + 0xda, + 0x5a, + 0x88, + 0x67, + 0x49, + 0x1b, + 0xbb, + 0x13, + 0x01, + 0x30 + ]; + let mut expected_7 = array![ + 0x15, + 0x35, + 0x8a, + 0x81, + 0x25, + 0x2e, + 0xc4, + 0x93, + 0x71, + 0x13, + 0xfe, + 0x36, + 0xc7, + 0x80, + 0x46, + 0xb7, + 0x11, + 0xfb, + 0xa1, + 0x97, + 0x34, + 0x91, + 0xbb, + 0x29, + 0x18, + 0x7a, + 0x00, + 0x78, + 0x5f, + 0xf8, + 0x52, + 0xae + ]; + let mut expected_8 = array![ + 0x68, + 0x91, + 0x42, + 0xd3, + 0x16, + 0xab, + 0xfa, + 0xa7, + 0x1c, + 0x8b, + 0xce, + 0xdf, + 0x49, + 0x20, + 0x1d, + 0xdb, + 0xb2, + 0x10, + 0x4e, + 0x25, + 0x0a, + 0xdc, + 0x90, + 0xc4, + 0xe8, + 0x56, + 0x22, + 0x1f, + 0x53, + 0x4a, + 0x96, + 0x58 + ]; + let mut expected_9 = array![ + 0xdc, + 0x36, + 0x50, + 0x99, + 0x25, + 0x34, + 0xfd, + 0xa8, + 0xa3, + 0x14, + 0xa7, + 0xdb, + 0xb0, + 0xae, + 0x3b, + 0xa8, + 0xc7, + 0x9d, + 0xb5, + 0x55, + 0x0c, + 0x69, + 0xce, + 0x2a, + 0x24, + 0x60, + 0xc0, + 0x07, + 0xad, + 0xc4, + 0xc1, + 0xa3 + ]; + let mut expected_10 = array![ + 0x20, + 0xb0, + 0x68, + 0x3b, + 0x66, + 0x55, + 0xb0, + 0x05, + 0x9e, + 0xe1, + 0x03, + 0xd0, + 0x4e, + 0x4b, + 0x50, + 0x6b, + 0xcb, + 0xc1, + 0x39, + 0x00, + 0x63, + 0x92, + 0xb7, + 0xda, + 0xb1, + 0x11, + 0x78, + 0xc2, + 0x66, + 0x03, + 0x42, + 0xe7 + ]; + let mut expected_11 = array![ + 0x8e, + 0xed, + 0xeb, + 0x45, + 0xfb, + 0x63, + 0x0f, + 0x1c, + 0xd9, + 0x97, + 0x36, + 0xeb, + 0x18, + 0x57, + 0x22, + 0x17, + 0xcb, + 0xc6, + 0xd5, + 0xf3, + 0x15, + 0xb7, + 0x1b, + 0xe2, + 0x03, + 0xb0, + 0x3c, + 0xe8, + 0xd9, + 0x9b, + 0x26, + 0x14 + ]; + let mut expected_12 = array![ + 0x79, + 0x23, + 0xa3, + 0x3d, + 0xf6, + 0x5a, + 0x98, + 0x6f, + 0xd5, + 0xe7, + 0xf9, + 0xe6, + 0xe4, + 0xc2, + 0xb9, + 0x69, + 0x73, + 0x6b, + 0x08, + 0x94, + 0x4e, + 0xbe, + 0x99, + 0x39, + 0x4a, + 0x86, + 0x14, + 0x61, + 0x2f, + 0xe6, + 0x09, + 0xf3 + ]; + let mut expected_13 = array![ + 0x65, + 0x34, + 0xd7, + 0xd0, + 0x1a, + 0x20, + 0x71, + 0x4a, + 0xa4, + 0xfb, + 0x2a, + 0x55, + 0xb9, + 0x46, + 0xce, + 0x64, + 0xc3, + 0x22, + 0x2d, + 0xff, + 0xad, + 0x2a, + 0xa2, + 0xd1, + 0x8a, + 0x92, + 0x34, + 0x73, + 0xc9, + 0x2a, + 0xb1, + 0xfd + ]; + let mut expected_14 = array![ + 0xbf, + 0xf9, + 0xc2, + 0x8b, + 0xfe, + 0xb8, + 0xbf, + 0x2d, + 0xa9, + 0xb6, + 0x18, + 0xc8, + 0xc3, + 0xb0, + 0x6f, + 0xe8, + 0x0c, + 0xb1, + 0xc0, + 0xbd, + 0x14, + 0x47, + 0x38, + 0xf7, + 0xc4, + 0x21, + 0x61, + 0xff, + 0x29, + 0xe2, + 0x50, + 0x2f + ]; + let mut expected_15 = array![ + 0x7f, + 0x14, + 0x61, + 0x69, + 0x3c, + 0x70, + 0x4e, + 0xa5, + 0x02, + 0x1b, + 0xbb, + 0xa3, + 0x5e, + 0x72, + 0xc5, + 0x02, + 0xf6, + 0x43, + 0x9e, + 0x45, + 0x8f, + 0x98, + 0x24, + 0x2e, + 0xd0, + 0x37, + 0x48, + 0xea, + 0x8f, + 0xe2, + 0xb3, + 0x5f + ]; + let mut expected_16 = ArrayTrait::new(); + + let mut expected = array![ + expected_0.span(), + expected_1.span(), + expected_2.span(), + expected_3.span(), + expected_4.span(), + expected_5.span(), + expected_6.span(), + expected_7.span(), + expected_8.span(), + expected_9.span(), + expected_10.span(), + expected_11.span(), + expected_12.span(), + expected_13.span(), + expected_14.span(), + expected_15.span(), + expected_16.span() + ]; + let expected_item = RLPItem::List(expected.span()); + + assert(res == expected_item, 'Wrong value'); +} diff --git a/crates/utils/src/types.cairo b/crates/utils/src/types.cairo new file mode 100644 index 000000000..37e3dcad5 --- /dev/null +++ b/crates/utils/src/types.cairo @@ -0,0 +1,2 @@ +mod bytes; +mod byte; diff --git a/crates/utils/src/types/byte.cairo b/crates/utils/src/types/byte.cairo new file mode 100644 index 000000000..f3043a67b --- /dev/null +++ b/crates/utils/src/types/byte.cairo @@ -0,0 +1,16 @@ +use utils::bitwise::right_shift; + +type Byte = u8; + +#[generate_trait] +impl ByteImpl of ByteTrait { + // @notice Extracts the high and low nibbles from a byte + // @return (high, low) + fn extract_nibbles(self: Byte) -> (Byte, Byte) { + let masked = self & 0xf0; + let high = right_shift(masked, 4); + let low = self & 0x0f; + + (high, low) + } +} diff --git a/crates/utils/src/types/bytes.cairo b/crates/utils/src/types/bytes.cairo new file mode 100644 index 000000000..1e7b5b455 --- /dev/null +++ b/crates/utils/src/types/bytes.cairo @@ -0,0 +1,104 @@ +use utils::bitwise::{left_shift, left_shift_felt252}; +use utils::types::byte::Byte; +use array::SpanTrait; +use traits::{TryInto, Into}; + +type Bytes = Span; + +impl BytesTryIntoU256 of TryInto { + fn try_into(self: Bytes) -> Option { + let len = self.len(); + if len > 32 { + return Option::None(()); + } + + if self.is_empty() { + return Option::Some(0); + } + + let offset = len.into() - 1; + let mut result: u256 = 0; + let mut i: usize = 0; + loop { + if i >= len { + break (); + } + let byte: u256 = (*self.at(i)).into(); + result += left_shift(byte, 8 * (offset - i.into())); + + i += 1; + }; + + Option::Some(result) + } +} + +impl BytesTryIntoFelt252 of TryInto { + fn try_into(self: Bytes) -> Option { + let len = self.len(); + if len > 31 { + return Option::None(()); + } + + if self.is_empty() { + return Option::Some(0); + } + + let offset = len.into() - 1; + let mut result: felt252 = 0; + let mut i: usize = 0; + loop { + if i >= len { + break (); + } + let byte: felt252 = (*self.at(i)).into(); + result += left_shift_felt252(byte, 8 * (offset - i.into())); + + i += 1; + }; + + Option::Some(result) + } +} + +impl BytesPartialEq of PartialEq { + fn eq(lhs: @Bytes, rhs: @Bytes) -> bool { + let len_lhs = (*lhs).len(); + if len_lhs != (*rhs).len() { + return false; + } + + let mut i: usize = 0; + loop { + if i >= len_lhs { + break true; + } + + if (*lhs).at(i) != (*rhs).at(i) { + break false; + } + + i += 1; + } + } + + fn ne(lhs: @Bytes, rhs: @Bytes) -> bool { + let len_lhs = (*lhs).len(); + if len_lhs != (*rhs).len() { + return true; + } + + let mut i: usize = 0; + loop { + if i >= len_lhs { + break false; + } + + if (*lhs).at(i) != (*rhs).at(i) { + break true; + } + + i += 1; + } + } +} From 9c726c255f6779b0c4d02b912b368085ba795ff3 Mon Sep 17 00:00:00 2001 From: Quentash Date: Sat, 7 Oct 2023 00:32:30 +0200 Subject: [PATCH 03/12] separated and cleaned --- crates/utils/src/bitwise.cairo | 78 ------------- crates/utils/src/eth_transaction.cairo | 60 ++-------- crates/utils/src/helpers.cairo | 29 +++++ crates/utils/src/lib.cairo | 2 - crates/utils/src/rlp.cairo | 13 +-- crates/utils/src/tests.cairo | 2 - crates/utils/src/tests/test_bitwise.cairo | 34 ------ .../src/tests/test_eth_transaction.cairo | 49 --------- crates/utils/src/tests/test_rlp.cairo | 1 - crates/utils/src/types.cairo | 2 - crates/utils/src/types/byte.cairo | 16 --- crates/utils/src/types/bytes.cairo | 104 ------------------ 12 files changed, 42 insertions(+), 348 deletions(-) delete mode 100644 crates/utils/src/bitwise.cairo delete mode 100644 crates/utils/src/tests/test_bitwise.cairo delete mode 100644 crates/utils/src/tests/test_eth_transaction.cairo delete mode 100644 crates/utils/src/types.cairo delete mode 100644 crates/utils/src/types/byte.cairo delete mode 100644 crates/utils/src/types/bytes.cairo diff --git a/crates/utils/src/bitwise.cairo b/crates/utils/src/bitwise.cairo deleted file mode 100644 index 81c6ae311..000000000 --- a/crates/utils/src/bitwise.cairo +++ /dev/null @@ -1,78 +0,0 @@ -use utils::math::{pow, pow_felt252}; -use math::Oneable; -use zeroable::Zeroable; - -// @notice Bitwise left shift -// @param num The number to be shifted -// @param shift The number of bits to shift -// @return The left shifted number -fn left_shift< - T, - impl TZeroable: Zeroable, - impl TAdd: Add, - impl TSub: Sub, - impl TMul: Mul, - impl TOneable: Oneable, - impl TCopy: Copy, - impl TDrop: Drop -// TODO refactor shift type from T to usize ->( - num: T, shift: T -) -> T { - let two = TOneable::one() + TOneable::one(); - num * pow(two, shift) -} - -fn left_shift_felt252(num: felt252, shift: felt252) -> felt252 { - num * pow_felt252(2, shift) -} - -// @notice Bitwise right shift -// @param num The number to be shifted -// @param shift The number of bits to shift -// @return The right shifted number -fn right_shift< - T, - impl TZeroable: Zeroable, - impl TAdd: Add, - impl TSub: Sub, - impl TMul: Mul, - impl TDiv: Div, - impl TOneable: Oneable, - impl TCopy: Copy, - impl TDrop: Drop -// TODO refactor shift type from T to usize ->( - num: T, shift: T -) -> T { - let two = TOneable::one() + TOneable::one(); - num / pow(two, shift) -} - -// @notice Bit length of a number -// @param num The number to be measured -// @return The number of bits in the number -fn bit_length< - T, - impl TZeroable: Zeroable, - impl TPartialOrd: PartialOrd, - impl TAddImpl: Add, - impl TSub: Sub, - impl TMul: Mul, - impl TOneable: Oneable, - impl TCopy: Copy, - impl TDrop: Drop ->( - num: T -) -> T { - let mut bit_position = TZeroable::zero(); - let mut cur_n = TOneable::one(); - loop { - if cur_n > num { - break (); - }; - bit_position = bit_position + TOneable::one(); - cur_n = left_shift(cur_n, TOneable::one()); - }; - bit_position -} diff --git a/crates/utils/src/eth_transaction.cairo b/crates/utils/src/eth_transaction.cairo index 0bd9cecb9..69cfee447 100644 --- a/crates/utils/src/eth_transaction.cairo +++ b/crates/utils/src/eth_transaction.cairo @@ -1,28 +1,19 @@ use starknet::EthAddress; -use utils::traits::{SpanDefault, EthAddressDefault, ContractAddressDefault}; -use utils::rlp; -use utils::types::bytes::{Bytes, BytesTryIntoU256}; -use utils::types::byte::Byte; -use utils::helpers::SpanExtensionTrait; -use traits::{Into, TryInto}; -use debug::PrintTrait; -#[derive(Drop, Copy, Default)] struct EthereumTransaction { nonce: u128, gas_price: u128, gas_limit: u128, destination: EthAddress, amount: u256, - payload: Span, + payload: Span, tx_hash: u256, v: u128, r: u256, s: u256, } -#[generate_trait] -impl EthTransaction of EthTransactionTrait { +trait EthTransaction { /// Decode a legacy Ethereum transaction /// This function decodes a legacy Ethereum transaction in accordance with EIP-155. /// It returns transaction details including nonce, gas price, gas limit, destination address, amount, payload, @@ -30,10 +21,7 @@ impl EthTransaction of EthTransactionTrait { /// transaction data, which includes the chain ID in accordance with EIP-155. /// # Arguments /// tx_data The raw transaction data - fn decode_legacy_tx(tx_data: Span) -> EthereumTransaction { - let eth_transaction = Default::default(); - eth_transaction - } + fn decode_legacy_tx(tx_data: Span) -> EthereumTransaction; /// Decode a modern Ethereum transaction /// This function decodes a modern Ethereum transaction in accordance with EIP-2718. @@ -42,42 +30,14 @@ impl EthTransaction of EthTransactionTrait { /// transaction data, which includes the chain ID as part of the transaction data itself. /// # Arguments /// tx_data The raw transaction data - fn decode_tx(tx_data: Span) -> EthereumTransaction { - let mut eth_transaction: EthereumTransaction = Default::default(); - - let (items, size) = rlp::rlp_decode(tx_data).unwrap(); - - match items { - rlp::RLPItem::Bytes(value) => {}, - rlp::RLPItem::List(list) => { - let nonce: u256 = (*list[0]).try_into().unwrap(); - eth_transaction.nonce = nonce.try_into().unwrap(); - - let gas_price: u256 = (*list[1]).try_into().unwrap(); - eth_transaction.gas_price = gas_price.try_into().unwrap(); - - let gas_limit: u256 = (*list[2]).try_into().unwrap(); - eth_transaction.gas_limit = gas_limit.try_into().unwrap(); - - let destination: u256 = (*list[3]).try_into().unwrap(); - eth_transaction.destination = destination.into(); - - let amount: u256 = (*list[4]).try_into().unwrap(); - eth_transaction.amount = amount.into(); - - eth_transaction.payload = (*list[5]).try_into().unwrap(); - }, - } - - eth_transaction - } + fn decode_tx(tx_data: Span) -> EthereumTransaction; /// Check if a raw transaction is a legacy Ethereum transaction /// This function checks if a raw transaction is a legacy Ethereum transaction by checking the transaction type /// according to EIP-2718. If the transaction type is less than or equal to 0xc0, it's a legacy transaction. /// # Arguments /// - `tx_data` The raw transaction data - //fn is_legacy_tx(tx_data: Span) -> bool; + fn is_legacy_tx(tx_data: Span) -> bool; /// Decode a raw Ethereum transaction /// This function decodes a raw Ethereum transaction. It checks if the transaction @@ -85,11 +45,7 @@ impl EthTransaction of EthTransactionTrait { /// resp. `decode_legacy_tx` or `decode_tx` based on the result. /// # Arguments /// - `tx_data` The raw transaction data - fn decode(tx_data: Span, r: u256, s: u256, v: u128) -> EthereumTransaction { - let mut eth_transaction: EthereumTransaction = EthTransaction::decode_tx(tx_data); - - eth_transaction - } + fn decode(tx_data: Span) -> EthereumTransaction; /// Validate an Ethereum transaction /// This function validates an Ethereum transaction by checking if the transaction @@ -101,7 +57,5 @@ impl EthTransaction of EthTransactionTrait { /// - `address` The ethereum address that is supposed to have signed the transaction /// - `account_nonce` The nonce of the account /// - `param tx_data` The raw transaction data - fn validate_eth_tx(address: EthAddress, account_nonce: u128, tx_data: Span) -> bool { - true - } + fn validate_eth_tx(address: EthAddress, account_nonce: u128, tx_data: Span) -> bool; } diff --git a/crates/utils/src/helpers.cairo b/crates/utils/src/helpers.cairo index 09468e64e..edea16f8e 100644 --- a/crates/utils/src/helpers.cairo +++ b/crates/utils/src/helpers.cairo @@ -7,6 +7,7 @@ use utils::constants::{ POW_256_16, }; use keccak::u128_split; +use utils::math::Bitshift; /// Ceils a number of bits to the next word (32 bytes) @@ -519,6 +520,34 @@ impl SpanExtension of SpanExtensionTrait { } } +impl SpanU8TryIntoU256 of TryInto, u256> { + fn try_into(self: Span) -> Option { + let len = self.len(); + if len > 32 { + return Option::None(()); + } + + if self.is_empty() { + return Option::Some(0); + } + + let offset = len.into() - 1; + let mut result: u256 = 0; + let mut i: usize = 0; + loop { + if i >= len { + break (); + } + let byte: u256 = (*self.at(i)).into(); + result += Bitshift::shl(byte, 8 * (offset - i.into())); + + i += 1; + }; + + Option::Some(result) + } +} + #[generate_trait] impl U256Impl of U256Trait { diff --git a/crates/utils/src/lib.cairo b/crates/utils/src/lib.cairo index 59092406a..f43891b42 100644 --- a/crates/utils/src/lib.cairo +++ b/crates/utils/src/lib.cairo @@ -7,8 +7,6 @@ mod eth_transaction; mod rlp; mod traits; mod i256; -mod types; -mod bitwise; #[cfg(test)] mod tests; diff --git a/crates/utils/src/rlp.cairo b/crates/utils/src/rlp.cairo index 6e723c0df..93ae56b81 100644 --- a/crates/utils/src/rlp.cairo +++ b/crates/utils/src/rlp.cairo @@ -3,8 +3,7 @@ use option::OptionTrait; use array::{Array, ArrayTrait, Span, SpanTrait}; use clone::Clone; use traits::{Into, TryInto}; -use utils::types::bytes::{Bytes, BytesTryIntoU256}; -use utils::types::byte::Byte; +use utils::helpers::SpanU8TryIntoU256; // @notice Enum with all possible RLP types #[derive(Drop, PartialEq)] @@ -21,7 +20,7 @@ impl RLPTypeImpl of RLPTypeTrait { // @notice Returns RLPType from the leading byte // @param byte Leading byte // @return Result with RLPType - fn from_byte(byte: Byte) -> Result { + fn from_byte(byte: u8) -> Result { if byte <= 0x7f { Result::Ok(RLPType::String(())) } else if byte <= 0xb7 { @@ -41,15 +40,15 @@ impl RLPTypeImpl of RLPTypeTrait { // @notice Represent a RLP item #[derive(Drop)] enum RLPItem { - Bytes: Bytes, + Bytes: Span, // Should be Span to allow for any depth/recursion, not yet supported by the compiler - List: Span + List: Span> } // @notice RLP decodes a rlp encoded byte array // @param input RLP encoded bytes // @return Result with RLPItem and size of the decoded item -fn rlp_decode(input: Bytes) -> Result<(RLPItem, usize), felt252> { +fn rlp_decode(input: Span) -> Result<(RLPItem, usize), felt252> { let prefix = *input.at(0); // Unwrap is impossible to panic here @@ -96,7 +95,7 @@ fn rlp_decode(input: Bytes) -> Result<(RLPItem, usize), felt252> { } } -fn rlp_decode_list(ref input: Bytes) -> Span { +fn rlp_decode_list(ref input: Span) -> Span> { let mut i = 0; let len = input.len(); let mut output = ArrayTrait::new(); diff --git a/crates/utils/src/tests.cairo b/crates/utils/src/tests.cairo index c97875f94..3794d004a 100644 --- a/crates/utils/src/tests.cairo +++ b/crates/utils/src/tests.cairo @@ -1,6 +1,4 @@ mod test_helpers; mod test_math; mod test_i256; -mod test_eth_transaction; -mod test_bitwise; mod test_rlp; diff --git a/crates/utils/src/tests/test_bitwise.cairo b/crates/utils/src/tests/test_bitwise.cairo deleted file mode 100644 index 34bf18e39..000000000 --- a/crates/utils/src/tests/test_bitwise.cairo +++ /dev/null @@ -1,34 +0,0 @@ -use utils::bitwise::{left_shift, right_shift, bit_length}; - -#[test] -#[available_gas(999999)] -fn test_left_shift() { - assert(left_shift(1_u32, 0_u32) == 1, '1 << 0'); - assert(left_shift(1_u32, 1_u32) == 2, '1 << 1'); - assert(left_shift(1_u32, 2_u32) == 4, '1 << 2'); - assert(left_shift(1_u32, 8_u32) == 256, '1 << 8'); - assert(left_shift(2_u32, 8_u32) == 512, '2 << 8'); - assert(left_shift(255_u32, 8_u32) == 65280, '255 << 8'); -} - -#[test] -#[available_gas(999999)] -fn test_right_shift() { - assert(right_shift(1_u32, 0_u32) == 1, '1 >> 0'); - assert(right_shift(2_u32, 1_u32) == 1, '2 >> 1'); - assert(right_shift(4_u32, 2_u32) == 1, '4 >> 2'); - assert(right_shift(256_u32, 8_u32) == 1, '256 >> 8'); - assert(right_shift(512_u32, 8_u32) == 2, '512 >> 8'); - assert(right_shift(65280_u32, 8_u32) == 255, '65280 >> 8'); -} - -#[test] -#[available_gas(999999)] -fn test_bit_length() { - assert(bit_length(0_u32) == 0, 'bit length of 0 is 0'); - assert(bit_length(1_u32) == 1, 'bit length of 1 is 1'); - assert(bit_length(2_u128) == 2, 'bit length of 2 is 2'); - assert(bit_length(5_u8) == 3, 'bit length of 5 is 3'); - assert(bit_length(7_u128) == 3, 'bit length of 7 is 3'); - assert(bit_length(8_u32) == 4, 'bit length of 8 is 4'); -} diff --git a/crates/utils/src/tests/test_eth_transaction.cairo b/crates/utils/src/tests/test_eth_transaction.cairo deleted file mode 100644 index 99a604fb1..000000000 --- a/crates/utils/src/tests/test_eth_transaction.cairo +++ /dev/null @@ -1,49 +0,0 @@ -use utils::helpers; -use utils::helpers::{ - SpanExtension, SpanExtensionTrait, ArrayExtension, ArrayExtensionTrait, U256Trait -}; -use utils::eth_transaction::{EthTransactionTrait, EthereumTransaction}; -use debug::PrintTrait; - -#[test] -#[available_gas(2000000000)] -fn test_decode() { - let mut a = helpers::u256_to_bytes_array( - 0xec098504a817c800825208943535353535353535353535353535353535353535 - ); - let b = helpers::u256_to_bytes_array(0x880de0b6b3a764000080018080); - ArrayExtension::concat(ref a, b.span().slice(32 - 13, 13)); - - let r = 18515461264373351373200002665853028612451056578545711640558177340181847433846; - let s = 46948507304638947509940763649030358759909902576025900602547168820602576006531; - let v = 37; - - let eth_transaction = EthTransactionTrait::decode(a.span(), r, s, v); - - assert(eth_transaction.nonce == 9, 'wrong nonce'); - assert(eth_transaction.gas_price == 20000000000, 'wrong gas_price'); - assert(eth_transaction.gas_limit == 21000, 'wrong gas_limit'); - assert( - eth_transaction.destination.into() == 0x3535353535353535353535353535353535353535, - 'wrong destination' - ); - assert(eth_transaction.amount == 1000000000000000000, 'wrong amount'); - assert(eth_transaction.payload.len() == 0, 'wrong payload size'); -} -/// example taken from : https://eips.ethereum.org/EIPS/eip-155 -/// chainId : 1 -/// nonce : 9 -/// gasprice : 20000000000 -/// startgas : 21000 -/// to : 0x3535353535353535353535353535353535353535 -/// value : 1000000000000000000 -/// data : [] -/// r = 18515461264373351373200002665853028612451056578545711640558177340181847433846; -/// s = 46948507304638947509940763649030358759909902576025900602547168820602576006531; -/// v = 37; -/// privkey = 0x4646464646464646464646464646464646464646464646464646464646464646 -/// signing data : 0xec098504a817c800825208943535353535353535353535353535353535353535880de0b6b3a764000080018080 -/// signing hash : 0xdaf5a779ae972f972197303d7b574746c7ef83eadac0f2791ad23db92e4c8e53 -/// signed tx : 0xf86c098504a817c800825208943535353535353535353535353535353535353535880de0b6b3a76400008025a028ef61340bd939bc2195fe537567866003e1a15d3c71ff63e1590620aa636276a067cbe9d8997f761aecb703304b3800ccf555c9f3dc64214b297fb1966a3b6d83 - - diff --git a/crates/utils/src/tests/test_rlp.cairo b/crates/utils/src/tests/test_rlp.cairo index fabf9e556..715463d37 100644 --- a/crates/utils/src/tests/test_rlp.cairo +++ b/crates/utils/src/tests/test_rlp.cairo @@ -1,5 +1,4 @@ use utils::rlp::{rlp_decode, RLPType, RLPTypeTrait, RLPItem}; -use utils::types::bytes::Bytes; use array::{ArrayTrait, SpanTrait}; use result::ResultTrait; diff --git a/crates/utils/src/types.cairo b/crates/utils/src/types.cairo deleted file mode 100644 index 37e3dcad5..000000000 --- a/crates/utils/src/types.cairo +++ /dev/null @@ -1,2 +0,0 @@ -mod bytes; -mod byte; diff --git a/crates/utils/src/types/byte.cairo b/crates/utils/src/types/byte.cairo deleted file mode 100644 index f3043a67b..000000000 --- a/crates/utils/src/types/byte.cairo +++ /dev/null @@ -1,16 +0,0 @@ -use utils::bitwise::right_shift; - -type Byte = u8; - -#[generate_trait] -impl ByteImpl of ByteTrait { - // @notice Extracts the high and low nibbles from a byte - // @return (high, low) - fn extract_nibbles(self: Byte) -> (Byte, Byte) { - let masked = self & 0xf0; - let high = right_shift(masked, 4); - let low = self & 0x0f; - - (high, low) - } -} diff --git a/crates/utils/src/types/bytes.cairo b/crates/utils/src/types/bytes.cairo deleted file mode 100644 index 1e7b5b455..000000000 --- a/crates/utils/src/types/bytes.cairo +++ /dev/null @@ -1,104 +0,0 @@ -use utils::bitwise::{left_shift, left_shift_felt252}; -use utils::types::byte::Byte; -use array::SpanTrait; -use traits::{TryInto, Into}; - -type Bytes = Span; - -impl BytesTryIntoU256 of TryInto { - fn try_into(self: Bytes) -> Option { - let len = self.len(); - if len > 32 { - return Option::None(()); - } - - if self.is_empty() { - return Option::Some(0); - } - - let offset = len.into() - 1; - let mut result: u256 = 0; - let mut i: usize = 0; - loop { - if i >= len { - break (); - } - let byte: u256 = (*self.at(i)).into(); - result += left_shift(byte, 8 * (offset - i.into())); - - i += 1; - }; - - Option::Some(result) - } -} - -impl BytesTryIntoFelt252 of TryInto { - fn try_into(self: Bytes) -> Option { - let len = self.len(); - if len > 31 { - return Option::None(()); - } - - if self.is_empty() { - return Option::Some(0); - } - - let offset = len.into() - 1; - let mut result: felt252 = 0; - let mut i: usize = 0; - loop { - if i >= len { - break (); - } - let byte: felt252 = (*self.at(i)).into(); - result += left_shift_felt252(byte, 8 * (offset - i.into())); - - i += 1; - }; - - Option::Some(result) - } -} - -impl BytesPartialEq of PartialEq { - fn eq(lhs: @Bytes, rhs: @Bytes) -> bool { - let len_lhs = (*lhs).len(); - if len_lhs != (*rhs).len() { - return false; - } - - let mut i: usize = 0; - loop { - if i >= len_lhs { - break true; - } - - if (*lhs).at(i) != (*rhs).at(i) { - break false; - } - - i += 1; - } - } - - fn ne(lhs: @Bytes, rhs: @Bytes) -> bool { - let len_lhs = (*lhs).len(); - if len_lhs != (*rhs).len() { - return true; - } - - let mut i: usize = 0; - loop { - if i >= len_lhs { - break false; - } - - if (*lhs).at(i) != (*rhs).at(i) { - break true; - } - - i += 1; - } - } -} From bb79fa781b2c180072e04887248afedbed82ed3a Mon Sep 17 00:00:00 2001 From: Quentash Date: Wed, 11 Oct 2023 01:04:05 +0200 Subject: [PATCH 04/12] corrections --- crates/utils/src/errors.cairo | 16 +++++ crates/utils/src/helpers.cairo | 12 ++-- crates/utils/src/lib.cairo | 1 + crates/utils/src/math.cairo | 57 +++++++++-------- crates/utils/src/rlp.cairo | 89 ++++++++------------------ crates/utils/src/tests/test_math.cairo | 3 +- crates/utils/src/tests/test_rlp.cairo | 39 ++++++----- 7 files changed, 104 insertions(+), 113 deletions(-) create mode 100644 crates/utils/src/errors.cairo diff --git a/crates/utils/src/errors.cairo b/crates/utils/src/errors.cairo new file mode 100644 index 000000000..b163c8fde --- /dev/null +++ b/crates/utils/src/errors.cairo @@ -0,0 +1,16 @@ +// LENGTH +const RLP_INVALID_LENGTH: felt252 = 'KKT: RlpInvalidLength'; + +#[derive(Drop, Copy, PartialEq)] +enum RLPError { + RlpInvalidLength: felt252, +} + + +impl RLPErrorIntoU256 of Into { + fn into(self: RLPError) -> u256 { + match self { + RLPError::RlpInvalidLength(error_message) => error_message.into(), + } + } +} diff --git a/crates/utils/src/helpers.cairo b/crates/utils/src/helpers.cairo index 763df637e..0fcbd841f 100644 --- a/crates/utils/src/helpers.cairo +++ b/crates/utils/src/helpers.cairo @@ -505,10 +505,10 @@ impl SpanExtension, +Drop> of SpanExtensionTrait { } } -impl SpanU8TryIntoU256 of TryInto, u256> { - fn try_into(self: Span) -> Option { +impl SpanU8TryIntoU32 of TryInto, u32> { + fn try_into(self: Span) -> Option { let len = self.len(); - if len > 32 { + if len > 4 { return Option::None(()); } @@ -517,14 +517,14 @@ impl SpanU8TryIntoU256 of TryInto, u256> { } let offset = len.into() - 1; - let mut result: u256 = 0; + let mut result: u32 = 0; let mut i: usize = 0; loop { if i >= len { break (); } - let byte: u256 = (*self.at(i)).into(); - result += Bitshift::shl(byte, 8 * (offset - i.into())); + let byte: u32 = (*self.at(i)).into(); + result += byte.shl(8 * (offset - i.into())); i += 1; }; diff --git a/crates/utils/src/lib.cairo b/crates/utils/src/lib.cairo index f43891b42..baf9443ef 100644 --- a/crates/utils/src/lib.cairo +++ b/crates/utils/src/lib.cairo @@ -7,6 +7,7 @@ mod eth_transaction; mod rlp; mod traits; mod i256; +mod errors; #[cfg(test)] mod tests; diff --git a/crates/utils/src/math.cairo b/crates/utils/src/math.cairo index d1d9f7576..efbdb46d5 100644 --- a/crates/utils/src/math.cairo +++ b/crates/utils/src/math.cairo @@ -95,6 +95,19 @@ impl Felt252WrappingExpImpl of WrappingExponentiation { } } +impl U32ExpImpl of Exponentiation { + fn pow(self: u32, mut exponent: u32) -> u32 { + if self == 0 { + return 0; + } + if exponent == 0 { + return 1; + } else { + return self * Exponentiation::pow(self, exponent - 1); + } + } +} + /// Adds two 256-bit unsigned integers, returning a 512-bit unsigned integer result. /// @@ -148,6 +161,24 @@ impl U256BitshiftImpl of Bitshift { } } +impl U32BitshiftImpl of Bitshift { + fn shl(self: u32, shift: u32) -> u32 { + if shift > 31 { + // 2.pow(shift) for shift > 32 will panic with 'u32_mul Overflow' + panic_with_felt252('u32_mul Overflow'); + } + self * 2.pow(shift) + } + + fn shr(self: u32, shift: u32) -> u32 { + if shift > 31 { + // 2.pow(shift) for shift > 32 will panic with 'u32_mul Overflow' + panic_with_felt252('u32_mul Overflow'); + } + self / 2.pow(shift) + } +} + trait WrappingBitshift { // Shift a number left by a given number of bits. // If the shift is greater than 255, the result is 0. @@ -231,29 +262,3 @@ impl U128WrappingBitshiftImpl of WrappingBitshift { self / 2.pow(shift) } } - -fn pow< - T, - impl TZeroable: Zeroable, - impl TSub: Sub, - impl TMul: Mul, - impl TOneable: Oneable, - impl TCopy: Copy, - impl TDrop: Drop ->( - base: T, mut exp: T -) -> T { - if exp.is_zero() { - TOneable::one() - } else { - base * pow(base, exp - TOneable::one()) - } -} - -fn pow_felt252(base: felt252, exp: felt252) -> felt252 { - if exp == 0 { - 1 - } else { - base * pow_felt252(base, exp - 1) - } -} diff --git a/crates/utils/src/rlp.cairo b/crates/utils/src/rlp.cairo index 93ae56b81..423d6c673 100644 --- a/crates/utils/src/rlp.cairo +++ b/crates/utils/src/rlp.cairo @@ -3,16 +3,17 @@ use option::OptionTrait; use array::{Array, ArrayTrait, Span, SpanTrait}; use clone::Clone; use traits::{Into, TryInto}; -use utils::helpers::SpanU8TryIntoU256; +use utils::helpers::SpanU8TryIntoU32; +use utils::errors::{RLPError, RLP_INVALID_LENGTH}; // @notice Enum with all possible RLP types #[derive(Drop, PartialEq)] enum RLPType { - String: (), - StringShort: (), - StringLong: (), - ListShort: (), - ListLong: (), + String, + StringShort, + StringLong, + ListShort, + ListLong, } #[generate_trait] @@ -20,25 +21,23 @@ impl RLPTypeImpl of RLPTypeTrait { // @notice Returns RLPType from the leading byte // @param byte Leading byte // @return Result with RLPType - fn from_byte(byte: u8) -> Result { + fn from_byte(byte: u8) -> RLPType { if byte <= 0x7f { - Result::Ok(RLPType::String(())) + RLPType::String(()) } else if byte <= 0xb7 { - Result::Ok(RLPType::StringShort(())) + RLPType::StringShort(()) } else if byte <= 0xbf { - Result::Ok(RLPType::StringLong(())) + RLPType::StringLong(()) } else if byte <= 0xf7 { - Result::Ok(RLPType::ListShort(())) - } else if byte <= 0xff { - Result::Ok(RLPType::ListLong(())) + RLPType::ListShort(()) } else { - Result::Err('Invalid byte') + RLPType::ListLong(()) } } } // @notice Represent a RLP item -#[derive(Drop)] +#[derive(Drop, PartialEq)] enum RLPItem { Bytes: Span, // Should be Span to allow for any depth/recursion, not yet supported by the compiler @@ -46,13 +45,16 @@ enum RLPItem { } // @notice RLP decodes a rlp encoded byte array +// as described in https://ethereum.org/en/developers/docs/data-structures-and-encoding/rlp/ // @param input RLP encoded bytes // @return Result with RLPItem and size of the decoded item -fn rlp_decode(input: Span) -> Result<(RLPItem, usize), felt252> { +fn rlp_decode(input: Span) -> Result<(RLPItem, usize), RLPError> { + if input.len() == 0 { + return Result::Err(RLPError::RlpInvalidLength(RLP_INVALID_LENGTH)); + } let prefix = *input.at(0); - // Unwrap is impossible to panic here - let rlp_type = RLPTypeTrait::from_byte(prefix).unwrap(); + let rlp_type = RLPTypeTrait::from_byte(prefix); match rlp_type { RLPType::String(()) => { let mut arr = array![prefix]; @@ -65,12 +67,12 @@ fn rlp_decode(input: Span) -> Result<(RLPItem, usize), felt252> { Result::Ok((RLPItem::Bytes(res), 1 + len)) }, RLPType::StringLong(()) => { + // Extract the amount of bytes representing the data payload length let len_len = prefix.into() - 0xb7; let len_span = input.slice(1, len_len); - // Bytes => u256 => u32 - let len256: u256 = len_span.try_into().unwrap(); - let len: u32 = len256.try_into().unwrap(); + // Bytes => u32 + let len: u32 = len_span.try_into().unwrap(); let res = input.slice(1 + len_len, len); Result::Ok((RLPItem::Bytes(res), 1 + len_len + len)) @@ -82,12 +84,12 @@ fn rlp_decode(input: Span) -> Result<(RLPItem, usize), felt252> { Result::Ok((RLPItem::List(res), 1 + len)) }, RLPType::ListLong(()) => { + // Extract the amount of bytes representing the data payload length let len_len = prefix.into() - 0xf7; let len_span = input.slice(1, len_len); - // Bytes => u256 => u32 - let len256: u256 = len_span.try_into().unwrap(); - let len: u32 = len256.try_into().unwrap(); + // Bytes => u32 + let len: u32 = len_span.try_into().unwrap(); let mut in = input.slice(1 + len_len, len); let res = rlp_decode_list(ref in); Result::Ok((RLPItem::List(res), 1 + len_len + len)) @@ -117,42 +119,3 @@ fn rlp_decode_list(ref input: Span) -> Span> { }; output.span() } - -impl RLPItemPartialEq of PartialEq { - fn eq(lhs: @RLPItem, rhs: @RLPItem) -> bool { - match lhs { - RLPItem::Bytes(b) => { - match rhs { - RLPItem::Bytes(b2) => { b == b2 }, - RLPItem::List(_) => false - } - }, - RLPItem::List(l) => { - match rhs { - RLPItem::Bytes(_) => false, - RLPItem::List(l2) => { - let len_l = (*l).len(); - if len_l != (*l2).len() { - return false; - } - let mut i: usize = 0; - loop { - if i >= len_l { - break true; - } - if (*l).at(i) != (*l2).at(i) { - break false; - } - i += 1; - } - } - } - } - } - } - - fn ne(lhs: @RLPItem, rhs: @RLPItem) -> bool { - // TODO optimize - !(lhs == rhs) - } -} diff --git a/crates/utils/src/tests/test_math.cairo b/crates/utils/src/tests/test_math.cairo index 9a9bf8fea..27c035055 100644 --- a/crates/utils/src/tests/test_math.cairo +++ b/crates/utils/src/tests/test_math.cairo @@ -1,6 +1,6 @@ use integer::{u256_overflowing_add, BoundedInt, u512}; use utils::math::{ - Exponentiation, WrappingExponentiation, u256_wide_add, Bitshift, WrappingBitshift, pow + Exponentiation, WrappingExponentiation, u256_wide_add, Bitshift, WrappingBitshift }; #[test] @@ -253,4 +253,3 @@ fn test_felt252_wrapping_shr_to_zero() { let expected = 0_felt252; assert(result == expected, 'wrong result'); } - diff --git a/crates/utils/src/tests/test_rlp.cairo b/crates/utils/src/tests/test_rlp.cairo index 715463d37..be7b04adb 100644 --- a/crates/utils/src/tests/test_rlp.cairo +++ b/crates/utils/src/tests/test_rlp.cairo @@ -1,6 +1,7 @@ use utils::rlp::{rlp_decode, RLPType, RLPTypeTrait, RLPItem}; use array::{ArrayTrait, SpanTrait}; use result::ResultTrait; +use utils::errors::{RLPError, RLP_INVALID_LENGTH}; #[test] #[available_gas(9999999)] @@ -8,26 +9,17 @@ fn test_rlp_types() { let mut i = 0; loop { if i <= 0x7f { - assert(RLPTypeTrait::from_byte(i).unwrap() == RLPType::String(()), 'Parse type String'); + assert(RLPTypeTrait::from_byte(i) == RLPType::String(()), 'Parse type String'); } else if i <= 0xb7 { assert( - RLPTypeTrait::from_byte(i).unwrap() == RLPType::StringShort(()), - 'Parse type StringShort' + RLPTypeTrait::from_byte(i) == RLPType::StringShort(()), 'Parse type StringShort' ); } else if i <= 0xbf { - assert( - RLPTypeTrait::from_byte(i).unwrap() == RLPType::StringLong(()), - 'Parse type StringLong' - ); + assert(RLPTypeTrait::from_byte(i) == RLPType::StringLong(()), 'Parse type StringLong'); } else if i <= 0xf7 { - assert( - RLPTypeTrait::from_byte(i).unwrap() == RLPType::ListShort(()), - 'Parse type ListShort' - ); + assert(RLPTypeTrait::from_byte(i) == RLPType::ListShort(()), 'Parse type ListShort'); } else if i <= 0xff { - assert( - RLPTypeTrait::from_byte(i).unwrap() == RLPType::ListLong(()), 'Parse type ListLong' - ); + assert(RLPTypeTrait::from_byte(i) == RLPType::ListLong(()), 'Parse type ListLong'); } if i == 0xff { @@ -37,6 +29,18 @@ fn test_rlp_types() { }; } +#[test] +#[available_gas(9999999)] +fn test_rlp_empty() { + let res = rlp_decode(ArrayTrait::new().span()); + + assert(res.is_err(), 'should return an error'); + assert( + res.unwrap_err() == RLPError::RlpInvalidLength(RLP_INVALID_LENGTH), + 'err != RlpInvalidLength' + ); +} + #[test] #[available_gas(99999999)] fn test_rlp_decode_string() { @@ -93,6 +97,7 @@ fn test_rlp_decode_short_string() { let (res, len) = rlp_decode(arr.span()).unwrap(); assert(len == 1 + (0x9b - 0x80), 'Wrong len'); + // Remove the byte representing the data type arr.pop_front(); let expected_item = RLPItem::Bytes(arr.span()); @@ -101,7 +106,7 @@ fn test_rlp_decode_short_string() { #[test] #[available_gas(99999999)] -fn test_rlp_decode_long_string_len_of_len_1() { +fn test_rlp_decode_long_string_with_payload_len_on_1_byte() { let mut arr = array![ 0xb8, 0x3c, @@ -170,6 +175,7 @@ fn test_rlp_decode_long_string_len_of_len_1() { let (res, len) = rlp_decode(arr.span()).unwrap(); assert(len == 1 + (0xb8 - 0xb7) + 0x3c, 'Wrong len'); + // Remove the bytes representing the data type and their length arr.pop_front(); arr.pop_front(); let expected_item = RLPItem::Bytes(arr.span()); @@ -179,7 +185,7 @@ fn test_rlp_decode_long_string_len_of_len_1() { #[test] #[available_gas(99999999)] -fn test_rlp_decode_long_string_len_of_len_2() { +fn test_rlp_decode_long_string_with_payload_len_on_2_bytes() { let mut arr = array![ 0xb9, 0x01, @@ -447,6 +453,7 @@ fn test_rlp_decode_long_string_len_of_len_2() { let (res, len) = rlp_decode(arr.span()).unwrap(); assert(len == 1 + (0xb9 - 0xb7) + 0x0102, 'Wrong len'); + // Remove the bytes representing the data type and their length arr.pop_front(); arr.pop_front(); arr.pop_front(); From 72e6cf30a75fb2991206df6486ef42b86a92bfa3 Mon Sep 17 00:00:00 2001 From: Quentash Date: Thu, 12 Oct 2023 09:54:15 +0200 Subject: [PATCH 05/12] before merge --- crates/utils/src/math.cairo | 1 - crates/utils/src/tests/test_rlp.cairo | 73 +++++++++++++++++++++++++++ 2 files changed, 73 insertions(+), 1 deletion(-) diff --git a/crates/utils/src/math.cairo b/crates/utils/src/math.cairo index efbdb46d5..d4f98cef9 100644 --- a/crates/utils/src/math.cairo +++ b/crates/utils/src/math.cairo @@ -1,7 +1,6 @@ use integer::{ u256, u256_overflow_mul, u256_overflowing_add, u512, BoundedInt, u128_overflowing_mul }; -use math::Oneable; trait Exponentiation { /// Raise a number to a power. diff --git a/crates/utils/src/tests/test_rlp.cairo b/crates/utils/src/tests/test_rlp.cairo index be7b04adb..583418383 100644 --- a/crates/utils/src/tests/test_rlp.cairo +++ b/crates/utils/src/tests/test_rlp.cairo @@ -3,6 +3,7 @@ use array::{ArrayTrait, SpanTrait}; use result::ResultTrait; use utils::errors::{RLPError, RLP_INVALID_LENGTH}; +// Tests source : https://github.com/HerodotusDev/cairo-lib/blob/main/src/encoding/tests/test_rlp.cairo #[test] #[available_gas(9999999)] fn test_rlp_types() { @@ -103,6 +104,78 @@ fn test_rlp_decode_short_string() { assert(res == expected_item, 'Wrong value'); } +#[test] +#[available_gas(99999999)] +fn test_rlp_decode_short_string2() { + let mut arr = array![ + 0x39, + 0xc0, + 0x34, + 0xf6, + 0x6c, + 0x80, + 0x5a, + 0x9b, + 0x1e, + 0xa9, + 0x49, + 0xfd, + 0x89, + 0x2d, + 0x8f, + 0x8d, + 0xbb, + 0x94, + 0x84, + 0xcd, + 0x74, + 0xa4, + 0x3d, + 0xf3, + 0xa8, + 0xda, + 0x3b, + 0xf7 + ]; + + let (res, len) = rlp_decode(arr.span()).unwrap(); + assert(len == 1 + (0x9b - 0x80), 'Wrong len'); + + let mut arr = array![ + 0x8d, + 0x39, + 0xc0, + 0x34, + 0xf6, + 0x6c, + 0x80, + 0x5a, + 0xf3, + 0x1e, + 0xa9, + 0x49, + 0xfd, + 0x89, + 0x2d, + 0x8f, + 0xf7, + 0xbb, + 0x94, + 0x84, + 0xcd, + 0x74, + 0xa4, + 0x3d, + 0xa8, + 0xda, + 0x3b + ]; + // Remove the byte representing the data type + arr.pop_front(); + let expected_item = RLPItem::Bytes(arr.span()); + + assert(res == expected_item, 'Wrong value'); +} #[test] #[available_gas(99999999)] From caa13cb39fc12d2dc60c0d309d1682d972af8452 Mon Sep 17 00:00:00 2001 From: Quentash Date: Thu, 12 Oct 2023 21:11:18 +0200 Subject: [PATCH 06/12] first correct --- crates/utils/src/helpers.cairo | 33 ++++----- crates/utils/src/math.cairo | 33 --------- crates/utils/src/num.cairo | 33 +++++++++ crates/utils/src/rlp.cairo | 63 +++++++++-------- crates/utils/src/tests/test_helpers.cairo | 13 +++- crates/utils/src/tests/test_rlp.cairo | 84 ++--------------------- 6 files changed, 101 insertions(+), 158 deletions(-) diff --git a/crates/utils/src/helpers.cairo b/crates/utils/src/helpers.cairo index 1c48aa633..067fad7ab 100644 --- a/crates/utils/src/helpers.cairo +++ b/crates/utils/src/helpers.cairo @@ -7,6 +7,7 @@ use utils::constants::{ POW_256_16, }; use keccak::u128_split; +use utils::num::{Zero, One, SizeOf}; use utils::math::Bitshift; @@ -505,35 +506,36 @@ impl SpanExtension, +Drop> of SpanExtensionTrait { } } -impl SpanU8TryIntoU32 of TryInto, u32> { - fn try_into(self: Span) -> Option { - let len = self.len(); +trait BytesSerde { + /// Serialize/deserialize bytes into/from + /// an array of bytes. + fn serialize(self: @T, ref output: Array); + fn deserialize(self: @Span) -> Option; +} + +impl BytesSerdeU32Impl of BytesSerde { + fn serialize(self: @u32, ref output: Array) {} + fn deserialize(self: @Span) -> Option { + let len = (*self).len(); if len > 4 { return Option::None(()); } - - if self.is_empty() { - return Option::Some(0); - } - - let offset = len.into() - 1; + let offset: u32 = len - 1; let mut result: u32 = 0; - let mut i: usize = 0; + let mut i: u32 = 0; loop { - if i >= len { + if i == len { break (); } - let byte: u32 = (*self.at(i)).into(); - result += byte.shl(8 * (offset - i.into())); + let byte: u32 = (*(*self).at(i)).into(); + result += byte.shl(8 * (offset - i)); i += 1; }; - Option::Some(result) } } - #[generate_trait] impl U256Impl of U256Trait { /// Splits an u256 into 4 little endian u64. @@ -551,4 +553,3 @@ impl U256Impl of U256Trait { u256 { low: new_low, high: new_high } } } - diff --git a/crates/utils/src/math.cairo b/crates/utils/src/math.cairo index 938096f36..f49337b43 100644 --- a/crates/utils/src/math.cairo +++ b/crates/utils/src/math.cairo @@ -86,19 +86,6 @@ impl Felt252WrappingExpImpl of WrappingExponentiation { } } -impl U32ExpImpl of Exponentiation { - fn pow(self: u32, mut exponent: u32) -> u32 { - if self == 0 { - return 0; - } - if exponent == 0 { - return 1; - } else { - return self * Exponentiation::pow(self, exponent - 1); - } - } -} - // === BitShift === @@ -148,26 +135,6 @@ impl BitshiftImpl< } } -impl U32BitshiftImpl of Bitshift { - fn shl(self: u32, shift: u32) -> u32 { - if shift > 31 { - // 2.pow(shift) for shift > 32 will panic with 'u32_mul Overflow' - panic_with_felt252('u32_mul Overflow'); - } - let two = One::one() + One::one(); - self * two.pow(shift) - } - - fn shr(self: u32, shift: u32) -> u32 { - // early return to save gas if shift > nb_bits of T - if shift > shift.size_of() - One::one() { - panic_with_felt252('mul Overflow'); - } - let two = One::one() + One::one(); - self / two.pow(shift) - } -} - trait WrappingBitshift { // Shift a number left by a given number of bits. // If the shift is greater than 255, the result is 0. diff --git a/crates/utils/src/num.cairo b/crates/utils/src/num.cairo index b612d69e8..daa12e9cb 100644 --- a/crates/utils/src/num.cairo +++ b/crates/utils/src/num.cairo @@ -105,6 +105,22 @@ impl Felt252Zero of Zero { } } +impl U32Zero of Zero { + #[inline(always)] + fn zero() -> u32 { + 0 + } + + #[inline(always)] + fn is_zero(self: @u32) -> bool { + *self == Zero::zero() + } + + #[inline(always)] + fn is_non_zero(self: @u32) -> bool { + !self.is_zero() + } +} impl U128Zero of Zero { #[inline(always)] @@ -171,6 +187,23 @@ impl Felt252One of One { } } +impl U32One of One { + #[inline(always)] + fn one() -> u32 { + 1 + } + + #[inline(always)] + fn is_one(self: @u32) -> bool { + *self == One::one() + } + + #[inline(always)] + fn is_non_one(self: @u32) -> bool { + !self.is_one() + } +} + impl U128One of One { #[inline(always)] fn one() -> u128 { diff --git a/crates/utils/src/rlp.cairo b/crates/utils/src/rlp.cairo index 423d6c673..600366c1a 100644 --- a/crates/utils/src/rlp.cairo +++ b/crates/utils/src/rlp.cairo @@ -3,10 +3,10 @@ use option::OptionTrait; use array::{Array, ArrayTrait, Span, SpanTrait}; use clone::Clone; use traits::{Into, TryInto}; -use utils::helpers::SpanU8TryIntoU32; use utils::errors::{RLPError, RLP_INVALID_LENGTH}; +use utils::helpers::BytesSerde; -// @notice Enum with all possible RLP types +// All possible RLP types #[derive(Drop, PartialEq)] enum RLPType { String, @@ -16,38 +16,43 @@ enum RLPType { ListLong, } +#[derive(Drop, PartialEq)] +enum RLPItem { + Bytes: Span, + // Should be Span to allow for any depth/recursion, not yet supported by the compiler + List: Span> +} + #[generate_trait] impl RLPTypeImpl of RLPTypeTrait { - // @notice Returns RLPType from the leading byte - // @param byte Leading byte - // @return Result with RLPType + /// Returns RLPType from the leading byte + /// + /// # Arguments + /// + /// * `byte` - Leading byte + /// Return result with RLPType fn from_byte(byte: u8) -> RLPType { if byte <= 0x7f { - RLPType::String(()) + RLPType::String } else if byte <= 0xb7 { - RLPType::StringShort(()) + RLPType::StringShort } else if byte <= 0xbf { - RLPType::StringLong(()) + RLPType::StringLong } else if byte <= 0xf7 { - RLPType::ListShort(()) + RLPType::ListShort } else { - RLPType::ListLong(()) + RLPType::ListLong } } } -// @notice Represent a RLP item -#[derive(Drop, PartialEq)] -enum RLPItem { - Bytes: Span, - // Should be Span to allow for any depth/recursion, not yet supported by the compiler - List: Span> -} - -// @notice RLP decodes a rlp encoded byte array -// as described in https://ethereum.org/en/developers/docs/data-structures-and-encoding/rlp/ -// @param input RLP encoded bytes -// @return Result with RLPItem and size of the decoded item +/// RLP decodes a rlp encoded byte array +/// as described in https://ethereum.org/en/developers/docs/data-structures-and-encoding/rlp/ +/// +/// # Arguments +/// +/// * `input` - RLP encoded bytes +/// Return result with RLPItem and size of the decoded item fn rlp_decode(input: Span) -> Result<(RLPItem, usize), RLPError> { if input.len() == 0 { return Result::Err(RLPError::RlpInvalidLength(RLP_INVALID_LENGTH)); @@ -56,40 +61,40 @@ fn rlp_decode(input: Span) -> Result<(RLPItem, usize), RLPError> { let rlp_type = RLPTypeTrait::from_byte(prefix); match rlp_type { - RLPType::String(()) => { + RLPType::String => { let mut arr = array![prefix]; Result::Ok((RLPItem::Bytes(arr.span()), 1)) }, - RLPType::StringShort(()) => { + RLPType::StringShort => { let len = prefix.into() - 0x80; let res = input.slice(1, len); Result::Ok((RLPItem::Bytes(res), 1 + len)) }, - RLPType::StringLong(()) => { + RLPType::StringLong => { // Extract the amount of bytes representing the data payload length let len_len = prefix.into() - 0xb7; let len_span = input.slice(1, len_len); // Bytes => u32 - let len: u32 = len_span.try_into().unwrap(); + let len: u32 = len_span.deserialize().unwrap(); let res = input.slice(1 + len_len, len); Result::Ok((RLPItem::Bytes(res), 1 + len_len + len)) }, - RLPType::ListShort(()) => { + RLPType::ListShort => { let len = prefix.into() - 0xc0; let mut in = input.slice(1, len); let res = rlp_decode_list(ref in); Result::Ok((RLPItem::List(res), 1 + len)) }, - RLPType::ListLong(()) => { + RLPType::ListLong => { // Extract the amount of bytes representing the data payload length let len_len = prefix.into() - 0xf7; let len_span = input.slice(1, len_len); // Bytes => u32 - let len: u32 = len_span.try_into().unwrap(); + let len: u32 = len_span.deserialize().unwrap(); let mut in = input.slice(1 + len_len, len); let res = rlp_decode_list(ref in); Result::Ok((RLPItem::List(res), 1 + len_len + len)) diff --git a/crates/utils/src/tests/test_helpers.cairo b/crates/utils/src/tests/test_helpers.cairo index baf6eb5b0..d1ac93846 100644 --- a/crates/utils/src/tests/test_helpers.cairo +++ b/crates/utils/src/tests/test_helpers.cairo @@ -1,6 +1,6 @@ use utils::helpers; use utils::helpers::{ - SpanExtension, SpanExtensionTrait, ArrayExtension, ArrayExtensionTrait, U256Trait + SpanExtension, SpanExtensionTrait, ArrayExtension, ArrayExtensionTrait, U256Trait, BytesSerde }; use debug::PrintTrait; @@ -201,3 +201,14 @@ fn test_split_u256_into_u64_little() { assert(high_l == 0x0000450000DEFA00, 'split mismatch'); assert(low_l == 0x00200400000000AD, 'split mismatch'); } + +#[test] +#[available_gas(2000000000)] +fn test_bytes_serde_u32_deserialize() { + let input: Array = array![0xf4, 0x32, 0x15, 0x62]; + let res: Option = input.span().deserialize(); + + assert(res != Option::None, 'should have a value'); + let res = res.unwrap(); + assert(res == 0xf4321562, 'wrong result value'); +} diff --git a/crates/utils/src/tests/test_rlp.cairo b/crates/utils/src/tests/test_rlp.cairo index 583418383..f523ae5da 100644 --- a/crates/utils/src/tests/test_rlp.cairo +++ b/crates/utils/src/tests/test_rlp.cairo @@ -10,17 +10,15 @@ fn test_rlp_types() { let mut i = 0; loop { if i <= 0x7f { - assert(RLPTypeTrait::from_byte(i) == RLPType::String(()), 'Parse type String'); + assert(RLPTypeTrait::from_byte(i) == RLPType::String, 'Parse type String'); } else if i <= 0xb7 { - assert( - RLPTypeTrait::from_byte(i) == RLPType::StringShort(()), 'Parse type StringShort' - ); + assert(RLPTypeTrait::from_byte(i) == RLPType::StringShort, 'Parse type StringShort'); } else if i <= 0xbf { - assert(RLPTypeTrait::from_byte(i) == RLPType::StringLong(()), 'Parse type StringLong'); + assert(RLPTypeTrait::from_byte(i) == RLPType::StringLong, 'Parse type StringLong'); } else if i <= 0xf7 { - assert(RLPTypeTrait::from_byte(i) == RLPType::ListShort(()), 'Parse type ListShort'); + assert(RLPTypeTrait::from_byte(i) == RLPType::ListShort, 'Parse type ListShort'); } else if i <= 0xff { - assert(RLPTypeTrait::from_byte(i) == RLPType::ListLong(()), 'Parse type ListLong'); + assert(RLPTypeTrait::from_byte(i) == RLPType::ListLong, 'Parse type ListLong'); } if i == 0xff { @@ -104,78 +102,6 @@ fn test_rlp_decode_short_string() { assert(res == expected_item, 'Wrong value'); } -#[test] -#[available_gas(99999999)] -fn test_rlp_decode_short_string2() { - let mut arr = array![ - 0x39, - 0xc0, - 0x34, - 0xf6, - 0x6c, - 0x80, - 0x5a, - 0x9b, - 0x1e, - 0xa9, - 0x49, - 0xfd, - 0x89, - 0x2d, - 0x8f, - 0x8d, - 0xbb, - 0x94, - 0x84, - 0xcd, - 0x74, - 0xa4, - 0x3d, - 0xf3, - 0xa8, - 0xda, - 0x3b, - 0xf7 - ]; - - let (res, len) = rlp_decode(arr.span()).unwrap(); - assert(len == 1 + (0x9b - 0x80), 'Wrong len'); - - let mut arr = array![ - 0x8d, - 0x39, - 0xc0, - 0x34, - 0xf6, - 0x6c, - 0x80, - 0x5a, - 0xf3, - 0x1e, - 0xa9, - 0x49, - 0xfd, - 0x89, - 0x2d, - 0x8f, - 0xf7, - 0xbb, - 0x94, - 0x84, - 0xcd, - 0x74, - 0xa4, - 0x3d, - 0xa8, - 0xda, - 0x3b - ]; - // Remove the byte representing the data type - arr.pop_front(); - let expected_item = RLPItem::Bytes(arr.span()); - - assert(res == expected_item, 'Wrong value'); -} #[test] #[available_gas(99999999)] From 862b0035cbe0e0b3cb91fa62e2a0099d3196330e Mon Sep 17 00:00:00 2001 From: Quentash Date: Thu, 12 Oct 2023 23:03:29 +0200 Subject: [PATCH 07/12] added input too short checks and errors --- crates/utils/src/errors.cairo | 9 +- crates/utils/src/rlp.cairo | 54 ++++--- crates/utils/src/tests/test_rlp.cairo | 204 +++++++++++++++++++++++++- 3 files changed, 241 insertions(+), 26 deletions(-) diff --git a/crates/utils/src/errors.cairo b/crates/utils/src/errors.cairo index b163c8fde..babf13209 100644 --- a/crates/utils/src/errors.cairo +++ b/crates/utils/src/errors.cairo @@ -1,16 +1,19 @@ // LENGTH -const RLP_INVALID_LENGTH: felt252 = 'KKT: RlpInvalidLength'; +const RLP_EMPTY_INPUT: felt252 = 'KKT: RlpEmptyInput'; +const RLP_INPUT_TOO_SHORT: felt252 = 'KKT: RlpInputTooShort'; #[derive(Drop, Copy, PartialEq)] enum RLPError { - RlpInvalidLength: felt252, + RlpEmptyInput: felt252, + RlpInputTooShort: felt252, } impl RLPErrorIntoU256 of Into { fn into(self: RLPError) -> u256 { match self { - RLPError::RlpInvalidLength(error_message) => error_message.into(), + RLPError::RlpEmptyInput(error_message) => error_message.into(), + RLPError::RlpInputTooShort(error_message) => error_message.into(), } } } diff --git a/crates/utils/src/rlp.cairo b/crates/utils/src/rlp.cairo index 600366c1a..42732e0df 100644 --- a/crates/utils/src/rlp.cairo +++ b/crates/utils/src/rlp.cairo @@ -3,7 +3,7 @@ use option::OptionTrait; use array::{Array, ArrayTrait, Span, SpanTrait}; use clone::Clone; use traits::{Into, TryInto}; -use utils::errors::{RLPError, RLP_INVALID_LENGTH}; +use utils::errors::{RLPError, RLP_EMPTY_INPUT, RLP_INPUT_TOO_SHORT}; use utils::helpers::BytesSerde; // All possible RLP types @@ -55,7 +55,7 @@ impl RLPTypeImpl of RLPTypeTrait { /// Return result with RLPItem and size of the decoded item fn rlp_decode(input: Span) -> Result<(RLPItem, usize), RLPError> { if input.len() == 0 { - return Result::Err(RLPError::RlpInvalidLength(RLP_INVALID_LENGTH)); + return Result::Err(RLPError::RlpEmptyInput(RLP_EMPTY_INPUT)); } let prefix = *input.at(0); @@ -67,37 +67,55 @@ fn rlp_decode(input: Span) -> Result<(RLPItem, usize), RLPError> { }, RLPType::StringShort => { let len = prefix.into() - 0x80; + if input.len() < len + 1 { + return Result::Err(RLPError::RlpInputTooShort(RLP_INPUT_TOO_SHORT)); + } let res = input.slice(1, len); Result::Ok((RLPItem::Bytes(res), 1 + len)) }, RLPType::StringLong => { - // Extract the amount of bytes representing the data payload length - let len_len = prefix.into() - 0xb7; - let len_span = input.slice(1, len_len); + let len_of_len = prefix.into() - 0xb7; + if input.len() < len_of_len + 1 { + return Result::Err(RLPError::RlpInputTooShort(RLP_INPUT_TOO_SHORT)); + } + + let len_in_bytes = input.slice(1, len_of_len); + let len: u32 = len_in_bytes.deserialize().unwrap(); + if input.len() < len + len_of_len + 1 { + return Result::Err(RLPError::RlpInputTooShort(RLP_INPUT_TOO_SHORT)); + } - // Bytes => u32 - let len: u32 = len_span.deserialize().unwrap(); - let res = input.slice(1 + len_len, len); + let res = input.slice(1 + len_of_len, len); - Result::Ok((RLPItem::Bytes(res), 1 + len_len + len)) + Result::Ok((RLPItem::Bytes(res), 1 + len_of_len + len)) }, RLPType::ListShort => { let len = prefix.into() - 0xc0; - let mut in = input.slice(1, len); - let res = rlp_decode_list(ref in); + if input.len() < len + 1 { + return Result::Err(RLPError::RlpInputTooShort(RLP_INPUT_TOO_SHORT)); + } + + let mut list_input = input.slice(1, len); + let res = rlp_decode_list(ref list_input); Result::Ok((RLPItem::List(res), 1 + len)) }, RLPType::ListLong => { // Extract the amount of bytes representing the data payload length - let len_len = prefix.into() - 0xf7; - let len_span = input.slice(1, len_len); + let len_of_len = prefix.into() - 0xf7; + if input.len() < len_of_len + 1 { + return Result::Err(RLPError::RlpInputTooShort(RLP_INPUT_TOO_SHORT)); + } + + let len_in_bytes = input.slice(1, len_of_len); + let len: u32 = len_in_bytes.deserialize().unwrap(); + if input.len() < len + len_of_len + 1 { + return Result::Err(RLPError::RlpInputTooShort(RLP_INPUT_TOO_SHORT)); + } - // Bytes => u32 - let len: u32 = len_span.deserialize().unwrap(); - let mut in = input.slice(1 + len_len, len); - let res = rlp_decode_list(ref in); - Result::Ok((RLPItem::List(res), 1 + len_len + len)) + let mut list_input = input.slice(1 + len_of_len, len); + let res = rlp_decode_list(ref list_input); + Result::Ok((RLPItem::List(res), 1 + len_of_len + len)) } } } diff --git a/crates/utils/src/tests/test_rlp.cairo b/crates/utils/src/tests/test_rlp.cairo index f523ae5da..d1a020b93 100644 --- a/crates/utils/src/tests/test_rlp.cairo +++ b/crates/utils/src/tests/test_rlp.cairo @@ -1,7 +1,7 @@ use utils::rlp::{rlp_decode, RLPType, RLPTypeTrait, RLPItem}; use array::{ArrayTrait, SpanTrait}; use result::ResultTrait; -use utils::errors::{RLPError, RLP_INVALID_LENGTH}; +use utils::errors::{RLPError, RLP_EMPTY_INPUT, RLP_INPUT_TOO_SHORT}; // Tests source : https://github.com/HerodotusDev/cairo-lib/blob/main/src/encoding/tests/test_rlp.cairo #[test] @@ -34,10 +34,7 @@ fn test_rlp_empty() { let res = rlp_decode(ArrayTrait::new().span()); assert(res.is_err(), 'should return an error'); - assert( - res.unwrap_err() == RLPError::RlpInvalidLength(RLP_INVALID_LENGTH), - 'err != RlpInvalidLength' - ); + assert(res.unwrap_err() == RLPError::RlpEmptyInput(RLP_EMPTY_INPUT), 'err != RlpInvalidLength'); } #[test] @@ -103,6 +100,47 @@ fn test_rlp_decode_short_string() { assert(res == expected_item, 'Wrong value'); } +#[test] +#[available_gas(99999999)] +fn test_rlp_decode_short_string_input_too_short() { + let mut arr = array![ + 0x9b, + 0x5a, + 0x80, + 0x6c, + 0xf6, + 0x34, + 0xc0, + 0x39, + 0x8d, + 0x8f, + 0x2d, + 0x89, + 0xfd, + 0x49, + 0xa9, + 0x1e, + 0xf3, + 0x3d, + 0xa4, + 0x74, + 0xcd, + 0x84, + 0x94, + 0xbb, + 0xa8, + 0xda, + 0x3b + ]; + + let res = rlp_decode(arr.span()); + assert(res.is_err(), 'should return an RLPError'); + assert( + res.unwrap_err() == RLPError::RlpInputTooShort(RLP_INPUT_TOO_SHORT), + 'err != RlpInputTooShort' + ); +} + #[test] #[available_gas(99999999)] fn test_rlp_decode_long_string_with_payload_len_on_1_byte() { @@ -182,6 +220,80 @@ fn test_rlp_decode_long_string_with_payload_len_on_1_byte() { assert(res == expected_item, 'Wrong value'); } +#[test] +#[available_gas(99999999)] +fn test_rlp_decode_long_string_with_input_too_short() { + let mut arr = array![ + 0xb8, + 0x3c, + 0xf7, + 0xa1, + 0x7e, + 0xf9, + 0x59, + 0xd4, + 0x88, + 0x38, + 0x8d, + 0xdc, + 0x34, + 0x7b, + 0x3a, + 0x10, + 0xdd, + 0x85, + 0x43, + 0x1d, + 0x0c, + 0x37, + 0x98, + 0x6a, + 0x63, + 0xbd, + 0x18, + 0xba, + 0xa3, + 0x8d, + 0xb1, + 0xa4, + 0x81, + 0x6f, + 0x24, + 0xde, + 0xc3, + 0xec, + 0x16, + 0x6e, + 0xb3, + 0xb2, + 0xac, + 0xc4, + 0xc4, + 0xf7, + 0x79, + 0x04, + 0xba, + 0x76, + 0x3c, + 0x67, + 0xc6, + 0xd0, + 0x53, + 0xda, + 0xea, + 0x10, + 0x86, + 0x19, + ]; + + let res = rlp_decode(arr.span()); + assert(res.is_err(), 'should return an RLPError'); + assert( + res.unwrap_err() == RLPError::RlpInputTooShort(RLP_INPUT_TOO_SHORT), + 'err != RlpInputTooShort' + ); +} + #[test] #[available_gas(99999999)] fn test_rlp_decode_long_string_with_payload_len_on_2_bytes() { @@ -461,6 +573,20 @@ fn test_rlp_decode_long_string_with_payload_len_on_2_bytes() { assert(res == expected_item, 'Wrong value'); } + +#[test] +#[available_gas(99999999)] +fn test_rlp_decode_long_string_with_payload_len_too_short() { + let mut arr = array![0xb9, 0x01,]; + + let res = rlp_decode(arr.span()); + assert(res.is_err(), 'should return an RLPError'); + assert( + res.unwrap_err() == RLPError::RlpInputTooShort(RLP_INPUT_TOO_SHORT), + 'err != RlpInputTooShort' + ); +} + #[test] #[available_gas(99999999999)] fn test_rlp_decode_short_list() { @@ -478,6 +604,19 @@ fn test_rlp_decode_short_list() { assert(res == expected_item, 'Wrong value'); } +#[test] +#[available_gas(99999999999)] +fn test_rlp_decode_short_list_with_input_too_short() { + let mut arr = array![0xc9, 0x83, 0x35, 0x35, 0x89, 0x42, 0x83, 0x45, 0x38]; + + let res = rlp_decode(arr.span()); + assert(res.is_err(), 'should return an RLPError'); + assert( + res.unwrap_err() == RLPError::RlpInputTooShort(RLP_INPUT_TOO_SHORT), + 'err != RlpInputTooShort' + ); +} + #[test] #[available_gas(99999999999)] fn test_rlp_decode_long_list() { @@ -1587,3 +1726,58 @@ fn test_rlp_decode_long_list() { assert(res == expected_item, 'Wrong value'); } + +#[test] +#[available_gas(99999999999)] +fn test_rlp_decode_long_list_with_input_too_short() { + let mut arr = array![ + 0xf9, + 0x02, + 0x11, + 0xa0, + 0x77, + 0x70, + 0xcf, + 0x09, + 0xb5, + 0x06, + 0x7a, + 0x1b, + 0x35, + 0xdf, + 0x62, + 0xa9, + 0x24, + 0x89, + 0x81, + 0x75, + 0xce, + 0xae, + 0xec, + 0xad, + 0x1f, + 0x68, + 0xcd, + 0xb4 + ]; + + let res = rlp_decode(arr.span()); + assert(res.is_err(), 'should return an RLPError'); + assert( + res.unwrap_err() == RLPError::RlpInputTooShort(RLP_INPUT_TOO_SHORT), + 'err != RlpInputTooShort' + ); +} + +#[test] +#[available_gas(99999999999)] +fn test_rlp_decode_long_list_with_len_too_short() { + let mut arr = array![0xf9, 0x02,]; + + let res = rlp_decode(arr.span()); + assert(res.is_err(), 'should return an RLPError'); + assert( + res.unwrap_err() == RLPError::RlpInputTooShort(RLP_INPUT_TOO_SHORT), + 'err != RlpInputTooShort' + ); +} From db22acdcb7870b9c7a81e0d9f0190f4a841f7808 Mon Sep 17 00:00:00 2001 From: Quentash Date: Fri, 13 Oct 2023 12:10:11 +0200 Subject: [PATCH 08/12] secured decode_rlp inside decode_list --- crates/utils/src/rlp.cairo | 25 ++++++++++++++++++++----- 1 file changed, 20 insertions(+), 5 deletions(-) diff --git a/crates/utils/src/rlp.cairo b/crates/utils/src/rlp.cairo index 42732e0df..80d778909 100644 --- a/crates/utils/src/rlp.cairo +++ b/crates/utils/src/rlp.cairo @@ -98,7 +98,10 @@ fn rlp_decode(input: Span) -> Result<(RLPItem, usize), RLPError> { let mut list_input = input.slice(1, len); let res = rlp_decode_list(ref list_input); - Result::Ok((RLPItem::List(res), 1 + len)) + if res.is_err() { + return Result::Err(res.unwrap_err()); + } + Result::Ok((RLPItem::List(res.unwrap()), 1 + len)) }, RLPType::ListLong => { // Extract the amount of bytes representing the data payload length @@ -115,22 +118,31 @@ fn rlp_decode(input: Span) -> Result<(RLPItem, usize), RLPError> { let mut list_input = input.slice(1 + len_of_len, len); let res = rlp_decode_list(ref list_input); - Result::Ok((RLPItem::List(res), 1 + len_of_len + len)) + if res.is_err() { + return Result::Err(res.unwrap_err()); + } + Result::Ok((RLPItem::List(res.unwrap()), 1 + len_of_len + len)) } } } -fn rlp_decode_list(ref input: Span) -> Span> { +fn rlp_decode_list(ref input: Span) -> Result>, RLPError> { let mut i = 0; let len = input.len(); let mut output = ArrayTrait::new(); + let mut decode_error: Option = Option::None; loop { if i >= len { break (); } - let (decoded, decoded_len) = rlp_decode(input).unwrap(); + let res = rlp_decode(input); + if res.is_err() { + decode_error = Option::Some(res.unwrap_err()); + break; + } + let (decoded, decoded_len) = res.unwrap(); match decoded { RLPItem::Bytes(b) => { output.append(b); @@ -140,5 +152,8 @@ fn rlp_decode_list(ref input: Span) -> Span> { } i += decoded_len; }; - output.span() + if decode_error != Option::None { + return Result::Err(decode_error.unwrap()); + } + Result::Ok(output.span()) } From 7c69d8c9e75749f94dfa5f96ac30f06628c2050c Mon Sep 17 00:00:00 2001 From: Quentash Date: Fri, 13 Oct 2023 12:29:14 +0200 Subject: [PATCH 09/12] used ? --- crates/utils/src/rlp.cairo | 14 ++++---------- 1 file changed, 4 insertions(+), 10 deletions(-) diff --git a/crates/utils/src/rlp.cairo b/crates/utils/src/rlp.cairo index 80d778909..eeaf66461 100644 --- a/crates/utils/src/rlp.cairo +++ b/crates/utils/src/rlp.cairo @@ -97,11 +97,8 @@ fn rlp_decode(input: Span) -> Result<(RLPItem, usize), RLPError> { } let mut list_input = input.slice(1, len); - let res = rlp_decode_list(ref list_input); - if res.is_err() { - return Result::Err(res.unwrap_err()); - } - Result::Ok((RLPItem::List(res.unwrap()), 1 + len)) + let res = rlp_decode_list(ref list_input)?; + Result::Ok((RLPItem::List(res), 1 + len)) }, RLPType::ListLong => { // Extract the amount of bytes representing the data payload length @@ -117,11 +114,8 @@ fn rlp_decode(input: Span) -> Result<(RLPItem, usize), RLPError> { } let mut list_input = input.slice(1 + len_of_len, len); - let res = rlp_decode_list(ref list_input); - if res.is_err() { - return Result::Err(res.unwrap_err()); - } - Result::Ok((RLPItem::List(res.unwrap()), 1 + len_of_len + len)) + let res = rlp_decode_list(ref list_input)?; + Result::Ok((RLPItem::List(res), 1 + len_of_len + len)) } } } From a6436953165348689352b492ca0de336ae72e031 Mon Sep 17 00:00:00 2001 From: Quentash Date: Mon, 16 Oct 2023 10:18:15 +0200 Subject: [PATCH 10/12] review correction --- crates/utils/src/helpers.cairo | 2 -- crates/utils/src/rlp.cairo | 18 ++++++++---------- 2 files changed, 8 insertions(+), 12 deletions(-) diff --git a/crates/utils/src/helpers.cairo b/crates/utils/src/helpers.cairo index 067fad7ab..ac8a97c41 100644 --- a/crates/utils/src/helpers.cairo +++ b/crates/utils/src/helpers.cairo @@ -509,12 +509,10 @@ impl SpanExtension, +Drop> of SpanExtensionTrait { trait BytesSerde { /// Serialize/deserialize bytes into/from /// an array of bytes. - fn serialize(self: @T, ref output: Array); fn deserialize(self: @Span) -> Option; } impl BytesSerdeU32Impl of BytesSerde { - fn serialize(self: @u32, ref output: Array) {} fn deserialize(self: @Span) -> Option { let len = (*self).len(); if len > 4 { diff --git a/crates/utils/src/rlp.cairo b/crates/utils/src/rlp.cairo index eeaf66461..a4f2fb7f4 100644 --- a/crates/utils/src/rlp.cairo +++ b/crates/utils/src/rlp.cairo @@ -101,7 +101,6 @@ fn rlp_decode(input: Span) -> Result<(RLPItem, usize), RLPError> { Result::Ok((RLPItem::List(res), 1 + len)) }, RLPType::ListLong => { - // Extract the amount of bytes representing the data payload length let len_of_len = prefix.into() - 0xf7; if input.len() < len_of_len + 1 { return Result::Err(RLPError::RlpInputTooShort(RLP_INPUT_TOO_SHORT)); @@ -124,19 +123,18 @@ fn rlp_decode_list(ref input: Span) -> Result>, RLPError> { let mut i = 0; let len = input.len(); let mut output = ArrayTrait::new(); - let mut decode_error: Option = Option::None; - loop { + let mut decode_error: Option = loop { if i >= len { - break (); + break Option::None; } let res = rlp_decode(input); - if res.is_err() { - decode_error = Option::Some(res.unwrap_err()); - break; - } - let (decoded, decoded_len) = res.unwrap(); + + let (decoded, decoded_len) = match res { + Result::Ok(res_dec) => { res_dec }, + Result::Err(err) => { break Option::Some(err); } + }; match decoded { RLPItem::Bytes(b) => { output.append(b); @@ -146,7 +144,7 @@ fn rlp_decode_list(ref input: Span) -> Result>, RLPError> { } i += decoded_len; }; - if decode_error != Option::None { + if decode_error.is_some() { return Result::Err(decode_error.unwrap()); } Result::Ok(output.span()) From df4e404982f6ca452c98373e0f00f95c5be470f3 Mon Sep 17 00:00:00 2001 From: Quentash Date: Wed, 18 Oct 2023 09:59:13 +0200 Subject: [PATCH 11/12] correction naming --- crates/utils/src/helpers.cairo | 8 +++---- crates/utils/src/rlp.cairo | 42 +++++++++++++++++----------------- 2 files changed, 25 insertions(+), 25 deletions(-) diff --git a/crates/utils/src/helpers.cairo b/crates/utils/src/helpers.cairo index ac8a97c41..918ddf947 100644 --- a/crates/utils/src/helpers.cairo +++ b/crates/utils/src/helpers.cairo @@ -509,12 +509,12 @@ impl SpanExtension, +Drop> of SpanExtensionTrait { trait BytesSerde { /// Serialize/deserialize bytes into/from /// an array of bytes. - fn deserialize(self: @Span) -> Option; + fn deserialize(self: Span) -> Option; } impl BytesSerdeU32Impl of BytesSerde { - fn deserialize(self: @Span) -> Option { - let len = (*self).len(); + fn deserialize(self: Span) -> Option { + let len = self.len(); if len > 4 { return Option::None(()); } @@ -525,7 +525,7 @@ impl BytesSerdeU32Impl of BytesSerde { if i == len { break (); } - let byte: u32 = (*(*self).at(i)).into(); + let byte: u32 = (*self.at(i)).into(); result += byte.shl(8 * (offset - i)); i += 1; diff --git a/crates/utils/src/rlp.cairo b/crates/utils/src/rlp.cairo index a4f2fb7f4..ab8ce13af 100644 --- a/crates/utils/src/rlp.cairo +++ b/crates/utils/src/rlp.cairo @@ -67,54 +67,54 @@ fn rlp_decode(input: Span) -> Result<(RLPItem, usize), RLPError> { }, RLPType::StringShort => { let len = prefix.into() - 0x80; - if input.len() < len + 1 { + if input.len() <= len { return Result::Err(RLPError::RlpInputTooShort(RLP_INPUT_TOO_SHORT)); } - let res = input.slice(1, len); + let decoded_string = input.slice(1, len); - Result::Ok((RLPItem::Bytes(res), 1 + len)) + Result::Ok((RLPItem::Bytes(decoded_string), 1 + len)) }, RLPType::StringLong => { - let len_of_len = prefix.into() - 0xb7; - if input.len() < len_of_len + 1 { + let len_bytes_count = prefix.into() - 0xb7; + if input.len() <= len_bytes_count { return Result::Err(RLPError::RlpInputTooShort(RLP_INPUT_TOO_SHORT)); } - let len_in_bytes = input.slice(1, len_of_len); - let len: u32 = len_in_bytes.deserialize().unwrap(); - if input.len() < len + len_of_len + 1 { + let string_len_bytes = input.slice(1, len_bytes_count); + let string_len: u32 = string_len_bytes.deserialize().unwrap(); + if input.len() <= string_len + len_bytes_count { return Result::Err(RLPError::RlpInputTooShort(RLP_INPUT_TOO_SHORT)); } - let res = input.slice(1 + len_of_len, len); + let decoded_string = input.slice(1 + len_bytes_count, string_len); - Result::Ok((RLPItem::Bytes(res), 1 + len_of_len + len)) + Result::Ok((RLPItem::Bytes(decoded_string), 1 + len_bytes_count + string_len)) }, RLPType::ListShort => { let len = prefix.into() - 0xc0; - if input.len() < len + 1 { + if input.len() <= len { return Result::Err(RLPError::RlpInputTooShort(RLP_INPUT_TOO_SHORT)); } let mut list_input = input.slice(1, len); - let res = rlp_decode_list(ref list_input)?; - Result::Ok((RLPItem::List(res), 1 + len)) + let decoded_list = rlp_decode_list(ref list_input)?; + Result::Ok((RLPItem::List(decoded_list), 1 + len)) }, RLPType::ListLong => { - let len_of_len = prefix.into() - 0xf7; - if input.len() < len_of_len + 1 { + let len_bytes_count = prefix.into() - 0xf7; + if input.len() <= len_bytes_count { return Result::Err(RLPError::RlpInputTooShort(RLP_INPUT_TOO_SHORT)); } - let len_in_bytes = input.slice(1, len_of_len); - let len: u32 = len_in_bytes.deserialize().unwrap(); - if input.len() < len + len_of_len + 1 { + let list_len_bytes = input.slice(1, len_bytes_count); + let list_len: u32 = list_len_bytes.deserialize().unwrap(); + if input.len() < list_len + len_bytes_count + 1 { return Result::Err(RLPError::RlpInputTooShort(RLP_INPUT_TOO_SHORT)); } - let mut list_input = input.slice(1 + len_of_len, len); - let res = rlp_decode_list(ref list_input)?; - Result::Ok((RLPItem::List(res), 1 + len_of_len + len)) + let mut list_input = input.slice(1 + len_bytes_count, list_len); + let decoded_list = rlp_decode_list(ref list_input)?; + Result::Ok((RLPItem::List(decoded_list), 1 + len_bytes_count + list_len)) } } } From a0fb18689ff655c013bec17857c0fbccf5c96aa7 Mon Sep 17 00:00:00 2001 From: Quentash Date: Wed, 18 Oct 2023 10:10:58 +0200 Subject: [PATCH 12/12] forgot fmt oopsie --- crates/utils/src/tests/test_helpers.cairo | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/crates/utils/src/tests/test_helpers.cairo b/crates/utils/src/tests/test_helpers.cairo index 317a9ce02..b96d37548 100644 --- a/crates/utils/src/tests/test_helpers.cairo +++ b/crates/utils/src/tests/test_helpers.cairo @@ -1,5 +1,7 @@ use utils::helpers; -use utils::helpers::{SpanExtension, SpanExtTrait, ArrayExtension, ArrayExtTrait, U256Trait, BytesSerde}; +use utils::helpers::{ + SpanExtension, SpanExtTrait, ArrayExtension, ArrayExtTrait, U256Trait, BytesSerde +}; use utils::helpers::{ByteArrayExTrait}; use debug::PrintTrait;