Skip to content

Commit

Permalink
Refactor AccessControl into a component
Browse files Browse the repository at this point in the history
  • Loading branch information
archseer committed Jan 9, 2024
1 parent d3c8d06 commit ec48e66
Show file tree
Hide file tree
Showing 7 changed files with 144 additions and 250 deletions.
48 changes: 11 additions & 37 deletions contracts/src/access_control/access_controller.cairo
Original file line number Diff line number Diff line change
@@ -1,70 +1,44 @@
use starknet::ContractAddress;

#[starknet::contract]
mod AccessController {
use starknet::ContractAddress;
use starknet::class_hash::ClassHash;

use chainlink::libraries::access_control::{AccessControl, IAccessController};
use chainlink::libraries::access_control::{AccessControlComponent, IAccessController};
use chainlink::libraries::ownable::{OwnableComponent, IOwnable};
use chainlink::libraries::upgradeable::{Upgradeable, IUpgradeable};

component!(path: OwnableComponent, storage: ownable, event: OwnableEvent);
component!(path: AccessControlComponent, storage: access_control, event: AccessControlEvent);

#[abi(embed_v0)]
impl OwnableImpl = OwnableComponent::OwnableImpl<ContractState>;
impl InternalImpl = OwnableComponent::InternalImpl<ContractState>;

#[abi(embed_v0)]
impl AccessControlImpl = AccessControlComponent::AccessControlImpl<ContractState>;
impl AccessControlInternalImpl = AccessControlComponent::InternalImpl<ContractState>;

#[event]
#[derive(Drop, starknet::Event)]
enum Event {
#[flat]
OwnableEvent: OwnableComponent::Event,
#[flat]
AccessControlEvent: AccessControlComponent::Event,
}

#[storage]
struct Storage {
#[substorage(v0)]
ownable: OwnableComponent::Storage,
#[substorage(v0)]
access_control: AccessControlComponent::Storage,
}

#[constructor]
fn constructor(ref self: ContractState, owner_address: ContractAddress) {
self.ownable.initializer(owner_address);
let mut access_control = AccessControl::unsafe_new_contract_state();
AccessControl::constructor(ref access_control);
}

#[external(v0)]
impl AccessControllerImpl of IAccessController<ContractState> {
fn has_access(self: @ContractState, user: ContractAddress, data: Array<felt252>) -> bool {
let state = AccessControl::unsafe_new_contract_state();
AccessControl::has_access(@state, user, data)
}

fn add_access(ref self: ContractState, user: ContractAddress) {
self.ownable.assert_only_owner();
let mut state = AccessControl::unsafe_new_contract_state();
AccessControl::add_access(ref state, user);
}

fn remove_access(ref self: ContractState, user: ContractAddress) {
self.ownable.assert_only_owner();
let mut state = AccessControl::unsafe_new_contract_state();
AccessControl::remove_access(ref state, user);
}

fn enable_access_check(ref self: ContractState) {
self.ownable.assert_only_owner();
let mut state = AccessControl::unsafe_new_contract_state();
AccessControl::enable_access_check(ref state);
}

fn disable_access_check(ref self: ContractState) {
self.ownable.assert_only_owner();
let mut state = AccessControl::unsafe_new_contract_state();
AccessControl::disable_access_check(ref state);
}
self.access_control.initializer();
}

///
Expand Down
56 changes: 13 additions & 43 deletions contracts/src/emergency/sequencer_uptime_feed.cairo
Original file line number Diff line number Diff line change
Expand Up @@ -28,23 +28,30 @@ mod SequencerUptimeFeed {
use zeroable::Zeroable;

use chainlink::libraries::ownable::{OwnableComponent, IOwnable};
use chainlink::libraries::access_control::{AccessControl, IAccessController};
use chainlink::libraries::access_control::{AccessControlComponent, IAccessController};
use chainlink::libraries::access_control::AccessControlComponent::InternalTrait as AccessControlInternalTrait;
use chainlink::ocr2::aggregator::Round;
use chainlink::ocr2::aggregator::IAggregator;
use chainlink::ocr2::aggregator::{Transmission};
use chainlink::libraries::upgradeable::Upgradeable;

component!(path: OwnableComponent, storage: ownable, event: OwnableEvent);
component!(path: AccessControlComponent, storage: access_control, event: AccessControlEvent);

#[abi(embed_v0)]
impl OwnableImpl = OwnableComponent::OwnableImpl<ContractState>;
impl InternalImpl = OwnableComponent::InternalImpl<ContractState>;
impl OwnableInternalImpl = OwnableComponent::InternalImpl<ContractState>;

#[abi(embed_v0)]
impl AccessControlImpl = AccessControlComponent::AccessControlImpl<ContractState>;
impl AccessControlInternalImpl = AccessControlComponent::InternalImpl<ContractState>;

#[storage]
struct Storage {
#[substorage(v0)]
ownable: OwnableComponent::Storage,
#[substorage(v0)]
access_control: AccessControlComponent::Storage,

// l1 sender is an starknet validator ethereum address
_l1_sender: felt252,
Expand All @@ -58,6 +65,8 @@ mod SequencerUptimeFeed {
enum Event {
#[flat]
OwnableEvent: OwnableComponent::Event,
#[flat]
AccessControlEvent: AccessControlComponent::Event,
RoundUpdated: RoundUpdated,
NewRound: NewRound,
AnswerUpdated: AnswerUpdated,
Expand Down Expand Up @@ -213,43 +222,6 @@ mod SequencerUptimeFeed {
Upgradeable::upgrade(new_impl)
}

///
/// Access Control
///

#[external(v0)]
impl AccessControllerImpl of IAccessController<ContractState> {
fn has_access(self: @ContractState, user: ContractAddress, data: Array<felt252>) -> bool {
let state = AccessControl::unsafe_new_contract_state();
AccessControl::has_access(@state, user, data)
}

fn add_access(ref self: ContractState, user: ContractAddress) {
self.ownable.assert_only_owner();
let mut state = AccessControl::unsafe_new_contract_state();
AccessControl::add_access(ref state, user)
}

fn remove_access(ref self: ContractState, user: ContractAddress) {
self.ownable.assert_only_owner();
let mut state = AccessControl::unsafe_new_contract_state();
AccessControl::remove_access(ref state, user)
}

fn enable_access_check(ref self: ContractState) {
self.ownable.assert_only_owner();
let mut state = AccessControl::unsafe_new_contract_state();
AccessControl::enable_access_check(ref state)
}

fn disable_access_check(ref self: ContractState) {
self.ownable.assert_only_owner();
let mut state = AccessControl::unsafe_new_contract_state();
AccessControl::disable_access_check(ref state)
}
}


///
/// Internals
///
Expand All @@ -258,16 +230,14 @@ mod SequencerUptimeFeed {
impl Internals of InternalTrait {
fn _require_read_access(self: @ContractState) {
let sender = starknet::info::get_caller_address();
let access_control = AccessControl::unsafe_new_contract_state();
AccessControl::check_read_access(@access_control, sender);
self.access_control.check_read_access(sender);
}

fn _initializer(
ref self: ContractState, initial_status: u128, owner_address: ContractAddress
) {
self.ownable.initializer(owner_address);
let mut access_control = AccessControl::unsafe_new_contract_state();
AccessControl::constructor(ref access_control);
self.access_control.initializer();
let round_id = 1_u128;
let timestamp = starknet::info::get_block_timestamp();
self._record_round(round_id, initial_status, timestamp);
Expand Down
137 changes: 76 additions & 61 deletions contracts/src/libraries/access_control.cairo
Original file line number Diff line number Diff line change
Expand Up @@ -2,18 +2,23 @@ use starknet::ContractAddress;
#[starknet::interface]
trait IAccessController<TContractState> {
fn has_access(self: @TContractState, user: ContractAddress, data: Array<felt252>) -> bool;
fn has_read_access(self: @TContractState, user: ContractAddress, data: Array<felt252>) -> bool;
fn add_access(ref self: TContractState, user: ContractAddress);
fn remove_access(ref self: TContractState, user: ContractAddress);
fn enable_access_check(ref self: TContractState);
fn disable_access_check(ref self: TContractState);
}

#[starknet::contract]
mod AccessControl {
// Requires Ownable subcomponent.
#[starknet::component]
mod AccessControlComponent {
use starknet::ContractAddress;
use starknet::class_hash::ClassHash;
use zeroable::Zeroable;

use chainlink::libraries::ownable::{OwnableComponent};
use OwnableComponent::InternalImpl as OwnableInternalImpl;

#[storage]
struct Storage {
_check_enabled: bool,
Expand Down Expand Up @@ -45,83 +50,93 @@ mod AccessControl {
#[derive(Drop, starknet::Event)]
struct AccessControlDisabled {}

fn has_access(self: @ContractState, user: ContractAddress, data: Array<felt252>) -> bool {
let has_access = self._access_list.read(user);
if has_access {
return true;
#[embeddable_as(AccessControlImpl)]
impl AccessControl<
TContractState, +HasComponent<TContractState>, impl Ownable: OwnableComponent::HasComponent<TContractState>, +Drop<TContractState>,
> of super::IAccessController<ComponentState<TContractState>> {
fn has_access(self: @ComponentState<TContractState>, user: ContractAddress, data: Array<felt252>) -> bool {
let has_access = self._access_list.read(user);
if has_access {
return true;
}

let check_enabled = self._check_enabled.read();
if !check_enabled {
return true;
}

false
}

let check_enabled = self._check_enabled.read();
if !check_enabled {
return true;
}
fn has_read_access(self: @ComponentState<TContractState>, user: ContractAddress, data: Array<felt252>) -> bool {
let _has_access = self.has_access(user, data);
if _has_access {
return true;
}

false
}
// NOTICE: read access is granted to direct calls, to enable off-chain reads.
if user.is_zero() {
return true;
}

fn has_read_access(self: @ContractState, user: ContractAddress, data: Array<felt252>) -> bool {
let _has_access = has_access(self, user, data);
if _has_access {
return true;
false
}

// NOTICE: read access is granted to direct calls, to enable off-chain reads.
if user.is_zero() {
return true;
fn add_access(ref self: ComponentState<TContractState>, user: ContractAddress) {
get_dep_component!(self, Ownable).assert_only_owner();
let has_access = self._access_list.read(user);
if !has_access {
self._access_list.write(user, true);
self.emit(Event::AddedAccess(AddedAccess { user: user }));
}
}

false
}

fn check_access(self: @ContractState, user: ContractAddress) {
let allowed = has_access(self, user, ArrayTrait::new());
assert(allowed, 'user does not have access');
}

fn check_read_access(self: @ContractState, user: ContractAddress) {
let allowed = has_read_access(self, user, ArrayTrait::new());
assert(allowed, 'user does not have read access');
}

//
// Unprotected
//

#[constructor]
fn constructor(ref self: ContractState) {
self._check_enabled.write(true);
self.emit(Event::AccessControlEnabled(AccessControlEnabled {}));
}
fn remove_access(ref self: ComponentState<TContractState>, user: ContractAddress) {
get_dep_component!(self, Ownable).assert_only_owner();
let has_access = self._access_list.read(user);
if has_access {
self._access_list.write(user, false);
self.emit(Event::RemovedAccess(RemovedAccess { user: user }));
}
}

fn add_access(ref self: ContractState, user: ContractAddress) {
let has_access = self._access_list.read(user);
if !has_access {
self._access_list.write(user, true);
self.emit(Event::AddedAccess(AddedAccess { user: user }));
fn enable_access_check(ref self: ComponentState<TContractState>) {
get_dep_component!(self, Ownable).assert_only_owner();
let check_enabled = self._check_enabled.read();
if !check_enabled {
self._check_enabled.write(true);
self.emit(Event::AccessControlEnabled(AccessControlEnabled {}));
}
}
}

fn remove_access(ref self: ContractState, user: ContractAddress) {
let has_access = self._access_list.read(user);
if has_access {
self._access_list.write(user, false);
self.emit(Event::RemovedAccess(RemovedAccess { user: user }));
fn disable_access_check(ref self: ComponentState<TContractState>) {
get_dep_component!(self, Ownable).assert_only_owner();
let check_enabled = self._check_enabled.read();
if check_enabled {
self._check_enabled.write(false);
self.emit(Event::AccessControlDisabled(AccessControlDisabled {}));
}
}
}

fn enable_access_check(ref self: ContractState) {
let check_enabled = self._check_enabled.read();
if !check_enabled {
#[generate_trait]
impl InternalImpl<
TContractState, +HasComponent<TContractState>, impl Ownable: OwnableComponent::HasComponent<TContractState>, +Drop<TContractState>,
> of InternalTrait<TContractState> {
fn initializer(ref self: ComponentState<TContractState>) {
self._check_enabled.write(true);
self.emit(Event::AccessControlEnabled(AccessControlEnabled {}));
}
}

fn disable_access_check(ref self: ContractState) {
let check_enabled = self._check_enabled.read();
if check_enabled {
self._check_enabled.write(false);
self.emit(Event::AccessControlDisabled(AccessControlDisabled {}));
fn check_access(self: @ComponentState<TContractState>, user: ContractAddress) {
let allowed = AccessControl::has_access(self, user, ArrayTrait::new());
assert(allowed, 'user does not have access');
}

fn check_read_access(self: @ComponentState<TContractState>, user: ContractAddress) {
let allowed = AccessControl::has_read_access(self, user, ArrayTrait::new());
assert(allowed, 'user does not have read access');
}
}

}
Loading

0 comments on commit ec48e66

Please sign in to comment.