diff --git a/Cargo.lock b/Cargo.lock index 85f26a42cdf..3f62b4073b0 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2324,17 +2324,6 @@ dependencies = [ "memchr", ] -[[package]] -name = "comfy-table" -version = "7.1.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4a65ebfec4fb190b6f90e944a817d60499ee0744e582530e2c9900a22e591d9a" -dependencies = [ - "crossterm", - "unicode-segmentation", - "unicode-width 0.2.1", -] - [[package]] name = "concurrent-queue" version = "2.5.0" @@ -2797,28 +2786,6 @@ version = "0.8.21" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d0a5c400df2834b80a4c3327b3aad3a4c4cd4de0629063962b03235697506a28" -[[package]] -name = "crossterm" -version = "0.28.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "829d955a0bb380ef178a640b91779e3987da38c9aea133b20614cfed8cdea9c6" -dependencies = [ - "bitflags 2.9.1", - "crossterm_winapi", - "parking_lot", - "rustix 0.38.44", - "winapi", -] - -[[package]] -name = "crossterm_winapi" -version = "0.9.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "acdd7c62a3665c7f6830a51635d9ac9b23ed385797f70a83bb8bafe9c572ab2b" -dependencies = [ - "winapi", -] - [[package]] name = "crowd-funding" version = "0.1.0" @@ -5770,7 +5737,6 @@ dependencies = [ "clap", "clap-markdown", "colored", - "comfy-table", "convert_case", "counter", "counter-no-graphql", @@ -10867,12 +10833,6 @@ version = "0.1.14" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7dd6e30e90baa6f72411720665d41d89b9a3d039dc45b8faea1ddd07f617f6af" -[[package]] -name = "unicode-width" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4a1a07cc7db3810833284e8d372ccdc6da29741639ecc70c9ec107df0fa6154c" - [[package]] name = "unicode-xid" version = "0.2.6" @@ -11475,7 +11435,7 @@ dependencies = [ "bumpalo", "leb128", "memchr", - "unicode-width 0.1.14", + "unicode-width", "wasm-encoder 0.217.1", ] diff --git a/Cargo.toml b/Cargo.toml index 21131d737eb..548e57b3243 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -100,7 +100,6 @@ chrono = { version = "0.4.35", default-features = false } clap = { version = "4", features = ["cargo", "derive", "env"] } clap-markdown = "0.1.3" colored = "2.1.0" -comfy-table = "7.1.0" console_error_panic_hook = "0.1.7" convert_case = "0.6.0" criterion = { version = "0.5.1", default-features = false } diff --git a/linera-base/src/data_types.rs b/linera-base/src/data_types.rs index fd648508b4c..afeb890dfe3 100644 --- a/linera-base/src/data_types.rs +++ b/linera-base/src/data_types.rs @@ -724,6 +724,14 @@ impl ChainOrigin { pub fn is_child(&self) -> bool { matches!(self, ChainOrigin::Child { .. }) } + + /// Returns the root chain number, if this is a root chain. + pub fn root(&self) -> Option { + match self { + ChainOrigin::Root(i) => Some(*i), + ChainOrigin::Child { .. } => None, + } + } } /// A number identifying the configuration of the chain (aka the committee). diff --git a/linera-service/Cargo.toml b/linera-service/Cargo.toml index 1c3d714209a..cf826809bf7 100644 --- a/linera-service/Cargo.toml +++ b/linera-service/Cargo.toml @@ -80,7 +80,6 @@ chrono = { workspace = true, features = ["clock"] } clap.workspace = true clap-markdown.workspace = true colored.workspace = true -comfy-table.workspace = true convert_case.workspace = true current_platform = "0.2.0" custom_debug_derive.workspace = true diff --git a/linera-service/src/cli/main.rs b/linera-service/src/cli/main.rs index 0bbbcc3a6f5..cec47c6a030 100644 --- a/linera-service/src/cli/main.rs +++ b/linera-service/src/cli/main.rs @@ -2576,7 +2576,8 @@ async fn run(options: &ClientOptions) -> Result { short, owned, } => { - let start_time = Instant::now(); + let wallet_path = options.wallet_path()?; + tracing::info!("Reading wallet from file: {}", wallet_path.display()); let wallet = options.wallet()?; let chain_ids = if let Some(chain_id) = chain_id { ensure!(!owned, "Cannot specify both --owned and a chain ID"); @@ -2593,7 +2594,6 @@ async fn run(options: &ClientOptions) -> Result { } else { wallet::pretty_print(&wallet, chain_ids); } - info!("Wallet shown in {} ms", start_time.elapsed().as_millis()); Ok(0) } diff --git a/linera-service/src/wallet.rs b/linera-service/src/wallet.rs index b27cbaf404f..79d43819cc6 100644 --- a/linera-service/src/wallet.rs +++ b/linera-service/src/wallet.rs @@ -1,64 +1,103 @@ // Copyright (c) Zefchain Labs, Inc. // SPDX-License-Identifier: Apache-2.0 -use comfy_table::{ - modifiers::UTF8_ROUND_CORNERS, presets::UTF8_FULL, Attribute, Cell, Color, ContentArrangement, - Table, +use linera_base::{ + data_types::{ChainDescription, ChainOrigin}, + identifiers::ChainId, }; -use linera_base::identifiers::ChainId; pub use linera_client::wallet::*; -pub fn pretty_print(wallet: &Wallet, chain_ids: impl IntoIterator) { - let mut table = Table::new(); - table - .load_preset(UTF8_FULL) - .apply_modifier(UTF8_ROUND_CORNERS) - .set_content_arrangement(ContentArrangement::Dynamic) - .set_header(vec![ - Cell::new("Chain ID").add_attribute(Attribute::Bold), - Cell::new("Latest Block").add_attribute(Attribute::Bold), - ]); - for chain_id in chain_ids { +pub fn pretty_print(wallet: &Wallet, chain_ids: Vec) { + let chain_ids: Vec<_> = chain_ids.into_iter().collect(); + let total_chains = chain_ids.len(); + + let plural_s = if total_chains == 1 { "" } else { "s" }; + tracing::info!("Found {total_chains} chain{plural_s}"); + + let mut chains = chain_ids + .into_iter() + .map(|chain_id| ChainDetails::new(chain_id, wallet)) + .collect::>(); + // Print first the default, then the admin chain, then other root chains, and finally the + // child chains. + chains.sort_unstable_by_key(|chain| { + let root_id = chain + .origin + .and_then(|origin| origin.root()) + .unwrap_or(u32::MAX); + let chain_id = chain.user_chain.chain_id; + (!chain.is_default, !chain.is_admin, root_id, chain_id) + }); + for chain in chains { + chain.print_paragraph(); + } + println!("------------------------"); +} + +struct ChainDetails<'a> { + is_default: bool, + is_admin: bool, + origin: Option, + user_chain: &'a UserChain, +} + +impl<'a> ChainDetails<'a> { + fn new(chain_id: ChainId, wallet: &'a Wallet) -> Self { let Some(user_chain) = wallet.chains.get(&chain_id) else { panic!("Chain {} not found.", chain_id); }; - update_table_with_chain( - &mut table, - chain_id, + ChainDetails { + is_default: Some(chain_id) == wallet.default, + is_admin: chain_id == wallet.genesis_admin_chain(), + origin: wallet + .genesis_config() + .chains + .iter() + .find(|description| description.id() == chain_id) + .map(ChainDescription::origin), user_chain, - Some(chain_id) == wallet.default, - ); + } } - println!("{}", table); -} -fn update_table_with_chain( - table: &mut Table, - chain_id: ChainId, - user_chain: &UserChain, - is_default_chain: bool, -) { - let chain_id_cell = if is_default_chain { - Cell::new(format!("{}", chain_id)).fg(Color::Green) - } else { - Cell::new(format!("{}", chain_id)) - }; - let account_owner = user_chain.owner; - table.add_row(vec![ - chain_id_cell, - Cell::new(format!( - r#"AccountOwner: {} -Block Hash: {} -Timestamp: {} -Next Block Height: {}"#, - account_owner - .as_ref() - .map_or_else(|| "-".to_string(), |o| o.to_string()), - user_chain - .block_hash - .map_or_else(|| "-".to_string(), |bh| bh.to_string()), - user_chain.timestamp, - user_chain.next_block_height - )), - ]); + fn print_paragraph(&self) { + println!("-----------------------"); + println!("{:<20} {}", "Chain ID:", self.user_chain.chain_id); + + let mut tags = Vec::new(); + if self.is_default { + tags.push("DEFAULT"); + } + if self.is_admin { + tags.push("ADMIN"); + } + if !tags.is_empty() { + println!("{:<20} {}", "Tags:", tags.join(", ")); + } + + match self.origin { + Some(ChainOrigin::Root(_)) | None => { + println!("{:<20} -", "Parent chain:"); + } + Some(ChainOrigin::Child { parent, .. }) => { + println!("{:<20} {parent}", "Parent chain:"); + } + } + + if let Some(owner) = &self.user_chain.owner { + println!("{:<20} {owner}", "Default owner:"); + } else { + println!("{:<20} No owner key", "Default owner:"); + } + + println!("{:<20} {}", "Timestamp:", self.user_chain.timestamp); + println!("{:<20} {}", "Blocks:", self.user_chain.next_block_height); + + if let Some(hash) = self.user_chain.block_hash { + println!("{:<20} {hash}", "Latest block hash:"); + } + + if self.user_chain.pending_proposal.is_some() { + println!("{:<20} present", "Pending proposal:"); + } + } }