From 84b77369dd541489d91764e64a6378875368e83a Mon Sep 17 00:00:00 2001 From: Tarrence van As Date: Thu, 15 Jun 2023 11:40:16 -0400 Subject: [PATCH] Refactor database --- crates/dojo-core/src/database.cairo | 94 +++++++++++++++++++ crates/dojo-core/src/database/index.cairo | 92 ++++++++++++++++++ .../src/{storage => database}/query.cairo | 0 crates/dojo-core/src/database/storage.cairo | 63 +++++++++++++ .../src/{storage => database}/utils.cairo | 0 crates/dojo-core/src/interfaces.cairo | 2 +- crates/dojo-core/src/lib.cairo | 2 +- crates/dojo-core/src/storage.cairo | 5 - crates/dojo-core/src/storage/db.cairo | 92 ------------------ crates/dojo-core/src/storage/index.cairo | 62 ------------ crates/dojo-core/src/storage/kv.cairo | 67 ------------- crates/dojo-core/src/world.cairo | 11 ++- .../src/{storage.cairo => database.cairo} | 0 .../dojo-core/tests/src/database/index.cairo | 65 +++++++++++++ .../src/{storage => database}/query.cairo | 10 +- .../src/{storage => database}/utils.cairo | 2 +- crates/dojo-core/tests/src/lib.cairo | 2 +- .../dojo-core/tests/src/storage/index.cairo | 65 ------------- crates/dojo-core/tests/src/world.cairo | 2 +- crates/dojo-erc/src/erc20/erc20.cairo | 2 +- crates/dojo-lang/src/commands/entities.rs | 2 +- crates/dojo-lang/src/plugin_test_data/system | 60 ++++++------ crates/dojo-lang/src/system.rs | 18 ++-- crates/dojo-world/src/component.rs | 15 ++- 24 files changed, 377 insertions(+), 356 deletions(-) create mode 100644 crates/dojo-core/src/database.cairo create mode 100644 crates/dojo-core/src/database/index.cairo rename crates/dojo-core/src/{storage => database}/query.cairo (100%) create mode 100644 crates/dojo-core/src/database/storage.cairo rename crates/dojo-core/src/{storage => database}/utils.cairo (100%) delete mode 100644 crates/dojo-core/src/storage.cairo delete mode 100644 crates/dojo-core/src/storage/db.cairo delete mode 100644 crates/dojo-core/src/storage/index.cairo delete mode 100644 crates/dojo-core/src/storage/kv.cairo rename crates/dojo-core/tests/src/{storage.cairo => database.cairo} (100%) create mode 100644 crates/dojo-core/tests/src/database/index.cairo rename crates/dojo-core/tests/src/{storage => database}/query.cairo (87%) rename crates/dojo-core/tests/src/{storage => database}/utils.cairo (98%) delete mode 100644 crates/dojo-core/tests/src/storage/index.cairo diff --git a/crates/dojo-core/src/database.cairo b/crates/dojo-core/src/database.cairo new file mode 100644 index 0000000000..16ddf11509 --- /dev/null +++ b/crates/dojo-core/src/database.cairo @@ -0,0 +1,94 @@ +use array::{ArrayTrait, SpanTrait}; +use traits::{Into, TryInto}; +use serde::Serde; +use hash::LegacyHash; +use poseidon::poseidon_hash_span; + +mod index; +mod query; +mod storage; +mod utils; + +use dojo_core::database::{query::{Query, QueryTrait}}; +use dojo_core::interfaces::{IComponentLibraryDispatcher, IComponentDispatcherTrait}; +use dojo_core::serde::SpanSerde; + +fn get( + class_hash: starknet::ClassHash, table: felt252, query: Query, offset: u8, length: usize +) -> Option> { + let mut length = length; + if length == 0 { + length = IComponentLibraryDispatcher { class_hash: class_hash }.len(); + } + + let id = query.hash(); + let mut keys = ArrayTrait::new(); + keys.append('dojo_storage'); + keys.append(table); + keys.append(id); + match index::exists(0, table, id) { + bool::False(()) => Option::None(()), + bool::True(()) => Option::Some(storage::get_many(0, keys.span(), offset, length)), + } +} + +fn set( + class_hash: starknet::ClassHash, table: felt252, query: Query, offset: u8, value: Span +) { + let keys = query.keys(); + let id = query.hash(); + + let length = IComponentLibraryDispatcher { class_hash: class_hash }.len(); + assert(value.len() <= length, 'Value too long'); + + index::create(0, table, id); + + let mut keys = ArrayTrait::new(); + keys.append('dojo_storage'); + keys.append(table); + keys.append(id); + storage::set_many(0, keys.span(), offset, value); +} + +fn del(class_hash: starknet::ClassHash, table: felt252, query: Query) { + index::delete(0, table, query.hash()); +} + +// returns a tuple of spans, first contains the entity IDs, +// second the deserialized entities themselves +fn all( + class_hash: starknet::ClassHash, component: felt252, partition: felt252 +) -> (Span, Span>) { + let table = { + if partition == 0.into() { + component + } else { + let mut serialized = ArrayTrait::new(); + component.serialize(ref serialized); + partition.serialize(ref serialized); + let hash = poseidon_hash_span(serialized.span()); + hash.into() + } + }; + + let all_ids = index::get(0, table); + let length = IComponentLibraryDispatcher { class_hash: class_hash }.len(); + + let mut ids = all_ids.span(); + let mut entities: Array> = ArrayTrait::new(); + loop { + match ids.pop_front() { + Option::Some(id) => { + let mut keys = ArrayTrait::new(); + keys.append('dojo_storage'); + keys.append(table); + keys.append(*id); + let value: Span = storage::get_many(0, keys.span(), 0_u8, length); + entities.append(value); + }, + Option::None(_) => { + break (all_ids.span(), entities.span()); + } + }; + } +} diff --git a/crates/dojo-core/src/database/index.cairo b/crates/dojo-core/src/database/index.cairo new file mode 100644 index 0000000000..522e2ba53b --- /dev/null +++ b/crates/dojo-core/src/database/index.cairo @@ -0,0 +1,92 @@ +use array::{ArrayTrait, SpanTrait}; +use traits::Into; +use option::OptionTrait; +use poseidon::poseidon_hash_span; +use serde::Serde; + +use dojo_core::database::storage; +use dojo_core::serde::SpanSerde; + +fn create(address_domain: u32, index: felt252, id: felt252) { + if exists(address_domain, index, id) { + return (); + } + + let index_len_key = build_index_len_key(index); + let index_len = storage::get(address_domain, index_len_key); + storage::set(address_domain, build_index_item_key(index, id), index_len + 1); + storage::set(address_domain, index_len_key, index_len + 1); + storage::set(address_domain, build_index_key(index, index_len), id); +} + +fn delete(address_domain: u32, index: felt252, id: felt252) { + if !exists(address_domain, index, id) { + return (); + } + + let index_len_key = build_index_len_key(index); + let replace_item_idx = storage::get(address_domain, index_len_key) - 1; + + let index_item_key = build_index_item_key(index, id); + let delete_item_idx = storage::get(address_domain, index_item_key) - 1; + + storage::set(address_domain, index_item_key, 0); + storage::set(address_domain, index_len_key, replace_item_idx); + + // Replace the deleted element with the last element. + // NOTE: We leave the last element set as to not produce an unncessary state diff. + let replace_item_value = storage::get(address_domain, build_index_key(index, replace_item_idx)); + storage::set(address_domain, build_index_key(index, delete_item_idx), replace_item_value); +} + +fn exists(address_domain: u32, index: felt252, id: felt252) -> bool { + storage::get(address_domain, build_index_item_key(index, id)) != 0 +} + +fn get(address_domain: u32, index: felt252) -> Array { + let mut res = ArrayTrait::new(); + + let index_len_key = build_index_len_key(index); + let index_len = storage::get(address_domain, index_len_key); + let mut idx = 0; + + loop { + if idx == index_len { + break (); + } + + res.append(storage::get(address_domain, build_index_key(index, idx))); + idx += 1; + }; + + res +} + +fn index_key_prefix() -> Array { + let mut prefix = ArrayTrait::new(); + prefix.append('dojo_index'); + prefix +} + +fn build_index_len_key(index: felt252) -> Span { + let mut index_len_key = index_key_prefix(); + index_len_key.append('index_lens'); + index_len_key.append(index); + index_len_key.span() +} + +fn build_index_key(index: felt252, idx: felt252) -> Span { + let mut key = index_key_prefix(); + key.append('indexes'); + key.append(index); + key.append(idx); + key.span() +} + +fn build_index_item_key(index: felt252, id: felt252) -> Span { + let mut index_len_key = index_key_prefix(); + index_len_key.append('index_ids'); + index_len_key.append(index); + index_len_key.append(id); + index_len_key.span() +} diff --git a/crates/dojo-core/src/storage/query.cairo b/crates/dojo-core/src/database/query.cairo similarity index 100% rename from crates/dojo-core/src/storage/query.cairo rename to crates/dojo-core/src/database/query.cairo diff --git a/crates/dojo-core/src/database/storage.cairo b/crates/dojo-core/src/database/storage.cairo new file mode 100644 index 0000000000..5762df7678 --- /dev/null +++ b/crates/dojo-core/src/database/storage.cairo @@ -0,0 +1,63 @@ +use array::{ArrayTrait, SpanTrait}; +use option::OptionTrait; +use starknet::SyscallResultTrait; +use traits::Into; +use poseidon::poseidon_hash_span; +use serde::Serde; +use dojo_core::serde::SpanSerde; + +fn get(address_domain: u32, keys: Span) -> felt252 { + let base = starknet::storage_base_address_from_felt252(poseidon_hash_span(keys)); + starknet::storage_read_syscall( + address_domain, starknet::storage_address_from_base(base) + ).unwrap_syscall() +} + +fn get_many(address_domain: u32, keys: Span, offset: u8, length: usize) -> Span { + let base = starknet::storage_base_address_from_felt252(poseidon_hash_span(keys)); + let mut value = ArrayTrait::new(); + + let mut offset = offset; + loop { + if length == offset.into() { + break (); + } + + value + .append( + starknet::storage_read_syscall( + address_domain, starknet::storage_address_from_base_and_offset(base, offset) + ).unwrap_syscall() + ); + + offset += 1; + }; + + value.span() +} + +fn set(address_domain: u32, keys: Span, value: felt252) { + let base = starknet::storage_base_address_from_felt252(poseidon_hash_span(keys)); + starknet::storage_write_syscall( + address_domain, starknet::storage_address_from_base(base), value + ); +} + +fn set_many(address_domain: u32, keys: Span, offset: u8, mut value: Span) { + let base = starknet::storage_base_address_from_felt252(poseidon_hash_span(keys)); + + let mut offset = offset; + loop { + match value.pop_front() { + Option::Some(v) => { + starknet::storage_write_syscall( + address_domain, starknet::storage_address_from_base_and_offset(base, offset), *v + ); + offset += 1 + }, + Option::None(_) => { + break (); + }, + }; + }; +} diff --git a/crates/dojo-core/src/storage/utils.cairo b/crates/dojo-core/src/database/utils.cairo similarity index 100% rename from crates/dojo-core/src/storage/utils.cairo rename to crates/dojo-core/src/database/utils.cairo diff --git a/crates/dojo-core/src/interfaces.cairo b/crates/dojo-core/src/interfaces.cairo index 008f489f6c..7e3f81a76e 100644 --- a/crates/dojo-core/src/interfaces.cairo +++ b/crates/dojo-core/src/interfaces.cairo @@ -1,5 +1,5 @@ use dojo_core::{ - serde::SpanSerde, storage::query::Query, + serde::SpanSerde, database::query::Query, auth::systems::Route, auth::components::AuthRole, execution_context::Context }; use starknet::{ClassHash, ContractAddress}; diff --git a/crates/dojo-core/src/lib.cairo b/crates/dojo-core/src/lib.cairo index b4731236f3..70fab9a833 100644 --- a/crates/dojo-core/src/lib.cairo +++ b/crates/dojo-core/src/lib.cairo @@ -3,7 +3,7 @@ mod executor; mod execution_context; mod interfaces; mod serde; -mod storage; +mod database; mod world; mod world_factory; mod test_utils; diff --git a/crates/dojo-core/src/storage.cairo b/crates/dojo-core/src/storage.cairo deleted file mode 100644 index 62304e6238..0000000000 --- a/crates/dojo-core/src/storage.cairo +++ /dev/null @@ -1,5 +0,0 @@ -mod index; -mod query; -mod kv; -mod db; -mod utils; diff --git a/crates/dojo-core/src/storage/db.cairo b/crates/dojo-core/src/storage/db.cairo deleted file mode 100644 index 87546566ba..0000000000 --- a/crates/dojo-core/src/storage/db.cairo +++ /dev/null @@ -1,92 +0,0 @@ -#[contract] -mod Database { - use array::{ArrayTrait, SpanTrait}; - use traits::{Into, TryInto}; - use serde::Serde; - use hash::LegacyHash; - use poseidon::poseidon_hash_span; - - use dojo_core::serde::SpanSerde; - use dojo_core::storage::{index::Index, kv::KeyValueStore, query::{Query, QueryTrait}}; - use dojo_core::interfaces::{IComponentLibraryDispatcher, IComponentDispatcherTrait}; - - #[event] - fn StoreSetRecord(table_id: felt252, keys: Span, value: Span) {} - - #[event] - fn StoreSetField(table_id: felt252, keys: Span, offset: u8, value: Span) {} - - #[event] - fn StoreDeleteRecord(table_id: felt252, keys: Span) {} - - fn get( - class_hash: starknet::ClassHash, table: felt252, query: Query, offset: u8, length: usize - ) -> Option> { - let mut length = length; - if length == 0 { - length = IComponentLibraryDispatcher { class_hash: class_hash }.len(); - } - - let id = query.hash(); - match Index::exists(table, id) { - bool::False(()) => Option::None(()), - bool::True(()) => Option::Some(KeyValueStore::get(table, id, offset, length)) - } - } - - fn set( - class_hash: starknet::ClassHash, table: felt252, query: Query, offset: u8, value: Span - ) { - let keys = query.keys(); - let id = query.hash(); - - let length = IComponentLibraryDispatcher { class_hash: class_hash }.len(); - assert(value.len() <= length, 'Value too long'); - - Index::create(table, id); - KeyValueStore::set(table, id, offset, value); - - StoreSetRecord(table, keys, value); - StoreSetField(table, keys, offset, value); - } - - fn del(class_hash: starknet::ClassHash, table: felt252, query: Query) { - Index::delete(table, query.hash()); - StoreDeleteRecord(table, query.keys()); - } - - // returns a tuple of spans, first contains the entity IDs, - // second the deserialized entities themselves - fn all( - class_hash: starknet::ClassHash, component: felt252, partition: felt252 - ) -> (Span, Span>) { - let table = { - if partition == 0.into() { - component - } else { - let mut serialized = ArrayTrait::new(); - component.serialize(ref serialized); - partition.serialize(ref serialized); - let hash = poseidon_hash_span(serialized.span()); - hash.into() - } - }; - - let all_ids = Index::query(table); - let length = IComponentLibraryDispatcher { class_hash: class_hash }.len(); - - let mut ids = all_ids.span(); - let mut entities: Array> = ArrayTrait::new(); - loop { - match ids.pop_front() { - Option::Some(id) => { - let value: Span = KeyValueStore::get(table, *id, 0_u8, length); - entities.append(value); - }, - Option::None(_) => { - break (all_ids.span(), entities.span()); - } - }; - } - } -} diff --git a/crates/dojo-core/src/storage/index.cairo b/crates/dojo-core/src/storage/index.cairo deleted file mode 100644 index f74b25c8c4..0000000000 --- a/crates/dojo-core/src/storage/index.cairo +++ /dev/null @@ -1,62 +0,0 @@ -#[contract] -mod Index { - use array::{ArrayTrait, SpanTrait}; - use traits::Into; - use option::OptionTrait; - - struct Storage { - // Maps id to its position in the table. - // NOTE: ids is 1-indexed to allow for 0 - // to be used as a sentinel value. - ids: LegacyMap::<(felt252, felt252), usize>, - table_lens: LegacyMap::, - tables: LegacyMap::<(felt252, usize), felt252>, - } - - fn create(table: felt252, id: felt252) { - if exists(table, id) { - return (); - } - - let table_len = table_lens::read(table); - ids::write((table, id), table_len + 1); - table_lens::write(table, table_len + 1); - tables::write((table, table_len), id); - } - - fn delete(table: felt252, id: felt252) { - if !exists(table, id) { - return (); - } - - let table_len = table_lens::read(table); - let table_idx = ids::read((table, id)) - 1; - ids::write((table, id), 0); - table_lens::write(table, table_len - 1); - - // Replace the deleted element with the last element. - // NOTE: We leave the last element set as to not produce an unncessary state diff. - tables::write((table, table_idx), tables::read((table, table_len - 1))); - } - - fn exists(table: felt252, id: felt252) -> bool { - ids::read((table, id)) != 0 - } - - fn query(table: felt252) -> Array { - let mut res = ArrayTrait::new(); - let table_len = table_lens::read(table); - let mut idx: usize = 0; - - loop { - if idx == table_len { - break (); - } - - res.append(tables::read((table, idx))); - idx += 1; - }; - - res - } -} diff --git a/crates/dojo-core/src/storage/kv.cairo b/crates/dojo-core/src/storage/kv.cairo deleted file mode 100644 index 924d57af49..0000000000 --- a/crates/dojo-core/src/storage/kv.cairo +++ /dev/null @@ -1,67 +0,0 @@ -mod KeyValueStore { - use array::{ArrayTrait, SpanTrait}; - use traits::Into; - use starknet::SyscallResultTrait; - use option::OptionTrait; - - use dojo_core::serde::SpanSerde; - - fn address(table: felt252, key: felt252) -> starknet::StorageBaseAddress { - starknet::storage_base_address_from_felt252(hash::LegacyHash::hash(table.into(), key)) - } - - #[view] - fn get(table: felt252, key: felt252, offset: u8, length: usize) -> Span { - let address_domain = 0; - let base = address(table, key); - let mut value = ArrayTrait::new(); - _get(address_domain, base, ref value, offset, length); - value.span() - } - - fn _get( - address_domain: u32, - base: starknet::StorageBaseAddress, - ref value: Array, - offset: u8, - length: usize - ) { - if length == offset.into() { - return (); - } - - value - .append( - starknet::storage_read_syscall( - address_domain, starknet::storage_address_from_base_and_offset(base, offset) - ) - .unwrap_syscall() - ); - - return _get(address_domain, base, ref value, offset + 1, length); - } - - #[external] - fn set(table: felt252, query: felt252, offset: u8, value: Span) { - let address_domain = 0; - let base = address(table, query); - _set(address_domain, base, value, offset: offset); - } - - fn _set( - address_domain: u32, - base: starknet::StorageBaseAddress, - mut value: Span, - offset: u8 - ) { - match value.pop_front() { - Option::Some(v) => { - starknet::storage_write_syscall( - address_domain, starknet::storage_address_from_base_and_offset(base, offset), *v - ); - _set(address_domain, base, value, offset + 1); - }, - Option::None(_) => {}, - } - } -} diff --git a/crates/dojo-core/src/world.cairo b/crates/dojo-core/src/world.cairo index bb80f9ef76..95a3787367 100644 --- a/crates/dojo-core/src/world.cairo +++ b/crates/dojo-core/src/world.cairo @@ -10,7 +10,8 @@ mod World { contract_address::ContractAddressIntoFelt252, ClassHash, Zeroable, ContractAddress }; - use dojo_core::storage::{db::Database, query::{Query, QueryTrait}}; + use dojo_core::database; + use dojo_core::database::{query::{Query, QueryTrait}}; use dojo_core::execution_context::Context; use dojo_core::auth::components::AuthRole; use dojo_core::auth::systems::Route; @@ -278,7 +279,7 @@ mod World { // Set the entity let table = query.table(component); let component_class_hash = component_registry::read(component); - Database::set(component_class_hash, table, query, offset, value) + database::set(component_class_hash, table, query, offset, value) } /// Delete a component from an entity @@ -303,7 +304,7 @@ mod World { // Delete the entity let table = query.table(component); let component_class_hash = component_registry::read(component); - let res = Database::del(component_class_hash, component.into(), query); + let res = database::del(component_class_hash, component.into(), query); } /// Get the component value for an entity @@ -321,7 +322,7 @@ mod World { fn entity(component: felt252, query: Query, offset: u8, length: usize) -> Span { let class_hash = component_registry::read(component); let table = query.table(component); - match Database::get(class_hash, table, query, offset, length) { + match database::get(class_hash, table, query, offset, length) { Option::Some(res) => res, Option::None(_) => { ArrayTrait::new().span() @@ -343,7 +344,7 @@ mod World { #[view] fn entities(component: felt252, partition: felt252) -> (Span, Span>) { let class_hash = component_registry::read(component); - Database::all(class_hash, component.into(), partition) + database::all(class_hash, component.into(), partition) } /// Set the executor contract address diff --git a/crates/dojo-core/tests/src/storage.cairo b/crates/dojo-core/tests/src/database.cairo similarity index 100% rename from crates/dojo-core/tests/src/storage.cairo rename to crates/dojo-core/tests/src/database.cairo diff --git a/crates/dojo-core/tests/src/database/index.cairo b/crates/dojo-core/tests/src/database/index.cairo new file mode 100644 index 0000000000..c48b2fd3c7 --- /dev/null +++ b/crates/dojo-core/tests/src/database/index.cairo @@ -0,0 +1,65 @@ +use array::ArrayTrait; +use traits::Into; + +use dojo_core::database::index; + +#[test] +#[available_gas(2000000)] +fn test_index_entity() { + let no_query = index::get(0, 69); + assert(no_query.len() == 0, 'entity indexed'); + + index::create(0, 69, 420); + let query = index::get(0, 69); + assert(query.len() == 1, 'entity not indexed'); + assert(*query.at(0) == 420, 'entity value incorrect'); + + index::create(0, 69, 420); + let noop_query = index::get(0, 69); + assert(noop_query.len() == 1, 'index should be noop'); + + index::create(0, 69, 1337); + let two_query = index::get(0, 69); + assert(two_query.len() == 2, 'index should have two query'); + assert(*two_query.at(1) == 1337, 'entity value incorrect'); +} + +#[test] +#[available_gas(2000000)] +fn test_entity_delete_basic() { + index::create(0, 69, 420); + let query = index::get(0, 69); + assert(query.len() == 1, 'entity not indexed'); + assert(*query.at(0) == 420, 'entity value incorrect'); + + assert(index::exists(0, 69, 420), 'entity should exist'); + + index::delete(0, 69, 420); + + assert(!index::exists(0, 69, 420), 'entity should not exist'); + let no_query = index::get(0, 69); + assert(no_query.len() == 0, 'index should have no query'); +} + +#[test] +#[available_gas(20000000)] +fn test_entity_query_delete_shuffle() { + let table = 1; + index::create(0, table, 10); + index::create(0, table, 20); + index::create(0, table, 30); + assert(index::get(0, table).len() == 3, 'wrong size'); + + index::delete(0, table, 10); + let entities = index::get(0, table); + assert(entities.len() == 2, 'wrong size'); + assert(*entities.at(0) == 30, 'idx 0 not 30'); + assert(*entities.at(1) == 20, 'idx 1 not 20'); +} + +#[test] +#[available_gas(20000000)] +fn test_entity_query_delete_non_existing() { + assert(index::get(0, 69).len() == 0, 'table len != 0'); + index::delete(0, 69, 999); // deleting non-existing should not panic +} diff --git a/crates/dojo-core/tests/src/storage/query.cairo b/crates/dojo-core/tests/src/database/query.cairo similarity index 87% rename from crates/dojo-core/tests/src/storage/query.cairo rename to crates/dojo-core/tests/src/database/query.cairo index 8fc88906e1..5fd9547516 100644 --- a/crates/dojo-core/tests/src/storage/query.cairo +++ b/crates/dojo-core/tests/src/database/query.cairo @@ -8,11 +8,11 @@ use zeroable::IsZeroResult; use starknet::ClassHashIntoFelt252; use poseidon::poseidon_hash_span; use dojo_core::serde::SpanSerde; -use dojo_core::storage::query::IntoPartitioned; -use dojo_core::storage::query::TupleSize2IntoQuery; -use dojo_core::storage::query::TupleSize3IntoQuery; -use dojo_core::storage::query::Query; -use dojo_core::storage::query::QueryTrait; +use dojo_core::database::query::IntoPartitioned; +use dojo_core::database::query::TupleSize2IntoQuery; +use dojo_core::database::query::TupleSize3IntoQuery; +use dojo_core::database::query::Query; +use dojo_core::database::query::QueryTrait; #[test] #[available_gas(2000000)] diff --git a/crates/dojo-core/tests/src/storage/utils.cairo b/crates/dojo-core/tests/src/database/utils.cairo similarity index 98% rename from crates/dojo-core/tests/src/storage/utils.cairo rename to crates/dojo-core/tests/src/database/utils.cairo index 5e02711c31..c1f495b8c7 100644 --- a/crates/dojo-core/tests/src/storage/utils.cairo +++ b/crates/dojo-core/tests/src/database/utils.cairo @@ -2,7 +2,7 @@ use array::{ArrayTrait, SpanTrait}; use option::OptionTrait; use traits::Into; -use dojo_core::storage::utils::find_matching; +use dojo_core::database::utils::find_matching; fn build_fake_entity(v: felt252) -> Span { let mut e = ArrayTrait::new(); diff --git a/crates/dojo-core/tests/src/lib.cairo b/crates/dojo-core/tests/src/lib.cairo index 47d74b77b5..0c1fe3adc7 100644 --- a/crates/dojo-core/tests/src/lib.cairo +++ b/crates/dojo-core/tests/src/lib.cairo @@ -1,4 +1,4 @@ mod executor; -mod storage; +mod database; mod world; mod world_factory; diff --git a/crates/dojo-core/tests/src/storage/index.cairo b/crates/dojo-core/tests/src/storage/index.cairo deleted file mode 100644 index a429b4b09f..0000000000 --- a/crates/dojo-core/tests/src/storage/index.cairo +++ /dev/null @@ -1,65 +0,0 @@ -use array::ArrayTrait; -use traits::Into; - -use dojo_core::storage::index::Index; - -#[test] -#[available_gas(2000000)] -fn test_index_entity() { - let no_query = Index::query(69.into()); - assert(no_query.len() == 0, 'entity indexed'); - - Index::create(69.into(), 420.into()); - let query = Index::query(69.into()); - assert(query.len() == 1, 'entity not indexed'); - assert(*query.at(0) == 420.into(), 'entity value incorrect'); - - Index::create(69.into(), 420.into()); - let noop_query = Index::query(69.into()); - assert(noop_query.len() == 1, 'index should be noop'); - - Index::create(69.into(), 1337.into()); - let two_query = Index::query(69.into()); - assert(two_query.len() == 2, 'index should have two query'); - assert(*two_query.at(1) == 1337.into(), 'entity value incorrect'); -} - -#[test] -#[available_gas(2000000)] -fn test_entity_delete_basic() { - Index::create(69.into(), 420.into()); - let query = Index::query(69.into()); - assert(query.len() == 1, 'entity not indexed'); - assert(*query.at(0) == 420.into(), 'entity value incorrect'); - - assert(Index::exists(69.into(), 420.into()), 'entity should exist'); - - Index::delete(69.into(), 420.into()); - - assert(!Index::exists(69.into(), 420.into()), 'entity should not exist'); - let no_query = Index::query(69.into()); - assert(no_query.len() == 0, 'index should have no query'); -} - -#[test] -#[available_gas(20000000)] -fn test_entity_query_delete_shuffle() { - let table = 1.into(); - Index::create(table, 10.into()); - Index::create(table, 20.into()); - Index::create(table, 30.into()); - assert(Index::query(table).len() == 3, 'wrong size'); - - Index::delete(table, 10.into()); - let entities = Index::query(table); - assert(entities.len() == 2, 'wrong size'); - assert(*entities.at(0) == 30.into(), 'idx 0 not 30'); - assert(*entities.at(1) == 20.into(), 'idx 1 not 20'); -} - -#[test] -#[available_gas(20000000)] -fn test_entity_query_delete_non_existing() { - assert(Index::query(69.into()).len() == 0, 'table len != 0'); - Index::delete(69.into(), 999.into()); // deleting non-existing should not panic -} diff --git a/crates/dojo-core/tests/src/world.cairo b/crates/dojo-core/tests/src/world.cairo index 5db088548f..d14705fcb7 100644 --- a/crates/dojo-core/tests/src/world.cairo +++ b/crates/dojo-core/tests/src/world.cairo @@ -9,7 +9,7 @@ use starknet::class_hash::Felt252TryIntoClassHash; use starknet::syscalls::deploy_syscall; use starknet::contract_address_const; -use dojo_core::storage::query::QueryTrait; +use dojo_core::database::query::QueryTrait; use dojo_core::interfaces::IWorldDispatcher; use dojo_core::interfaces::IWorldDispatcherTrait; use dojo_core::executor::Executor; diff --git a/crates/dojo-erc/src/erc20/erc20.cairo b/crates/dojo-erc/src/erc20/erc20.cairo index 26cc3f5386..e4d9e75e5d 100644 --- a/crates/dojo-erc/src/erc20/erc20.cairo +++ b/crates/dojo-erc/src/erc20/erc20.cairo @@ -14,7 +14,7 @@ mod ERC20 { use traits::Into; use zeroable::Zeroable; - use dojo_core::storage::query::{ + use dojo_core::database::query::{ Query, LiteralIntoQuery, TupleSize1IntoQuery, diff --git a/crates/dojo-lang/src/commands/entities.rs b/crates/dojo-lang/src/commands/entities.rs index 8cde03c3df..6401bdd044 100644 --- a/crates/dojo-lang/src/commands/entities.rs +++ b/crates/dojo-lang/src/commands/entities.rs @@ -81,7 +81,7 @@ impl CommandTrait for EntitiesCommand { command.data.rewrite_nodes.push(RewriteNode::interpolate_patched( " - let mut __$query_id$_matching_entities = dojo_core::storage::utils::find_matching( + let mut __$query_id$_matching_entities = dojo_core::database::utils::find_matching( __$query_id$_ids.span(), __$query_id$_entities_raw.span() ); diff --git a/crates/dojo-lang/src/plugin_test_data/system b/crates/dojo-lang/src/plugin_test_data/system index 7dd898c1d6..febbfb1109 100644 --- a/crates/dojo-lang/src/plugin_test_data/system +++ b/crates/dojo-lang/src/plugin_test_data/system @@ -262,14 +262,14 @@ mod Spawn { use dojo_core::world; use dojo_core::interfaces::IWorldDispatcher; use dojo_core::interfaces::IWorldDispatcherTrait; - use dojo_core::storage::query::Query; - use dojo_core::storage::query::QueryTrait; - use dojo_core::storage::query::LiteralIntoQuery; - use dojo_core::storage::query::TupleSize1IntoQuery; - use dojo_core::storage::query::TupleSize2IntoQuery; - use dojo_core::storage::query::TupleSize3IntoQuery; - use dojo_core::storage::query::IntoPartitioned; - use dojo_core::storage::query::IntoPartitionedQuery; + use dojo_core::database::query::Query; + use dojo_core::database::query::QueryTrait; + use dojo_core::database::query::LiteralIntoQuery; + use dojo_core::database::query::TupleSize1IntoQuery; + use dojo_core::database::query::TupleSize2IntoQuery; + use dojo_core::database::query::TupleSize3IntoQuery; + use dojo_core::database::query::IntoPartitioned; + use dojo_core::database::query::IntoPartitionedQuery; use dojo_core::execution_context::Context; #[view] @@ -423,14 +423,14 @@ mod Move { use dojo_core::world; use dojo_core::interfaces::IWorldDispatcher; use dojo_core::interfaces::IWorldDispatcherTrait; - use dojo_core::storage::query::Query; - use dojo_core::storage::query::QueryTrait; - use dojo_core::storage::query::LiteralIntoQuery; - use dojo_core::storage::query::TupleSize1IntoQuery; - use dojo_core::storage::query::TupleSize2IntoQuery; - use dojo_core::storage::query::TupleSize3IntoQuery; - use dojo_core::storage::query::IntoPartitioned; - use dojo_core::storage::query::IntoPartitionedQuery; + use dojo_core::database::query::Query; + use dojo_core::database::query::QueryTrait; + use dojo_core::database::query::LiteralIntoQuery; + use dojo_core::database::query::TupleSize1IntoQuery; + use dojo_core::database::query::TupleSize2IntoQuery; + use dojo_core::database::query::TupleSize3IntoQuery; + use dojo_core::database::query::IntoPartitioned; + use dojo_core::database::query::IntoPartitionedQuery; use dojo_core::execution_context::Context; #[view] @@ -473,7 +473,7 @@ mod Move { __positions_query_ids.append(__positions_query_player_ids); __positions_query_entities_raw.append(__positions_query_player_raw); - let mut __positions_query_matching_entities = dojo_core::storage::utils::find_matching( + let mut __positions_query_matching_entities = dojo_core::database::utils::find_matching( __positions_query_ids.span(), __positions_query_entities_raw.span() ); @@ -536,7 +536,7 @@ mod Move { __players_query_ids.append(__players_query_player_ids); __players_query_entities_raw.append(__players_query_player_raw); - let mut __players_query_matching_entities = dojo_core::storage::utils::find_matching( + let mut __players_query_matching_entities = dojo_core::database::utils::find_matching( __players_query_ids.span(), __players_query_entities_raw.span() ); @@ -631,7 +631,7 @@ mod Move { __positions_query_entities_raw.append(__positions_query_player_raw); let mut __positions_query_matching_entities = - dojo_core::storage::utils::find_matching( + dojo_core::database::utils::find_matching( __positions_query_ids.span(), __positions_query_entities_raw.span() ); @@ -830,7 +830,7 @@ mod Move { __positions_query_entities_raw.append(__positions_query_player_raw); let mut __positions_query_matching_entities = - dojo_core::storage::utils::find_matching( + dojo_core::database::utils::find_matching( __positions_query_ids.span(), __positions_query_entities_raw.span() ); @@ -960,14 +960,14 @@ mod Proxy { use dojo_core::world; use dojo_core::interfaces::IWorldDispatcher; use dojo_core::interfaces::IWorldDispatcherTrait; - use dojo_core::storage::query::Query; - use dojo_core::storage::query::QueryTrait; - use dojo_core::storage::query::LiteralIntoQuery; - use dojo_core::storage::query::TupleSize1IntoQuery; - use dojo_core::storage::query::TupleSize2IntoQuery; - use dojo_core::storage::query::TupleSize3IntoQuery; - use dojo_core::storage::query::IntoPartitioned; - use dojo_core::storage::query::IntoPartitionedQuery; + use dojo_core::database::query::Query; + use dojo_core::database::query::QueryTrait; + use dojo_core::database::query::LiteralIntoQuery; + use dojo_core::database::query::TupleSize1IntoQuery; + use dojo_core::database::query::TupleSize2IntoQuery; + use dojo_core::database::query::TupleSize3IntoQuery; + use dojo_core::database::query::IntoPartitioned; + use dojo_core::database::query::IntoPartitionedQuery; use dojo_core::execution_context::Context; #[view] @@ -1000,12 +1000,12 @@ error: Plugin diagnostic: Component types cannot be empty let err = commands::<>::entity(player_id.into()); ^************************************^ -error: Plugin diagnostic: Unexpected argument type. Expected: "dojo_core::storage::query::Query", found: "(?89, ?90, ?91, ?92, ?93)". +error: Plugin diagnostic: Unexpected argument type. Expected: "dojo_core::database::query::Query", found: "(?89, ?90, ?91, ?92, ?93)". --> Spawn:95:61 ctx.world.set_entity(ctx, 'Player', (0, 0, 0, 0, 0), 0_u8, array::ArrayTrait::span(@calldata)); ^*************^ -error: Plugin diagnostic: Unexpected argument type. Expected: "dojo_core::storage::query::Query", found: "(?101, ?102, ?103, ?104, ?105)". +error: Plugin diagnostic: Unexpected argument type. Expected: "dojo_core::database::query::Query", found: "(?101, ?102, ?103, ?104, ?105)". --> Spawn:101:63 ctx.world.set_entity(ctx, 'Position', (0, 0, 0, 0, 0), 0_u8, array::ArrayTrait::span(@calldata)); ^*************^ diff --git a/crates/dojo-lang/src/system.rs b/crates/dojo-lang/src/system.rs index 190442764a..74793be3d4 100644 --- a/crates/dojo-lang/src/system.rs +++ b/crates/dojo-lang/src/system.rs @@ -52,16 +52,16 @@ impl System { use dojo_core::world; use dojo_core::interfaces::IWorldDispatcher; use dojo_core::interfaces::IWorldDispatcherTrait; - use dojo_core::storage::query::Query; - use dojo_core::storage::query::QueryTrait; - use dojo_core::storage::query::LiteralIntoQuery; - use dojo_core::storage::query::TupleSize1IntoQuery; - use dojo_core::storage::query::TupleSize2IntoQuery; - use dojo_core::storage::query::TupleSize3IntoQuery; - use dojo_core::storage::query::IntoPartitioned; - use dojo_core::storage::query::IntoPartitionedQuery; + use dojo_core::database::query::Query; + use dojo_core::database::query::QueryTrait; + use dojo_core::database::query::LiteralIntoQuery; + use dojo_core::database::query::TupleSize1IntoQuery; + use dojo_core::database::query::TupleSize2IntoQuery; + use dojo_core::database::query::TupleSize3IntoQuery; + use dojo_core::database::query::IntoPartitioned; + use dojo_core::database::query::IntoPartitionedQuery; use dojo_core::execution_context::Context; - + #[view] fn name() -> felt252 { '$name$' diff --git a/crates/dojo-world/src/component.rs b/crates/dojo-world/src/component.rs index 82083c5bf5..02f728f314 100644 --- a/crates/dojo-world/src/component.rs +++ b/crates/dojo-world/src/component.rs @@ -1,11 +1,11 @@ use std::vec; -use starknet::core::crypto::pedersen_hash; use starknet::core::types::{BlockId, FieldElement, FunctionCall}; use starknet::core::utils::{ cairo_short_string_to_felt, get_selector_from_name, parse_cairo_short_string, CairoShortStringToFeltError, ParseCairoShortStringError, }; +use starknet::macros::short_string; use starknet::providers::{Provider, ProviderError}; use starknet_crypto::poseidon_hash_many; @@ -111,14 +111,11 @@ impl<'a, P: Provider + Sync> ComponentReader<'a, P> { } else { poseidon_hash_many(&[self.name, partition_id]) }; - let keys_hash = if keys.len() == 1 { - keys[0] - } else { - let mut keys = keys; - keys.insert(0, keys.len().into()); - poseidon_hash_many(&keys) - }; - let key = pedersen_hash(&table, &keys_hash); + + let mut keys_qualified = vec![(keys.len() + 2).into(), short_string!("dojo_storage"), table]; + keys_qualified.extend(keys); + + let key = poseidon_hash_many(&keys_qualified); let mut values = vec![]; for member in members {