Skip to content

Commit

Permalink
refactor: extract resolve_util to avoid duplicate hash computation
Browse files Browse the repository at this point in the history
  • Loading branch information
Th0rgal committed Oct 8, 2023
1 parent c19c35e commit 28f7d17
Showing 1 changed file with 68 additions and 53 deletions.
121 changes: 68 additions & 53 deletions src/naming/main.cairo
Original file line number Diff line number Diff line change
Expand Up @@ -139,45 +139,21 @@ mod Naming {
// naming.resolve(['alice'], 'bitcoin')
// Use it with caution in smartcontracts as it can call untrusted contracts
fn resolve(self: @ContractState, domain: Span<felt252>, field: felt252) -> felt252 {
let (resolver, parent_start) = self.domain_to_resolver(domain, 0);
if (resolver != ContractAddressZeroable::zero()) {
IResolverDispatcher { contract_address: resolver }
.resolve(domain.slice(parent_start, domain.len() - parent_start), field)
} else {
let domain_data = self._domain_data.read(self.hash_domain(domain));
// circuit breaker for root domain
if (domain.len() == 1) {
IIdentityDispatcher { contract_address: self.starknetid_contract.read() }
.get_crosschecked_user_data(domain_data.owner, field)
// handle reset subdomains
} else {
// todo: optimize by changing the hash definition from H(b, a) to H(a, b)
let parent_key = self
._domain_data
.read(self.hash_domain(domain.slice(1, domain.len() - 1)))
.key;

if parent_key == domain_data.parent_key {
IIdentityDispatcher { contract_address: self.starknetid_contract.read() }
.get_crosschecked_user_data(domain_data.owner, field)
} else {
0
}
}
}
let (_, value) = self.resolve_util(domain, field);
value
}

// This functions allows to resolve a domain to a native address. Its output is designed
// to be used as a parameter for other functions (for example if you want to send ERC20
// to a .stark)
fn domain_to_address(self: @ContractState, domain: Span<felt252>) -> ContractAddress {
// resolve must be performed first because it calls untrusted resolving contracts
let resolve_result = self.resolve(domain, 'starknet');
if resolve_result != 0 {
let addr: Option<ContractAddress> = resolve_result.try_into();
let (hashed_domain, value) = self.resolve_util(domain, 'starknet');
if value != 0 {
let addr: Option<ContractAddress> = value.try_into();
return addr.unwrap();
}
let data = self._domain_data.read(self.hash_domain(domain));
};
let data = self._domain_data.read(hashed_domain);
if data.address.into() != 0 {
if domain.len() != 1 {
let parent_key = self
Expand All @@ -186,10 +162,10 @@ mod Naming {
.key;
if parent_key == data.parent_key {
return data.address;
}
}
};
};
return data.address;
}
};
IIdentityDispatcher { contract_address: self.starknetid_contract.read() }
.owner_of(self.domain_to_id(domain))
}
Expand All @@ -209,15 +185,15 @@ mod Naming {
.key;
if parent_key != data.parent_key {
return 0;
}
}
};
};
data.owner
}

// This function allows to find which domain to use to display an account
fn address_to_domain(self: @ContractState, address: ContractAddress) -> Span<felt252> {
let mut domain = ArrayTrait::new();
self._address_to_domain_util(address, ref domain);
self.read_address_to_domain(address, ref domain);
if domain.len() != 0 && self.domain_to_address(domain.span()) == address {
domain.span()
} else {
Expand Down Expand Up @@ -430,7 +406,7 @@ mod Naming {
fn hash_domain(self: @ContractState, domain: Span<felt252>) -> 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));
Expand All @@ -445,7 +421,7 @@ mod Naming {
let domain_part = self._hash_to_domain.read((domain_hash, i));
if domain_part == 0 {
break;
}
};
domain.append(domain_part);
i += 1;
};
Expand Down Expand Up @@ -475,14 +451,14 @@ mod Naming {
self: @ContractState, domain: Span<felt252>, account: ContractAddress
) {
// 1. account owns the domain
self._assert_is_owner(domain, account);
self.assert_is_owner(domain, account);
// 2. check domain expiration
let hashed_root_domain = self.hash_domain(domain.slice(domain.len() - 1, 1));
let root_domain_data = self._domain_data.read(hashed_root_domain);
assert(get_block_timestamp() <= root_domain_data.expiry, 'this domain has expired');
}

