Skip to content

Commit

Permalink
Feat: Completed Implementation of role_store contract. (#443)
Browse files Browse the repository at this point in the history
completed role_store + tests

Co-authored-by: zarboq <[email protected]>
  • Loading branch information
ametel01 and zarboq authored Sep 25, 2023
1 parent c2cbb4e commit ac48e57
Show file tree
Hide file tree
Showing 2 changed files with 285 additions and 181 deletions.
249 changes: 173 additions & 76 deletions src/role/role_store.cairo
Original file line number Diff line number Diff line change
Expand Up @@ -43,32 +43,30 @@ trait IRoleStore<TContractState> {
/// Returns the number of roles stored in the contract.
/// # Return
/// The number of roles.
fn get_role_count(self: @TContractState) -> u128;
/// # [TO FIX]
/// Returns the keys of the roles stored in the contract.
/// # Arguments
/// `start` - The starting index of the range of roles to return.
/// `end` - The ending index of the range of roles to return.
/// # Return
/// The keys of the roles.
/// fn get_roles(self: @TContractState, start: u32, end: u32) -> Array<felt252>;

/// # [TO DO]
/// Returns the number of members of the specified role.
/// # Arguments
/// `role_key` - The key of the role.
/// # Return
/// The number of members of the role.
/// fn get_role_member_count(self: @TContractState, role_key: felt252) -> u128;

/// Returns the members of the specified role.
/// # Arguments
/// `role_key` - The key of the role.
/// `start` - The start index, the value for this index will be included.
/// `end` - The end index, the value for this index will not be included.
/// # Return
/// The members of the role.
/// fn get_role_members(self: @TContractState, role_key: felt252, start: u128, end: u128) -> Array<ContractAddress>;
fn get_role_count(self: @TContractState) -> u32;
/// Returns the keys of the roles stored in the contract.
/// # Arguments
/// `start` - The starting index of the range of roles to return.
/// `end` - The ending index of the range of roles to return.
/// # Return
/// The keys of the roles.
fn get_roles(self: @TContractState, start: u32, end: u32) -> Array<felt252>;
/// Returns the number of members of the specified role.
/// # Arguments
/// `role_key` - The key of the role.
/// # Return
/// The number of members of the role.
fn get_role_member_count(self: @TContractState, role_key: felt252) -> u32;
/// Returns the members of the specified role.
/// # Arguments
/// `role_key` - The key of the role.
/// `start` - The start index, the value for this index will be included.
/// `end` - The end index, the value for this index will not be included.
/// # Return
/// The members of the role.
fn get_role_members(
self: @TContractState, role_key: felt252, start: u32, end: u32
) -> Array<ContractAddress>;
}

#[starknet::contract]
Expand All @@ -78,8 +76,8 @@ mod RoleStore {
// *************************************************************************

// Core lib imports.
use starknet::{ContractAddress, get_caller_address};
//use array::ArrayTrait;
use core::zeroable::Zeroable;
use starknet::{ContractAddress, get_caller_address, contract_address_const};

// Local imports.
use satoru::role::{role, error::RoleError};
Expand All @@ -90,13 +88,17 @@ mod RoleStore {
#[storage]
struct Storage {
/// Maps accounts to their roles.
role_members: LegacyMap::<(felt252, ContractAddress), bool>,
has_role: LegacyMap::<(felt252, ContractAddress), bool>,
/// Stores the number of the indexes used to a specific role.
role_members_count: LegacyMap::<felt252, u32>,
/// Stores all the account that have a specific role.
role_members: LegacyMap::<(felt252, u32), ContractAddress>,
/// Stores unique role names.
role_names: LegacyMap::<felt252, bool>,
/// Store the number of unique roles.
role_count: u128,
/// List of all role keys.
///role_keys: Array<felt252>,
/// Store the number of indexes of the roles.
roles_count: u32,
/// List of all role keys.
roles: LegacyMap::<u32, felt252>,
}

// *************************************************************************
Expand Down Expand Up @@ -140,8 +142,7 @@ mod RoleStore {
let caller = get_caller_address();
// Grant the caller admin role.
self._grant_role(caller, role::ROLE_ADMIN);
// Initialize the role_count to 1 due to the line just above.
self.role_count.write(1);
// Initialize the role_count to 1 due to the line just above.
}

// *************************************************************************
Expand All @@ -154,17 +155,15 @@ mod RoleStore {
}

fn grant_role(ref self: ContractState, account: ContractAddress, role_key: felt252) {
let caller = get_caller_address();
// Check that the caller has the admin role.
self._assert_only_role(caller, role::ROLE_ADMIN);
self._assert_only_role(get_caller_address(), role::ROLE_ADMIN);
// Grant the role.
self._grant_role(account, role_key);
}

fn revoke_role(ref self: ContractState, account: ContractAddress, role_key: felt252) {
let caller = get_caller_address();
// Check that the caller has the admin role.
self._assert_only_role(caller, role::ROLE_ADMIN);
self._assert_only_role(get_caller_address(), role::ROLE_ADMIN);
// Revoke the role.
self._revoke_role(account, role_key);
}
Expand All @@ -173,29 +172,78 @@ mod RoleStore {
self._assert_only_role(account, role_key);
}

fn get_role_count(self: @ContractState) -> u128 {
return self.role_count.read();
fn get_role_count(self: @ContractState) -> u32 {
let mut count = 0;
let mut i = 1;
loop {
if i > self.roles_count.read() {
break;
}
if !self.roles.read(i).is_zero() {
count += 1;
}
i += 1;
};
count
}

fn get_roles(self: @ContractState, start: u32, mut end: u32) -> Array<felt252> {
let mut arr = array![];
let roles_count = self.roles_count.read();
if end > roles_count {
end = roles_count;
}
let mut i = start;
loop {
if i > end {
break;
}
let role = self.roles.read(i);
if !role.is_zero() {
arr.append(role);
}
i += 1;
};
arr
}

fn get_role_member_count(self: @ContractState, role_key: felt252) -> u32 {
let mut count = 0;
let mut i = 1;
loop {
if i > self.role_members_count.read(role_key) {
break;
}
if !(self.role_members.read((role_key, i)) == contract_address_const::<0>()) {
count += 1;
}
i += 1;
};
count
}

fn get_role_members(
self: @ContractState, role_key: felt252, start: u32, mut end: u32
) -> Array<ContractAddress> {
let mut arr: Array<ContractAddress> = array![];
let mut i = start;
loop {
if i > end || i > self.role_members_count.read(role_key) {
break;
}
let role_member = self.role_members.read((role_key, i));
// Since some role members will have indexes with zero address if a zero address
// is found end increase by 1 to mock array behaviour.
if role_member.is_zero() {
end += 1;
}
if !(role_member == contract_address_const::<0>()) {
arr.append(role_member);
}
i += 1;
};
arr
}
//fn get_roles(self: @ContractState, start: u32, end: u32) -> Array<felt252> {
// Create a new array to store the result.
//let mut result = ArrayTrait::<felt252>::new();
//let role_keys_length = self.role_keys.read().len();
// Ensure the range is valid.
//assert(start < end, "InvalidRange");
//assert(end <= role_keys_length, "EndOutOfBounds");
//let mut current_index = start;
//loop {
// Check if we've reached the end of the specified range.
//if current_index >= end {
//break;
//}
//let key = *self.role_keys.read().at(current_index);
//result.append(key);
// Increment the index.
//current_index += 1;
//};
//return result;
//}
}

// *************************************************************************
Expand All @@ -205,7 +253,7 @@ mod RoleStore {
impl InternalFunctions of InternalFunctionsTrait {
#[inline(always)]
fn _has_role(self: @ContractState, account: ContractAddress, role_key: felt252) -> bool {
self.role_members.read((role_key, account))
self.has_role.read((role_key, account))
}

#[inline(always)]
Expand All @@ -216,31 +264,80 @@ mod RoleStore {
fn _grant_role(ref self: ContractState, account: ContractAddress, role_key: felt252) {
// Only grant the role if the account doesn't already have it.
if !self._has_role(account, role_key) {
let caller: ContractAddress = get_caller_address();
self.role_members.write((role_key, account), true);
self.has_role.write((role_key, account), true);
// Iterates through indexes for role members, if an index has zero ContractAddress
// it writes the account to that index for the role.
let roles_members_count = self.role_members_count.read(role_key);
let current_roles_count = self.roles_count.read();
let mut i = 1;
loop {
let stored_role_member = self.role_members.read((role_key, i));
if stored_role_member.is_zero() {
self.role_members.write((role_key, i), account);
self.role_members_count.write(role_key, roles_members_count + 1);
break;
}
i += 1;
};

// Store the role name if it's not already stored.
if self.role_names.read(role_key) == false {
self.role_names.write(role_key, true);
let mut current_count: u128 = self.role_count.read();
self.role_count.write(current_count + 1);
// Read the current state of role_keys into a local variable.
// let mut local_role_keys = self.role_keys.read();
// Modify the local variable.
// local_role_keys.append(role_key);
// Write back the modified local variable to the contract state.
// self.role_keys.write(local_role_keys);
self.roles_count.write(current_roles_count + 1);
}
self.emit(RoleGranted { role_key, account, sender: caller });
self.emit(RoleGranted { role_key, account, sender: get_caller_address() });
}
// Iterates through indexes in stored_roles and if a value for the index is zero
// it writes the role_key to that index.
let mut i = 1;
loop {
let stored_role = self.roles.read(i);
if stored_role.is_zero() {
self.roles.write(i, role_key);
break;
}
i += 1;
};
}

fn _revoke_role(ref self: ContractState, account: ContractAddress, role_key: felt252) {
let current_roles_count = self.roles_count.read();
// Only revoke the role if the account has it.
if self._has_role(account, role_key) {
let caller: ContractAddress = get_caller_address();
self.role_members.write((role_key, account), false);
self.emit(RoleRevoked { role_key, account, sender: caller });
self.has_role.write((role_key, account), false);
self.emit(RoleRevoked { role_key, account, sender: get_caller_address() });
let current_role_members_count = self.role_members_count.read(role_key);
let mut i = 1;
loop {
let stored_role_member = self.role_members.read((role_key, i));
if stored_role_member == account {
self.role_members.write((role_key, i), contract_address_const::<0>());
break;
}
i += 1;
};
// If the role has no members remove the role from roles.
if self.get_role_member_count(role_key).is_zero() {
let role_index = self._find_role_index(role_key);
self.roles.write(role_index, Zeroable::zero());
}
}
}

fn _find_role_index(ref self: ContractState, role_key: felt252) -> u32 {
let mut index = 0;
let mut i = 1;
loop {
if i > self.roles_count.read() {
break;
}
if self.roles.read(i) == role_key {
index = i;
break;
}
i += 1;
};
index
}
}
}
Loading

0 comments on commit ac48e57

Please sign in to comment.