Skip to content
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

feat: ProtocolHandler self management functions #1641

Open
wants to merge 3 commits into
base: protocol-handler
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
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
3 changes: 3 additions & 0 deletions cairo/protocol_handler/.gitignore
Original file line number Diff line number Diff line change
@@ -1 +1,4 @@
target
.snfoundry_versioned_programs/
coverage/
snfoundry_trace/
7 changes: 7 additions & 0 deletions cairo/protocol_handler/Scarb.toml
Original file line number Diff line number Diff line change
Expand Up @@ -12,10 +12,17 @@ openzeppelin = { git = "https://github.com/OpenZeppelin/cairo-contracts.git", ta
[dev-dependencies]
snforge_std = { git = "https://github.com/foundry-rs/starknet-foundry.git", tag = "v0.31.0" }
snforge_utils = { path = "../snforge_utils" }
assert_macros = "2.8.2"

[[target.starknet-contract]]
casm = true
casm-add-pythonic-hints = true

[scripts]
test = "snforge test"
test-coverage = "snforge test --coverage"

[profile.dev.cairo]
unstable-add-statements-code-locations-debug-info = true
unstable-add-statements-functions-debug-info = true
inlining-strategy = "avoid"
168 changes: 151 additions & 17 deletions cairo/protocol_handler/src/protocol_handler.cairo
Original file line number Diff line number Diff line change
Expand Up @@ -70,16 +70,65 @@ pub trait IProtocolHandler<TContractState> {
/// * `UNAUTHORIZED_SELECTOR` in case the selector is not authorized
/// * `ONLY_KAKAROT_CAN_BE_CALLED` in case the call is not to the Kakarot contract
fn execute_call(ref self: TContractState, call: Call);

/// Change the operator of the Kakarot contract.
/// Only the security council can call this function.
/// # Arguments
/// * `new_operator_address` - The new operator address
///
/// # Panics
/// * `Caller is missing role` in case the caller is not the security council
fn change_operator(ref self: TContractState, new_operator_address: ContractAddress);

/// Change the security council of the Kakarot contract.
/// Only the security council can call this function.
/// # Arguments
/// * `new_security_council_address` - The new security council address
///
/// # Panics
/// * `Caller is missing role` in case the caller is not the security council
fn change_security_council(
ref self: TContractState, new_security_council_address: ContractAddress
);

/// Change the gas price admin of the Kakarot contract.
/// Only the security council can call this function.
/// # Arguments
/// * `new_gas_price_admin` - The new gas price admin address
///
/// # Panics
/// * `Caller is missing role` in case the caller is not the security council
fn change_gas_price_admin(ref self: TContractState, new_gas_price_admin: ContractAddress);

/// Change the guardians of the Kakarot contract.
/// Only the security council can call this function.
/// # Arguments
/// * `new_guardians_address` - The new guardians address
///
/// # Panics
/// * `Caller is missing role` in case the caller is not the security council
fn add_guardian(ref self: TContractState, new_guardian_address: ContractAddress);

/// Remove a guardian from the Kakarot contract.
/// Only the security council can call this function.
/// # Arguments
/// * `guardian_address` - The guardian address to be removed
///
/// # Panics
/// * `Caller is missing role` in case the caller is not the security council
fn remove_guardian(ref self: TContractState, guardian_address: ContractAddress);
}

#[starknet::contract]
pub mod ProtocolHandler {
use starknet::event::EventEmitter;
use starknet::account::Call;
use starknet::{ContractAddress, ClassHash, get_block_timestamp, SyscallResultTrait};
use starknet::{
ContractAddress, contract_address_const, ClassHash, get_block_timestamp, SyscallResultTrait
};
use starknet::storage::{
Map, StoragePointerReadAccess, StoragePointerWriteAccess, StorageMapReadAccess,
StorageMapWriteAccess
StorageMapWriteAccess, Vec, MutableVecTrait
};
use openzeppelin_access::accesscontrol::AccessControlComponent;
use openzeppelin_introspection::src5::SRC5Component;
Expand All @@ -104,10 +153,10 @@ pub mod ProtocolHandler {
//* ------------------------------------------------------------------------ *//

// Access controls roles
const SECURITY_COUNCIL_ROLE: felt252 = selector!("SECURITY_COUNCIL_ROLE");
const OPERATOR_ROLE: felt252 = selector!("OPERATOR_ROLE");
const GUARDIAN_ROLE: felt252 = selector!("GUARDIAN_ROLE");
const GAS_PRICE_ADMIN_ROLE: felt252 = selector!("GAS_PRICE_ADMIN_ROLE");
pub const SECURITY_COUNCIL_ROLE: felt252 = selector!("SECURITY_COUNCIL_ROLE");
pub const OPERATOR_ROLE: felt252 = selector!("OPERATOR_ROLE");
pub const GUARDIAN_ROLE: felt252 = selector!("GUARDIAN_ROLE");
pub const GAS_PRICE_ADMIN_ROLE: felt252 = selector!("GAS_PRICE_ADMIN_ROLE");
// Pause delay
pub const SOFT_PAUSE_DELAY: u64 = 12 * 60 * 60; // 12 hours
pub const HARD_PAUSE_DELAY: u64 = 7 * 24 * 60 * 60; // 7 days
Expand All @@ -130,8 +179,9 @@ pub mod ProtocolHandler {
#[storage]
pub struct Storage {
pub kakarot: IKakarotDispatcher,
pub security_council: ContractAddress,
pub operator: ContractAddress,
pub guardians: Map<ContractAddress, bool>,
pub guardians: Vec<ContractAddress>,
Copy link
Collaborator Author

@obatirou obatirou Nov 26, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe add documentation on the behavior of Vec with the remove and add ?
Should this storage variable be kept ?

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't understand your comment

Copy link
Collaborator Author

@obatirou obatirou Nov 27, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is related to the discussion about storage vec not being able to remove an element. The length will not change when a guardian is removed.

pub gas_price_admin: ContractAddress,
pub protocol_frozen_until: u64,
pub authorized_operator_selector: Map<felt252, bool>,
Expand Down Expand Up @@ -213,19 +263,14 @@ pub mod ProtocolHandler {
gas_price_admin: ContractAddress,
mut guardians: Span<ContractAddress>
) {
// Store the Kakarot address
// Store the Kakarot, security council, operator, gas price admin and guardians addresses
self.kakarot.write(IKakarotDispatcher { contract_address: kakarot });

// AccessControl-related initialization
self.accesscontrol.initializer();

// Grant roles
self.accesscontrol._grant_role(SECURITY_COUNCIL_ROLE, security_council);
self.accesscontrol._grant_role(OPERATOR_ROLE, operator);
self.security_council.write(security_council);
self.operator.write(operator);
self.gas_price_admin.write(gas_price_admin);
for guardian in guardians {
self.accesscontrol._grant_role(GUARDIAN_ROLE, *guardian);
self.guardians.append().write(*guardian);
};
self.accesscontrol._grant_role(GAS_PRICE_ADMIN_ROLE, gas_price_admin);

// Store the authorized selectors for the operator
self.authorized_operator_selector.write(selector!("set_native_token"), true);
Expand All @@ -245,6 +290,17 @@ pub mod ProtocolHandler {
self
.authorized_operator_selector
.write(selector!("set_l1_messaging_contract_address"), true);

// AccessControl-related initialization
self.accesscontrol.initializer();

// Grant roles
self.accesscontrol._grant_role(SECURITY_COUNCIL_ROLE, security_council);
self.accesscontrol._grant_role(OPERATOR_ROLE, operator);
for guardian in guardians {
self.accesscontrol._grant_role(GUARDIAN_ROLE, *guardian);
};
self.accesscontrol._grant_role(GAS_PRICE_ADMIN_ROLE, gas_price_admin);
}

#[abi(embed_v0)]
Expand Down Expand Up @@ -386,5 +442,83 @@ pub mod ProtocolHandler {
// Emit Event Execution event
self.emit(Execution { call });
}

//* ------------------------------------------------------------------------ *//
//* SELF MANAGEMENT *//
//* ------------------------------------------------------------------------ *//

fn change_operator(ref self: ContractState, new_operator_address: ContractAddress) {
// Check only security council can call
self.accesscontrol.assert_only_role(SECURITY_COUNCIL_ROLE);

// Revoke the OPERATOR_ROLE from the current operator
self.accesscontrol._revoke_role(OPERATOR_ROLE, self.operator.read());

// Grant role to the new operator
self.accesscontrol._grant_role(OPERATOR_ROLE, new_operator_address);

// Update the operator
self.operator.write(new_operator_address);
}

fn change_security_council(
ref self: ContractState, new_security_council_address: ContractAddress
) {
// Check only security council can call
self.accesscontrol.assert_only_role(SECURITY_COUNCIL_ROLE);

// Revoke the SECURITY_COUNCIL_ROLE from the current security council
self.accesscontrol._revoke_role(SECURITY_COUNCIL_ROLE, self.security_council.read());

// Grant role to the new security council
self.accesscontrol._grant_role(SECURITY_COUNCIL_ROLE, new_security_council_address);

// Update the security council
self.security_council.write(new_security_council_address);
}

fn change_gas_price_admin(ref self: ContractState, new_gas_price_admin: ContractAddress) {
// Check only security council can call
self.accesscontrol.assert_only_role(SECURITY_COUNCIL_ROLE);

// Revoke the GAS_PRICE_ADMIN_ROLE from the current gas price admin
self.accesscontrol._revoke_role(GAS_PRICE_ADMIN_ROLE, self.gas_price_admin.read());

// Grant role to the new gas price admin
self.accesscontrol._grant_role(GAS_PRICE_ADMIN_ROLE, new_gas_price_admin);

// Update the gas price admin
self.gas_price_admin.write(new_gas_price_admin);
}

fn add_guardian(ref self: ContractState, new_guardian_address: ContractAddress) {
// Check only security council can call
self.accesscontrol.assert_only_role(SECURITY_COUNCIL_ROLE);

// Grant the GUARDIAN_ROLE to the new guardian
self.accesscontrol._grant_role(GUARDIAN_ROLE, new_guardian_address);

// Add the guardian to the guardians list
self.guardians.append().write(new_guardian_address)
}

fn remove_guardian(ref self: ContractState, guardian_address: ContractAddress) {
// Check only security council can call
self.accesscontrol.assert_only_role(SECURITY_COUNCIL_ROLE);

// Revoke the GUARDIAN_ROLE from the guardian
self.accesscontrol._revoke_role(GUARDIAN_ROLE, guardian_address);

// Remove the guardian from the guardians list
for i in 0
..self
.guardians
.len() {
let guardian = self.guardians.at(i).read();
if guardian == guardian_address {
self.guardians.at(i).write(contract_address_const::<0>());
}
};
}
}
}
Loading
Loading