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

refactor: use clap as a commands parser #3867

Merged
merged 66 commits into from
Mar 15, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
66 commits
Select commit Hold shift + click to select a range
0cf35c6
feat: add clap and CommandContext
therustmonk Feb 21, 2022
d7a772a
fix: use Args for commands parsing
therustmonk Feb 21, 2022
8a04d9a
fix: implement check-for-updates command
therustmonk Feb 21, 2022
b25bb67
fix: add Status command
therustmonk Feb 21, 2022
2b7fb97
fix: add get-chain-metadata command
therustmonk Feb 21, 2022
bb6c66f
fix: add get-db-stats command
therustmonk Feb 21, 2022
6375142
fix: implement list-peers command
therustmonk Feb 22, 2022
2466770
fix: add dial-peer command
therustmonk Feb 22, 2022
9d824f9
fix: add ping-peer command
therustmonk Feb 22, 2022
d606045
fix: add ban-peer command
therustmonk Feb 22, 2022
ca35a65
refactor: improve ban and unban peers commands
therustmonk Feb 22, 2022
df9a658
fix: add docs to check_for_updates and dial_peer commands
therustmonk Feb 22, 2022
6113c61
fix: add docs to get-chain-meradata and get-db-stats commands
therustmonk Feb 22, 2022
86642ae
fix: add docs to list-peers
therustmonk Feb 22, 2022
d111e24
fix: add docs to the ping-peer command
therustmonk Feb 22, 2022
4546dc8
fix: add list-banned-peers
therustmonk Feb 22, 2022
b6d0f2d
fix: add docs to the version command
therustmonk Feb 22, 2022
d4d98c5
fix: add unban-all-peers
therustmonk Feb 22, 2022
dc2cd26
fix: add list-connections command
therustmonk Feb 22, 2022
6b78131
fix: add reset-offline-peers command
therustmonk Feb 22, 2022
a54adf3
fix: add block-timing command
therustmonk Feb 22, 2022
2481f41
fix: add get-mempool-stats command
therustmonk Feb 22, 2022
58f8e2f
fix: add get-mempool-state command
therustmonk Feb 22, 2022
4608f76
fix: add check-db command
therustmonk Feb 22, 2022
e0150e1
fix: add list-reorgs command
therustmonk Feb 22, 2022
25a7e08
fix: add get-network-stats
therustmonk Feb 22, 2022
a17694f
fix: add whoami command
therustmonk Feb 22, 2022
dd96ca6
fix: add rewind-blockchain command
therustmonk Feb 22, 2022
47c427b
fix: add derive feature to the strum dependency
therustmonk Feb 22, 2022
d062512
fix: add search-utxo command
therustmonk Feb 22, 2022
131247a
fix: add search-kernel command
therustmonk Feb 22, 2022
9eae9af
fix: clean imports
therustmonk Feb 22, 2022
d718bee
fix: add get-block-command
therustmonk Feb 22, 2022
7b60a32
fix: add discover-peer command
therustmonk Feb 22, 2022
828dfaf
fix: add header-stats command
therustmonk Feb 22, 2022
ec03955
fix: add period-stats command
therustmonk Feb 22, 2022
ab8d629
fix: add get-peer command
therustmonk Feb 22, 2022
10c4490
fix: remove CommandHandler
therustmonk Mar 2, 2022
2ba9f4b
fix: remove Args
therustmonk Mar 2, 2022
03fad87
fix: remove unused imports
therustmonk Mar 2, 2022
85746ee
fix: remove ArgsError
therustmonk Mar 2, 2022
bc144bc
fix: add commands variants
therustmonk Mar 2, 2022
93067e5
fix: add Quit command
therustmonk Mar 2, 2022
d343853
fix: fix length field name
therustmonk Mar 2, 2022
651c3ba
fix: add original_string to the get-peer command
therustmonk Mar 4, 2022
0802a81
fix: fix get-network-stats command
therustmonk Mar 4, 2022
b78e685
fix: use matches to get a command
therustmonk Mar 4, 2022
3f8beea
feat: add watch_command module
therustmonk Mar 4, 2022
6ba9dd6
refactor: handle commands from a string
therustmonk Mar 4, 2022
2f2f17c
feat: add watch result
therustmonk Mar 4, 2022
10c6e3d
feat: add watch loop
therustmonk Mar 4, 2022
9cd0b08
fix: add line method to the Command
therustmonk Mar 4, 2022
e5e7746
fix: run watched command on loop init
therustmonk Mar 4, 2022
6a0c2eb
fix: clippy warning fix
therustmonk Mar 5, 2022
38127d2
fix: add parameters parser (nom-based)
therustmonk Mar 5, 2022
6f729a1
fix: use nom parser for commands
therustmonk Mar 5, 2022
46eb7ec
fix: support items without quotes
therustmonk Mar 5, 2022
52b7ed2
fix: add tests for the nom parser
therustmonk Mar 5, 2022
4275fe1
fix: repair the status loop
therustmonk Mar 5, 2022
e78882a
fix: use the watch interval parameter
therustmonk Mar 5, 2022
4e84719
fix: execute watch command on start
therustmonk Mar 13, 2022
6254f0e
fix: implement Default for the WatchCommand
therustmonk Mar 13, 2022
25dde41
feat: add watch mode (like non-interactive with status)
therustmonk Mar 13, 2022
9847ad8
feat: override watch command in the non-interactive mode
therustmonk Mar 14, 2022
df853f3
fix: catch signal in the status loop
therustmonk Mar 14, 2022
a501d79
Merge branch 'development' into clap-commands
aviator-app[bot] Mar 15, 2022
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
18 changes: 18 additions & 0 deletions Cargo.lock

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

