Skip to content

Commit

Permalink
feat: add processing of messages from state update
Browse files Browse the repository at this point in the history
  • Loading branch information
glihm committed Feb 8, 2024
1 parent 108064b commit fd2a503
Show file tree
Hide file tree
Showing 7 changed files with 580 additions and 69 deletions.
52 changes: 51 additions & 1 deletion src/appchain.cairo
Original file line number Diff line number Diff line change
Expand Up @@ -4,17 +4,28 @@

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.
#[starknet::contract]
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<ContractState>;
Expand All @@ -25,6 +36,8 @@ mod appchain {
ownable: ownable_cpt::Storage,
#[substorage(v0)]
config: config_cpt::Storage,
#[substorage(v0)]
messaging: messaging_cpt::Storage,
}

#[event]
Expand All @@ -34,6 +47,8 @@ mod appchain {
OwnableEvent: ownable_cpt::Event,
#[flat]
ConfigEvent: config_cpt::Event,
#[flat]
MessagingEvent: messaging_cpt::Event,
}

/// Initializes the contract.
Expand All @@ -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<felt252>) {
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);
}
}
2 changes: 2 additions & 0 deletions src/lib.cairo
Original file line number Diff line number Diff line change
Expand Up @@ -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};
Expand Down
162 changes: 98 additions & 64 deletions src/messaging/component.cairo
Original file line number Diff line number Diff line change
Expand Up @@ -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';
Expand All @@ -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;

Expand Down Expand Up @@ -74,6 +77,8 @@ mod messaging_cpt {
MessageConsumed: MessageConsumed,
MessageCancellationStarted: MessageCancellationStarted,
MessageCanceled: MessageCanceled,
MessageToStarknetReceived: MessageToStarknetReceived,
MessageToAppchainSealed: MessageToAppchainSealed,
}

#[derive(Drop, starknet::Event)]
Expand Down Expand Up @@ -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<felt252>,
}

#[derive(Drop, starknet::Event)]
struct MessageToAppchainSealed {
#[key]
message_hash: MessageHash,
#[key]
from: ContractAddress,
#[key]
to: ContractAddress,
selector: felt252,
nonce: Nonce,
payload: Span<felt252>,
}

#[embeddable_as(MessagingImpl)]
impl Messaging<
TContractState, +HasComponent<TContractState>, +Drop<TContractState>
Expand All @@ -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(
Expand All @@ -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);
Expand All @@ -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
Expand All @@ -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
Expand Down Expand Up @@ -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.
///
/// <https://github.com/starkware-libs/cairo-lang/blob/caba294d82eeeccc3d86a158adb8ba209bf2d8fc/src/starkware/starknet/solidity/StarknetMessaging.sol#L88>
/// 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<TContractState>,
nonce: Nonce,
to_address: ContractAddress,
selector: felt252,
payload: Span<felt252>
) -> MessageHash {
let mut hash_data = array![nonce, to_address.into(), selector,];
/// * `messages` - The messages to Starknet.
fn process_messages_to_starknet(
ref self: ComponentState<TContractState>, messages: Span<MessageToStarknet>,
) {
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.
///
/// <https://github.com/starkware-libs/cairo-lang/blob/caba294d82eeeccc3d86a158adb8ba209bf2d8fc/src/starkware/starknet/solidity/StarknetMessaging.sol#L137>
/// 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<TContractState>,
from_address: ContractAddress,
to_address: ContractAddress,
payload: Span<felt252>
) -> MessageHash {
let mut hash_data: Array<felt252> = array![
from_address.into(), to_address.into(), payload.len().into(),
];
/// * `messages` - The messages to Appchain.
fn process_messages_to_appchain(
ref self: ComponentState<TContractState>, messages: Span<MessageToAppchain>,
) {
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())
}
}
}
Loading

0 comments on commit fd2a503

Please sign in to comment.