diff --git a/Cargo.lock b/Cargo.lock index c000f050..c93ad177 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2940,7 +2940,7 @@ dependencies = [ [[package]] name = "dojo-metrics" version = "1.6.0-alpha.1" -source = "git+https://github.com/dojoengine/dojo?rev=054623b#054623bbf939a3bf90786355bd9b081a2b1b3c3a" +source = "git+https://github.com/bengineer42/dojo?branch=add-write-schema-1.5#5b52d44512b6524da2156875432f76c5944f415e" dependencies = [ "bytes", "http-body-util", @@ -2961,7 +2961,7 @@ dependencies = [ [[package]] name = "dojo-test-utils" version = "1.6.0-alpha.1" -source = "git+https://github.com/dojoengine/dojo?rev=054623b#054623bbf939a3bf90786355bd9b081a2b1b3c3a" +source = "git+https://github.com/bengineer42/dojo?branch=add-write-schema-1.5#5b52d44512b6524da2156875432f76c5944f415e" dependencies = [ "anyhow", "assert_fs", @@ -2996,7 +2996,7 @@ dependencies = [ [[package]] name = "dojo-types" version = "1.6.0-alpha.1" -source = "git+https://github.com/dojoengine/dojo?rev=054623b#054623bbf939a3bf90786355bd9b081a2b1b3c3a" +source = "git+https://github.com/bengineer42/dojo?branch=add-write-schema-1.5#5b52d44512b6524da2156875432f76c5944f415e" dependencies = [ "anyhow", "cainome 0.6.1", @@ -3018,7 +3018,7 @@ dependencies = [ [[package]] name = "dojo-utils" version = "1.6.0-alpha.1" -source = "git+https://github.com/dojoengine/dojo?rev=054623b#054623bbf939a3bf90786355bd9b081a2b1b3c3a" +source = "git+https://github.com/bengineer42/dojo?branch=add-write-schema-1.5#5b52d44512b6524da2156875432f76c5944f415e" dependencies = [ "anyhow", "colored_json", @@ -3035,7 +3035,7 @@ dependencies = [ [[package]] name = "dojo-world" version = "1.6.0-alpha.1" -source = "git+https://github.com/dojoengine/dojo?rev=054623b#054623bbf939a3bf90786355bd9b081a2b1b3c3a" +source = "git+https://github.com/bengineer42/dojo?branch=add-write-schema-1.5#5b52d44512b6524da2156875432f76c5944f415e" dependencies = [ "anyhow", "async-trait", @@ -6892,8 +6892,8 @@ dependencies = [ [[package]] name = "merge-options" -version = "1.5.0" -source = "git+https://github.com/dojoengine/dojo?rev=82fe9bd#82fe9bde8bb840db507dc11e26b3085a54dbd0d9" +version = "1.6.0-alpha.1" +source = "git+https://github.com/bengineer42/dojo?branch=add-write-schema-1.5#5b52d44512b6524da2156875432f76c5944f415e" dependencies = [ "proc-macro2", "quote", @@ -10062,7 +10062,7 @@ dependencies = [ [[package]] name = "sozo-ops" version = "1.6.0-alpha.1" -source = "git+https://github.com/dojoengine/dojo?rev=054623b#054623bbf939a3bf90786355bd9b081a2b1b3c3a" +source = "git+https://github.com/bengineer42/dojo?branch=add-write-schema-1.5#5b52d44512b6524da2156875432f76c5944f415e" dependencies = [ "anyhow", "async-trait", @@ -10088,7 +10088,7 @@ dependencies = [ [[package]] name = "sozo-scarbext" version = "1.6.0-alpha.1" -source = "git+https://github.com/dojoengine/dojo?rev=054623b#054623bbf939a3bf90786355bd9b081a2b1b3c3a" +source = "git+https://github.com/bengineer42/dojo?branch=add-write-schema-1.5#5b52d44512b6524da2156875432f76c5944f415e" dependencies = [ "anyhow", "camino", diff --git a/Cargo.toml b/Cargo.toml index 2c2186dc..64e03004 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -8,9 +8,13 @@ members = [ "crates/client", "crates/messaging", "crates/server", - "crates/sqlite/types", + "crates/sqlite/types", "xtask/generate-test-db", - "crates/task-network", "crates/storage", "crates/cache", "crates/math", "crates/controllers", + "crates/task-network", + "crates/storage", + "crates/cache", + "crates/math", + "crates/controllers", ] [workspace.package] @@ -35,11 +39,11 @@ cainome = { git = "https://github.com/Larkooo/cainome", branch = "patch-1" } cainome-cairo-serde = { git = "https://github.com/Larkooo/cainome", branch = "patch-1" } cainome-cairo-serde-derive = { git = "https://github.com/Larkooo/cainome", branch = "patch-1" } -dojo-utils = { git = "https://github.com/dojoengine/dojo", rev = "054623b" } -dojo-types = { git = "https://github.com/dojoengine/dojo", rev = "054623b" } -dojo-world = { git = "https://github.com/dojoengine/dojo", rev = "054623b" } -dojo-test-utils = { git = "https://github.com/dojoengine/dojo", rev = "054623b" } -dojo-metrics = { git = "https://github.com/dojoengine/dojo", rev = "054623b" } +dojo-utils = { git = "https://github.com/bengineer42/dojo", branch = "add-write-schema-1.5" } +dojo-types = { git = "https://github.com/bengineer42/dojo", branch = "add-write-schema-1.5" } +dojo-world = { git = "https://github.com/bengineer42/dojo", branch = "add-write-schema-1.5" } +dojo-test-utils = { git = "https://github.com/bengineer42/dojo", branch = "add-write-schema-1.5" } +dojo-metrics = { git = "https://github.com/bengineer42/dojo", branch = "add-write-schema-1.5" } # torii torii-broker = { path = "crates/broker" } @@ -66,7 +70,7 @@ torii-cache = { path = "crates/cache" } torii-controllers = { path = "crates/controllers" } # macros -merge-options = { git = "https://github.com/dojoengine/dojo", rev = "82fe9bd" } +merge-options = { git = "https://github.com/bengineer42/dojo", branch = "add-write-schema-1.5" } bitflags = "2.9.1" @@ -74,10 +78,10 @@ anyhow = "1.0.89" assert_matches = "1.5.0" async-trait = "0.1.82" base64 = "0.21.2" -camino = { version = "1.1.2", features = [ "serde1" ] } -chrono = { version = "0.4.24", features = [ "serde" ] } -clap = { version = "4.5.16", features = [ "derive", "env" ] } -crypto-bigint = { version = "0.5.3", features = [ "serde" ] } +camino = { version = "1.1.2", features = ["serde1"] } +chrono = { version = "0.4.24", features = ["serde"] } +clap = { version = "4.5.16", features = ["derive", "env"] } +crypto-bigint = { version = "0.5.3", features = ["serde"] } data-url = "0.3" flate2 = "1.0.35" futures = "0.3.30" @@ -94,34 +98,49 @@ parking_lot = "0.12.1" rand = "0.8.5" rayon = "1.8.0" regex = "1.10.3" -reqwest = { version = "0.11.27", features = [ "json", "rustls-tls", "stream" ], default-features = false } -serde = { version = "1.0", features = [ "derive" ] } -serde_json = { version = "1.0", features = [ "arbitrary_precision" ] } -sqlx = { version = "0.8.2", features = [ "chrono", "macros", "regexp", "runtime-async-std", "runtime-tokio", "sqlite", "uuid" ] } +reqwest = { version = "0.11.27", features = [ + "json", + "rustls-tls", + "stream", +], default-features = false } +serde = { version = "1.0", features = ["derive"] } +serde_json = { version = "1.0", features = ["arbitrary_precision"] } +sqlx = { version = "0.8.2", features = [ + "chrono", + "macros", + "regexp", + "runtime-async-std", + "runtime-tokio", + "sqlite", + "uuid", +] } strum = "0.25" strum_macros = "0.25" tempfile = "3.9.0" thiserror = "1.0.32" -tokio = { version = "1.39.2", features = [ "full" ] } +tokio = { version = "1.39.2", features = ["full"] } tokio-util = "0.7.12" toml = "0.8" tower = "0.4.13" tower-http = "0.4.4" -tracing = { version = "0.1.38", features = [ "log" ], default-features = false } -tracing-subscriber = { version = "0.3.16", features = [ "env-filter", "json" ] } +tracing = { version = "0.1.38", features = ["log"], default-features = false } +tracing-subscriber = { version = "0.3.16", features = ["env-filter", "json"] } # indicatif tracing-indicatif = "0.3.9" indicatif = "0.17.9" terminal_size = "0.3.0" -url = { version = "2.4.0", features = [ "serde" ] } +url = { version = "2.4.0", features = ["serde"] } # TODO: see if we still need the git version -ipfs-api-backend-hyper = { git = "https://github.com/ferristseng/rust-ipfs-api", rev = "af2c17f7b19ef5b9898f458d97a90055c3605633", features = [ "with-hyper-rustls", "with-send-sync" ] } +ipfs-api-backend-hyper = { git = "https://github.com/ferristseng/rust-ipfs-api", rev = "af2c17f7b19ef5b9898f458d97a90055c3605633", features = [ + "with-hyper-rustls", + "with-send-sync", +] } mime_guess = "2.0" -sozo-scarbext = { git = "https://github.com/dojoengine/dojo", rev = "054623b" } +sozo-scarbext = { git = "https://github.com/bengineer42/dojo", branch = "add-write-schema-1.5" } scarb = { git = "https://github.com/dojoengine/scarb", rev = "38f13cb6f9f33836a61b231f7e00c5c4dc5cafd6" } -sozo-ops = { git = "https://github.com/dojoengine/dojo", rev = "054623b" } +sozo-ops = { git = "https://github.com/bengineer42/dojo", branch = "add-write-schema-1.5" } katana-runner = { git = "https://github.com/dojoengine/katana", rev = "f472095" } @@ -131,7 +150,12 @@ warp = "0.3" # gRPC prost = "0.12" -tonic = { version = "0.11", features = [ "gzip", "tls", "tls-roots", "tls-webpki-roots" ] } +tonic = { version = "0.11", features = [ + "gzip", + "tls", + "tls-roots", + "tls-webpki-roots", +] } tonic-build = "0.11" tonic-reflection = "0.11" tonic-web = "0.11" @@ -139,8 +163,14 @@ tonic-web = "0.11" # WASM-compatible gRPC deps tonic-web-wasm-client = "0.6.0" wasm-prost = { version = "0.13", package = "prost" } -wasm-tonic = { version = "0.12", default-features = false, features = [ "codegen", "gzip", "prost" ], package = "tonic" } -wasm-tonic-build = { version = "0.12", default-features = false, features = [ "prost" ], package = "tonic-build" } +wasm-tonic = { version = "0.12", default-features = false, features = [ + "codegen", + "gzip", + "prost", +], package = "tonic" } +wasm-tonic-build = { version = "0.12", default-features = false, features = [ + "prost", +], package = "tonic-build" } starknet-core = "0.12.3" starknet = "0.14.0" diff --git a/crates/indexer/engine/src/test.rs b/crates/indexer/engine/src/test.rs index 965c0998..2e4cc1ef 100644 --- a/crates/indexer/engine/src/test.rs +++ b/crates/indexer/engine/src/test.rs @@ -1089,6 +1089,34 @@ async fn test_load_from_remote_update(sequencer: &RunnerCtx) { .await .unwrap(); + let res = account + .execute_v3(vec![Call { + to: actions_address, + selector: get_selector_from_name("update_player_config_items").unwrap(), + calldata: vec![Felt::ZERO], + }]) + .send_with_cfg(&TxnConfig::init_wait()) + .await + .unwrap(); + + TransactionWaiter::new(res.transaction_hash, &provider) + .await + .unwrap(); + + let res = account + .execute_v3(vec![Call { + to: actions_address, + selector: get_selector_from_name("update_player_config_items").unwrap(), + calldata: vec![Felt::from(1), Felt::from(1), Felt::from(20), Felt::from(12)], + }]) + .send_with_cfg(&TxnConfig::init_wait()) + .await + .unwrap(); + + TransactionWaiter::new(res.transaction_hash, &provider) + .await + .unwrap(); + let world_reader = WorldContractReader::new(world_address, Arc::clone(&provider)); let tempfile = NamedTempFile::new().unwrap(); diff --git a/crates/processors/src/processors/mod.rs b/crates/processors/src/processors/mod.rs index 60e9ed06..9dcabe3b 100644 --- a/crates/processors/src/processors/mod.rs +++ b/crates/processors/src/processors/mod.rs @@ -20,6 +20,7 @@ use store_del_record::StoreDelRecordProcessor; use store_set_record::StoreSetRecordProcessor; use store_transaction::StoreTransactionProcessor; use store_update_member::StoreUpdateMemberProcessor; +use store_update_members::StoreUpdateMembersProcessor; use store_update_record::StoreUpdateRecordProcessor; use torii_storage::types::ContractType; use upgrade_event::UpgradeEventProcessor; @@ -45,6 +46,7 @@ mod store_del_record; mod store_set_record; mod store_transaction; mod store_update_member; +mod store_update_members; mod store_update_record; mod upgrade_event; mod upgrade_model; @@ -88,6 +90,7 @@ impl Processors

{ Box::new(StoreSetRecordProcessor), Box::new(StoreDelRecordProcessor), Box::new(StoreUpdateRecordProcessor), + Box::new(StoreUpdateMembersProcessor), Box::new(StoreUpdateMemberProcessor), Box::new(MetadataUpdateProcessor), Box::new(EventMessageProcessor), diff --git a/crates/processors/src/processors/store_update_members.rs b/crates/processors/src/processors/store_update_members.rs new file mode 100644 index 00000000..920bc6d6 --- /dev/null +++ b/crates/processors/src/processors/store_update_members.rs @@ -0,0 +1,149 @@ +use std::hash::{DefaultHasher, Hash, Hasher}; + +use async_trait::async_trait; +use dojo_types::schema::{Struct, Ty}; +use dojo_world::contracts::abigen::world::Event as WorldEvent; +use starknet::core::types::Event; +use starknet::core::utils::get_selector_from_name; +use starknet::providers::Provider; +use tracing::{debug, info}; + +use crate::error::Error; +use crate::task_manager::TaskId; +use crate::{EventProcessor, EventProcessorConfig, EventProcessorContext}; +use crate::{IndexingMode, Result}; + +pub(crate) const LOG_TARGET: &str = "torii::indexer::processors::store_update_members"; + +#[derive(Default, Debug)] +pub struct StoreUpdateMembersProcessor; + +#[async_trait] +impl

EventProcessor

for StoreUpdateMembersProcessor +where + P: Provider + Send + Sync + std::fmt::Debug + 'static, +{ + fn event_key(&self) -> String { + "StoreUpdateMembers".to_string() + } + + fn validate(&self, _event: &Event) -> bool { + true + } + + fn task_identifier(&self, event: &Event) -> TaskId { + let mut hasher = DefaultHasher::new(); + // model selector + event.keys[1].hash(&mut hasher); + // entity id + event.keys[2].hash(&mut hasher); + hasher.finish() + } + + fn task_dependencies(&self, event: &Event) -> Vec { + let mut hasher = DefaultHasher::new(); + event.keys[1].hash(&mut hasher); // Use the model selector to create a unique ID + vec![hasher.finish()] // Return the dependency on the register_model task + } + + fn indexing_mode(&self, event: &Event, config: &EventProcessorConfig) -> IndexingMode { + let model_id = event.keys[1]; + let is_historical = config.is_historical(&model_id); + if is_historical { + IndexingMode::Historical + } else { + let mut hasher = DefaultHasher::new(); + event.keys[0].hash(&mut hasher); + let n_members: u32 = event.data[0].try_into().unwrap(); + let mut members = event.data[1..(1 + n_members as usize)].to_vec(); + members.sort(); + members.hash(&mut hasher); + IndexingMode::Latest(hasher.finish()) + } + } + + async fn process(&self, ctx: &EventProcessorContext

) -> Result<()> { + // Torii version is coupled to the world version, so we can expect the event to be well + // formed. + let event = match WorldEvent::try_from(&ctx.event).unwrap_or_else(|_| { + panic!( + "Expected {} event to be well formed.", + >::event_key(self) + ) + }) { + WorldEvent::StoreUpdateMembers(e) => e, + _ => { + unreachable!() + } + }; + + let model_selector = event.selector; + let entity_id = event.entity_id; + let member_selectors = event.member_selectors; + + // If the model does not exist, silently ignore it. + // This can happen if only specific namespaces are indexed. + let model = match ctx.cache.model(event.selector).await { + Ok(m) => m, + Err(e) if e.to_string().contains("no rows") && !ctx.config.namespaces.is_empty() => { + debug!( + target: LOG_TARGET, + selector = %event.selector, + "Model does not exist, skipping." + ); + return Ok(()); + } + Err(e) => { + return Err(e.into()); + } + }; + + let schema = model.schema; + let mut values = event.values.to_vec(); + + let mut members = vec![]; + for member_selector in member_selectors { + let mut member = schema + .as_struct() + .expect("model schema must be a struct") + .children + .iter() + .find(|c| { + get_selector_from_name(&c.name).expect("invalid selector for member name") + == member_selector + }) + .ok_or(Error::ModelMemberNotFound(format!( + "{:#x}", + member_selector + )))? + .clone(); + member.ty.deserialize(&mut values)?; + members.push(member); + } + + info!( + target: LOG_TARGET, + namespace = %model.namespace, + name = %model.name, + entity_id = format!("{:#x}", entity_id), + "Store update members.", + ); + + let wrapped_ty = Ty::Struct(Struct { + name: schema.name(), + children: members, + }); + + ctx.storage + .set_entity( + wrapped_ty, + &ctx.event_id, + ctx.block_timestamp, + entity_id, + model_selector, + None, + ) + .await?; + Ok(()) + } +}