Skip to content

Commit

Permalink
fix(sozo): fix profile detection and UI rework (#2606)
Browse files Browse the repository at this point in the history
* fix: ensures the banner chain_id printing takes in account the profile

* fix: rework MigrationUi for something easier to use

* fix: change the seed
  • Loading branch information
glihm authored Nov 1, 2024
1 parent e553774 commit b93f761
Show file tree
Hide file tree
Showing 13 changed files with 2,165 additions and 66 deletions.
4 changes: 4 additions & 0 deletions bin/sozo/src/commands/execute.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ use dojo_types::naming;
use dojo_utils::Invoker;
use dojo_world::contracts::naming::ensure_namespace;
use scarb::core::Config;
use sozo_ops::migration_ui::MigrationUi;
use sozo_scarbext::WorkspaceExt;
use sozo_walnut::WalnutDebugger;
use starknet::core::types::{Call, Felt};
Expand Down Expand Up @@ -79,11 +80,14 @@ impl ExecuteArgs {
);

config.tokio_handle().block_on(async {
let mut spinner = MigrationUi::new("").with_silent();

let (world_diff, account, _) = utils::get_world_diff_and_account(
self.account,
self.starknet.clone(),
self.world,
&ws,
&mut spinner,
)
.await?;

Expand Down
18 changes: 8 additions & 10 deletions bin/sozo/src/commands/migrate.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,9 @@ use colored::Colorize;
use dojo_utils::{self, TxnConfig};
use dojo_world::contracts::WorldContract;
use scarb::core::{Config, Workspace};
use sozo_ops::migrate::{Migration, MigrationResult, MigrationUi};
use sozo_ops::migrate::{Migration, MigrationResult};
use sozo_ops::migration_ui::MigrationUi;
use sozo_scarbext::WorkspaceExt;
use spinoff::{spinner, spinners, Spinner};
use starknet::core::utils::parse_cairo_short_string;
use starknet::providers::Provider;
use tabled::settings::Style;
Expand Down Expand Up @@ -45,20 +45,17 @@ impl MigrateArgs {

let MigrateArgs { world, starknet, account, .. } = self;

let frames = spinner!(["⛩️ ", "🎃", "👻", "🧟", "💀"], 500);
// let frames = spinner!(["⛩️ ", "🥷 ", "🗡️ "], 500);

config.tokio_handle().block_on(async {
print_banner(&ws, &starknet).await?;

let mut spinner =
MigrationUi::Spinner(Spinner::new(frames, "Evaluating world diff...", None));
let mut spinner = MigrationUi::new("Evaluating world diff...");

let mut txn_config: TxnConfig = self.transaction.into();
txn_config.wait = true;

let (world_diff, account, rpc_url) =
utils::get_world_diff_and_account(account, starknet, world, &ws).await?;
utils::get_world_diff_and_account(account, starknet, world, &ws, &mut spinner)
.await?;

let world_address = world_diff.world_info.address;

Expand All @@ -84,7 +81,7 @@ impl MigrateArgs {
("🎃", format!("No changes for world at address {:#066x}", world_address))
};

spinner.stop_and_persist(symbol, Box::leak(end_text.into_boxed_str()));
spinner.stop_and_persist_boxed(symbol, end_text);

Ok(())
})
Expand All @@ -100,7 +97,8 @@ pub struct Banner {

/// Prints the migration banner.
async fn print_banner(ws: &Workspace<'_>, starknet: &StarknetOptions) -> Result<()> {
let (provider, rpc_url) = starknet.provider(None)?;
let profile_config = ws.load_profile_config()?;
let (provider, rpc_url) = starknet.provider(profile_config.env.as_ref())?;

let chain_id = provider.chain_id().await?;
let chain_id = parse_cairo_short_string(&chain_id)
Expand Down
2 changes: 1 addition & 1 deletion bin/sozo/src/commands/options/account/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -106,8 +106,8 @@ impl AccountOptions {
let account_address = self.account_address(env_metadata)?;

let signer = self.signer.signer(env_metadata, false)?;
trace!(?signer, "Signer obtained.");

trace!("Fetching chain id...");
let chain_id = provider.chain_id().await?;
trace!(?chain_id);

Expand Down
7 changes: 7 additions & 0 deletions bin/sozo/src/utils.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ use dojo_world::local::WorldLocal;
use katana_rpc_api::starknet::RPC_SPEC_VERSION;
use scarb::core::{TomlManifest, Workspace};
use semver::Version;
use sozo_ops::migration_ui::MigrationUi;
use sozo_scarbext::WorkspaceExt;
use starknet::accounts::{Account, ConnectedAccount};
use starknet::core::types::Felt;
Expand Down Expand Up @@ -144,19 +145,25 @@ pub async fn get_world_diff_and_account(
starknet: StarknetOptions,
world: WorldOptions,
ws: &Workspace<'_>,
ui: &mut MigrationUi,
) -> Result<(WorldDiff, SozoAccount<JsonRpcClient<HttpTransport>>, String)> {
let profile_config = ws.load_profile_config()?;
let env = profile_config.env.as_ref();

let (world_diff, provider, rpc_url) =
get_world_diff_and_provider(starknet.clone(), world, ws).await?;

// Ensures we don't interfere with the spinner if a password must be prompted.
ui.stop();

let account = {
account
.account(provider, world_diff.world_info.address, &starknet, env, &world_diff)
.await?
};

ui.restart("Verifying account...");

if !dojo_utils::is_deployed(account.address(), &account.provider()).await? {
return Err(anyhow!("Account with address {:#x} doesn't exist.", account.address()));
}
Expand Down
2 changes: 1 addition & 1 deletion crates/dojo/utils/src/keystore.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,6 @@ pub fn prompt_password_if_needed(maybe_password: Option<&str>, no_wait: bool) ->
} else if no_wait {
Err(anyhow!("Could not find password. Please specify the password."))
} else {
Ok(rpassword::prompt_password("Enter password: ")?.to_owned())
Ok(rpassword::prompt_password("Enter the keystore password: ")?.to_owned())
}
}
5 changes: 5 additions & 0 deletions crates/dojo/world/src/local/artifact_to_local.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,11 @@ const EVENT_INTF: &str = "dojo::event::interface::IEvent";

impl WorldLocal {
pub fn from_directory<P: AsRef<Path>>(dir: P, profile_config: ProfileConfig) -> Result<Self> {
trace!(
?profile_config,
directory = %dir.as_ref().to_string_lossy(),
"Loading world from directory."
);
let mut resources = vec![];

let mut world_class = None;
Expand Down
1 change: 1 addition & 0 deletions crates/sozo/ops/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

pub mod account;
pub mod migrate;
pub mod migration_ui;

#[cfg(test)]
pub mod tests;
117 changes: 66 additions & 51 deletions crates/sozo/ops/src/migrate/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,6 @@
//! initialization of contracts can mutate resources.

use std::collections::HashMap;
use std::fmt;
use std::str::FromStr;

use cainome::cairo_serde::{ByteArray, ClassHash, ContractAddress};
Expand All @@ -30,14 +29,15 @@ use dojo_world::diff::{Manifest, ResourceDiff, WorldDiff, WorldStatus};
use dojo_world::local::ResourceLocal;
use dojo_world::remote::ResourceRemote;
use dojo_world::{utils, ResourceType};
use spinoff::Spinner;
use starknet::accounts::{ConnectedAccount, SingleOwnerAccount};
use starknet::core::types::{Call, FlattenedSierraClass};
use starknet::providers::AnyProvider;
use starknet::signers::LocalWallet;
use starknet_crypto::Felt;
use tracing::trace;

use crate::migration_ui::MigrationUi;

pub mod error;
pub use error::MigrationError;

Expand All @@ -61,36 +61,6 @@ pub struct MigrationResult {
pub manifest: Manifest,
}

pub enum MigrationUi {
Spinner(Spinner),
None,
}

impl fmt::Debug for MigrationUi {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::Spinner(_) => write!(f, "Spinner"),
Self::None => write!(f, "None"),
}
}
}

impl MigrationUi {
pub fn update_text(&mut self, text: &'static str) {
match self {
Self::Spinner(s) => s.update_text(text),
Self::None => (),
}
}

pub fn stop_and_persist(&mut self, symbol: &'static str, text: &'static str) {
match self {
Self::Spinner(s) => s.stop_and_persist(symbol, text),
Self::None => (),
}
}
}

impl<A> Migration<A>
where
A: ConnectedAccount + Sync + Send,
Expand All @@ -113,23 +83,17 @@ where
/// spinner.
pub async fn migrate(
&self,
spinner: &mut MigrationUi,
ui: &mut MigrationUi,
) -> Result<MigrationResult, MigrationError<A::SignError>> {
spinner.update_text("Deploying world...");
ui.update_text("Deploying world...");
let world_has_changed = self.ensure_world().await?;

let resources_have_changed = if !self.diff.is_synced() {
spinner.update_text("Syncing resources...");
self.sync_resources().await?
} else {
false
};
let resources_have_changed =
if !self.diff.is_synced() { self.sync_resources(ui).await? } else { false };

spinner.update_text("Syncing permissions...");
let permissions_have_changed = self.sync_permissions().await?;
let permissions_have_changed = self.sync_permissions(ui).await?;

spinner.update_text("Initializing contracts...");
let contracts_have_changed = self.initialize_contracts().await?;
let contracts_have_changed = self.initialize_contracts(ui).await?;

Ok(MigrationResult {
has_changes: world_has_changed
Expand All @@ -152,7 +116,12 @@ where
/// found in the [`ProfileConfig`].
///
/// Returns true if at least one contract has been initialized, false otherwise.
async fn initialize_contracts(&self) -> Result<bool, MigrationError<A::SignError>> {
async fn initialize_contracts(
&self,
ui: &mut MigrationUi,
) -> Result<bool, MigrationError<A::SignError>> {
ui.update_text("Initializing contracts...");

let mut invoker = Invoker::new(&self.world.account, self.txn_config);

let init_call_args = if let Some(init_call_args) = &self.profile_config.init_call_args {
Expand Down Expand Up @@ -235,10 +204,21 @@ where

let has_changed = !invoker.calls.is_empty();

if self.do_multicall() {
invoker.multicall().await?;
} else {
invoker.invoke_all_sequentially().await?;
if !ordered_init_calls.is_empty() {
if self.do_multicall() {
let ui_text = format!("Initializing {} contracts...", ordered_init_calls.len());
ui.update_text_boxed(ui_text);

invoker.multicall().await?;
} else {
let ui_text = format!(
"Initializing {} contracts (sequentially)...",
ordered_init_calls.len()
);
ui.update_text_boxed(ui_text);

invoker.invoke_all_sequentially().await?;
}
}

Ok(has_changed)
Expand All @@ -257,7 +237,12 @@ where
/// overlay resource, which can contain also writers.
///
/// Returns true if at least one permission has changed, false otherwise.
async fn sync_permissions(&self) -> Result<bool, MigrationError<A::SignError>> {
async fn sync_permissions(
&self,
ui: &mut MigrationUi,
) -> Result<bool, MigrationError<A::SignError>> {
ui.update_text("Syncing permissions...");

let mut invoker = Invoker::new(&self.world.account, self.txn_config);

// Only takes the local permissions that are not already set onchain to apply them.
Expand Down Expand Up @@ -296,8 +281,14 @@ where
let has_changed = !invoker.calls.is_empty();

if self.do_multicall() {
let ui_text = format!("Syncing {} permissions...", invoker.calls.len());
ui.update_text_boxed(ui_text);

invoker.multicall().await?;
} else {
let ui_text = format!("Syncing {} permissions (sequentially)...", invoker.calls.len());
ui.update_text_boxed(ui_text);

invoker.invoke_all_sequentially().await?;
}

Expand All @@ -307,13 +298,19 @@ where
/// Syncs the resources by declaring the classes and registering/upgrading the resources.
///
/// Returns true if at least one resource has changed, false otherwise.
async fn sync_resources(&self) -> Result<bool, MigrationError<A::SignError>> {
async fn sync_resources(
&self,
ui: &mut MigrationUi,
) -> Result<bool, MigrationError<A::SignError>> {
ui.update_text("Syncing resources...");

let mut invoker = Invoker::new(&self.world.account, self.txn_config);

// Namespaces must be synced first, since contracts, models and events are namespaced.
self.namespaces_getcalls(&mut invoker).await?;

let mut classes: HashMap<Felt, FlattenedSierraClass> = HashMap::new();
let mut n_resources = 0;

// Collects the calls and classes to be declared to sync the resources.
for resource in self.diff.resources.values() {
Expand All @@ -327,16 +324,19 @@ where
self.contracts_calls_classes(resource).await?;
invoker.extend_calls(contract_calls);
classes.extend(contract_classes);
n_resources += 1;
}
ResourceType::Model => {
let (model_calls, model_classes) = self.models_calls_classes(resource).await?;
invoker.extend_calls(model_calls);
classes.extend(model_classes);
n_resources += 1;
}
ResourceType::Event => {
let (event_calls, event_classes) = self.events_calls_classes(resource).await?;
invoker.extend_calls(event_calls);
classes.extend(event_classes);
n_resources += 1;
}
_ => continue,
}
Expand All @@ -350,11 +350,16 @@ where
// Since migrator account from `self.world.account` is under the [`ConnectedAccount`] trait,
// we can group it with the predeployed accounts which are concrete types.
let accounts = self.get_accounts().await;
let n_classes = classes.len();

if accounts.is_empty() {
trace!("Declaring classes with migrator account.");
let mut declarer = Declarer::new(&self.world.account, self.txn_config);
declarer.extend_classes(classes.into_iter().collect());

let ui_text = format!("Declaring {} classes...", n_classes);
ui.update_text_boxed(ui_text);

declarer.declare_all().await?;
} else {
trace!("Declaring classes with {} accounts.", accounts.len());
Expand All @@ -368,6 +373,10 @@ where
declarers[declarer_idx].add_class(casm_class_hash, class);
}

let ui_text =
format!("Declaring {} classes with {} accounts...", n_classes, declarers.len());
ui.update_text_boxed(ui_text);

let declarers_futures =
futures::future::join_all(declarers.into_iter().map(|d| d.declare_all())).await;

Expand All @@ -388,8 +397,14 @@ where
}

if self.do_multicall() {
let ui_text = format!("Registering {} resources...", n_resources);
ui.update_text_boxed(ui_text);

invoker.multicall().await?;
} else {
let ui_text = format!("Registering {} resources (sequentially)...", n_resources);
ui.update_text_boxed(ui_text);

invoker.invoke_all_sequentially().await?;
}

Expand Down
Loading

0 comments on commit b93f761

Please sign in to comment.