From 8c74f18de2fde767be3fb6588524ac706eb26851 Mon Sep 17 00:00:00 2001 From: tiagofneto Date: Mon, 30 Oct 2023 19:04:08 +0600 Subject: [PATCH] 5.7 Words64TryIntoU256LE Does Not Automatically Pad Input --- src/data_structures/eth_mpt.cairo | 44 +++++++++------ src/data_structures/tests/test_eth_mpt.cairo | 9 ++- src/utils/tests/test_bitwise.cairo | 21 ++++++- src/utils/types/tests/test_words64.cairo | 40 +++++++------ src/utils/types/words64.cairo | 59 +++++++++++++------- 5 files changed, 117 insertions(+), 56 deletions(-) diff --git a/src/data_structures/eth_mpt.cairo b/src/data_structures/eth_mpt.cairo index 3d8820c..655bea2 100644 --- a/src/data_structures/eth_mpt.cairo +++ b/src/data_structures/eth_mpt.cairo @@ -1,8 +1,8 @@ use cairo_lib::hashing::keccak::keccak_cairo_words64; use cairo_lib::encoding::rlp::{RLPItem, rlp_decode, rlp_decode_list_lazy}; use cairo_lib::utils::types::byte::{Byte, ByteTrait}; -use cairo_lib::utils::bitwise::{right_shift, left_shift}; -use cairo_lib::utils::types::words64::{Words64, Words64Trait, Words64TryIntoU256LE}; +use cairo_lib::utils::bitwise::{right_shift, left_shift, reverse_endianness_u256}; +use cairo_lib::utils::types::words64::{Words64, Words64Trait}; use cairo_lib::utils::math::pow; // @notice Ethereum Merkle Patricia Trie struct @@ -113,9 +113,9 @@ impl MPTImpl of MPTTrait { if current_hash_words.len() == 0 { 0 } else { - match current_hash_words.try_into() { - Option::Some(h) => h, - Option::None(_) => { + match current_hash_words.as_u256_be(32) { + Result::Ok(h) => reverse_endianness_u256(h), + Result::Err(_) => { break Result::Err('Invalid hash'); } } @@ -272,18 +272,28 @@ impl MPTImpl of MPTTrait { let n_nibbles = (first_len * 2) - 1; if prefix == 0 { - match second.try_into() { - Option::Some(n) => Result::Ok( - (MPTNode::Extension((first, n, 2, n_nibbles - 1)), rlp_byte_len) + match second.as_u256_be(32) { + Result::Ok(n) => Result::Ok( + ( + MPTNode::Extension( + (first, reverse_endianness_u256(n), 2, n_nibbles - 1) + ), + rlp_byte_len + ) ), - Option::None(_) => Result::Err('Invalid next node') + Result::Err(_) => Result::Err('Invalid next node') } } else if prefix == 1 { - match second.try_into() { - Option::Some(n) => Result::Ok( - (MPTNode::Extension((first, n, 1, n_nibbles)), rlp_byte_len) + match second.as_u256_be(32) { + Result::Ok(n) => Result::Ok( + ( + MPTNode::Extension( + (first, reverse_endianness_u256(n), 1, n_nibbles) + ), + rlp_byte_len + ) ), - Option::None(_) => Result::Err('Invalid next node') + Result::Err(_) => Result::Err('Invalid next node') } } else if prefix == 2 { Result::Ok((MPTNode::Leaf((first, second, 2, n_nibbles - 1)), rlp_byte_len)) @@ -310,9 +320,11 @@ impl MPTImpl of MPTTrait { RLPItem::Bytes(_) => Result::Err('Invalid RLP for node'), RLPItem::List(l) => { let (hash_words, _) = *l.at(0); - match (hash_words).try_into() { - Option::Some(h) => Result::Ok((MPTNode::LazyBranch(h), rlp_byte_len)), - Option::None(_) => Result::Err('Invalid hash') + match hash_words.as_u256_be(32) { + Result::Ok(h) => Result::Ok( + (MPTNode::LazyBranch(reverse_endianness_u256(h)), rlp_byte_len) + ), + Result::Err(_) => Result::Err('Invalid hash') } } } diff --git a/src/data_structures/tests/test_eth_mpt.cairo b/src/data_structures/tests/test_eth_mpt.cairo index d3f6768..ff1e06d 100644 --- a/src/data_structures/tests/test_eth_mpt.cairo +++ b/src/data_structures/tests/test_eth_mpt.cairo @@ -1,5 +1,6 @@ use cairo_lib::data_structures::eth_mpt::{MPTNode, MPTTrait}; -use cairo_lib::utils::types::words64::Words64TryIntoU256LE; +use cairo_lib::utils::types::words64::{Words64, Words64Trait}; +use cairo_lib::utils::bitwise::reverse_endianness_u256; #[test] #[available_gas(9999999999)] @@ -107,7 +108,11 @@ fn test_decode_rlp_node_branch() { if i >= hashes.len() { break (); } - assert((*hashes.at(i)).try_into().unwrap() == *expected.at(i), 'Wrong hash'); + assert( + reverse_endianness_u256((*hashes.at(i)).as_u256_be(32).unwrap()) == *expected + .at(i), + 'Wrong hash' + ); i += 1; }; }, diff --git a/src/utils/tests/test_bitwise.cairo b/src/utils/tests/test_bitwise.cairo index b486d17..58e0665 100644 --- a/src/utils/tests/test_bitwise.cairo +++ b/src/utils/tests/test_bitwise.cairo @@ -1,4 +1,4 @@ -use cairo_lib::utils::bitwise::{left_shift, right_shift, bit_length}; +use cairo_lib::utils::bitwise::{left_shift, right_shift, bit_length, reverse_endianness_u256}; #[test] #[available_gas(999999)] @@ -39,3 +39,22 @@ fn test_bit_length() { fn test_bit_length_most_significant_bit_one() { assert(bit_length(4294967295_u32) == 32, 'bit length of 2^32-1 is 32'); } + +#[test] +#[available_gas(999999)] +fn test_reverse_endianness_u256() { + assert(reverse_endianness_u256(0) == 0, 'reverse endianness of 0'); + assert( + reverse_endianness_u256( + 1 + ) == 0x0100000000000000000000000000000000000000000000000000000000000000, + 'reverse endianness of 1' + ); + assert( + reverse_endianness_u256( + 0x1307645868aee0028be496b378bfeee2bac59d1239549a8ef4bff9009af5ef + ) == 0xEFF59A00F9BFF48E9A5439129DC5BAE2EEBF78B396E48B02E0AE685864071300, + 'reverse endianness of 31 bytes' + ); +} + diff --git a/src/utils/types/tests/test_words64.cairo b/src/utils/types/tests/test_words64.cairo index a34abdf..9618d34 100644 --- a/src/utils/types/tests/test_words64.cairo +++ b/src/utils/types/tests/test_words64.cairo @@ -1,5 +1,5 @@ use cairo_lib::utils::types::words64::{ - Words64, Words64Trait, Words64TryIntoU256LE, reverse_endianness_u64, bytes_used_u64 + Words64, Words64Trait, reverse_endianness_u64, bytes_used_u64 }; #[test] @@ -47,33 +47,33 @@ fn test_slice_words64_le_single_word_full() { } #[test] -#[should_panic] #[available_gas(99999999)] -fn test_into_u256_le_wrong_num_words() { - let words = array![0x83498349, 0x83479735927498, 0x12345623ff458695, 0xabcd344, 0xabcdef3345] +fn test_as_u256_be_full() { + let words = array![ + 0x2e8b632605e21673, 0x480829ebcee54bc4, 0xb6f239256ff310f9, 0x09898da43a5d35f4, + ] .span(); - let res: u256 = words.try_into().unwrap(); + + let expected = 0x7316e20526638b2ec44be5ceeb290848f910f36f2539f2b6f4355d3aa48d8909; + assert(words.as_u256_be(32).unwrap() == expected, 'Wrong value'); } #[test] #[available_gas(99999999)] -fn test_into_u256_le() { - let words = array![ - 0x2e8b632605e21673, 0x480829ebcee54bc4, 0xb6f239256ff310f9, 0x09898da43a5d35f4, - ] - .span(); +fn test_as_u256_be_not_full() { + let words = array![0x2e8b632605e21673, 0x480829ebcee54bc4, 0xb6f2392a].span(); - let expected = 0x09898DA43A5D35F4B6F239256FF310F9480829EBCEE54BC42E8B632605E21673; - assert(words.try_into().unwrap() == expected, 'Wrong value'); + let expected = 0x7316e20526638b2ec44be5ceeb2908482a39f2b6; + assert(words.as_u256_be(20).unwrap() == expected, 'Wrong value'); } #[test] #[available_gas(99999999)] -fn test_into_u256_le_not_full() { - let words = array![0x2e8b632605e21673, 0x4bc4, 0, 0,].span(); +fn test_as_u256_be_not_full_start() { + let words = array![0x2e8b632605e20000, 0x480829ebcee54bc4, 0xb6f2392a].span(); - let expected = 0x4BC42E8B632605E21673; - assert(words.try_into().unwrap() == expected, 'Wrong value'); + let expected = 0xe20526638b2ec44be5ceeb2908482a39f2b6; + assert(words.as_u256_be(20).unwrap() == expected, 'Wrong value'); } #[test] @@ -92,6 +92,14 @@ fn test_reverse_endianness_not_full() { assert(reverse_endianness_u64(val, Option::Some(3)) == expected, 'Wrong value'); } +#[test] +#[available_gas(99999999)] +fn test_reverse_endianness_not_full_padding() { + let val = 0xabcdef; + let expected = 0xefcdab00; + assert(reverse_endianness_u64(val, Option::Some(4)) == expected, 'Wrong value'); +} + #[test] #[available_gas(99999999)] fn test_bytes_used() { diff --git a/src/utils/types/words64.cairo b/src/utils/types/words64.cairo index 2537bff..8c4d39b 100644 --- a/src/utils/types/words64.cairo +++ b/src/utils/types/words64.cairo @@ -5,40 +5,57 @@ use cairo_lib::utils::bitwise::left_shift; // Example: 0x34957c6d8a83f9cff74578dea9 is represented as [0xcff9838a6d7c9534, 0xa9de7845f7] type Words64 = Span; -impl Words64TryIntoU256LE of TryInto { - // @notice Converts a span of 64 bit little endian words into a little endian u256 - fn try_into(self: Words64) -> Option { - if self.len() > 4 { - return Option::None(()); +#[generate_trait] +impl Words64Impl of Words64Trait { + // @notice Converts little endian 64 bit words to a big endian u256 + // @param bytes_used The number of bytes used + // @return The big endian u256 representation of the words + fn as_u256_be(self: Words64, bytes_used: usize) -> Result { + let len = self.len(); + + if len > 4 { + return Result::Err('Too many words'); } - if self.len() == 0 { - return Option::Some(0); + if len == 0 || bytes_used == 0 { + return Result::Ok(0); } - let pows = array![ - 0x10000000000000000, // 2 ** 64 - 0x100000000000000000000000000000000, // 2 ** 128 - 0x1000000000000000000000000000000000000000000000000 // 2 ** 192 - ]; + let mut len_last_word = bytes_used % 8; + if len_last_word == 0 { + len_last_word = 8; + } + + let mut output: u256 = reverse_endianness_u64( + (*self.at(len - 1)), Option::Some(len_last_word) + ) + .into(); + + let word_pow2 = 0x10000000000000000; // 2 ** 64 + let mut current_pow2: u256 = if len_last_word == 8 { + word_pow2 + } else { + pow2(len_last_word * 8).into() + }; - let mut output: u256 = (*self.at(0)).into(); - let mut i: usize = 1; + let mut i = 1; loop { - if i == self.len() { - break Option::Some(output); + if i == len { + break Result::Ok(output); } - // left shift and add - output = output | ((*self.at(i)).into() * *pows.at(i - 1)); + output = output + | (reverse_endianness_u64(*self.at(len - i - 1), Option::None(())).into() + * current_pow2); + + if i < len - 1 { + current_pow2 = current_pow2 * word_pow2; + } i += 1; } } -} -#[generate_trait] -impl Words64Impl of Words64Trait { // @notice Slices 64 bit little endian words from a starting byte and a length // @param start The starting byte // The starting byte is counted from the left. Example: 0xabcdef -> byte 0 is 0xab, byte 1 is 0xcd...