From 4a0e3d29f556ffeb6912eef48710219b550c07a2 Mon Sep 17 00:00:00 2001 From: Tarrence van As Date: Fri, 5 Jan 2024 16:53:15 -0500 Subject: [PATCH] Update model register and contract deploy apis to take preimage --- Cargo.lock | 2 +- crates/benches/contracts/src/actions.cairo | 1 - crates/dojo-core/src/base_test.cairo | 4 +- crates/dojo-core/src/database_test.cairo | 1 - crates/dojo-core/src/model.cairo | 4 +- crates/dojo-core/src/test_utils.cairo | 3 +- crates/dojo-core/src/world.cairo | 79 +++++++------------ crates/dojo-core/src/world_test.cairo | 34 ++++---- crates/dojo-lang/src/inline_macros/delete.rs | 2 +- crates/dojo-lang/src/inline_macros/set.rs | 2 +- .../src/manifest_test_data/cairo_v240 | 18 +++-- .../dojo-lang/src/manifest_test_data/manifest | 26 +++--- crates/dojo-lang/src/model.rs | 40 +++++----- crates/dojo-lang/src/plugin_test_data/model | 24 +++--- crates/dojo-lang/src/semantics/test_data/set | 2 +- crates/dojo-world/src/contracts/abi/world.rs | 16 ++-- .../dojo-world/src/contracts/cairo_utils.rs | 23 ++++-- crates/dojo-world/src/contracts/model_test.rs | 2 +- crates/dojo-world/src/contracts/world_test.rs | 10 ++- crates/dojo-world/src/migration/strategy.rs | 22 ++---- crates/sozo/src/commands/register.rs | 34 +++++++- crates/sozo/src/ops/execute.rs | 4 +- crates/sozo/src/ops/migration/mod.rs | 10 ++- crates/sozo/src/ops/register.rs | 8 +- 24 files changed, 197 insertions(+), 174 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 9403e074b1..468168b43e 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2827,7 +2827,7 @@ dependencies = [ [[package]] name = "dojo-world-abigen" -version = "0.1.0" +version = "0.4.4" dependencies = [ "cairo-lang-starknet", "camino", diff --git a/crates/benches/contracts/src/actions.cairo b/crates/benches/contracts/src/actions.cairo index 991aa58998..2a48f98b57 100644 --- a/crates/benches/contracts/src/actions.cairo +++ b/crates/benches/contracts/src/actions.cairo @@ -67,7 +67,6 @@ mod actions { name: felt252, } - #[external(v0)] impl ActionsImpl of IActions { fn spawn(self: @ContractState) { diff --git a/crates/dojo-core/src/base_test.cairo b/crates/dojo-core/src/base_test.cairo index 2a86667870..2d350e90f5 100644 --- a/crates/dojo-core/src/base_test.cairo +++ b/crates/dojo-core/src/base_test.cairo @@ -41,7 +41,7 @@ fn deploy_world() -> IWorldDispatcher { fn test_upgrade_from_world() { let world = deploy_world(); - let base_address = world.deploy_contract('salt', base::TEST_CLASS_HASH.try_into().unwrap()); + let base_address = world.deploy_contract(array!['base'].span(), base::TEST_CLASS_HASH.try_into().unwrap()); let new_class_hash: ClassHash = contract_upgrade::TEST_CLASS_HASH.try_into().unwrap(); world.upgrade_contract(base_address, new_class_hash); @@ -56,7 +56,7 @@ fn test_upgrade_from_world() { fn test_upgrade_direct() { let world = deploy_world(); - let base_address = world.deploy_contract('salt', base::TEST_CLASS_HASH.try_into().unwrap()); + let base_address = world.deploy_contract(array!['base'].span(), base::TEST_CLASS_HASH.try_into().unwrap()); let new_class_hash: ClassHash = contract_upgrade::TEST_CLASS_HASH.try_into().unwrap(); let upgradeable_dispatcher = IUpgradeableDispatcher { contract_address: base_address }; diff --git a/crates/dojo-core/src/database_test.cairo b/crates/dojo-core/src/database_test.cairo index c1f760bb06..4daa33abea 100644 --- a/crates/dojo-core/src/database_test.cairo +++ b/crates/dojo-core/src/database_test.cairo @@ -8,7 +8,6 @@ use traits::{Into, TryInto}; use starknet::syscalls::deploy_syscall; use starknet::class_hash::{Felt252TryIntoClassHash, ClassHash}; use dojo::world::{IWorldDispatcher}; -use dojo::executor::executor; use dojo::database::{get, set, set_with_index, del, scan}; use dojo::database::index::WhereCondition; diff --git a/crates/dojo-core/src/model.cairo b/crates/dojo-core/src/model.cairo index f8657cc666..4819d66b67 100644 --- a/crates/dojo-core/src/model.cairo +++ b/crates/dojo-core/src/model.cairo @@ -1,5 +1,5 @@ trait Model { - fn name(self: @T) -> felt252; + fn name_hash(self: @T) -> felt252; fn keys(self: @T) -> Span; fn values(self: @T) -> Span; fn layout(self: @T) -> Span; @@ -8,7 +8,7 @@ trait Model { #[starknet::interface] trait IModel { - fn name(self: @T) -> felt252; + fn name_hash(self: @T) -> felt252; fn layout(self: @T) -> Span; fn schema(self: @T) -> Span; } diff --git a/crates/dojo-core/src/test_utils.cairo b/crates/dojo-core/src/test_utils.cairo index 8cc2bec88d..8c676e3a49 100644 --- a/crates/dojo-core/src/test_utils.cairo +++ b/crates/dojo-core/src/test_utils.cairo @@ -47,6 +47,7 @@ fn spawn_test_world(models: Array) -> IWorldDispatcher { executor::TEST_CLASS_HASH.try_into().unwrap(), 0, constructor_calldata.span(), false ) .unwrap(); + // deploy world let (world_address, _) = deploy_syscall( world::TEST_CLASS_HASH.try_into().unwrap(), @@ -63,7 +64,7 @@ fn spawn_test_world(models: Array) -> IWorldDispatcher { if index == models.len() { break (); } - world.register_model((*models[index]).try_into().unwrap()); + world.register_model(array![index.into()].span(), (*models[index]).try_into().unwrap()); index += 1; }; diff --git a/crates/dojo-core/src/world.cairo b/crates/dojo-core/src/world.cairo index 1271a3a621..b21e9943aa 100644 --- a/crates/dojo-core/src/world.cairo +++ b/crates/dojo-core/src/world.cairo @@ -6,9 +6,9 @@ use option::OptionTrait; trait IWorld { fn metadata_uri(self: @T, resource: felt252) -> Span; fn set_metadata_uri(ref self: T, resource: felt252, uri: Span); - fn model(self: @T, name: felt252) -> ClassHash; - fn register_model(ref self: T, class_hash: ClassHash); - fn deploy_contract(ref self: T, salt: felt252, class_hash: ClassHash) -> ContractAddress; + fn model(self: @T, name_hash: felt252) -> ClassHash; + fn register_model(ref self: T, name: Span, class_hash: ClassHash); + fn deploy_contract(ref self: T, name: Span, class_hash: ClassHash) -> ContractAddress; fn upgrade_contract(ref self: T, address: ContractAddress, class_hash: ClassHash) -> ClassHash; fn uuid(ref self: T) -> usize; fn emit(self: @T, keys: Array, values: Span); @@ -47,7 +47,7 @@ trait IWorld { #[starknet::interface] trait IUpgradeableWorld { - fn upgrade(ref self: T, new_class_hash : ClassHash); + fn upgrade(ref self: T, new_class_hash: ClassHash); } #[starknet::interface] @@ -70,19 +70,16 @@ mod world { use starknet::{ get_caller_address, get_contract_address, get_tx_info, contract_address::ContractAddressIntoFelt252, ClassHash, Zeroable, ContractAddress, - syscalls::{deploy_syscall, emit_event_syscall, replace_class_syscall}, SyscallResult, SyscallResultTrait, - SyscallResultTraitImpl + syscalls::{deploy_syscall, emit_event_syscall, replace_class_syscall}, SyscallResult, + SyscallResultTrait, SyscallResultTraitImpl }; use dojo::database; use dojo::database::index::WhereCondition; use dojo::executor::{IExecutorDispatcher, IExecutorDispatcherTrait}; use dojo::world::{IWorldDispatcher, IWorld, IUpgradeableWorld}; - - use dojo::components::upgradeable::{IUpgradeableDispatcher, IUpgradeableDispatcherTrait}; - const NAME_ENTRYPOINT: felt252 = - 0x0361458367e696363fbcc70777d07ebbd2394e89fd0adcaf147faccd1d294d60; + use dojo::components::upgradeable::{IUpgradeableDispatcher, IUpgradeableDispatcherTrait}; const WORLD: felt252 = 0; @@ -112,10 +109,10 @@ mod world { struct WorldUpgraded { class_hash: ClassHash, } - + #[derive(Drop, starknet::Event)] struct ContractDeployed { - salt: felt252, + name: Span, class_hash: ClassHash, address: ContractAddress, } @@ -134,7 +131,7 @@ mod world { #[derive(Drop, starknet::Event)] struct ModelRegistered { - name: felt252, + name: Span, class_hash: ClassHash, prev_class_hash: ClassHash } @@ -173,7 +170,6 @@ mod world { prev_address: ContractAddress, } - #[storage] struct Storage { executor_dispatcher: IExecutorDispatcher, @@ -195,24 +191,6 @@ mod world { EventEmitter::emit(ref self, WorldSpawned { address: get_contract_address(), creator }); } - /// Call Helper, - /// Call the provided `entrypoint` method on the given `class_hash`. - /// - /// # Arguments - /// - /// * `class_hash` - Class Hash to call. - /// * `entrypoint` - Entrypoint to call. - /// * `calldata` - The calldata to pass. - /// - /// # Returns - /// - /// The return value of the call. - fn class_call( - self: @ContractState, class_hash: ClassHash, entrypoint: felt252, calldata: Span - ) -> Span { - self.executor_dispatcher.read().call(class_hash, entrypoint, calldata) - } - #[external(v0)] impl World of IWorld { /// Returns the metadata URI of the world. @@ -372,22 +350,21 @@ mod world { /// # Arguments /// /// * `class_hash` - The class hash of the model to be registered. - fn register_model(ref self: ContractState, class_hash: ClassHash) { + fn register_model(ref self: ContractState, name: Span, class_hash: ClassHash) { let caller = get_caller_address(); - let calldata = ArrayTrait::new(); - let name = *class_call(@self, class_hash, NAME_ENTRYPOINT, calldata.span())[0]; + let name_hash = poseidon::poseidon_hash_span(name); let mut prev_class_hash = starknet::class_hash::ClassHashZeroable::zero(); // If model is already registered, validate permission to update. - let current_class_hash = self.models.read(name); + let current_class_hash = self.models.read(name_hash); if current_class_hash.is_non_zero() { - assert(self.is_owner(caller, name), 'only owner can update'); + assert(self.is_owner(caller, name_hash), 'only owner can update'); prev_class_hash = current_class_hash; } else { - self.owners.write((name, caller), true); + self.owners.write((name_hash, caller), true); }; - self.models.write(name, class_hash); + self.models.write(name_hash, class_hash); EventEmitter::emit(ref self, ModelRegistered { name, class_hash, prev_class_hash }); } @@ -395,13 +372,13 @@ mod world { /// /// # Arguments /// - /// * `name` - The name of the model. + /// * `name_hash` - The has of the model name. /// /// # Returns /// /// * `ClassHash` - The class hash of the model. - fn model(self: @ContractState, name: felt252) -> ClassHash { - self.models.read(name) + fn model(self: @ContractState, name_hash: felt252) -> ClassHash { + self.models.read(name_hash) } /// Deploys a contract associated with the world. @@ -415,10 +392,11 @@ mod world { /// /// * `ContractAddress` - The address of the newly deployed contract. fn deploy_contract( - ref self: ContractState, salt: felt252, class_hash: ClassHash + ref self: ContractState, name: Span, class_hash: ClassHash ) -> ContractAddress { + let name_hash = poseidon::poseidon_hash_span(name); let (contract_address, _) = deploy_syscall( - self.contract_base.read(), salt, array![].span(), false + self.contract_base.read(), name_hash, array![].span(), false ) .unwrap_syscall(); let upgradeable_dispatcher = IUpgradeableDispatcher { contract_address }; @@ -427,7 +405,7 @@ mod world { self.owners.write((contract_address.into(), get_caller_address()), true); EventEmitter::emit( - ref self, ContractDeployed { salt, class_hash, address: contract_address } + ref self, ContractDeployed { name, class_hash, address: contract_address } ); contract_address @@ -641,17 +619,18 @@ mod world { /// # Arguments /// /// * `new_class_hash` - The new world class hash. - fn upgrade(ref self: ContractState, new_class_hash : ClassHash){ + fn upgrade(ref self: ContractState, new_class_hash: ClassHash) { assert(new_class_hash.is_non_zero(), 'invalid class_hash'); - assert(IWorld::is_owner(@self, get_tx_info().unbox().account_contract_address, WORLD), 'only owner can upgrade'); + assert( + IWorld::is_owner(@self, get_tx_info().unbox().account_contract_address, WORLD), + 'only owner can upgrade' + ); // upgrade to new_class_hash replace_class_syscall(new_class_hash).unwrap(); // emit Upgrade Event - EventEmitter::emit( - ref self, WorldUpgraded {class_hash: new_class_hash } - ); + EventEmitter::emit(ref self, WorldUpgraded { class_hash: new_class_hash }); } } diff --git a/crates/dojo-core/src/world_test.cairo b/crates/dojo-core/src/world_test.cairo index 07bb4e1e9b..3149486e3c 100644 --- a/crates/dojo-core/src/world_test.cairo +++ b/crates/dojo-core/src/world_test.cairo @@ -8,8 +8,10 @@ use starknet::{contract_address_const, ContractAddress, ClassHash, get_caller_ad use starknet::syscalls::deploy_syscall; use dojo::benchmarks; -use dojo::executor::executor; -use dojo::world::{IWorldDispatcher, IWorldDispatcherTrait, world, IUpgradeableWorld, IUpgradeableWorldDispatcher, IUpgradeableWorldDispatcherTrait }; +use dojo::world::{ + IWorldDispatcher, IWorldDispatcherTrait, world, IUpgradeableWorld, IUpgradeableWorldDispatcher, + IUpgradeableWorldDispatcherTrait +}; use dojo::database::introspect::Introspect; use dojo::test_utils::{spawn_test_world, deploy_with_world_address}; use dojo::benchmarks::{Character, end}; @@ -115,7 +117,7 @@ mod bar { fn deploy_world_and_bar() -> (IWorldDispatcher, IbarDispatcher) { // Spawn empty world let world = deploy_world(); - world.register_model(foo::TEST_CLASS_HASH.try_into().unwrap()); + world.register_model(array!['foo'].span(), foo::TEST_CLASS_HASH.try_into().unwrap()); // System contract let bar_contract = IbarDispatcher { @@ -129,7 +131,7 @@ fn deploy_world_and_bar() -> (IWorldDispatcher, IbarDispatcher) { #[available_gas(2000000)] fn test_model() { let world = deploy_world(); - world.register_model(foo::TEST_CLASS_HASH.try_into().unwrap()); + world.register_model(array!['foo'].span(), foo::TEST_CLASS_HASH.try_into().unwrap()); } #[test] @@ -167,9 +169,10 @@ fn test_delete() { #[available_gas(6000000)] fn test_model_class_hash_getter() { let world = deploy_world(); - world.register_model(foo::TEST_CLASS_HASH.try_into().unwrap()); + let name = array!['foo'].span(); + world.register_model(name, foo::TEST_CLASS_HASH.try_into().unwrap()); - let foo = world.model('Foo'); + let foo = world.model(poseidon::poseidon_hash_span(name)); assert(foo == foo::TEST_CLASS_HASH.try_into().unwrap(), 'foo does not exists'); } @@ -212,7 +215,7 @@ fn test_set_entity_unauthorized() { contract_address: deploy_with_world_address(bar::TEST_CLASS_HASH, world) }; - world.register_model(foo::TEST_CLASS_HASH.try_into().unwrap()); + world.register_model(array!['foo'].span(), foo::TEST_CLASS_HASH.try_into().unwrap()); let caller = starknet::contract_address_const::<0x1337>(); starknet::testing::set_account_contract_address(caller); @@ -502,10 +505,10 @@ mod worldupgrade { struct Storage { world: IWorldDispatcher, } - + #[external(v0)] impl IWorldUpgradeImpl of super::IWorldUpgrade { - fn hello(self: @ContractState) -> felt252{ + fn hello(self: @ContractState) -> felt252 { 'dojo' } } @@ -515,7 +518,6 @@ mod worldupgrade { #[test] #[available_gas(60000000)] fn test_upgradeable_world() { - // Deploy world contract let world = deploy_world(); @@ -524,18 +526,15 @@ fn test_upgradeable_world() { }; upgradeable_world_dispatcher.upgrade(worldupgrade::TEST_CLASS_HASH.try_into().unwrap()); - let res = (IWorldUpgradeDispatcher { - contract_address: world.contract_address - }).hello(); + let res = (IWorldUpgradeDispatcher { contract_address: world.contract_address }).hello(); assert(res == 'dojo', 'should return dojo'); } #[test] #[available_gas(60000000)] -#[should_panic(expected:('invalid class_hash', 'ENTRYPOINT_FAILED'))] +#[should_panic(expected: ('invalid class_hash', 'ENTRYPOINT_FAILED'))] fn test_upgradeable_world_with_class_hash_zero() { - // Deploy world contract let world = deploy_world(); @@ -549,9 +548,8 @@ fn test_upgradeable_world_with_class_hash_zero() { #[test] #[available_gas(60000000)] -#[should_panic( expected: ('only owner can upgrade', 'ENTRYPOINT_FAILED'))] +#[should_panic(expected: ('only owner can upgrade', 'ENTRYPOINT_FAILED'))] fn test_upgradeable_world_from_non_owner() { - // Deploy world contract let world = deploy_world(); @@ -563,4 +561,4 @@ fn test_upgradeable_world_from_non_owner() { contract_address: world.contract_address }; upgradeable_world_dispatcher.upgrade(worldupgrade::TEST_CLASS_HASH.try_into().unwrap()); -} \ No newline at end of file +} diff --git a/crates/dojo-lang/src/inline_macros/delete.rs b/crates/dojo-lang/src/inline_macros/delete.rs index 728a7ee10f..b3bd2e916d 100644 --- a/crates/dojo-lang/src/inline_macros/delete.rs +++ b/crates/dojo-lang/src/inline_macros/delete.rs @@ -142,7 +142,7 @@ impl InlineMacroExprPlugin for DeleteMacro { builder.add_str(&format!( " let __delete_macro_value__ = {}; - {}.delete_entity(dojo::model::Model::name(@__delete_macro_value__), + {}.delete_entity(dojo::model::Model::name_hash(@__delete_macro_value__), dojo::model::Model::keys(@__delete_macro_value__), dojo::model::Model::layout(@__delete_macro_value__));", entity, diff --git a/crates/dojo-lang/src/inline_macros/set.rs b/crates/dojo-lang/src/inline_macros/set.rs index d17c7cb48f..dc090cc272 100644 --- a/crates/dojo-lang/src/inline_macros/set.rs +++ b/crates/dojo-lang/src/inline_macros/set.rs @@ -156,7 +156,7 @@ impl InlineMacroExprPlugin for SetMacro { builder.add_str(&format!( " let __set_macro_value__ = {}; - {}.set_entity(dojo::model::Model::name(@__set_macro_value__), + {}.set_entity(dojo::model::Model::name_hash(@__set_macro_value__), dojo::model::Model::keys(@__set_macro_value__), 0_u8, dojo::model::Model::values(@__set_macro_value__), dojo::model::Model::layout(@__set_macro_value__));", diff --git a/crates/dojo-lang/src/manifest_test_data/cairo_v240 b/crates/dojo-lang/src/manifest_test_data/cairo_v240 index ab46139485..83bb17f08d 100644 --- a/crates/dojo-lang/src/manifest_test_data/cairo_v240 +++ b/crates/dojo-lang/src/manifest_test_data/cairo_v240 @@ -8,7 +8,7 @@ test_compiler_cairo_v240 "world": { "name": "dojo::world::world", "address": null, - "class_hash": "0x5ac623f0c96059936bd2d0904bdd31799e430fe08a0caff7a5f497260b16497", + "class_hash": "0x439034396f2e01cf02122b1816378fddfa33b92109f80642306a3bcddd18d8e", "abi": [ { "type": "impl", @@ -114,7 +114,7 @@ test_compiler_cairo_v240 "name": "model", "inputs": [ { - "name": "name", + "name": "name_hash", "type": "core::felt252" } ], @@ -129,6 +129,10 @@ test_compiler_cairo_v240 "type": "function", "name": "register_model", "inputs": [ + { + "name": "name", + "type": "core::array::Span::" + }, { "name": "class_hash", "type": "core::starknet::class_hash::ClassHash" @@ -142,8 +146,8 @@ test_compiler_cairo_v240 "name": "deploy_contract", "inputs": [ { - "name": "salt", - "type": "core::felt252" + "name": "name", + "type": "core::array::Span::" }, { "name": "class_hash", @@ -532,8 +536,8 @@ test_compiler_cairo_v240 "kind": "struct", "members": [ { - "name": "salt", - "type": "core::felt252", + "name": "name", + "type": "core::array::Span::", "kind": "data" }, { @@ -601,7 +605,7 @@ test_compiler_cairo_v240 "members": [ { "name": "name", - "type": "core::felt252", + "type": "core::array::Span::", "kind": "data" }, { diff --git a/crates/dojo-lang/src/manifest_test_data/manifest b/crates/dojo-lang/src/manifest_test_data/manifest index 1f9e0fb0e9..6daf78b1bb 100644 --- a/crates/dojo-lang/src/manifest_test_data/manifest +++ b/crates/dojo-lang/src/manifest_test_data/manifest @@ -8,7 +8,7 @@ test_manifest_file "world": { "name": "dojo::world::world", "address": null, - "class_hash": "0x5ac623f0c96059936bd2d0904bdd31799e430fe08a0caff7a5f497260b16497", + "class_hash": "0x439034396f2e01cf02122b1816378fddfa33b92109f80642306a3bcddd18d8e", "abi": [ { "type": "impl", @@ -114,7 +114,7 @@ test_manifest_file "name": "model", "inputs": [ { - "name": "name", + "name": "name_hash", "type": "core::felt252" } ], @@ -129,6 +129,10 @@ test_manifest_file "type": "function", "name": "register_model", "inputs": [ + { + "name": "name", + "type": "core::array::Span::" + }, { "name": "class_hash", "type": "core::starknet::class_hash::ClassHash" @@ -142,8 +146,8 @@ test_manifest_file "name": "deploy_contract", "inputs": [ { - "name": "salt", - "type": "core::felt252" + "name": "name", + "type": "core::array::Span::" }, { "name": "class_hash", @@ -532,8 +536,8 @@ test_manifest_file "kind": "struct", "members": [ { - "name": "salt", - "type": "core::felt252", + "name": "name", + "type": "core::array::Span::", "kind": "data" }, { @@ -601,7 +605,7 @@ test_manifest_file "members": [ { "name": "name", - "type": "core::felt252", + "type": "core::array::Span::", "kind": "data" }, { @@ -1205,11 +1209,11 @@ test_manifest_file { "name": "dojo_examples::models::moves", "address": null, - "class_hash": "0x64495ca6dc1dc328972697b30468cea364bcb7452bbb6e4aaad3e4b3f190147", + "class_hash": "0x268a4fa3b20c32f89ca204c679e5b1080950aa07ab81f5735abb2fa1db7187c", "abi": [ { "type": "function", - "name": "name", + "name": "name_hash", "inputs": [], "outputs": [ { @@ -1374,11 +1378,11 @@ test_manifest_file { "name": "dojo_examples::models::position", "address": null, - "class_hash": "0x4cd20d231b04405a77b184c115dc60637e186504fad7f0929bd76cbd09c10b", + "class_hash": "0x165b41f5633015dc37f000b24019e8cb32c06570d4b2baf026ca5b6b707940f", "abi": [ { "type": "function", - "name": "name", + "name": "name_hash", "inputs": [], "outputs": [ { diff --git a/crates/dojo-lang/src/model.rs b/crates/dojo-lang/src/model.rs index 3a42488add..300de52246 100644 --- a/crates/dojo-lang/src/model.rs +++ b/crates/dojo-lang/src/model.rs @@ -7,6 +7,7 @@ use cairo_lang_syntax::node::{Terminal, TypedSyntaxNode}; use cairo_lang_utils::unordered_hash_map::UnorderedHashMap; use convert_case::{Case, Casing}; use dojo_world::manifest::Member; +use dojo_world::migration::strategy::poseidon_hash_str; use crate::introspect::handle_introspect_struct; use crate::plugin::{DojoAuxData, Model}; @@ -75,40 +76,41 @@ pub fn handle_model_struct( members.iter().filter_map(|m| serialize_member(m, false)).collect::<_>(); let name = struct_ast.name(db).text(db); + let name_hash = format!("0x{:x}", poseidon_hash_str(name.as_str())); aux_data.models.push(Model { name: name.to_string(), members: members.to_vec() }); ( RewriteNode::interpolate_patched( " - impl $type_name$Model of dojo::model::Model<$type_name$> { + impl $name$Model of dojo::model::Model<$name$> { #[inline(always)] - fn name(self: @$type_name$) -> felt252 { - '$type_name$' + fn name_hash(self: @$name$) -> felt252 { + '$name$' } #[inline(always)] - fn keys(self: @$type_name$) -> Span { + fn keys(self: @$name$) -> Span { let mut serialized = core::array::ArrayTrait::new(); $serialized_keys$ core::array::ArrayTrait::span(@serialized) } #[inline(always)] - fn values(self: @$type_name$) -> Span { + fn values(self: @$name$) -> Span { let mut serialized = core::array::ArrayTrait::new(); $serialized_values$ core::array::ArrayTrait::span(@serialized) } #[inline(always)] - fn layout(self: @$type_name$) -> Span { + fn layout(self: @$name$) -> Span { let mut layout = core::array::ArrayTrait::new(); - dojo::database::introspect::Introspect::<$type_name$>::layout(ref layout); + dojo::database::introspect::Introspect::<$name$>::layout(ref layout); core::array::ArrayTrait::span(@layout) } #[inline(always)] - fn packed_size(self: @$type_name$) -> usize { + fn packed_size(self: @$name$) -> usize { let mut layout = self.layout(); dojo::packing::calculate_packed_size(ref layout) } @@ -117,31 +119,31 @@ pub fn handle_model_struct( $schema_introspection$ #[starknet::interface] - trait I$type_name$ { + trait I$name$ { fn name(self: @T) -> felt252; } #[starknet::contract] mod $contract_name$ { - use super::$type_name$; + use super::$name$; #[storage] struct Storage {} #[external(v0)] - fn name(self: @ContractState) -> felt252 { - '$type_name$' + fn name_hash(self: @ContractState) -> felt252 { + $name_hash$ } #[external(v0)] fn unpacked_size(self: @ContractState) -> usize { - dojo::database::introspect::Introspect::<$type_name$>::size() + dojo::database::introspect::Introspect::<$name$>::size() } #[external(v0)] fn packed_size(self: @ContractState) -> usize { let mut layout = core::array::ArrayTrait::new(); - dojo::database::introspect::Introspect::<$type_name$>::layout(ref layout); + dojo::database::introspect::Introspect::<$name$>::layout(ref layout); let mut layout_span = layout.span(); dojo::packing::calculate_packed_size(ref layout_span) } @@ -149,22 +151,20 @@ pub fn handle_model_struct( #[external(v0)] fn layout(self: @ContractState) -> Span { let mut layout = core::array::ArrayTrait::new(); - dojo::database::introspect::Introspect::<$type_name$>::layout(ref layout); + dojo::database::introspect::Introspect::<$name$>::layout(ref layout); core::array::ArrayTrait::span(@layout) } #[external(v0)] fn schema(self: @ContractState) -> dojo::database::introspect::Ty { - dojo::database::introspect::Introspect::<$type_name$>::ty() + dojo::database::introspect::Introspect::<$name$>::ty() } } ", &UnorderedHashMap::from([ ("contract_name".to_string(), RewriteNode::Text(name.to_case(Case::Snake))), - ( - "type_name".to_string(), - RewriteNode::new_trimmed(struct_ast.name(db).as_syntax_node()), - ), + ("name".to_string(), RewriteNode::Text(name.to_string())), + ("name_hash".to_string(), RewriteNode::Text(name_hash.to_string())), ("schema_introspection".to_string(), handle_introspect_struct(db, struct_ast)), ("serialized_keys".to_string(), RewriteNode::new_modified(serialized_keys)), ("serialized_values".to_string(), RewriteNode::new_modified(serialized_values)), diff --git a/crates/dojo-lang/src/plugin_test_data/model b/crates/dojo-lang/src/plugin_test_data/model index bcaac79822..34be89a320 100644 --- a/crates/dojo-lang/src/plugin_test_data/model +++ b/crates/dojo-lang/src/plugin_test_data/model @@ -802,7 +802,7 @@ impl PositionSerde of core::serde::Serde:: { impl PositionModel of dojo::model::Model { #[inline(always)] - fn name(self: @Position) -> felt252 { + fn name_hash(self: @Position) -> felt252 { 'Position' } @@ -879,8 +879,8 @@ impl PositionIntrospect<> of dojo::database::introspect::Introspect> struct Storage {} #[external(v0)] - fn name(self: @ContractState) -> felt252 { - 'Position' + fn name_hash(self: @ContractState) -> felt252 { + 0x7b7bd4fefed73494631bcd45cf49e26b9c08d0cf2eef498e296ae6a76c106e4 } #[external(v0)] @@ -921,7 +921,7 @@ impl RolesSerde of core::serde::Serde:: { impl RolesModel of dojo::model::Model { #[inline(always)] - fn name(self: @Roles) -> felt252 { + fn name_hash(self: @Roles) -> felt252 { 'Roles' } @@ -994,8 +994,8 @@ impl RolesIntrospect<> of dojo::database::introspect::Introspect> { struct Storage {} #[external(v0)] - fn name(self: @ContractState) -> felt252 { - 'Roles' + fn name_hash(self: @ContractState) -> felt252 { + 0xe7952dd4a6d8cadc01c549ea1f202191a0371fde8260f06166932e8b366c4a } #[external(v0)] @@ -1036,7 +1036,7 @@ impl OnlyKeyModelSerde of core::serde::Serde:: { impl OnlyKeyModelModel of dojo::model::Model { #[inline(always)] - fn name(self: @OnlyKeyModel) -> felt252 { + fn name_hash(self: @OnlyKeyModel) -> felt252 { 'OnlyKeyModel' } @@ -1108,8 +1108,8 @@ impl OnlyKeyModelIntrospect<> of dojo::database::introspect::Introspect felt252 { - 'OnlyKeyModel' + fn name_hash(self: @ContractState) -> felt252 { + 0xadf5de5b7ca949565c1ab1e99d5c3501b0f2694b919bf9df06177fca2daa3e } #[external(v0)] @@ -1156,7 +1156,7 @@ impl PlayerSerde of core::serde::Serde:: { impl PlayerModel of dojo::model::Model { #[inline(always)] - fn name(self: @Player) -> felt252 { + fn name_hash(self: @Player) -> felt252 { 'Player' } @@ -1237,8 +1237,8 @@ impl PlayerIntrospect<> of dojo::database::introspect::Introspect> { struct Storage {} #[external(v0)] - fn name(self: @ContractState) -> felt252 { - 'Player' + fn name_hash(self: @ContractState) -> felt252 { + 0x3ef60e19d392fb1385879854ee6baf68dae43234ef5932a2c8087825dd75585 } #[external(v0)] diff --git a/crates/dojo-lang/src/semantics/test_data/set b/crates/dojo-lang/src/semantics/test_data/set index cca850737a..fcb1b3870a 100644 --- a/crates/dojo-lang/src/semantics/test_data/set +++ b/crates/dojo-lang/src/semantics/test_data/set @@ -148,7 +148,7 @@ Block( Value( FunctionCall( ExprFunctionCall { - function: test::HealthModel::name, + function: test::HealthModel::name_hash, args: [ Value( Snapshot( diff --git a/crates/dojo-world/src/contracts/abi/world.rs b/crates/dojo-world/src/contracts/abi/world.rs index de66ca66b0..c1fd490436 100644 --- a/crates/dojo-world/src/contracts/abi/world.rs +++ b/crates/dojo-world/src/contracts/abi/world.rs @@ -109,7 +109,7 @@ abigen!( "name": "model", "inputs": [ { - "name": "name", + "name": "name_hash", "type": "core::felt252" } ], @@ -124,6 +124,10 @@ abigen!( "type": "function", "name": "register_model", "inputs": [ + { + "name": "name", + "type": "core::array::Span::" + }, { "name": "class_hash", "type": "core::starknet::class_hash::ClassHash" @@ -137,8 +141,8 @@ abigen!( "name": "deploy_contract", "inputs": [ { - "name": "salt", - "type": "core::felt252" + "name": "name", + "type": "core::array::Span::" }, { "name": "class_hash", @@ -527,8 +531,8 @@ abigen!( "kind": "struct", "members": [ { - "name": "salt", - "type": "core::felt252", + "name": "name", + "type": "core::array::Span::", "kind": "data" }, { @@ -596,7 +600,7 @@ abigen!( "members": [ { "name": "name", - "type": "core::felt252", + "type": "core::array::Span::", "kind": "data" }, { diff --git a/crates/dojo-world/src/contracts/cairo_utils.rs b/crates/dojo-world/src/contracts/cairo_utils.rs index b43ff5be7d..d4d74961d8 100644 --- a/crates/dojo-world/src/contracts/cairo_utils.rs +++ b/crates/dojo-world/src/contracts/cairo_utils.rs @@ -2,6 +2,7 @@ use anyhow::{anyhow, Result}; use http::uri::Uri; use starknet::core::types::FieldElement; use starknet::core::utils::cairo_short_string_to_felt; +use starknet_crypto::poseidon_hash_many; pub fn str_to_felt(string: &str) -> Result { cairo_short_string_to_felt(string).map_err(|e| { @@ -9,18 +10,24 @@ pub fn str_to_felt(string: &str) -> Result { }) } -pub fn encode_uri(uri: &str) -> Result> { - let parsed: Uri = - uri.try_into().map_err(|e| anyhow!("Failed to encode URI `{}`: {}", uri, e))?; - - Ok(parsed - .to_string() - .chars() +pub fn str_to_felts(s: &str) -> Result> { + Ok(s.chars() .collect::>() .chunks(31) .map(|chunk| { let s: String = chunk.iter().collect(); - cairo_short_string_to_felt(&s) + str_to_felt(&s) }) .collect::, _>>()?) } + +pub fn encode_uri(uri: &str) -> Result> { + let parsed: Uri = + uri.try_into().map_err(|e| anyhow!("Failed to encode URI `{}`: {}", uri, e))?; + + str_to_felts(&parsed.to_string()) +} + +pub fn poseidon_hash_str(value: &str) -> Result { + Ok(poseidon_hash_many(&str_to_felts(&value)?)) +} diff --git a/crates/dojo-world/src/contracts/model_test.rs b/crates/dojo-world/src/contracts/model_test.rs index ce03fd3f3a..efab444182 100644 --- a/crates/dojo-world/src/contracts/model_test.rs +++ b/crates/dojo-world/src/contracts/model_test.rs @@ -63,7 +63,7 @@ async fn test_model() { assert_eq!( position.class_hash(), FieldElement::from_hex_be( - "0x004cd20d231b04405a77b184c115dc60637e186504fad7f0929bd76cbd09c10b" + "0x02b233bba9a232a5e891c85eca9f67beedca7a12f9768729ff017bcb62d25c9d" ) .unwrap() ); diff --git a/crates/dojo-world/src/contracts/world_test.rs b/crates/dojo-world/src/contracts/world_test.rs index 2028fad8b8..50e8171571 100644 --- a/crates/dojo-world/src/contracts/world_test.rs +++ b/crates/dojo-world/src/contracts/world_test.rs @@ -8,6 +8,7 @@ use starknet::accounts::{Account, ConnectedAccount}; use starknet::core::types::FieldElement; use super::{WorldContract, WorldContractReader}; +use crate::contracts::cairo_utils::str_to_felts; use crate::manifest::Manifest; use crate::migration::strategy::prepare_for_migration; use crate::migration::world::WorldDiff; @@ -76,7 +77,7 @@ pub async fn deploy_world( let mut declare_output = vec![]; for model in strategy.models { let res = model.declare(&account, Default::default()).await.unwrap(); - declare_output.push(res); + declare_output.push((model, res)); } // wait for the tx to be mined @@ -86,7 +87,12 @@ pub async fn deploy_world( let calls = declare_output .iter() - .map(|o| world.register_model_getcall(&o.class_hash.into())) + .map(|(model, declare)| { + world.register_model_getcall( + &str_to_felts(&model.diff.name).unwrap(), + &declare.class_hash.into(), + ) + }) .collect::>(); let _ = account.execute(calls).send().await.unwrap(); diff --git a/crates/dojo-world/src/migration/strategy.rs b/crates/dojo-world/src/migration/strategy.rs index cde6af17ee..fd9119381f 100644 --- a/crates/dojo-world/src/migration/strategy.rs +++ b/crates/dojo-world/src/migration/strategy.rs @@ -4,8 +4,10 @@ use std::path::{Path, PathBuf}; use anyhow::{anyhow, Context, Result}; use starknet::core::types::FieldElement; -use starknet::core::utils::{cairo_short_string_to_felt, get_contract_address}; -use starknet_crypto::{poseidon_hash_many, poseidon_hash_single}; +use starknet::core::utils::get_contract_address; +use starknet_crypto::poseidon_hash_single; + +use crate::contracts::cairo_utils::poseidon_hash_str; use super::class::{ClassDiff, ClassMigration}; use super::contract::{ContractDiff, ContractMigration}; @@ -185,7 +187,7 @@ fn evaluate_contracts_to_migrate( comps_to_migrate.push(ContractMigration { diff: c.clone(), artifact_path: path.clone(), - salt: generate_salt(&c.name), + salt: poseidon_hash_str(&c.name)?, ..Default::default() }); } @@ -224,17 +226,3 @@ fn find_artifact_path<'a>( .get(contract_name) .with_context(|| anyhow!("missing contract artifact for `{}` contract", contract_name)) } - -pub fn generate_salt(value: &str) -> FieldElement { - poseidon_hash_many( - &value - .chars() - .collect::>() - .chunks(31) - .map(|chunk| { - let s: String = chunk.iter().collect(); - cairo_short_string_to_felt(&s).unwrap() - }) - .collect::>(), - ) -} diff --git a/crates/sozo/src/commands/register.rs b/crates/sozo/src/commands/register.rs index 6ff53cdfd6..0ae7e78dca 100644 --- a/crates/sozo/src/commands/register.rs +++ b/crates/sozo/src/commands/register.rs @@ -1,3 +1,6 @@ +use anyhow::Error; +use std::str::FromStr; + use anyhow::Result; use clap::{Args, Subcommand}; use dojo_world::metadata::dojo_metadata_from_workspace; @@ -10,6 +13,29 @@ use super::options::transaction::TransactionOptions; use super::options::world::WorldOptions; use crate::ops::register; +#[derive(Clone, Debug)] +pub struct Model { + pub name: String, + pub class_hash: FieldElement, +} + +impl FromStr for Model { + type Err = Error; + + fn from_str(s: &str) -> Result { + let parts: Vec<&str> = s.split(',').collect(); + if parts.len() != 2 { + return Err(Error::new(std::io::Error::new( + std::io::ErrorKind::InvalidInput, + "Invalid input", + ))); + } + let name = parts[0].to_string(); + let class_hash = parts[1].parse()?; + Ok(Model { name, class_hash }) + } +} + #[derive(Debug, Args)] pub struct RegisterArgs { #[command(subcommand)] @@ -20,11 +46,11 @@ pub struct RegisterArgs { pub enum RegisterCommand { #[command(about = "Register a model to a world.")] Model { - #[arg(num_args = 1..)] + // #[arg(num_args = 1..)] #[arg(required = true)] - #[arg(value_name = "CLASS_HASH")] - #[arg(help = "The class hash of the models to register.")] - models: Vec, + #[arg(value_name = "MODEL")] + #[arg(help = "A (name, class hash) tuple of the models to register.")] + models: Vec, #[command(flatten)] world: WorldOptions, diff --git a/crates/sozo/src/ops/execute.rs b/crates/sozo/src/ops/execute.rs index 868f69993c..b415ceb03e 100644 --- a/crates/sozo/src/ops/execute.rs +++ b/crates/sozo/src/ops/execute.rs @@ -1,6 +1,6 @@ use anyhow::{Context, Result}; use dojo_world::metadata::Environment; -use dojo_world::migration::strategy::generate_salt; +use dojo_world::migration::strategy::poseidon_hash_str; use dojo_world::utils::TransactionWaiter; use starknet::accounts::{Account, Call}; use starknet::core::types::{BlockId, BlockTag, FieldElement, FunctionCall}; @@ -36,7 +36,7 @@ pub async fn execute(args: ExecuteArgs, env_metadata: Option) -> Re .await?; get_contract_address( - generate_salt(&contract), + poseidon_hash_str(&contract), contract_class_hash[0], &[], FieldElement::from_hex_be(&world_address).unwrap(), diff --git a/crates/sozo/src/ops/migration/mod.rs b/crates/sozo/src/ops/migration/mod.rs index 1fbfb96b29..b5535b9fde 100644 --- a/crates/sozo/src/ops/migration/mod.rs +++ b/crates/sozo/src/ops/migration/mod.rs @@ -1,12 +1,12 @@ use std::path::Path; use anyhow::{anyhow, bail, Context, Result}; -use dojo_world::contracts::cairo_utils; +use dojo_world::contracts::cairo_utils::{self, str_to_felts}; use dojo_world::contracts::world::WorldContract; use dojo_world::manifest::{Manifest, ManifestError}; use dojo_world::metadata::dojo_metadata_from_workspace; use dojo_world::migration::contract::ContractMigration; -use dojo_world::migration::strategy::{generate_salt, prepare_for_migration, MigrationStrategy}; +use dojo_world::migration::strategy::{poseidon_hash_str, prepare_for_migration, MigrationStrategy}; use dojo_world::migration::world::WorldDiff; use dojo_world::migration::{ Declarable, DeployOutput, Deployable, MigrationError, RegisterOutput, StateDiff, @@ -102,7 +102,7 @@ where }; local_manifest.contracts.iter_mut().for_each(|c| { - let salt = generate_salt(&c.name); + let salt = poseidon_hash_str(&c.name); c.address = Some(get_contract_address(salt, base_class_hash, &[], world_address)); }); @@ -468,7 +468,9 @@ where let calls = models .iter() - .map(|c| world.register_model_getcall(&c.diff.local.into())) + .map(|c| { + world.register_model_getcall(&str_to_felts(&c.diff.name).unwrap(), &c.diff.local.into()) + }) .collect::>(); let InvokeTransactionResult { transaction_hash } = migrator diff --git a/crates/sozo/src/ops/register.rs b/crates/sozo/src/ops/register.rs index c0d37d8199..b442352f74 100644 --- a/crates/sozo/src/ops/register.rs +++ b/crates/sozo/src/ops/register.rs @@ -1,4 +1,5 @@ use anyhow::{Context, Result}; +use dojo_world::contracts::cairo_utils::str_to_felts; use dojo_world::contracts::WorldContract; use dojo_world::metadata::Environment; use dojo_world::utils::TransactionWaiter; @@ -17,7 +18,12 @@ pub async fn execute(command: RegisterCommand, env_metadata: Option let calls = models .iter() - .map(|c| world.register_model_getcall(&(*c).into())) + .map(|m| { + world.register_model_getcall( + &str_to_felts(&m.name).unwrap(), + &m.class_hash.into(), + ) + }) .collect::>(); let res = account