From dbe0f058e1d60c2f2549ff0aa9a63b235ea2409a Mon Sep 17 00:00:00 2001 From: Quentash Date: Mon, 13 Nov 2023 11:32:53 +0100 Subject: [PATCH 01/12] rlp decode + tests --- Scarb.lock | 4 + src/encoding/Scarb.toml | 1 + src/encoding/src/lib.cairo | 1 + src/encoding/src/rlp.cairo | 113 ++ src/encoding/src/tests.cairo | 1 + src/encoding/src/tests/rlp_test.cairo | 1894 +++++++++++++++++++++++++ src/numeric/Scarb.toml | 3 + src/numeric/src/integers.cairo | 33 + src/numeric/src/lib.cairo | 1 + 9 files changed, 2051 insertions(+) create mode 100644 src/encoding/src/rlp.cairo create mode 100644 src/encoding/src/tests/rlp_test.cairo create mode 100644 src/numeric/src/integers.cairo diff --git a/Scarb.lock b/Scarb.lock index f6adb87a..6e5f826c 100644 --- a/Scarb.lock +++ b/Scarb.lock @@ -28,6 +28,7 @@ name = "alexandria_encoding" version = "0.1.0" dependencies = [ "alexandria_math", + "alexandria_numeric", ] [[package]] @@ -48,6 +49,9 @@ version = "0.1.0" [[package]] name = "alexandria_numeric" version = "0.1.0" +dependencies = [ + "alexandria_math", +] [[package]] name = "alexandria_searching" diff --git a/src/encoding/Scarb.toml b/src/encoding/Scarb.toml index 2dcd0e80..2e4256a4 100644 --- a/src/encoding/Scarb.toml +++ b/src/encoding/Scarb.toml @@ -9,3 +9,4 @@ fmt.workspace = true [dependencies] alexandria_math = { path = "../math" } +alexandria_numeric = { path = "../numeric" } diff --git a/src/encoding/src/lib.cairo b/src/encoding/src/lib.cairo index e6aec3b2..0d146631 100644 --- a/src/encoding/src/lib.cairo +++ b/src/encoding/src/lib.cairo @@ -1,5 +1,6 @@ mod base64; mod reversible; +mod rlp; #[cfg(test)] mod tests; diff --git a/src/encoding/src/rlp.cairo b/src/encoding/src/rlp.cairo new file mode 100644 index 00000000..cab2463d --- /dev/null +++ b/src/encoding/src/rlp.cairo @@ -0,0 +1,113 @@ +use alexandria_data_structures::array_ext::SpanTraitExt; +use alexandria_numeric::integers::U32Trait; +use core::result::ResultTrait; + +// Possible RLP types +#[derive(Drop, PartialEq)] +enum RLPType { + String, + List +} + +#[derive(Drop, Copy, PartialEq)] +enum RLPItem { + String: Span, + List: Span +} + +#[generate_trait] +impl RLPImpl of RLPTrait { + /// Returns RLPType from the leading byte with + /// its offset in the array as well as its size. + /// + /// # Arguments + /// * `input` - Array of byte to decode + /// # Returns + /// * `(RLPType, offset, size)` - A tuple containing the RLPType + /// the offset and the size of the RLPItem to decode + /// # Errors + /// * RLPError::EmptyInput - if the input is empty + /// * RLPError::InputTooShort - if the input is too short for a given + fn decode_type(input: Span) -> (RLPType, u32, u32) { + let input_len = input.len(); + if input_len == 0 { + panic_with_felt252('empty input'); + } + + let prefix_byte = *input[0]; + + if prefix_byte < 0x80 { // Char + (RLPType::String, 0, 1) + } else if prefix_byte < 0xb8 { // Short String + (RLPType::String, 1, prefix_byte.into() - 0x80) + } else if prefix_byte < 0xc0 { // Long String + let len_bytes_count: u32 = (prefix_byte - 0xb7).into(); + if input_len <= len_bytes_count { + panic_with_felt252('input too short'); + } + let string_len_bytes = input.slice(1, len_bytes_count); + let string_len: u32 = U32Trait::from_bytes(string_len_bytes).unwrap(); + + (RLPType::String, 1 + len_bytes_count, string_len) + } else if prefix_byte < 0xf8 { // Short List + (RLPType::List, 1, prefix_byte.into() - 0xc0) + } else { // Long List + let len_bytes_count = prefix_byte.into() - 0xf7; + if input.len() <= len_bytes_count { + panic_with_felt252('input too short'); + } + + let list_len_bytes = input.slice(1, len_bytes_count); + let list_len: u32 = U32Trait::from_bytes(list_len_bytes).unwrap(); + (RLPType::List, 1 + len_bytes_count, list_len) + } + } + + /// RLP decodes a rlp encoded byte array + /// as described in https://ethereum.org/en/developers/docs/data-structures-and-encoding/rlp/ + /// + /// # Arguments + /// * `input` - Array of bytes to decode + /// # Returns + /// * `Span` - Span of RLPItem + /// # Errors + /// * RLPError::InputTooShort - if the input is too short for a given + fn decode(input: Span) -> Span { + let mut output: Array = Default::default(); + let input_len = input.len(); + + let (rlp_type, offset, len) = RLPTrait::decode_type(input); + + if input_len < offset + len { + panic_with_felt252('input too short'); + } + + match rlp_type { + RLPType::String => { + // checking for default value `0` + if (len == 0) { + output.append(RLPItem::String(array![0].span())); + } else { + output.append(RLPItem::String(input.slice(offset, len))); + } + }, + RLPType::List => { + if len > 0 { + let res = RLPTrait::decode(input.slice(offset, len)); + output.append(RLPItem::List(res)); + } else { + output.append(RLPItem::List(array![].span())); + } + } + }; + + let total_item_len = len + offset; + if total_item_len < input_len { + return output + .span() + .concat(RLPTrait::decode(input.slice(total_item_len, input_len - total_item_len))); + } + + output.span() + } +} diff --git a/src/encoding/src/tests.cairo b/src/encoding/src/tests.cairo index 4959d948..87c88a06 100644 --- a/src/encoding/src/tests.cairo +++ b/src/encoding/src/tests.cairo @@ -1,2 +1,3 @@ mod base64_test; mod reversible_test; +mod rlp_test; diff --git a/src/encoding/src/tests/rlp_test.cairo b/src/encoding/src/tests/rlp_test.cairo new file mode 100644 index 00000000..1596f0d8 --- /dev/null +++ b/src/encoding/src/tests/rlp_test.cairo @@ -0,0 +1,1894 @@ +use alexandria_encoding::rlp::{RLPType, RLPTrait, RLPItem}; +use core::array::SpanTrait; +use core::option::OptionTrait; +use core::traits::Into; + +use result::ResultTrait; + +#[test] +#[available_gas(99999999)] +fn test_rlp_decode_type_byte() { + let mut arr = array![0x78]; + + let (rlp_type, offset, size) = RLPTrait::decode_type(arr.span()); + + assert(rlp_type == RLPType::String, 'Wrong type'); + assert(offset == 0, 'Wrong offset'); + assert(size == 1, 'Wrong size'); +} + +#[test] +#[available_gas(99999999)] +fn test_rlp_decode_type_short_string() { + let mut arr = array![0x82]; + + let (rlp_type, offset, size) = RLPTrait::decode_type(arr.span()); + + assert(rlp_type == RLPType::String, 'Wrong type'); + assert(offset == 1, 'Wrong offset'); + assert(size == 2, 'Wrong size'); +} + + +#[test] +#[available_gas(99999999)] +fn test_rlp_decode_type_long_string() { + let mut arr = array![0xb9, 0x01, 0x02]; + + let (rlp_type, offset, size) = RLPTrait::decode_type(arr.span()); + + assert(rlp_type == RLPType::String, 'Wrong type'); + assert(offset == 3, 'Wrong offset'); + assert(size == 258, 'Wrong size'); +} + +#[test] +#[available_gas(99999999)] +fn test_rlp_decode_type_short_list() { + let mut arr = array![0xc3]; + + let (rlp_type, offset, size) = RLPTrait::decode_type(arr.span()); + + assert(rlp_type == RLPType::List, 'Wrong type'); + assert(offset == 1, 'Wrong offset'); + assert(size == 3, 'Wrong size'); +} + +#[test] +#[available_gas(99999999)] +fn test_rlp_decode_type_long_list() { + let mut arr = array![0xf9, 0x01, 0x02]; + + let (rlp_type, offset, size) = RLPTrait::decode_type(arr.span()); + + assert(rlp_type == RLPType::List, 'Wrong type'); + assert(offset == 3, 'Wrong offset'); + assert(size == 258, 'Wrong size'); +} + +#[test] +#[should_panic(expected: ('input too short',))] +#[available_gas(99999999)] +fn test_rlp_decode_type_long_list_len_too_short() { + let mut arr = array![0xf9, 0x01]; + + let res = RLPTrait::decode_type(arr.span()); +} + +#[test] +#[should_panic(expected: ('empty input',))] +#[available_gas(9999999)] +fn test_rlp_empty() { + let res = RLPTrait::decode(ArrayTrait::new().span()); +} + +#[test] +#[available_gas(99999999)] +fn test_rlp_decode_string_default_value() { + let mut arr = array![0x80]; + + let rlp_item = RLPTrait::decode(arr.span()); + let expected = RLPItem::String(array![0].span()); + + assert(rlp_item.len() == 1, 'item length not 1'); + assert(*rlp_item[0] == expected, 'default value not 0'); +} + + +#[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 = RLPTrait::decode(arr.span()); + + assert(res == array![RLPItem::String(arr.span())].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 = RLPTrait::decode(arr.span()); + + // Remove the byte representing the data type + arr.pop_front(); + let expected_item = array![RLPItem::String(arr.span())].span(); + + assert(res == expected_item, 'Wrong value'); +} + + +#[test] +#[should_panic(expected: ('input too short',))] +#[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 = RLPTrait::decode(arr.span()); +} + +#[test] +#[available_gas(99999999)] +fn test_rlp_decode_long_string_with_payload_len_on_1_byte() { + 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 = RLPTrait::decode(arr.span()); + + // Remove the bytes representing the data type and their length + arr.pop_front(); + arr.pop_front(); + let expected_item = array![RLPItem::String(arr.span())].span(); + + assert(res == expected_item, 'Wrong value'); +} + +#[test] +#[should_panic(expected: ('input too short',))] +#[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 = RLPTrait::decode(arr.span()); +} + + +#[test] +#[available_gas(99999999)] +fn test_rlp_decode_long_string_with_payload_len_on_2_bytes() { + 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 = RLPTrait::decode(arr.span()); + + // Remove the bytes representing the data type and their length + arr.pop_front(); + arr.pop_front(); + arr.pop_front(); + let expected_item = array![RLPItem::String(arr.span())].span(); + + assert(res == expected_item, 'Wrong value'); +} + +#[test] +#[should_panic(expected: ('input too short',))] +#[available_gas(99999999)] +fn test_rlp_decode_long_string_with_payload_len_too_short() { + let mut arr = array![0xb9, 0x01,]; + + let res = RLPTrait::decode(arr.span()); +} + +#[test] +#[available_gas(99999999999)] +fn test_rlp_decode_short_list() { + let mut arr = array![0xc9, 0x83, 0x35, 0x35, 0x35, 0x42, 0x83, 0x45, 0x38, 0x92]; + let res = RLPTrait::decode(arr.span()); + + let mut expected_0 = RLPItem::String(array![0x35, 0x35, 0x35].span()); + let mut expected_1 = RLPItem::String(array![0x42].span()); + let mut expected_2 = RLPItem::String(array![0x45, 0x38, 0x92].span()); + + let expected_list = RLPItem::List(array![expected_0, expected_1, expected_2].span()); + + assert(res == array![expected_list].span(), 'Wrong value'); +} + +#[test] +#[available_gas(99999999999)] +fn test_rlp_decode_short_nested_list() { + let mut arr = array![0xc7, 0xc0, 0xc1, 0xc0, 0xc3, 0xc0, 0xc1, 0xc0]; + let res = RLPTrait::decode(arr.span()); + + let mut expected_0 = RLPItem::List(array![].span()); + let mut expected_1 = RLPItem::List(array![expected_0].span()); + let mut expected_2 = RLPItem::List(array![expected_0, expected_1].span()); + + let expected = RLPItem::List(array![expected_0, expected_1, expected_2].span()); + + assert(res == array![expected].span(), 'Wrong value'); +} + +#[test] +#[available_gas(99999999999)] +fn test_rlp_decode_multi_list() { + let mut arr = array![0xc6, 0x82, 0x7a, 0x77, 0xc1, 0x04, 0x01,]; + + let res = RLPTrait::decode(arr.span()); + + let mut expected_0 = RLPItem::String(array![0x7a, 0x77].span()); + let mut expected_1 = RLPItem::String(array![0x04].span()); + let mut expected_1 = RLPItem::List(array![expected_1].span()); + let mut expected_2 = RLPItem::String(array![0x01].span()); + let mut expected = RLPItem::List(array![expected_0, expected_1, expected_2].span()); + + assert(res == array![expected].span(), 'Wrong value'); +} + +#[test] +#[should_panic(expected: ('input too short',))] +#[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 = RLPTrait::decode(arr.span()); +} + +#[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 = RLPTrait::decode(arr.span()); + + let mut expected_0 = RLPItem::String( + 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 + ] + .span() + ); + let mut expected_1 = RLPItem::String( + 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 + ] + .span() + ); + let mut expected_2 = RLPItem::String( + 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 + ] + .span() + ); + let mut expected_3 = RLPItem::String( + 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 + ] + .span() + ); + let mut expected_4 = RLPItem::String( + 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 + ] + .span() + ); + let mut expected_5 = RLPItem::String( + 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 + ] + .span() + ); + let mut expected_6 = RLPItem::String( + 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 + ] + .span() + ); + let mut expected_7 = RLPItem::String( + 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 + ] + .span() + ); + let mut expected_8 = RLPItem::String( + 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 + ] + .span() + ); + let mut expected_9 = RLPItem::String( + 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 + ] + .span() + ); + let mut expected_10 = RLPItem::String( + 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 + ] + .span() + ); + let mut expected_11 = RLPItem::String( + 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 + ] + .span() + ); + let mut expected_12 = RLPItem::String( + 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 + ] + .span() + ); + let mut expected_13 = RLPItem::String( + 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 + ] + .span() + ); + let mut expected_14 = RLPItem::String( + 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 + ] + .span() + ); + let mut expected_15 = RLPItem::String( + 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 + ] + .span() + ); + + let mut expected_16 = RLPItem::String(array![0].span()); + + let mut expected = array![ + expected_0, + expected_1, + expected_2, + expected_3, + expected_4, + expected_5, + expected_6, + expected_7, + expected_8, + expected_9, + expected_10, + expected_11, + expected_12, + expected_13, + expected_14, + expected_15, + expected_16 + ]; + + let expected_item = RLPItem::List(expected.span()); + + assert(res == array![expected_item].span(), 'Wrong value'); +} + + +#[test] +#[should_panic(expected: ('input too short',))] +#[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 = RLPTrait::decode(arr.span()); +} + +#[test] +#[should_panic(expected: ('input too short',))] +#[available_gas(99999999999)] +fn test_rlp_decode_long_list_with_len_too_short() { + let mut arr = array![0xf9, 0x02,]; + + let res = RLPTrait::decode(arr.span()); +} diff --git a/src/numeric/Scarb.toml b/src/numeric/Scarb.toml index 61defe63..ddc57201 100644 --- a/src/numeric/Scarb.toml +++ b/src/numeric/Scarb.toml @@ -6,3 +6,6 @@ homepage = "https://github.com/keep-starknet-strange/alexandria/tree/main/src/nu [tool] fmt.workspace = true + +[dependencies] +alexandria_math = { path = "../math" } \ No newline at end of file diff --git a/src/numeric/src/integers.cairo b/src/numeric/src/integers.cairo new file mode 100644 index 00000000..3b74aec4 --- /dev/null +++ b/src/numeric/src/integers.cairo @@ -0,0 +1,33 @@ +use alexandria_math::BitShift; + +#[generate_trait] +impl U32Impl of U32Trait { + /// Packs 4 bytes into a u32 + /// # Arguments + /// * `input` a Span of len <=4 + /// # Returns + /// * Option::Some(u32) if the operation succeeds + /// * Option::None otherwise + fn from_bytes(input: Span) -> Option { + let len = input.len(); + if len == 0 { + return Option::None; + } + if len > 4 { + return Option::None; + } + let offset: u32 = len - 1; + let mut result: u32 = 0; + let mut i: u32 = 0; + loop { + if i == len { + break (); + } + let byte: u32 = (*input.at(i)).into(); + result += BitShift::shl(byte, 8 * (offset - i)); + + i += 1; + }; + Option::Some(result) + } +} diff --git a/src/numeric/src/lib.cairo b/src/numeric/src/lib.cairo index 777ad2f4..5ff687ad 100644 --- a/src/numeric/src/lib.cairo +++ b/src/numeric/src/lib.cairo @@ -1,5 +1,6 @@ mod cumsum; mod diff; +mod integers; mod interpolate; #[cfg(test)] From ec5441969f68efd416a872e30b7589566fa9fc15 Mon Sep 17 00:00:00 2001 From: Quentash Date: Tue, 14 Nov 2023 12:26:44 +0100 Subject: [PATCH 02/12] implement rlp encode with recurs list --- src/data_structures/src/array_ext.cairo | 10 + src/encoding/src/rlp.cairo | 78 +- src/encoding/src/tests/rlp_test.cairo | 1359 +++++++++++++++++++++++ src/numeric/src/integers.cairo | 44 + 4 files changed, 1490 insertions(+), 1 deletion(-) diff --git a/src/data_structures/src/array_ext.cairo b/src/data_structures/src/array_ext.cairo index e83887c0..4e7c8516 100644 --- a/src/data_structures/src/array_ext.cairo +++ b/src/data_structures/src/array_ext.cairo @@ -4,6 +4,7 @@ trait ArrayTraitExt { fn reverse(self: @Array) -> Array; fn contains<+PartialEq>(self: @Array, item: T) -> bool; fn concat(self: @Array, a: @Array) -> Array; + fn concat_span<+Drop>(ref self: Array, arr2: Span); fn index_of<+PartialEq>(self: @Array, item: T) -> Option; fn occurrences_of<+PartialEq>(self: @Array, item: T) -> usize; fn min<+PartialEq, +PartialOrd>(self: @Array) -> Option; @@ -85,6 +86,15 @@ impl ArrayImpl, +Drop> of ArrayTraitExt { ret } + fn concat_span<+Destruct>(ref self: Array, mut arr2: Span) { + loop { + match arr2.pop_front() { + Option::Some(elem) => self.append(*elem), + Option::None => { break; } + }; + } + } + fn index_of<+PartialEq>(self: @Array, item: T) -> Option { self.span().index_of(item) } diff --git a/src/encoding/src/rlp.cairo b/src/encoding/src/rlp.cairo index cab2463d..7f2d9449 100644 --- a/src/encoding/src/rlp.cairo +++ b/src/encoding/src/rlp.cairo @@ -1,7 +1,8 @@ -use alexandria_data_structures::array_ext::SpanTraitExt; +use alexandria_data_structures::array_ext::{SpanTraitExt, ArrayTraitExt}; use alexandria_numeric::integers::U32Trait; use core::result::ResultTrait; +use debug::PrintTrait; // Possible RLP types #[derive(Drop, PartialEq)] enum RLPType { @@ -63,6 +64,81 @@ impl RLPImpl of RLPTrait { } } + /// RLP encodes multiple RLPItem + /// # Arguments + /// * `input` - Span of RLPItem to encode + /// # Returns + /// * `ByteArray - RLP encoded ByteArray + /// # Errors + /// * RLPError::RlpEmptyInput - if the input is empty + fn encode(mut input: Span) -> Span { + if input.len() == 0 { + panic_with_felt252('empty input'); + } + + let mut output: Array = Default::default(); + let item = input.pop_front().unwrap(); + + match item { + RLPItem::String(string) => { output.concat_span(RLPTrait::encode_string(*string)); }, + RLPItem::List(list) => { + if (*list).len() == 0 { + output.append(0xc0); + } else { + let payload = RLPTrait::encode(*list); + let payload_len = payload.len(); + if payload_len > 55 { + let len_in_bytes = payload_len.to_bytes(); + output.append(0xf7 + len_in_bytes.len().try_into().unwrap()); + output.concat_span(len_in_bytes); + } else { + output.append(0xc0 + payload_len.try_into().unwrap()); + } + output.concat_span(payload); + } + } + } + + if input.len() > 0 { + output.concat_span(RLPTrait::encode(input)); + } + + output.span() + } + + /// RLP encodes a Span, which is the underlying type used to represent + /// string data in Cairo. Since RLP encoding is only used for eth_address + /// computation by calculating the RLP::encode(deployer_address, deployer_nonce) + /// and then hash it, the input is a ByteArray and not a Span + /// # Arguments + /// * `input` - Span to encode + /// # Returns + /// * `ByteArray - RLP encoded ByteArray + /// # Errors + /// * RLPError::RlpEmptyInput - if the input is empty + fn encode_string(input: Span) -> Span { + let len = input.len(); + if len == 0 { + return array![0x80].span(); + } else if len == 1 && *input[0] < 0x80 { + return input; + } else if len < 56 { + let mut encoding: Array = Default::default(); + encoding.append(0x80 + len.try_into().unwrap()); + encoding.concat_span(input); + return encoding.span(); + } else { + let mut encoding: Array = Default::default(); + let len_as_bytes = len.to_bytes(); + let len_bytes_count = len_as_bytes.len(); + let prefix = 0xb7 + len_bytes_count.try_into().unwrap(); + encoding.append(prefix); + encoding.concat_span(len_as_bytes); + encoding.concat_span(input); + return encoding.span(); + } + } + /// RLP decodes a rlp encoded byte array /// as described in https://ethereum.org/en/developers/docs/data-structures-and-encoding/rlp/ /// diff --git a/src/encoding/src/tests/rlp_test.cairo b/src/encoding/src/tests/rlp_test.cairo index 1596f0d8..fcbc5fa9 100644 --- a/src/encoding/src/tests/rlp_test.cairo +++ b/src/encoding/src/tests/rlp_test.cairo @@ -1892,3 +1892,1362 @@ fn test_rlp_decode_long_list_with_len_too_short() { let res = RLPTrait::decode(arr.span()); } + +#[test] +#[should_panic(expected: ('empty input',))] +#[available_gas(20000000)] +fn test_rlp_encode_empty_input_should_fail() { + let mut input = array![]; + + let res = RLPTrait::encode(input.span()); +} + + +#[test] +#[available_gas(20000000)] +fn test_rlp_encode_default_value() { + let mut input = RLPItem::String(array![].span()); + + let res = RLPTrait::encode(array![input].span()); + + assert(res.len() == 1, 'wrong len'); + assert(*res[0] == 0x80, 'wrong encoded value'); +} + +#[test] +#[available_gas(20000000)] +fn test_rlp_encode_string_single_byte_lt_0x80() { + let mut input: Array = Default::default(); + input.append(0x40); + + let res = RLPTrait::encode(array![RLPItem::String(input.span())].span()); + + assert(res.len() == 1, 'wrong len'); + assert(*res[0] == 0x40, 'wrong encoded value'); +} + + +#[test] +#[available_gas(20000000)] +fn test_rlp_encode_string_single_byte_ge_0x80() { + let mut input: Array = Default::default(); + input.append(0x80); + + let res = RLPTrait::encode(array![RLPItem::String(input.span())].span()); + + assert(res.len() == 2, 'wrong len'); + assert(*res[0] == 0x81, 'wrong prefix'); + assert(*res[1] == 0x80, 'wrong encoded value'); +} + +#[test] +#[available_gas(20000000)] +fn test_rlp_encode_string_length_between_2_and_55() { + let mut input: Array = Default::default(); + input.append(0x40); + input.append(0x50); + + let res = RLPTrait::encode(array![RLPItem::String(input.span())].span()); + + assert(res.len() == 3, 'wrong len'); + assert(*res[0] == 0x82, 'wrong prefix'); + assert(*res[1] == 0x40, 'wrong first value'); + assert(*res[2] == 0x50, 'wrong second value'); +} + +#[test] +#[available_gas(20000000)] +fn test_rlp_encode_string_length_exactly_56() { + let mut input: Array = Default::default(); + let mut i = 0; + loop { + if i == 56 { + break; + } + input.append(0x60); + i += 1; + }; + + let res = RLPTrait::encode(array![RLPItem::String(input.span())].span()); + + assert(res.len() == 58, 'wrong len'); + assert(*res[0] == 0xb8, 'wrong prefix'); + assert(*res[1] == 56, 'wrong string length'); + let mut i = 2; + loop { + if i == 58 { + break; + } + assert(*res[i] == 0x60, 'wrong value in sequence'); + i += 1; + }; +} + + +#[test] +#[available_gas(20000000)] +fn test_rlp_encode_string_length_greater_than_56() { + let mut input: Array = Default::default(); + let mut i = 0; + loop { + if i == 60 { + break; + } + input.append(0x70); + i += 1; + }; + + let res = RLPTrait::encode(array![RLPItem::String(input.span())].span()); + + assert(res.len() == 62, 'wrong len'); + assert(*res[0] == 0xb8, 'wrong prefix'); + assert(*res[1] == 60, 'wrong length byte'); + let mut i = 2; + loop { + if i == 62 { + break; + } + assert(*res[i] == 0x70, 'wrong value in sequence'); + i += 1; + } +} + +#[test] +#[available_gas(200000000)] +fn test_rlp_encode_string_large_bytearray_inputs() { + let mut input: Array = Default::default(); + let mut i = 0; + loop { + if i == 500 { + break; + } + input.append(0x70); + i += 1; + }; + + let res = RLPTrait::encode(array![RLPItem::String(input.span())].span()); + + assert(res.len() == 503, 'wrong len'); + assert(*res[0] == 0xb9, 'wrong prefix'); + assert(*res[1] == 0x01, 'wrong first length byte'); + assert(*res[2] == 0xF4, 'wrong second length byte'); + let mut i = 3; + loop { + if i == 503 { + break; + } + assert(*res[i] == 0x70, 'wrong value in sequence'); + i += 1; + } +} + +#[test] +#[available_gas(20000000)] +fn test_rlp_encode_mutilple_string() { + let mut input = array![]; + input.append(RLPItem::String(array![0x40, 0x53, 0x15, 0x94, 0x50, 0x40, 0x40, 0x40].span())); + input.append(RLPItem::String(array![0x03].span())); + + let res = RLPTrait::encode(input.span()); + + assert(res.len() == 10, 'wrong len'); + assert(*res[0] == 0x88, 'wrong prefix'); + assert(*res[1] == 0x40, 'wrong first value'); + assert(*res[2] == 0x53, 'wrong second value'); +} + +#[test] +#[available_gas(99999999999)] +fn test_rlp_encode_short_list() { + let mut expected_0 = RLPItem::String(array![0x35, 0x35, 0x35].span()); + let mut expected_1 = RLPItem::String(array![0x42].span()); + let mut expected_2 = RLPItem::String(array![0x45, 0x38, 0x92].span()); + + let expected_list = RLPItem::List(array![expected_0, expected_1, expected_2].span()); + let res = RLPTrait::encode(array![expected_list].span()); + + let mut arr = array![0xc9, 0x83, 0x35, 0x35, 0x35, 0x42, 0x83, 0x45, 0x38, 0x92]; + + assert(res.len() == 10, 'wrong len'); + assert(*res[0] == 0xc9, 'wrong prefix'); + assert(*res[1] == 0x83, 'wrong first value'); + assert(*res[2] == 0x35, 'wrong second value'); +} + +#[test] +#[available_gas(99999999999)] +fn test_rlp_encode_short_nested_list() { + let mut expected_0 = RLPItem::List(array![].span()); + let mut expected_1 = RLPItem::List(array![expected_0].span()); + let mut expected_2 = RLPItem::List(array![expected_0, expected_1].span()); + + let expected = RLPItem::List(array![expected_0, expected_1, expected_2].span()); + + let mut arr = array![0xc7, 0xc0, 0xc1, 0xc0, 0xc3, 0xc0, 0xc1, 0xc0]; + let res = RLPTrait::encode(array![expected].span()); + + assert(res.len() == 8, 'wrong len'); + assert(*res[0] == 0xc7, 'wrong prefix'); + assert(*res[1] == 0xc0, 'wrong first value'); + assert(*res[2] == 0xc1, 'wrong second value'); +} + +#[test] +#[available_gas(99999999999)] +fn test_rlp_encode_long_list() { + let mut expected_0 = RLPItem::String( + 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 + ] + .span() + ); + let mut expected_1 = RLPItem::String( + 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 + ] + .span() + ); + let mut expected_2 = RLPItem::String( + 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 + ] + .span() + ); + let mut expected_3 = RLPItem::String( + 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 + ] + .span() + ); + let mut expected_4 = RLPItem::String( + 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 + ] + .span() + ); + let mut expected_5 = RLPItem::String( + 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 + ] + .span() + ); + let mut expected_6 = RLPItem::String( + 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 + ] + .span() + ); + let mut expected_7 = RLPItem::String( + 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 + ] + .span() + ); + let mut expected_8 = RLPItem::String( + 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 + ] + .span() + ); + let mut expected_9 = RLPItem::String( + 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 + ] + .span() + ); + let mut expected_10 = RLPItem::String( + 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 + ] + .span() + ); + let mut expected_11 = RLPItem::String( + 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 + ] + .span() + ); + let mut expected_12 = RLPItem::String( + 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 + ] + .span() + ); + let mut expected_13 = RLPItem::String( + 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 + ] + .span() + ); + let mut expected_14 = RLPItem::String( + 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 + ] + .span() + ); + let mut expected_15 = RLPItem::String( + 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 + ] + .span() + ); + + let mut expected_16 = RLPItem::String(array![].span()); + + let mut expected = array![ + expected_0, + expected_1, + expected_2, + expected_3, + expected_4, + expected_5, + expected_6, + expected_7, + expected_8, + expected_9, + expected_10, + expected_11, + expected_12, + expected_13, + expected_14, + expected_15, + expected_16 + ]; + + let expected_item = RLPItem::List(expected.span()); + + let res = RLPTrait::encode(array![expected_item].span()); + + 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 + ]; + + assert(res == arr.span(), 'Wrong value'); +} diff --git a/src/numeric/src/integers.cairo b/src/numeric/src/integers.cairo index 3b74aec4..fb48ecbf 100644 --- a/src/numeric/src/integers.cairo +++ b/src/numeric/src/integers.cairo @@ -30,4 +30,48 @@ impl U32Impl of U32Trait { }; Option::Some(result) } + + /// Unpacks a u32 into an array of bytes + /// # Arguments + /// * `self` a `u32` value. + /// # Returns + /// * The bytes array representation of the value. + fn to_bytes(mut self: u32) -> Span { + let bytes_used: u32 = self.bytes_used().into(); + let mut bytes: Array = Default::default(); + let mut i = 0; + loop { + if i == bytes_used { + break (); + } + let val = BitShift::shr(self, 8 * (bytes_used.try_into().unwrap() - i - 1)); + bytes.append((val & 0xFF).try_into().unwrap()); + i += 1; + }; + + bytes.span() + } + + /// Returns the number of bytes used to represent a `u32` value. + /// # Arguments + /// * `self` - The value to check. + /// # Returns + /// The number of bytes used to represent the value. + fn bytes_used(self: usize) -> u8 { + if self < 0x10000 { // 256^2 + if self < 0x100 { // 256^1 + if self == 0 { + return 0; + } else { + return 1; + }; + } + return 2; + } else { + if self < 0x1000000 { // 256^6 + return 3; + } + return 4; + } + } } From 195462aab23fd61e75f4044f92d59a6705bf8474 Mon Sep 17 00:00:00 2001 From: Quentash Date: Tue, 14 Nov 2023 12:46:51 +0100 Subject: [PATCH 03/12] corrected decode + fn comments --- src/encoding/src/rlp.cairo | 38 +++++++++++---------------- src/encoding/src/tests/rlp_test.cairo | 12 +++------ 2 files changed, 20 insertions(+), 30 deletions(-) diff --git a/src/encoding/src/rlp.cairo b/src/encoding/src/rlp.cairo index 7f2d9449..3307ff71 100644 --- a/src/encoding/src/rlp.cairo +++ b/src/encoding/src/rlp.cairo @@ -1,8 +1,6 @@ -use alexandria_data_structures::array_ext::{SpanTraitExt, ArrayTraitExt}; +use alexandria_data_structures::array_ext::ArrayTraitExt; use alexandria_numeric::integers::U32Trait; -use core::result::ResultTrait; -use debug::PrintTrait; // Possible RLP types #[derive(Drop, PartialEq)] enum RLPType { @@ -27,8 +25,8 @@ impl RLPImpl of RLPTrait { /// * `(RLPType, offset, size)` - A tuple containing the RLPType /// the offset and the size of the RLPItem to decode /// # Errors - /// * RLPError::EmptyInput - if the input is empty - /// * RLPError::InputTooShort - if the input is too short for a given + /// * empty input - if the input is empty + /// * input too short - if the input is too short for a given fn decode_type(input: Span) -> (RLPType, u32, u32) { let input_len = input.len(); if input_len == 0 { @@ -64,13 +62,13 @@ impl RLPImpl of RLPTrait { } } - /// RLP encodes multiple RLPItem + /// Recursively encodes multiple RLPItems /// # Arguments /// * `input` - Span of RLPItem to encode /// # Returns - /// * `ByteArray - RLP encoded ByteArray + /// * `Span - RLP encoded items as a span of bytes /// # Errors - /// * RLPError::RlpEmptyInput - if the input is empty + /// * empty input - if the input is empty fn encode(mut input: Span) -> Span { if input.len() == 0 { panic_with_felt252('empty input'); @@ -106,16 +104,11 @@ impl RLPImpl of RLPTrait { output.span() } - /// RLP encodes a Span, which is the underlying type used to represent - /// string data in Cairo. Since RLP encoding is only used for eth_address - /// computation by calculating the RLP::encode(deployer_address, deployer_nonce) - /// and then hash it, the input is a ByteArray and not a Span + /// RLP encodes a Array of bytes representing a RLP String. /// # Arguments - /// * `input` - Span to encode + /// * `input` - Array of bytes representing a RLP String to encode /// # Returns - /// * `ByteArray - RLP encoded ByteArray - /// # Errors - /// * RLPError::RlpEmptyInput - if the input is empty + /// * `Span - RLP encoded items as a span of bytes fn encode_string(input: Span) -> Span { let len = input.len(); if len == 0 { @@ -139,7 +132,7 @@ impl RLPImpl of RLPTrait { } } - /// RLP decodes a rlp encoded byte array + /// Recursively decodes a rlp encoded byte array /// as described in https://ethereum.org/en/developers/docs/data-structures-and-encoding/rlp/ /// /// # Arguments @@ -147,7 +140,7 @@ impl RLPImpl of RLPTrait { /// # Returns /// * `Span` - Span of RLPItem /// # Errors - /// * RLPError::InputTooShort - if the input is too short for a given + /// * input too short - if the input is too short for a given fn decode(input: Span) -> Span { let mut output: Array = Default::default(); let input_len = input.len(); @@ -162,7 +155,7 @@ impl RLPImpl of RLPTrait { RLPType::String => { // checking for default value `0` if (len == 0) { - output.append(RLPItem::String(array![0].span())); + output.append(RLPItem::String(array![].span())); } else { output.append(RLPItem::String(input.slice(offset, len))); } @@ -179,9 +172,10 @@ impl RLPImpl of RLPTrait { let total_item_len = len + offset; if total_item_len < input_len { - return output - .span() - .concat(RLPTrait::decode(input.slice(total_item_len, input_len - total_item_len))); + output + .concat_span( + RLPTrait::decode(input.slice(total_item_len, input_len - total_item_len)) + ); } output.span() diff --git a/src/encoding/src/tests/rlp_test.cairo b/src/encoding/src/tests/rlp_test.cairo index fcbc5fa9..01dec81a 100644 --- a/src/encoding/src/tests/rlp_test.cairo +++ b/src/encoding/src/tests/rlp_test.cairo @@ -1,8 +1,4 @@ use alexandria_encoding::rlp::{RLPType, RLPTrait, RLPItem}; -use core::array::SpanTrait; -use core::option::OptionTrait; -use core::traits::Into; - use result::ResultTrait; #[test] @@ -88,10 +84,10 @@ fn test_rlp_decode_string_default_value() { let mut arr = array![0x80]; let rlp_item = RLPTrait::decode(arr.span()); - let expected = RLPItem::String(array![0].span()); + let expected = RLPItem::String(array![].span()); - assert(rlp_item.len() == 1, 'item length not 1'); - assert(*rlp_item[0] == expected, 'default value not 0'); + assert(rlp_item.len() == 1, 'item length not 0'); + assert(*rlp_item[0] == expected, 'wrong item'); } @@ -1818,7 +1814,7 @@ fn test_rlp_decode_long_list() { .span() ); - let mut expected_16 = RLPItem::String(array![0].span()); + let mut expected_16 = RLPItem::String(array![].span()); let mut expected = array![ expected_0, From 5e5e98229e17f42df54acdd23ffeb3703b5faef2 Mon Sep 17 00:00:00 2001 From: Quentash Date: Thu, 16 Nov 2023 15:45:21 +0100 Subject: [PATCH 04/12] changed panic to results --- src/encoding/src/errors.cairo | 18 +++++ src/encoding/src/lib.cairo | 1 + src/encoding/src/rlp.cairo | 53 +++++++------- src/encoding/src/tests/rlp_test.cairo | 101 +++++++++++++++++--------- 4 files changed, 113 insertions(+), 60 deletions(-) create mode 100644 src/encoding/src/errors.cairo diff --git a/src/encoding/src/errors.cairo b/src/encoding/src/errors.cairo new file mode 100644 index 00000000..9d28b327 --- /dev/null +++ b/src/encoding/src/errors.cairo @@ -0,0 +1,18 @@ +// LENGTH +const RLP_EMPTY_INPUT: felt252 = 'KKT: EmptyInput'; +const RLP_INPUT_TOO_SHORT: felt252 = 'KKT: InputTooShort'; + +#[derive(Drop, Copy, PartialEq)] +enum RLPError { + EmptyInput: felt252, + InputTooShort: felt252, +} + +impl RLPErrorIntoU256 of Into { + fn into(self: RLPError) -> u256 { + match self { + RLPError::EmptyInput(error_message) => error_message.into(), + RLPError::InputTooShort(error_message) => error_message.into(), + } + } +} diff --git a/src/encoding/src/lib.cairo b/src/encoding/src/lib.cairo index 0d146631..4a82dfd2 100644 --- a/src/encoding/src/lib.cairo +++ b/src/encoding/src/lib.cairo @@ -1,4 +1,5 @@ mod base64; +mod errors; mod reversible; mod rlp; diff --git a/src/encoding/src/rlp.cairo b/src/encoding/src/rlp.cairo index 3307ff71..0e29a479 100644 --- a/src/encoding/src/rlp.cairo +++ b/src/encoding/src/rlp.cairo @@ -1,4 +1,5 @@ use alexandria_data_structures::array_ext::ArrayTraitExt; +use alexandria_encoding::errors::{RLPError, RLP_EMPTY_INPUT, RLP_INPUT_TOO_SHORT}; use alexandria_numeric::integers::U32Trait; // Possible RLP types @@ -27,38 +28,38 @@ impl RLPImpl of RLPTrait { /// # Errors /// * empty input - if the input is empty /// * input too short - if the input is too short for a given - fn decode_type(input: Span) -> (RLPType, u32, u32) { + fn decode_type(input: Span) -> Result<(RLPType, u32, u32), RLPError> { let input_len = input.len(); if input_len == 0 { - panic_with_felt252('empty input'); + return Result::Err(RLPError::EmptyInput(RLP_EMPTY_INPUT)); } let prefix_byte = *input[0]; if prefix_byte < 0x80 { // Char - (RLPType::String, 0, 1) + return Result::Ok((RLPType::String, 0, 1)); } else if prefix_byte < 0xb8 { // Short String - (RLPType::String, 1, prefix_byte.into() - 0x80) + return Result::Ok((RLPType::String, 1, prefix_byte.into() - 0x80)); } else if prefix_byte < 0xc0 { // Long String let len_bytes_count: u32 = (prefix_byte - 0xb7).into(); if input_len <= len_bytes_count { - panic_with_felt252('input too short'); + return Result::Err(RLPError::InputTooShort(RLP_INPUT_TOO_SHORT)); } let string_len_bytes = input.slice(1, len_bytes_count); let string_len: u32 = U32Trait::from_bytes(string_len_bytes).unwrap(); - (RLPType::String, 1 + len_bytes_count, string_len) + return Result::Ok((RLPType::String, 1 + len_bytes_count, string_len)); } else if prefix_byte < 0xf8 { // Short List - (RLPType::List, 1, prefix_byte.into() - 0xc0) + return Result::Ok((RLPType::List, 1, prefix_byte.into() - 0xc0)); } else { // Long List let len_bytes_count = prefix_byte.into() - 0xf7; if input.len() <= len_bytes_count { - panic_with_felt252('input too short'); + return Result::Err(RLPError::InputTooShort(RLP_INPUT_TOO_SHORT)); } let list_len_bytes = input.slice(1, len_bytes_count); let list_len: u32 = U32Trait::from_bytes(list_len_bytes).unwrap(); - (RLPType::List, 1 + len_bytes_count, list_len) + return Result::Ok((RLPType::List, 1 + len_bytes_count, list_len)); } } @@ -69,21 +70,21 @@ impl RLPImpl of RLPTrait { /// * `Span - RLP encoded items as a span of bytes /// # Errors /// * empty input - if the input is empty - fn encode(mut input: Span) -> Span { + fn encode(mut input: Span) -> Result, RLPError> { if input.len() == 0 { - panic_with_felt252('empty input'); + return Result::Err(RLPError::EmptyInput(RLP_EMPTY_INPUT)); } let mut output: Array = Default::default(); let item = input.pop_front().unwrap(); match item { - RLPItem::String(string) => { output.concat_span(RLPTrait::encode_string(*string)); }, + RLPItem::String(string) => { output.concat_span(RLPTrait::encode_string(*string)?); }, RLPItem::List(list) => { if (*list).len() == 0 { output.append(0xc0); } else { - let payload = RLPTrait::encode(*list); + let payload = RLPTrait::encode(*list)?; let payload_len = payload.len(); if payload_len > 55 { let len_in_bytes = payload_len.to_bytes(); @@ -98,10 +99,10 @@ impl RLPImpl of RLPTrait { } if input.len() > 0 { - output.concat_span(RLPTrait::encode(input)); + output.concat_span(RLPTrait::encode(input)?); } - output.span() + Result::Ok(output.span()) } /// RLP encodes a Array of bytes representing a RLP String. @@ -109,17 +110,17 @@ impl RLPImpl of RLPTrait { /// * `input` - Array of bytes representing a RLP String to encode /// # Returns /// * `Span - RLP encoded items as a span of bytes - fn encode_string(input: Span) -> Span { + fn encode_string(input: Span) -> Result, RLPError> { let len = input.len(); if len == 0 { - return array![0x80].span(); + return Result::Ok(array![0x80].span()); } else if len == 1 && *input[0] < 0x80 { - return input; + return Result::Ok(input); } else if len < 56 { let mut encoding: Array = Default::default(); encoding.append(0x80 + len.try_into().unwrap()); encoding.concat_span(input); - return encoding.span(); + return Result::Ok(encoding.span()); } else { let mut encoding: Array = Default::default(); let len_as_bytes = len.to_bytes(); @@ -128,7 +129,7 @@ impl RLPImpl of RLPTrait { encoding.append(prefix); encoding.concat_span(len_as_bytes); encoding.concat_span(input); - return encoding.span(); + return Result::Ok(encoding.span()); } } @@ -141,14 +142,14 @@ impl RLPImpl of RLPTrait { /// * `Span` - Span of RLPItem /// # Errors /// * input too short - if the input is too short for a given - fn decode(input: Span) -> Span { + fn decode(input: Span) -> Result, RLPError> { let mut output: Array = Default::default(); let input_len = input.len(); - let (rlp_type, offset, len) = RLPTrait::decode_type(input); + let (rlp_type, offset, len) = RLPTrait::decode_type(input)?; if input_len < offset + len { - panic_with_felt252('input too short'); + return Result::Err(RLPError::InputTooShort(RLP_INPUT_TOO_SHORT)); } match rlp_type { @@ -162,7 +163,7 @@ impl RLPImpl of RLPTrait { }, RLPType::List => { if len > 0 { - let res = RLPTrait::decode(input.slice(offset, len)); + let res = RLPTrait::decode(input.slice(offset, len))?; output.append(RLPItem::List(res)); } else { output.append(RLPItem::List(array![].span())); @@ -174,10 +175,10 @@ impl RLPImpl of RLPTrait { if total_item_len < input_len { output .concat_span( - RLPTrait::decode(input.slice(total_item_len, input_len - total_item_len)) + RLPTrait::decode(input.slice(total_item_len, input_len - total_item_len))? ); } - output.span() + Result::Ok(output.span()) } } diff --git a/src/encoding/src/tests/rlp_test.cairo b/src/encoding/src/tests/rlp_test.cairo index 01dec81a..40ae88c5 100644 --- a/src/encoding/src/tests/rlp_test.cairo +++ b/src/encoding/src/tests/rlp_test.cairo @@ -1,3 +1,4 @@ +use alexandria_encoding::errors::{RLPError, RLP_EMPTY_INPUT, RLP_INPUT_TOO_SHORT}; use alexandria_encoding::rlp::{RLPType, RLPTrait, RLPItem}; use result::ResultTrait; @@ -6,7 +7,7 @@ use result::ResultTrait; fn test_rlp_decode_type_byte() { let mut arr = array![0x78]; - let (rlp_type, offset, size) = RLPTrait::decode_type(arr.span()); + let (rlp_type, offset, size) = RLPTrait::decode_type(arr.span()).unwrap(); assert(rlp_type == RLPType::String, 'Wrong type'); assert(offset == 0, 'Wrong offset'); @@ -18,7 +19,7 @@ fn test_rlp_decode_type_byte() { fn test_rlp_decode_type_short_string() { let mut arr = array![0x82]; - let (rlp_type, offset, size) = RLPTrait::decode_type(arr.span()); + let (rlp_type, offset, size) = RLPTrait::decode_type(arr.span()).unwrap(); assert(rlp_type == RLPType::String, 'Wrong type'); assert(offset == 1, 'Wrong offset'); @@ -31,7 +32,7 @@ fn test_rlp_decode_type_short_string() { fn test_rlp_decode_type_long_string() { let mut arr = array![0xb9, 0x01, 0x02]; - let (rlp_type, offset, size) = RLPTrait::decode_type(arr.span()); + let (rlp_type, offset, size) = RLPTrait::decode_type(arr.span()).unwrap(); assert(rlp_type == RLPType::String, 'Wrong type'); assert(offset == 3, 'Wrong offset'); @@ -43,7 +44,7 @@ fn test_rlp_decode_type_long_string() { fn test_rlp_decode_type_short_list() { let mut arr = array![0xc3]; - let (rlp_type, offset, size) = RLPTrait::decode_type(arr.span()); + let (rlp_type, offset, size) = RLPTrait::decode_type(arr.span()).unwrap(); assert(rlp_type == RLPType::List, 'Wrong type'); assert(offset == 1, 'Wrong offset'); @@ -55,7 +56,7 @@ fn test_rlp_decode_type_short_list() { fn test_rlp_decode_type_long_list() { let mut arr = array![0xf9, 0x01, 0x02]; - let (rlp_type, offset, size) = RLPTrait::decode_type(arr.span()); + let (rlp_type, offset, size) = RLPTrait::decode_type(arr.span()).unwrap(); assert(rlp_type == RLPType::List, 'Wrong type'); assert(offset == 3, 'Wrong offset'); @@ -63,19 +64,25 @@ fn test_rlp_decode_type_long_list() { } #[test] -#[should_panic(expected: ('input too short',))] #[available_gas(99999999)] fn test_rlp_decode_type_long_list_len_too_short() { let mut arr = array![0xf9, 0x01]; let res = RLPTrait::decode_type(arr.span()); + + assert(res.is_err(), 'Should have failed'); + assert( + res.unwrap_err() == RLPError::InputTooShort(RLP_INPUT_TOO_SHORT), 'err != InputTooShort' + ); } #[test] -#[should_panic(expected: ('empty input',))] #[available_gas(9999999)] fn test_rlp_empty() { let res = RLPTrait::decode(ArrayTrait::new().span()); + + assert(res.is_err(), 'Should have failed'); + assert(res.unwrap_err() == RLPError::EmptyInput(RLP_EMPTY_INPUT), 'err != EmptyInput'); } #[test] @@ -83,7 +90,7 @@ fn test_rlp_empty() { fn test_rlp_decode_string_default_value() { let mut arr = array![0x80]; - let rlp_item = RLPTrait::decode(arr.span()); + let rlp_item = RLPTrait::decode(arr.span()).unwrap(); let expected = RLPItem::String(array![].span()); assert(rlp_item.len() == 1, 'item length not 0'); @@ -102,7 +109,7 @@ fn test_rlp_decode_string() { let mut arr = ArrayTrait::new(); arr.append(i); - let res = RLPTrait::decode(arr.span()); + let res = RLPTrait::decode(arr.span()).unwrap(); assert(res == array![RLPItem::String(arr.span())].span(), 'Wrong value'); @@ -144,7 +151,7 @@ fn test_rlp_decode_short_string() { 0xf7 ]; - let res = RLPTrait::decode(arr.span()); + let res = RLPTrait::decode(arr.span()).unwrap(); // Remove the byte representing the data type arr.pop_front(); @@ -155,7 +162,6 @@ fn test_rlp_decode_short_string() { #[test] -#[should_panic(expected: ('input too short',))] #[available_gas(99999999)] fn test_rlp_decode_short_string_input_too_short() { let mut arr = array![ @@ -189,6 +195,11 @@ fn test_rlp_decode_short_string_input_too_short() { ]; let res = RLPTrait::decode(arr.span()); + + assert(res.is_err(), 'Should have failed'); + assert( + res.unwrap_err() == RLPError::InputTooShort(RLP_INPUT_TOO_SHORT), 'err != InputTooShort' + ); } #[test] @@ -259,7 +270,7 @@ fn test_rlp_decode_long_string_with_payload_len_on_1_byte() { 0xd9 ]; - let res = RLPTrait::decode(arr.span()); + let res = RLPTrait::decode(arr.span()).unwrap(); // Remove the bytes representing the data type and their length arr.pop_front(); @@ -270,7 +281,6 @@ fn test_rlp_decode_long_string_with_payload_len_on_1_byte() { } #[test] -#[should_panic(expected: ('input too short',))] #[available_gas(99999999)] fn test_rlp_decode_long_string_with_input_too_short() { let mut arr = array![ @@ -337,6 +347,11 @@ fn test_rlp_decode_long_string_with_input_too_short() { ]; let res = RLPTrait::decode(arr.span()); + + assert(res.is_err(), 'Should have failed'); + assert( + res.unwrap_err() == RLPError::InputTooShort(RLP_INPUT_TOO_SHORT), 'err != InputTooShort' + ); } @@ -607,7 +622,7 @@ fn test_rlp_decode_long_string_with_payload_len_on_2_bytes() { 0x60 ]; - let res = RLPTrait::decode(arr.span()); + let res = RLPTrait::decode(arr.span()).unwrap(); // Remove the bytes representing the data type and their length arr.pop_front(); @@ -619,19 +634,23 @@ fn test_rlp_decode_long_string_with_payload_len_on_2_bytes() { } #[test] -#[should_panic(expected: ('input too short',))] #[available_gas(99999999)] fn test_rlp_decode_long_string_with_payload_len_too_short() { let mut arr = array![0xb9, 0x01,]; let res = RLPTrait::decode(arr.span()); + + assert(res.is_err(), 'Should have failed'); + assert( + res.unwrap_err() == RLPError::InputTooShort(RLP_INPUT_TOO_SHORT), 'err != InputTooShort' + ); } #[test] #[available_gas(99999999999)] fn test_rlp_decode_short_list() { let mut arr = array![0xc9, 0x83, 0x35, 0x35, 0x35, 0x42, 0x83, 0x45, 0x38, 0x92]; - let res = RLPTrait::decode(arr.span()); + let res = RLPTrait::decode(arr.span()).unwrap(); let mut expected_0 = RLPItem::String(array![0x35, 0x35, 0x35].span()); let mut expected_1 = RLPItem::String(array![0x42].span()); @@ -646,7 +665,7 @@ fn test_rlp_decode_short_list() { #[available_gas(99999999999)] fn test_rlp_decode_short_nested_list() { let mut arr = array![0xc7, 0xc0, 0xc1, 0xc0, 0xc3, 0xc0, 0xc1, 0xc0]; - let res = RLPTrait::decode(arr.span()); + let res = RLPTrait::decode(arr.span()).unwrap(); let mut expected_0 = RLPItem::List(array![].span()); let mut expected_1 = RLPItem::List(array![expected_0].span()); @@ -662,7 +681,7 @@ fn test_rlp_decode_short_nested_list() { fn test_rlp_decode_multi_list() { let mut arr = array![0xc6, 0x82, 0x7a, 0x77, 0xc1, 0x04, 0x01,]; - let res = RLPTrait::decode(arr.span()); + let res = RLPTrait::decode(arr.span()).unwrap(); let mut expected_0 = RLPItem::String(array![0x7a, 0x77].span()); let mut expected_1 = RLPItem::String(array![0x04].span()); @@ -674,12 +693,16 @@ fn test_rlp_decode_multi_list() { } #[test] -#[should_panic(expected: ('input too short',))] #[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 = RLPTrait::decode(arr.span()); + + assert(res.is_err(), 'Should have failed'); + assert( + res.unwrap_err() == RLPError::InputTooShort(RLP_INPUT_TOO_SHORT), 'err != InputTooShort' + ); } #[test] @@ -1219,7 +1242,7 @@ fn test_rlp_decode_long_list() { 0x5f, 0x80 ]; - let res = RLPTrait::decode(arr.span()); + let res = RLPTrait::decode(arr.span()).unwrap(); let mut expected_0 = RLPItem::String( array![ @@ -1843,7 +1866,6 @@ fn test_rlp_decode_long_list() { #[test] -#[should_panic(expected: ('input too short',))] #[available_gas(99999999999)] fn test_rlp_decode_long_list_with_input_too_short() { let mut arr = array![ @@ -1878,24 +1900,35 @@ fn test_rlp_decode_long_list_with_input_too_short() { ]; let res = RLPTrait::decode(arr.span()); + + assert(res.is_err(), 'Should have failed'); + assert( + res.unwrap_err() == RLPError::InputTooShort(RLP_INPUT_TOO_SHORT), 'err != InputTooShort' + ); } #[test] -#[should_panic(expected: ('input too short',))] #[available_gas(99999999999)] fn test_rlp_decode_long_list_with_len_too_short() { let mut arr = array![0xf9, 0x02,]; let res = RLPTrait::decode(arr.span()); + + assert(res.is_err(), 'Should have failed'); + assert( + res.unwrap_err() == RLPError::InputTooShort(RLP_INPUT_TOO_SHORT), 'err != InputTooShort' + ); } #[test] -#[should_panic(expected: ('empty input',))] #[available_gas(20000000)] fn test_rlp_encode_empty_input_should_fail() { let mut input = array![]; let res = RLPTrait::encode(input.span()); + + assert(res.is_err(), 'Should have failed'); + assert(res.unwrap_err() == RLPError::EmptyInput(RLP_EMPTY_INPUT), 'err != EmptyInput'); } @@ -1904,7 +1937,7 @@ fn test_rlp_encode_empty_input_should_fail() { fn test_rlp_encode_default_value() { let mut input = RLPItem::String(array![].span()); - let res = RLPTrait::encode(array![input].span()); + let res = RLPTrait::encode(array![input].span()).unwrap(); assert(res.len() == 1, 'wrong len'); assert(*res[0] == 0x80, 'wrong encoded value'); @@ -1916,7 +1949,7 @@ fn test_rlp_encode_string_single_byte_lt_0x80() { let mut input: Array = Default::default(); input.append(0x40); - let res = RLPTrait::encode(array![RLPItem::String(input.span())].span()); + let res = RLPTrait::encode(array![RLPItem::String(input.span())].span()).unwrap(); assert(res.len() == 1, 'wrong len'); assert(*res[0] == 0x40, 'wrong encoded value'); @@ -1929,7 +1962,7 @@ fn test_rlp_encode_string_single_byte_ge_0x80() { let mut input: Array = Default::default(); input.append(0x80); - let res = RLPTrait::encode(array![RLPItem::String(input.span())].span()); + let res = RLPTrait::encode(array![RLPItem::String(input.span())].span()).unwrap(); assert(res.len() == 2, 'wrong len'); assert(*res[0] == 0x81, 'wrong prefix'); @@ -1943,7 +1976,7 @@ fn test_rlp_encode_string_length_between_2_and_55() { input.append(0x40); input.append(0x50); - let res = RLPTrait::encode(array![RLPItem::String(input.span())].span()); + let res = RLPTrait::encode(array![RLPItem::String(input.span())].span()).unwrap(); assert(res.len() == 3, 'wrong len'); assert(*res[0] == 0x82, 'wrong prefix'); @@ -1964,7 +1997,7 @@ fn test_rlp_encode_string_length_exactly_56() { i += 1; }; - let res = RLPTrait::encode(array![RLPItem::String(input.span())].span()); + let res = RLPTrait::encode(array![RLPItem::String(input.span())].span()).unwrap(); assert(res.len() == 58, 'wrong len'); assert(*res[0] == 0xb8, 'wrong prefix'); @@ -1993,7 +2026,7 @@ fn test_rlp_encode_string_length_greater_than_56() { i += 1; }; - let res = RLPTrait::encode(array![RLPItem::String(input.span())].span()); + let res = RLPTrait::encode(array![RLPItem::String(input.span())].span()).unwrap(); assert(res.len() == 62, 'wrong len'); assert(*res[0] == 0xb8, 'wrong prefix'); @@ -2021,7 +2054,7 @@ fn test_rlp_encode_string_large_bytearray_inputs() { i += 1; }; - let res = RLPTrait::encode(array![RLPItem::String(input.span())].span()); + let res = RLPTrait::encode(array![RLPItem::String(input.span())].span()).unwrap(); assert(res.len() == 503, 'wrong len'); assert(*res[0] == 0xb9, 'wrong prefix'); @@ -2044,7 +2077,7 @@ fn test_rlp_encode_mutilple_string() { input.append(RLPItem::String(array![0x40, 0x53, 0x15, 0x94, 0x50, 0x40, 0x40, 0x40].span())); input.append(RLPItem::String(array![0x03].span())); - let res = RLPTrait::encode(input.span()); + let res = RLPTrait::encode(input.span()).unwrap(); assert(res.len() == 10, 'wrong len'); assert(*res[0] == 0x88, 'wrong prefix'); @@ -2060,7 +2093,7 @@ fn test_rlp_encode_short_list() { let mut expected_2 = RLPItem::String(array![0x45, 0x38, 0x92].span()); let expected_list = RLPItem::List(array![expected_0, expected_1, expected_2].span()); - let res = RLPTrait::encode(array![expected_list].span()); + let res = RLPTrait::encode(array![expected_list].span()).unwrap(); let mut arr = array![0xc9, 0x83, 0x35, 0x35, 0x35, 0x42, 0x83, 0x45, 0x38, 0x92]; @@ -2080,7 +2113,7 @@ fn test_rlp_encode_short_nested_list() { let expected = RLPItem::List(array![expected_0, expected_1, expected_2].span()); let mut arr = array![0xc7, 0xc0, 0xc1, 0xc0, 0xc3, 0xc0, 0xc1, 0xc0]; - let res = RLPTrait::encode(array![expected].span()); + let res = RLPTrait::encode(array![expected].span()).unwrap(); assert(res.len() == 8, 'wrong len'); assert(*res[0] == 0xc7, 'wrong prefix'); @@ -2708,7 +2741,7 @@ fn test_rlp_encode_long_list() { let expected_item = RLPItem::List(expected.span()); - let res = RLPTrait::encode(array![expected_item].span()); + let res = RLPTrait::encode(array![expected_item].span()).unwrap(); let mut arr = array![ 0xf9, From 845d895f85fcb7c58c1e9489449418761d2bcf56 Mon Sep 17 00:00:00 2001 From: Quentash Date: Mon, 20 Nov 2023 20:53:41 +0100 Subject: [PATCH 05/12] removed useless errors pasted code and added comments --- src/encoding/src/errors.cairo | 18 ------------------ src/encoding/src/rlp.cairo | 24 ++++++++++++++++++------ src/encoding/src/tests/rlp_test.cairo | 21 ++++++++++----------- 3 files changed, 28 insertions(+), 35 deletions(-) delete mode 100644 src/encoding/src/errors.cairo diff --git a/src/encoding/src/errors.cairo b/src/encoding/src/errors.cairo deleted file mode 100644 index 9d28b327..00000000 --- a/src/encoding/src/errors.cairo +++ /dev/null @@ -1,18 +0,0 @@ -// LENGTH -const RLP_EMPTY_INPUT: felt252 = 'KKT: EmptyInput'; -const RLP_INPUT_TOO_SHORT: felt252 = 'KKT: InputTooShort'; - -#[derive(Drop, Copy, PartialEq)] -enum RLPError { - EmptyInput: felt252, - InputTooShort: felt252, -} - -impl RLPErrorIntoU256 of Into { - fn into(self: RLPError) -> u256 { - match self { - RLPError::EmptyInput(error_message) => error_message.into(), - RLPError::InputTooShort(error_message) => error_message.into(), - } - } -} diff --git a/src/encoding/src/rlp.cairo b/src/encoding/src/rlp.cairo index 0e29a479..534a0e49 100644 --- a/src/encoding/src/rlp.cairo +++ b/src/encoding/src/rlp.cairo @@ -1,7 +1,12 @@ use alexandria_data_structures::array_ext::ArrayTraitExt; -use alexandria_encoding::errors::{RLPError, RLP_EMPTY_INPUT, RLP_INPUT_TOO_SHORT}; use alexandria_numeric::integers::U32Trait; +#[derive(Drop, Copy, PartialEq)] +enum RLPError { + EmptyInput, + InputTooShort +} + // Possible RLP types #[derive(Drop, PartialEq)] enum RLPType { @@ -31,7 +36,7 @@ impl RLPImpl of RLPTrait { fn decode_type(input: Span) -> Result<(RLPType, u32, u32), RLPError> { let input_len = input.len(); if input_len == 0 { - return Result::Err(RLPError::EmptyInput(RLP_EMPTY_INPUT)); + return Result::Err(RLPError::EmptyInput(())); } let prefix_byte = *input[0]; @@ -43,7 +48,7 @@ impl RLPImpl of RLPTrait { } else if prefix_byte < 0xc0 { // Long String let len_bytes_count: u32 = (prefix_byte - 0xb7).into(); if input_len <= len_bytes_count { - return Result::Err(RLPError::InputTooShort(RLP_INPUT_TOO_SHORT)); + return Result::Err(RLPError::InputTooShort(())); } let string_len_bytes = input.slice(1, len_bytes_count); let string_len: u32 = U32Trait::from_bytes(string_len_bytes).unwrap(); @@ -54,7 +59,7 @@ impl RLPImpl of RLPTrait { } else { // Long List let len_bytes_count = prefix_byte.into() - 0xf7; if input.len() <= len_bytes_count { - return Result::Err(RLPError::InputTooShort(RLP_INPUT_TOO_SHORT)); + return Result::Err(RLPError::InputTooShort(())); } let list_len_bytes = input.slice(1, len_bytes_count); @@ -72,10 +77,11 @@ impl RLPImpl of RLPTrait { /// * empty input - if the input is empty fn encode(mut input: Span) -> Result, RLPError> { if input.len() == 0 { - return Result::Err(RLPError::EmptyInput(RLP_EMPTY_INPUT)); + return Result::Err(RLPError::EmptyInput(())); } let mut output: Array = Default::default(); + // Safe to unwrap because input length is not 0 let item = input.pop_front().unwrap(); match item { @@ -87,10 +93,13 @@ impl RLPImpl of RLPTrait { let payload = RLPTrait::encode(*list)?; let payload_len = payload.len(); if payload_len > 55 { + // The payload length being a u32, the length in bytes + // will maximum be equal to 4, making the unwrap safe let len_in_bytes = payload_len.to_bytes(); output.append(0xf7 + len_in_bytes.len().try_into().unwrap()); output.concat_span(len_in_bytes); } else { + // Safe to unwrap because payload_len<55 output.append(0xc0 + payload_len.try_into().unwrap()); } output.concat_span(payload); @@ -118,11 +127,14 @@ impl RLPImpl of RLPTrait { return Result::Ok(input); } else if len < 56 { let mut encoding: Array = Default::default(); + // Safe to unwrap because len<56 encoding.append(0x80 + len.try_into().unwrap()); encoding.concat_span(input); return Result::Ok(encoding.span()); } else { let mut encoding: Array = Default::default(); + // The payload length being a u32, the length in bytes + // will maximum be equal to 4, making the unwrap safe let len_as_bytes = len.to_bytes(); let len_bytes_count = len_as_bytes.len(); let prefix = 0xb7 + len_bytes_count.try_into().unwrap(); @@ -149,7 +161,7 @@ impl RLPImpl of RLPTrait { let (rlp_type, offset, len) = RLPTrait::decode_type(input)?; if input_len < offset + len { - return Result::Err(RLPError::InputTooShort(RLP_INPUT_TOO_SHORT)); + return Result::Err(RLPError::InputTooShort(())); } match rlp_type { diff --git a/src/encoding/src/tests/rlp_test.cairo b/src/encoding/src/tests/rlp_test.cairo index 40ae88c5..7846b50b 100644 --- a/src/encoding/src/tests/rlp_test.cairo +++ b/src/encoding/src/tests/rlp_test.cairo @@ -1,5 +1,4 @@ -use alexandria_encoding::errors::{RLPError, RLP_EMPTY_INPUT, RLP_INPUT_TOO_SHORT}; -use alexandria_encoding::rlp::{RLPType, RLPTrait, RLPItem}; +use alexandria_encoding::rlp::{RLPError, RLPType, RLPTrait, RLPItem}; use result::ResultTrait; #[test] @@ -72,7 +71,7 @@ fn test_rlp_decode_type_long_list_len_too_short() { assert(res.is_err(), 'Should have failed'); assert( - res.unwrap_err() == RLPError::InputTooShort(RLP_INPUT_TOO_SHORT), 'err != InputTooShort' + res.unwrap_err() == RLPError::InputTooShort(()), 'err != InputTooShort' ); } @@ -82,7 +81,7 @@ fn test_rlp_empty() { let res = RLPTrait::decode(ArrayTrait::new().span()); assert(res.is_err(), 'Should have failed'); - assert(res.unwrap_err() == RLPError::EmptyInput(RLP_EMPTY_INPUT), 'err != EmptyInput'); + assert(res.unwrap_err() == RLPError::EmptyInput(()), 'err != EmptyInput'); } #[test] @@ -198,7 +197,7 @@ fn test_rlp_decode_short_string_input_too_short() { assert(res.is_err(), 'Should have failed'); assert( - res.unwrap_err() == RLPError::InputTooShort(RLP_INPUT_TOO_SHORT), 'err != InputTooShort' + res.unwrap_err() == RLPError::InputTooShort(()), 'err != InputTooShort' ); } @@ -350,7 +349,7 @@ fn test_rlp_decode_long_string_with_input_too_short() { assert(res.is_err(), 'Should have failed'); assert( - res.unwrap_err() == RLPError::InputTooShort(RLP_INPUT_TOO_SHORT), 'err != InputTooShort' + res.unwrap_err() == RLPError::InputTooShort(()), 'err != InputTooShort' ); } @@ -642,7 +641,7 @@ fn test_rlp_decode_long_string_with_payload_len_too_short() { assert(res.is_err(), 'Should have failed'); assert( - res.unwrap_err() == RLPError::InputTooShort(RLP_INPUT_TOO_SHORT), 'err != InputTooShort' + res.unwrap_err() == RLPError::InputTooShort(()), 'err != InputTooShort' ); } @@ -701,7 +700,7 @@ fn test_rlp_decode_short_list_with_input_too_short() { assert(res.is_err(), 'Should have failed'); assert( - res.unwrap_err() == RLPError::InputTooShort(RLP_INPUT_TOO_SHORT), 'err != InputTooShort' + res.unwrap_err() == RLPError::InputTooShort(()), 'err != InputTooShort' ); } @@ -1903,7 +1902,7 @@ fn test_rlp_decode_long_list_with_input_too_short() { assert(res.is_err(), 'Should have failed'); assert( - res.unwrap_err() == RLPError::InputTooShort(RLP_INPUT_TOO_SHORT), 'err != InputTooShort' + res.unwrap_err() == RLPError::InputTooShort(()), 'err != InputTooShort' ); } @@ -1916,7 +1915,7 @@ fn test_rlp_decode_long_list_with_len_too_short() { assert(res.is_err(), 'Should have failed'); assert( - res.unwrap_err() == RLPError::InputTooShort(RLP_INPUT_TOO_SHORT), 'err != InputTooShort' + res.unwrap_err() == RLPError::InputTooShort(()), 'err != InputTooShort' ); } @@ -1928,7 +1927,7 @@ fn test_rlp_encode_empty_input_should_fail() { let res = RLPTrait::encode(input.span()); assert(res.is_err(), 'Should have failed'); - assert(res.unwrap_err() == RLPError::EmptyInput(RLP_EMPTY_INPUT), 'err != EmptyInput'); + assert(res.unwrap_err() == RLPError::EmptyInput(()), 'err != EmptyInput'); } From b729542a5a3fb04e4cd725531f2638cef95419c3 Mon Sep 17 00:00:00 2001 From: Quentash Date: Mon, 20 Nov 2023 21:03:11 +0100 Subject: [PATCH 06/12] made generic UIntBytes trait --- src/encoding/src/lib.cairo | 1 - src/encoding/src/rlp.cairo | 6 +++--- src/numeric/src/integers.cairo | 11 ++++++++--- 3 files changed, 11 insertions(+), 7 deletions(-) diff --git a/src/encoding/src/lib.cairo b/src/encoding/src/lib.cairo index 4a82dfd2..0d146631 100644 --- a/src/encoding/src/lib.cairo +++ b/src/encoding/src/lib.cairo @@ -1,5 +1,4 @@ mod base64; -mod errors; mod reversible; mod rlp; diff --git a/src/encoding/src/rlp.cairo b/src/encoding/src/rlp.cairo index 534a0e49..76547441 100644 --- a/src/encoding/src/rlp.cairo +++ b/src/encoding/src/rlp.cairo @@ -1,5 +1,5 @@ use alexandria_data_structures::array_ext::ArrayTraitExt; -use alexandria_numeric::integers::U32Trait; +use alexandria_numeric::integers::UIntBytes; #[derive(Drop, Copy, PartialEq)] enum RLPError { @@ -51,7 +51,7 @@ impl RLPImpl of RLPTrait { return Result::Err(RLPError::InputTooShort(())); } let string_len_bytes = input.slice(1, len_bytes_count); - let string_len: u32 = U32Trait::from_bytes(string_len_bytes).unwrap(); + let string_len: u32 = UIntBytes::from_bytes(string_len_bytes).unwrap(); return Result::Ok((RLPType::String, 1 + len_bytes_count, string_len)); } else if prefix_byte < 0xf8 { // Short List @@ -63,7 +63,7 @@ impl RLPImpl of RLPTrait { } let list_len_bytes = input.slice(1, len_bytes_count); - let list_len: u32 = U32Trait::from_bytes(list_len_bytes).unwrap(); + let list_len: u32 = UIntBytes::from_bytes(list_len_bytes).unwrap(); return Result::Ok((RLPType::List, 1 + len_bytes_count, list_len)); } } diff --git a/src/numeric/src/integers.cairo b/src/numeric/src/integers.cairo index fb48ecbf..0510a87e 100644 --- a/src/numeric/src/integers.cairo +++ b/src/numeric/src/integers.cairo @@ -1,7 +1,12 @@ use alexandria_math::BitShift; -#[generate_trait] -impl U32Impl of U32Trait { +trait UIntBytes { + fn from_bytes(input: Span) -> Option; + fn to_bytes(self: T) -> Span; + fn bytes_used(self: T) -> u8; +} + +impl U32BytesImpl of UIntBytes { /// Packs 4 bytes into a u32 /// # Arguments /// * `input` a Span of len <=4 @@ -57,7 +62,7 @@ impl U32Impl of U32Trait { /// * `self` - The value to check. /// # Returns /// The number of bytes used to represent the value. - fn bytes_used(self: usize) -> u8 { + fn bytes_used(self: u32) -> u8 { if self < 0x10000 { // 256^2 if self < 0x100 { // 256^1 if self == 0 { From 174fc64dfc087d821957792f3341d8869102793f Mon Sep 17 00:00:00 2001 From: Quentash Date: Mon, 20 Nov 2023 21:08:58 +0100 Subject: [PATCH 07/12] modified comment --- src/encoding/src/rlp.cairo | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/encoding/src/rlp.cairo b/src/encoding/src/rlp.cairo index 76547441..3eb84057 100644 --- a/src/encoding/src/rlp.cairo +++ b/src/encoding/src/rlp.cairo @@ -1,6 +1,7 @@ use alexandria_data_structures::array_ext::ArrayTraitExt; use alexandria_numeric::integers::UIntBytes; +// Possible RLP errors #[derive(Drop, Copy, PartialEq)] enum RLPError { EmptyInput, @@ -68,7 +69,7 @@ impl RLPImpl of RLPTrait { } } - /// Recursively encodes multiple RLPItems + /// Recursively encodes multiple a list of RLPItems /// # Arguments /// * `input` - Span of RLPItem to encode /// # Returns From d396a37e0bd45de599ffde15149b20bc561cc3e9 Mon Sep 17 00:00:00 2001 From: Quentash Date: Mon, 20 Nov 2023 23:48:46 +0100 Subject: [PATCH 08/12] corrected tests variables names --- src/encoding/src/tests/rlp_test.cairo | 140 +++++++++++--------------- 1 file changed, 61 insertions(+), 79 deletions(-) diff --git a/src/encoding/src/tests/rlp_test.cairo b/src/encoding/src/tests/rlp_test.cairo index 7846b50b..759710f3 100644 --- a/src/encoding/src/tests/rlp_test.cairo +++ b/src/encoding/src/tests/rlp_test.cairo @@ -70,9 +70,7 @@ fn test_rlp_decode_type_long_list_len_too_short() { let res = RLPTrait::decode_type(arr.span()); assert(res.is_err(), 'Should have failed'); - assert( - res.unwrap_err() == RLPError::InputTooShort(()), 'err != InputTooShort' - ); + assert(res.unwrap_err() == RLPError::InputTooShort(()), 'err != InputTooShort'); } #[test] @@ -196,9 +194,7 @@ fn test_rlp_decode_short_string_input_too_short() { let res = RLPTrait::decode(arr.span()); assert(res.is_err(), 'Should have failed'); - assert( - res.unwrap_err() == RLPError::InputTooShort(()), 'err != InputTooShort' - ); + assert(res.unwrap_err() == RLPError::InputTooShort(()), 'err != InputTooShort'); } #[test] @@ -348,9 +344,7 @@ fn test_rlp_decode_long_string_with_input_too_short() { let res = RLPTrait::decode(arr.span()); assert(res.is_err(), 'Should have failed'); - assert( - res.unwrap_err() == RLPError::InputTooShort(()), 'err != InputTooShort' - ); + assert(res.unwrap_err() == RLPError::InputTooShort(()), 'err != InputTooShort'); } @@ -640,9 +634,7 @@ fn test_rlp_decode_long_string_with_payload_len_too_short() { let res = RLPTrait::decode(arr.span()); assert(res.is_err(), 'Should have failed'); - assert( - res.unwrap_err() == RLPError::InputTooShort(()), 'err != InputTooShort' - ); + assert(res.unwrap_err() == RLPError::InputTooShort(()), 'err != InputTooShort'); } #[test] @@ -699,9 +691,7 @@ fn test_rlp_decode_short_list_with_input_too_short() { let res = RLPTrait::decode(arr.span()); assert(res.is_err(), 'Should have failed'); - assert( - res.unwrap_err() == RLPError::InputTooShort(()), 'err != InputTooShort' - ); + assert(res.unwrap_err() == RLPError::InputTooShort(()), 'err != InputTooShort'); } #[test] @@ -1901,9 +1891,7 @@ fn test_rlp_decode_long_list_with_input_too_short() { let res = RLPTrait::decode(arr.span()); assert(res.is_err(), 'Should have failed'); - assert( - res.unwrap_err() == RLPError::InputTooShort(()), 'err != InputTooShort' - ); + assert(res.unwrap_err() == RLPError::InputTooShort(()), 'err != InputTooShort'); } #[test] @@ -1914,9 +1902,7 @@ fn test_rlp_decode_long_list_with_len_too_short() { let res = RLPTrait::decode(arr.span()); assert(res.is_err(), 'Should have failed'); - assert( - res.unwrap_err() == RLPError::InputTooShort(()), 'err != InputTooShort' - ); + assert(res.unwrap_err() == RLPError::InputTooShort(()), 'err != InputTooShort'); } #[test] @@ -2087,43 +2073,39 @@ fn test_rlp_encode_mutilple_string() { #[test] #[available_gas(99999999999)] fn test_rlp_encode_short_list() { - let mut expected_0 = RLPItem::String(array![0x35, 0x35, 0x35].span()); - let mut expected_1 = RLPItem::String(array![0x42].span()); - let mut expected_2 = RLPItem::String(array![0x45, 0x38, 0x92].span()); + let mut string_0 = RLPItem::String(array![0x35, 0x35, 0x35].span()); + let mut string_1 = RLPItem::String(array![0x42].span()); + let mut string_2 = RLPItem::String(array![0x45, 0x38, 0x92].span()); - let expected_list = RLPItem::List(array![expected_0, expected_1, expected_2].span()); - let res = RLPTrait::encode(array![expected_list].span()).unwrap(); + let list = RLPItem::List(array![string_0, string_1, string_2].span()); + let res = RLPTrait::encode(array![list].span()).unwrap(); - let mut arr = array![0xc9, 0x83, 0x35, 0x35, 0x35, 0x42, 0x83, 0x45, 0x38, 0x92]; + let expected = array![0xc9, 0x83, 0x35, 0x35, 0x35, 0x42, 0x83, 0x45, 0x38, 0x92]; assert(res.len() == 10, 'wrong len'); - assert(*res[0] == 0xc9, 'wrong prefix'); - assert(*res[1] == 0x83, 'wrong first value'); - assert(*res[2] == 0x35, 'wrong second value'); + assert(res == expected.span(), 'wrong encoded result'); } #[test] #[available_gas(99999999999)] fn test_rlp_encode_short_nested_list() { - let mut expected_0 = RLPItem::List(array![].span()); - let mut expected_1 = RLPItem::List(array![expected_0].span()); - let mut expected_2 = RLPItem::List(array![expected_0, expected_1].span()); + let mut string_0 = RLPItem::List(array![].span()); + let mut string_1 = RLPItem::List(array![string_0].span()); + let mut string_2 = RLPItem::List(array![string_0, string_1].span()); - let expected = RLPItem::List(array![expected_0, expected_1, expected_2].span()); + let list = RLPItem::List(array![string_0, string_1, string_2].span()); + let res = RLPTrait::encode(array![list].span()).unwrap(); - let mut arr = array![0xc7, 0xc0, 0xc1, 0xc0, 0xc3, 0xc0, 0xc1, 0xc0]; - let res = RLPTrait::encode(array![expected].span()).unwrap(); + let mut expected = array![0xc7, 0xc0, 0xc1, 0xc0, 0xc3, 0xc0, 0xc1, 0xc0]; assert(res.len() == 8, 'wrong len'); - assert(*res[0] == 0xc7, 'wrong prefix'); - assert(*res[1] == 0xc0, 'wrong first value'); - assert(*res[2] == 0xc1, 'wrong second value'); + assert(res == expected.span(), 'wrong encoded result'); } #[test] #[available_gas(99999999999)] fn test_rlp_encode_long_list() { - let mut expected_0 = RLPItem::String( + let mut string_0 = RLPItem::String( array![ 0x77, 0x70, @@ -2160,7 +2142,7 @@ fn test_rlp_encode_long_list() { ] .span() ); - let mut expected_1 = RLPItem::String( + let mut string_1 = RLPItem::String( array![ 0x1e, 0xa3, @@ -2197,7 +2179,7 @@ fn test_rlp_encode_long_list() { ] .span() ); - let mut expected_2 = RLPItem::String( + let mut string_2 = RLPItem::String( array![ 0x2c, 0x4c, @@ -2234,7 +2216,7 @@ fn test_rlp_encode_long_list() { ] .span() ); - let mut expected_3 = RLPItem::String( + let mut string_3 = RLPItem::String( array![ 0xa9, 0xdc, @@ -2271,7 +2253,7 @@ fn test_rlp_encode_long_list() { ] .span() ); - let mut expected_4 = RLPItem::String( + let mut string_4 = RLPItem::String( array![ 0xa9, 0x5f, @@ -2308,7 +2290,7 @@ fn test_rlp_encode_long_list() { ] .span() ); - let mut expected_5 = RLPItem::String( + let mut string_5 = RLPItem::String( array![ 0x39, 0xd4, @@ -2345,7 +2327,7 @@ fn test_rlp_encode_long_list() { ] .span() ); - let mut expected_6 = RLPItem::String( + let mut string_6 = RLPItem::String( array![ 0x7a, 0xcc, @@ -2382,7 +2364,7 @@ fn test_rlp_encode_long_list() { ] .span() ); - let mut expected_7 = RLPItem::String( + let mut string_7 = RLPItem::String( array![ 0x15, 0x35, @@ -2419,7 +2401,7 @@ fn test_rlp_encode_long_list() { ] .span() ); - let mut expected_8 = RLPItem::String( + let mut string_8 = RLPItem::String( array![ 0x68, 0x91, @@ -2456,7 +2438,7 @@ fn test_rlp_encode_long_list() { ] .span() ); - let mut expected_9 = RLPItem::String( + let mut string_9 = RLPItem::String( array![ 0xdc, 0x36, @@ -2493,7 +2475,7 @@ fn test_rlp_encode_long_list() { ] .span() ); - let mut expected_10 = RLPItem::String( + let mut string_10 = RLPItem::String( array![ 0x20, 0xb0, @@ -2530,7 +2512,7 @@ fn test_rlp_encode_long_list() { ] .span() ); - let mut expected_11 = RLPItem::String( + let mut string_11 = RLPItem::String( array![ 0x8e, 0xed, @@ -2567,7 +2549,7 @@ fn test_rlp_encode_long_list() { ] .span() ); - let mut expected_12 = RLPItem::String( + let mut string_12 = RLPItem::String( array![ 0x79, 0x23, @@ -2604,7 +2586,7 @@ fn test_rlp_encode_long_list() { ] .span() ); - let mut expected_13 = RLPItem::String( + let mut string_13 = RLPItem::String( array![ 0x65, 0x34, @@ -2641,7 +2623,7 @@ fn test_rlp_encode_long_list() { ] .span() ); - let mut expected_14 = RLPItem::String( + let mut string_14 = RLPItem::String( array![ 0xbf, 0xf9, @@ -2678,7 +2660,7 @@ fn test_rlp_encode_long_list() { ] .span() ); - let mut expected_15 = RLPItem::String( + let mut string_15 = RLPItem::String( array![ 0x7f, 0x14, @@ -2716,33 +2698,33 @@ fn test_rlp_encode_long_list() { .span() ); - let mut expected_16 = RLPItem::String(array![].span()); - - let mut expected = array![ - expected_0, - expected_1, - expected_2, - expected_3, - expected_4, - expected_5, - expected_6, - expected_7, - expected_8, - expected_9, - expected_10, - expected_11, - expected_12, - expected_13, - expected_14, - expected_15, - expected_16 + let mut string_16 = RLPItem::String(array![].span()); + + let mut strings_list = array![ + string_0, + string_1, + string_2, + string_3, + string_4, + string_5, + string_6, + string_7, + string_8, + string_9, + string_10, + string_11, + string_12, + string_13, + string_14, + string_15, + string_16 ]; - let expected_item = RLPItem::List(expected.span()); + let list = RLPItem::List(strings_list.span()); - let res = RLPTrait::encode(array![expected_item].span()).unwrap(); + let res = RLPTrait::encode(array![list].span()).unwrap(); - let mut arr = array![ + let mut expected = array![ 0xf9, 0x02, 0x11, @@ -3277,5 +3259,5 @@ fn test_rlp_encode_long_list() { 0x80 ]; - assert(res == arr.span(), 'Wrong value'); + assert(res == expected.span(), 'Wrong value'); } From 2d30fffb93cf37aee19a3710482ffb028ce6f79f Mon Sep 17 00:00:00 2001 From: Quentash Date: Tue, 21 Nov 2023 00:22:48 +0100 Subject: [PATCH 09/12] added from bytes/to bytes tests --- src/numeric/src/tests.cairo | 1 + src/numeric/src/tests/integers_test.cairo | 76 +++++++++++++++++++++++ 2 files changed, 77 insertions(+) create mode 100644 src/numeric/src/tests/integers_test.cairo diff --git a/src/numeric/src/tests.cairo b/src/numeric/src/tests.cairo index 57e3d3d8..b914422b 100644 --- a/src/numeric/src/tests.cairo +++ b/src/numeric/src/tests.cairo @@ -1,4 +1,5 @@ mod cumsum_test; mod diff_test; +mod integers_test; mod interpolate_test; mod trapezoidal_rule_test; diff --git a/src/numeric/src/tests/integers_test.cairo b/src/numeric/src/tests/integers_test.cairo new file mode 100644 index 00000000..ea03e952 --- /dev/null +++ b/src/numeric/src/tests/integers_test.cairo @@ -0,0 +1,76 @@ +use alexandria_numeric::integers::UIntBytes; + +#[test] +#[available_gas(2000000)] +fn test_u32_from_bytes() { + let input: Array = array![0xf4, 0x32, 0x15, 0x62]; + let res: Option = UIntBytes::from_bytes(input.span()); + + assert(res.is_some(), 'should have a value'); + assert(res.unwrap() == 0xf4321562, 'wrong result value'); +} + +#[test] +#[available_gas(2000000)] +fn test_u32_from_bytes_too_big() { + let input: Array = array![0xf4, 0x32, 0x15, 0x62, 0x01]; + let res: Option = UIntBytes::from_bytes(input.span()); + + assert(res.is_none(), 'should not have a value'); +} + + +#[test] +#[available_gas(2000000)] +fn test_u32_to_bytes_full() { + let input: u32 = 0xf4321562; + let res: Span = input.to_bytes(); + + assert(res.len() == 4, 'wrong result length'); + assert(*res[0] == 0xf4, 'wrong result value'); + assert(*res[1] == 0x32, 'wrong result value'); + assert(*res[2] == 0x15, 'wrong result value'); + assert(*res[3] == 0x62, 'wrong result value'); +} + +#[test] +#[available_gas(2000000)] +fn test_u32_to_bytes_partial() { + let input: u32 = 0xf43215; + let res: Span = input.to_bytes(); + + assert(res.len() == 3, 'wrong result length'); + assert(*res[0] == 0xf4, 'wrong result value'); + assert(*res[1] == 0x32, 'wrong result value'); + assert(*res[2] == 0x15, 'wrong result value'); +} + + +#[test] +#[available_gas(2000000)] +fn test_u32_to_bytes_leading_zeros() { + let input: u32 = 0x00f432; + let res: Span = input.to_bytes(); + + assert(res.len() == 2, 'wrong result length'); + assert(*res[0] == 0xf4, 'wrong result value'); + assert(*res[1] == 0x32, 'wrong result value'); +} + +#[test] +#[available_gas(20000000)] +fn test_u32_bytes_used() { + let len: u32 = 0x1234; + let bytes_count = len.bytes_used(); + + assert(bytes_count == 2, 'wrong bytes count'); +} + +#[test] +#[available_gas(20000000)] +fn test_u32_bytes_used_leading_zeroes() { + let len: u32 = 0x001234; + let bytes_count = len.bytes_used(); + + assert(bytes_count == 2, 'wrong bytes count'); +} From 9eaf7cde788da785fe04d2d782003cbe1e9be07d Mon Sep 17 00:00:00 2001 From: Quentash Date: Wed, 22 Nov 2023 10:40:56 +0100 Subject: [PATCH 10/12] added payloadtoolong error --- src/encoding/src/rlp.cairo | 25 +++++++++++++++++-------- src/encoding/src/tests/rlp_test.cairo | 26 ++++++++++++++++++++++++-- 2 files changed, 41 insertions(+), 10 deletions(-) diff --git a/src/encoding/src/rlp.cairo b/src/encoding/src/rlp.cairo index 3eb84057..97af2c1b 100644 --- a/src/encoding/src/rlp.cairo +++ b/src/encoding/src/rlp.cairo @@ -5,7 +5,8 @@ use alexandria_numeric::integers::UIntBytes; #[derive(Drop, Copy, PartialEq)] enum RLPError { EmptyInput, - InputTooShort + InputTooShort, + PayloadTooLong } // Possible RLP types @@ -37,7 +38,7 @@ impl RLPImpl of RLPTrait { fn decode_type(input: Span) -> Result<(RLPType, u32, u32), RLPError> { let input_len = input.len(); if input_len == 0 { - return Result::Err(RLPError::EmptyInput(())); + return Result::Err(RLPError::EmptyInput); } let prefix_byte = *input[0]; @@ -49,9 +50,14 @@ impl RLPImpl of RLPTrait { } else if prefix_byte < 0xc0 { // Long String let len_bytes_count: u32 = (prefix_byte - 0xb7).into(); if input_len <= len_bytes_count { - return Result::Err(RLPError::InputTooShort(())); + return Result::Err(RLPError::InputTooShort); + } + if len_bytes_count > 4 { + return Result::Err(RLPError::PayloadTooLong); } let string_len_bytes = input.slice(1, len_bytes_count); + // Length bytes count value being 1 <= x <= 4, + // we're sure to retrieve an u32 value making the unwrap safe let string_len: u32 = UIntBytes::from_bytes(string_len_bytes).unwrap(); return Result::Ok((RLPType::String, 1 + len_bytes_count, string_len)); @@ -60,10 +66,14 @@ impl RLPImpl of RLPTrait { } else { // Long List let len_bytes_count = prefix_byte.into() - 0xf7; if input.len() <= len_bytes_count { - return Result::Err(RLPError::InputTooShort(())); + return Result::Err(RLPError::InputTooShort); + } + if len_bytes_count > 4 { + return Result::Err(RLPError::PayloadTooLong); } - let list_len_bytes = input.slice(1, len_bytes_count); + // Length bytes count value being 1 <= x <= 4, + // we're sure to retrieve an u32 value making the unwrap safe let list_len: u32 = UIntBytes::from_bytes(list_len_bytes).unwrap(); return Result::Ok((RLPType::List, 1 + len_bytes_count, list_len)); } @@ -78,7 +88,7 @@ impl RLPImpl of RLPTrait { /// * empty input - if the input is empty fn encode(mut input: Span) -> Result, RLPError> { if input.len() == 0 { - return Result::Err(RLPError::EmptyInput(())); + return Result::Err(RLPError::EmptyInput); } let mut output: Array = Default::default(); @@ -162,12 +172,11 @@ impl RLPImpl of RLPTrait { let (rlp_type, offset, len) = RLPTrait::decode_type(input)?; if input_len < offset + len { - return Result::Err(RLPError::InputTooShort(())); + return Result::Err(RLPError::InputTooShort); } match rlp_type { RLPType::String => { - // checking for default value `0` if (len == 0) { output.append(RLPItem::String(array![].span())); } else { diff --git a/src/encoding/src/tests/rlp_test.cairo b/src/encoding/src/tests/rlp_test.cairo index 759710f3..2ddb9d7e 100644 --- a/src/encoding/src/tests/rlp_test.cairo +++ b/src/encoding/src/tests/rlp_test.cairo @@ -1,4 +1,6 @@ use alexandria_encoding::rlp::{RLPError, RLPType, RLPTrait, RLPItem}; + +use debug::PrintTrait; use result::ResultTrait; #[test] @@ -25,7 +27,6 @@ fn test_rlp_decode_type_short_string() { assert(size == 2, 'Wrong size'); } - #[test] #[available_gas(99999999)] fn test_rlp_decode_type_long_string() { @@ -73,6 +74,28 @@ fn test_rlp_decode_type_long_list_len_too_short() { assert(res.unwrap_err() == RLPError::InputTooShort(()), 'err != InputTooShort'); } +#[test] +#[available_gas(99999999)] +fn test_rlp_decode_type_long_string_payload_too_long() { + let mut arr = array![0xbf, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02]; + + let res = RLPTrait::decode_type(arr.span()); + + assert(res.is_err(), 'Should have failed'); + assert(res.unwrap_err() == RLPError::PayloadTooLong(()), 'err != PayloadTooLong'); +} + +#[test] +#[available_gas(99999999)] +fn test_rlp_decode_type_long_list_payload_too_long() { + let mut arr = array![0xfc, 0x01, 0x02, 0x02, 0x02, 0x02]; + + let res = RLPTrait::decode_type(arr.span()); + + assert(res.is_err(), 'Should have failed'); + assert(res.unwrap_err() == RLPError::PayloadTooLong(()), 'err != PayloadTooLong'); +} + #[test] #[available_gas(9999999)] fn test_rlp_empty() { @@ -1940,7 +1963,6 @@ fn test_rlp_encode_string_single_byte_lt_0x80() { assert(*res[0] == 0x40, 'wrong encoded value'); } - #[test] #[available_gas(20000000)] fn test_rlp_encode_string_single_byte_ge_0x80() { From ee01ddea030c28bb6aa5df6070805de98fefd070 Mon Sep 17 00:00:00 2001 From: Quentash Date: Wed, 29 Nov 2023 16:13:39 +0100 Subject: [PATCH 11/12] ok_or(payloadtoolong) --- src/encoding/src/rlp.cairo | 16 ++++------------ 1 file changed, 4 insertions(+), 12 deletions(-) diff --git a/src/encoding/src/rlp.cairo b/src/encoding/src/rlp.cairo index 97af2c1b..0aec7624 100644 --- a/src/encoding/src/rlp.cairo +++ b/src/encoding/src/rlp.cairo @@ -52,13 +52,9 @@ impl RLPImpl of RLPTrait { if input_len <= len_bytes_count { return Result::Err(RLPError::InputTooShort); } - if len_bytes_count > 4 { - return Result::Err(RLPError::PayloadTooLong); - } let string_len_bytes = input.slice(1, len_bytes_count); - // Length bytes count value being 1 <= x <= 4, - // we're sure to retrieve an u32 value making the unwrap safe - let string_len: u32 = UIntBytes::from_bytes(string_len_bytes).unwrap(); + let string_len: u32 = UIntBytes::from_bytes(string_len_bytes) + .ok_or(RLPError::PayloadTooLong)?; return Result::Ok((RLPType::String, 1 + len_bytes_count, string_len)); } else if prefix_byte < 0xf8 { // Short List @@ -68,13 +64,9 @@ impl RLPImpl of RLPTrait { if input.len() <= len_bytes_count { return Result::Err(RLPError::InputTooShort); } - if len_bytes_count > 4 { - return Result::Err(RLPError::PayloadTooLong); - } let list_len_bytes = input.slice(1, len_bytes_count); - // Length bytes count value being 1 <= x <= 4, - // we're sure to retrieve an u32 value making the unwrap safe - let list_len: u32 = UIntBytes::from_bytes(list_len_bytes).unwrap(); + let list_len: u32 = UIntBytes::from_bytes(list_len_bytes) + .ok_or(RLPError::PayloadTooLong)?; return Result::Ok((RLPType::List, 1 + len_bytes_count, list_len)); } } From 7fc9a89b7a93791eed2be96467f942c1af1a7085 Mon Sep 17 00:00:00 2001 From: Quentash Date: Thu, 30 Nov 2023 17:25:25 +0100 Subject: [PATCH 12/12] corrected tests --- src/encoding/src/rlp.cairo | 6 +++--- src/encoding/src/tests/rlp_test.cairo | 24 ++++++++++++------------ src/numeric/src/integers.cairo | 4 ++-- 3 files changed, 17 insertions(+), 17 deletions(-) diff --git a/src/encoding/src/rlp.cairo b/src/encoding/src/rlp.cairo index 0aec7624..10fa6184 100644 --- a/src/encoding/src/rlp.cairo +++ b/src/encoding/src/rlp.cairo @@ -96,9 +96,9 @@ impl RLPImpl of RLPTrait { let payload = RLPTrait::encode(*list)?; let payload_len = payload.len(); if payload_len > 55 { + let len_in_bytes = payload_len.to_bytes(); // The payload length being a u32, the length in bytes // will maximum be equal to 4, making the unwrap safe - let len_in_bytes = payload_len.to_bytes(); output.append(0xf7 + len_in_bytes.len().try_into().unwrap()); output.concat_span(len_in_bytes); } else { @@ -136,10 +136,10 @@ impl RLPImpl of RLPTrait { return Result::Ok(encoding.span()); } else { let mut encoding: Array = Default::default(); - // The payload length being a u32, the length in bytes - // will maximum be equal to 4, making the unwrap safe let len_as_bytes = len.to_bytes(); let len_bytes_count = len_as_bytes.len(); + // The payload length being a u32, the length in bytes + // will maximum be equal to 4, making the unwrap safe let prefix = 0xb7 + len_bytes_count.try_into().unwrap(); encoding.append(prefix); encoding.concat_span(len_as_bytes); diff --git a/src/encoding/src/tests/rlp_test.cairo b/src/encoding/src/tests/rlp_test.cairo index 2ddb9d7e..c1e3b446 100644 --- a/src/encoding/src/tests/rlp_test.cairo +++ b/src/encoding/src/tests/rlp_test.cairo @@ -71,7 +71,7 @@ fn test_rlp_decode_type_long_list_len_too_short() { let res = RLPTrait::decode_type(arr.span()); assert(res.is_err(), 'Should have failed'); - assert(res.unwrap_err() == RLPError::InputTooShort(()), 'err != InputTooShort'); + assert(res.unwrap_err() == RLPError::InputTooShort, 'err != InputTooShort'); } #[test] @@ -82,7 +82,7 @@ fn test_rlp_decode_type_long_string_payload_too_long() { let res = RLPTrait::decode_type(arr.span()); assert(res.is_err(), 'Should have failed'); - assert(res.unwrap_err() == RLPError::PayloadTooLong(()), 'err != PayloadTooLong'); + assert(res.unwrap_err() == RLPError::PayloadTooLong, 'err != PayloadTooLong'); } #[test] @@ -93,7 +93,7 @@ fn test_rlp_decode_type_long_list_payload_too_long() { let res = RLPTrait::decode_type(arr.span()); assert(res.is_err(), 'Should have failed'); - assert(res.unwrap_err() == RLPError::PayloadTooLong(()), 'err != PayloadTooLong'); + assert(res.unwrap_err() == RLPError::PayloadTooLong, 'err != PayloadTooLong'); } #[test] @@ -102,7 +102,7 @@ fn test_rlp_empty() { let res = RLPTrait::decode(ArrayTrait::new().span()); assert(res.is_err(), 'Should have failed'); - assert(res.unwrap_err() == RLPError::EmptyInput(()), 'err != EmptyInput'); + assert(res.unwrap_err() == RLPError::EmptyInput, 'err != EmptyInput'); } #[test] @@ -124,7 +124,7 @@ fn test_rlp_decode_string() { let mut i = 0; loop { if i == 0x80 { - break (); + break; } let mut arr = ArrayTrait::new(); arr.append(i); @@ -217,7 +217,7 @@ fn test_rlp_decode_short_string_input_too_short() { let res = RLPTrait::decode(arr.span()); assert(res.is_err(), 'Should have failed'); - assert(res.unwrap_err() == RLPError::InputTooShort(()), 'err != InputTooShort'); + assert(res.unwrap_err() == RLPError::InputTooShort, 'err != InputTooShort'); } #[test] @@ -367,7 +367,7 @@ fn test_rlp_decode_long_string_with_input_too_short() { let res = RLPTrait::decode(arr.span()); assert(res.is_err(), 'Should have failed'); - assert(res.unwrap_err() == RLPError::InputTooShort(()), 'err != InputTooShort'); + assert(res.unwrap_err() == RLPError::InputTooShort, 'err != InputTooShort'); } @@ -657,7 +657,7 @@ fn test_rlp_decode_long_string_with_payload_len_too_short() { let res = RLPTrait::decode(arr.span()); assert(res.is_err(), 'Should have failed'); - assert(res.unwrap_err() == RLPError::InputTooShort(()), 'err != InputTooShort'); + assert(res.unwrap_err() == RLPError::InputTooShort, 'err != InputTooShort'); } #[test] @@ -714,7 +714,7 @@ fn test_rlp_decode_short_list_with_input_too_short() { let res = RLPTrait::decode(arr.span()); assert(res.is_err(), 'Should have failed'); - assert(res.unwrap_err() == RLPError::InputTooShort(()), 'err != InputTooShort'); + assert(res.unwrap_err() == RLPError::InputTooShort, 'err != InputTooShort'); } #[test] @@ -1914,7 +1914,7 @@ fn test_rlp_decode_long_list_with_input_too_short() { let res = RLPTrait::decode(arr.span()); assert(res.is_err(), 'Should have failed'); - assert(res.unwrap_err() == RLPError::InputTooShort(()), 'err != InputTooShort'); + assert(res.unwrap_err() == RLPError::InputTooShort, 'err != InputTooShort'); } #[test] @@ -1925,7 +1925,7 @@ fn test_rlp_decode_long_list_with_len_too_short() { let res = RLPTrait::decode(arr.span()); assert(res.is_err(), 'Should have failed'); - assert(res.unwrap_err() == RLPError::InputTooShort(()), 'err != InputTooShort'); + assert(res.unwrap_err() == RLPError::InputTooShort, 'err != InputTooShort'); } #[test] @@ -1936,7 +1936,7 @@ fn test_rlp_encode_empty_input_should_fail() { let res = RLPTrait::encode(input.span()); assert(res.is_err(), 'Should have failed'); - assert(res.unwrap_err() == RLPError::EmptyInput(()), 'err != EmptyInput'); + assert(res.unwrap_err() == RLPError::EmptyInput, 'err != EmptyInput'); } diff --git a/src/numeric/src/integers.cairo b/src/numeric/src/integers.cairo index 0510a87e..03111de5 100644 --- a/src/numeric/src/integers.cairo +++ b/src/numeric/src/integers.cairo @@ -26,7 +26,7 @@ impl U32BytesImpl of UIntBytes { let mut i: u32 = 0; loop { if i == len { - break (); + break; } let byte: u32 = (*input.at(i)).into(); result += BitShift::shl(byte, 8 * (offset - i)); @@ -47,7 +47,7 @@ impl U32BytesImpl of UIntBytes { let mut i = 0; loop { if i == bytes_used { - break (); + break; } let val = BitShift::shr(self, 8 * (bytes_used.try_into().unwrap() - i - 1)); bytes.append((val & 0xFF).try_into().unwrap());