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(sozo): add events command back and event better #2619

Merged
merged 2 commits into from
Nov 4, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
371 changes: 329 additions & 42 deletions bin/sozo/src/commands/events.rs
Original file line number Diff line number Diff line change
@@ -1,8 +1,16 @@
use std::collections::HashMap;
use std::sync::Arc;

use anyhow::Result;
use clap::Args;
use colored::Colorize;
use dojo_world::contracts::abigen::world::{self, Event as WorldEvent};
use dojo_world::diff::WorldDiff;
use scarb::core::Config;
use sozo_ops::events;
use tracing::trace;
use sozo_ops::model;
use starknet::core::types::{BlockId, BlockTag, EventFilter, Felt};
use starknet::core::utils::starknet_keccak;
use starknet::providers::Provider;

use super::options::starknet::StarknetOptions;
use super::options::world::WorldOptions;
Expand All @@ -24,6 +32,7 @@

#[arg(short, long)]
#[arg(help = "Number of events to return per page")]
#[arg(default_value_t = 100)]

Check warning on line 35 in bin/sozo/src/commands/events.rs

View check run for this annotation

Codecov / codecov/patch

bin/sozo/src/commands/events.rs#L35

Added line #L35 was not covered by tests
pub chunk_size: u64,

#[arg(long)]
Expand All @@ -43,47 +52,325 @@

impl EventsArgs {
pub fn run(self, config: &Config) -> Result<()> {
let env_metadata = utils::load_metadata_from_config(config)?;
trace!(?env_metadata, "Metadata loaded from config.");

let ws = scarb::ops::read_workspace(config.manifest_path(), config)?;
trace!(ws_members_count = ws.members().count(), "Read workspace.");

let project_dir = ws.manifest_path().parent().unwrap().to_path_buf();
trace!(?project_dir, "Project directory defined from workspace.");

let provider = self.starknet.provider(env_metadata.as_ref())?;
trace!(?provider, "Starknet RPC client provider.");

let world_address = self.world.address(env_metadata.as_ref())?;
let event_filter = events::get_event_filter(
self.from_block,
self.to_block,
self.events,
Some(world_address),
);
trace!(
from_block = self.from_block,
to_block = self.to_block,
chunk_size = self.chunk_size,
"Created event filter."
);
let profile_name =
ws.current_profile().expect("Scarb profile expected at this point.").to_string();
trace!(profile_name, "Current profile.");

config.tokio_handle().block_on(async {
trace!("Starting async event parsing.");
events::parse(
self.chunk_size,
provider,
self.continuation_token,
event_filter,
self.json,
&project_dir,
&profile_name,
)
.await
let ws = scarb::ops::read_workspace(config.manifest_path(), config)?;

Check warning on line 56 in bin/sozo/src/commands/events.rs

View check run for this annotation

Codecov / codecov/patch

bin/sozo/src/commands/events.rs#L56

Added line #L56 was not covered by tests

Comment on lines +56 to +57
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Ensure proper error handling when loading the workspace.

In let ws = scarb::ops::read_workspace(config.manifest_path(), config)?;, if the workspace fails to load, the error might not provide sufficient context. Enhance error messages to aid in troubleshooting.

Would you like assistance in improving the error handling for workspace loading?

let (world_diff, provider, _) =
utils::get_world_diff_and_provider(self.starknet, self.world, &ws).await?;

Check warning on line 59 in bin/sozo/src/commands/events.rs

View check run for this annotation

Codecov / codecov/patch

bin/sozo/src/commands/events.rs#L58-L59

Added lines #L58 - L59 were not covered by tests

let provider = Arc::new(provider);

let from_block = self.from_block.map(BlockId::Number);
let to_block = self.to_block.map(BlockId::Number);
let keys = self
.events
.map(|e| vec![e.iter().map(|event| starknet_keccak(event.as_bytes())).collect()]);
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Handle empty event lists gracefully.

When mapping over self.events, if the list is empty, the event filter may not behave as expected. Consider adding a check to handle empty event lists to prevent potential issues.


let event_filter = EventFilter {
from_block,
to_block,
address: Some(world_diff.world_info.address),
keys,
};

Check warning on line 74 in bin/sozo/src/commands/events.rs

View check run for this annotation

Codecov / codecov/patch

bin/sozo/src/commands/events.rs#L61-L74

Added lines #L61 - L74 were not covered by tests

let res =
provider.get_events(event_filter, self.continuation_token, self.chunk_size).await?;

Check warning on line 77 in bin/sozo/src/commands/events.rs

View check run for this annotation

Codecov / codecov/patch

bin/sozo/src/commands/events.rs#L76-L77

Added lines #L76 - L77 were not covered by tests

for event in &res.events {
match world::Event::try_from(event) {
Ok(ev) => {
match_event(
&ev,
&world_diff,
event.block_number,
event.transaction_hash,
&provider,
)
.await
.unwrap_or_else(|e| {
tracing::error!(?e, "Failed to process event: {:?}", ev);
});
}
Err(e) => {
tracing::error!(

Check warning on line 95 in bin/sozo/src/commands/events.rs

View check run for this annotation

Codecov / codecov/patch

bin/sozo/src/commands/events.rs#L79-L95

Added lines #L79 - L95 were not covered by tests
?e,
"Failed to parse remote world event which is supposed to be valid."

Check warning on line 97 in bin/sozo/src/commands/events.rs

View check run for this annotation

Codecov / codecov/patch

bin/sozo/src/commands/events.rs#L97

Added line #L97 was not covered by tests
);
}
}
}

if let Some(continuation_token) = res.continuation_token {
println!("Continuation token: {:?}", continuation_token);
println!("----------------------------------------------");
}

Check warning on line 106 in bin/sozo/src/commands/events.rs

View check run for this annotation

Codecov / codecov/patch

bin/sozo/src/commands/events.rs#L103-L106

Added lines #L103 - L106 were not covered by tests

Ok(())

Check warning on line 108 in bin/sozo/src/commands/events.rs

View check run for this annotation

Codecov / codecov/patch

bin/sozo/src/commands/events.rs#L108

Added line #L108 was not covered by tests
})
}
}

