diff --git a/packages/contracts/resolver/scripts/deploy.sh b/packages/contracts/resolver/scripts/deploy.sh index 56a31731a..afd4fdc66 100755 --- a/packages/contracts/resolver/scripts/deploy.sh +++ b/packages/contracts/resolver/scripts/deploy.sh @@ -8,13 +8,13 @@ export ACCOUNT_KEYSTORE="../../../../../cartridge/keystore" export UDC_ADDRESS="0x41a78e741e5af2fec34b695679bc6891742439f7afb8484ecd7766661ad02bf" -export OWNER_ADDRESS="0x" +export ADMIN_ADDRESS="0x6bd82a20984e638c8e1d45770e2924e274e315b9609eb15c26384eac0094cf1" -export EXECUTOR_PUB_KEY_ASIA="0x180ee4f1a9a0b27a444bf960822003a0748aa0cdd69f34c2792f42e13dc805e" -export EXECUTOR_PUB_KEY_EU="0x717642da0ea4a1dd4551404571b2ff9a4c256328f098e3276e027aeb37038e1" -export EXECUTOR_PUB_KEY_US="0x3acd845dff3a582501a330dec9865865ba3f8cced45c918f66ed5f2b538f082" +export EXECUTOR_ADDRESS_1="0x1" +export EXECUTOR_ADDRESS_2="0x2" +export EXECUTOR_ADDRESS_3="0x6bd82a20984e638c8e1d45770e2924e274e315b9609eb15c26384eac0094cf1" -export RESOLVER_CLASS_HASH="0x" +export RESOLVER_CLASS_HASH="0x0502af1b1d34e7d5f9ff6f9495e0f7a921dd48dd84055c4d8f6fc1b0a2021881" starkli invoke $UDC_ADDRESS -w --account $ACCOUNT \ @@ -26,7 +26,7 @@ starkli invoke $UDC_ADDRESS -w --account $ACCOUNT \ 0x0 \ 0x0 \ 0x5 \ - $OWNER_ADDRESS \ - 0x3 $EXECUTOR_PUB_KEY_US $EXECUTOR_PUB_KEY_EU $EXECUTOR_PUB_KEY_ASIA \ + $ADMIN_ADDRESS \ + 0x3 $EXECUTOR_ADDRESS_1 $EXECUTOR_ADDRESS_2 $EXECUTOR_ADDRESS_3 \ diff --git a/packages/contracts/resolver/scripts/reset_name.sh b/packages/contracts/resolver/scripts/reset_name.sh index 195b5d3aa..b6f4c765c 100755 --- a/packages/contracts/resolver/scripts/reset_name.sh +++ b/packages/contracts/resolver/scripts/reset_name.sh @@ -6,7 +6,7 @@ export RPC_URL="http://localhost:8001/x/starknet/sepolia" export ACCOUNT="../../../../../cartridge/account.json" export ACCOUNT_KEYSTORE="../../../../../cartridge/keystore" -export RESOLVER_ADDRESS="0x" +export RESOLVER_ADDRESS="0x468a75c755cc663618f5424fae7e2c2f85c1a8d7a38157f4b0ecb0f1b815443" export NAME=$(starkli to-cairo-string $1) diff --git a/packages/contracts/resolver/scripts/get_name.sh b/packages/contracts/resolver/scripts/resolve.sh similarity index 83% rename from packages/contracts/resolver/scripts/get_name.sh rename to packages/contracts/resolver/scripts/resolve.sh index 1b100d22b..954d84d07 100755 --- a/packages/contracts/resolver/scripts/get_name.sh +++ b/packages/contracts/resolver/scripts/resolve.sh @@ -6,7 +6,7 @@ export RPC_URL="http://localhost:8001/x/starknet/sepolia" export ACCOUNT="../../../../../cartridge/account.json" export ACCOUNT_KEYSTORE="../../../../../cartridge/keystore" -export RESOLVER_ADDRESS="0x" +export RESOLVER_ADDRESS="0x468a75c755cc663618f5424fae7e2c2f85c1a8d7a38157f4b0ecb0f1b815443" export NAME=$(starkli to-cairo-string $1) export STARKNET=$(starkli to-cairo-string starknet) diff --git a/packages/contracts/resolver/scripts/set_name.sh b/packages/contracts/resolver/scripts/set_name.sh new file mode 100755 index 000000000..3e5436111 --- /dev/null +++ b/packages/contracts/resolver/scripts/set_name.sh @@ -0,0 +1,23 @@ +#!/bin/bash +set -euo pipefail +pushd $(dirname "$0")/.. + +export RPC_URL="http://localhost:8001/x/starknet/sepolia" +export ACCOUNT="../../../../../cartridge/account.json" +export ACCOUNT_KEYSTORE="../../../../../cartridge/keystore" + +export RESOLVER_ADDRESS="0x468a75c755cc663618f5424fae7e2c2f85c1a8d7a38157f4b0ecb0f1b815443" + +export NAME=$(starkli to-cairo-string $1) +export STARKNET=$(starkli to-cairo-string starknet) + +echo $NAME +echo $STARKNET + +starkli invoke \ + --rpc $RPC_URL \ + --account $ACCOUNT \ + --keystore $ACCOUNT_KEYSTORE \ + --keystore-password apidev \ + $RESOLVER_ADDRESS set_name \ + $NAME 0x555 \ diff --git a/packages/contracts/resolver/src/interface.cairo b/packages/contracts/resolver/src/interface.cairo index dd7b2a89d..1816a81c8 100644 --- a/packages/contracts/resolver/src/interface.cairo +++ b/packages/contracts/resolver/src/interface.cairo @@ -5,9 +5,3 @@ trait IResolver { ) -> felt252; } - - -#[starknet::interface] -trait IExecutorAccount { - fn get_public_key(self: @TContractState) -> felt252; -} diff --git a/packages/contracts/resolver/src/lib.cairo b/packages/contracts/resolver/src/lib.cairo index 57bebd6c4..c07b635e2 100644 --- a/packages/contracts/resolver/src/lib.cairo +++ b/packages/contracts/resolver/src/lib.cairo @@ -1,4 +1,3 @@ mod interface; mod resolver; -// mod resolver_ccip; \ No newline at end of file diff --git a/packages/contracts/resolver/src/resolver.cairo b/packages/contracts/resolver/src/resolver.cairo index 2dd084b49..54ad9b904 100644 --- a/packages/contracts/resolver/src/resolver.cairo +++ b/packages/contracts/resolver/src/resolver.cairo @@ -2,41 +2,45 @@ trait IControllerResolverDelegation { fn set_name(ref self: TContractState, name: felt252, address: starknet::ContractAddress); fn reset_name(ref self: TContractState, name: felt252); - - fn set_owner(ref self: TContractState, new_owner: starknet::ContractAddress); - - fn grant_executors(ref self: TContractState, executor_pub_keys: Span); - fn revoke_executors(ref self: TContractState, executor_pub_keys: Span); } +const EXECUTOR_ROLE: felt252 = selector!("EXECUTOR_ROLE"); + #[starknet::contract] mod ControllerResolverDelegation { use core::panics::panic_with_byte_array; use starknet::{get_caller_address, ContractAddress, ClassHash, storage::Map}; use starknet::contract_address::ContractAddressZeroable; - use resolver::interface::{ - IResolver, IExecutorAccount, IExecutorAccountDispatcher, IExecutorAccountDispatcherTrait - }; + use resolver::interface::IResolver; use openzeppelin::upgrades::UpgradeableComponent; use openzeppelin::upgrades::interface::IUpgradeable; + use openzeppelin::introspection::src5::SRC5Component; + use openzeppelin::access::accesscontrol::AccessControlComponent; + use openzeppelin::access::accesscontrol::DEFAULT_ADMIN_ROLE; + use super::EXECUTOR_ROLE; - // Upgradeable + component!(path: UpgradeableComponent, storage: upgradeable, event: UpgradeableEvent); + component!(path: AccessControlComponent, storage: accesscontrol, event: AccessControlEvent); + component!(path: SRC5Component, storage: src5, event: SRC5Event); + + #[abi(embed_v0)] + impl AccessControlMixinImpl = AccessControlComponent::AccessControlMixinImpl; + impl UpgradeableInternalImpl = UpgradeableComponent::InternalImpl; - + impl AccessControlInternalImpl = AccessControlComponent::InternalImpl; #[storage] struct Storage { // name -> address name_owners: Map::, - // pub_key -> allowed - executor_pub_keys: Map::, - // owner - owner: ContractAddress, - // upgradeable #[substorage(v0)] upgradeable: UpgradeableComponent::Storage, + #[substorage(v0)] + accesscontrol: AccessControlComponent::Storage, + #[substorage(v0)] + src5: SRC5Component::Storage, } #[event] @@ -44,7 +48,11 @@ mod ControllerResolverDelegation { enum Event { DomainToAddressUpdate: DomainToAddressUpdate, #[flat] - UpgradeableEvent: UpgradeableComponent::Event + UpgradeableEvent: UpgradeableComponent::Event, + #[flat] + AccessControlEvent: AccessControlComponent::Event, + #[flat] + SRC5Event: SRC5Component::Event, } #[derive(Drop, starknet::Event)] @@ -57,13 +65,16 @@ mod ControllerResolverDelegation { #[constructor] fn constructor( - ref self: ContractState, owner: ContractAddress, mut executor_pub_keys: Span + ref self: ContractState, admin: ContractAddress, mut executors: Span ) { - self.owner.write(owner); + self.accesscontrol.initializer(); - while let Option::Some(pub_key) = executor_pub_keys.pop_front() { - self.executor_pub_keys.write(*pub_key, true); + self.accesscontrol._grant_role(DEFAULT_ADMIN_ROLE, admin); + + while let Option::Some(executor) = executors.pop_front() { + self.accesscontrol._grant_role(EXECUTOR_ROLE, *executor); } + } #[abi(embed_v0)] @@ -88,7 +99,7 @@ mod ControllerResolverDelegation { #[abi(embed_v0)] impl ControllerResolverDelegationImpl of super::IControllerResolverDelegation { fn set_name(ref self: ContractState, name: felt252, address: ContractAddress) { - self.assert_executor(); + self.accesscontrol.assert_only_role(EXECUTOR_ROLE); let owner = self.name_owners.read(name); assert(owner == ContractAddressZeroable::zero(), 'Name is already taken'); @@ -103,57 +114,19 @@ mod ControllerResolverDelegation { } fn reset_name(ref self: ContractState, name: felt252) { - self.assert_executor(); + self.accesscontrol.assert_only_role(EXECUTOR_ROLE); self.name_owners.write(name, ContractAddressZeroable::zero()); } - fn set_owner(ref self: ContractState, new_owner: ContractAddress) { - self.assert_owner(); - self.owner.write(new_owner); - } - - - fn grant_executors(ref self: ContractState, mut executor_pub_keys: Span) { - self.assert_owner(); - - while let Option::Some(pub_key) = executor_pub_keys.pop_front() { - self.executor_pub_keys.write(*pub_key, true); - } - } - - fn revoke_executors(ref self: ContractState, mut executor_pub_keys: Span) { - self.assert_owner(); - - while let Option::Some(pub_key) = executor_pub_keys.pop_front() { - self.executor_pub_keys.write(*pub_key, false); - } - } } #[abi(embed_v0)] impl UpgradeableImpl of IUpgradeable { fn upgrade(ref self: ContractState, new_class_hash: ClassHash) { - self.assert_owner(); - self.upgradeable.upgrade(new_class_hash); - } - } - - #[generate_trait] - impl InternalImpl of InternalTrait { - fn assert_owner(self: @ContractState) { - let caller = get_caller_address(); - let owner = self.owner.read(); - assert(caller == owner, 'caller is not owner'); - } - - fn assert_executor(self: @ContractState) { - // retrieve caller public key - let caller_disp = IExecutorAccountDispatcher { contract_address: get_caller_address() }; - let public_key = caller_disp.get_public_key(); + self.accesscontrol.assert_only_role(DEFAULT_ADMIN_ROLE); - let is_executor = self.executor_pub_keys.read(public_key); - assert(is_executor, 'caller is not executor'); + self.upgradeable.upgrade(new_class_hash); } } } diff --git a/packages/contracts/resolver/src/resolver_ccip.cairo b/packages/contracts/resolver/src/resolver_ccip.cairo deleted file mode 100644 index d70e0faf7..000000000 --- a/packages/contracts/resolver/src/resolver_ccip.cairo +++ /dev/null @@ -1,214 +0,0 @@ - -#[starknet::contract] -mod Resolver { - use core::option::OptionTrait; - use core::traits::TryInto; - use core::array::SpanTrait; - use starknet::{ContractAddress, get_block_timestamp, storage::Map}; - use ecdsa::check_ecdsa_signature; - use resolver::interface::{IResolver, IResolverDispatcher, IResolverDispatcherTrait}; - use storage_read::{main::storage_read_component, interface::IStorageRead}; - use openzeppelin::access::ownable::OwnableComponent; - - component!(path: storage_read_component, storage: api_url, event: StorageReadEvent); - component!(path: OwnableComponent, storage: ownable, event: OwnableEvent); - - #[abi(embed_v0)] - impl StorageReadImpl = storage_read_component::StorageRead; - #[abi(embed_v0)] - impl OwnableImpl = OwnableComponent::OwnableImpl; - impl OwnableInternalImpl = OwnableComponent::InternalImpl; - - #[storage] - struct Storage { - public_key: felt252, - uri: Map<(felt252, felt252), felt252>, - #[substorage(v0)] - api_url: storage_read_component::Storage, - #[substorage(v0)] - ownable: OwnableComponent::Storage, - } - - #[event] - #[derive(Drop, starknet::Event)] - enum Event { - StarknetIDOffChainResolverUpdate: StarknetIDOffChainResolverUpdate, - #[flat] - StorageReadEvent: storage_read_component::Event, - #[flat] - OwnableEvent: OwnableComponent::Event, - } - - #[derive(Drop, starknet::Event)] - struct StarknetIDOffChainResolverUpdate { - uri_added: Span, - uri_removed: Span, - } - - - #[constructor] - fn constructor(ref self: ContractState, owner: ContractAddress, public_key: felt252) { - self.ownable.initializer(owner); - self.public_key.write(public_key); - } - - #[abi(embed_v0)] - impl ResolverImpl of IResolver { - fn resolve( - self: @ContractState, domain: Span, field: felt252, hint: Span - ) -> felt252 { - if hint.len() != 4 { - panic(self.get_error_array(array!['offchain_resolving'], domain)); - } - - let max_validity = *hint.at(3); - assert(get_block_timestamp() < max_validity.try_into().unwrap(), 'Signature expired'); - - let hashed_domain = self.hash_domain(domain); - let message_hash: felt252 = hash::LegacyHash::hash( - hash::LegacyHash::hash( - hash::LegacyHash::hash( - hash::LegacyHash::hash('ccip_demo resolving', max_validity), hashed_domain - ), - field - ), - *hint.at(0) - ); - - let public_key = self.public_key.read(); - let is_valid = check_ecdsa_signature( - message_hash, public_key, *hint.at(1), *hint.at(2) - ); - assert(is_valid, 'Invalid signature'); - - return *hint.at(0); - } - - fn get_uris(self: @ContractState) -> Array { - let mut res: Array = array![]; - let mut i: felt252 = 0; - loop { - if self.uri.read((i, 0)) == 0 { - break; - } - if self.uri.read((i, 0)) != 'this call was removed' { - // we get the next uri at index i - let mut new_uri = self.get_uri_at_index(i); - res.append(new_uri.len().into()); - loop { - match new_uri.pop_front() { - Option::Some(value) => { res.append(value); }, - Option::None => { break; } - } - }; - } - i += 1; - }; - res - } - - fn add_uri(ref self: ContractState, new_uri: Span) { - self.ownable.assert_only_owner(); - let mut i: felt252 = 0; - loop { - if self.uri.read((i, 0)) == 0 { - self - .emit( - StarknetIDOffChainResolverUpdate { - uri_added: new_uri, uri_removed: array![].span() - } - ); - self.store_uri(new_uri, i); - - break; - } - i += 1; - }; - } - - fn remove_uri(ref self: ContractState, index: felt252) { - self.ownable.assert_only_owner(); - let uri_removed = self.get_uri_at_index(index).span(); - self.emit(StarknetIDOffChainResolverUpdate { uri_added: array![].span(), uri_removed }); - self.uri.write((index, 0), 'this call was removed'); - } - } - - #[generate_trait] - impl InternalImpl of InternalTrait { - fn get_uri_at_index(self: @ContractState, index: felt252) -> Array { - let mut res = array![]; - let mut j: felt252 = 0; - loop { - let value = self.uri.read((index, j)); - if value == 0 { - break; - } - res.append(value); - j += 1; - }; - res - } - - fn store_uri(ref self: ContractState, mut uri: Span, index: felt252) { - let mut j = 0; - loop { - match uri.pop_front() { - Option::Some(value) => { - self.uri.write((index, j), *value); - j += 1; - }, - Option::None => { break; } - } - }; - } - - fn get_error_array( - self: @ContractState, mut res: Array, mut domain: Span - ) -> Array { - // append domain to error array - res.append(domain.len().into()); - loop { - match domain.pop_front() { - Option::Some(value) => { res.append(*value); }, - Option::None => { break; } - } - }; - // append uris to error array - self.append_uris(res) - } - - fn append_uris(self: @ContractState, mut res: Array) -> Array { - let mut i: felt252 = 0; - loop { - if self.uri.read((i, 0)) == 0 { - break; - } - if self.uri.read((i, 0)) != 'this call was removed' { - // we get the next uri at index i - let mut new_uri = self.get_uri_at_index(i); - res.append(new_uri.len().into()); - loop { - match new_uri.pop_front() { - Option::Some(value) => { res.append(value); }, - Option::None => { break; } - } - }; - } - i += 1; - }; - res - } - - fn hash_domain(self: @ContractState, domain: Span) -> felt252 { - if domain.len() == 0 { - return 0; - }; - let new_len = domain.len() - 1; - let x = *domain[new_len]; - let y = self.hash_domain(domain.slice(0, new_len)); - let hashed_domain = pedersen::pedersen(x, y); - return hashed_domain; - } - } -} \ No newline at end of file