From fd2a503dfe7847a2088e5fb4cd8d77d45166207f Mon Sep 17 00:00:00 2001 From: glihm Date: Wed, 7 Feb 2024 23:37:43 -0600 Subject: [PATCH] feat: add processing of messages from state update --- src/appchain.cairo | 52 ++++++- src/lib.cairo | 2 + src/messaging/component.cairo | 162 +++++++++++-------- src/messaging/hash.cairo | 68 ++++++++ src/messaging/output_process.cairo | 125 +++++++++++++++ src/snos_output.cairo | 1 + tests/test_messaging.cairo | 239 ++++++++++++++++++++++++++++- 7 files changed, 580 insertions(+), 69 deletions(-) create mode 100644 src/messaging/hash.cairo create mode 100644 src/messaging/output_process.cairo create mode 100644 src/snos_output.cairo diff --git a/src/appchain.cairo b/src/appchain.cairo index 245fca0..457790b 100644 --- a/src/appchain.cairo +++ b/src/appchain.cairo @@ -4,6 +4,8 @@ mod errors { const INVALID_ADDRESS: felt252 = 'Config: invalid address'; + const SNOS_INVALID_PROGRAM_OUTPUT_SIZE: felt252 = 'snos: invalid output size'; + const SNOS_INVALID_MESSAGES_SEGMENTS: felt252 = 'snos: invalid messages segments'; } /// Appchain settlement contract on starknet. @@ -11,10 +13,19 @@ mod errors { mod appchain { use openzeppelin::access::ownable::{OwnableComponent as ownable_cpt, interface::IOwnable}; use piltover::config::{config_cpt, config_cpt::InternalTrait as ConfigInternal, IConfig}; + use piltover::messaging::{ + messaging_cpt, messaging_cpt::InternalTrait as MessagingInternal, IMessaging, + output_process, + output_process::{MessageToStarknet, MessageToAppchain}, + }; use starknet::ContractAddress; + use super::errors; + + const SNOS_OUTPUT_HEADER_SIZE: usize = 5; component!(path: ownable_cpt, storage: ownable, event: OwnableEvent); component!(path: config_cpt, storage: config, event: ConfigEvent); + component!(path: messaging_cpt, storage: messaging, event: MessagingEvent); #[abi(embed_v0)] impl ConfigImpl = config_cpt::ConfigImpl; @@ -25,6 +36,8 @@ mod appchain { ownable: ownable_cpt::Storage, #[substorage(v0)] config: config_cpt::Storage, + #[substorage(v0)] + messaging: messaging_cpt::Storage, } #[event] @@ -34,6 +47,8 @@ mod appchain { OwnableEvent: ownable_cpt::Event, #[flat] ConfigEvent: config_cpt::Event, + #[flat] + MessagingEvent: messaging_cpt::Event, } /// Initializes the contract. @@ -45,6 +60,41 @@ mod appchain { fn constructor(ref self: ContractState, owner: ContractAddress) { self.ownable.transfer_ownership(owner); - assert(self.config.is_owner_or_operator(owner), 'bad'); + let cancellation_delay_secs = 432000; + self.messaging.initialize(cancellation_delay_secs); + } + + /// Updates the states of the Appchain on Starknet, + /// based on a proof of the StarknetOS that the state transition + /// is valid. + /// + /// # Arguments + /// + /// * `program_output` - The StarknetOS state update output. + /// TODO: DA + facts. + fn update_state(ref self: ContractState, program_output: Span) { + self.config.is_owner_or_operator(starknet::get_caller_address()); + + // TODO: reentrancy guard. + // TODO: facts verification. + // TODO: update the current state (component needed). + + // Header size + 2 messages segments len. + assert( + program_output.len() > SNOS_OUTPUT_HEADER_SIZE + 2, + errors::SNOS_INVALID_PROGRAM_OUTPUT_SIZE + ); + + let mut offset = SNOS_OUTPUT_HEADER_SIZE; + + // TODO: We should update SNOS output to have the messages count + // instead of the messages segment len. + + let mut messages_segments = program_output.slice(offset, program_output.len() - offset); + + let (messages_to_starknet, messages_to_appchain) = output_process::gather_messages_from_output(messages_segments); + + self.messaging.process_messages_to_starknet(messages_to_starknet); + self.messaging.process_messages_to_appchain(messages_to_appchain); } } diff --git a/src/lib.cairo b/src/lib.cairo index 02a01f3..3b21bcd 100644 --- a/src/lib.cairo +++ b/src/lib.cairo @@ -14,8 +14,10 @@ mod config { mod messaging { mod component; + mod hash; mod interface; mod mock; + mod output_process; use component::messaging_cpt; use interface::{IMessaging, IMessagingDispatcher, IMessagingDispatcherTrait}; diff --git a/src/messaging/component.cairo b/src/messaging/component.cairo index 6908582..4295675 100644 --- a/src/messaging/component.cairo +++ b/src/messaging/component.cairo @@ -28,6 +28,7 @@ mod errors { const INVALID_NONCE: felt252 = 'INVALID_NONCE'; const INVALID_MESSAGE_TO_CONSUME: felt252 = 'INVALID_MESSAGE_TO_CONSUME'; + const INVALID_MESSAGE_TO_SEAL: felt252 = 'INVALID_MESSAGE_TO_SEAL'; const NO_MESSAGE_TO_CANCEL: felt252 = 'NO_MESSAGE_TO_CANCEL'; const CANCELLATION_NOT_REQUESTED: felt252 = 'CANCELLATION_NOT_REQUESTED'; const CANCELLATION_NOT_ALLOWED_YET: felt252 = 'CANCELLATION_NOT_ALLOWED_YET'; @@ -45,7 +46,9 @@ mod messaging_cpt { OwnableComponent as ownable_cpt, OwnableComponent::InternalTrait as OwnableInternal, interface::IOwnable, }; - use piltover::messaging::interface::IMessaging; + use piltover::messaging::{ + hash, interface::IMessaging, output_process::{MessageToStarknet, MessageToAppchain}, + }; use starknet::ContractAddress; use super::errors; @@ -74,6 +77,8 @@ mod messaging_cpt { MessageConsumed: MessageConsumed, MessageCancellationStarted: MessageCancellationStarted, MessageCanceled: MessageCanceled, + MessageToStarknetReceived: MessageToStarknetReceived, + MessageToAppchainSealed: MessageToAppchainSealed, } #[derive(Drop, starknet::Event)] @@ -126,6 +131,30 @@ mod messaging_cpt { nonce: Nonce, } + #[derive(Drop, starknet::Event)] + struct MessageToStarknetReceived { + #[key] + message_hash: MessageHash, + #[key] + from: ContractAddress, + #[key] + to: ContractAddress, + payload: Span, + } + + #[derive(Drop, starknet::Event)] + struct MessageToAppchainSealed { + #[key] + message_hash: MessageHash, + #[key] + from: ContractAddress, + #[key] + to: ContractAddress, + selector: felt252, + nonce: Nonce, + payload: Span, + } + #[embeddable_as(MessagingImpl)] impl Messaging< TContractState, +HasComponent, +Drop @@ -140,8 +169,9 @@ mod messaging_cpt { let nonce = self.sn_to_appc_nonce.read() + 1; self.sn_to_appc_nonce.write(nonce); - let message_hash = self - .compute_message_hash_sn_to_appc(nonce, to_address, selector, payload); + let message_hash = hash::compute_message_hash_sn_to_appc( + nonce, to_address, selector, payload + ); self .emit( @@ -166,8 +196,9 @@ mod messaging_cpt { ) -> MessageHash { let to_address = starknet::get_caller_address(); - let message_hash = self - .compute_message_hash_appc_to_sn(from_address, to_address, payload); + let message_hash = hash::compute_message_hash_appc_to_sn( + from_address, to_address, payload + ); let count = self.appc_to_sn_messages.read(message_hash); assert(count.is_non_zero(), errors::INVALID_MESSAGE_TO_CONSUME); @@ -192,8 +223,9 @@ mod messaging_cpt { assert(nonce.is_non_zero(), errors::INVALID_NONCE); let from = starknet::get_caller_address(); - let message_hash = self - .compute_message_hash_sn_to_appc(nonce, to_address, selector, payload); + let message_hash = hash::compute_message_hash_sn_to_appc( + nonce, to_address, selector, payload + ); assert( self.sn_to_appc_messages.read(message_hash) == nonce, errors::NO_MESSAGE_TO_CANCEL @@ -220,8 +252,9 @@ mod messaging_cpt { ) -> MessageHash { let from = starknet::get_caller_address(); - let message_hash = self - .compute_message_hash_sn_to_appc(nonce, to_address, selector, payload); + let message_hash = hash::compute_message_hash_sn_to_appc( + nonce, to_address, selector, payload + ); assert( self.sn_to_appc_messages.read(message_hash) == nonce, errors::NO_MESSAGE_TO_CANCEL @@ -262,75 +295,76 @@ mod messaging_cpt { self.cancellation_delay_secs.write(cancellation_delay_secs); } - /// Computes the hash of a message that is sent from Starknet to the Appchain. - /// - /// + /// Processes the messages to Starknet from StarknetOS output. + /// Once processed, messages are ready to be consumed using + /// `consume_message_from_appchain` entry point. /// /// # Arguments /// - /// * `nonce` - Nonce of the message. - /// * `to_address` - Contract address to send the message to on the Appchain. - /// * `selector` - The `l1_handler` function selector of the contract on the Appchain - /// to execute. - /// * `payload` - The message payload. - /// - /// # Returns - /// - /// The hash of the message from Starknet to the Appchain. - fn compute_message_hash_sn_to_appc( - ref self: ComponentState, - nonce: Nonce, - to_address: ContractAddress, - selector: felt252, - payload: Span - ) -> MessageHash { - let mut hash_data = array![nonce, to_address.into(), selector,]; + /// * `messages` - The messages to Starknet. + fn process_messages_to_starknet( + ref self: ComponentState, messages: Span, + ) { + let mut messages = messages; - let mut i = 0_usize; loop { - if i == payload.len() { - break; - } - hash_data.append((*payload[i])); - i += 1; - }; + match messages.pop_front() { + Option::Some(m) => { + let from = *m.from_address; + let to = *m.to_address; + let payload = *m.payload; - core::poseidon::poseidon_hash_span(hash_data.span()) + let message_hash = hash::compute_message_hash_appc_to_sn(from, to, payload); + + self.emit(MessageToStarknetReceived { message_hash, from, to, payload }); + + let ref_count = self.appc_to_sn_messages.read(message_hash); + self.appc_to_sn_messages.write(message_hash, ref_count + 1); + }, + Option::None => { break; }, + }; + }; } - /// Computes the hash of a message that is sent from the Appchain to Starknet. - /// - /// + /// Processes the messages to Appchain from StarknetOS output. /// /// # Arguments /// - /// * `from_address` - Contract address of the message sender on the Appchain. - /// * `to_address` - Contract address to send the message to on the Appchain. - /// * `payload` - The message payload. - /// - /// # Returns - /// - /// The hash of the message from the Appchain to Starknet. - fn compute_message_hash_appc_to_sn( - ref self: ComponentState, - from_address: ContractAddress, - to_address: ContractAddress, - payload: Span - ) -> MessageHash { - let mut hash_data: Array = array![ - from_address.into(), to_address.into(), payload.len().into(), - ]; + /// * `messages` - The messages to Appchain. + fn process_messages_to_appchain( + ref self: ComponentState, messages: Span, + ) { + let mut messages = messages; - let mut i = 0_usize; loop { - if i == payload.len() { - break; - } - hash_data.append((*payload[i])); - i += 1; + match messages.pop_front() { + Option::Some(m) => { + let from = *m.from_address; + let to = *m.to_address; + let payload = *m.payload; + let selector = *m.selector; + let nonce = *m.nonce; + + let message_hash = hash::compute_message_hash_sn_to_appc( + nonce, to, selector, payload + ); + assert( + self.sn_to_appc_messages.read(message_hash).is_non_zero(), + errors::INVALID_MESSAGE_TO_SEAL + ); + + self.sn_to_appc_messages.write(message_hash, 0); + + self + .emit( + MessageToAppchainSealed { + message_hash, from, to, selector, payload, nonce + } + ); + }, + Option::None => { break; }, + }; }; - - core::poseidon::poseidon_hash_span(hash_data.span()) } } } diff --git a/src/messaging/hash.cairo b/src/messaging/hash.cairo new file mode 100644 index 0000000..67cd408 --- /dev/null +++ b/src/messaging/hash.cairo @@ -0,0 +1,68 @@ +//! SPDX-License-Identifier: MIT +//! +//! Hash utilities. +use starknet::ContractAddress; + +/// Computes the hash of a message that is sent from Starknet to the Appchain. +/// +/// +/// +/// # Arguments +/// +/// * `nonce` - Nonce of the message. +/// * `to_address` - Contract address to send the message to on the Appchain. +/// * `selector` - The `l1_handler` function selector of the contract on the Appchain +/// to execute. +/// * `payload` - The message payload. +/// +/// # Returns +/// +/// The hash of the message from Starknet to the Appchain. +fn compute_message_hash_sn_to_appc( + nonce: felt252, to_address: ContractAddress, selector: felt252, payload: Span +) -> felt252 { + let mut hash_data = array![nonce, to_address.into(), selector,]; + + let mut i = 0_usize; + loop { + if i == payload.len() { + break; + } + hash_data.append((*payload[i])); + i += 1; + }; + + core::poseidon::poseidon_hash_span(hash_data.span()) +} + +/// Computes the hash of a message that is sent from the Appchain to Starknet. +/// +/// +/// +/// # Arguments +/// +/// * `from_address` - Contract address of the message sender on the Appchain. +/// * `to_address` - Contract address to send the message to on the Appchain. +/// * `payload` - The message payload. +/// +/// # Returns +/// +/// The hash of the message from the Appchain to Starknet. +fn compute_message_hash_appc_to_sn( + from_address: ContractAddress, to_address: ContractAddress, payload: Span +) -> felt252 { + let mut hash_data: Array = array![ + from_address.into(), to_address.into(), payload.len().into(), + ]; + + let mut i = 0_usize; + loop { + if i == payload.len() { + break; + } + hash_data.append((*payload[i])); + i += 1; + }; + + core::poseidon::poseidon_hash_span(hash_data.span()) +} diff --git a/src/messaging/output_process.cairo b/src/messaging/output_process.cairo new file mode 100644 index 0000000..938233a --- /dev/null +++ b/src/messaging/output_process.cairo @@ -0,0 +1,125 @@ +//! SPDX-License-Identifier: MIT +//! +//! Helpers functions to process messages from +//! state update buffer. +//! +//! The output of StarknetOS can be found here: +//! +//! +//! Solidity code related to message processing: +//! . +//! +use starknet::ContractAddress; + +/// Message to Starknet. +#[derive(Drop, Serde)] +struct MessageToStarknet { + /// Appchain contract address sending the message. + from_address: ContractAddress, + /// Starknet contract address receiving the message. + to_address: ContractAddress, + /// Payload of the message. + payload: Span, +} + +/// Message to Appchain. +#[derive(Drop, Serde)] +struct MessageToAppchain { + /// Starknet address sending the message. + from_address: ContractAddress, + /// Appchain address receiving the message. + to_address: ContractAddress, + /// Nonce. + nonce: felt252, + /// Function selector (with #[l1 handler] attribute). + selector: felt252, + /// Payload size. + payload: Span, +} + +const MESSAGE_TO_STARKNET_HEADER_SIZE: usize = 3; +const MESSAGE_TO_APPCHAIN_HEADER_SIZE: usize = 5; + +/// Function to gather the messages from SNOS output. +/// TODO: this must be removed if SNOS output is changed to use messages +/// count instead of segment length. +/// +/// # Argument +/// +/// * `output_messages` - The messages segments of the SNOS output. +/// +/// # Returns +/// +/// A tuple with the messages to Starknet and messages to Appchain +/// deserialized. +fn gather_messages_from_output(output_messages: Span) -> (Span, Span) { + let mut messages_to_starknet = array![]; + let mut messages_to_appchain = array![]; + + // Messages to Starknet. + let segment_len: usize = (*output_messages[0]).try_into().expect('invalid seg size sn'); + + let mut offset = 1; + let segment_end = offset + segment_len; + + if segment_end > output_messages.len() { + core::panic_with_felt252('invalid message segment sn'); + } + + loop { + if offset >= segment_end { + break; + } + + if offset + MESSAGE_TO_STARKNET_HEADER_SIZE > segment_end { + core::panic_with_felt252('invalid message sn'); + } + + // +2 due to fields arrangement. + let payload_size: usize = (*output_messages[offset + 2]).try_into().expect('invalid size sn'); + + // +1 for payload size and +2 for remaining fields. + let mut slice = output_messages.slice(offset, payload_size + 1 + 2); + + let m: MessageToStarknet = Serde::deserialize(ref slice).expect('bad format message sn'); + + messages_to_starknet.append(m); + + offset += payload_size + 1 + 2; + }; + + // Messages to Appchain. + let segment_len: usize = (*output_messages[offset]).try_into().expect('invalid seg size appc'); + + // Move to the first message. + offset += 1; + let segment_end = offset + segment_len; + + if segment_end > output_messages.len() { + core::panic_with_felt252('invalid message segment appc'); + } + + loop { + if offset >= segment_end { + break; + } + + if offset + MESSAGE_TO_APPCHAIN_HEADER_SIZE > segment_end { + core::panic_with_felt252('invalid message appc'); + } + + // +4 due to fields arrangement. + let payload_size: usize = (*output_messages[offset + 4]).try_into().expect('invalid size appc'); + + // +1 for payload size and +4 for remaining fields. + let mut slice = output_messages.slice(offset, payload_size + 1 + 4); + + let m: MessageToAppchain = Serde::deserialize(ref slice).expect('bad format message appc'); + + messages_to_appchain.append(m); + + offset += payload_size + 1 + 4; + }; + + (messages_to_starknet.span(), messages_to_appchain.span()) +} diff --git a/src/snos_output.cairo b/src/snos_output.cairo new file mode 100644 index 0000000..8b13789 --- /dev/null +++ b/src/snos_output.cairo @@ -0,0 +1 @@ + diff --git a/tests/test_messaging.cairo b/tests/test_messaging.cairo index e95749c..2d3492d 100644 --- a/tests/test_messaging.cairo +++ b/tests/test_messaging.cairo @@ -1,13 +1,17 @@ use piltover::messaging::{ messaging_cpt, messaging_cpt::InternalTrait as MessagingInternal, IMessaging, IMessagingDispatcherTrait, IMessagingDispatcher, messaging_mock, - messaging_cpt::{Event, MessageSent, MessageCancellationStarted, MessageCanceled}, + messaging_cpt::{ + Event, MessageSent, MessageCancellationStarted, MessageCanceled, MessageToStarknetReceived, + MessageToAppchainSealed, + }, + output_process::{MessageToStarknet, MessageToAppchain}, hash, output_process, }; use snforge_std as snf; use snforge_std::{ - CheatTarget, ContractClassTrait, SpyOn, EventSpy, cheatcodes::events::EventAssertions + CheatTarget, ContractClassTrait, SpyOn, EventSpy, cheatcodes::events::EventAssertions, }; -use starknet::ContractAddress; +use starknet::{ContractAddress, storage::StorageMemberAccessTrait}; use super::constants as c; /// Deploys the mock with a specific cancellation delay. @@ -27,7 +31,130 @@ fn deploy_mock() -> (IMessagingDispatcher, EventSpy) { deploy_mock_with_delay(432000) } -// TODO: test consume message once the state update can increment the refcount of a message. +/// Returns the state of a component for testing. This must be used +/// to test internal functions or directly access the storage. +/// You can't spy event with this. Use deploy instead. +fn mock_state_testing() -> messaging_cpt::ComponentState { + messaging_cpt::component_state_for_testing() +} + +/// Messages to starknet taken from mainnet: +/// . +fn get_message_to_starknet() -> MessageToStarknet { + let mut felts = array![ + 3256441166037631918262930812410838598500200462657642943867372734773841898370, + 993696174272377493693496825928908586134624850969, + 4, + 0, + 917360325178274450223200079540424150242461675748, + 300000000000000, + 0, + ] + .span(); + + Serde::deserialize(ref felts).unwrap() +} + +/// Messages to appchain taken from mainnet: +/// . +fn get_message_to_appchain() -> MessageToAppchain { + let mut felts = array![ + 993696174272377493693496825928908586134624850969, + 3256441166037631918262930812410838598500200462657642943867372734773841898370, + 1629170, + 1285101517810983806491589552491143496277809242732141897358598292095611420389, + 3, + 1905350129216923298156817020930524704572804705313566176282348575247442538663, + 100000000000000000, + 0, + ] + .span(); + + Serde::deserialize(ref felts).unwrap() +} + +/// Messages segments of SNOS output from mainnet: +/// . +fn get_messages_segments() -> Span { + array![ + // Header + // 2308509181970242579758367820250590423941246005755407149765148974993919671160, + // 1400208033537979038273563301858781654076731580449174584651309975875760580865, + // 535683, + // 2885081770536693045243577840233106668867645710434679941076039698247255604327, + // 2590421891839256512113614983194993186457498815986333310670788206383913888162, + + // message to l1 (starknet in this context). + 7, + 3256441166037631918262930812410838598500200462657642943867372734773841898370, + 993696174272377493693496825928908586134624850969, + 4, + 0, + 917360325178274450223200079540424150242461675748, + 300000000000000, + 0, + + // message to l2 (appchain in this context). + 8, + 993696174272377493693496825928908586134624850969, + 3256441166037631918262930812410838598500200462657642943867372734773841898370, + 1629170, + 1285101517810983806491589552491143496277809242732141897358598292095611420389, + 3, + 1905350129216923298156817020930524704572804705313566176282348575247442538663, + 100000000000000000, + 0, + + ] + .span() +} + +#[test] +fn message_to_starknet_deser() { + let m = get_message_to_starknet(); + + assert( + m + .from_address + .into() == 3256441166037631918262930812410838598500200462657642943867372734773841898370, + 'invalid from' + ); + assert(m.to_address.into() == 993696174272377493693496825928908586134624850969, 'invalid to'); + assert(m.payload.len() == 4, 'invalid payoad len'); + assert((*m.payload[0]) == 0, 'invalid payoad 0'); + assert((*m.payload[1]) == 917360325178274450223200079540424150242461675748, 'invalid payoad 1'); + assert((*m.payload[2]) == 300000000000000, 'invalid payoad 2'); + assert((*m.payload[3]) == 0, 'invalid payoad 3'); +} + +#[test] +fn message_to_appchain_deser() { + let m = get_message_to_appchain(); + + assert( + m.from_address.into() == 993696174272377493693496825928908586134624850969, 'invalid from' + ); + assert( + m + .to_address + .into() == 3256441166037631918262930812410838598500200462657642943867372734773841898370, + 'invalid to' + ); + assert(m.nonce == 1629170, 'invalid nonce'); + assert( + m.selector == 1285101517810983806491589552491143496277809242732141897358598292095611420389, + 'invalid selector' + ); + + assert(m.payload.len() == 3, 'invalid payoad len'); + assert( + (*m + .payload[0]) == 1905350129216923298156817020930524704572804705313566176282348575247442538663, + 'invalid payoad 0' + ); + assert((*m.payload[1]) == 100000000000000000, 'invalid payoad 1'); + assert((*m.payload[2]) == 0, 'invalid payoad 2'); +} #[test] fn send_message_ok() { @@ -208,3 +335,107 @@ fn cancel_message_cancellation_not_allowed_yet() { snf::start_warp(CheatTarget::One(mock.contract_address), 5); mock.cancel_message(to, selector, payload.span(), nonce); } + +#[test] +fn gather_messages_from_output_ok() { + let felts = get_messages_segments(); + let (messages_to_starknet, messages_to_appchain) = output_process::gather_messages_from_output(felts); + + assert(messages_to_starknet.len() == 1, 'missing msgs to sn'); + assert(messages_to_appchain.len() == 1, 'missing msgs to appc'); +} + +#[test] +fn process_messages_to_starknet_ok() { + let mut mock = mock_state_testing(); + + let m = get_message_to_starknet(); + let message_hash = hash::compute_message_hash_appc_to_sn( + m.from_address, m.to_address, m.payload + ); + + let messages = array![m].span(); + + mock.process_messages_to_starknet(messages); +} + +#[test] +fn process_messages_to_appchain_ok() { + let mut mock = mock_state_testing(); + + let from = c::contract_a(); + let to = c::contract_b(); + let selector = selector!("func1"); + let payload = array![1, 2, 3]; + + snf::start_prank(CheatTarget::One(starknet::get_contract_address()), c::contract_a()); + let (message_hash, nonce) = mock.send_message_to_appchain(to, selector, payload.span()); + + let m = MessageToAppchain { + from_address: from, to_address: to, nonce: 1, selector, payload: payload.span(), + }; + + let message_hash = hash::compute_message_hash_sn_to_appc( + m.nonce, m.to_address, m.selector, m.payload + ); + + let messages = array![m].span(); + + mock.process_messages_to_appchain(messages); +} + +#[test] +#[should_panic(expected: ('INVALID_MESSAGE_TO_SEAL',))] +fn process_messages_to_appchain_no_seal() { + let mut mock = mock_state_testing(); + + let m = get_message_to_appchain(); + // 'm' was never sent, nothing to seal. + + let message_hash = hash::compute_message_hash_sn_to_appc( + m.nonce, m.to_address, m.selector, m.payload + ); + + let messages = array![m].span(); + + mock.process_messages_to_appchain(messages); +} + +#[test] +fn consume_message_from_appchain_ok() { + let mut mock = mock_state_testing(); + + let from = c::contract_a(); + let to = starknet::get_contract_address(); + let payload = array![1, 2, 3].span(); + + let message_hash = hash::compute_message_hash_appc_to_sn(from, to, payload); + + let messages = array![MessageToStarknet { + from_address: from, + to_address: to, + payload, + }].span(); + + mock.process_messages_to_starknet(messages); + + // Ensure the caller address inside the mock function is correctly set. + snf::start_prank(CheatTarget::One(to), to); + mock.consume_message_from_appchain(from, payload); +} + +#[test] +#[should_panic(expected: ('INVALID_MESSAGE_TO_CONSUME',))] +fn consume_message_from_appchain_invalid_to_consume() { + let mut mock = mock_state_testing(); + + let from = c::contract_a(); + let to = starknet::get_contract_address(); + let payload = array![1, 2, 3].span(); + + // Don't process the messages to starknet. + + // Ensure the caller address inside the mock function is correctly set. + snf::start_prank(CheatTarget::One(to), to); + mock.consume_message_from_appchain(from, payload); +}