Skip to content

feat: ensure snos program hash is checked #46

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 5 commits into from
Feb 4, 2025
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4,177 changes: 1 addition & 4,176 deletions bindings/src/lib.rs

Large diffs are not rendered by default.

7 changes: 7 additions & 0 deletions src/appchain.cairo
Original file line number Diff line number Diff line change
Expand Up @@ -139,9 +139,16 @@ mod appchain {
self.reentrancy_guard.start();
self.config.assert_only_owner_or_operator();

//Snos proof is wrapped in bootloader so 3rd element is program hash of
//bootloaded program in our case StarknetOs
let snos_program_hash = snos_output.at(2);
let stored_snos_program_hash = self.config.snos_program_hash.read();
assert!(stored_snos_program_hash == *snos_program_hash);

let snos_output_hash = poseidon_hash_span(snos_output);
let snos_output_hash_in_bridge_output = program_output.at(4);
assert!(snos_output_hash == *snos_output_hash_in_bridge_output);

let output_hash = poseidon_hash_span(program_output);

let mut snos_output_iter = snos_output.into_iter();
Expand Down
30 changes: 30 additions & 0 deletions src/config/component.cairo
Original file line number Diff line number Diff line change
Expand Up @@ -32,12 +32,15 @@ mod config_cpt {
program_info: (felt252, felt252),
/// Facts registry contract address.
facts_registry: ContractAddress,
/// Snos program hash.
snos_program_hash: felt252
}

#[event]
#[derive(Copy, Drop, starknet::Event)]
enum Event {
ProgramInfoChanged: ProgramInfoChanged,
SnosProgramHashChanged: SnosProgramHashChanged
}

#[derive(Copy, Drop, starknet::Event)]
Expand All @@ -49,6 +52,13 @@ mod config_cpt {
new_config_hash: felt252,
}

#[derive(Copy, Drop, starknet::Event)]
struct SnosProgramHashChanged {
changed_by: ContractAddress,
old_snos_program_hash: felt252,
new_snos_program_hash: felt252,
}

#[embeddable_as(ConfigImpl)]
impl Config<
TContractState,
Expand Down Expand Up @@ -101,6 +111,26 @@ mod config_cpt {
fn get_facts_registry(self: @ComponentState<TContractState>) -> ContractAddress {
self.facts_registry.read()
}

fn set_snos_program_hash(
ref self: ComponentState<TContractState>, snos_program_hash: felt252
) {
self.assert_only_owner_or_operator();
let old_snos_program_hash = self.snos_program_hash.read();
self.snos_program_hash.write(snos_program_hash);
self
.emit(
SnosProgramHashChanged {
changed_by: starknet::get_caller_address(),
old_snos_program_hash,
new_snos_program_hash: snos_program_hash
}
);
}

fn get_snos_program_hash(self: @ComponentState<TContractState>) -> felt252 {
self.snos_program_hash.read()
}
}

#[generate_trait]
Expand Down
21 changes: 18 additions & 3 deletions src/config/interface.cairo
Original file line number Diff line number Diff line change
Expand Up @@ -28,8 +28,8 @@ trait IConfig<T> {
/// True if the address is an operator, false otherwise.
fn is_operator(self: @T, address: ContractAddress) -> bool;

/// Sets the information of the program that generates the
/// state transition trace (namely StarknetOS).
/// Sets the information of the program that generates
/// layout bridge (Cairo verifier ran with StarknetOs proof, wrapped in bootloader).
///
/// # Arguments
///
Expand All @@ -38,7 +38,7 @@ trait IConfig<T> {
fn set_program_info(ref self: T, program_hash: felt252, config_hash: felt252);

/// Gets the information of the program that generates the
/// state transition trace (namely StarknetOS).
/// layout bridge (Cairo verifier ran with StarknetOs proof, wrapped in bootloader).
///
/// # Returns
///
Expand All @@ -59,4 +59,19 @@ trait IConfig<T> {
///
/// The contract address of the facts registry.
fn get_facts_registry(self: @T) -> ContractAddress;

/// Sets the information of the program that generates the
/// state transition trace (namely StarknetOS).
///
/// # Arguments
///
/// * `snos_program_hash` - The program hash.
fn set_snos_program_hash(ref self: T, snos_program_hash: felt252);

/// Gets the snos program hash used to prove block.
///
/// # Returns
///
/// Snos program hash.
fn get_snos_program_hash(self: @T) -> felt252;
}
28 changes: 28 additions & 0 deletions src/config/tests/test_config.cairo
Original file line number Diff line number Diff line change
Expand Up @@ -141,3 +141,31 @@ fn config_set_facts_registry_unauthorized() {
snf::start_cheat_caller_address(mock.contract_address, c::OTHER());
mock.set_facts_registry(facts_registry_address);
}

#[test]
fn config_set_snos_program_hash_ok() {
let mock = deploy_mock();

snf::start_cheat_caller_address(mock.contract_address, c::OWNER());

// Owner sets the info.
mock.set_snos_program_hash(0x1);
assert(mock.get_snos_program_hash() == 0x1, 'expect correct hashes');

mock.register_operator(c::OPERATOR());

// Operator can also set the program info.
snf::start_cheat_caller_address(mock.contract_address, c::OPERATOR());
mock.set_snos_program_hash(0x11);

assert(mock.get_snos_program_hash() == 0x11, 'expect operator hashes');
}

#[test]
#[should_panic(expected: ('Config: not owner or operator',))]
fn config_set_snos_program_hash_unauthorized() {
let mock = deploy_mock();

snf::start_cheat_caller_address(mock.contract_address, c::OPERATOR());
mock.set_snos_program_hash(0x11);
}
2 changes: 1 addition & 1 deletion tests/test_appchain.cairo
Original file line number Diff line number Diff line change
Expand Up @@ -223,7 +223,7 @@ fn update_state_ok() {
config_hash: 8868593919264901768958912247765226517850727970326290266005120699201631282
);
iconfig.set_facts_registry(address: fact_registry_mock.contract_address);

iconfig.set_snos_program_hash(snos_program_hash: 3);
// The state update contains a message to appchain, therefore, before
// being sealed, it must be sent first.
// The nonce must be adjusted to ensure the correct message to be sent.
Expand Down