Skip to content

Commit

Permalink
5.2 Keccak Discards Leading Zero Bytes in Last Little Endian Words64
Browse files Browse the repository at this point in the history
  • Loading branch information
tiagofneto committed Oct 26, 2023
1 parent 287c3ec commit 35b363a
Show file tree
Hide file tree
Showing 4 changed files with 48 additions and 39 deletions.
41 changes: 24 additions & 17 deletions src/data_structures/eth_mpt.cairo
Original file line number Diff line number Diff line change
Expand Up @@ -61,18 +61,23 @@ impl MPTImpl of MPTTrait {
if proof_index == proof.len() {
break Result::Err('Proof reached end');
}

let node = *proof.at(proof_index);

let hash = MPTTrait::hash_rlp_node(node);
assert(hash == current_hash, 'Element not matching');

let decoded = match MPTTrait::decode_rlp_node(node) {
Result::Ok(decoded) => decoded,
let (decoded, rlp_byte_len) = match MPTTrait::decode_rlp_node(node) {
Result::Ok(d) => d,
Result::Err(e) => {
break Result::Err(e);
}
};

let mut last_word_byte_len = rlp_byte_len % 8;
if last_word_byte_len == 0 {
last_word_byte_len = 8;
}

let hash = MPTTrait::hash_rlp_node(node, last_word_byte_len);
assert(hash == current_hash, 'Element not matching');

match decoded {
MPTNode::Branch((
nibbles, value
Expand Down Expand Up @@ -212,9 +217,9 @@ impl MPTImpl of MPTTrait {

// @notice Decodes an RLP encoded node
// @param rlp RLP encoded node
// @return Result with the decoded node
fn decode_rlp_node(rlp: Words64) -> Result<MPTNode, felt252> {
let (item, _) = rlp_decode(rlp)?;
// @return Result with the decoded node and the RLP byte length
fn decode_rlp_node(rlp: Words64) -> Result<(MPTNode, usize), felt252> {
let (item, rlp_byte_len) = rlp_decode(rlp)?;
match item {
RLPItem::Bytes(_) => Result::Err('Invalid RLP for node'),
RLPItem::List(l) => {
Expand All @@ -225,7 +230,9 @@ impl MPTImpl of MPTTrait {
loop {
if i == 16 {
let (value, _) = *l.at(16);
break Result::Ok(MPTNode::Branch((nibble_hashes.span(), value)));
break Result::Ok(
(MPTNode::Branch((nibble_hashes.span(), value)), rlp_byte_len)
);
}

let (current_hash, _) = *l.at(i);
Expand All @@ -244,21 +251,21 @@ impl MPTImpl of MPTTrait {
if prefix == 0 {
match second.try_into() {
Option::Some(n) => Result::Ok(
MPTNode::Extension((first, n, 2, n_nibbles - 1))
(MPTNode::Extension((first, n, 2, n_nibbles - 1)), rlp_byte_len)
),
Option::None(_) => 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))
(MPTNode::Extension((first, n, 1, n_nibbles)), rlp_byte_len)
),
Option::None(_) => Result::Err('Invalid next node')
}
} else if prefix == 2 {
Result::Ok(MPTNode::Leaf((first, second, 2, n_nibbles - 1)))
Result::Ok((MPTNode::Leaf((first, second, 2, n_nibbles - 1)), rlp_byte_len))
} else if prefix == 3 {
Result::Ok(MPTNode::Leaf((first, second, 1, n_nibbles)))
Result::Ok((MPTNode::Leaf((first, second, 1, n_nibbles)), rlp_byte_len))
} else {
Result::Err('Invalid RLP prefix')
}
Expand All @@ -268,11 +275,11 @@ impl MPTImpl of MPTTrait {
}
}
}

// @notice keccak256 hashes an RLP encoded node
// @param rlp RLP encoded node
// @param last_word_bytes number of bytes in the last worf of the RLP encoded node
// @return keccak256 hash of the node
fn hash_rlp_node(rlp: Words64) -> u256 {
keccak_cairo_words64(rlp)
fn hash_rlp_node(rlp: Words64, last_word_bytes: usize) -> u256 {
keccak_cairo_words64(rlp, last_word_bytes)
}
}
14 changes: 10 additions & 4 deletions src/data_structures/tests/test_eth_mpt.cairo
Original file line number Diff line number Diff line change
Expand Up @@ -94,7 +94,9 @@ fn test_decode_rlp_node_branch() {
]
.span();

let decoded = MPTTrait::decode_rlp_node(rlp_node.span()).unwrap();
let (decoded, rlp_byte_len) = MPTTrait::decode_rlp_node(rlp_node.span()).unwrap();
assert(rlp_byte_len == 66 * 8 + 4, 'Wrong RLP len');

match decoded {
MPTNode::Branch((
hashes, value
Expand Down Expand Up @@ -153,7 +155,9 @@ fn test_decode_rlp_node_leaf_odd() {
0xc71a5df8340f
];

let decoded = MPTTrait::decode_rlp_node(rlp_node.span()).unwrap();
let (decoded, rlp_byte_len) = MPTTrait::decode_rlp_node(rlp_node.span()).unwrap();
assert(rlp_byte_len == 13 * 8, 'Wrong RLP len');

let expected_node = MPTNode::Leaf((expected_key_end.span(), expected_value.span(), 1, 57));
assert(decoded == expected_node, 'Even leaf node differs');
}
Expand Down Expand Up @@ -192,7 +196,9 @@ fn test_decode_rlp_node_leaf_even() {
0xc71a5df8340f
];

let decoded = MPTTrait::decode_rlp_node(rlp_node.span()).unwrap();
let (decoded, rlp_byte_len) = MPTTrait::decode_rlp_node(rlp_node.span()).unwrap();
assert(rlp_byte_len == 13 * 8, 'Wrong RLP len');

let expected_node = MPTNode::Leaf((expected_key_end.span(), expected_value.span(), 2, 56));
assert(decoded == expected_node, 'Even leaf node differs');
}
Expand All @@ -216,7 +222,7 @@ fn test_hash_rlp_node() {
0xc71a5df8340f6249
];

let hash = MPTTrait::hash_rlp_node(rlp_node.span());
let hash = MPTTrait::hash_rlp_node(rlp_node.span(), 8);
assert(
hash == 0x035F9A54E8BEE015293EB9791C7FEC6A4A111DB8B32464597B6F8E63B1167FA1,
'Wrong node rlp hash'
Expand Down
28 changes: 12 additions & 16 deletions src/hashing/keccak.cairo
Original file line number Diff line number Diff line change
Expand Up @@ -3,29 +3,25 @@ use keccak::cairo_keccak;

// @notice Wrapper arround cairo_keccak that format the input for compatibility with EVM
// @param words The input data, as a list of 64-bit little-endian words
// @param last_word_bytes Number of bytes in the last word
// @return The keccak hash of the input, matching the output of the EVM's keccak256 opcode
fn keccak_cairo_words64(words: Words64) -> u256 {
fn keccak_cairo_words64(words: Words64, last_word_bytes: usize) -> u256 {
let n = words.len();

let mut keccak_input = ArrayTrait::new();
let mut i: usize = 0;
if n > 1 {
loop {
if i >= n - 1 {
break ();
}
keccak_input.append(*words.at(i));
i += 1;
};
}
loop {
if i >= n - 1 {
break ();
}
keccak_input.append(*words.at(i));
i += 1;
};

let mut last = *words.at(n - 1);
let mut last_word_bytes = bytes_used_u64(last);
if last_word_bytes == 8 {
keccak_input.append(last);
last = 0;
last_word_bytes = 0;
cairo_keccak(ref keccak_input, 0, 0)
} else {
cairo_keccak(ref keccak_input, last, last_word_bytes)
}

cairo_keccak(ref keccak_input, last, last_word_bytes)
}
4 changes: 2 additions & 2 deletions src/hashing/tests/test_keccak.cairo
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ use cairo_lib::hashing::keccak::keccak_cairo_words64;
fn test_keccak_cairo_word64_full_byte() {
let input = array![0xffffffffffffffff];

let res = keccak_cairo_words64(input.span());
let res = keccak_cairo_words64(input.span(), 8);
assert(
res == 0xAF7D4E460ACF8E540E682A9EE91EA1C08C1615C3889D75EB0A70660A4BFB0BAD,
'Keccak output not matching'
Expand All @@ -17,7 +17,7 @@ fn test_keccak_cairo_word64_full_byte() {
fn test_keccak_cairo_word64_remainder() {
let mut input = array![0x23FDAE89F78C76AB, 0x45D2BC4A];

let res = keccak_cairo_words64(input.span());
let res = keccak_cairo_words64(input.span(), 4);
assert(
res == 0x82CBD5B00CD06A188C831D69CB9629C92A2D5E7A78CEA913C5F9AFF62E66BBB9,
'Keccak output not matching'
Expand Down

0 comments on commit 35b363a

Please sign in to comment.