Skip to content

Commit

Permalink
Implement Commands of Massa private-api in the client (and performs…
Browse files Browse the repository at this point in the history
… btw few refactoring)

- `Commands.run()` now return a `String` (`cli_help()` have been rewritten for the occasion)
- Staking addresses related endpoints have been renamed to match commands
- Massa `start_node` command is now implemented outside of the API
  • Loading branch information
yvan-sraka committed Oct 5, 2021
1 parent 05d1125 commit 008f769
Show file tree
Hide file tree
Showing 5 changed files with 182 additions and 88 deletions.
39 changes: 20 additions & 19 deletions api-private/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -29,11 +29,6 @@ pub struct ApiMassaPrivate {
/// Private Massa-RPC "manager mode" endpoints
#[rpc(server)]
pub trait MassaPrivate {
/// Starts the node and waits for node to start.
/// Signals if the node is already running.
#[rpc(name = "start_node")]
fn start_node(&self) -> Result<(), PrivateApiError>;

/// Gracefully stop the node.
#[rpc(name = "stop_node")]
fn stop_node(&self) -> BoxFuture<Result<(), PrivateApiError>>;
Expand All @@ -48,17 +43,20 @@ pub trait MassaPrivate {

/// Add a vec of new private keys for the node to use to stake.
/// No confirmation to expect.
#[rpc(name = "add_staking_keys")]
fn add_staking_keys(&self, _: Vec<PrivateKey>) -> BoxFuture<Result<(), PrivateApiError>>;
#[rpc(name = "add_staking_private_keys")]
fn add_staking_private_keys(
&self,
_: Vec<PrivateKey>,
) -> BoxFuture<Result<(), PrivateApiError>>;

/// Remove a vec of addresses used to stake.
/// No confirmation to expect.
#[rpc(name = "remove_staking_keys")]
fn remove_staking_keys(&self, _: Vec<Address>) -> BoxFuture<Result<(), PrivateApiError>>;
#[rpc(name = "remove_staking_addresses")]
fn remove_staking_addresses(&self, _: Vec<Address>) -> BoxFuture<Result<(), PrivateApiError>>;

/// Return hashset of staking addresses.
#[rpc(name = "list_staking_keys")]
fn list_staking_keys(&self) -> BoxFuture<Result<AddressHashSet, PrivateApiError>>;
#[rpc(name = "get_staking_addresses")]
fn get_staking_addresses(&self) -> BoxFuture<Result<AddressHashSet, PrivateApiError>>;

/// Bans given node id
/// No confirmation to expect.
Expand Down Expand Up @@ -109,10 +107,6 @@ impl ApiMassaPrivate {
}

impl MassaPrivate for ApiMassaPrivate {
fn start_node(&self) -> Result<(), PrivateApiError> {
todo!()
}

fn stop_node(&self) -> BoxFuture<Result<(), PrivateApiError>> {
let stop = self.stop_node_channel.clone();
let closure = async move || {
Expand All @@ -134,13 +128,19 @@ impl MassaPrivate for ApiMassaPrivate {
Box::pin(closure())
}

fn add_staking_keys(&self, keys: Vec<PrivateKey>) -> BoxFuture<Result<(), PrivateApiError>> {
fn add_staking_private_keys(
&self,
keys: Vec<PrivateKey>,
) -> BoxFuture<Result<(), PrivateApiError>> {
let cmd_sender = self.consensus_command_sender.clone();
let closure = async move || Ok(cmd_sender.register_staking_private_keys(keys).await?);
Box::pin(closure())
}

fn remove_staking_keys(&self, keys: Vec<Address>) -> BoxFuture<Result<(), PrivateApiError>> {
fn remove_staking_addresses(
&self,
keys: Vec<Address>,
) -> BoxFuture<Result<(), PrivateApiError>> {
let cmd_sender = self.consensus_command_sender.clone();
let closure = async move || {
Ok(cmd_sender
Expand All @@ -150,7 +150,7 @@ impl MassaPrivate for ApiMassaPrivate {
Box::pin(closure())
}

fn list_staking_keys(&self) -> BoxFuture<Result<AddressHashSet, PrivateApiError>> {
fn get_staking_addresses(&self) -> BoxFuture<Result<AddressHashSet, PrivateApiError>> {
let cmd_sender = self.consensus_command_sender.clone();
let closure = async move || Ok(cmd_sender.get_staking_addresses().await?);
Box::pin(closure())
Expand All @@ -166,8 +166,9 @@ impl MassaPrivate for ApiMassaPrivate {
let network_command_sender = self.network_command_sender.clone();
let closure = async move || {
for ip in ips {
Ok(network_command_sender.unban(ip).await?)
network_command_sender.unban(ip).await?
}
Ok(())
};
Box::pin(closure())
}
Expand Down
182 changes: 133 additions & 49 deletions rpc-client/src/cmds.rs
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,9 @@ pub enum Command {
)]
ban,

#[strum(ascii_case_insensitive, message = "start a node")]
node_start,

#[strum(ascii_case_insensitive, message = "stops the node")]
node_stop,

Expand Down Expand Up @@ -127,16 +130,30 @@ pub enum Command {
send_transaction,
}

macro_rules! repl_error {
macro_rules! repl_err {
($err: expr) => {
style(format!("Error: {}", $err)).red().to_string()
};
}

macro_rules! repl_ok {
($ok: expr) => {
$ok.to_string()
};
}

// TODO: Commands could also not be APIs calls (like Wallet ones)
impl Command {
pub(crate) fn not_found() -> String {
repl_error!("Command not found!\ntype \"help\" to get the list of commands")
repl_err!("Command not found!\ntype \"help\" to get the list of commands")
}

pub(crate) fn wrong_parameters(&self) -> String {
repl_err!(format!(
"{} given is not well formed...\ntype \"help {}\" to more info",
self.get_str("args").unwrap(),
self.to_string()
))
}

pub(crate) fn help(&self) -> String {
Expand All @@ -153,64 +170,131 @@ impl Command {
)
}

// TODO: should run(...) be impl on Command or on some struct containing clients?
pub(crate) async fn run(&self, client: &Client, parameters: &Vec<String>, json: bool) {
// TODO: Return type should be something like:
// use std::fmt::Display;
// use serde_json::ser::Formatter;
// pub(crate) async fn run<T: Display + Formatter>(&self, client: &Client, parameters: &Vec<String>) -> T
pub(crate) async fn run(&self, client: &Client, parameters: &Vec<String>) -> String {
match self {
Command::exit => process::exit(0),

Command::help => {
if !parameters.is_empty() {
if let Ok(c) = parameters[0].parse::<Command>() {
println!("{}", c.help());
c.help()
} else {
println!("{}", Command::not_found());
Command::not_found()
}
} else {
cli_help();
format!(
"HELP of Massa client (list of available commands):\n{}",
Command::iter()
.map(|c| c.help())
.collect::<Vec<String>>()
.join("\n")
)
}
}
Command::unban => println!(
"{}",
// TODO: (de)serialize input/output from/to JSON with serde should be less verbose
match IpAddr::from_str(&parameters[0]) {
Ok(ip) => match &client.private.unban(&vec![ip]).await {
Ok(output) =>
if json {
serde_json::to_string(output)
.expect("Failed to serialized command output ...")
} else {
"IP successfully unbanned!".to_string()
},
Err(e) => repl_error!(e),
},
Err(_) => repl_error!(
"IP given is not well formed...\ntype \"help unban\" to more info"
),

Command::unban => match IpAddr::from_str(&parameters[0]) {
Ok(ip) => match &client.private.unban(&vec![ip]).await {
Ok(_) => repl_ok!("Request of unbanning successfully sent!"),
Err(e) => repl_err!(e),
},
Err(_) => self.wrong_parameters(),
},

Command::ban => match serde_json::from_str(&parameters[0]) {
Ok(node_id) => match &client.private.ban(node_id).await {
Ok(_) => repl_ok!("Request of banning successfully sent!"),
Err(e) => repl_err!(e),
},
Err(_) => self.wrong_parameters(),
},

Command::node_start => match process::Command::new("massa-node").spawn() {
Ok(_) => repl_ok!("Node successfully started!"),
Err(e) => repl_err!(e),
},

Command::node_stop => match &client.private.stop_node().await {
Ok(_) => repl_ok!("Request of stopping the Node successfully sent"),
Err(e) => repl_err!(e),
},

Command::node_get_staking_addresses => {
match &client.private.get_staking_addresses().await {
Ok(output) => serde_json::to_string(output)
.expect("Failed to serialized command output ..."),
Err(e) => repl_err!(e),
}
),
Command::ban => {}
Command::node_stop => {}
Command::node_get_staking_addresses => {}
Command::node_remove_staking_addresses => {}
Command::node_add_staking_private_keys => {}
Command::node_testnet_rewards_program_ownership_proof => {}
Command::get_status => {}
Command::get_addresses_info => {}
Command::get_blocks_info => {}
Command::get_endorsements_info => {}
Command::get_operations_info => {}
Command::wallet_info => {}
Command::wallet_add_private_keys => {}
Command::wallet_remove_addresses => {}
Command::buy_rolls => {}
Command::sell_rolls => {}
Command::send_transaction => {}
}
}
}
}

Command::node_remove_staking_addresses => match serde_json::from_str(&parameters[0]) {
Ok(addresses) => match &client.private.remove_staking_addresses(addresses).await {
Ok(_) => repl_ok!("Addresses successfully removed!"),
Err(e) => repl_err!(e),
},
Err(_) => self.wrong_parameters(),
},

Command::node_add_staking_private_keys => match serde_json::from_str(&parameters[0]) {
Ok(private_keys) => {
match &client.private.add_staking_private_keys(private_keys).await {
Ok(_) => repl_ok!("Private keys successfully added!"),
Err(e) => repl_err!(e),
}
}
Err(_) => self.wrong_parameters(),
},

Command::node_testnet_rewards_program_ownership_proof => {
todo!()
}

Command::get_status => {
todo!()
}

Command::get_addresses_info => {
todo!()
}

Command::get_blocks_info => {
todo!()
}

Command::get_endorsements_info => {
todo!()
}

Command::get_operations_info => {
todo!()
}

fn cli_help() {
println!("HELP of Massa client (list of available commands):");
for c in Command::iter() {
println!("{}", c.help());
Command::wallet_info => {
todo!()
}

Command::wallet_add_private_keys => {
todo!()
}

Command::wallet_remove_addresses => {
todo!()
}

Command::buy_rolls => {
todo!()
}

Command::sell_rolls => {
todo!()
}

Command::send_transaction => {
todo!()
}
}
}
}
11 changes: 10 additions & 1 deletion rpc-client/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,16 @@ fn main(args: Args) {
if atty::is(Stream::Stdout) && args.command == Command::help {
repl::run(&client).await; // Interactive mode
} else {
args.command.run(&client, &args.parameters, args.json).await; // Non-Interactive mode
let output = args.command.run(&client, &args.parameters).await; // Non-Interactive mode
println!(
"{}",
if args.json {
serde_json::to_string(&output)
.expect("Failed to serialized command output ...")
} else {
output
}
);
}
});
}
11 changes: 7 additions & 4 deletions rpc-client/src/repl.rs
Original file line number Diff line number Diff line change
Expand Up @@ -38,10 +38,13 @@ pub(crate) async fn run(client: &Client) {
let cmd: Result<Command, ParseError> = input[0].parse();
let parameters = input[1..].to_vec();
// Print result of evaluated command
match cmd {
Ok(command) => command.run(client, &parameters, false).await,
Err(_) => println!("{}", Command::not_found()),
}
println!(
"{}",
match cmd {
Ok(command) => command.run(client, &parameters).await,
Err(_) => Command::not_found(),
}
);
}
}
}
Expand Down
Loading

0 comments on commit 008f769

Please sign in to comment.