From 69d83d092f2076c25fa2fa982ee1779f7ea619b5 Mon Sep 17 00:00:00 2001 From: Aroso Emmanuel Date: Mon, 6 Jan 2025 09:48:44 +0100 Subject: [PATCH 1/9] fix(sozo): model get not using prefixes --- bin/sozo/src/commands/auth.rs | 36 +++++++++++++++++--------- bin/sozo/src/commands/mod.rs | 6 +++-- bin/sozo/src/commands/model.rs | 46 +++++++++++++++++++++++++++++++--- 3 files changed, 71 insertions(+), 17 deletions(-) diff --git a/bin/sozo/src/commands/auth.rs b/bin/sozo/src/commands/auth.rs index 10e02841ce..4f405b9d8d 100644 --- a/bin/sozo/src/commands/auth.rs +++ b/bin/sozo/src/commands/auth.rs @@ -242,7 +242,11 @@ async fn clone_permissions( .external_writers .iter() .filter_map(|(resource_selector, writers)| { - if writers.contains(&from_address) { Some(*resource_selector) } else { None } + if writers.contains(&from_address) { + Some(*resource_selector) + } else { + None + } }) .collect(); @@ -251,7 +255,11 @@ async fn clone_permissions( .external_owners .iter() .filter_map(|(resource_selector, owners)| { - if owners.contains(&from_address) { Some(*resource_selector) } else { None } + if owners.contains(&from_address) { + Some(*resource_selector) + } else { + None + } }) .collect(); @@ -303,16 +311,20 @@ async fn clone_permissions( writers_resource_selectors.extend(external_writer_of.iter().copied()); owners_resource_selectors.extend(external_owner_of.iter().copied()); - writer_of.extend( - external_writer_of - .iter() - .map(|r| if r != &WORLD { format!("{:#066x}", r) } else { "World".to_string() }), - ); - owner_of.extend( - external_owner_of - .iter() - .map(|r| if r != &WORLD { format!("{:#066x}", r) } else { "World".to_string() }), - ); + writer_of.extend(external_writer_of.iter().map(|r| { + if r != &WORLD { + format!("{:#066x}", r) + } else { + "World".to_string() + } + })); + owner_of.extend(external_owner_of.iter().map(|r| { + if r != &WORLD { + format!("{:#066x}", r) + } else { + "World".to_string() + } + })); // Sort the tags to have a deterministic output. let mut writer_of = writer_of.into_iter().collect::>(); diff --git a/bin/sozo/src/commands/mod.rs b/bin/sozo/src/commands/mod.rs index 9ca42277f3..d5ee653c98 100644 --- a/bin/sozo/src/commands/mod.rs +++ b/bin/sozo/src/commands/mod.rs @@ -44,8 +44,10 @@ pub enum Commands { Build(Box), #[command(about = "Build and migrate the world every time a file changes")] Dev(Box), - #[command(about = "Run a migration, declaring and deploying contracts as necessary to update \ - the world")] + #[command( + about = "Run a migration, declaring and deploying contracts as necessary to update \ + the world" + )] Migrate(Box), #[command(about = "Execute a system with the given calldata.")] Execute(Box), diff --git a/bin/sozo/src/commands/model.rs b/bin/sozo/src/commands/model.rs index 4458f940cc..599c7fd697 100644 --- a/bin/sozo/src/commands/model.rs +++ b/bin/sozo/src/commands/model.rs @@ -1,5 +1,6 @@ use anyhow::Result; use clap::{Args, Subcommand}; +use dojo_world::config::calldata_decoder; use scarb::core::Config; use sozo_ops::model; use sozo_ops::resource_descriptor::ResourceDescriptor; @@ -109,8 +110,11 @@ hashes, called 'hash' in the following documentation. #[arg(value_name = "KEYS")] #[arg(value_delimiter = ',')] - #[arg(help = "Comma seperated values e.g., 0x12345,0x69420,...")] - keys: Vec, + #[arg(help = "Comma separated values e.g., \ + 0x12345,0x69420,sstr:\"hello\",sstr:\"misty\". Supported prefixes:\n \ + - sstr: A cairo short string\n \ + - no prefix: A cairo felt")] + keys: String, #[command(flatten)] world: WorldOptions, @@ -119,7 +123,9 @@ hashes, called 'hash' in the following documentation. starknet: StarknetOptions, #[arg(short, long)] - #[arg(help = "Block number at which to retrieve the model data (pending block by default)")] + #[arg( + help = "Block number at which to retrieve the model data (pending block by default)" + )] block: Option, }, } @@ -205,6 +211,8 @@ impl ModelArgs { let (world_diff, provider, _) = utils::get_world_diff_and_provider(starknet, world, &ws).await?; + let keys = calldata_decoder::decode_calldata(&keys)?; + let (record, _, _) = model::model_get( tag.to_string(), keys, @@ -222,3 +230,35 @@ impl ModelArgs { }) } } + +#[cfg(test)] +mod tests { + use super::*; + use starknet::core::utils::cairo_short_string_to_felt; + + #[test] + fn test_short_string_equals_felt() { + // Test that sstr:"misty" equals 0x6d69737479 + let with_prefix = "sstr:\"misty\""; + let with_hex = "0x6d69737479"; + + let felt_from_string = calldata_decoder::decode_calldata(with_prefix).unwrap(); + let felt_from_hex = calldata_decoder::decode_calldata(with_hex).unwrap(); + + assert_eq!(felt_from_string, felt_from_hex); + assert_eq!(felt_from_string[0], Felt::from_hex_str("0x6d69737479").unwrap()); + } + + #[test] + fn test_hex_equals_decimal() { + // Test that 0x6d69737479 equals 469920609401 + let with_hex = "0x6d69737479"; + let with_decimal = "469920609401"; + + let felt_from_hex = calldata_decoder::decode_calldata(with_hex).unwrap(); + let felt_from_decimal = calldata_decoder::decode_calldata(with_decimal).unwrap(); + + assert_eq!(felt_from_hex, felt_from_decimal); + assert_eq!(felt_from_hex[0], Felt::from(469920609401u128)); + } +} From f34a85e50a4f45535bb2775f401a6f08e355d8ee Mon Sep 17 00:00:00 2001 From: Aroso Emmanuel Date: Thu, 16 Jan 2025 16:45:51 +0100 Subject: [PATCH 2/9] rewrote model get key parser --- bin/sozo/src/commands/model.rs | 91 +++++++++++++------ .../dojo/world/src/config/calldata_decoder.rs | 9 +- 2 files changed, 73 insertions(+), 27 deletions(-) diff --git a/bin/sozo/src/commands/model.rs b/bin/sozo/src/commands/model.rs index 599c7fd697..90315eb83f 100644 --- a/bin/sozo/src/commands/model.rs +++ b/bin/sozo/src/commands/model.rs @@ -1,5 +1,5 @@ use anyhow::Result; -use clap::{Args, Subcommand}; +use clap::{value_parser, Args, Subcommand}; use dojo_world::config::calldata_decoder; use scarb::core::Config; use sozo_ops::model; @@ -111,10 +111,11 @@ hashes, called 'hash' in the following documentation. #[arg(value_name = "KEYS")] #[arg(value_delimiter = ',')] #[arg(help = "Comma separated values e.g., \ - 0x12345,0x69420,sstr:\"hello\",sstr:\"misty\". Supported prefixes:\n \ + 0x12345,0x69420,sstr:\"hello\". Supported prefixes:\n \ - sstr: A cairo short string\n \ - no prefix: A cairo felt")] - keys: String, + #[arg(value_parser = model_key_parser)] + keys: Vec, #[command(flatten)] world: WorldOptions, @@ -130,6 +131,15 @@ hashes, called 'hash' in the following documentation. }, } +// Custom parser for model keys +fn model_key_parser(s: &str) -> Result { + if s.contains(':') && !s.starts_with("sstr:") { + anyhow::bail!("Only 'sstr:' prefix is supported for model keys"); + } + let felts = calldata_decoder::decode_calldata(s)?; + Ok(felts[0]) +} + impl ModelArgs { pub fn run(self, config: &Config) -> Result<()> { trace!(args = ?self); @@ -211,8 +221,6 @@ impl ModelArgs { let (world_diff, provider, _) = utils::get_world_diff_and_provider(starknet, world, &ws).await?; - let keys = calldata_decoder::decode_calldata(&keys)?; - let (record, _, _) = model::model_get( tag.to_string(), keys, @@ -234,31 +242,62 @@ impl ModelArgs { #[cfg(test)] mod tests { use super::*; + use clap::Parser; use starknet::core::utils::cairo_short_string_to_felt; - #[test] - fn test_short_string_equals_felt() { - // Test that sstr:"misty" equals 0x6d69737479 - let with_prefix = "sstr:\"misty\""; - let with_hex = "0x6d69737479"; - - let felt_from_string = calldata_decoder::decode_calldata(with_prefix).unwrap(); - let felt_from_hex = calldata_decoder::decode_calldata(with_hex).unwrap(); - - assert_eq!(felt_from_string, felt_from_hex); - assert_eq!(felt_from_string[0], Felt::from_hex_str("0x6d69737479").unwrap()); + #[derive(Parser, Debug)] + struct TestCommand { + #[command(subcommand)] + command: ModelCommand, } #[test] - fn test_hex_equals_decimal() { - // Test that 0x6d69737479 equals 469920609401 - let with_hex = "0x6d69737479"; - let with_decimal = "469920609401"; - - let felt_from_hex = calldata_decoder::decode_calldata(with_hex).unwrap(); - let felt_from_decimal = calldata_decoder::decode_calldata(with_decimal).unwrap(); - - assert_eq!(felt_from_hex, felt_from_decimal); - assert_eq!(felt_from_hex[0], Felt::from(469920609401u128)); + fn test_model_get_argument_parsing() { + // Test parsing with hex + let args = TestCommand::parse_from([ + "model", // Placeholder for the binary name + "get", + "Account", + "0x054cb935d86d80b5a0a6e756edf448ab33876d01dd2b07a2a4e63a41e06d0ef5,0x6d69737479", + ]); + + if let ModelCommand::Get { keys, .. } = args.command { + let expected = vec![ + Felt::from_hex( + "0x054cb935d86d80b5a0a6e756edf448ab33876d01dd2b07a2a4e63a41e06d0ef5", + ) + .unwrap(), + Felt::from_hex("0x6d69737479").unwrap(), + ]; + assert_eq!(keys, expected); + } else { + panic!("Expected Get command"); + } + + // Test parsing with string prefix + let args = TestCommand::parse_from([ + "model", + "get", + "Account", + "0x054cb935d86d80b5a0a6e756edf448ab33876d01dd2b07a2a4e63a41e06d0ef5,sstr:\"misty\"", + ]); + + if let ModelCommand::Get { keys, .. } = args.command { + let expected = vec![ + Felt::from_hex( + "0x054cb935d86d80b5a0a6e756edf448ab33876d01dd2b07a2a4e63a41e06d0ef5", + ) + .unwrap(), + cairo_short_string_to_felt("misty").unwrap(), + ]; + assert_eq!(keys, expected); + } else { + panic!("Expected Get command"); + } + + // Test invalid prefix + let result = + TestCommand::try_parse_from(["model", "get", "Account", "0x123,str:\"hello\""]); + assert!(result.is_err()); } } diff --git a/crates/dojo/world/src/config/calldata_decoder.rs b/crates/dojo/world/src/config/calldata_decoder.rs index fd74e0aa46..06c2eca4da 100644 --- a/crates/dojo/world/src/config/calldata_decoder.rs +++ b/crates/dojo/world/src/config/calldata_decoder.rs @@ -161,7 +161,14 @@ fn decode_inner(item: &str) -> DecoderResult> { match prefix { "u256" => U256CalldataDecoder.decode(value)?, "str" => StrCalldataDecoder.decode(value)?, - "sstr" => ShortStrCalldataDecoder.decode(value)?, + "sstr" => { + let value = if value.starts_with('"') && value.ends_with('"') { + value.trim_matches('"') + } else { + value + }; + ShortStrCalldataDecoder.decode(value)? + }, "int" => SignedIntegerCalldataDecoder.decode(value)?, _ => DefaultCalldataDecoder.decode(item)?, } From 2b0dac4d1d0e2f985704cd0b160e3b7b122645b1 Mon Sep 17 00:00:00 2001 From: Aroso Emmanuel Date: Thu, 16 Jan 2025 17:56:08 +0100 Subject: [PATCH 3/9] handle the potential empty felts --- bin/sozo/src/commands/model.rs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/bin/sozo/src/commands/model.rs b/bin/sozo/src/commands/model.rs index 90315eb83f..9b48532fe6 100644 --- a/bin/sozo/src/commands/model.rs +++ b/bin/sozo/src/commands/model.rs @@ -137,6 +137,9 @@ fn model_key_parser(s: &str) -> Result { anyhow::bail!("Only 'sstr:' prefix is supported for model keys"); } let felts = calldata_decoder::decode_calldata(s)?; + if felts.is_empty() { + anyhow::bail!("Failed to parse key '{}': no values returned.", s); + } Ok(felts[0]) } @@ -255,7 +258,7 @@ mod tests { fn test_model_get_argument_parsing() { // Test parsing with hex let args = TestCommand::parse_from([ - "model", // Placeholder for the binary name + "model", "get", "Account", "0x054cb935d86d80b5a0a6e756edf448ab33876d01dd2b07a2a4e63a41e06d0ef5,0x6d69737479", From 183ae330094140b805cdda4cb91c7329ef918f12 Mon Sep 17 00:00:00 2001 From: Aroso Emmanuel Date: Fri, 17 Jan 2025 19:21:52 +0100 Subject: [PATCH 4/9] rust fmt --- bin/sozo/src/commands/model.rs | 15 ++++++--------- crates/dojo/world/src/config/calldata_decoder.rs | 2 +- 2 files changed, 7 insertions(+), 10 deletions(-) diff --git a/bin/sozo/src/commands/model.rs b/bin/sozo/src/commands/model.rs index 9b48532fe6..68f260615e 100644 --- a/bin/sozo/src/commands/model.rs +++ b/bin/sozo/src/commands/model.rs @@ -1,5 +1,5 @@ use anyhow::Result; -use clap::{value_parser, Args, Subcommand}; +use clap::{Args, Subcommand}; use dojo_world::config::calldata_decoder; use scarb::core::Config; use sozo_ops::model; @@ -110,10 +110,8 @@ hashes, called 'hash' in the following documentation. #[arg(value_name = "KEYS")] #[arg(value_delimiter = ',')] - #[arg(help = "Comma separated values e.g., \ - 0x12345,0x69420,sstr:\"hello\". Supported prefixes:\n \ - - sstr: A cairo short string\n \ - - no prefix: A cairo felt")] + #[arg(help = "Comma separated values e.g., 0x12345,0x69420,sstr:\"hello\". Supported \ + prefixes:\n - sstr: A cairo short string\n - no prefix: A cairo felt")] #[arg(value_parser = model_key_parser)] keys: Vec, @@ -124,9 +122,7 @@ hashes, called 'hash' in the following documentation. starknet: StarknetOptions, #[arg(short, long)] - #[arg( - help = "Block number at which to retrieve the model data (pending block by default)" - )] + #[arg(help = "Block number at which to retrieve the model data (pending block by default)")] block: Option, }, } @@ -244,10 +240,11 @@ impl ModelArgs { #[cfg(test)] mod tests { - use super::*; use clap::Parser; use starknet::core::utils::cairo_short_string_to_felt; + use super::*; + #[derive(Parser, Debug)] struct TestCommand { #[command(subcommand)] diff --git a/crates/dojo/world/src/config/calldata_decoder.rs b/crates/dojo/world/src/config/calldata_decoder.rs index aae2798609..37d08175cb 100644 --- a/crates/dojo/world/src/config/calldata_decoder.rs +++ b/crates/dojo/world/src/config/calldata_decoder.rs @@ -168,7 +168,7 @@ pub fn decode_single_calldata(item: &str) -> DecoderResult> { value }; ShortStrCalldataDecoder.decode(value)? - }, + } "int" => SignedIntegerCalldataDecoder.decode(value)?, _ => DefaultCalldataDecoder.decode(item)?, } From 018847fa35d6ed8f82f1a18c4c76e75e30e5e397 Mon Sep 17 00:00:00 2001 From: Aroso Emmanuel Date: Fri, 17 Jan 2025 19:30:43 +0100 Subject: [PATCH 5/9] rust fmt --- bin/sozo/src/commands/auth.rs | 36 ++++++----------- bin/sozo/src/commands/mod.rs | 75 ++++++++++------------------------- 2 files changed, 34 insertions(+), 77 deletions(-) diff --git a/bin/sozo/src/commands/auth.rs b/bin/sozo/src/commands/auth.rs index 4f405b9d8d..10e02841ce 100644 --- a/bin/sozo/src/commands/auth.rs +++ b/bin/sozo/src/commands/auth.rs @@ -242,11 +242,7 @@ async fn clone_permissions( .external_writers .iter() .filter_map(|(resource_selector, writers)| { - if writers.contains(&from_address) { - Some(*resource_selector) - } else { - None - } + if writers.contains(&from_address) { Some(*resource_selector) } else { None } }) .collect(); @@ -255,11 +251,7 @@ async fn clone_permissions( .external_owners .iter() .filter_map(|(resource_selector, owners)| { - if owners.contains(&from_address) { - Some(*resource_selector) - } else { - None - } + if owners.contains(&from_address) { Some(*resource_selector) } else { None } }) .collect(); @@ -311,20 +303,16 @@ async fn clone_permissions( writers_resource_selectors.extend(external_writer_of.iter().copied()); owners_resource_selectors.extend(external_owner_of.iter().copied()); - writer_of.extend(external_writer_of.iter().map(|r| { - if r != &WORLD { - format!("{:#066x}", r) - } else { - "World".to_string() - } - })); - owner_of.extend(external_owner_of.iter().map(|r| { - if r != &WORLD { - format!("{:#066x}", r) - } else { - "World".to_string() - } - })); + writer_of.extend( + external_writer_of + .iter() + .map(|r| if r != &WORLD { format!("{:#066x}", r) } else { "World".to_string() }), + ); + owner_of.extend( + external_owner_of + .iter() + .map(|r| if r != &WORLD { format!("{:#066x}", r) } else { "World".to_string() }), + ); // Sort the tags to have a deterministic output. let mut writer_of = writer_of.into_iter().collect::>(); diff --git a/bin/sozo/src/commands/mod.rs b/bin/sozo/src/commands/mod.rs index af8d2ac773..9ca42277f3 100644 --- a/bin/sozo/src/commands/mod.rs +++ b/bin/sozo/src/commands/mod.rs @@ -5,7 +5,6 @@ use auth::AuthArgs; use clap::Subcommand; use events::EventsArgs; use scarb::core::{Config, Package, Workspace}; -use semver::{Version, VersionReq}; use tracing::info_span; pub(crate) mod auth; @@ -33,8 +32,6 @@ use init::InitArgs; use inspect::InspectArgs; use migrate::MigrateArgs; use model::ModelArgs; -#[cfg(feature = "walnut")] -use sozo_walnut::walnut::WalnutArgs; use test::TestArgs; pub(crate) const LOG_TARGET: &str = "sozo::cli"; @@ -47,12 +44,10 @@ pub enum Commands { Build(Box), #[command(about = "Build and migrate the world every time a file changes")] Dev(Box), - #[command( - about = "Run a migration, declaring and deploying contracts as necessary to update \ - the world" - )] + #[command(about = "Run a migration, declaring and deploying contracts as necessary to update \ + the world")] Migrate(Box), - #[command(about = "Execute one or several systems with the given calldata.")] + #[command(about = "Execute a system with the given calldata.")] Execute(Box), #[command(about = "Inspect the world")] Inspect(Box), @@ -70,9 +65,6 @@ pub enum Commands { Model(Box), #[command(about = "Inspect events emitted by the world")] Events(Box), - #[cfg(feature = "walnut")] - #[command(about = "Interact with walnut.dev - transactions debugger and simulator")] - Walnut(Box), } impl fmt::Display for Commands { @@ -91,8 +83,6 @@ impl fmt::Display for Commands { Commands::Init(_) => write!(f, "Init"), Commands::Model(_) => write!(f, "Model"), Commands::Events(_) => write!(f, "Events"), - #[cfg(feature = "walnut")] - Commands::Walnut(_) => write!(f, "WalnutVerify"), } } } @@ -115,12 +105,10 @@ pub fn run(command: Commands, config: &Config) -> Result<()> { Commands::Clean(args) => args.run(config), Commands::Call(args) => args.run(config), Commands::Test(args) => args.run(config), - Commands::Hash(args) => args.run(config).map(|_| ()), + Commands::Hash(args) => args.run().map(|_| ()), Commands::Init(args) => args.run(config), Commands::Model(args) => args.run(config), Commands::Events(args) => args.run(config), - #[cfg(feature = "walnut")] - Commands::Walnut(args) => args.run(config), } } @@ -141,43 +129,24 @@ pub fn check_package_dojo_version(ws: &Workspace<'_>, package: &Package) -> anyh && dojo_dep_str.contains("tag=v") && !dojo_dep_str.contains(dojo_version) { - // safe to unwrap since we know the string contains "tag=v". - // "dojo * (git+https://github.com/dojoengine/dojo?tag=v1.0.10)" - let dojo_dep_version = dojo_dep_str.split("tag=v") - .nth(1) // Get the part after "tag=v" - .map(|s| s.trim_end_matches(')')) - .expect("Unexpected dojo dependency format"); - - let dojo_dep_version = Version::parse(dojo_dep_version).unwrap(); - - let version_parts: Vec<&str> = dojo_version.split('.').collect(); - let major_minor = format!("{}.{}", version_parts[0], version_parts[1]); - let dojo_req_version = VersionReq::parse(&format!(">={}", major_minor)).unwrap(); - - if !dojo_req_version.matches(&dojo_dep_version) { - if let Ok(cp) = ws.current_package() { - // Selected package. - let path = if cp.id == package.id { - package.manifest_path() - } else { - ws.manifest_path() - }; - - anyhow::bail!( - "Found dojo-core version mismatch: expected {}. Please verify your dojo \ - dependency in {}", - dojo_req_version, - path - ) - } else { - // Virtual workspace. - anyhow::bail!( - "Found dojo-core version mismatch: expected {}. Please verify your dojo \ - dependency in {}", - dojo_req_version, - ws.manifest_path() - ) - } + if let Ok(cp) = ws.current_package() { + let path = + if cp.id == package.id { package.manifest_path() } else { ws.manifest_path() }; + + anyhow::bail!( + "Found dojo-core version mismatch: expected {}. Please verify your dojo \ + dependency in {}", + dojo_version, + path + ) + } else { + // Virtual workspace. + anyhow::bail!( + "Found dojo-core version mismatch: expected {}. Please verify your dojo \ + dependency in {}", + dojo_version, + ws.manifest_path() + ) } } } From 47df4b207cb92d4785487a2ffcb93c909ec428ab Mon Sep 17 00:00:00 2001 From: Aroso Emmanuel Date: Fri, 17 Jan 2025 19:53:04 +0100 Subject: [PATCH 6/9] rust fmt --- bin/sozo/src/commands/mod.rs | 69 +++++++++++++++++++++++++----------- 1 file changed, 49 insertions(+), 20 deletions(-) diff --git a/bin/sozo/src/commands/mod.rs b/bin/sozo/src/commands/mod.rs index 9ca42277f3..bcc1e51d93 100644 --- a/bin/sozo/src/commands/mod.rs +++ b/bin/sozo/src/commands/mod.rs @@ -5,6 +5,7 @@ use auth::AuthArgs; use clap::Subcommand; use events::EventsArgs; use scarb::core::{Config, Package, Workspace}; +use semver::{Version, VersionReq}; use tracing::info_span; pub(crate) mod auth; @@ -32,6 +33,8 @@ use init::InitArgs; use inspect::InspectArgs; use migrate::MigrateArgs; use model::ModelArgs; +#[cfg(feature = "walnut")] +use sozo_walnut::walnut::WalnutArgs; use test::TestArgs; pub(crate) const LOG_TARGET: &str = "sozo::cli"; @@ -47,7 +50,7 @@ pub enum Commands { #[command(about = "Run a migration, declaring and deploying contracts as necessary to update \ the world")] Migrate(Box), - #[command(about = "Execute a system with the given calldata.")] + #[command(about = "Execute one or several systems with the given calldata.")] Execute(Box), #[command(about = "Inspect the world")] Inspect(Box), @@ -65,6 +68,9 @@ pub enum Commands { Model(Box), #[command(about = "Inspect events emitted by the world")] Events(Box), + #[cfg(feature = "walnut")] + #[command(about = "Interact with walnut.dev - transactions debugger and simulator")] + Walnut(Box), } impl fmt::Display for Commands { @@ -83,6 +89,8 @@ impl fmt::Display for Commands { Commands::Init(_) => write!(f, "Init"), Commands::Model(_) => write!(f, "Model"), Commands::Events(_) => write!(f, "Events"), + #[cfg(feature = "walnut")] + Commands::Walnut(_) => write!(f, "WalnutVerify"), } } } @@ -105,10 +113,12 @@ pub fn run(command: Commands, config: &Config) -> Result<()> { Commands::Clean(args) => args.run(config), Commands::Call(args) => args.run(config), Commands::Test(args) => args.run(config), - Commands::Hash(args) => args.run().map(|_| ()), + Commands::Hash(args) => args.run(config).map(|_| ()), Commands::Init(args) => args.run(config), Commands::Model(args) => args.run(config), Commands::Events(args) => args.run(config), + #[cfg(feature = "walnut")] + Commands::Walnut(args) => args.run(config), } } @@ -129,24 +139,43 @@ pub fn check_package_dojo_version(ws: &Workspace<'_>, package: &Package) -> anyh && dojo_dep_str.contains("tag=v") && !dojo_dep_str.contains(dojo_version) { - if let Ok(cp) = ws.current_package() { - let path = - if cp.id == package.id { package.manifest_path() } else { ws.manifest_path() }; - - anyhow::bail!( - "Found dojo-core version mismatch: expected {}. Please verify your dojo \ - dependency in {}", - dojo_version, - path - ) - } else { - // Virtual workspace. - anyhow::bail!( - "Found dojo-core version mismatch: expected {}. Please verify your dojo \ - dependency in {}", - dojo_version, - ws.manifest_path() - ) + // safe to unwrap since we know the string contains "tag=v". + // "dojo * (git+https://github.com/dojoengine/dojo?tag=v1.0.10)" + let dojo_dep_version = dojo_dep_str.split("tag=v") + .nth(1) // Get the part after "tag=v" + .map(|s| s.trim_end_matches(')')) + .expect("Unexpected dojo dependency format"); + + let dojo_dep_version = Version::parse(dojo_dep_version).unwrap(); + + let version_parts: Vec<&str> = dojo_version.split('.').collect(); + let major_minor = format!("{}.{}", version_parts[0], version_parts[1]); + let dojo_req_version = VersionReq::parse(&format!(">={}", major_minor)).unwrap(); + + if !dojo_req_version.matches(&dojo_dep_version) { + if let Ok(cp) = ws.current_package() { + // Selected package. + let path = if cp.id == package.id { + package.manifest_path() + } else { + ws.manifest_path() + }; + + anyhow::bail!( + "Found dojo-core version mismatch: expected {}. Please verify your dojo \ + dependency in {}", + dojo_req_version, + path + ) + } else { + // Virtual workspace. + anyhow::bail!( + "Found dojo-core version mismatch: expected {}. Please verify your dojo \ + dependency in {}", + dojo_req_version, + ws.manifest_path() + ) + } } } } From 597ba88c9f4f4dd4438d866bff93810ee01f8ff1 Mon Sep 17 00:00:00 2001 From: Aroso Emmanuel Date: Thu, 30 Jan 2025 16:50:19 +0100 Subject: [PATCH 7/9] add support for all prfixes --- bin/sozo/src/commands/model.rs | 127 ++++++++++++++++++++++++++------- 1 file changed, 101 insertions(+), 26 deletions(-) diff --git a/bin/sozo/src/commands/model.rs b/bin/sozo/src/commands/model.rs index 68f260615e..f92d249703 100644 --- a/bin/sozo/src/commands/model.rs +++ b/bin/sozo/src/commands/model.rs @@ -113,7 +113,7 @@ hashes, called 'hash' in the following documentation. #[arg(help = "Comma separated values e.g., 0x12345,0x69420,sstr:\"hello\". Supported \ prefixes:\n - sstr: A cairo short string\n - no prefix: A cairo felt")] #[arg(value_parser = model_key_parser)] - keys: Vec, + keys: Vec>, #[command(flatten)] world: WorldOptions, @@ -128,15 +128,9 @@ hashes, called 'hash' in the following documentation. } // Custom parser for model keys -fn model_key_parser(s: &str) -> Result { - if s.contains(':') && !s.starts_with("sstr:") { - anyhow::bail!("Only 'sstr:' prefix is supported for model keys"); - } - let felts = calldata_decoder::decode_calldata(s)?; - if felts.is_empty() { - anyhow::bail!("Failed to parse key '{}': no values returned.", s); - } - Ok(felts[0]) +fn model_key_parser(s: &str) -> Result> { + let felts = calldata_decoder::decode_calldata(&vec![s.to_string()])?; + Ok(felts) } impl ModelArgs { @@ -220,9 +214,11 @@ impl ModelArgs { let (world_diff, provider, _) = utils::get_world_diff_and_provider(starknet, world, &ws).await?; + let flattened_keys: Vec = keys.into_iter().flatten().collect(); + let (record, _, _) = model::model_get( tag.to_string(), - keys, + flattened_keys, world_diff.world_info.address, &provider, block_id, @@ -240,6 +236,9 @@ impl ModelArgs { #[cfg(test)] mod tests { + // To do: Add more tests for the flattening of keys + // let flattened_keys: Vec = keys.into_iter().flatten().collect(); + use clap::Parser; use starknet::core::utils::cairo_short_string_to_felt; @@ -263,18 +262,20 @@ mod tests { if let ModelCommand::Get { keys, .. } = args.command { let expected = vec![ - Felt::from_hex( - "0x054cb935d86d80b5a0a6e756edf448ab33876d01dd2b07a2a4e63a41e06d0ef5", - ) - .unwrap(), - Felt::from_hex("0x6d69737479").unwrap(), + vec![ + Felt::from_hex( + "0x054cb935d86d80b5a0a6e756edf448ab33876d01dd2b07a2a4e63a41e06d0ef5", + ) + .unwrap(), + ], + vec![Felt::from_hex("0x6d69737479").unwrap()], ]; assert_eq!(keys, expected); } else { panic!("Expected Get command"); } - // Test parsing with string prefix + // Test parsing with short string prefix let args = TestCommand::parse_from([ "model", "get", @@ -284,20 +285,94 @@ mod tests { if let ModelCommand::Get { keys, .. } = args.command { let expected = vec![ - Felt::from_hex( - "0x054cb935d86d80b5a0a6e756edf448ab33876d01dd2b07a2a4e63a41e06d0ef5", - ) - .unwrap(), - cairo_short_string_to_felt("misty").unwrap(), + vec![ + Felt::from_hex( + "0x054cb935d86d80b5a0a6e756edf448ab33876d01dd2b07a2a4e63a41e06d0ef5", + ) + .unwrap(), + ], + vec![cairo_short_string_to_felt("misty").unwrap()], ]; assert_eq!(keys, expected); } else { panic!("Expected Get command"); } - // Test invalid prefix - let result = - TestCommand::try_parse_from(["model", "get", "Account", "0x123,str:\"hello\""]); - assert!(result.is_err()); + // Test parsing with u256 prefix + let args = TestCommand::parse_from([ + "model", + "get", + "Account", + "0x054cb935d86d80b5a0a6e756edf448ab33876d01dd2b07a2a4e63a41e06d0ef5,u256:0x1", + ]); + + if let ModelCommand::Get { keys, .. } = args.command { + let expected = vec![ + vec![ + Felt::from_hex( + "0x054cb935d86d80b5a0a6e756edf448ab33876d01dd2b07a2a4e63a41e06d0ef5", + ) + .unwrap(), + ], + vec![Felt::ONE, Felt::ZERO], + ]; + assert_eq!(keys, expected); + } else { + panic!("Expected Get command"); + } + + // Test parsing with int prefix + let args = TestCommand::parse_from(["model", "get", "Account", "int:-123456789"]); + + if let ModelCommand::Get { keys, .. } = args.command { + let expected = vec![vec![(-123456789_i64).into()]]; + assert_eq!(keys, expected); + } else { + panic!("Expected Get command"); + } + + // Test parsing with str prefix + let args = TestCommand::parse_from(["model", "get", "Account", "str:hello"]); + + if let ModelCommand::Get { keys, .. } = args.command { + let expected = vec![vec![ + Felt::ZERO, + cairo_short_string_to_felt("hello").unwrap(), + Felt::from_dec_str("5").unwrap(), + ]]; + assert_eq!(keys, expected); + } else { + panic!("Expected Get command"); + } + + // Test parsing with all prefixes + let args = TestCommand::parse_from([ + "model", + "get", + "Account", + "0x054cb935d86d80b5a0a6e756edf448ab33876d01dd2b07a2a4e63a41e06d0ef5,u256:0x1,int:\ + -123456789,str:hello", + ]); + + if let ModelCommand::Get { keys, .. } = args.command { + let expected = vec![ + vec![ + Felt::from_hex( + "0x054cb935d86d80b5a0a6e756edf448ab33876d01dd2b07a2a4e63a41e06d0ef5", + ) + .unwrap(), + ], + vec![Felt::ONE, Felt::ZERO], + vec![(-123456789_i64).into()], + vec![ + Felt::ZERO, + cairo_short_string_to_felt("hello").unwrap(), + Felt::from_dec_str("5").unwrap(), + ], + ]; + assert_eq!(keys, expected); + } else { + panic!("Expected Get command"); + } } } From 0cfa68325e61d5e92d8b3003f03ff2fb803253cf Mon Sep 17 00:00:00 2001 From: Aroso Emmanuel Date: Thu, 30 Jan 2025 17:13:23 +0100 Subject: [PATCH 8/9] updated help text of model get keys to show supported prefixes --- bin/sozo/src/commands/model.rs | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/bin/sozo/src/commands/model.rs b/bin/sozo/src/commands/model.rs index f92d249703..59b2d94dd6 100644 --- a/bin/sozo/src/commands/model.rs +++ b/bin/sozo/src/commands/model.rs @@ -110,8 +110,11 @@ hashes, called 'hash' in the following documentation. #[arg(value_name = "KEYS")] #[arg(value_delimiter = ',')] - #[arg(help = "Comma separated values e.g., 0x12345,0x69420,sstr:\"hello\". Supported \ - prefixes:\n - sstr: A cairo short string\n - no prefix: A cairo felt")] + #[arg(help = "Comma separated values, e.g., 0x12345,0x69420,sstr:\"hello\", u256:0x123. + Supporting all prefixes:\n - u256: A 256-bit unsigned integer\n - str: A \ + cairo string (ByteArray)\n - sstr: A cairo short string\n - int: A \ + signed integer\n - no prefix: A cairo felt or any type that fits into \ + one felt")] #[arg(value_parser = model_key_parser)] keys: Vec>, From aa29464b1e240fd4a7300a59bf829ab6566d4250 Mon Sep 17 00:00:00 2001 From: Aroso Emmanuel Date: Fri, 31 Jan 2025 00:03:16 +0100 Subject: [PATCH 9/9] remove trimming of quotes --- crates/dojo/world/src/config/calldata_decoder.rs | 9 +-------- 1 file changed, 1 insertion(+), 8 deletions(-) diff --git a/crates/dojo/world/src/config/calldata_decoder.rs b/crates/dojo/world/src/config/calldata_decoder.rs index 359db39d0a..b59142ca5c 100644 --- a/crates/dojo/world/src/config/calldata_decoder.rs +++ b/crates/dojo/world/src/config/calldata_decoder.rs @@ -224,14 +224,7 @@ pub fn decode_single_calldata(item: &str) -> DecoderResult> { match prefix { "u256" => U256CalldataDecoder.decode(value)?, "str" => StrCalldataDecoder.decode(value)?, - "sstr" => { - let value = if value.starts_with('"') && value.ends_with('"') { - value.trim_matches('"') - } else { - value - }; - ShortStrCalldataDecoder.decode(value)? - } + "sstr" => ShortStrCalldataDecoder.decode(value)?, "int" => SignedIntegerCalldataDecoder.decode(value)?, "arr" => DynamicArrayCalldataDecoder.decode(value)?, "u256arr" => U256DynamicArrayCalldataDecoder.decode(value)?,