Skip to content
Draft
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
42 changes: 1 addition & 41 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 0 additions & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -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 }
Expand Down
8 changes: 8 additions & 0 deletions linera-base/src/data_types.rs
Original file line number Diff line number Diff line change
Expand Up @@ -751,6 +751,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<u32> {
match self {
ChainOrigin::Root(i) => Some(*i),
ChainOrigin::Child { .. } => None,
}
}
}

/// A number identifying the configuration of the chain (aka the committee).
Expand Down
1 change: 0 additions & 1 deletion linera-service/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
2 changes: 0 additions & 2 deletions linera-service/src/cli/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2417,7 +2417,6 @@ async fn run(options: &ClientOptions) -> Result<i32, Error> {
short,
owned,
} => {
let start_time = Instant::now();
let wallet = options.wallet()?;
let chain_ids = if let Some(chain_id) = chain_id {
ensure!(!owned, "Cannot specify both --owned and a chain ID");
Expand All @@ -2434,7 +2433,6 @@ async fn run(options: &ClientOptions) -> Result<i32, Error> {
} else {
wallet::pretty_print(&wallet, chain_ids);
}
info!("Wallet shown in {} ms", start_time.elapsed().as_millis());
Ok(0)
}

Expand Down
131 changes: 81 additions & 50 deletions linera-service/src/wallet.rs
Original file line number Diff line number Diff line change
@@ -1,64 +1,95 @@
// 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<Item = ChainId>) {
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 {
let chain_ids: Vec<_> = chain_ids.into_iter().collect();
let total_chains = chain_ids.len();

if total_chains == 0 {
println!("No chains in wallet.");
return;
}

let plural_s = if total_chains == 1 { "" } else { "s" };
println!("\n\x1b[1mWALLET ({total_chains} chain{plural_s} in total)\x1b[0m",);

let mut chains = chain_ids
.into_iter()
.map(|chain_id| ChainDetails::new(chain_id, wallet))
.collect::<Vec<_>>();
// 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 {
println!();
chain.print_paragraph();
}
}

struct ChainDetails<'a> {
is_default: bool,
is_admin: bool,
origin: Option<ChainOrigin>,
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) {
let title = if self.is_admin {
"Admin Chain".to_string()
} else {
match self.origin {
Some(ChainOrigin::Root(i)) => format!("Root Chain {i}"),
_ => "Child Chain".to_string(),
}
};
let default_marker = if self.is_default { " [DEFAULT]" } else { "" };

// Print chain header in bold
println!("\x1b[1m{}{}\x1b[0m", title, default_marker);
println!(" Chain ID: {}", self.user_chain.chain_id);
if let Some(owner) = &self.user_chain.owner {
println!(" Owner: {owner}");
} else {
println!(" Owner: No owner key");
}
println!(" Timestamp: {}", self.user_chain.timestamp);
println!(" Blocks: {}", self.user_chain.next_block_height);
if let Some(hash) = self.user_chain.block_hash {
println!(" Latest Block: {}", hash);
}
if self.user_chain.pending_proposal.is_some() {
println!(" Status: ⚠ Pending proposal");
}
}
}
Loading