/// Matches the event and prints it's content.
async fn match_event<P: Provider + Send + Sync>(
event: &WorldEvent,
world_diff: &WorldDiff,
block_number: Option<u64>,
transaction_hash: Felt,
provider: P,
) -> Result<()> {
// Get a mapping of all the known selectors and their addresses.
let contract_addresses_from_selector = world_diff.get_contracts_addresses();
// Do a reverse mapping to retrieve a contract selector from it's address.
let contract_selectors_from_address: HashMap<Felt, Felt> =
contract_addresses_from_selector.into_iter().map(|(s, a)| (a, s)).collect();
// Finally, cache all the known tags by creating them once for each selector.
let mut tags = HashMap::new();
for (s, r) in world_diff.resources.iter() {
tags.insert(s, r.tag());
}

Check warning on line 130 in bin/sozo/src/commands/events.rs

View check run for this annotation

Codecov / codecov/patch

bin/sozo/src/commands/events.rs#L114-L130

Added lines #L114 - L130 were not covered by tests

let block_id = if let Some(block_number) = block_number {
BlockId::Number(block_number)

Check warning on line 133 in bin/sozo/src/commands/events.rs

View check run for this annotation

Codecov / codecov/patch

bin/sozo/src/commands/events.rs#L132-L133

Added lines #L132 - L133 were not covered by tests
} else {
BlockId::Tag(BlockTag::Pending)

Check warning on line 135 in bin/sozo/src/commands/events.rs

View check run for this annotation

Codecov / codecov/patch

bin/sozo/src/commands/events.rs#L135

Added line #L135 was not covered by tests
};

let (name, content) = match event {
WorldEvent::WorldSpawned(e) => (
"World spawned".to_string(),
format!("Creator address: {:?}\nWorld class hash: {:#066x}", e.creator, e.class_hash.0),
),
WorldEvent::WorldUpgraded(e) => {
("World upgraded".to_string(), format!("World class hash: {:#066x}", e.class_hash.0))

Check warning on line 144 in bin/sozo/src/commands/events.rs

View check run for this annotation

Codecov / codecov/patch

bin/sozo/src/commands/events.rs#L138-L144

Added lines #L138 - L144 were not covered by tests
}
WorldEvent::NamespaceRegistered(e) => {
("Namespace registered".to_string(), format!("Namespace: {}", e.namespace.to_string()?))

Check warning on line 147 in bin/sozo/src/commands/events.rs

View check run for this annotation

Codecov / codecov/patch

bin/sozo/src/commands/events.rs#L146-L147

Added lines #L146 - L147 were not covered by tests
}
WorldEvent::ModelRegistered(e) => (
"Model registered".to_string(),
format!(
"Namespace: {}\nName: {}\nClass hash: {:#066x}\nAddress: {:#066x}",
e.namespace.to_string()?,
e.name.to_string()?,

Check warning on line 154 in bin/sozo/src/commands/events.rs

View check run for this annotation

Codecov / codecov/patch

bin/sozo/src/commands/events.rs#L149-L154

Added lines #L149 - L154 were not covered by tests
e.class_hash.0,
e.address.0
),
),
WorldEvent::EventRegistered(e) => (
"Event registered".to_string(),
format!(
"Namespace: {}\nName: {}\nClass hash: {:#066x}\nAddress: {:#066x}",
e.namespace.to_string()?,
e.name.to_string()?,

Check warning on line 164 in bin/sozo/src/commands/events.rs

View check run for this annotation

Codecov / codecov/patch

bin/sozo/src/commands/events.rs#L159-L164

Added lines #L159 - L164 were not covered by tests
e.class_hash.0,
e.address.0
),
),
WorldEvent::ContractRegistered(e) => (
"Contract registered".to_string(),
format!(
"Namespace: {}\nName: {}\nClass hash: {:#066x}\nAddress: {:#066x}\nSalt: {:#066x}",
e.namespace.to_string()?,
e.name.to_string()?,

Check warning on line 174 in bin/sozo/src/commands/events.rs

View check run for this annotation

Codecov / codecov/patch

bin/sozo/src/commands/events.rs#L169-L174

Added lines #L169 - L174 were not covered by tests
e.class_hash.0,
e.address.0,
e.salt
),
),
WorldEvent::ModelUpgraded(e) => {
let tag = tags.get(&e.selector).unwrap();
(
format!("Model upgraded ({})", tag),
format!(
"Selector: {:#066x}\nClass hash: {:#066x}\nAddress: {:#066x}\nPrev address: \
{:#066x}",
e.selector, e.class_hash.0, e.address.0, e.prev_address.0
),
)

Check warning on line 189 in bin/sozo/src/commands/events.rs

View check run for this annotation

Codecov / codecov/patch

bin/sozo/src/commands/events.rs#L180-L189

Added lines #L180 - L189 were not covered by tests
}
WorldEvent::EventUpgraded(e) => {
let tag = tags.get(&e.selector).unwrap();
(
format!("Event upgraded ({})", tag),
format!(
"Selector: {:#066x}\nClass hash: {:#066x}\nAddress: {:#066x}\nPrev address: \
{:#066x}",
e.selector, e.class_hash.0, e.address.0, e.prev_address.0
),
)

Check warning on line 200 in bin/sozo/src/commands/events.rs

View check run for this annotation

Codecov / codecov/patch

bin/sozo/src/commands/events.rs#L191-L200

Added lines #L191 - L200 were not covered by tests
}
WorldEvent::ContractUpgraded(e) => {
let tag = tags.get(&e.selector).unwrap();
(
format!("Contract upgraded ({})", tag),
format!("Selector: {:#066x}\nClass hash: {:#066x}", e.selector, e.class_hash.0,),
)

Check warning on line 207 in bin/sozo/src/commands/events.rs

View check run for this annotation

Codecov / codecov/patch

bin/sozo/src/commands/events.rs#L202-L207

Added lines #L202 - L207 were not covered by tests
}
WorldEvent::ContractInitialized(e) => {
let tag = tags.get(&e.selector).unwrap();
(
format!("Contract initialized ({})", tag),
format!(
"Selector: {:#066x}\nInit calldata: {}",
e.selector,
e.init_calldata
.iter()
.map(|f| format!("{:#066x}", f))
.collect::<Vec<String>>()
.join(", ")
),
)

Check warning on line 222 in bin/sozo/src/commands/events.rs

View check run for this annotation

Codecov / codecov/patch

bin/sozo/src/commands/events.rs#L209-L222

Added lines #L209 - L222 were not covered by tests
}
WorldEvent::WriterUpdated(e) => {
let tag = tags.get(&e.resource).unwrap();
let grantee =
if let Some(selector) = contract_selectors_from_address.get(&e.contract.into()) {
tags.get(selector).unwrap().to_string()

Check warning on line 228 in bin/sozo/src/commands/events.rs

View check run for this annotation

Codecov / codecov/patch

bin/sozo/src/commands/events.rs#L224-L228

Added lines #L224 - L228 were not covered by tests
} else {
format!("{:#066x}", e.contract.0)

Check warning on line 230 in bin/sozo/src/commands/events.rs

View check run for this annotation

Codecov / codecov/patch

bin/sozo/src/commands/events.rs#L230

Added line #L230 was not covered by tests
};

(
"Writer updated".to_string(),
format!("Target resource: {}\nContract: {}\nValue: {}", tag, grantee, e.value),
)

Check warning on line 236 in bin/sozo/src/commands/events.rs

View check run for this annotation

Codecov / codecov/patch

bin/sozo/src/commands/events.rs#L233-L236

Added lines #L233 - L236 were not covered by tests
}
WorldEvent::OwnerUpdated(e) => {
let tag = tags.get(&e.resource).unwrap();
let grantee =
if let Some(selector) = contract_selectors_from_address.get(&e.contract.into()) {
tags.get(selector).unwrap().to_string()

Check warning on line 242 in bin/sozo/src/commands/events.rs

View check run for this annotation

Codecov / codecov/patch

bin/sozo/src/commands/events.rs#L238-L242

Added lines #L238 - L242 were not covered by tests
} else {
format!("{:#066x}", e.contract.0)

Check warning on line 244 in bin/sozo/src/commands/events.rs

View check run for this annotation

Codecov / codecov/patch

bin/sozo/src/commands/events.rs#L244

Added line #L244 was not covered by tests
};
Comment on lines +225 to +245
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Ohayo, sensei! Ensure safe unwrapping of options in WriterUpdated and OwnerUpdated events.

In lines where tags.get(&e.resource).unwrap() and tags.get(selector).unwrap() are called, consider handling the None case to prevent potential panics.

Apply this diff to handle possible None values:

-let tag = tags.get(&e.resource).unwrap();
+let tag = match tags.get(&e.resource) {
+    Some(t) => t,
+    None => {
+        tracing::warn!("Tag not found for resource: {:#066x}", e.resource);
+        return Ok(());
+    }
+};

Committable suggestion skipped: line range outside the PR's diff.


(
"Owner updated".to_string(),
format!("Target resource: {}\nContract: {}\nValue: {}", tag, grantee, e.value),
)

Check warning on line 250 in bin/sozo/src/commands/events.rs

View check run for this annotation

Codecov / codecov/patch

bin/sozo/src/commands/events.rs#L247-L250

Added lines #L247 - L250 were not covered by tests
}
WorldEvent::StoreSetRecord(e) => {
let tag = tags.get(&e.selector).unwrap();
let (record, _, _) = model::model_get(
tag.clone(),
e.keys.clone(),
world_diff.world_info.address,
provider,
block_id,
)
.await?;

Check warning on line 261 in bin/sozo/src/commands/events.rs

View check run for this annotation

Codecov / codecov/patch

bin/sozo/src/commands/events.rs#L252-L261

Added lines #L252 - L261 were not covered by tests

(
format!("Store set record ({})", tag),
format!(
"Selector: {:#066x}\nEntity ID: {:#066x}\nKeys: {}\nValues: {}\nData:\n{}",
e.selector,
e.entity_id,
e.keys
.iter()
.map(|k| format!("{:#066x}", k))
.collect::<Vec<String>>()
.join(", "),
e.values
.iter()
.map(|v| format!("{:#066x}", v))
.collect::<Vec<String>>()
.join(", "),
record
),
)

Check warning on line 281 in bin/sozo/src/commands/events.rs

View check run for this annotation

Codecov / codecov/patch

bin/sozo/src/commands/events.rs#L263-L281

Added lines #L263 - L281 were not covered by tests
}
Comment on lines +253 to +282
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Handle potential errors in StoreSetRecord event processing.

In the StoreSetRecord event handling, ensure that the call to model::model_get handles errors appropriately, possibly with informative messages if the model retrieval fails.

WorldEvent::StoreUpdateRecord(e) => {
let tag = tags.get(&e.selector).unwrap();
// TODO: model value impl + print.
(
format!("Store update record ({})", tag),
format!(
"Selector: {:#066x}\nEntity ID: {:#066x}\nValues: {}",
e.selector,
e.entity_id,
e.values
.iter()
.map(|v| format!("{:#066x}", v))
.collect::<Vec<String>>()
.join(", "),
),
)

Check warning on line 298 in bin/sozo/src/commands/events.rs

View check run for this annotation

Codecov / codecov/patch

bin/sozo/src/commands/events.rs#L283-L298

Added lines #L283 - L298 were not covered by tests
}
WorldEvent::StoreUpdateMember(e) => {
let tag = tags.get(&e.selector).unwrap();
// TODO: pretty print of the value.
(
format!("Store update member ({})", tag),
format!(
"Selector: {:#066x}\nEntity ID: {:#066x}\nMember selector: {:#066x}\nValues: \
{}",
e.selector,
e.entity_id,
e.member_selector,
e.values
.iter()
.map(|v| format!("{:#066x}", v))
.collect::<Vec<String>>()
.join(", "),
),
)

Check warning on line 317 in bin/sozo/src/commands/events.rs

View check run for this annotation

Codecov / codecov/patch

bin/sozo/src/commands/events.rs#L300-L317

Added lines #L300 - L317 were not covered by tests
}
WorldEvent::StoreDelRecord(e) => {
let tag = tags.get(&e.selector).unwrap();
(
format!("Store del record ({})", tag),
format!("Selector: {:#066x}\nEntity ID: {:#066x}", e.selector, e.entity_id,),
)

Check warning on line 324 in bin/sozo/src/commands/events.rs

View check run for this annotation

Codecov / codecov/patch

bin/sozo/src/commands/events.rs#L319-L324

Added lines #L319 - L324 were not covered by tests
}
WorldEvent::EventEmitted(e) => {
let tag = tags.get(&e.selector).unwrap();
let contract_tag = if let Some(selector) =
contract_selectors_from_address.get(&e.system_address.into())

Check warning on line 329 in bin/sozo/src/commands/events.rs

View check run for this annotation

Codecov / codecov/patch

bin/sozo/src/commands/events.rs#L326-L329

Added lines #L326 - L329 were not covered by tests
{
tags.get(selector).unwrap().to_string()

Check warning on line 331 in bin/sozo/src/commands/events.rs

View check run for this annotation

Codecov / codecov/patch

bin/sozo/src/commands/events.rs#L331

Added line #L331 was not covered by tests
} else {
format!("{:#066x}", e.system_address.0)

Check warning on line 333 in bin/sozo/src/commands/events.rs

View check run for this annotation

Codecov / codecov/patch

bin/sozo/src/commands/events.rs#L333

Added line #L333 was not covered by tests
};

let (record, _, _) = model::model_get(
tag.clone(),
e.keys.clone(),
world_diff.world_info.address,
provider,
block_id,
)
.await?;

Check warning on line 343 in bin/sozo/src/commands/events.rs

View check run for this annotation

Codecov / codecov/patch

bin/sozo/src/commands/events.rs#L336-L343

Added lines #L336 - L343 were not covered by tests

(
format!("Event emitted ({})", tag),
format!(
"Selector: {:#066x}\nContract: {}\nHistorical: {}\nKeys: {}\nValues: \
{}\nData:\n{}",
e.selector,
contract_tag,
e.historical,
e.keys
.iter()
.map(|k| format!("{:#066x}", k))
.collect::<Vec<String>>()
.join(", "),
e.values
.iter()
.map(|v| format!("{:#066x}", v))
.collect::<Vec<String>>()
.join(", "),
record
),
)

Check warning on line 365 in bin/sozo/src/commands/events.rs

View check run for this annotation

Codecov / codecov/patch

bin/sozo/src/commands/events.rs#L345-L365

Added lines #L345 - L365 were not covered by tests
}
_ => ("Unprocessed event".to_string(), format!("Event: {:?}", event)),

Check warning on line 367 in bin/sozo/src/commands/events.rs

View check run for this annotation

Codecov / codecov/patch

bin/sozo/src/commands/events.rs#L367

Added line #L367 was not covered by tests
};

let block_str = block_number.map(|n| n.to_string()).unwrap_or("pending".to_string());
let ptr = format!("[block:{} / tx:{:#066x}]", block_str, transaction_hash).bright_black();

println!("> {name} {ptr}\n{content}\n-----\n");

Ok(())
}

Check warning on line 376 in bin/sozo/src/commands/events.rs

View check run for this annotation

Codecov / codecov/patch

bin/sozo/src/commands/events.rs#L370-L376

Added lines #L370 - L376 were not covered by tests
Comment on lines +113 to +376
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Refactor match_event for better readability and maintainability.

The match_event function is quite extensive, handling numerous event types in a single block. Breaking it down into smaller helper functions or modules could improve readability and make maintenance easier.

For example, you could extract each event type handling into its own function:

async fn handle_world_spawned_event(...) { ... }
async fn handle_world_upgraded_event(...) { ... }
// And so on for each event type.

Loading
Loading