Skip to content
This repository has been archived by the owner on Nov 11, 2024. It is now read-only.

Commit

Permalink
feat: add model upgradeability checks
Browse files Browse the repository at this point in the history
  • Loading branch information
glihm committed Nov 5, 2024
1 parent 64b572d commit 59becbf
Show file tree
Hide file tree
Showing 17 changed files with 307 additions and 128 deletions.
4 changes: 1 addition & 3 deletions crates/contracts/src/contract/interface.cairo
Original file line number Diff line number Diff line change
@@ -1,4 +1,2 @@
#[starknet::interface]
pub trait IContract<T> {
fn dojo_name(self: @T) -> ByteArray;
}
pub trait IContract<T> {}
31 changes: 31 additions & 0 deletions crates/contracts/src/event/component.cairo
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
use dojo::event::{Event, IEvent, EventDefinition};
use dojo::meta::{Layout, introspect::Struct};

#[starknet::embeddable]
pub impl IDeployedEventImpl<
TContractState, E, +Event<E>
> of dojo::meta::interface::IDeployedResource<TContractState> {
fn dojo_name(self: @TContractState) -> ByteArray {
Event::<E>::name()
}
}

#[starknet::embeddable]
pub impl IStoredEventImpl<
TContractState, E, +Event<E>
> of dojo::meta::interface::IStoredResource<TContractState> {
fn schema(self: @TContractState) -> Struct {
Event::<E>::schema()
}

fn layout(self: @TContractState) -> Layout {
Event::<E>::layout()
}
}

#[starknet::embeddable]
pub impl IEventImpl<TContractState, E, +Event<E>> of IEvent<TContractState> {
fn definition(self: @TContractState) -> EventDefinition {
Event::<E>::definition()
}
}
8 changes: 4 additions & 4 deletions crates/contracts/src/event/event.cairo
Original file line number Diff line number Diff line change
@@ -1,20 +1,20 @@
use dojo::meta::Layout;
use dojo::meta::introspect::Ty;
use dojo::meta::introspect::Struct;

#[derive(Drop, Serde, Debug, PartialEq)]
pub struct EventDefinition {
pub name: ByteArray,
pub layout: Layout,
pub schema: Ty
pub schema: Struct
}

pub trait Event<T> {
fn name() -> ByteArray;
fn definition() -> EventDefinition;
fn layout() -> Layout;
fn schema() -> Ty;
fn schema() -> Struct;
fn keys(self: @T) -> Span<felt252>;
fn values(self: @T) -> Span<felt252>;
/// Returns the selector of the model computed for the given namespace hash.
/// Returns the selector of the event computed for the given namespace hash.
fn selector(namespace_hash: felt252) -> felt252;
}
10 changes: 1 addition & 9 deletions crates/contracts/src/event/interface.cairo
Original file line number Diff line number Diff line change
@@ -1,12 +1,4 @@
use dojo::meta::Layout;
use dojo::meta::introspect::Ty;

use super::EventDefinition;

#[starknet::interface]
pub trait IEvent<T> {
fn dojo_name(self: @T) -> ByteArray;
fn definition(self: @T) -> EventDefinition;
fn layout(self: @T) -> Layout;
fn schema(self: @T) -> Ty;
fn definition(self: @T) -> super::EventDefinition;
}
18 changes: 12 additions & 6 deletions crates/contracts/src/lib.cairo
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@ pub mod contract {
}

