Skip to content

Commit

Permalink
Implement ModelReader trait
Browse files Browse the repository at this point in the history
  • Loading branch information
tarrencev committed Nov 1, 2023
1 parent 102b03b commit b365414
Show file tree
Hide file tree
Showing 5 changed files with 78 additions and 57 deletions.
118 changes: 67 additions & 51 deletions crates/dojo-world/src/contracts/model.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
use std::vec;

use async_trait::async_trait;
use dojo_types::packing::{parse_ty, unpack, PackingError, ParseError};
use dojo_types::primitive::PrimitiveError;
use dojo_types::schema::Ty;
Expand Down Expand Up @@ -46,7 +47,16 @@ pub enum ModelError<P> {
Packing(#[from] PackingError),
}

pub struct ModelReader<'a, P> {
#[async_trait]
pub trait ModelReader<E> {
fn class_hash(&self) -> FieldElement;
async fn schema(&self) -> Result<Ty, E>;
async fn packed_size(&self) -> Result<FieldElement, E>;
async fn unpacked_size(&self) -> Result<FieldElement, E>;
async fn layout(&self) -> Result<Vec<FieldElement>, E>;
}

pub struct ModelRPCReader<'a, P: Sync + Send> {
/// The name of the model
name: FieldElement,
/// The class hash of the model
Expand All @@ -55,14 +65,14 @@ pub struct ModelReader<'a, P> {
world_reader: &'a WorldContractReader<P>,
}

impl<'a, P> ModelReader<'a, P>
impl<'a, P> ModelRPCReader<'a, P>
where
P: Provider,
P: Provider + Sync + Send,
{
pub async fn new(
name: &str,
world: &'a WorldContractReader<P>,
) -> Result<ModelReader<'a, P>, ModelError<P::Error>> {
) -> Result<ModelRPCReader<'a, P>, ModelError<P::Error>> {
let name = cairo_short_string_to_felt(name)?;

let class_hash = world
Expand All @@ -88,11 +98,59 @@ where
Ok(Self { world_reader: world, class_hash, name })
}

pub fn class_hash(&self) -> FieldElement {
pub async fn entity_storage(
&self,
keys: &[FieldElement],
) -> Result<Vec<FieldElement>, ModelError<P::Error>> {
let packed_size: u8 =
self.packed_size().await?.try_into().map_err(ParseError::ValueOutOfRange)?;

let key = poseidon_hash_many(keys);
let key = poseidon_hash_many(&[short_string!("dojo_storage"), self.name, key]);

let mut packed = Vec::with_capacity(packed_size as usize);
for slot in 0..packed_size {
let value = self
.world_reader
.provider()
.get_storage_at(
self.world_reader.address(),
key + slot.into(),
self.world_reader.block_id(),
)
.await?;

packed.push(value);
}

Ok(packed)
}

pub async fn entity(&self, keys: &[FieldElement]) -> Result<Ty, ModelError<P::Error>> {
let mut schema = self.schema().await?;

let layout = self.layout().await?;
let raw_values = self.entity_storage(keys).await?;

let unpacked = unpack(raw_values, layout)?;
let mut keys_and_unpacked = [keys, &unpacked].concat();

schema.deserialize(&mut keys_and_unpacked)?;

Ok(schema)
}
}

