Skip to content

Commit

Permalink
fix selector functions for models and contracts (#2156)
Browse files Browse the repository at this point in the history
  • Loading branch information
remybar authored Jul 8, 2024
1 parent f9256ea commit 7c09e4a
Show file tree
Hide file tree
Showing 87 changed files with 1,763 additions and 1,052 deletions.
12 changes: 6 additions & 6 deletions crates/dojo-core/src/base_test.cairo
Original file line number Diff line number Diff line change
Expand Up @@ -112,7 +112,7 @@ trait IMetadataOnly<T> {
fn selector(self: @T) -> felt252;
fn name(self: @T) -> ByteArray;
fn namespace(self: @T) -> ByteArray;
fn namespace_selector(self: @T) -> felt252;
fn namespace_hash(self: @T) -> felt252;
}

#[starknet::contract]
Expand All @@ -131,7 +131,7 @@ mod invalid_legacy_model {
"dojo"
}

fn namespace_selector(self: @ContractState) -> felt252 {
fn namespace_hash(self: @ContractState) -> felt252 {
dojo::utils::hash(@Self::namespace(self))
}

Expand All @@ -158,7 +158,7 @@ mod invalid_legacy_model_world {
"dojo"
}

fn namespace_selector(self: @ContractState) -> felt252 {
fn namespace_hash(self: @ContractState) -> felt252 {
dojo::utils::hash(@Self::namespace(self))
}

Expand All @@ -178,14 +178,14 @@ mod invalid_model {
fn selector(self: @ContractState) -> felt252 {
// NOTE: Need to update this value if address changes
// Pre-computed address of a contract deployed through the world.
0x1130142d6bff3c9cb891a270922a4c6dbf4c222b675cdc1341905b17341e3ab
0x21b19f95ff0f382a069dc7034f95584b300133665ee506789c76ba729e42b66
}

fn namespace(self: @ContractState) -> ByteArray {
"dojo"
}

fn namespace_selector(self: @ContractState) -> felt252 {
fn namespace_hash(self: @ContractState) -> felt252 {
dojo::utils::hash(@Self::namespace(self))
}

Expand All @@ -212,7 +212,7 @@ mod invalid_model_world {
"dojo"
}

fn namespace_selector(self: @ContractState) -> felt252 {
fn namespace_hash(self: @ContractState) -> felt252 {
dojo::utils::hash(@Self::namespace(self))
}

Expand Down
13 changes: 4 additions & 9 deletions crates/dojo-core/src/contract.cairo
Original file line number Diff line number Diff line change
@@ -1,15 +1,10 @@
#[starknet::interface]
trait IContract<T> {
fn contract_name(self: @T) -> ByteArray;
fn selector(self: @T) -> felt252;

/// Returns the namespace of a contract.
fn namespace(self: @T) -> ByteArray;

// Returns the namespace selector built from its name.
/// namespace_selector = hash(namespace_name)
fn namespace_selector(self: @T) -> felt252;

// Returns the contract tag
fn tag(self: @T) -> ByteArray;

fn name_hash(self: @T) -> felt252;
fn namespace_hash(self: @T) -> felt252;
fn selector(self: @T) -> felt252;
}
32 changes: 17 additions & 15 deletions crates/dojo-core/src/model.cairo
Original file line number Diff line number Diff line change
Expand Up @@ -8,22 +8,22 @@ trait Model<T> {

/// Returns the name of the model as it was written in Cairo code.
fn name() -> ByteArray;

/// Returns the namespace of the model as it was written in the `dojo::model` attribute.
fn namespace() -> ByteArray;

// Returns the model tag which combines the namespace and the name.
fn tag() -> ByteArray;

fn version() -> u8;

/// Returns the model selector built from its name and its namespace.
/// model selector = hash(hash(namespace_name), hash(model_name))
/// model selector = hash(namespace_hash, model_hash)
fn selector() -> felt252;
fn instance_selector(self: @T) -> felt252;

/// Returns the namespace of the model as it was written in the `dojo::model` attribute.
fn namespace() -> ByteArray;

/// Returns the model namespace selector built from its namespace.
/// namespace_selector = hash(namespace_name)
fn namespace_selector() -> felt252;

// Returns the model tag
fn tag() -> ByteArray;
fn name_hash() -> felt252;
fn namespace_hash() -> felt252;

fn keys(self: @T) -> Span<felt252>;
fn values(self: @T) -> Span<felt252>;
Expand All @@ -34,12 +34,14 @@ trait Model<T> {

#[starknet::interface]
trait IModel<T> {
fn selector(self: @T) -> felt252;
fn name(self: @T) -> ByteArray;
fn version(self: @T) -> u8;
fn namespace(self: @T) -> ByteArray;
fn namespace_selector(self: @T) -> felt252;
fn tag(self: @T) -> ByteArray;
fn version(self: @T) -> u8;

fn selector(self: @T) -> felt252;
fn name_hash(self: @T) -> felt252;
fn namespace_hash(self: @T) -> felt252;
fn unpacked_size(self: @T) -> Option<usize>;
fn packed_size(self: @T) -> Option<usize>;
fn layout(self: @T) -> dojo::database::introspect::Layout;
Expand All @@ -63,6 +65,6 @@ fn deploy_and_get_metadata(
let name = model.name();
let selector = model.selector();
let namespace = model.namespace();
let namespace_selector = model.namespace_selector();
Result::Ok((contract_address, name, selector, namespace, namespace_selector))
let namespace_hash = model.namespace_hash();
Result::Ok((contract_address, name, selector, namespace, namespace_hash))
}
22 changes: 12 additions & 10 deletions crates/dojo-core/src/resource_metadata.cairo
Original file line number Diff line number Diff line change
Expand Up @@ -50,35 +50,37 @@ impl ResourceMetadataModel of dojo::model::Model<ResourceMetadata> {
"ResourceMetadata"
}

fn namespace() -> ByteArray {
"__DOJO__"
}

fn tag() -> ByteArray {
"__DOJO__-ResourceMetadata"
}

#[inline(always)]
fn version() -> u8 {
1
}

#[inline(always)]
fn selector() -> felt252 {
poseidon::poseidon_hash_span(
array![Self::namespace_selector(), dojo::utils::hash(@Self::name())].span()
)
poseidon::poseidon_hash_span(array![Self::namespace_hash(), Self::name_hash()].span())
}

#[inline(always)]
fn instance_selector(self: @ResourceMetadata) -> felt252 {
Self::selector()
}

fn namespace() -> ByteArray {
"__DOJO__"
fn name_hash() -> felt252 {
dojo::utils::hash(@Self::name())
}

fn namespace_selector() -> felt252 {
fn namespace_hash() -> felt252 {
dojo::utils::hash(@Self::namespace())
}

fn tag() -> ByteArray {
"__DOJO__:ResourceMetadata"
}

#[inline(always)]
fn keys(self: @ResourceMetadata) -> Span<felt252> {
let mut serialized = core::array::ArrayTrait::new();
Expand Down
4 changes: 2 additions & 2 deletions crates/dojo-core/src/utils_test.cairo
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ fn test_hash_computation() {
// Be sure that the namespace hash computed in `dojo-lang` in Rust is equal
// to the one computed in Cairo by dojo::utils:hash
let namespace = dojo::model::Model::<MyModel>::namespace();
let namespace_selector = dojo::model::Model::<MyModel>::namespace_selector();
let namespace_hash = dojo::model::Model::<MyModel>::namespace_hash();

assert(dojo::utils::hash(@namespace) == namespace_selector, 'invalid computed hash');
assert(dojo::utils::hash(@namespace) == namespace_hash, 'invalid computed hash');
}
22 changes: 9 additions & 13 deletions crates/dojo-core/src/world.cairo
Original file line number Diff line number Diff line change
Expand Up @@ -526,7 +526,7 @@ mod world {
let caller = get_caller_address();

let salt = self.models_count.read();
let (address, name, selector, namespace, namespace_selector) =
let (address, name, selector, namespace, namespace_hash) =
dojo::model::deploy_and_get_metadata(
salt.into(), class_hash
)
Expand All @@ -538,11 +538,9 @@ mod world {
starknet::contract_address::ContractAddressZeroable::zero(),
);

assert(self._is_namespace_registered(namespace_hash), Errors::NAMESPACE_NOT_REGISTERED);
assert(
self._is_namespace_registered(namespace_selector), Errors::NAMESPACE_NOT_REGISTERED
);
assert(
self.can_write_namespace(namespace_selector, get_caller_address()),
self.can_write_namespace(namespace_hash, get_caller_address()),
Errors::NO_NAMESPACE_WRITE_ACCESS
);

Expand Down Expand Up @@ -653,12 +651,10 @@ mod world {
let dispatcher = IContractDispatcher { contract_address };
let namespace = dispatcher.namespace();
let name = dispatcher.contract_name();
let namespace_selector = dispatcher.namespace_selector();
assert(
self._is_namespace_registered(namespace_selector), Errors::NAMESPACE_NOT_REGISTERED
);
let namespace_hash = dispatcher.namespace_hash();
assert(self._is_namespace_registered(namespace_hash), Errors::NAMESPACE_NOT_REGISTERED);
assert(
self.can_write_namespace(namespace_selector, get_caller_address()),
self.can_write_namespace(namespace_hash, get_caller_address()),
Errors::NO_NAMESPACE_WRITE_ACCESS
);

Expand Down Expand Up @@ -931,9 +927,9 @@ mod world {

/// Indicates if the provided namespace is already registered
#[inline(always)]
fn _is_namespace_registered(self: @ContractState, namespace_selector: felt252) -> bool {
fn _is_namespace_registered(self: @ContractState, namespace_hash: felt252) -> bool {
// TODO: use match self.resources... directly when https://github.com/starkware-libs/cairo/pull/5743 fixed
let resource: ResourceData = self.resources.read(namespace_selector);
let resource: ResourceData = self.resources.read(namespace_hash);
match resource {
ResourceData::Namespace => true,
_ => false
Expand Down Expand Up @@ -965,7 +961,7 @@ mod world {
&& !self.is_account_owner(model_id)
&& !self.is_account_writer(model_id) {
let model = IModelDispatcher { contract_address: model_address };
self._check_basic_write_access(model.namespace_selector(), contract)
self._check_basic_write_access(model.namespace_hash(), contract)
} else {
true
}
Expand Down
12 changes: 6 additions & 6 deletions crates/dojo-core/src/world_test.cairo
Original file line number Diff line number Diff line change
Expand Up @@ -260,7 +260,7 @@ trait IMetadataOnly<T> {
fn selector(self: @T) -> felt252;
fn name(self: @T) -> ByteArray;
fn namespace(self: @T) -> ByteArray;
fn namespace_selector(self: @T) -> felt252;
fn namespace_hash(self: @T) -> felt252;
}

#[starknet::contract]
Expand All @@ -278,7 +278,7 @@ mod resource_metadata_malicious {
"dojo"
}

fn namespace_selector(self: @ContractState) -> felt252 {
fn namespace_hash(self: @ContractState) -> felt252 {
dojo::utils::hash(@Self::namespace(self))
}

Expand Down Expand Up @@ -1493,7 +1493,7 @@ fn test_write_model_for_namespace_owner() {
let contract = starknet::contract_address_const::<0xdeadbeef>();

// the caller account is a model namespace owner
world.grant_owner(account, dojo::model::Model::<Foo>::namespace_selector());
world.grant_owner(account, dojo::model::Model::<Foo>::namespace_hash());
starknet::testing::set_account_contract_address(account);
starknet::testing::set_contract_address(contract);

Expand Down Expand Up @@ -1524,7 +1524,7 @@ fn test_write_model_for_namespace_writer() {
let account = starknet::contract_address_const::<0xb0b>();
let contract = starknet::contract_address_const::<0xdeadbeef>();

world.grant_writer(dojo::model::Model::<Foo>::namespace_selector(), contract);
world.grant_writer(dojo::model::Model::<Foo>::namespace_hash(), contract);

// the account does not own anything
starknet::testing::set_account_contract_address(account);
Expand Down Expand Up @@ -1557,7 +1557,7 @@ fn test_write_namespace_for_namespace_owner() {
let account = starknet::contract_address_const::<0xb0b>();
let contract = starknet::contract_address_const::<0xdeadbeef>();

world.grant_owner(account, dojo::model::Model::<Foo>::namespace_selector());
world.grant_owner(account, dojo::model::Model::<Foo>::namespace_hash());

// the account owns the Foo model namespace so it should be able to deploy
// and register the model.
Expand All @@ -1574,7 +1574,7 @@ fn test_write_namespace_for_namespace_writer() {
let account = starknet::contract_address_const::<0xb0b>();
let contract = starknet::contract_address_const::<0xdeadbeef>();

world.grant_writer(dojo::model::Model::<Foo>::namespace_selector(), account);
world.grant_writer(dojo::model::Model::<Foo>::namespace_hash(), account);

// the account has write access to the Foo model namespace so it should be able
// to deploy and register the model.
Expand Down
41 changes: 26 additions & 15 deletions crates/dojo-lang/src/contract.rs
Original file line number Diff line number Diff line change
Expand Up @@ -73,8 +73,10 @@ impl DojoContract {
}

let contract_tag = naming::get_tag(&contract_namespace, &name);
let contract_name_selector = naming::compute_bytearray_hash(&name);
let contract_namespace_selector = naming::compute_bytearray_hash(&contract_namespace);
let contract_name_hash = naming::compute_bytearray_hash(&name);
let contract_namespace_hash = naming::compute_bytearray_hash(&contract_namespace);
let contract_selector =
naming::compute_selector_from_hashes(contract_namespace_hash, contract_name_hash);

if let MaybeModuleBody::Some(body) = module_ast.body(db) {
let mut body_nodes: Vec<_> = body
Expand Down Expand Up @@ -165,21 +167,26 @@ impl DojoContract {
fn contract_name(self: @ContractState) -> ByteArray {
\"$name$\"
}
fn selector(self: @ContractState) -> felt252 {
$contract_name_selector$
}
fn namespace(self: @ContractState) -> ByteArray {
\"$contract_namespace$\"
}
fn namespace_selector(self: @ContractState) -> felt252 {
$contract_namespace_selector$
}
fn tag(self: @ContractState) -> ByteArray {
\"$contract_tag$\"
}
fn name_hash(self: @ContractState) -> felt252 {
$contract_name_hash$
}
fn namespace_hash(self: @ContractState) -> felt252 {
$contract_namespace_hash$
}
fn selector(self: @ContractState) -> felt252 {
$contract_selector$
}
}
#[abi(embed_v0)]
Expand All @@ -198,18 +205,22 @@ impl DojoContract {
",
&UnorderedHashMap::from([
("name".to_string(), RewriteNode::Text(name.to_string())),
(
"contract_name_selector".to_string(),
RewriteNode::Text(contract_name_selector.to_string()),
),
("body".to_string(), RewriteNode::new_modified(body_nodes)),
(
"contract_namespace".to_string(),
RewriteNode::Text(contract_namespace.clone()),
),
(
"contract_namespace_selector".to_string(),
RewriteNode::Text(contract_namespace_selector.to_string()),
"contract_name_hash".to_string(),
RewriteNode::Text(contract_name_hash.to_string()),
),
(
"contract_namespace_hash".to_string(),
RewriteNode::Text(contract_namespace_hash.to_string()),
),
(
"contract_selector".to_string(),
RewriteNode::Text(contract_selector.to_string()),
),
("contract_tag".to_string(), RewriteNode::Text(contract_tag)),
]),
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
kind = "Class"
class_hash = "0x21341ba6e0d96f909f15c4ed865188afc716563d34593fb443d56cb97ff7b92"
original_class_hash = "0x21341ba6e0d96f909f15c4ed865188afc716563d34593fb443d56cb97ff7b92"
class_hash = "0x210dd8e484e5555dc74a4a600b17878fc108911719b04139309acc874160c51"
original_class_hash = "0x210dd8e484e5555dc74a4a600b17878fc108911719b04139309acc874160c51"
abi = "manifests/dev/base/abis/dojo-world.json"
tag = "dojo-world"
manifest_name = "dojo-world"
Loading

0 comments on commit 7c09e4a

Please sign in to comment.