pub mod event {
pub mod component;

pub mod event;
pub use event::{Event, EventDefinition};

Expand All @@ -20,11 +22,17 @@ pub mod event {
}

pub mod meta {
pub mod interface;
pub use interface::{
IDeployedResource, IDeployedResourceDispatcher, IDeployedResourceDispatcherTrait,
IStoredResource, IStoredResourceDispatcher, IStoredResourceDispatcherTrait
};

pub mod introspect;
pub use introspect::{Introspect, Ty};
pub use introspect::{Introspect, Ty, StructCompareTrait};

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

pub mod model {
Expand All @@ -34,7 +42,7 @@ pub mod model {
pub use definition::{ModelIndex, ModelDefinition, ModelDef};

pub mod model;
pub use model::{Model, KeyParser};
pub use model::{Model, KeyParser, ModelPtr};

pub mod model_value;
pub use model_value::{ModelValue, ModelValueKey};
Expand All @@ -46,9 +54,7 @@ pub mod model {
pub use metadata::ResourceMetadata;

pub mod storage;
pub use storage::{
ModelStorage, ModelStorageTest, ModelValueStorage, ModelValueStorageTest, ModelPtr,
};
pub use storage::{ModelStorage, ModelStorageTest, ModelValueStorage, ModelValueStorageTest,};
}

pub mod storage {
Expand Down
21 changes: 21 additions & 0 deletions crates/contracts/src/meta/interface.cairo
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
use dojo::meta::Layout;
use dojo::meta::introspect::Struct;

/// The `IDeployedResource` starknet interface.
///
/// This is the interface used by offchain components and other contracts
/// to get some info such Dojo name from a deployed resource.
#[starknet::interface]
pub trait IDeployedResource<T> {
fn dojo_name(self: @T) -> ByteArray;
}

/// The `IStoredResource` starknet interface.
///
/// This is the interface used by offchain components and other contracts
/// to access to storage related data of a deployed resource.
#[starknet::interface]
pub trait IStoredResource<T> {
fn layout(self: @T) -> Layout;
fn schema(self: @T) -> Struct;
}
25 changes: 25 additions & 0 deletions crates/contracts/src/meta/introspect.cairo
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,31 @@ pub struct Member {
pub ty: Ty
}

#[generate_trait]
pub impl StructCompareImpl of StructCompareTrait {
fn is_an_upgrade_of(self: @Struct, old: @Struct) -> bool {
if self.name != old.name
|| self.attrs != old.attrs
|| (*self.children).len() < (*old.children).len() {
return false;
}

let mut i = 0;

loop {
if i >= (*old.children).len() {
break true;
}

if *old.children[i] != *self.children[i] {
break false;
}

i += 1;
}
}
}

pub trait Introspect<T> {
fn size() -> Option<usize>;
fn layout() -> Layout;
Expand Down
15 changes: 15 additions & 0 deletions crates/contracts/src/meta/layout.cairo
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,21 @@ pub enum Layout {
Enum: Span<FieldLayout>,
}

#[generate_trait]
pub impl LayoutCompareImpl of LayoutCompareTrait {
fn is_same_type_of(self: @Layout, old: @Layout) -> bool {
match (self, old) {
(Layout::Fixed(_), Layout::Fixed(_)) => true,
(Layout::Struct(_), Layout::Struct(_)) => true,
(Layout::Tuple(_), Layout::Tuple(_)) => true,
(Layout::Array(_), Layout::Array(_)) => true,
(Layout::ByteArray, Layout::ByteArray) => true,
(Layout::Enum(_), Layout::Enum(_)) => true,
_ => false
}
}
}

/// Compute the full size in bytes of a layout, when all the fields
/// are bit-packed.
/// Could be None if at least a field has a dynamic size.
Expand Down
21 changes: 14 additions & 7 deletions crates/contracts/src/model/component.cairo
Original file line number Diff line number Diff line change
@@ -1,23 +1,30 @@
use dojo::{model::{Model, IModel, ModelDef}, meta::{Layout, Ty}};
use dojo::model::{Model, IModel, ModelDef};
use dojo::meta::{Layout, introspect::Struct};

#[starknet::embeddable]
pub impl IModelImpl<TContractState, M, +Model<M>> of IModel<TContractState> {
pub impl IDeployedModelImpl<
TContractState, M, +Model<M>
> of dojo::meta::IDeployedResource<TContractState> {
fn dojo_name(self: @TContractState) -> ByteArray {
Model::<M>::name()
}
}

fn version(self: @TContractState) -> u8 {
Model::<M>::version()
}

fn schema(self: @TContractState) -> Ty {
#[starknet::embeddable]
pub impl IStoredModelImpl<
TContractState, M, +Model<M>
> of dojo::meta::IStoredResource<TContractState> {
fn schema(self: @TContractState) -> Struct {
Model::<M>::schema()
}

fn layout(self: @TContractState) -> Layout {
Model::<M>::layout()
}
}

#[starknet::embeddable]
pub impl IModelImpl<TContractState, M, +Model<M>> of IModel<TContractState> {
fn unpacked_size(self: @TContractState) -> Option<usize> {
Model::<M>::unpacked_size()
}
Expand Down
8 changes: 3 additions & 5 deletions crates/contracts/src/model/definition.cairo
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use dojo::meta::{Layout, introspect::Ty};
use dojo::meta::{Layout, introspect::Struct};

/// The `ModelIndex` provides encapsulation for different ways to access
/// a model's data.
Expand All @@ -19,19 +19,17 @@ pub enum ModelIndex {
/// Definition of the model containing all the fields that makes up a model.
pub trait ModelDefinition<T> {
fn name() -> ByteArray;
fn version() -> u8;
fn layout() -> Layout;
fn schema() -> Ty;
fn schema() -> Struct;
fn size() -> Option<usize>;
}

/// A plain struct with all the fields of a model definition.
#[derive(Drop, Serde, Debug, PartialEq)]
pub struct ModelDef {
pub name: ByteArray,
pub version: u8,
pub layout: Layout,
pub schema: Ty,
pub schema: Struct,
pub packed_size: Option<usize>,
pub unpacked_size: Option<usize>,
}
9 changes: 1 addition & 8 deletions crates/contracts/src/model/interface.cairo
Original file line number Diff line number Diff line change
@@ -1,17 +1,10 @@
use dojo::meta::{Layout, Ty};
use dojo::model::ModelDef;

/// The `IModel` starknet interface.
///
/// This is the interface used by offchain components and other contracts
/// to interact with deployed models.
#[starknet::interface]
pub trait IModel<T> {
fn dojo_name(self: @T) -> ByteArray;
fn version(self: @T) -> u8;
fn layout(self: @T) -> Layout;
fn schema(self: @T) -> Ty;
fn unpacked_size(self: @T) -> Option<usize>;
fn packed_size(self: @T) -> Option<usize>;
fn definition(self: @T) -> ModelDef;
fn definition(self: @T) -> dojo::model::ModelDef;
}
54 changes: 43 additions & 11 deletions crates/contracts/src/model/model.cairo
Original file line number Diff line number Diff line change
@@ -1,8 +1,17 @@
use dojo::{meta::{Layout, introspect::Ty, layout::compute_packed_size}, utils::entity_id_from_keys};
use dojo::{
meta::{Layout, introspect::Struct, layout::compute_packed_size},
utils::{entity_id_from_keys, find_model_field_layout, entity_id_from_key}
};

use super::{ModelDefinition, ModelDef};

/// Trait `KeyParser` defines a trait for parsing keys from a given model.
///
/// A pointer to a model, which can be expressed by an entity id.
#[derive(Copy, Drop, Serde, Debug, PartialEq)]
pub struct ModelPtr<M> {
pub id: felt252,
}

pub trait KeyParser<M, K> {
/// Parses the key from the given model.
fn parse_key(self: @M) -> K;
Expand Down Expand Up @@ -33,12 +42,12 @@ pub trait Model<M> {
/// Returns the name of the model. (TODO: internalizing the name_hash could reduce poseidon
/// costs).
fn name() -> ByteArray;
/// Returns the version of the model.
fn version() -> u8;
/// Returns the schema of the model.
fn schema() -> Ty;
fn schema() -> Struct;
/// Returns the memory layout of the model.
fn layout() -> Layout;
/// Returns the layout of a field in the model.
fn field_layout(field_selector: felt252) -> Option<Layout>;
/// Returns the unpacked size of the model. Only applicable for fixed size models.
fn unpacked_size() -> Option<usize>;
/// Returns the packed size of the model. Only applicable for fixed size models.
Expand All @@ -49,6 +58,14 @@ pub trait Model<M> {
fn definition() -> ModelDef;
/// Returns the selector of the model computed for the given namespace hash.
fn selector(namespace_hash: felt252) -> felt252;
/// Returns the pointer to the model from the key.
fn ptr_from_key<K, +Serde<K>, +Drop<K>>(key: K) -> ModelPtr<M>;
/// Returns the pointer to the model from the keys.
fn ptr_from_keys(keys: Span<felt252>) -> ModelPtr<M>;
/// Returns the pointer to the model from the entity id.
fn ptr_from_id(entity_id: felt252) -> ModelPtr<M>;
/// Returns the ptr of the model.
fn ptr(self: @M) -> ModelPtr<M>;
}

pub impl ModelImpl<M, +ModelParser<M>, +ModelDefinition<M>, +Serde<M>> of Model<M> {
Expand Down Expand Up @@ -84,15 +101,15 @@ pub impl ModelImpl<M, +ModelParser<M>, +ModelDefinition<M>, +Serde<M>> of Model<
dojo::utils::selector_from_namespace_and_name(namespace_hash, @Self::name())
}

fn version() -> u8 {
ModelDefinition::<M>::version()
}

fn layout() -> Layout {
ModelDefinition::<M>::layout()
}

fn schema() -> Ty {
fn field_layout(field_selector: felt252) -> Option<Layout> {
find_model_field_layout(Self::layout(), field_selector)
}

fn schema() -> Struct {
ModelDefinition::<M>::schema()
}

Expand All @@ -111,11 +128,26 @@ pub impl ModelImpl<M, +ModelParser<M>, +ModelDefinition<M>, +Serde<M>> of Model<
fn definition() -> ModelDef {
ModelDef {
name: Self::name(),
version: Self::version(),
layout: Self::layout(),
schema: Self::schema(),
packed_size: Self::packed_size(),
unpacked_size: Self::unpacked_size()
}
}

fn ptr_from_key<K, +Serde<K>, +Drop<K>>(key: K) -> ModelPtr<M> {
ModelPtr { id: entity_id_from_key(@key) }
}

fn ptr_from_keys(keys: Span<felt252>) -> ModelPtr<M> {
ModelPtr { id: entity_id_from_keys(keys) }
}

fn ptr_from_id(entity_id: felt252) -> ModelPtr<M> {
ModelPtr::<M> { id: entity_id }
}

fn ptr(self: @M) -> ModelPtr<M> {
ModelPtr::<M> { id: self.entity_id() }
}
}
Loading

0 comments on commit 59becbf

Please sign in to comment.