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

Fix/assert is owner #19

Merged
merged 3 commits into from
Jan 10, 2024
Merged
Show file tree
Hide file tree
Changes from 2 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
52 changes: 28 additions & 24 deletions src/naming/asserts.cairo
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
use core::traits::TryInto;
use core::array::SpanTrait;
use naming::{
interface::{
naming::{INaming, INamingDispatcher, INamingDispatcherTrait},
Expand Down Expand Up @@ -27,7 +29,7 @@ use openzeppelin::token::erc20::interface::{
};
use integer::{u256_safe_divmod, u256_as_non_zero};
use naming::naming::utils::UtilsTrait;

use debug::PrintTrait;
Copy link
Collaborator

Choose a reason for hiding this comment

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

can be removed


#[generate_trait]
impl AssertionsImpl of AssertionsTrait {
Expand Down Expand Up @@ -61,36 +63,38 @@ impl AssertionsImpl of AssertionsTrait {
assert(get_block_timestamp() <= root_domain_data.expiry, 'this domain has expired');
}

// ensures you own a domain or one of its parents
fn assert_is_owner(
self: @Naming::ContractState, domain: Span<felt252>, account: ContractAddress
) -> u32 {
let hashed_domain = self.hash_domain(domain);
let data = self._domain_data.read(hashed_domain);
) {
let mut i: felt252 = 1;
let stop = (domain.len() + 1).into();
let mut parent_key = 0;
loop {
assert(i != stop, 'you don\'t own this domain');
let i_gas_saver = i.try_into().unwrap();
let active_domain = domain.slice(domain.len() - i_gas_saver, i_gas_saver);
let hashed_domain = self.hash_domain(active_domain);
let data = self._domain_data.read(hashed_domain);

// because erc721 crashes on zero
let owner = if data.owner == 0 {
ContractAddressZeroable::zero()
} else {
IIdentityDispatcher { contract_address: self.starknetid_contract.read() }
.owner_from_id(data.owner)
};
assert(data.parent_key == parent_key, 'a parent domain was reset');

// if caller owns the starknet id, he owns the domain, we return the key
if owner == account {
return data.key;
};
// because erc721 crashes on zero
let owner = if data.owner == 0 {
ContractAddressZeroable::zero()
} else {
IIdentityDispatcher { contract_address: self.starknetid_contract.read() }
.owner_from_id(data.owner)
};

// otherwise, if it is a root domain, he doesn't own it
assert(domain.len() != 1 && domain.len() != 0, 'you don\'t own this domain');
// if caller owns the identity, he controls the domain and its children
if owner == account {
break;
};

// if he doesn't own the starknet id, and doesn't own the domain, he might own the parent domain
let parent_key = self.assert_is_owner(domain.slice(1, domain.len() - 1), account);
// we ensure that the key is the same as the parent key
// this is to allow to revoke all subdomains in o(1) writes, by juste updating the key of the parent
if (data.parent_key != 0) {
assert(parent_key == data.parent_key, 'you no longer own this domain');
parent_key = data.key;
i += 1;
};
data.key
}

// this ensures a non expired domain is not already written on this identity
Expand Down
58 changes: 57 additions & 1 deletion src/tests/naming/test_abuses.cairo
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,6 @@ use naming::naming::main::Naming;
use naming::pricing::Pricing;
use super::common::deploy;


#[test]
#[available_gas(2000000000)]
#[should_panic(expected: ('u256_sub Overflow', 'ENTRYPOINT_FAILED', 'ENTRYPOINT_FAILED'))]
Expand Down Expand Up @@ -243,3 +242,60 @@ fn test_non_admin_cannot_claim_balance() {
naming.claim_balance(eth.contract_address);
}

#[test]
#[available_gas(2000000000)]
#[should_panic(expected: ('a parent domain was reset', 'ENTRYPOINT_FAILED'))]
fn test_use_reset_subdomains() {
// setup
let (eth, pricing, identity, naming) = deploy();
let alpha = contract_address_const::<0x123>();
let bravo = contract_address_const::<0x456>();

// we mint the ids

set_contract_address(alpha);
identity.mint(1);
set_contract_address(bravo);
identity.mint(2);

set_contract_address(alpha);
let aller: felt252 = 35683102;

// we check how much a domain costs
let (_, price) = pricing.compute_buy_price(5, 365);

// we allow the naming to take our money
eth.approve(naming.contract_address, price);

// we buy with no resolver, no sponsor, no discount and empty metadata
naming
.buy(1, aller, 365, ContractAddressZeroable::zero(), ContractAddressZeroable::zero(), 0, 0);

let root_domain = array![aller].span();
let subdomain = array![aller, aller].span();

// we transfer aller.aller.stark to id2
naming.transfer_domain(subdomain, 2);

// and make sure the owner has been updated
assert(naming.domain_to_id(subdomain) == 2, 'owner not updated correctly');

// now bravo should be able to create a subsubdomain (charlie.aller.aller.stark):
set_contract_address(bravo);
let subsubdomain = array!['charlie', aller, aller].span();
naming.transfer_domain(subsubdomain, 3);

// alpha resets subdomains of aller.stark
set_contract_address(alpha);
naming.reset_subdomains(root_domain);

// ensure aller.stark still resolves
assert(naming.domain_to_id(root_domain) == 1, 'owner not updated correctly');
// ensure the subdomain was reset
assert(naming.domain_to_id(subdomain) == 0, 'owner not updated correctly');

set_contract_address(bravo);
let subsubdomain2 = array!['delta', aller, aller].span();
naming.transfer_domain(subsubdomain2, 4);
}