fn _assert_is_owner(
fn assert_is_owner(
self: @ContractState, domain: Span<felt252>, account: ContractAddress
) -> u32 {
let hashed_domain = self.hash_domain(domain);
Expand All @@ -499,18 +475,18 @@ mod Naming {
// if caller owns the starknet id, he owns the domain, we return the key
if owner == account {
return data.key;
}
};

// 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 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);
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');
}
};
data.key
}

Expand All @@ -527,15 +503,15 @@ mod Naming {
);
}

fn _address_to_domain_util(
fn read_address_to_domain(
self: @ContractState, address: ContractAddress, ref domain: Array<felt252>
) -> usize {
let subdomain = self._address_to_domain.read((address, domain.len()));
if subdomain == 0 {
domain.len()
} else {
domain.append(subdomain);
self._address_to_domain_util(address, ref domain)
self.read_address_to_domain(address, ref domain)
}
}

Expand All @@ -545,11 +521,9 @@ mod Naming {
match domain.pop_back() {
Option::Some(domain_part) => {
self._address_to_domain.write((address, domain.len()), *domain_part);
return self.set_address_to_domain_util(address, domain);
self.set_address_to_domain_util(address, domain)
},
Option::None => {
return;
}
Option::None => {}
}
}

Expand All @@ -558,7 +532,7 @@ mod Naming {
) -> (ContractAddress, u32) {
if parent_start_id == domain.len() {
return (ContractAddressZeroable::zero(), 0);
}
};

// hashing parent_domain
let hashed_domain = self
Expand Down Expand Up @@ -648,7 +622,7 @@ mod Naming {
fn get_chars_len(self: @ContractState, domain: u256) -> usize {
if domain == (u256 { low: 0, high: 0 }) {
return 0;
}
};
// 38 = simple_alphabet_size
let (p, q, _) = u256_safe_divmod(domain, u256_as_non_zero(u256 { low: 38, high: 0 }));
if q == (u256 { low: 37, high: 0 }) {
Expand All @@ -658,9 +632,50 @@ mod Naming {
);
let next = self.get_chars_len(shifted_p);
return 1 + next;
}
};
let next = self.get_chars_len(p);
1 + next
}

// returns domain_hash (or zero) and its value for a specific field
fn resolve_util(
self: @ContractState, domain: Span<felt252>, field: felt252
) -> (felt252, felt252) {
let (resolver, parent_start) = self.domain_to_resolver(domain, 0);
if (resolver != ContractAddressZeroable::zero()) {
(
0,
IResolverDispatcher { contract_address: resolver }
.resolve(domain.slice(parent_start, domain.len() - parent_start), field)
)
} else {
let hashed_domain = self.hash_domain(domain);
let domain_data = self._domain_data.read(hashed_domain);
// circuit breaker for root domain
(
hashed_domain,
if (domain.len() == 1) {
IIdentityDispatcher { contract_address: self.starknetid_contract.read() }
.get_crosschecked_user_data(domain_data.owner, field)
// handle reset subdomains
} else {
// todo: optimize by changing the hash definition from H(b, a) to H(a, b)
let parent_key = self
._domain_data
.read(self.hash_domain(domain.slice(1, domain.len() - 1)))
.key;

if parent_key == domain_data.parent_key {
IIdentityDispatcher {
contract_address: self.starknetid_contract.read()
}
.get_crosschecked_user_data(domain_data.owner, field)
} else {
0
}
}
)
}
}
}
}

0 comments on commit 28f7d17

Please sign in to comment.