From 0fa1b2ab2585fa18723872376896666a9a1552b1 Mon Sep 17 00:00:00 2001 From: Shramee Srivastav Date: Tue, 21 Nov 2023 22:34:06 +0800 Subject: [PATCH] core: delete entity models (#1191) * core: delete entity models * test: delete entity * chore: pass layout to delete * chore: fix manifest test * Fix records contract address --------- Co-authored-by: Tarrence van As --- crates/dojo-core/src/world.cairo | 29 ++++-- crates/dojo-core/src/world_test.cairo | 94 ++++++++++++------- .../dojo-lang/src/manifest_test_data/manifest | 6 +- crates/torii/graphql/src/tests/mod.rs | 2 +- 4 files changed, 86 insertions(+), 45 deletions(-) diff --git a/crates/dojo-core/src/world.cairo b/crates/dojo-core/src/world.cairo index 100a9a92d7..d38df35d3f 100644 --- a/crates/dojo-core/src/world.cairo +++ b/crates/dojo-core/src/world.cairo @@ -35,7 +35,7 @@ trait IWorld { fn set_executor(ref self: T, contract_address: ContractAddress); fn executor(self: @T) -> ContractAddress; fn base(self: @T) -> ClassHash; - fn delete_entity(ref self: T, model: felt252, keys: Span); + fn delete_entity(ref self: T, model: felt252, keys: Span, layout: Span); fn is_owner(self: @T, address: ContractAddress, resource: felt252) -> bool; fn grant_owner(ref self: T, address: ContractAddress, resource: felt252); fn revoke_owner(ref self: T, address: ContractAddress, resource: felt252); @@ -247,9 +247,7 @@ mod world { self.metadata_uri.write(i, *item); i += 1; }, - Option::None(_) => { - break; - } + Option::None(_) => { break; } }; }; } @@ -491,12 +489,27 @@ mod world { /// /// * `model` - The name of the model to be deleted. /// * `query` - The query to be used to find the entity. - fn delete_entity(ref self: ContractState, model: felt252, keys: Span) { - let system = get_caller_address(); - assert(system.is_non_zero(), 'must be called thru system'); - assert_can_write(@self, model, system); + fn delete_entity( + ref self: ContractState, model: felt252, keys: Span, layout: Span + ) { + assert_can_write(@self, model, get_caller_address()); + + let model_class_hash = self.models.read(model); + + let mut empty_values = ArrayTrait::new(); + let mut i = 0; + + loop { + if (i == layout.len()) { + break; + } + empty_values.append(0); + i += 1; + }; let key = poseidon::poseidon_hash_span(keys); + database::set(model, key, 0, empty_values.span(), layout); + // this deletes the index database::del(model, key); EventEmitter::emit(ref self, StoreDelRecord { table: model, keys }); diff --git a/crates/dojo-core/src/world_test.cairo b/crates/dojo-core/src/world_test.cairo index 36d260763b..8612168548 100644 --- a/crates/dojo-core/src/world_test.cairo +++ b/crates/dojo-core/src/world_test.cairo @@ -32,12 +32,13 @@ struct Fizz { #[starknet::interface] trait Ibar { fn set_foo(self: @TContractState, a: felt252, b: u128); + fn delete_foo(self: @TContractState); fn set_char(self: @TContractState, a: felt252, b: u32); } #[starknet::contract] mod bar { - use super::{Foo, IWorldDispatcher, IWorldDispatcherTrait}; + use super::{Foo, IWorldDispatcher, IWorldDispatcherTrait, SchemaIntrospection}; use super::benchmarks::{Character, Abilities, Stats, Weapon, Sword}; use traits::Into; use starknet::{get_caller_address, ContractAddress}; @@ -57,6 +58,15 @@ mod bar { set!(self.world.read(), Foo { caller: get_caller_address(), a, b }); } + fn delete_foo(self: @ContractState) { + let mut layout = array![]; + SchemaIntrospection::::layout(ref layout); + self + .world + .read() + .delete_entity('Foo', array![get_caller_address().into()].span(), layout.span()); + } + fn set_char(self: @ContractState, a: felt252, b: u32) { set!( self.world.read(), @@ -82,16 +92,12 @@ mod bar { finished: true, romances: 0x1234, }, - weapon: Weapon::DualWield(( - Sword { - swordsmith: get_caller_address(), - damage: 0x12345678, - }, - Sword { - swordsmith: get_caller_address(), - damage: 0x12345678, - } - )), + weapon: Weapon::DualWield( + ( + Sword { swordsmith: get_caller_address(), damage: 0x12345678, }, + Sword { swordsmith: get_caller_address(), damage: 0x12345678, } + ) + ), gold: b, } ); @@ -101,6 +107,19 @@ mod bar { // Tests +fn deploy_world_and_bar() -> (IWorldDispatcher, IbarDispatcher) { + // Spawn empty world + let world = deploy_world(); + world.register_model(foo::TEST_CLASS_HASH.try_into().unwrap()); + + // System contract + let bar_contract = IbarDispatcher { + contract_address: deploy_with_world_address(bar::TEST_CLASS_HASH, world) + }; + + (world, bar_contract) +} + #[test] #[available_gas(2000000)] fn test_model() { @@ -111,14 +130,7 @@ fn test_model() { #[test] #[available_gas(6000000)] fn test_system() { - // Spawn empty world - let world = deploy_world(); - world.register_model(foo::TEST_CLASS_HASH.try_into().unwrap()); - - // System contract - let bar_contract = IbarDispatcher { - contract_address: deploy_with_world_address(bar::TEST_CLASS_HASH, world) - }; + let (world, bar_contract) = deploy_world_and_bar(); bar_contract.set_foo(1337, 1337); @@ -127,6 +139,24 @@ fn test_system() { assert(stored.b == 1337, 'data not stored'); } +#[test] +#[available_gas(6000000)] +fn test_delete() { + let (world, bar_contract) = deploy_world_and_bar(); + + // set model + bar_contract.set_foo(1337, 1337); + let stored: Foo = get!(world, get_caller_address(), Foo); + assert(stored.a == 1337, 'data not stored'); + assert(stored.b == 1337, 'data not stored'); + + // delete model + bar_contract.delete_foo(); + let deleted: Foo = get!(world, get_caller_address(), Foo); + assert(deleted.a == 0, 'data not deleted'); + assert(deleted.b == 0, 'data not deleted'); +} + #[test] #[available_gas(6000000)] fn test_model_class_hash_getter() { @@ -153,13 +183,7 @@ fn test_emit() { #[test] #[available_gas(9000000)] fn test_set_entity_admin() { - // Spawn empty world - let world = deploy_world(); - world.register_model(foo::TEST_CLASS_HASH.try_into().unwrap()); - - let bar_contract = IbarDispatcher { - contract_address: deploy_with_world_address(bar::TEST_CLASS_HASH, world) - }; + let (world, bar_contract) = deploy_world_and_bar(); let alice = starknet::contract_address_const::<0x1337>(); starknet::testing::set_contract_address(alice); @@ -267,14 +291,14 @@ fn test_entities() { let ids = world.entity_ids('Foo'); assert(keys.len() == ids.len(), 'result differs in entity_ids'); assert(keys.len() == 0, 'found value for unindexed'); - // query_keys.append(0x1337); - // let (keys, values) = world.entities('Foo', 42, query_keys.span(), 2, layout); - // assert(keys.len() == 1, 'No keys found!'); - - // let mut query_keys = ArrayTrait::new(); - // query_keys.append(0x1338); - // let (keys, values) = world.entities('Foo', 42, query_keys.span(), 2, layout); - // assert(keys.len() == 0, 'Keys found!'); +// query_keys.append(0x1337); +// let (keys, values) = world.entities('Foo', 42, query_keys.span(), 2, layout); +// assert(keys.len() == 1, 'No keys found!'); + +// let mut query_keys = ArrayTrait::new(); +// query_keys.append(0x1338); +// let (keys, values) = world.entities('Foo', 42, query_keys.span(), 2, layout); +// assert(keys.len() == 0, 'Keys found!'); } #[test] @@ -486,4 +510,4 @@ fn bench_execute_complex() { end(gas, 'char get macro'); assert(data.heigth == 1337, 'data not stored'); -} \ No newline at end of file +} diff --git a/crates/dojo-lang/src/manifest_test_data/manifest b/crates/dojo-lang/src/manifest_test_data/manifest index 0d9f8519d6..1393fe2cdf 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": "world", "address": null, - "class_hash": "0x6441e7fc96d795ec94fca66634e6d87edf84362e564ef80eb13f459914b8b", + "class_hash": "0x481a9c7b128847a520bb234d93ef8a83b462de03fce7089eb4ed9135d2f31d9", "abi": [ { "type": "impl", @@ -357,6 +357,10 @@ test_manifest_file { "name": "keys", "type": "core::array::Span::" + }, + { + "name": "layout", + "type": "core::array::Span::" } ], "outputs": [], diff --git a/crates/torii/graphql/src/tests/mod.rs b/crates/torii/graphql/src/tests/mod.rs index 83c7a3839c..cabad47c4f 100644 --- a/crates/torii/graphql/src/tests/mod.rs +++ b/crates/torii/graphql/src/tests/mod.rs @@ -289,7 +289,7 @@ pub async fn spinup_types_test() -> Result { execute_strategy(&ws, &migration, &account, None).await.unwrap(); // Execute `create` and insert 10 records into storage - let records_contract = "0x2753d30656b393ecea156189bf0acf5e1063f3ac978fb5c3cebe7a4570bbc78"; + let records_contract = "0x58eaab9779694efb92b43ccad6dc9b40cd8fc69e3efa97137f1c83af69ca8a1"; let InvokeTransactionResult { transaction_hash } = account .execute(vec![Call { calldata: vec![FieldElement::from_str("0xa").unwrap()],