Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: add eventmessage for emitting models & start refactoring emit macro #1656

Merged
merged 38 commits into from
Mar 21, 2024
Merged
Show file tree
Hide file tree
Changes from 21 commits
Commits
Show all changes
38 commits
Select commit Hold shift + click to select a range
1fab000
feat: add eventmessage for emitting models & start refactoring emit m…
Larkooo Mar 12, 2024
92fc0ba
refactor: emit multiple models event
Larkooo Mar 12, 2024
3942b34
feat: event message processor
Larkooo Mar 13, 2024
bfec3ab
feat: emit_message world func emit event mssage
Larkooo Mar 13, 2024
9c590a0
refactor: catch all event processor
Larkooo Mar 13, 2024
3efd17f
refactor: check model key as name
Larkooo Mar 13, 2024
b947f26
feat: model name keccak as id & rework model events
Larkooo Mar 13, 2024
5b1e8f2
fix: storing entities with model hash
Larkooo Mar 13, 2024
e53e046
refactor: catch all event message and store
Larkooo Mar 14, 2024
d98f49e
feat: emit model evrnt from spawn and move spawn
Larkooo Mar 14, 2024
c9c3255
fix: pass keys as array
Larkooo Mar 14, 2024
655fb9f
feat: fix emit macro and correctly index model events
Larkooo Mar 14, 2024
b2f3bee
feat: event messages migrations & set
Larkooo Mar 14, 2024
61e0ca3
feat: store events messages and new id system
Larkooo Mar 14, 2024
201e019
chore: fmt
Larkooo Mar 14, 2024
82dc81f
fix: keys array
Larkooo Mar 14, 2024
40dddbc
feat: add grpc endpoint for event messages
Larkooo Mar 15, 2024
b048831
feat: graphql schema for event messages
Larkooo Mar 15, 2024
fa51fd2
cadd comments for moel name hash
Larkooo Mar 15, 2024
15f3031
revert world.cario changes
Larkooo Mar 15, 2024
aeaaa81
fix: graphql entity and model connection
Larkooo Mar 19, 2024
ba793f4
refactor: only test models name ordering
Larkooo Mar 20, 2024
10ff64b
fix: entity/modeldata relation
Larkooo Mar 20, 2024
9720ab3
refactor: remove event testing
Larkooo Mar 20, 2024
048bfa4
fix: cairo code
Larkooo Mar 20, 2024
80bc512
refactor: add back event rxample
Larkooo Mar 20, 2024
8b10b9a
Merge branch 'main' into emit-model
Larkooo Mar 20, 2024
f461512
fix: merge
Larkooo Mar 20, 2024
611f846
chore: migration
Larkooo Mar 20, 2024
9c8e37a
fix: subscription test
Larkooo Mar 20, 2024
56836dc
fix: tests
Larkooo Mar 20, 2024
423b1cb
fix: subscription test
Larkooo Mar 20, 2024
91e50ae
fix: sql test
Larkooo Mar 20, 2024
220d36c
chore: format model id correctly in test
Larkooo Mar 20, 2024
6f19ebd
fix: model subscription with id
Larkooo Mar 20, 2024
c6d33b7
fix: schema for model
Larkooo Mar 21, 2024
9ae11f4
chore: revert modelmmeber type
Larkooo Mar 21, 2024
ba880af
fix: event message query
Larkooo Mar 21, 2024
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
2 changes: 2 additions & 0 deletions bin/torii/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ use tokio::sync::broadcast;
use tokio::sync::broadcast::Sender;
use tokio_stream::StreamExt;
use torii_core::engine::{Engine, EngineConfig, Processors};
use torii_core::processors::event_message::EventMessageProcessor;
use torii_core::processors::metadata_update::MetadataUpdateProcessor;
use torii_core::processors::register_model::RegisterModelProcessor;
use torii_core::processors::store_del_record::StoreDelRecordProcessor;
Expand Down Expand Up @@ -152,6 +153,7 @@ async fn main() -> anyhow::Result<()> {
Box::new(StoreSetRecordProcessor),
Box::new(MetadataUpdateProcessor),
Box::new(StoreDelRecordProcessor),
Box::new(EventMessageProcessor),
],
transaction: vec![Box::new(StoreTransactionProcessor)],
..Processors::default()
Expand Down
94 changes: 76 additions & 18 deletions crates/dojo-lang/src/inline_macros/emit.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@ use cairo_lang_diagnostics::Severity;
use cairo_lang_semantic::inline_macros::unsupported_bracket_diagnostic;
use cairo_lang_syntax::node::{ast, TypedSyntaxNode};

