diff --git a/src/data_structures/eth_mpt.cairo b/src/data_structures/eth_mpt.cairo index 09d5150..3d8820c 100644 --- a/src/data_structures/eth_mpt.cairo +++ b/src/data_structures/eth_mpt.cairo @@ -1,5 +1,5 @@ use cairo_lib::hashing::keccak::keccak_cairo_words64; -use cairo_lib::encoding::rlp::{RLPItem, rlp_decode}; +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}; @@ -24,14 +24,16 @@ enum MPTNode { // @param hashes 16 hashes of children // @param value value of the node Branch: (Span, Words64), + // @param hash hash of the next node + LazyBranch: u256, // @param shared_nibbles // @param next_node // @param nibbles_skip number of nibbles to skip in shared nibbles // @param n_nibbles number of shared nibbles Extension: (Words64, u256, usize, usize), // @param key_end - // @param value of the node - // @param nibbles_skip Number of nibbles to skip in the key end + // @param value value of the node + // @param nibbles_skip number of nibbles to skip in the key end // @param n_nibbles number of nibbles in key_end Leaf: (Words64, Words64, usize, usize) } @@ -57,16 +59,33 @@ impl MPTImpl of MPTTrait { let mut proof_index: usize = 0; let mut key_pow2: u256 = pow(2, (key_len.into() - 1) * 4); + let proof_len = proof.len(); + loop { - if proof_index == proof.len() { + if proof_index == proof_len { break Result::Err('Proof reached end'); } + let node = *proof.at(proof_index); - let (decoded, rlp_byte_len) = match MPTTrait::decode_rlp_node(node) { - Result::Ok(d) => d, - Result::Err(e) => { - break Result::Err(e); + // If it's not the last node and more than 9 words, it must be a branch node + let (decoded, rlp_byte_len) = if proof_index != proof_len - 1 && node.len() > 9 { + let current_nibble = (key / key_pow2) & 0xf; + // Unwrap impossible to fail, as we are masking with 0xf, meaning the result is always a nibble + match MPTTrait::lazy_rlp_decode_branch_node( + node, current_nibble.try_into().unwrap() + ) { + Result::Ok(d) => d, + Result::Err(e) => { + break Result::Err(e); + } + } + } else { + match MPTTrait::decode_rlp_node(node) { + Result::Ok(d) => d, + Result::Err(e) => { + break Result::Err(e); + } } }; @@ -103,6 +122,10 @@ impl MPTImpl of MPTTrait { }; key_pow2 = key_pow2 / 16; }, + MPTNode::LazyBranch(next_node) => { + current_hash = next_node; + key_pow2 = key_pow2 / 16; + }, MPTNode::Extension(( shared_nibbles, next_node, nibbles_skip, n_nibbles )) => { @@ -235,7 +258,7 @@ impl MPTImpl of MPTTrait { ); } - let (current_hash, _) = *l.at(i); + let (current_hash, _) = (*l.at(i)); nibble_hashes.append(current_hash); i += 1; } @@ -275,6 +298,26 @@ impl MPTImpl of MPTTrait { } } } + + + fn lazy_rlp_decode_branch_node( + rlp: Words64, current_nibble: u8 + ) -> Result<(MPTNode, usize), felt252> { + let (lazy_item, rlp_byte_len) = rlp_decode_list_lazy( + rlp, array![current_nibble.into()].span() + )?; + match lazy_item { + 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') + } + } + } + } + // @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 diff --git a/src/data_structures/tests/test_eth_mpt.cairo b/src/data_structures/tests/test_eth_mpt.cairo index 74bc162..d3f6768 100644 --- a/src/data_structures/tests/test_eth_mpt.cairo +++ b/src/data_structures/tests/test_eth_mpt.cairo @@ -96,7 +96,6 @@ fn test_decode_rlp_node_branch() { 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 @@ -112,6 +111,9 @@ fn test_decode_rlp_node_branch() { i += 1; }; }, + MPTNode::LazyBranch(_) => { + panic_with_felt252('Branch node differs'); + }, MPTNode::Extension(_) => { panic_with_felt252('Branch node differs'); }, @@ -121,6 +123,89 @@ fn test_decode_rlp_node_branch() { } } +#[test] +#[available_gas(9999999999)] +fn test_lazy_rlp_node_branch() { + let rlp_node = array![ + 0x09cf7077a01102f9, + 0xa962df351b7a06b5, + 0xadecaece75818924, + 0x0c4044a8b4cd681f, + 0x85a31ea0f44ac173, + 0x045c6d4661b25ad0, + 0x1a9fc1344568fe87, + 0x35361adc184b5c4b, + 0x4c2ca0b471500260, + 0x1846d1d34035ce04, + 0x8366e5a5533c3072, + 0x0c80a8368d4f30c1, + 0xa9a0eecd3ffaf56a, + 0xc4d37d4bc58d77dc, + 0xb0fe61d139e72282, + 0x3717d5dcb2ceeec0, + 0xa05138a6378e5bf0, + 0xdd62df56554d5fa9, + 0x9b56ae97049962c2, + 0x9307207bdafd8ecd, + 0xd71897db4cded3f8, + 0x2238146d06d439a0, + 0x74a843e9c94aaf6e, + 0xb91dd8b05fc2a9a9, + 0x03e2b336138c1d86, + 0x6ab4637ccc7aa04c, + 0x25a141a0c9b318a4, + 0x7a396b316173cb6b, + 0x13bb1b4967885ada, + 0x25818a3515a03001, + 0xc736fe137193c42e, + 0x3497a1fb11b74680, + 0x5f78007a1829bb91, + 0xd3429168a0ae52f8, + 0xdfce8b1ca7faab16, + 0x254e10b2db1d2049, + 0x1f2256e8c490dc0a, + 0x5036dca058964a53, + 0xa714a3a8fd342599, + 0xb59dc7a83baeb0db, + 0xc060242ace690c55, + 0xb020a0a3c1c4ad07, + 0xe19e05b055663b68, + 0xc1cb6b504b4ed003, + 0x11b1dab792630039, + 0x8ea0e7420366c278, + 0xd91c0f63fb45ebed, + 0xcb17225718eb3697, + 0x03e21bb715f3d5c6, + 0xa014269bd9e83cb0, + 0x6f985af63da32379, + 0x69b9c2e4e6f9e7d5, + 0x3999be4e94086b73, + 0xf309e62f6114864a, + 0x71201ad0d73465a0, + 0xce46b9552afba44a, + 0xa22aadff2d22c364, + 0xb12ac97334928ad1, + 0xb8fe8bc2f9bfa0fd, + 0xb0c3c818b6a92dbf, + 0x4714bdc0b10ce86f, + 0xe229ff6121c4f738, + 0x3c6961147fa02f50, + 0x5ea3bb1b02a54e70, + 0x8f459e43f602c572, + 0x8fea4837d02e2498, + 0x805fb3e2 + ]; + + let expected = 0xE7420366C27811B1DAB792630039C1CB6B504B4ED003E19E05B055663B68B020; + + let (decoded, rlp_byte_len) = MPTTrait::lazy_rlp_decode_branch_node(rlp_node.span(), 0xa) + .unwrap(); + assert(rlp_byte_len == 66 * 8 + 4, 'Wrong RLP len'); + + let expected_node = MPTNode::LazyBranch(expected); + assert(decoded == expected_node, 'Lazy branch node diffes'); +} + #[test] #[available_gas(9999999999)] fn test_decode_rlp_node_leaf_odd() { diff --git a/src/encoding/rlp.cairo b/src/encoding/rlp.cairo index 4093152..f9d0701 100644 --- a/src/encoding/rlp.cairo +++ b/src/encoding/rlp.cairo @@ -1,5 +1,6 @@ -use cairo_lib::utils::types::words64::{Words64, Words64Trait, reverse_endianness_u64}; +use cairo_lib::utils::types::words64::{Words64, Words64Trait, reverse_endianness_u64, pow2}; use cairo_lib::utils::types::byte::Byte; +use cairo_lib::utils::array::span_contains; // @notice Enum with all possible RLP types // For more info: https://ethereum.org/en/developers/docs/data-structures-and-encoding/rlp/ @@ -36,7 +37,7 @@ impl RLPTypeImpl of RLPTypeTrait { } // @notice Represent a RLP item -#[derive(Drop)] +#[derive(Drop, PartialEq)] enum RLPItem { Bytes: (Words64, usize), // Should be Span to allow for any depth/recursion, not yet supported by the compiler @@ -141,43 +142,104 @@ fn rlp_decode_list(ref input: Words64, len: usize) -> Result { - fn eq(lhs: @RLPItem, rhs: @RLPItem) -> bool { - match lhs { - RLPItem::Bytes(b) => { - match rhs { - RLPItem::Bytes(b2) => { - b == b2 - }, - RLPItem::List(_) => false - } +fn rlp_decode_list_lazy(input: Words64, lazy: Span) -> Result<(RLPItem, usize), felt252> { + let mut output = ArrayTrait::new(); + let mut lazy_index = 0; + + let list_prefix: u32 = (*input.at(0) & 0xff).try_into().unwrap(); + let list_type = RLPTypeTrait::from_byte(list_prefix.try_into().unwrap()).unwrap(); + let (mut current_input_index, len) = match list_type { + RLPType::String(()) => { + return Result::Err('Not a list'); + }, + RLPType::StringShort(()) => { + return Result::Err('Not a list'); + }, + RLPType::StringLong(()) => { + return Result::Err('Not a list'); + }, + RLPType::ListShort(()) => (1, list_prefix - 0xc0), + RLPType::ListLong(()) => { + let len_len = list_prefix - 0xf7; + let len_span = input.slice_le(6, len_len); + // Enough to store 4.29 GB (fits in u32) + assert(len_span.len() == 1 && *len_span.at(0) <= 0xffffffff, 'Len of len too big'); + + // len fits in 32 bits, confirmed by previous assertion + let len = reverse_endianness_u64(*len_span.at(0), Option::Some(len_len.into())) + .try_into() + .unwrap(); + (1 + len_len, len) + } + }; + + let rlp_byte_len = current_input_index + len; + + loop { + if output.len() == lazy.len() { + break Result::Ok((RLPItem::List(output.span()), rlp_byte_len)); + } + + if current_input_index >= len { + break Result::Err('Too many items to decode'); + } + + let current_word = current_input_index / 8; + let current_word_offset = 7 - (current_input_index % 8); + + let pow2_shift = pow2((7 - current_word_offset) * 8); + let prefix = (*input.at(current_word) / pow2_shift) & 0xff; + + let rlp_type = RLPTypeTrait::from_byte(prefix.try_into().unwrap()).unwrap(); + let (item_start_skip, item_len) = match rlp_type { + RLPType::String(()) => { + (0, 1) }, - 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; - } - } - } + RLPType::StringShort(()) => { + let len = prefix - 0x80; + (1, len) + }, + RLPType::StringLong(()) => { + let len_len = prefix - 0xb7; + + let current_word = (current_input_index + 1) / 8; + let current_word_offset = 7 - ((current_input_index + 1) % 8); + + let len_span = input + .slice_le(current_word * 8 + current_word_offset, len_len.try_into().unwrap()); + // Enough to store 4.29 GB (fits in u32) + assert(len_span.len() == 1 && *len_span.at(0) <= 0xffffffff, 'Len of len too big'); + + // len fits in 32 bits, confirmed by previous assertion + let len: u32 = reverse_endianness_u64( + *len_span.at(0), Option::Some(len_len.try_into().unwrap()) + ) + .try_into() + .unwrap(); + + (1 + len_len, len.into()) + }, + RLPType::ListShort(()) => { + panic_with_felt252('Recursive list not supported') + }, + RLPType::ListLong(()) => { + panic_with_felt252('Recursive list not supported') } + }; + + current_input_index += item_start_skip.try_into().unwrap(); + if span_contains(lazy, lazy_index) { + let current_word = current_input_index / 8; + let current_word_offset = 7 - (current_input_index % 8); + let start = current_word * 8 + current_word_offset; + + let item_len = item_len.try_into().unwrap(); + let decoded = input.slice_le(start, item_len); + output.append((decoded, item_len)); } - } - fn ne(lhs: @RLPItem, rhs: @RLPItem) -> bool { - !(lhs == rhs) + current_input_index += item_len.try_into().unwrap(); + + lazy_index += 1; } } - diff --git a/src/encoding/tests/test_rlp.cairo b/src/encoding/tests/test_rlp.cairo index c02cefb..e797c4f 100644 --- a/src/encoding/tests/test_rlp.cairo +++ b/src/encoding/tests/test_rlp.cairo @@ -1,4 +1,4 @@ -use cairo_lib::encoding::rlp::{RLPItem, rlp_decode}; +use cairo_lib::encoding::rlp::{RLPItem, rlp_decode, rlp_decode_list_lazy}; #[test] #[available_gas(99999999)] @@ -162,6 +162,40 @@ fn test_rlp_decode_short_list() { assert(res == expected_item, 'Wrong value'); } +#[test] +#[available_gas(99999999)] +fn test_rlp_lazy_decode_short_list() { + let mut arr = array![0x45834289353583c9, 0x9238]; + let rlp_byte_len = 10; + + let res = rlp_decode_list_lazy(arr.span(), array![].span()).unwrap(); + let expected_res = (RLPItem::List(array![].span()), rlp_byte_len); + assert(res == expected_res, 'Wrong value indexes: empty'); + + let res = rlp_decode_list_lazy(arr.span(), array![1].span()).unwrap(); + let expected_res = (RLPItem::List(array![(array![0x42].span(), 1)].span()), rlp_byte_len); + assert(res == expected_res, 'Wrong value indexes: 1'); + + let res = rlp_decode_list_lazy(arr.span(), array![0, 1, 2].span()).unwrap(); + let mut expected_res = ( + RLPItem::List( + array![ + (array![0x893535].span(), 3), (array![0x42].span(), 1), (array![0x923845].span(), 3) + ] + .span() + ), + rlp_byte_len + ); + assert(res == expected_res, 'Wrong value: indexes: 0, 1, 2'); + + let res = rlp_decode_list_lazy(arr.span(), array![0, 2].span()).unwrap(); + let mut expected_res = ( + RLPItem::List(array![(array![0x893535].span(), 3), (array![0x923845].span(), 3)].span()), + rlp_byte_len + ); + assert(res == expected_res, 'Wrong value: indexes: 0, 2'); +} + #[test] #[available_gas(99999999)] fn test_rlp_decode_long_list() { @@ -324,3 +358,248 @@ fn test_rlp_decode_long_list() { let expected_item = RLPItem::List(expected_res.span()); assert(res == expected_item, 'Wrong value'); } + +#[test] +#[available_gas(99999999)] +fn test_rlp_lazy_decode_long_list() { + let mut arr = array![ + 0x09cf7077a01102f9, + 0xa962df351b7a06b5, + 0xadecaece75818924, + 0x0c4044a8b4cd681f, + 0x85a31ea0f44ac173, + 0x045c6d4661b25ad0, + 0x1a9fc1344568fe87, + 0x35361adc184b5c4b, + 0x4c2ca0b471500260, + 0x1846d1d34035ce04, + 0x8366e5a5533c3072, + 0x0c80a8368d4f30c1, + 0xa9a0eecd3ffaf56a, + 0xc4d37d4bc58d77dc, + 0xb0fe61d139e72282, + 0x3717d5dcb2ceeec0, + 0xa05138a6378e5bf0, + 0xdd62df56554d5fa9, + 0x9b56ae97049962c2, + 0x9307207bdafd8ecd, + 0xd71897db4cded3f8, + 0x2238146d06d439a0, + 0x74a843e9c94aaf6e, + 0xb91dd8b05fc2a9a9, + 0x03e2b336138c1d86, + 0x6ab4637ccc7aa04c, + 0x25a141a0c9b318a4, + 0x7a396b316173cb6b, + 0x13bb1b4967885ada, + 0x25818a3515a03001, + 0xc736fe137193c42e, + 0x3497a1fb11b74680, + 0x5f78007a1829bb91, + 0xd3429168a0ae52f8, + 0xdfce8b1ca7faab16, + 0x254e10b2db1d2049, + 0x1f2256e8c490dc0a, + 0x5036dca058964a53, + 0xa714a3a8fd342599, + 0xb59dc7a83baeb0db, + 0xc060242ace690c55, + 0xb020a0a3c1c4ad07, + 0xe19e05b055663b68, + 0xc1cb6b504b4ed003, + 0x11b1dab792630039, + 0x8ea0e7420366c278, + 0xd91c0f63fb45ebed, + 0xcb17225718eb3697, + 0x03e21bb715f3d5c6, + 0xa014269bd9e83cb0, + 0x6f985af63da32379, + 0x69b9c2e4e6f9e7d5, + 0x3999be4e94086b73, + 0xf309e62f6114864a, + 0x71201ad0d73465a0, + 0xce46b9552afba44a, + 0xa22aadff2d22c364, + 0xb12ac97334928ad1, + 0xb8fe8bc2f9bfa0fd, + 0xb0c3c818b6a92dbf, + 0x4714bdc0b10ce86f, + 0xe229ff6121c4f738, + 0x3c6961147fa02f50, + 0x5ea3bb1b02a54e70, + 0x8f459e43f602c572, + 0x8fea4837d02e2498, + 0x805fb3e2 + ]; + let rlp_byte_len = 66 * 8 + 4; + + let mut expected_res_full = array![ + array![0x1b7a06b509cf7077, 0x75818924a962df35, 0xb4cd681fadecaece, 0xf44ac1730c4044a8] + .span(), + array![0x4661b25ad085a31e, 0x344568fe87045c6d, 0xdc184b5c4b1a9fc1, 0xb47150026035361a] + .span(), + array![0xd1d34035ce044c2c, 0xe5a5533c30721846, 0xa8368d4f30c18366, 0xeecd3ffaf56a0c80] + .span(), + array![0xd37d4bc58d77dca9, 0xfe61d139e72282c4, 0x17d5dcb2ceeec0b0, 0x5138a6378e5bf037] + .span(), + array![0xdd62df56554d5fa9, 0x9b56ae97049962c2, 0x9307207bdafd8ecd, 0xd71897db4cded3f8] + .span(), + array![ // 5 + 0x6e2238146d06d439, 0xa974a843e9c94aaf, 0x86b91dd8b05fc2a9, 0x4c03e2b336138c1d] + .span(), + array![0x18a46ab4637ccc7a, 0xcb6b25a141a0c9b3, 0x5ada7a396b316173, 0x300113bb1b496788] + .span(), + array![0x93c42e25818a3515, 0xb74680c736fe1371, 0x29bb913497a1fb11, 0xae52f85f78007a18] + .span(), + array![0xa7faab16d3429168, 0xdb1d2049dfce8b1c, 0xc490dc0a254e10b2, 0x58964a531f2256e8] + .span(), + array![0xa8fd3425995036dc, 0xa83baeb0dba714a3, 0x2ace690c55b59dc7, 0xa3c1c4ad07c06024] + .span(), + array![0x05b055663b68b020, 0x6b504b4ed003e19e, 0xdab792630039c1cb, 0xe7420366c27811b1] + .span(), + array![0x1c0f63fb45ebed8e, 0x17225718eb3697d9, 0xe21bb715f3d5c6cb, 0x14269bd9e83cb003] + .span(), + array![0x6f985af63da32379, 0x69b9c2e4e6f9e7d5, 0x3999be4e94086b73, 0xf309e62f6114864a] + .span(), + array![0x4a71201ad0d73465, 0x64ce46b9552afba4, 0xd1a22aadff2d22c3, 0xfdb12ac97334928a] + .span(), + array![0x2dbfb8fe8bc2f9bf, 0xe86fb0c3c818b6a9, 0xf7384714bdc0b10c, 0x2f50e229ff6121c4] + .span(), + array![0xa54e703c6961147f, 0x02c5725ea3bb1b02, 0x2e24988f459e43f6, 0x5fb3e28fea4837d0] + .span(), + array![].span() + ]; + + let res = rlp_decode_list_lazy(arr.span(), array![].span()).unwrap(); + let expected_res = (RLPItem::List(array![].span()), rlp_byte_len); + assert(res == expected_res, 'Wrong value indexes: empty'); + + let res = rlp_decode_list_lazy(arr.span(), array![0].span()).unwrap(); + let expected_res = (RLPItem::List(array![(*expected_res_full.at(0), 32)].span()), rlp_byte_len); + assert(res == expected_res, 'Wrong value indexes: 0'); + + let res = rlp_decode_list_lazy(arr.span(), array![1].span()).unwrap(); + let expected_res = (RLPItem::List(array![(*expected_res_full.at(1), 32)].span()), rlp_byte_len); + assert(res == expected_res, 'Wrong value indexes: 1'); + + let res = rlp_decode_list_lazy(arr.span(), array![0xa].span()).unwrap(); + let expected_res = ( + RLPItem::List(array![(*expected_res_full.at(0xa), 32)].span()), rlp_byte_len + ); + assert(res == expected_res, 'Wrong value indexes: 10'); + + let res = rlp_decode_list_lazy(arr.span(), array![0x5, 0x9, 0xf].span()).unwrap(); + let expected_res = ( + RLPItem::List( + array![ + (*expected_res_full.at(0x5), 32), + (*expected_res_full.at(0x9), 32), + (*expected_res_full.at(0xf), 32) + ] + .span() + ), + rlp_byte_len + ); + assert(res == expected_res, 'Wrong value indexes: 5, 9, 15'); +} + +#[test] +#[available_gas(99999999)] +fn test_rlp_decode_list_long_string() { + let arr = array![ + 0x7235e356aca05bf8, + 0x7f0b03476f57b94f, + 0x4760f75aaf1d2720, + 0xa9c2173ae53aab1f, + 0xf338d438b8ed276f, + 0x27777eada3968dad, + 0x53189e661865fe38, + 0xc101f7b5d6dffd52, + 0x65454695474abcbb, + 0x4567644756674547, + 0x5663776535476567, + 0xfa77645733, + ]; + + let (res, len) = rlp_decode(arr.span()).unwrap(); + assert(len == 1 + (0xf8 - 0xf7) + 0x5b, 'Wrong len'); + + let expected_res = array![ + ( + array![0x57b94f7235e356ac, 0x1d27207f0b03476f, 0x3aab1f4760f75aaf, 0xed276fa9c2173ae5,] + .span(), + 32 + ), + ( + array![ + 0xada3968dadf338d4, + 0x661865fe3827777e, + 0xb5d6dffd5253189e, + 0x95474abcbbc101f7, + 0x4756674547654546, + 0x6535476567456764, + 0xfa77645733566377, + ] + .span(), + 56 + ) + ]; + let expected_item = RLPItem::List(expected_res.span()); + assert(res == expected_item, 'Wrong value'); +} + +#[test] +#[available_gas(99999999)] +fn test_rlp_lazy_decode_list_long_string() { + let arr = array![ + 0x7235e356aca05bf8, + 0x7f0b03476f57b94f, + 0x4760f75aaf1d2720, + 0xa9c2173ae53aab1f, + 0xf338d438b8ed276f, + 0x27777eada3968dad, + 0x53189e661865fe38, + 0xc101f7b5d6dffd52, + 0x65454695474abcbb, + 0x4567644756674547, + 0x5663776535476567, + 0xfa77645733, + ]; + let rlp_byte_len = 11 * 8 + 5; + + let expected_res_full = array![ + array![0x57b94f7235e356ac, 0x1d27207f0b03476f, 0x3aab1f4760f75aaf, 0xed276fa9c2173ae5,] + .span(), + array![ + 0xada3968dadf338d4, + 0x661865fe3827777e, + 0xb5d6dffd5253189e, + 0x95474abcbbc101f7, + 0x4756674547654546, + 0x6535476567456764, + 0xfa77645733566377, + ] + .span(), + ]; + + let res = rlp_decode_list_lazy(arr.span(), array![].span()).unwrap(); + let expected_res = (RLPItem::List(array![].span()), rlp_byte_len); + assert(res == expected_res, 'Wrong value indexes: empty'); + + let res = rlp_decode_list_lazy(arr.span(), array![0].span()).unwrap(); + let expected_res = (RLPItem::List(array![(*expected_res_full.at(0), 32)].span()), rlp_byte_len); + assert(res == expected_res, 'Wrong value indexes: 0'); + + let res = rlp_decode_list_lazy(arr.span(), array![1].span()).unwrap(); + let expected_res = (RLPItem::List(array![(*expected_res_full.at(1), 56)].span()), rlp_byte_len); + assert(res == expected_res, 'Wrong value indexes: 1'); + + let res = rlp_decode_list_lazy(arr.span(), array![0, 1].span()).unwrap(); + let expected_res = ( + RLPItem::List( + array![(*expected_res_full.at(0), 32), (*expected_res_full.at(1), 56)].span() + ), + rlp_byte_len + ); + assert(res == expected_res, 'Wrong value indexes: 0, 1'); +}