#[async_trait]
impl<'a, P> ModelReader<ModelError<P::Error>> for ModelRPCReader<'a, P>
where
P: Provider + Sync + Send,
{
fn class_hash(&self) -> FieldElement {
self.class_hash
}

pub async fn schema(&self) -> Result<Ty, ModelError<P::Error>> {
async fn schema(&self) -> Result<Ty, ModelError<P::Error>> {
let entrypoint = get_selector_from_name(SCHEMA_SELECTOR_STR).unwrap();

let res = self
Expand All @@ -103,7 +161,7 @@ where
Ok(parse_ty(&res[1..])?)
}

pub async fn packed_size(&self) -> Result<FieldElement, ModelError<P::Error>> {
async fn packed_size(&self) -> Result<FieldElement, ModelError<P::Error>> {
let entrypoint = get_selector_from_name(PACKED_SIZE_SELECTOR_STR).unwrap();

let res = self
Expand All @@ -114,7 +172,7 @@ where
Ok(res[1])
}

pub async fn unpacked_size(&self) -> Result<FieldElement, ModelError<P::Error>> {
async fn unpacked_size(&self) -> Result<FieldElement, ModelError<P::Error>> {
let entrypoint = get_selector_from_name(UNPACKED_SIZE_SELECTOR_STR).unwrap();

let res = self
Expand All @@ -125,7 +183,7 @@ where
Ok(res[1])
}

pub async fn layout(&self) -> Result<Vec<FieldElement>, ModelError<P::Error>> {
async fn layout(&self) -> Result<Vec<FieldElement>, ModelError<P::Error>> {
let entrypoint = get_selector_from_name(LAYOUT_SELECTOR_STR).unwrap();

let res = self
Expand All @@ -135,46 +193,4 @@ where

Ok(res[2..].into())
}

pub async fn entity_storage(
&self,
keys: &[FieldElement],
) -> Result<Vec<FieldElement>, ModelError<P::Error>> {
let packed_size: u8 =
self.packed_size().await?.try_into().map_err(ParseError::ValueOutOfRange)?;

let key = poseidon_hash_many(keys);
let key = poseidon_hash_many(&[short_string!("dojo_storage"), self.name, key]);

let mut packed = Vec::with_capacity(packed_size as usize);
for slot in 0..packed_size {
let value = self
.world_reader
.provider()
.get_storage_at(
self.world_reader.address(),
key + slot.into(),
self.world_reader.block_id(),
)
.await?;

packed.push(value);
}

Ok(packed)
}

pub async fn entity(&self, keys: &[FieldElement]) -> Result<Ty, ModelError<P::Error>> {
let mut schema = self.schema().await?;

let layout = self.layout().await?;
let raw_values = self.entity_storage(keys).await?;

let unpacked = unpack(raw_values, layout)?;
let mut keys_and_unpacked = [keys, &unpacked].concat();

schema.deserialize(&mut keys_and_unpacked)?;

Ok(schema)
}
}
1 change: 1 addition & 0 deletions crates/dojo-world/src/contracts/model_test.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ use dojo_types::schema::{Enum, EnumOption, Member, Struct, Ty};
use starknet::accounts::ConnectedAccount;
use starknet::core::types::FieldElement;

use crate::contracts::model::ModelReader;
use crate::contracts::world::test::deploy_world;
use crate::contracts::world::WorldContractReader;

Expand Down
13 changes: 8 additions & 5 deletions crates/dojo-world/src/contracts/world.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ use starknet::core::utils::{
use starknet::macros::selector;
use starknet::providers::{Provider, ProviderError};

use super::model::{ModelError, ModelReader};
use super::model::{ModelError, ModelRPCReader};

#[cfg(test)]
#[path = "world_test.rs"]
Expand Down Expand Up @@ -177,7 +177,7 @@ where
pub async fn model(
&'a self,
name: &str,
) -> Result<ModelReader<'_, &'a A::Provider>, ModelError<<A::Provider as Provider>::Error>>
) -> Result<ModelRPCReader<'_, &'a A::Provider>, ModelError<<A::Provider as Provider>::Error>>
{
self.reader.model(name).await
}
Expand Down Expand Up @@ -340,9 +340,12 @@ where

impl<'a, P> WorldContractReader<P>
where
P: Provider,
P: Provider + Sync + Send,
{
pub async fn model(&'a self, name: &str) -> Result<ModelReader<'a, P>, ModelError<P::Error>> {
ModelReader::new(name, self).await
pub async fn model(
&'a self,
name: &str,
) -> Result<ModelRPCReader<'a, P>, ModelError<P::Error>> {
ModelRPCReader::new(name, self).await
}
}
2 changes: 1 addition & 1 deletion crates/sozo/src/ops/model.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
use anyhow::Result;
use dojo_world::contracts::world::WorldContractReader;
use dojo_world::contracts::{world::WorldContractReader, model::ModelReader};
use dojo_world::metadata::Environment;
use starknet::core::types::{BlockId, BlockTag};

Expand Down
1 change: 1 addition & 0 deletions crates/torii/core/src/processors/register_model.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
use anyhow::{Error, Ok, Result};
use async_trait::async_trait;
use dojo_world::contracts::model::ModelReader;
use dojo_world::contracts::world::WorldContractReader;
use starknet::core::types::{BlockWithTxs, Event, InvokeTransactionReceipt};
use starknet::core::utils::parse_cairo_short_string;
Expand Down

0 comments on commit b365414

Please sign in to comment.