use super::unsupported_arg_diagnostic;

#[derive(Debug, Default)]
pub struct EmitMacro;

Expand All @@ -23,11 +25,7 @@ impl InlineMacroExprPlugin for EmitMacro {
return unsupported_bracket_diagnostic(db, syntax);
};
let mut builder = PatchBuilder::new(db);
builder.add_str(
"{
let mut keys = Default::<core::array::Array>::default();
let mut data = Default::<core::array::Array>::default();",
);
builder.add_str("{");

let args = arg_list.arguments(db).elements(db);

Expand All @@ -36,25 +34,85 @@ impl InlineMacroExprPlugin for EmitMacro {
code: None,
diagnostics: vec![PluginDiagnostic {
stable_ptr: arg_list.arguments(db).stable_ptr().untyped(),
message: "Invalid arguments. Expected \"emit!(world, event)\"".to_string(),
message: "Invalid arguments. Expected \"emit!(world, models,)\"".to_string(),
severity: Severity::Error,
}],
};
}

let world = &args[0];
let event = &args[1];

builder.add_str(
"\n starknet::Event::append_keys_and_data(@core::traits::Into::<_, \
Event>::into(",
);
builder.add_node(event.as_syntax_node());
builder.add_str("), ref keys, ref data);");

builder.add_str("\n ");
builder.add_node(world.as_syntax_node());
builder.add_str(".emit(keys, data.span());");

let ast::ArgClause::Unnamed(models) = args[1].arg_clause(db) else {
return unsupported_arg_diagnostic(db, syntax);
};

let mut bundle = vec![];

match models.value(db) {
ast::Expr::Parenthesized(parens) => {
let syntax_node = parens.expr(db).as_syntax_node();
bundle.push((syntax_node.get_text(db), syntax_node));
}
ast::Expr::Tuple(list) => {
list.expressions(db).elements(db).into_iter().for_each(|expr| {
let syntax_node = expr.as_syntax_node();
bundle.push((syntax_node.get_text(db), syntax_node));
})
}
ast::Expr::StructCtorCall(ctor) => {
let syntax_node = ctor.as_syntax_node();
bundle.push((syntax_node.get_text(db), syntax_node));
}
_ => {
return InlinePluginResult {
code: None,
diagnostics: vec![PluginDiagnostic {
message: "Invalid arguments. Expected \"(world, (models,))\"".to_string(),
stable_ptr: arg_list.arguments(db).stable_ptr().untyped(),
severity: Severity::Error,
}],
};
}
}

if bundle.is_empty() {
return InlinePluginResult {
code: None,
diagnostics: vec![PluginDiagnostic {
message: "Invalid arguments: No models provided.".to_string(),
stable_ptr: arg_list.arguments(db).stable_ptr().untyped(),
severity: Severity::Error,
}],
};
}

for (event, _) in bundle {
builder.add_str("{");

builder.add_str(
"
let mut keys = Default::<core::array::Array>::default();
let mut data = Default::<core::array::Array>::default();",
);

builder.add_str(&format!(
"keys.append(selector!(\"{}\"));",
Larkooo marked this conversation as resolved.
Show resolved Hide resolved
event.split_whitespace().next().unwrap()
));

builder.add_str(&format!(
"
starknet::Event::append_keys_and_data(@{event}, ref keys, ref data);",
event = event
));

builder.add_str("\n ");
builder.add_node(world.as_syntax_node());
builder.add_str(".emit(keys, data.span());");

builder.add_str("}");
}

builder.add_str("}");