2 changes: 2 additions & 0 deletions applications/tari_app_utilities/src/utilities.rs
Original file line number Diff line number Diff line change
Expand Up @@ -205,6 +205,7 @@ pub fn either_to_node_id(either: Either<CommsPublicKey, NodeId>) -> NodeId {
}
}

#[derive(Debug)]
pub struct UniPublicKey(PublicKey);

impl FromStr for UniPublicKey {
Expand All @@ -227,6 +228,7 @@ impl From<UniPublicKey> for PublicKey {
}
}

#[derive(Debug)]
pub enum UniNodeId {
PublicKey(PublicKey),
NodeId(NodeId),
Expand Down
5 changes: 4 additions & 1 deletion applications/tari_base_node/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -24,8 +24,10 @@ tari_shutdown = { path = "../../infrastructure/shutdown" }
tari_utilities = { git = "https://github.com/tari-project/tari_utilities.git", tag = "v0.3.1" }

anyhow = "1.0.53"
async-trait = "0.1.52"
bincode = "1.3.1"
chrono = { version = "0.4.19", default-features = false }
clap = { version = "3.1.1", features = ["derive"] }
config = { version = "0.9.3" }
crossterm = "0.22"
derive_more = "0.99.17"
Expand All @@ -34,10 +36,11 @@ futures = { version = "^0.3.16", default-features = false, features = ["alloc"]
log = { version = "0.4.8", features = ["std"] }
log-mdc = "0.1.0"
num_cpus = "1"
nom = "7.1.0"
regex = "1"
rustyline = "9.0"
rustyline-derive = "0.5"
strum = "0.22"
strum = { version = "0.22", features = ["derive"] }
strum_macros = "0.22"
thiserror = "^1.0.26"
tokio = { version = "1.11", features = ["signal"] }
Expand Down
96 changes: 0 additions & 96 deletions applications/tari_base_node/src/commands/args.rs
Original file line number Diff line number Diff line change
@@ -1,97 +1 @@
use std::{
iter::Peekable,
str::{FromStr, SplitWhitespace},
};

use tari_utilities::hex::{Hex, HexError};
use thiserror::Error;

#[derive(Debug, Error)]
#[error("{name} {reason}")]
pub struct ArgsError {
name: &'static str,
reason: ArgsReason,
}

impl ArgsError {
pub fn new(name: &'static str, reason: impl Into<ArgsReason>) -> Self {
Self {
name,
reason: reason.into(),
}
}
}

#[derive(Debug, Error)]
pub enum ArgsReason {
#[error("argument required")]
Required,
#[error("argument can't be parsed: {details}")]
NotParsed { details: String },
#[error("argument is not valid: {description}")]
Inconsistent { description: String },
}

impl<T: AsRef<str>> From<T> for ArgsReason {
fn from(value: T) -> Self {
Self::Inconsistent {
description: value.as_ref().to_owned(),
}
}
}

pub struct Args<'a> {
splitted: Peekable<SplitWhitespace<'a>>,
}

impl<'a> Args<'a> {
pub fn split(s: &'a str) -> Self {
Self {
splitted: s.split_whitespace().peekable(),
}
}

// TODO: Remove
pub fn shift_one(&mut self) {
self.splitted.next();
}

// TODO: Use `next` always
pub fn try_take_next<T>(&mut self, name: &'static str) -> Result<Option<T>, ArgsError>
where
T: FromStr,
T::Err: ToString,
{
match self.splitted.peek().map(|s| s.parse()) {
Some(Ok(value)) => Ok(Some(value)),
Some(Err(err)) => Err(ArgsError::new(name, ArgsReason::NotParsed {
details: err.to_string(),
})),
None => Ok(None),
}
}

pub fn take_next<T>(&mut self, name: &'static str) -> Result<T, ArgsError>
where
T: FromStr,
T::Err: ToString,
{
match self.try_take_next(name)? {
Some(value) => {
self.shift_one();
Ok(value)
},
None => Err(ArgsError::new(name, ArgsReason::Required)),
}
}
}

pub struct FromHex<T>(pub T);

impl<T: Hex> FromStr for FromHex<T> {
type Err = HexError;

fn from_str(s: &str) -> Result<Self, Self::Err> {
T::from_hex(s).map(Self)
}
}
83 changes: 83 additions & 0 deletions applications/tari_base_node/src/commands/command/ban_peer.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
use std::time::Duration;

use anyhow::Error;
use async_trait::async_trait;
use clap::Parser;
use tari_app_utilities::utilities::UniNodeId;
use tari_comms::peer_manager::NodeId;

use super::{CommandContext, HandleCommand};
use crate::LOG_TARGET;

/// Bans a peer
#[derive(Debug, Parser)]
pub struct ArgsBan {
/// hex public key or emoji id
node_id: UniNodeId,
/// length of time to ban the peer for in seconds
#[clap(default_value_t = std::u64::MAX)]
length: u64,
}

/// Removes a peer ban
#[derive(Debug, Parser)]
pub struct ArgsUnban {
/// hex public key or emoji id
node_id: UniNodeId,
/// length of time to ban the peer for in seconds
#[clap(default_value_t = std::u64::MAX)]
length: u64,
}

#[async_trait]
impl HandleCommand<ArgsBan> for CommandContext {
async fn handle_command(&mut self, args: ArgsBan) -> Result<(), Error> {
let node_id = args.node_id.into();
let duration = Duration::from_secs(args.length);
self.ban_peer(node_id, duration, true).await
}
}

#[async_trait]
impl HandleCommand<ArgsUnban> for CommandContext {
async fn handle_command(&mut self, args: ArgsUnban) -> Result<(), Error> {
let node_id = args.node_id.into();
let duration = Duration::from_secs(args.length);
self.ban_peer(node_id, duration, false).await
}
}

impl CommandContext {
pub async fn ban_peer(&mut self, node_id: NodeId, duration: Duration, must_ban: bool) -> Result<(), Error> {
if self.base_node_identity.node_id() == &node_id {
println!("Cannot ban our own node");
} else if must_ban {
// TODO: Use errors
match self
.connectivity
.ban_peer_until(node_id.clone(), duration, "UI manual ban".to_string())
.await
{
Ok(_) => println!("Peer was banned in base node."),
Err(err) => {
println!("Failed to ban peer: {:?}", err);
log::error!(target: LOG_TARGET, "Could not ban peer: {:?}", err);
},
}
} else {
match self.peer_manager.unban_peer(&node_id).await {
Ok(_) => {
println!("Peer ban was removed from base node.");
},
Err(err) if err.is_peer_not_found() => {
println!("Peer not found in base node");
},
Err(err) => {
println!("Failed to ban peer: {:?}", err);
log::error!(target: LOG_TARGET, "Could not ban peer: {:?}", err);
},
}
}
Ok(())
}
}
48 changes: 48 additions & 0 deletions applications/tari_base_node/src/commands/command/block_timing.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
use anyhow::Error;
use async_trait::async_trait;
use clap::Parser;
use tari_core::blocks::BlockHeader;

use super::{CommandContext, HandleCommand};

/// Calculates the maximum, minimum, and average time taken to mine a given range of blocks
#[derive(Debug, Parser)]
pub struct Args {
/// number of blocks from chain tip or start height
start: u64,
/// end height
end: Option<u64>,
}

#[async_trait]
impl HandleCommand<Args> for CommandContext {
async fn handle_command(&mut self, args: Args) -> Result<(), Error> {
// TODO: is that possible to validate it with clap?
if args.end.is_none() && args.start < 2 {
Err(Error::msg("Number of headers must be at least 2."))
} else {
self.block_timing(args.start, args.end).await
}
}
}

impl CommandContext {
pub async fn block_timing(&self, start: u64, end: Option<u64>) -> Result<(), Error> {
let headers = self.get_chain_headers(start, end).await?;
if !headers.is_empty() {
let headers = headers.into_iter().map(|ch| ch.into_header()).rev().collect::<Vec<_>>();
let (max, min, avg) = BlockHeader::timing_stats(&headers);
println!(
"Timing for blocks #{} - #{}",
headers.first().unwrap().height,
headers.last().unwrap().height
);
println!("Max block time: {}", max);
println!("Min block time: {}", min);
println!("Avg block time: {}", avg);
} else {
println!("No headers found");
}
Ok(())
}
}
63 changes: 63 additions & 0 deletions applications/tari_base_node/src/commands/command/check_db.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
use anyhow::Error;
use async_trait::async_trait;
use clap::Parser;
use tokio::io::{self, AsyncWriteExt};

use super::{CommandContext, HandleCommand};
use crate::LOG_TARGET;

/// Checks the blockchain database for missing blocks and headers
#[derive(Debug, Parser)]
pub struct Args {}

#[async_trait]
impl HandleCommand<Args> for CommandContext {
async fn handle_command(&mut self, _: Args) -> Result<(), Error> {
self.check_db().await
}
}

impl CommandContext {
/// Function to process the check-db command
pub async fn check_db(&mut self) -> Result<(), Error> {
let meta = self.node_service.get_metadata().await?;
let mut height = meta.height_of_longest_chain();
let mut missing_blocks = Vec::new();
let mut missing_headers = Vec::new();
print!("Searching for height: ");
// We need to check every header, but not every block.
let horizon_height = meta.horizon_block(height);
while height > 0 {
print!("{}", height);
io::stdout().flush().await?;
// we can only check till the pruning horizon, 0 is archive node so it needs to check every block.
if height > horizon_height {
match self.node_service.get_block(height).await {
Err(err) => {
// We need to check the data itself, as FetchMatchingBlocks will suppress any error, only
// logging it.
log::error!(target: LOG_TARGET, "{}", err);
missing_blocks.push(height);
},
Ok(Some(_)) => {},
Ok(None) => missing_blocks.push(height),
};
}
height -= 1;
let next_header = self.node_service.get_header(height).await.ok().flatten();
if next_header.is_none() {
// this header is missing, so we stop here and need to ask for this header
missing_headers.push(height);
};
print!("\x1B[{}D\x1B[K", (height + 1).to_string().chars().count());
}
println!("Complete");
for missing_block in missing_blocks {
println!("Missing block at height: {}", missing_block);
}
for missing_header_height in missing_headers {
println!("Missing header at height: {}", missing_header_height)
}
Ok(())
}
}
Loading