Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 21 additions & 0 deletions bin/sozo/src/commands/events.rs
Original file line number Diff line number Diff line change
Expand Up @@ -365,6 +365,27 @@ async fn match_event<P: Provider + Send + Sync>(
),
)
}
WorldEvent::StoreUpdateMembers(e) => {
let tag = get_tag(e.selector, &tags);
(
format!("Store update member ({})", tag),
format!(
"Selector: {:#066x}\nEntity ID: {:#066x}\nMember selectors: {}\nValues: {}",
e.selector,
e.entity_id,
e.member_selectors
.iter()
.map(|v| format!("{:#066x}", v))
.collect::<Vec<String>>()
.join(", "),
e.values
.iter()
.map(|v| format!("{:#066x}", v))
.collect::<Vec<String>>()
.join(", "),
),
)
}
WorldEvent::StoreDelRecord(e) => {
let tag = get_tag(e.selector, &tags);
(
Expand Down
22 changes: 19 additions & 3 deletions crates/dojo/core-tests/src/tests/model/model.cairo
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ struct Foo3 {
v2: u32,
}

#[derive(Copy, Drop, Serde, Debug, Introspect)]
#[derive(Copy, Drop, Serde, Debug, Introspect, PartialEq)]
struct AStruct {
a: u8,
b: u8,
Expand All @@ -46,7 +46,7 @@ struct AStruct {
}

#[dojo::model]
#[derive(Copy, Drop, Serde, Debug)]
#[derive(Copy, Drop, Serde, Debug, PartialEq)]
struct Foo4 {
#[key]
id: felt252,
Expand All @@ -56,7 +56,7 @@ struct Foo4 {
v3: AStruct,
}

#[derive(Copy, Drop, Serde, Debug, Introspect)]
#[derive(Copy, Drop, Serde, Debug, Introspect, PartialEq)]
struct FooSchema {
v0: u256,
v3: AStruct,
Expand Down Expand Up @@ -390,6 +390,22 @@ fn test_write_members() {
assert!(v2s_read == array![foo.v2, foo2.v2]);
}


#[test]
fn test_write_schema() {
let mut world = spawn_foo_world();
let foo = Foo4 { id: 1, v0: 2, v1: 3, v2: 4, v3: AStruct { a: 5, b: 6, c: 7, d: 8 } };
world.write_model(@foo);
let new_struct = AStruct { a: 10, b: 11, c: 12, d: 13 };
let schema = FooSchema { v0: 42, v3: new_struct };
let mut new_foo = foo;
new_foo.v0 = schema.v0;
new_foo.v3 = schema.v3;
world.write_schema(foo.ptr(), @schema);
let model: Foo4 = world.read_model(foo.id);
assert_eq!(model, new_foo);
}

#[test]
fn test_ptr_from() {
let foo = Foo { k1: 1, k2: 2, v1: 3, v2: 4 };
Expand Down
2 changes: 1 addition & 1 deletion crates/dojo/core/src/lib.cairo
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ pub mod meta {
pub use introspect::{Introspect, Ty, TyCompareTrait};

pub mod layout;
pub use layout::{FieldLayout, Layout, LayoutCompareTrait};
pub use layout::{FieldLayout, FieldLayoutsTrait, Layout, LayoutTrait};
}

pub mod model {
Expand Down
19 changes: 18 additions & 1 deletion crates/dojo/core/src/meta/layout.cairo
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ pub enum Layout {
}

#[generate_trait]
pub impl LayoutCompareImpl of LayoutCompareTrait {
pub impl LayoutImpl of LayoutTrait {
fn is_same_type_of(self: @Layout, old: @Layout) -> bool {
match (self, old) {
(Layout::Fixed(_), Layout::Fixed(_)) => true,
Expand All @@ -37,6 +37,23 @@ pub impl LayoutCompareImpl of LayoutCompareTrait {
_ => false,
}
}
fn struct_fields(self: @Layout) -> Span<FieldLayout> {
match self {
Layout::Struct(fields) => *fields,
_ => { panic!("Unexpected layout type for a Struct.") },
}
}
}

#[generate_trait]
pub impl FieldLayoutsImpl of FieldLayoutsTrait {
fn selectors(self: Span<FieldLayout>) -> Array<felt252> {
let mut selectors: Array<felt252> = Default::default();
for field_layout in self {
selectors.append(*field_layout.selector);
};
selectors
}
}

/// Compute the full size in bytes of a layout, when all the fields
Expand Down
1 change: 1 addition & 0 deletions crates/dojo/core/src/model/definition.cairo
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ pub enum ModelIndex {
Id: felt252,
// (entity_id, member_id)
MemberId: (felt252, felt252),
Schema: felt252,
}

/// The `ModelDefinition` trait.
Expand Down
9 changes: 9 additions & 0 deletions crates/dojo/core/src/model/model.cairo
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ pub struct ModelPtr<M> {
/// Converts the span of model pointers to a span of model indexes.
pub trait ModelPtrsTrait<M> {
fn to_indexes(self: Span<ModelPtr<M>>) -> Span<ModelIndex>;
fn to_schemas(self: Span<ModelPtr<M>>) -> Span<ModelIndex>;
fn to_member_indexes(self: Span<ModelPtr<M>>, field_selector: felt252) -> Span<ModelIndex>;
}

Expand All @@ -33,6 +34,14 @@ pub impl ModelPtrsImpl<M> of ModelPtrsTrait<M> {
ids.span()
}

fn to_schemas(self: Span<ModelPtr<M>>) -> Span<ModelIndex> {
let mut ids = ArrayTrait::<ModelIndex>::new();
for ptr in self {
ids.append(ModelIndex::Schema(*ptr.id));
};
ids.span()
}

fn to_member_indexes(self: Span<ModelPtr<M>>, field_selector: felt252) -> Span<ModelIndex> {
let mut ids = ArrayTrait::<ModelIndex>::new();
for ptr in self {
Expand Down
8 changes: 8 additions & 0 deletions crates/dojo/core/src/model/storage.cairo
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,14 @@ pub trait ModelStorage<S, M> {
self: @S, ptrs: Span<ModelPtr<M>>,
) -> Array<T>;

/// Writes a subset of members in a model, matching a defined schema <T>.
fn write_schema<T, +Serde<T>, +Introspect<T>>(ref self: S, ptr: ModelPtr<M>, schema: @T);

/// Writes part of multiple models, matching a schema.
fn write_schemas<T, +Serde<T>, +Introspect<T>>(
ref self: S, ptrs: Span<ModelPtr<M>>, schemas: Span<@T>,
);

/// Returns the current namespace hash.
fn namespace_hash(self: @S) -> felt252;
}
Expand Down
1 change: 1 addition & 0 deletions crates/dojo/core/src/world/errors.cairo
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
use starknet::ContractAddress;

pub const DELETE_ENTITY_MEMBER: felt252 = 'Cannot delete entity member';
pub const DELETE_ENTITY_SCHEMA: felt252 = 'Cannot delete entity schema';

pub fn lengths_mismatch(a: @ByteArray, b: @ByteArray, context: @ByteArray) -> ByteArray {
format!("Length mismatch: `{a}` and `{b}` in `{context}`")
Expand Down
33 changes: 31 additions & 2 deletions crates/dojo/core/src/world/storage.cairo
Original file line number Diff line number Diff line change
Expand Up @@ -307,7 +307,7 @@ pub impl ModelStorageWorldStorageImpl<M, +Model<M>, +Drop<M>> of ModelStorage<Wo
IWorldDispatcherTrait::entity(
*self.dispatcher,
Model::<M>::selector(*self.namespace_hash),
ModelIndex::Id(ptr.id),
ModelIndex::Schema(ptr.id),
Introspect::<T>::layout(),
),
)
Expand All @@ -321,14 +321,43 @@ pub impl ModelStorageWorldStorageImpl<M, +Model<M>, +Drop<M>> of ModelStorage<Wo
for entity in IWorldDispatcherTrait::entities(
*self.dispatcher,
Model::<M>::selector(*self.namespace_hash),
ptrs.to_indexes(),
ptrs.to_schemas(),
Introspect::<T>::layout(),
) {
values.append(deserialize_unwrap(*entity));
}
values
}

fn write_schema<T, +Serde<T>, +Introspect<T>>(
ref self: WorldStorage, ptr: ModelPtr<M>, schema: @T,
) {
IWorldDispatcherTrait::set_entity(
self.dispatcher,
Model::<M>::selector(self.namespace_hash),
ModelIndex::Schema(ptr.id),
serialize_inline(schema),
Introspect::<T>::layout(),
);
}

fn write_schemas<T, +Serde<T>, +Introspect<T>>(
ref self: WorldStorage, ptrs: Span<ModelPtr<M>>, schemas: Span<@T>,
) {
let mut serialized_schemas = ArrayTrait::<Span<felt252>>::new();
for schema in schemas {
serialized_schemas.append(serialize_inline(*schema));
};

IWorldDispatcherTrait::set_entities(
self.dispatcher,
Model::<M>::selector(self.namespace_hash),
ptrs.to_schemas(),
serialized_schemas.span(),
Introspect::<T>::layout(),
);
}

fn namespace_hash(self: @WorldStorage) -> felt252 {
*self.namespace_hash
}
Expand Down
35 changes: 31 additions & 4 deletions crates/dojo/core/src/world/world_contract.cairo
Original file line number Diff line number Diff line change
Expand Up @@ -30,9 +30,9 @@ pub mod world {
IUpgradeableDispatcher, IUpgradeableDispatcherTrait,
};
use dojo::meta::{
IDeployedResourceDispatcher, IDeployedResourceDispatcherTrait,
IDeployedResourceLibraryDispatcher, IStoredResourceDispatcher,
IStoredResourceDispatcherTrait, Layout, LayoutCompareTrait, TyCompareTrait,
Layout, IStoredResourceDispatcher, IStoredResourceDispatcherTrait,
IDeployedResourceDispatcher, IDeployedResourceDispatcherTrait, FieldLayoutsTrait,
LayoutTrait, IDeployedResourceLibraryDispatcher, TyCompareTrait,
};
use dojo::model::{Model, ModelIndex, ResourceMetadata, metadata};
use dojo::storage;
Expand Down Expand Up @@ -76,6 +76,7 @@ pub mod world {
StoreSetRecord: StoreSetRecord,
StoreUpdateRecord: StoreUpdateRecord,
StoreUpdateMember: StoreUpdateMember,
StoreUpdateMembers: StoreUpdateMembers,
StoreDelRecord: StoreDelRecord,
WriterUpdated: WriterUpdated,
OwnerUpdated: OwnerUpdated,
Expand Down Expand Up @@ -230,6 +231,16 @@ pub mod world {
pub values: Span<felt252>,
}

#[derive(Drop, starknet::Event)]
pub struct StoreUpdateMembers {
#[key]
pub selector: felt252,
#[key]
pub entity_id: felt252,
pub member_selectors: Span<felt252>,
pub values: Span<felt252>,
}

#[derive(Drop, starknet::Event)]
pub struct StoreDelRecord {
#[key]
Expand Down Expand Up @@ -1431,6 +1442,20 @@ pub mod world {
},
);
},
ModelIndex::Schema(entity_id) => {
let fields = layout.struct_fields();
let mut offset = 0;
storage::layout::write_struct_layout(
model_selector, entity_id, values, ref offset, fields,
);
let member_selectors = fields.selectors().span();
self
.emit(
StoreUpdateMembers {
selector: model_selector, entity_id, member_selectors, values,
},
);
},
}
}

Expand All @@ -1455,6 +1480,7 @@ pub mod world {
self.emit(StoreDelRecord { selector: model_selector, entity_id });
},
ModelIndex::MemberId(_) => { panic_with_felt252(errors::DELETE_ENTITY_MEMBER); },
ModelIndex::Schema(_) => { panic_with_felt252(errors::DELETE_ENTITY_SCHEMA); },
}
}

Expand All @@ -1473,7 +1499,8 @@ pub mod world {
let entity_id = entity_id_from_serialized_keys(keys);
storage::entity_model::read_model_entity(model_selector, entity_id, layout)
},
ModelIndex::Id(entity_id) => {
ModelIndex::Id(entity_id) |
ModelIndex::Schema(entity_id) => {
storage::entity_model::read_model_entity(model_selector, entity_id, layout)
},
ModelIndex::MemberId((
Expand Down
Loading
Loading