InlinePluginResult {
Expand Down
5 changes: 5 additions & 0 deletions crates/dojo-world/src/contracts/model.rs
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ pub enum ModelError {
#[cfg_attr(not(target_arch = "wasm32"), async_trait)]
#[cfg_attr(target_arch = "wasm32", async_trait(?Send))]
pub trait ModelReader<E> {
fn name(&self) -> String;
fn class_hash(&self) -> FieldElement;
fn contract_address(&self) -> FieldElement;
async fn schema(&self) -> Result<Ty, E>;
Expand Down Expand Up @@ -146,6 +147,10 @@ impl<'a, P> ModelReader<ModelError> for ModelRPCReader<'a, P>
where
P: Provider + Sync + Send,
{
fn name(&self) -> String {
self.name.to_string()
}
Larkooo marked this conversation as resolved.
Show resolved Hide resolved

fn class_hash(&self) -> FieldElement {
self.class_hash
}
Expand Down
5 changes: 4 additions & 1 deletion crates/torii/core/src/engine.rs
Original file line number Diff line number Diff line change
Expand Up @@ -284,7 +284,10 @@ impl<'db, P: Provider + Sync> Engine<'db, P> {
};
self.db.store_event(event_id, event, transaction_hash);
for processor in &self.processors.event {
if get_selector_from_name(&processor.event_key())? == event.keys[0]
// If the processor has no event_key, means it's a catch-all processor.
// We also validate the event
if (processor.event_key().is_empty()
|| get_selector_from_name(&processor.event_key())? == event.keys[0])
&& processor.validate(event)
{
processor
Expand Down
4 changes: 3 additions & 1 deletion crates/torii/core/src/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ use std::num::ParseIntError;
use dojo_types::primitive::PrimitiveError;
use dojo_types::schema::EnumError;
use starknet::core::types::{FromByteSliceError, FromStrError};
use starknet::core::utils::CairoShortStringToFeltError;
use starknet::core::utils::{CairoShortStringToFeltError, NonAsciiNameError};

#[derive(Debug, thiserror::Error)]
pub enum Error {
Expand All @@ -21,6 +21,8 @@ pub enum Error {

#[derive(Debug, thiserror::Error)]
pub enum ParseError {
#[error(transparent)]
NonAsciiName(#[from] NonAsciiNameError),
#[error(transparent)]
FromStr(#[from] FromStrError),
#[error(transparent)]
Expand Down
11 changes: 10 additions & 1 deletion crates/torii/core/src/model.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ use dojo_world::contracts::model::ModelReader;
use sqlx::sqlite::SqliteRow;
use sqlx::{Pool, Row, Sqlite};
use starknet::core::types::FieldElement;
use starknet::core::utils::get_selector_from_name;

use super::error::{self, Error};
use crate::error::{ParseError, QueryError};
Expand Down Expand Up @@ -59,6 +60,10 @@ impl ModelSQLReader {
#[cfg_attr(not(target_arch = "wasm32"), async_trait)]
#[cfg_attr(target_arch = "wasm32", async_trait(?Send))]
impl ModelReader<Error> for ModelSQLReader {
fn name(&self) -> String {
self.name.to_string()
}

fn class_hash(&self) -> FieldElement {
self.class_hash
}
Expand All @@ -68,11 +73,15 @@ impl ModelReader<Error> for ModelSQLReader {
}

async fn schema(&self) -> Result<Ty, Error> {
// this is temporary until the hash for the model name is precomputed
let model_selector =
get_selector_from_name(&self.name).map_err(error::ParseError::NonAsciiName)?;
Larkooo marked this conversation as resolved.
Show resolved Hide resolved

let model_members: Vec<SqlModelMember> = sqlx::query_as(
"SELECT id, model_idx, member_idx, name, type, type_enum, enum_options, key FROM \
model_members WHERE model_id = ? ORDER BY model_idx ASC, member_idx ASC",
)
.bind(self.name.clone())
.bind(format!("{:#x}", model_selector))
.fetch_all(&self.pool)
.await?;

Expand Down
65 changes: 65 additions & 0 deletions crates/torii/core/src/processors/event_message.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
use anyhow::{Error, Result};
use async_trait::async_trait;
use dojo_world::contracts::model::ModelReader;
use dojo_world::contracts::world::WorldContractReader;
use starknet::core::types::{BlockWithTxs, Event, TransactionReceipt};
use starknet::providers::Provider;
use tracing::info;

use super::EventProcessor;
use crate::processors::MODEL_INDEX;
use crate::sql::Sql;

#[derive(Default)]
pub struct EventMessageProcessor;

#[async_trait]
impl<P> EventProcessor<P> for EventMessageProcessor
where
P: Provider + Send + Sync,
{
fn event_key(&self) -> String {
"".to_string()
}

fn validate(&self, event: &Event) -> bool {
// we expect at least 3 keys
// 1: event selector
// 2: model keys, arbitrary length
// last key: system key
if event.keys.len() < 3 {
return false;
}

true
Larkooo marked this conversation as resolved.
Show resolved Hide resolved
}

async fn process(
&self,
_world: &WorldContractReader<P>,
db: &mut Sql,
_block: &BlockWithTxs,
_transaction_receipt: &TransactionReceipt,
event_id: &str,
event: &Event,
) -> Result<(), Error> {
// silently ignore if the model is not found
let model = match db.model(&format!("{:#x}", event.keys[MODEL_INDEX])).await {
Ok(model) => model,
Err(_) => return Ok(()),
};

info!("store event message: {}", model.name());

// skip the first key, as its the event selector
// and dont include last key as its the system key
let mut keys_and_unpacked =
[event.keys[1..event.keys.len() - 1].to_vec(), event.data.clone()].concat();

let mut entity = model.schema().await?;
entity.deserialize(&mut keys_and_unpacked)?;

db.set_event_message(entity, event_id).await?;
Ok(())
}
}
1 change: 1 addition & 0 deletions crates/torii/core/src/processors/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ use starknet::providers::Provider;

use crate::sql::Sql;

pub mod event_message;
pub mod metadata_update;
pub mod register_model;
pub mod store_del_record;
Expand Down
5 changes: 3 additions & 2 deletions crates/torii/core/src/processors/store_del_record.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ use async_trait::async_trait;
use dojo_world::contracts::model::ModelReader;
use dojo_world::contracts::world::WorldContractReader;
use starknet::core::types::{BlockWithTxs, Event, TransactionReceipt};
use starknet::core::utils::parse_cairo_short_string;
use starknet::core::utils::{get_selector_from_name, parse_cairo_short_string};
use starknet::providers::Provider;
use tracing::info;

Expand Down Expand Up @@ -47,7 +47,8 @@ where
let name = parse_cairo_short_string(&event.data[MODEL_INDEX])?;
info!("store delete record: {}", name);

let model = db.model(&name).await?;
// this is temporary until the model name hash is precomputed
let model = db.model(&format!("{:#x}", get_selector_from_name(&name)?)).await?;

let keys_start = NUM_KEYS_INDEX + 1;
let keys = event.data[keys_start..].to_vec();
Expand Down
5 changes: 3 additions & 2 deletions crates/torii/core/src/processors/store_set_record.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ use async_trait::async_trait;
use dojo_world::contracts::model::ModelReader;
use dojo_world::contracts::world::WorldContractReader;
use starknet::core::types::{BlockWithTxs, Event, TransactionReceipt};
use starknet::core::utils::parse_cairo_short_string;
use starknet::core::utils::{get_selector_from_name, parse_cairo_short_string};
use starknet::providers::Provider;
use tracing::info;

Expand Down Expand Up @@ -47,7 +47,8 @@ where
let name = parse_cairo_short_string(&event.data[MODEL_INDEX])?;
info!("store set record: {}", name);

let model = db.model(&name).await?;
// this is temporary until the model name hash is precomputed
let model = db.model(&format!("{:#x}", get_selector_from_name(&name)?)).await?;

let keys_start = NUM_KEYS_INDEX + 1;
let keys_end: usize = keys_start + usize::from(u8::try_from(event.data[NUM_KEYS_INDEX])?);
Expand Down
Loading
Loading