diff --git a/ampd/src/starknet/events/contract_call.rs b/ampd/src/starknet/events/contract_call.rs new file mode 100644 index 000000000..2155ef17d --- /dev/null +++ b/ampd/src/starknet/events/contract_call.rs @@ -0,0 +1,282 @@ +use std::num::TryFromIntError; + +use ethers::types::H256; +use starknet_core::types::ValueOutOfRangeError; +use starknet_core::utils::{parse_cairo_short_string, ParseCairoShortStringError}; +use thiserror::Error; + +use crate::starknet::events::EventType; +use crate::starknet::types::byte_array::{ByteArray, ByteArrayError}; +use crate::types::Hash; + +/// This is the event emitted by the gateway cairo contract on Starknet, +/// when the call_contract method is called from a third party. +#[derive(Debug, PartialEq)] +pub struct ContractCallEvent { + pub destination_address: String, + pub destination_chain: String, + pub source_address: String, + pub payload_hash: Hash, +} + +/// An error, representing failure to convert/parse a starknet event +/// to some specific event. +#[derive(Error, Debug)] +pub enum ContractCallError { + #[error("Invalid ContractCall event: {0}")] + InvalidEvent(String), + #[error("Cairo short string parse error: {0}")] + Cairo(#[from] ParseCairoShortStringError), + #[error("FieldElement operation errored with out of range: {0}")] + FeltOutOfRange(#[from] ValueOutOfRangeError), + #[error("Failed int conversion: {0}")] + TryFromConversion(#[from] TryFromIntError), + #[error("Event data/keys array index is out of bounds")] + OutOfBound, + #[error("ByteArray type error: {0}")] + ByteArray(#[from] ByteArrayError), +} + +impl TryFrom for ContractCallEvent { + type Error = ContractCallError; + + fn try_from(starknet_event: starknet_core::types::Event) -> Result { + if starknet_event.keys.len() != 2 { + return Err(ContractCallError::InvalidEvent( + "ContractCall should have exactly 2 event keys - event_type and destination_chain" + .to_owned(), + )); + } + + // first key is always the event type + let event_type_felt = starknet_event.keys[0]; + if !matches!( + EventType::parse(event_type_felt), + Some(EventType::ContractCall) + ) { + return Err(ContractCallError::InvalidEvent( + "not a ContractCall event".to_owned(), + )); + } + + // destination_chain is the second key in the event keys list (the first key + // defined from the event) + // + // This field, should not exceed 252 bits (a felt's length) + let destination_chain = parse_cairo_short_string(&starknet_event.keys[1])?; + + // source_address represents the original caller of the `call_contract` gateway + // method. It is the first field in data, by the order defined in the + // event. + // + // TODO: Not sure if `064x` is the correct formatting. Maybe we should calculate + // the pedersen hash of the felt as described here, to get the actual address, + // although I'm not sure that we can do it as described here: + // https://docs.starknet.io/documentation/architecture_and_concepts/Smart_Contracts/contract-address/ + let source_address = format!("0x{:064x}", starknet_event.data[0]); + + // destination_contract_address (ByteArray) is composed of FieldElements + // from the second element to elemet X. + let destination_address_chunks_count_felt = starknet_event.data[1]; + let da_chunks_count: usize = u8::try_from(destination_address_chunks_count_felt)?.into(); + + // It's + 3, because we need to offset the 0th element, pending_word and + // pending_word_count, in addition to all chunks (da_chunks_count_usize) + let da_elements_start_index: usize = 1; + let da_elements_end_index: usize = da_chunks_count.wrapping_add(3); + let destination_address_byte_array: ByteArray = ByteArray::try_from( + starknet_event + .data + .get(da_elements_start_index..=da_elements_end_index) + .ok_or(ContractCallError::OutOfBound)? + .to_vec(), + )?; + let destination_address = destination_address_byte_array.try_to_string()?; + + // payload_hash is a keccak256, which is a combination of two felts (chunks) + // - first felt contains the 128 least significat bits (LSB) + // - second felt contains the 128 most significat bits (MSG) + let ph_chunk1_index: usize = da_elements_end_index.wrapping_add(1); + let ph_chunk2_index: usize = ph_chunk1_index.wrapping_add(1); + let mut payload_hash = [0; 32]; + let lsb: [u8; 32] = starknet_event + .data + .get(ph_chunk1_index) + .ok_or(ContractCallError::InvalidEvent( + "payload_hash chunk 1 out of range".to_owned(), + ))? + .to_bytes_be(); + let msb: [u8; 32] = starknet_event + .data + .get(ph_chunk2_index) + .ok_or(ContractCallError::InvalidEvent( + "payload_hash chunk 2 out of range".to_owned(), + ))? + .to_bytes_be(); + + // most significat bits, go before least significant bits for u256 construction + // check - https://docs.starknet.io/documentation/architecture_and_concepts/Smart_Contracts/serialization_of_Cairo_types/#serialization_in_u256_values + payload_hash[..16].copy_from_slice(&msb[16..]); + payload_hash[16..].copy_from_slice(&lsb[16..]); + + Ok(ContractCallEvent { + destination_address, + destination_chain, + source_address, + payload_hash: H256::from_slice(&payload_hash), + }) + } +} + +#[cfg(test)] +mod tests { + use std::str::FromStr; + + use ethers::types::H256; + use starknet_core::types::FieldElement; + use starknet_core::utils::starknet_keccak; + + use super::ContractCallEvent; + use crate::starknet::events::contract_call::ContractCallError; + + #[test] + fn destination_address_chunks_offset_out_of_range() { + let mut starknet_event = get_dummy_event(); + // longer chunk, which offsets the destination_address byte array out of range + starknet_event.data[1] = FieldElement::from_str( + "0x0000000000000000000000000000000000000000000000000000000000000001", + ) + .unwrap(); + + let event = ContractCallEvent::try_from(starknet_event).unwrap_err(); + assert!(matches!(event, ContractCallError::ByteArray(_))); + } + + #[test] + fn destination_address_chunks_count_too_long() { + let mut starknet_event = get_dummy_event(); + // too long for u32 + starknet_event.data[1] = FieldElement::MAX; + + let event = ContractCallEvent::try_from(starknet_event).unwrap_err(); + assert!(matches!(event, ContractCallError::FeltOutOfRange(_))); + } + + #[test] + fn invalid_dest_chain() { + let mut starknet_event = get_dummy_event(); + // too long for Cairo long string too long + starknet_event.keys[1] = FieldElement::MAX; + + let event = ContractCallEvent::try_from(starknet_event).unwrap_err(); + assert!(matches!(event, ContractCallError::Cairo(_))); + } + + #[test] + fn more_than_2_keys() { + // the payload is the word "hello" + let mut starknet_event = get_dummy_event(); + starknet_event + .keys + .push(starknet_keccak("additional_element".as_bytes())); + + let event = ContractCallEvent::try_from(starknet_event).unwrap_err(); + assert!(matches!(event, ContractCallError::InvalidEvent(_))); + } + + #[test] + fn wrong_event_type() { + // the payload is the word "hello" + let mut starknet_event = get_dummy_event(); + starknet_event.keys[0] = starknet_keccak("NOTContractCall".as_bytes()); + + let event = ContractCallEvent::try_from(starknet_event).unwrap_err(); + assert!(matches!(event, ContractCallError::InvalidEvent(_))); + } + + #[test] + fn valid_call_contract_event() { + // the payload is the word "hello" + let starknet_event = get_dummy_event(); + + let event = ContractCallEvent::try_from(starknet_event).unwrap(); + assert_eq!( + event, + ContractCallEvent { + destination_address: String::from("hello"), + destination_chain: String::from("destination_chain"), + source_address: String::from( + "0x00b3ff441a68610b30fd5e2abbf3a1548eb6ba6f3559f2862bf2dc757e5828ca" + ), + payload_hash: H256::from_slice(&[ + 28u8, 138, 255, 149, 6, 133, 194, 237, 75, 195, 23, 79, 52, 114, 40, 123, 86, + 217, 81, 123, 156, 148, 129, 39, 49, 154, 9, 167, 163, 109, 234, 200 + ]) + } + ); + } + + fn get_dummy_event() -> starknet_core::types::Event { + starknet_core::types::Event { + // I think it's a pedersen hash, but we don't use it, so any value should do + from_address: starknet_keccak("some_from_address".as_bytes()), + keys: vec![ + starknet_keccak("ContractCall".as_bytes()), + FieldElement::from_str( + "0x00000000000000000000000000000064657374696e6174696f6e5f636861696e", + ) + .unwrap(), + ], + data: vec![ + FieldElement::from_str( + "0xb3ff441a68610b30fd5e2abbf3a1548eb6ba6f3559f2862bf2dc757e5828ca", + ) + .unwrap(), + FieldElement::from_str( + "0x0000000000000000000000000000000000000000000000000000000000000000", + ) + .unwrap(), + FieldElement::from_str( + "0x00000000000000000000000000000000000000000000000000000068656c6c6f", + ) + .unwrap(), + FieldElement::from_str( + "0x0000000000000000000000000000000000000000000000000000000000000005", + ) + .unwrap(), + FieldElement::from_str( + "0x0000000000000000000000000000000056d9517b9c948127319a09a7a36deac8", + ) + .unwrap(), + FieldElement::from_str( + "0x000000000000000000000000000000001c8aff950685c2ed4bc3174f3472287b", + ) + .unwrap(), + FieldElement::from_str( + "0x0000000000000000000000000000000000000000000000000000000000000005", + ) + .unwrap(), + FieldElement::from_str( + "0x0000000000000000000000000000000000000000000000000000000000000068", + ) + .unwrap(), + FieldElement::from_str( + "0x0000000000000000000000000000000000000000000000000000000000000065", + ) + .unwrap(), + FieldElement::from_str( + "0x000000000000000000000000000000000000000000000000000000000000006c", + ) + .unwrap(), + FieldElement::from_str( + "0x000000000000000000000000000000000000000000000000000000000000006c", + ) + .unwrap(), + FieldElement::from_str( + "0x000000000000000000000000000000000000000000000000000000000000006f", + ) + .unwrap(), + ], + } + } +} diff --git a/ampd/src/starknet/events/mod.rs b/ampd/src/starknet/events/mod.rs new file mode 100644 index 000000000..5de126531 --- /dev/null +++ b/ampd/src/starknet/events/mod.rs @@ -0,0 +1,51 @@ +use std::sync::OnceLock; + +use starknet_core::types::FieldElement; +use starknet_core::utils::starknet_keccak; + +pub mod contract_call; + +// Since a keccak hash over a string is a deterministic operation, +// we can use `OnceLock` to eliminate useless hash calculations. +static CALL_CONTRACT_FELT: OnceLock = OnceLock::new(); + +/// All Axelar event types supported by starknet +#[derive(Debug)] +pub enum EventType { + ContractCall, +} + +impl EventType { + fn parse(event_type_felt: FieldElement) -> Option { + let contract_call_type = + CALL_CONTRACT_FELT.get_or_init(|| starknet_keccak("ContractCall".as_bytes())); + + if event_type_felt == *contract_call_type { + Some(EventType::ContractCall) + } else { + None + } + } +} + +#[cfg(test)] +mod event_type_tests { + use starknet_core::utils::starknet_keccak; + + use crate::starknet::events::EventType; + + #[test] + fn parse_contract_call() { + let contract_call_felt = starknet_keccak("ContractCall".as_bytes()); + assert!(matches!( + EventType::parse(contract_call_felt), + Some(EventType::ContractCall) + )); + } + + #[test] + fn parse_unknown_event() { + let contract_call_felt = starknet_keccak("UnknownEvent".as_bytes()); + assert!(EventType::parse(contract_call_felt).is_none()); + } +} diff --git a/ampd/src/starknet/mod.rs b/ampd/src/starknet/mod.rs index cd408564e..863a5e1c9 100644 --- a/ampd/src/starknet/mod.rs +++ b/ampd/src/starknet/mod.rs @@ -1 +1,2 @@ +pub mod events; pub mod types; diff --git a/ampd/src/starknet/types/array_span.rs b/ampd/src/starknet/types/array_span.rs index 2aaf27520..d761f0ecd 100644 --- a/ampd/src/starknet/types/array_span.rs +++ b/ampd/src/starknet/types/array_span.rs @@ -38,16 +38,16 @@ use thiserror::Error; /// .unwrap(), /// ]; /// -/// let array_span = ArraySpan::try_from(data).unwrap(); -/// assert_eq!(array_span.bytes, vec![104, 101, 108, 108, 111]); -/// assert_eq!(String::from_utf8(array_span.bytes).unwrap(), "hello"); +/// let array_span = ArraySpan::::try_from(data).unwrap(); +/// assert_eq!(array_span.data, vec![104, 101, 108, 108, 111]); +/// assert_eq!(String::from_utf8(array_span.data).unwrap(), "hello"); /// ``` /// /// For more info: /// https://docs.starknet.io/documentation/architecture_and_concepts/Smart_Contracts/serialization_of_Cairo_types/#serialization_of_byte_arrays #[derive(Debug)] -pub struct ArraySpan { - pub bytes: Vec, +pub struct ArraySpan { + pub data: Vec, } #[derive(Error, Debug)] @@ -58,7 +58,7 @@ pub enum ArraySpanError { ParsingFelt(#[from] ValueOutOfRangeError), } -impl TryFrom> for ArraySpan { +impl TryFrom> for ArraySpan { type Error = ArraySpanError; fn try_from(data: Vec) -> Result { @@ -79,7 +79,7 @@ impl TryFrom> for ArraySpan { .map(|e| e.try_into().map_err(ArraySpanError::ParsingFelt)) .collect(); - Ok(ArraySpan { bytes: bytes? }) + Ok(ArraySpan { data: bytes? }) } } @@ -87,7 +87,7 @@ impl TryFrom> for ArraySpan { mod array_span_tests { use std::str::FromStr; - use starknet_core::types::FieldElement; + use starknet_core::types::{FieldElement, FromStrError}; use crate::starknet::types::array_span::ArraySpan; @@ -99,105 +99,63 @@ mod array_span_tests { ) .unwrap()]; - let array_span = ArraySpan::try_from(data).unwrap(); - assert_eq!(array_span.bytes, Vec::::new()); + let array_span = ArraySpan::::try_from(data).unwrap(); + assert_eq!(array_span.data, Vec::::new()); } #[test] fn try_from_failed_to_parse_element_to_u8() { // the string "hello", but FieldElement is bigger than u8::max - let data = vec![ - FieldElement::from_str( - "0x0000000000000000000000000000000000000000000000000000000000000005", - ) - .unwrap(), - FieldElement::from_str( - "0x00FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF", - ) - .unwrap(), - FieldElement::from_str( - "0x0000000000000000000000000000000000000000000000000000000000000065", - ) - .unwrap(), - FieldElement::from_str( - "0x000000000000000000000000000000000000000000000000000000000000006c", - ) - .unwrap(), - FieldElement::from_str( - "0x000000000000000000000000000000000000000000000000000000000000006c", - ) - .unwrap(), - FieldElement::from_str( - "0x000000000000000000000000000000000000000000000000000000000000006f", - ) - .unwrap(), - ]; - - let array_span = ArraySpan::try_from(data); + let data: Result, FromStrError> = vec![ + "0x0000000000000000000000000000000000000000000000000000000000000005", + "0x00FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF", + "0x0000000000000000000000000000000000000000000000000000000000000065", + "0x000000000000000000000000000000000000000000000000000000000000006c", + "0x000000000000000000000000000000000000000000000000000000000000006c", + "0x000000000000000000000000000000000000000000000000000000000000006f", + ] + .into_iter() + .map(FieldElement::from_str) + .collect(); + + let array_span = ArraySpan::::try_from(data.unwrap()); assert!(array_span.is_err()); } #[test] fn try_from_failed_to_parse_elements_length_to_u32() { // the string "hello", but element counte bigger than u32::max - let data = vec![ - FieldElement::from_str( - "0x00FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF", - ) - .unwrap(), - FieldElement::from_str( - "0x0000000000000000000000000000000000000000000000000000000000000068", - ) - .unwrap(), - FieldElement::from_str( - "0x0000000000000000000000000000000000000000000000000000000000000065", - ) - .unwrap(), - FieldElement::from_str( - "0x000000000000000000000000000000000000000000000000000000000000006c", - ) - .unwrap(), - FieldElement::from_str( - "0x000000000000000000000000000000000000000000000000000000000000006c", - ) - .unwrap(), - FieldElement::from_str( - "0x000000000000000000000000000000000000000000000000000000000000006f", - ) - .unwrap(), - ]; - - let array_span = ArraySpan::try_from(data); + let data: Result, FromStrError> = vec![ + "0x00FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF", + "0x0000000000000000000000000000000000000000000000000000000000000068", + "0x0000000000000000000000000000000000000000000000000000000000000065", + "0x000000000000000000000000000000000000000000000000000000000000006c", + "0x000000000000000000000000000000000000000000000000000000000000006c", + "0x000000000000000000000000000000000000000000000000000000000000006f", + ] + .into_iter() + .map(FieldElement::from_str) + .collect(); + + let array_span = ArraySpan::::try_from(data.unwrap()); assert!(array_span.is_err()); } #[test] fn try_from_invalid_number_of_elements() { // the string "hello", but with only 4 bytes - let data = vec![ - FieldElement::from_str( - "0x0000000000000000000000000000000000000000000000000000000000000005", - ) - .unwrap(), - FieldElement::from_str( - "0x0000000000000000000000000000000000000000000000000000000000000068", - ) - .unwrap(), - FieldElement::from_str( - "0x0000000000000000000000000000000000000000000000000000000000000065", - ) - .unwrap(), - FieldElement::from_str( - "0x000000000000000000000000000000000000000000000000000000000000006c", - ) - .unwrap(), - FieldElement::from_str( - "0x000000000000000000000000000000000000000000000000000000000000006c", - ) - .unwrap(), - ]; - - let array_span = ArraySpan::try_from(data); + let data: Result, FromStrError> = vec![ + "0x0000000000000000000000000000000000000000000000000000000000000005", + "0x0000000000000000000000000000000000000000000000000000000000000068", + "0x0000000000000000000000000000000000000000000000000000000000000065", + "0x000000000000000000000000000000000000000000000000000000000000006c", + "0x000000000000000000000000000000000000000000000000000000000000006c", + ] + .into_iter() + .map(FieldElement::from_str) + .collect(); + + let array_span = ArraySpan::::try_from(data.unwrap()); assert!(array_span.is_err()); } @@ -205,68 +163,38 @@ mod array_span_tests { fn try_from_invalid_declared_length() { // the string "hello", with correct number of bytes, but only 4 declared, // instead of 5 - let data = vec![ - FieldElement::from_str( - "0x0000000000000000000000000000000000000000000000000000000000000004", - ) - .unwrap(), - FieldElement::from_str( - "0x0000000000000000000000000000000000000000000000000000000000000068", - ) - .unwrap(), - FieldElement::from_str( - "0x0000000000000000000000000000000000000000000000000000000000000065", - ) - .unwrap(), - FieldElement::from_str( - "0x000000000000000000000000000000000000000000000000000000000000006c", - ) - .unwrap(), - FieldElement::from_str( - "0x000000000000000000000000000000000000000000000000000000000000006c", - ) - .unwrap(), - FieldElement::from_str( - "0x000000000000000000000000000000000000000000000000000000000000006f", - ) - .unwrap(), - ]; - - let array_span = ArraySpan::try_from(data); + let data: Result, FromStrError> = vec![ + "0x0000000000000000000000000000000000000000000000000000000000000004", + "0x0000000000000000000000000000000000000000000000000000000000000068", + "0x0000000000000000000000000000000000000000000000000000000000000065", + "0x000000000000000000000000000000000000000000000000000000000000006c", + "0x000000000000000000000000000000000000000000000000000000000000006c", + "0x000000000000000000000000000000000000000000000000000000000000006f", + ] + .into_iter() + .map(FieldElement::from_str) + .collect(); + + let array_span = ArraySpan::::try_from(data.unwrap()); assert!(array_span.is_err()); } #[test] fn try_from_valid() { // the string "hello" - let data = vec![ - FieldElement::from_str( - "0x0000000000000000000000000000000000000000000000000000000000000005", - ) - .unwrap(), - FieldElement::from_str( - "0x0000000000000000000000000000000000000000000000000000000000000068", - ) - .unwrap(), - FieldElement::from_str( - "0x0000000000000000000000000000000000000000000000000000000000000065", - ) - .unwrap(), - FieldElement::from_str( - "0x000000000000000000000000000000000000000000000000000000000000006c", - ) - .unwrap(), - FieldElement::from_str( - "0x000000000000000000000000000000000000000000000000000000000000006c", - ) - .unwrap(), - FieldElement::from_str( - "0x000000000000000000000000000000000000000000000000000000000000006f", - ) - .unwrap(), - ]; - - let array_span = ArraySpan::try_from(data).unwrap(); - assert_eq!(array_span.bytes, vec![104, 101, 108, 108, 111]); + let data: Result, FromStrError> = vec![ + "0x0000000000000000000000000000000000000000000000000000000000000005", + "0x0000000000000000000000000000000000000000000000000000000000000068", + "0x0000000000000000000000000000000000000000000000000000000000000065", + "0x000000000000000000000000000000000000000000000000000000000000006c", + "0x000000000000000000000000000000000000000000000000000000000000006c", + "0x000000000000000000000000000000000000000000000000000000000000006f", + ] + .into_iter() + .map(FieldElement::from_str) + .collect(); + + let array_span = ArraySpan::::try_from(data.unwrap()).unwrap(); + assert_eq!(array_span.data, vec![104, 101, 108, 108, 111]); } } diff --git a/ampd/src/starknet/types/byte_array.rs b/ampd/src/starknet/types/byte_array.rs index aa3467ad0..7ea1cf25a 100644 --- a/ampd/src/starknet/types/byte_array.rs +++ b/ampd/src/starknet/types/byte_array.rs @@ -13,23 +13,18 @@ use thiserror::Error; /// use ampd::starknet::types::byte_array::ByteArray; /// use std::str::FromStr; /// use starknet_core::types::FieldElement; +/// use starknet_core::types::FromStrError; /// -/// let data = vec![ -/// FieldElement::from_str( +/// let data: Result, FromStrError> = vec![ /// "0x0000000000000000000000000000000000000000000000000000000000000000", -/// ) -/// .unwrap(), -/// FieldElement::from_str( /// "0x00000000000000000000000000000000000000000000000000000068656c6c6f", -/// ) -/// .unwrap(), -/// FieldElement::from_str( /// "0x0000000000000000000000000000000000000000000000000000000000000005", -/// ) -/// .unwrap(), -/// ]; +/// ] +/// .into_iter() +/// .map(FieldElement::from_str) +/// .collect(); /// -/// let byte_array = ByteArray::try_from(data); +/// let byte_array = ByteArray::try_from(data.unwrap()); /// assert!(byte_array.is_ok()); /// ``` /// @@ -144,23 +139,18 @@ impl ByteArray { /// use ampd::starknet::types::byte_array::ByteArray; /// use std::str::FromStr; /// use starknet_core::types::FieldElement; + /// use starknet_core::types::FromStrError; /// - /// let data = vec![ - /// FieldElement::from_str( + /// let data: Result, FromStrError> = vec![ /// "0x0000000000000000000000000000000000000000000000000000000000000000", - /// ) - /// .unwrap(), - /// FieldElement::from_str( /// "0x00000000000000000000000000000000000000000000000000000068656c6c6f", - /// ) - /// .unwrap(), - /// FieldElement::from_str( /// "0x0000000000000000000000000000000000000000000000000000000000000005", - /// ) - /// .unwrap(), - /// ]; + /// ] + /// .into_iter() + /// .map(FieldElement::from_str) + /// .collect(); /// - /// let byte_array = ByteArray::try_from(data).unwrap(); + /// let byte_array = ByteArray::try_from(data.unwrap()).unwrap(); /// assert_eq!("hello", byte_array.try_to_string().unwrap()); /// ``` /// @@ -184,7 +174,7 @@ impl ByteArray { mod byte_array_tests { use std::str::FromStr; - use starknet_core::types::FieldElement; + use starknet_core::types::{FieldElement, FromStrError}; use crate::starknet::types::byte_array::ByteArray; @@ -194,24 +184,18 @@ mod byte_array_tests { // https://docs.starknet.io/documentation/architecture_and_concepts/Smart_Contracts/serialization_of_Cairo_types/#serialization_of_byte_arrays // // So this is the string "hello" - let data = vec![ - FieldElement::from_str( - "0x0000000000000000000000000000000000000000000000000000000000000000", - ) - .unwrap(), - FieldElement::from_str( - "0x0000000000000000000000000000000000000000000000000000068656c6c6f", - ) - .unwrap(), - FieldElement::from_str( - // Should be of length 5 bytes, but we put 6 bytes, in order to fail - // the parsing - "0x0000000000000000000000000000000000000000000000000000000000000020", - ) - .unwrap(), - ]; - - let byte_array = ByteArray::try_from(data); + let data: Result, FromStrError> = vec![ + "0x0000000000000000000000000000000000000000000000000000000000000000", + "0x0000000000000000000000000000000000000000000000000000068656c6c6f", + // Should be of length 5 bytes, but we put 6 bytes, in order to fail + // the parsing + "0x0000000000000000000000000000000000000000000000000000000000000020", + ] + .into_iter() + .map(FieldElement::from_str) + .collect(); + + let byte_array = ByteArray::try_from(data.unwrap()); assert!(byte_array.is_err()); } @@ -221,25 +205,19 @@ mod byte_array_tests { // https://docs.starknet.io/documentation/architecture_and_concepts/Smart_Contracts/serialization_of_Cairo_types/#serialization_of_byte_arrays // // So this is the string "hello" - let data = vec![ - FieldElement::from_str( - "0x0000000000000000000000000000000000000000000000000000000000000000", - ) - .unwrap(), + let data: Result, FromStrError> = vec![ + "0x0000000000000000000000000000000000000000000000000000000000000000", // Note the 01 in the beginning. This is what causes the parse // function to error. - FieldElement::from_str( - "0x01000000000000000000000000000000000000000000000000000068656c6c6f", - ) - .unwrap(), - FieldElement::from_str( - // 32(0x20) bytes long pending_word - "0x0000000000000000000000000000000000000000000000000000000000000020", - ) - .unwrap(), - ]; - - let byte_array = ByteArray::try_from(data).unwrap(); + "0x01000000000000000000000000000000000000000000000000000068656c6c6f", + // 32(0x20) bytes long pending_word + "0x0000000000000000000000000000000000000000000000000000000000000020", + ] + .into_iter() + .map(FieldElement::from_str) + .collect(); + + let byte_array = ByteArray::try_from(data.unwrap()).unwrap(); assert!(byte_array.try_to_string().is_err()); } @@ -249,22 +227,16 @@ mod byte_array_tests { // https://docs.starknet.io/documentation/architecture_and_concepts/Smart_Contracts/serialization_of_Cairo_types/#serialization_of_byte_arrays // // So this is the string "hello" - let data = vec![ - FieldElement::from_str( - "0x0000000000000000000000000000000000000000000000000000000000000000", - ) - .unwrap(), - FieldElement::from_str( - "0x00000000000000000000000000000000000000000000000000000068656c6c6f", - ) - .unwrap(), - FieldElement::from_str( - "0x0000000000000000000000000000000000000000000000000000000000000005", - ) - .unwrap(), - ]; + let data: Result, FromStrError> = vec![ + "0x0000000000000000000000000000000000000000000000000000000000000000", + "0x00000000000000000000000000000000000000000000000000000068656c6c6f", + "0x0000000000000000000000000000000000000000000000000000000000000005", + ] + .into_iter() + .map(FieldElement::from_str) + .collect(); - let byte_array = ByteArray::try_from(data).unwrap(); + let byte_array = ByteArray::try_from(data.unwrap()).unwrap(); assert_eq!("hello", byte_array.try_to_string().unwrap()); } @@ -276,94 +248,65 @@ mod byte_array_tests { // So this is the string "Long long string, a lot more than 31 characters that // wouldn't even fit in two felts, so we'll have at least two felts and a // pending word." - let data = vec![ - FieldElement::from_str( - "0x0000000000000000000000000000000000000000000000000000000000000004", - ) - .unwrap(), - FieldElement::from_str( - "0x00004c6f6e67206c6f6e6720737472696e672c2061206c6f74206d6f72652074", - ) - .unwrap(), - FieldElement::from_str( - "0x000068616e2033312063686172616374657273207468617420776f756c646e27", - ) - .unwrap(), - FieldElement::from_str( - "0x000074206576656e2066697420696e2074776f2066656c74732c20736f207765", - ) - .unwrap(), - FieldElement::from_str( - "0x0000276c6c2068617665206174206c656173742074776f2066656c747320616e", - ) - .unwrap(), - FieldElement::from_str( - "0x0000000000000000000000000000006420612070656e64696e6720776f72642e", - ) - .unwrap(), - FieldElement::from_str( - "0x0000000000000000000000000000000000000000000000000000000000000011", - ) - .unwrap(), - ]; - - let byte_array = ByteArray::try_from(data).unwrap(); + let data: Result, FromStrError> = vec![ + "0x0000000000000000000000000000000000000000000000000000000000000004", + "0x00004c6f6e67206c6f6e6720737472696e672c2061206c6f74206d6f72652074", + "0x000068616e2033312063686172616374657273207468617420776f756c646e27", + "0x000074206576656e2066697420696e2074776f2066656c74732c20736f207765", + "0x0000276c6c2068617665206174206c656173742074776f2066656c747320616e", + "0x0000000000000000000000000000006420612070656e64696e6720776f72642e", + "0x0000000000000000000000000000000000000000000000000000000000000011", + ] + .into_iter() + .map(FieldElement::from_str) + .collect(); + + let byte_array = ByteArray::try_from(data.unwrap()).unwrap(); assert_eq!("Long long string, a lot more than 31 characters that wouldn't even fit in two felts, so we'll have at least two felts and a pending word.", byte_array.try_to_string().unwrap()); } #[test] fn try_from_vec_count_less_then_3() { - let data = vec![FieldElement::from_str( - "0x0000000000000000000000000000000000000000000000000000000000000005", - ) - .unwrap()]; + let data: Result, FromStrError> = + vec!["0x0000000000000000000000000000000000000000000000000000000000000005"] + .into_iter() + .map(FieldElement::from_str) + .collect(); - let byte_array_err = ByteArray::try_from(data); + let byte_array_err = ByteArray::try_from(data.unwrap()); assert!(byte_array_err.is_err()); } #[test] fn try_from_non_u32_word_count() { - let data = vec![ + let data: Result, FromStrError> = vec![ // should be 0, because the message is short // enough to fit in a single FieldElement - FieldElement::from_str( - "0x00000000000000000000000000000000000000000000000000000068656c6c6f", - ) - .unwrap(), - FieldElement::from_str( - "0x00000000000000000000000000000000000000000000000000000068656c6c6f", - ) - .unwrap(), - FieldElement::from_str( - "0x0000000000000000000000000000000000000000000000000000000000000005", - ) - .unwrap(), - ]; + "0x00000000000000000000000000000000000000000000000000000068656c6c6f", + "0x00000000000000000000000000000000000000000000000000000068656c6c6f", + "0x0000000000000000000000000000000000000000000000000000000000000005", + ] + .into_iter() + .map(FieldElement::from_str) + .collect(); - let byte_array_err = ByteArray::try_from(data); + let byte_array_err = ByteArray::try_from(data.unwrap()); assert!(byte_array_err.is_err()); } #[test] fn try_from_invalid_byte_array_element_count() { - let data = vec![ + let data: Result, FromStrError> = vec![ // should be 0, because the message is short // enough to fit in a single FieldElement - FieldElement::from_str( - "0x0000000000000000000000000000000000000000000000000000000000000005", - ) - .unwrap(), - FieldElement::from_str( - "0x00000000000000000000000000000000000000000000000000000068656c6c6f", - ) - .unwrap(), - FieldElement::from_str( - "0x0000000000000000000000000000000000000000000000000000000000000005", - ) - .unwrap(), - ]; + "0x0000000000000000000000000000000000000000000000000000000000000005", + "0x00000000000000000000000000000000000000000000000000000068656c6c6f", + "0x0000000000000000000000000000000000000000000000000000000000000005", + ] + .into_iter() + .map(FieldElement::from_str) + .collect(); - let byte_array_err = ByteArray::try_from(data); + let byte_array_err = ByteArray::try_from(data.unwrap()); assert!(byte_array_err.is_err()); } @@ -373,22 +316,16 @@ mod byte_array_tests { // https://docs.starknet.io/documentation/architecture_and_concepts/Smart_Contracts/serialization_of_Cairo_types/#serialization_of_byte_arrays // // So this is the string "hello" - let data = vec![ - FieldElement::from_str( - "0x0000000000000000000000000000000000000000000000000000000000000000", - ) - .unwrap(), - FieldElement::from_str( - "0x00000000000000000000000000000000000000000000000000000068656c6c6f", - ) - .unwrap(), - FieldElement::from_str( - "0x00000000000000000000000000000000000000000000000000000068656c6c6f", - ) - .unwrap(), - ]; - - let byte_array = ByteArray::try_from(data); + let data: Result, FromStrError> = vec![ + "0x0000000000000000000000000000000000000000000000000000000000000000", + "0x00000000000000000000000000000000000000000000000000000068656c6c6f", + "0x00000000000000000000000000000000000000000000000000000068656c6c6f", + ] + .into_iter() + .map(FieldElement::from_str) + .collect(); + + let byte_array = ByteArray::try_from(data.unwrap()); assert!(byte_array.is_err()); } @@ -398,22 +335,16 @@ mod byte_array_tests { // https://docs.starknet.io/documentation/architecture_and_concepts/Smart_Contracts/serialization_of_Cairo_types/#serialization_of_byte_arrays // // So this is the string "hello" - let data = vec![ - FieldElement::from_str( - "0x0000000000000000000000000000000000000000000000000000000000000000", - ) - .unwrap(), - FieldElement::from_str( - "0x00000000000000000000000000000000000000000000000000000068656c6c6f", - ) - .unwrap(), - FieldElement::from_str( - "0x0000000000000000000000000000000000000000000000000000000000000005", - ) - .unwrap(), - ]; + let data: Result, FromStrError> = vec![ + "0x0000000000000000000000000000000000000000000000000000000000000000", + "0x00000000000000000000000000000000000000000000000000000068656c6c6f", + "0x0000000000000000000000000000000000000000000000000000000000000005", + ] + .into_iter() + .map(FieldElement::from_str) + .collect(); - let byte_array = ByteArray::try_from(data).unwrap(); + let byte_array = ByteArray::try_from(data.unwrap()).unwrap(); assert_eq!(byte_array.data, vec![]); assert_eq!( @@ -434,38 +365,20 @@ mod byte_array_tests { // So this is the string "Long long string, a lot more than 31 characters that // wouldn't even fit in two felts, so we'll have at least two felts and a // pending word." - let data = vec![ - FieldElement::from_str( - "0x0000000000000000000000000000000000000000000000000000000000000004", - ) - .unwrap(), - FieldElement::from_str( - "0x00004c6f6e67206c6f6e6720737472696e672c2061206c6f74206d6f72652074", - ) - .unwrap(), - FieldElement::from_str( - "0x000068616e2033312063686172616374657273207468617420776f756c646e27", - ) - .unwrap(), - FieldElement::from_str( - "0x000074206576656e2066697420696e2074776f2066656c74732c20736f207765", - ) - .unwrap(), - FieldElement::from_str( - "0x0000276c6c2068617665206174206c656173742074776f2066656c747320616e", - ) - .unwrap(), - FieldElement::from_str( - "0x0000000000000000000000000000006420612070656e64696e6720776f72642e", - ) - .unwrap(), - FieldElement::from_str( - "0x0000000000000000000000000000000000000000000000000000000000000011", - ) - .unwrap(), - ]; - - let byte_array = ByteArray::try_from(data).unwrap(); + let data: Result, FromStrError> = vec![ + "0x0000000000000000000000000000000000000000000000000000000000000004", + "0x00004c6f6e67206c6f6e6720737472696e672c2061206c6f74206d6f72652074", + "0x000068616e2033312063686172616374657273207468617420776f756c646e27", + "0x000074206576656e2066697420696e2074776f2066656c74732c20736f207765", + "0x0000276c6c2068617665206174206c656173742074776f2066656c747320616e", + "0x0000000000000000000000000000006420612070656e64696e6720776f72642e", + "0x0000000000000000000000000000000000000000000000000000000000000011", + ] + .into_iter() + .map(FieldElement::from_str) + .collect(); + + let byte_array = ByteArray::try_from(data.unwrap()).unwrap(); assert_eq!( byte_array.data, @@ -504,26 +417,17 @@ mod byte_array_tests { // https://docs.starknet.io/documentation/architecture_and_concepts/Smart_Contracts/serialization_of_Cairo_types/#serialization_of_byte_arrays // // So this is the string "Long string, more than 31 characters." - let data = vec![ - FieldElement::from_str( - "0x0000000000000000000000000000000000000000000000000000000000000001", - ) - .unwrap(), - FieldElement::from_str( - "0x004c6f6e6720737472696e672c206d6f7265207468616e203331206368617261", - ) - .unwrap(), - FieldElement::from_str( - "0x000000000000000000000000000000000000000000000000000063746572732e", - ) - .unwrap(), - FieldElement::from_str( - "0x0000000000000000000000000000000000000000000000000000000000000006", - ) - .unwrap(), - ]; - - let byte_array = ByteArray::try_from(data).unwrap(); + let data: Result, FromStrError> = vec![ + "0x0000000000000000000000000000000000000000000000000000000000000001", + "0x004c6f6e6720737472696e672c206d6f7265207468616e203331206368617261", + "0x000000000000000000000000000000000000000000000000000063746572732e", + "0x0000000000000000000000000000000000000000000000000000000000000006", + ] + .into_iter() + .map(FieldElement::from_str) + .collect(); + + let byte_array = ByteArray::try_from(data.unwrap()).unwrap(); assert_eq!( byte_array.data,