Skip to content

Commit

Permalink
5.7 Words64TryIntoU256LE Does Not Automatically Pad Input
Browse files Browse the repository at this point in the history
  • Loading branch information
tiagofneto committed Oct 30, 2023
1 parent 0af6115 commit 8c74f18
Show file tree
Hide file tree
Showing 5 changed files with 117 additions and 56 deletions.
44 changes: 28 additions & 16 deletions src/data_structures/eth_mpt.cairo
Original file line number Diff line number Diff line change
@@ -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
Expand Down Expand Up @@ -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');
}
}
Expand Down Expand Up @@ -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))
Expand All @@ -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')
}
}
}
Expand Down
9 changes: 7 additions & 2 deletions src/data_structures/tests/test_eth_mpt.cairo
Original file line number Diff line number Diff line change
@@ -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)]
Expand Down Expand Up @@ -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;
};
},
Expand Down
21 changes: 20 additions & 1 deletion src/utils/tests/test_bitwise.cairo
Original file line number Diff line number Diff line change
@@ -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)]
Expand Down Expand Up @@ -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'
);
}

40 changes: 24 additions & 16 deletions src/utils/types/tests/test_words64.cairo
Original file line number Diff line number Diff line change
@@ -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]
Expand Down Expand Up @@ -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]
Expand All @@ -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() {
Expand Down
59 changes: 38 additions & 21 deletions src/utils/types/words64.cairo
Original file line number Diff line number Diff line change
Expand Up @@ -5,40 +5,57 @@ use cairo_lib::utils::bitwise::left_shift;
// Example: 0x34957c6d8a83f9cff74578dea9 is represented as [0xcff9838a6d7c9534, 0xa9de7845f7]
type Words64 = Span<u64>;

impl Words64TryIntoU256LE of TryInto<Words64, u256> {
// @notice Converts a span of 64 bit little endian words into a little endian u256
fn try_into(self: Words64) -> Option<u256> {
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<u256, felt252> {
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...
Expand Down

0 comments on commit 8c74f18

Please sign in to comment.