From bc04040ac3ea796b7371e749f5c0d67f3fb9cbbb Mon Sep 17 00:00:00 2001 From: Bear Wang Date: Wed, 8 Nov 2023 09:26:30 +0800 Subject: [PATCH] Add open browser feature (#44) * Add open browser feature * Add comment --- .rustfmt.toml | 2 +- Cargo.toml | 1 + src/app/command.rs | 7 ++ src/app/dashboard/mod.rs | 43 ++-------- src/app/dashboard/tab_blocks.rs | 10 +-- src/app/dashboard/tab_events.rs | 5 +- src/app/helper.rs | 47 ++--------- src/app/mod.rs | 9 +-- src/handler/mod.rs | 134 +++++++++++++------------------- src/handler/printer.rs | 10 +-- src/main.rs | 13 +--- src/networks/mod.rs | 36 ++++----- src/rpc/api.rs | 45 +++-------- src/rpc/client.rs | 38 ++------- src/rpc/storage.rs | 3 +- 15 files changed, 123 insertions(+), 280 deletions(-) diff --git a/.rustfmt.toml b/.rustfmt.toml index 8a01897..3cf70c0 100644 --- a/.rustfmt.toml +++ b/.rustfmt.toml @@ -1,7 +1,7 @@ # Basic edition = "2021" hard_tabs = true -max_width = 100 +max_width = 140 tab_spaces = 4 # Imports diff --git a/Cargo.toml b/Cargo.toml index 3bee919..8f6b698 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -32,6 +32,7 @@ comfy-table = { version = "7.0.1" } ratatui = { version = "0.23.0", features = ["all-widgets"] } crossterm = { version = "0.27.0" } array-bytes = { version = "6.1.0" } +open = { version = "5.0.0" } # polkadot sp-core = { git = "https://github.com/paritytech/polkadot-sdk", branch = "release-polkadot-v1.1.0" } sp-runtime = { git = "https://github.com/paritytech/polkadot-sdk", branch = "release-polkadot-v1.1.0" } diff --git a/src/app/command.rs b/src/app/command.rs index 0013e3d..7c946dc 100644 --- a/src/app/command.rs +++ b/src/app/command.rs @@ -34,6 +34,13 @@ pub enum ApplicationCommand { #[arg(name = "network", long)] network: Network, }, + /// Open block, extrinsic with default browser. + OpenWithBrowser { + #[arg(name = "block-hash", long)] + block_hash: Option, + #[arg(name = "extrinsic-hash", long)] + extrinsic_hash: Option, + }, /// Display dashboard information. DashBoard, /// Clean the history. diff --git a/src/app/dashboard/mod.rs b/src/app/dashboard/mod.rs index 8e3cb82..6f95c25 100644 --- a/src/app/dashboard/mod.rs +++ b/src/app/dashboard/mod.rs @@ -7,10 +7,7 @@ use std::{collections::VecDeque, io, sync::Arc}; // crates.io use crossterm::event::{read, Event, KeyCode, KeyEventKind}; use ratatui::{ - prelude::{ - text::Line, Backend, Color, Constraint, Direction, Frame, Layout, Rect, Span, Style, - Terminal, - }, + prelude::{text::Line, Backend, Color, Constraint, Direction, Frame, Layout, Rect, Span, Style, Terminal}, style::Stylize, widgets::*, }; @@ -81,23 +78,12 @@ impl DashBoard { .types() .types .iter() - .find(|ty| { - ty.ty.path - == Path::from_segments_unchecked(vec![ - "frame_system".to_string(), - "EventRecord".to_string(), - ]) - }) + .find(|ty| ty.ty.path == Path::from_segments_unchecked(vec!["frame_system".to_string(), "EventRecord".to_string()])) .map(|ty| ty.id) .unwrap(); let ty_mut = metadata.types_mut(); - let vec_event_records_ty = Type::new( - Path::default(), - vec![], - TypeDefSequence::new(event_records_type_id.into()), - vec![], - ); + let vec_event_records_ty = Type::new(Path::default(), vec![], TypeDefSequence::new(event_records_type_id.into()), vec![]); let vec_event_records_type_id = ty_mut.types.len() as u32; ty_mut .types @@ -109,11 +95,7 @@ impl DashBoard { if let Ok(storage_data) = self.events_rev.try_recv() { for data in storage_data { - let value = decode_as_type( - &mut data.0.as_ref(), - vec_event_records_type_id, - self.metadata.types(), - ); + let value = decode_as_type(&mut data.0.as_ref(), vec_event_records_type_id, self.metadata.types()); if let Ok(event_records) = value { match event_records.value { @@ -124,16 +106,11 @@ impl DashBoard { match record.value { ValueDef::Composite(inner) => match inner { Composite::Named(v) => { - let event_values: Vec> = v - .into_iter() - .filter(|d| d.0 == "event") - .map(|d| d.1) - .collect(); + let event_values: Vec> = + v.into_iter().filter(|d| d.0 == "event").map(|d| d.1).collect(); for event in event_values { - if self.events.items.len() - == self.events.items.capacity() - { + if self.events.items.len() == self.events.items.capacity() { self.events.items.pop_front(); } else { self.events.items.push_back(event); @@ -180,11 +157,7 @@ impl DashBoard { } } -pub async fn run_dashboard( - client: Arc>, - terminal: &mut Terminal, - mut dash_board: DashBoard, -) -> io::Result<()> +pub async fn run_dashboard(client: Arc>, terminal: &mut Terminal, mut dash_board: DashBoard) -> io::Result<()> where B: Backend, CI: ChainInfo, diff --git a/src/app/dashboard/tab_blocks.rs b/src/app/dashboard/tab_blocks.rs index db4b001..6fe07ec 100644 --- a/src/app/dashboard/tab_blocks.rs +++ b/src/app/dashboard/tab_blocks.rs @@ -1,9 +1,6 @@ // crates.io use ratatui::{ - prelude::{ - text::Line, Backend, Color, Constraint, Direction, Frame, Layout, Modifier, Rect, Span, - Style, - }, + prelude::{text::Line, Backend, Color, Constraint, Direction, Frame, Layout, Modifier, Rect, Span, Style}, widgets::*, }; use sp_core::Encode; @@ -94,10 +91,7 @@ where // Extrinsics items.push(ListItem::new("Extrinsic => ".to_string())); for (i, e) in b.extrinsics().iter().enumerate() { - items.push(ListItem::new(format!( - " ext{i} => {:?}", - CI::Hashing::hash(&e.encode()) - ))); + items.push(ListItem::new(format!(" ext{i} => {:?}", CI::Hashing::hash(&e.encode())))); } let l = List::new(items).block(block); diff --git a/src/app/dashboard/tab_events.rs b/src/app/dashboard/tab_events.rs index 0ece4a2..c6d771d 100644 --- a/src/app/dashboard/tab_events.rs +++ b/src/app/dashboard/tab_events.rs @@ -14,10 +14,7 @@ where { let mut text = "".to_string(); for e in &app.events.items { - text.push_str(&format!( - "{}\n", - serde_json::to_string(e).unwrap_or("Decode Error Occurred.".to_string()) - )); + text.push_str(&format!("{}\n", serde_json::to_string(e).unwrap_or("Decode Error Occurred.".to_string()))); } let l = Paragraph::new(text) .wrap(Wrap { trim: true }) diff --git a/src/app/helper.rs b/src/app/helper.rs index 1064c8a..6b58a07 100644 --- a/src/app/helper.rs +++ b/src/app/helper.rs @@ -35,16 +35,7 @@ pub struct Config { const ESCAPE_CHAR: Option = Some('\\'); const fn default_break_chars(c: char) -> bool { - matches!( - c, - ' ' | '\t' - | '\n' | '"' | '\\' - | '\'' | '`' | '@' - | '$' | '>' | '<' - | '=' | ';' | '|' - | '&' | '{' | '(' - | '\0' - ) + matches!(c, ' ' | '\t' | '\n' | '"' | '\\' | '\'' | '`' | '@' | '$' | '>' | '<' | '=' | ';' | '|' | '&' | '{' | '(' | '\0') } /// Create a line editor. @@ -133,11 +124,7 @@ impl Highlighter for EditorHelper { Owned(format!("\x1b[1m{hint}\x1b[m")) } - fn highlight_candidate<'c>( - &self, - candidate: &'c str, - _completion: CompletionType, - ) -> Cow<'c, str> { + fn highlight_candidate<'c>(&self, candidate: &'c str, _completion: CompletionType) -> Cow<'c, str> { Owned(candidate.to_string()) } } @@ -154,27 +141,17 @@ impl Hinter for EditorHelper { impl Completer for EditorHelper { type Candidate = Pair; - fn complete( - &self, - line: &str, - pos: usize, - _ctx: &Context<'_>, - ) -> rustyline::Result<(usize, Vec)> { + fn complete(&self, line: &str, pos: usize, _ctx: &Context<'_>) -> rustyline::Result<(usize, Vec)> { println!(); let (start, mut word) = extract_word(line, pos, ESCAPE_CHAR, default_break_chars); let prefixes = shell_words::split(&line[..pos]).unwrap(); let mut candidates = Vec::new(); - if let Some(command) = - prefix_command(&self.command, prefixes.iter().map(String::as_str).peekable()) - { + if let Some(command) = prefix_command(&self.command, prefixes.iter().map(String::as_str).peekable()) { candidates = command .get_subcommands() .cloned() - .map(|i| Pair { - display: i.get_name().to_owned(), - replacement: i.get_name().to_owned(), - }) + .map(|i| Pair { display: i.get_name().to_owned(), replacement: i.get_name().to_owned() }) .filter(|c| c.display.starts_with(word) && word != command.get_name()) .collect(); @@ -199,21 +176,14 @@ impl Completer for EditorHelper { } } -fn prefix_command<'s, I: Iterator>( - command: &Command, - mut prefixes: iter::Peekable, -) -> Option { +fn prefix_command<'s, I: Iterator>(command: &Command, mut prefixes: iter::Peekable) -> Option { if let Some(prefix) = prefixes.next() { for subcommand in command.get_subcommands() { if subcommand.get_name() == prefix || subcommand.get_display_name().unwrap_or_default() == prefix || subcommand.get_all_aliases().any(|s| s == prefix) { - return if prefixes.peek().is_none() { - Some(subcommand.clone()) - } else { - prefix_command(subcommand, prefixes) - }; + return if prefixes.peek().is_none() { Some(subcommand.clone()) } else { prefix_command(subcommand, prefixes) }; } } } @@ -245,8 +215,7 @@ pub fn metadata_path() -> Result { /// Print the app welcome message. pub fn print_welcome_message() { - const INTRODUCTION: &str = - "This is the all-in-one substrate command assistant, the Polkadot Apps CLI edition."; + const INTRODUCTION: &str = "This is the all-in-one substrate command assistant, the Polkadot Apps CLI edition."; const USAGE: &str = " Tips: - `usage` to ask help. diff --git a/src/app/mod.rs b/src/app/mod.rs index 4e0a638..b672e01 100644 --- a/src/app/mod.rs +++ b/src/app/mod.rs @@ -2,11 +2,6 @@ mod command; mod dashboard; mod helper; -pub use command::{ - AccountInfoCommand, AppCommand, ApplicationCommand, ChainCommand, RpcCommand, RuntimeCommand, -}; +pub use command::{AccountInfoCommand, AppCommand, ApplicationCommand, ChainCommand, RpcCommand, RuntimeCommand}; pub use dashboard::{run_dashboard, DashBoard}; -pub use helper::{ - app_root_path, create_editor, history_path, metadata_path, print_welcome_message, Config, - EditorHelper, POLKADOT_CLI, -}; +pub use helper::{app_root_path, create_editor, history_path, metadata_path, print_welcome_message, Config, EditorHelper, POLKADOT_CLI}; diff --git a/src/handler/mod.rs b/src/handler/mod.rs index 10d1603..7658ff1 100644 --- a/src/handler/mod.rs +++ b/src/handler/mod.rs @@ -26,16 +26,13 @@ use tokio::sync::mpsc; // this crate use crate::{ app::{ - metadata_path, run_dashboard, AccountInfoCommand, AppCommand, ApplicationCommand, - ChainCommand, DashBoard, RpcCommand, RuntimeCommand, POLKADOT_CLI, + metadata_path, run_dashboard, AccountInfoCommand, AppCommand, ApplicationCommand, ChainCommand, DashBoard, RpcCommand, + RuntimeCommand, POLKADOT_CLI, }, errors::AppError, handler::printer::{print_result, print_storage_type, print_usage}, networks::{ChainInfo, Network}, - rpc::{ - single_map_storage_key, AccountBalances, AccountNonce, ChainApi, RpcClient, RpcError, - StateApi, SubscribeApi, SystemApi, - }, + rpc::{single_map_storage_key, AccountBalances, AccountNonce, ChainApi, RpcClient, RpcError, StateApi, SubscribeApi, SystemApi}, }; pub struct Handler { @@ -48,22 +45,18 @@ impl Handler { pub async fn new(client: Arc>) -> Result, AppError> { let metadata_path = metadata_path().expect("Failed to get metadata path"); let runtime_version = client - .runtime_version( - client.get_finalized_head().await?.expect("Failed to get finalized head"), - ) + .runtime_version(client.get_finalized_head().await?.expect("Failed to get finalized head")) .await .expect("Failed to get runtime version"); - let file_name = - format!("{}-runtime-{}.meta", runtime_version.spec_name, runtime_version.spec_version); + let file_name = format!("{}-runtime-{}.meta", runtime_version.spec_name, runtime_version.spec_version); let runtime_file = metadata_path.join(file_name); log::debug!(target: "cli", "Runtime metadata file path: {:?}", runtime_file); if !runtime_file.is_file() { // New network or new runtime version detected. let metadata = client.runtime_metadata().await?; - let mut metadata_file = File::create(runtime_file).map_err(|e| { - AppError::Custom(format!("Failed to create metadata file: {:?}", e)) - })?; + let mut metadata_file = + File::create(runtime_file).map_err(|e| AppError::Custom(format!("Failed to create metadata file: {:?}", e)))?; metadata_file .write_all(&metadata.encode()) .map_err(|e| AppError::Custom(format!("Failed to write metadata file: {:?}", e)))?; @@ -71,16 +64,12 @@ impl Handler { Ok(Self { client, metadata }) } else { // Reload from the existed runtime metadata file. - let mut f = File::open(runtime_file).map_err(|e| { - AppError::Custom(format!("Failed to open runtime metadata file: {:?}", e)) - })?; + let mut f = File::open(runtime_file).map_err(|e| AppError::Custom(format!("Failed to open runtime metadata file: {:?}", e)))?; let mut bytes = Vec::new(); - f.read_to_end(&mut bytes).map_err(|e| { - AppError::Custom(format!("Failed to read runtime metadata file: {:?}", e)) - })?; - let metadata = Metadata::decode(&mut bytes.as_slice()).map_err(|e| { - AppError::Custom(format!("Failed to decode runtime metadata file: {:?}", e)) - })?; + f.read_to_end(&mut bytes) + .map_err(|e| AppError::Custom(format!("Failed to read runtime metadata file: {:?}", e)))?; + let metadata = Metadata::decode(&mut bytes.as_slice()) + .map_err(|e| AppError::Custom(format!("Failed to decode runtime metadata file: {:?}", e)))?; log::debug!(target: "cli", "Reload runtime metadata file successfully"); Ok(Self { client, metadata }) } @@ -119,11 +108,7 @@ impl Handler { tokio::spawn(async move { while let Some(storage_set) = events_subs.next().await { if let Ok(storage) = storage_set { - let data = storage - .changes - .into_iter() - .filter_map(|(_k, data)| data) - .collect(); + let data = storage.changes.into_iter().filter_map(|(_k, data)| data).collect(); if events_tx.send(data).is_err() { break; } @@ -132,12 +117,7 @@ impl Handler { }); let system_pane_info = self.client.system_pane_info().await?; - let dashboard = DashBoard::new( - system_pane_info, - blocks_rx, - events_rx, - self.metadata.clone(), - ); + let dashboard = DashBoard::new(system_pane_info, blocks_rx, events_rx, self.metadata.clone()); run_dashboard(self.client.clone(), &mut terminal, dashboard).await?; // restore terminal @@ -145,6 +125,29 @@ impl Handler { execute!(terminal.backend_mut(), LeaveAlternateScreen, DisableMouseCapture)?; terminal.show_cursor()?; }, + ApplicationCommand::OpenWithBrowser { block_hash, extrinsic_hash } => { + let maybe_url = match (block_hash, extrinsic_hash) { + (Some(block_hash), None) if ::Hash::from_str(&block_hash).is_ok() => + Some(format!("https://{:?}.subscan.io/block/{}", ::NET_WORK, block_hash)), + (None, Some(extrinsic_hash)) if ::Hash::from_str(&extrinsic_hash).is_ok() => + Some(format!("https://{:?}.subscan.io/extrinsic/{}", ::NET_WORK, extrinsic_hash)), + _ => None, + }; + + if let Some(url) = maybe_url { + match open::that(url.clone()) { + Ok(_) => { + println!("Opened {} with browser successfully", Colorize::green(url.as_str())); + }, + Err(e) => { + println!("Opened {} with browser failed, error: {}", url, Colorize::red(e.to_string().as_str())); + }, + } + } else { + println!("{}", Colorize::red("Please specify valid input either block hash or extrinsic hash")); + return Err(AppError::Custom("Please specify valid input either block hash or extrinsic hash".to_string())); + } + }, ApplicationCommand::CleanHistory => { todo!(); }, @@ -188,8 +191,7 @@ impl Handler { }, AppCommand::Chain(sub_command) => match sub_command { ChainCommand::GetBlock { hash } => { - let hash = ::Hash::from_str(hash.as_str()) - .map_err(|_| RpcError::InvalidParams)?; + let hash = ::Hash::from_str(hash.as_str()).map_err(|_| RpcError::InvalidParams)?; let res = self.client.get_block(hash).await; print_result(res); }, @@ -211,8 +213,7 @@ impl Handler { } }, ChainCommand::GetHeader { hash } => { - let hash = ::Hash::from_str(hash.as_str()) - .map_err(|_| RpcError::InvalidParams)?; + let hash = ::Hash::from_str(hash.as_str()).map_err(|_| RpcError::InvalidParams)?; let res = self.client.get_header(hash).await; print_result(res); }, @@ -225,30 +226,22 @@ impl Handler { AccountInfoCommand::Balances { account_id, at_block } => { let hash = at_block.and_then(|s| ::Hash::from_str(&s).ok()); - let key = ::AccountId::from_str(account_id.as_str()) - .map_err(|_| RpcError::InvalidParams)?; + let key = ::AccountId::from_str(account_id.as_str()).map_err(|_| RpcError::InvalidParams)?; let storage_key = - single_map_storage_key(&self.metadata, "System", "Account", key) - .map_err(|_| RpcError::GenerateStorageKeyFailed)?; + single_map_storage_key(&self.metadata, "System", "Account", key).map_err(|_| RpcError::GenerateStorageKeyFailed)?; let account: Option>> = self.client.get_storage(storage_key, hash).await?; if let Some(a) = account { - print_result(Ok(AccountBalances { - free: a.data.free, - reserved: a.data.reserved, - frozen: a.data.frozen, - })); + print_result(Ok(AccountBalances { free: a.data.free, reserved: a.data.reserved, frozen: a.data.frozen })); } }, AccountInfoCommand::Nonce { account_id, at_block } => { let hash = at_block.and_then(|s| ::Hash::from_str(&s).ok()); - let key = ::AccountId::from_str(account_id.as_str()) - .map_err(|_| RpcError::InvalidParams)?; + let key = ::AccountId::from_str(account_id.as_str()).map_err(|_| RpcError::InvalidParams)?; let storage_key = - single_map_storage_key(&self.metadata, "System", "Account", key) - .map_err(|_| RpcError::GenerateStorageKeyFailed)?; + single_map_storage_key(&self.metadata, "System", "Account", key).map_err(|_| RpcError::GenerateStorageKeyFailed)?; let account: Option>> = self.client.get_storage(storage_key, hash).await?; @@ -276,8 +269,7 @@ impl Handler { }, RuntimeCommand::StoragesOfPallet { pallet_name, pallet_id } => { let pallet: Option = match (pallet_name, pallet_id) { - (Some(name), Some(id)) => - self.metadata.pallets().find(|p| p.name() == name && p.index() == id), + (Some(name), Some(id)) => self.metadata.pallets().find(|p| p.name() == name && p.index() == id), (Some(name), None) => self.metadata.pallet_by_name(&name), (None, Some(id)) => self.metadata.pallet_by_index(id), _ => None, @@ -313,8 +305,7 @@ impl Handler { }, RuntimeCommand::ConstantsOfPallet { pallet_name, pallet_id } => { let pallet: Option = match (pallet_name, pallet_id) { - (Some(name), Some(id)) => - self.metadata.pallets().find(|p| p.name() == name && p.index() == id), + (Some(name), Some(id)) => self.metadata.pallets().find(|p| p.name() == name && p.index() == id), (Some(name), None) => self.metadata.pallet_by_name(&name), (None, Some(id)) => self.metadata.pallet_by_index(id), _ => None, @@ -329,8 +320,7 @@ impl Handler { .apply_modifier(UTF8_ROUND_CORNERS) .set_content_arrangement(ContentArrangement::Dynamic); p.constants().for_each(|c| { - table - .add_row(vec![c.name(), c.docs().get(0).unwrap_or(&"".to_owned())]); + table.add_row(vec![c.name(), c.docs().get(0).unwrap_or(&"".to_owned())]); }); println!( @@ -345,8 +335,7 @@ impl Handler { }, RuntimeCommand::GetConstantByName { pallet_name, pallet_id, constant_name } => { let pallet: Option = match (pallet_name, pallet_id) { - (Some(name), Some(id)) => - self.metadata.pallets().find(|p| p.name() == name && p.index() == id), + (Some(name), Some(id)) => self.metadata.pallets().find(|p| p.name() == name && p.index() == id), (Some(name), None) => self.metadata.pallet_by_name(&name), (None, Some(id)) => self.metadata.pallet_by_index(id), _ => None, @@ -357,22 +346,9 @@ impl Handler { if let Some(c) = p.constants().find(|c| c.name() == constant_name) { let ty_id = c.ty(); let mut bytes = c.value(); - let value = scale_value::scale::decode_as_type( - &mut bytes, - ty_id, - self.metadata.types(), - ) - .map_err(|e| { - AppError::Custom(format!( - "Failed to decode constant value: {:?}", - e - )) - })?; - println!( - "{} => {}", - c.name(), - serde_json::to_string_pretty(&value).unwrap() - ); + let value = scale_value::scale::decode_as_type(&mut bytes, ty_id, self.metadata.types()) + .map_err(|e| AppError::Custom(format!("Failed to decode constant value: {:?}", e)))?; + println!("{} => {}", c.name(), serde_json::to_string_pretty(&value).unwrap()); } else { println!("Did not find the constant."); } @@ -382,13 +358,9 @@ impl Handler { }, RuntimeCommand::GetRuntimeVersion { hash } => { let hash = if let Some(hash) = hash { - ::Hash::from_str(hash.as_str()) - .map_err(|_| RpcError::InvalidParams)? + ::Hash::from_str(hash.as_str()).map_err(|_| RpcError::InvalidParams)? } else { - self.client - .get_finalized_head() - .await? - .expect("Failed to get finalized head") + self.client.get_finalized_head().await?.expect("Failed to get finalized head") }; let res = self.client.runtime_version(hash).await; diff --git a/src/handler/printer.rs b/src/handler/printer.rs index 10eee9c..f13e54c 100644 --- a/src/handler/printer.rs +++ b/src/handler/printer.rs @@ -19,8 +19,7 @@ pub fn print_storage_type(entry_type: StorageEntryType, metadata: &Metadata) -> log::debug!(target: "cli", "ident: {:?}", i); if VEC_WRAPPER.contains(&i.as_str()) { - let inner_ty = - ty.type_params[0].ty.expect("This type missed generic type T"); + let inner_ty = ty.type_params[0].ty.expect("This type missed generic type T"); format!("{}<{}>", i, ty_name(inner_ty, metadata)) } else { format!("{}", i) @@ -69,11 +68,8 @@ pub fn print_storage_type(entry_type: StorageEntryType, metadata: &Metadata) -> match entry_type { StorageEntryType::Plain(t) => ty_name(t.into(), metadata), - StorageEntryType::Map { hashers: _, key_ty, value_ty } => format!( - "{} -> {}", - ty_name(key_ty.into(), metadata), - ty_name(value_ty.into(), metadata) - ), + StorageEntryType::Map { hashers: _, key_ty, value_ty } => + format!("{} -> {}", ty_name(key_ty.into(), metadata), ty_name(value_ty.into(), metadata)), } } diff --git a/src/main.rs b/src/main.rs index 870e630..c4bb180 100644 --- a/src/main.rs +++ b/src/main.rs @@ -11,9 +11,7 @@ use std::sync::Arc; use clap::Parser; use colored::Colorize; use handler::ExecutionResult; -use networks::{ - ChainInfo, CrabChain, DarwiniaChain, KusamaChain, Network, PangoroChain, PolkadotChain, -}; +use networks::{ChainInfo, CrabChain, DarwiniaChain, KusamaChain, Network, PangoroChain, PolkadotChain}; use rpc::RpcClient; use rustyline::{hint::HistoryHinter, history::FileHistory, Editor}; // this crate @@ -107,18 +105,13 @@ pub async fn run( Ok(prompt) => match AppCommand::try_parse_from(prompt.split_whitespace()) { Ok(command) => { log::debug!(target: "cli", "command: {:?}", command); - if let Ok(ExecutionResult::SwitchNetworkTo(network)) = - handler.handle_command(command).await - { + if let Ok(ExecutionResult::SwitchNetworkTo(network)) = handler.handle_command(command).await { return Ok(ExecutionResult::SwitchNetworkTo(network)); } }, Err(e) => { log::debug!(target: "cli", "failed to parse the command, err: {:?}", e); - println!( - "{}", - "invalid input, TAB to complete.".to_string().italic().bright_red() - ); + println!("{}", "invalid input, TAB to complete.".to_string().italic().bright_red()); continue; }, }, diff --git a/src/networks/mod.rs b/src/networks/mod.rs index e690589..2d32568 100644 --- a/src/networks/mod.rs +++ b/src/networks/mod.rs @@ -17,6 +17,20 @@ use sp_runtime::{ DeserializeOwned, OpaqueExtrinsic, }; +#[derive(Subcommand, Clone, Debug, Serialize, Deserialize, Default, ValueEnum)] +pub enum Network { + Local, + // Polkadot + Polkadot, + Kusama, + // Darwinia + Pangolin, + Pangoro, + #[default] + Darwinia, + Crab, +} + /// The ChainInfo API pub trait ChainInfo: Sync + Send { /// The ws endpoint of the chain @@ -33,30 +47,10 @@ pub trait ChainInfo: Sync + Send { /// The block number type of the chain type BlockNumber: Serialize + DeserializeOwned + Send + From; /// The hash type of the chain - type Hash: Serialize - + DeserializeOwned - + 'static - + Send - + FromStr - + From - + From<::Hash>; + type Hash: Serialize + DeserializeOwned + 'static + Send + FromStr + From + From<::Hash>; type Hashing: HashT; /// The header type of the chain type Header: Serialize + DeserializeOwned + HeaderT; /// The nonce type of the chain type Nonce: Serialize + DeserializeOwned + Decode + Debug; } - -#[derive(Subcommand, Clone, Debug, Serialize, Deserialize, Default, ValueEnum)] -pub enum Network { - Local, - // parity - Polkadot, - Kusama, - // Darwinia - Pangolin, - Pangoro, - #[default] - Darwinia, - Crab, -} diff --git a/src/rpc/api.rs b/src/rpc/api.rs index 0ebd286..b840f2b 100644 --- a/src/rpc/api.rs +++ b/src/rpc/api.rs @@ -52,25 +52,17 @@ pub trait ChainApi { type ChainInfo: ChainInfo; /// Get the chain block - async fn get_block( - &self, - hash: HashForChain, - ) -> RpcResult::Block>>; + async fn get_block(&self, hash: HashForChain) -> RpcResult::Block>>; /// Get the block hash for a specific block - async fn get_block_hash( - &self, - number: BlockNumberForChain, - ) -> RpcResult::Hash>>; + async fn get_block_hash(&self, number: BlockNumberForChain) + -> RpcResult::Hash>>; /// Get the hash of the last finalized block in the canon chain async fn get_finalized_head(&self) -> RpcResult>>; /// Retrieves the header for a specific block - async fn get_header( - &self, - hash: HashForChain, - ) -> RpcResult>; + async fn get_header(&self, hash: HashForChain) -> RpcResult>; } /// The State API provides access to chain state and storage. @@ -80,10 +72,7 @@ pub trait StateApi { type ChainInfo: ChainInfo; /// Get the runtime version - async fn runtime_version( - &self, - hash: HashForChain, - ) -> RpcResult; + async fn runtime_version(&self, hash: HashForChain) -> RpcResult; /// Get the runtime metadata async fn runtime_metadata(&self) -> RpcResult; @@ -112,28 +101,16 @@ pub trait SubscribeApi { Params: ToRpcParams + Send, Notif: DeserializeOwned; - async fn subscribe_finalized_heads( - &self, - ) -> RpcResult>> { - self.subscribe( - "chain_subscribeFinalizedHeads", - rpc_params![], - "chain_unsubscribeFinalizedHeads", - ) - .await + async fn subscribe_finalized_heads(&self) -> RpcResult>> { + self.subscribe("chain_subscribeFinalizedHeads", rpc_params![], "chain_unsubscribeFinalizedHeads") + .await } - async fn subscribe_events( - &self, - ) -> RpcResult>>> { + async fn subscribe_events(&self) -> RpcResult>>> { let mut key = twox_128("System".as_bytes()).to_vec(); key.extend(twox_128("Events".as_bytes())); - self.subscribe( - "state_subscribeStorage", - rpc_params![vec![StorageKey(key)]], - "state_unsubscribeStorage", - ) - .await + self.subscribe("state_subscribeStorage", rpc_params![vec![StorageKey(key)]], "state_unsubscribeStorage") + .await } } diff --git a/src/rpc/client.rs b/src/rpc/client.rs index adf93ab..b0a57bf 100644 --- a/src/rpc/client.rs +++ b/src/rpc/client.rs @@ -19,10 +19,7 @@ use sp_version::RuntimeVersion; use subxt_metadata::Metadata; // this crate use super::{ - api::{ - BlockForChain, BlockNumberForChain, ChainApi, HashForChain, HeaderForChain, StateApi, - SubscribeApi, SystemApi, - }, + api::{BlockForChain, BlockNumberForChain, ChainApi, HashForChain, HeaderForChain, StateApi, SubscribeApi, SystemApi}, errors::RpcError, types::{this_crate_types::SystemPaneInfo, ChainType, Health, Properties}, }; @@ -67,15 +64,7 @@ impl RpcClient { let hash = self.get_finalized_head().await?.expect("Failed to get finalized head"); let runtime_version = self.runtime_version(hash).await?; - Ok(SystemPaneInfo { - system_name, - system_version, - chain_type, - chain_name, - token_symbol, - token_decimal, - runtime_version, - }) + Ok(SystemPaneInfo { system_name, system_version, chain_type, chain_name, token_symbol, token_decimal, runtime_version }) } } @@ -157,10 +146,7 @@ impl ChainApi for RpcClient { type ChainInfo = CI; /// Get the chain block - async fn get_block( - &self, - hash: HashForChain, - ) -> RpcResult>> { + async fn get_block(&self, hash: HashForChain) -> RpcResult>> { let res = self .client .request("chain_getBlock", rpc_params![hash]) @@ -170,10 +156,7 @@ impl ChainApi for RpcClient { } /// Get the block hash for a specific block - async fn get_block_hash( - &self, - number: BlockNumberForChain, - ) -> RpcResult>> { + async fn get_block_hash(&self, number: BlockNumberForChain) -> RpcResult>> { let res = self .client .request("chain_getBlockHash", rpc_params![number]) @@ -193,10 +176,7 @@ impl ChainApi for RpcClient { } /// Retrieves the header for a specific block - async fn get_header( - &self, - hash: HashForChain, - ) -> RpcResult> { + async fn get_header(&self, hash: HashForChain) -> RpcResult> { let res = self .client .request("chain_getHeader", rpc_params![hash]) @@ -211,10 +191,7 @@ impl StateApi for RpcClient { type ChainInfo = CI; /// Get the runtime version - async fn runtime_version( - &self, - hash: HashForChain, - ) -> RpcResult { + async fn runtime_version(&self, hash: HashForChain) -> RpcResult { let res = self .client .request("state_getRuntimeVersion", rpc_params![hash]) @@ -230,8 +207,7 @@ impl StateApi for RpcClient { .request("state_getMetadata", rpc_params![]) .await .map_err(RpcError::from)?; - let metadata_prefix = RuntimeMetadataPrefixed::decode(&mut metadata_bytes.0.as_slice()) - .map_err(|_| RpcError::DecodeError)?; + let metadata_prefix = RuntimeMetadataPrefixed::decode(&mut metadata_bytes.0.as_slice()).map_err(|_| RpcError::DecodeError)?; let metadata = metadata_prefix.try_into().map_err(|_| RpcError::DecodeError)?; Ok(metadata) diff --git a/src/rpc/storage.rs b/src/rpc/storage.rs index 9d95ef7..46aacca 100644 --- a/src/rpc/storage.rs +++ b/src/rpc/storage.rs @@ -43,7 +43,6 @@ fn key_hash(key: &K, hasher: &StorageHasher) -> Vec { StorageHasher::Blake2_256 => sp_core::blake2_256(&encoded_key).to_vec(), StorageHasher::Twox128 => sp_core::twox_128(&encoded_key).to_vec(), StorageHasher::Twox256 => sp_core::twox_256(&encoded_key).to_vec(), - StorageHasher::Twox64Concat => - sp_core::twox_64(&encoded_key).iter().chain(&encoded_key).cloned().collect(), + StorageHasher::Twox64Concat => sp_core::twox_64(&encoded_key).iter().chain(&encoded_key).cloned().collect(), } }