feat(cli): implement node block/account cmds; fix params to positional array
This commit is contained in:
parent
f009e50ee8
commit
e0f7fe6bd6
5 changed files with 2368 additions and 26 deletions
2287
Cargo.lock
generated
Normal file
2287
Cargo.lock
generated
Normal file
File diff suppressed because it is too large
Load diff
|
|
@ -8,7 +8,7 @@ name = "nu"
|
|||
path = "src/main.rs"
|
||||
|
||||
[dependencies]
|
||||
clap = { version = "4", features = ["derive"] }
|
||||
clap = { version = "4", features = ["derive", "env"] }
|
||||
tokio = { version = "1", features = ["full"] }
|
||||
serde = { version = "1", features = ["derive"] }
|
||||
serde_json = "1"
|
||||
|
|
|
|||
|
|
@ -1,25 +1,56 @@
|
|||
use anyhow::Result;
|
||||
use clap::Subcommand;
|
||||
use serde_json::json;
|
||||
use crate::rpc::Client;
|
||||
|
||||
#[derive(Subcommand)]
|
||||
pub enum NodeCmd {
|
||||
/// Show chain info
|
||||
/// Show chain info and latest block
|
||||
Info,
|
||||
/// Register as validator
|
||||
Stake { #[arg(long)] amount: u64 },
|
||||
/// Unstake from validator set
|
||||
Unstake { #[arg(long)] amount: u64 },
|
||||
/// Show a specific block by height
|
||||
Block {
|
||||
height: u64,
|
||||
},
|
||||
/// Show account state
|
||||
Account {
|
||||
address: String,
|
||||
},
|
||||
/// Register as validator (stub — Faz 1)
|
||||
Stake {
|
||||
#[arg(long)] amount: u64,
|
||||
},
|
||||
/// Unstake from validator set (stub — Faz 1)
|
||||
Unstake {
|
||||
#[arg(long)] amount: u64,
|
||||
},
|
||||
}
|
||||
|
||||
pub async fn run(cmd: NodeCmd, rpc: &Client) -> Result<()> {
|
||||
match cmd {
|
||||
NodeCmd::Info => {
|
||||
let info = rpc.call("nu_chainInfo", serde_json::json!({})).await?;
|
||||
let info = rpc.call("nu_chainInfo", vec![]).await?;
|
||||
println!("{}", serde_json::to_string_pretty(&info)?);
|
||||
}
|
||||
NodeCmd::Stake { amount } => println!("TODO: stake {amount} NUT"),
|
||||
NodeCmd::Unstake { amount } => println!("TODO: unstake {amount} NUT"),
|
||||
NodeCmd::Block { height } => {
|
||||
let block = rpc.call("nu_getBlock", vec![json!(height)]).await?;
|
||||
if block.is_null() {
|
||||
println!("Block {height} not found");
|
||||
} else {
|
||||
println!("{}", serde_json::to_string_pretty(&block)?);
|
||||
}
|
||||
}
|
||||
NodeCmd::Account { address } => {
|
||||
let account = rpc.call("nu_getAccount", vec![json!(address)]).await?;
|
||||
let balance_shell: u64 = account["balance"].as_u64().unwrap_or(0);
|
||||
let staked_shell: u64 = account["staked"].as_u64().unwrap_or(0);
|
||||
println!("Address : {address}");
|
||||
println!("Balance : {} Shell ({:.4} NU)", balance_shell, balance_shell as f64 / 100_000.0);
|
||||
println!("Staked : {} Shell ({:.4} NU)", staked_shell, staked_shell as f64 / 100_000.0);
|
||||
println!("Nonce : {}", account["nonce"].as_u64().unwrap_or(0));
|
||||
println!("PoN : {}", account["pon_score"].as_f64().unwrap_or(1.0));
|
||||
}
|
||||
NodeCmd::Stake { amount } => println!("TODO: stake {amount} Shell (Faz 1)"),
|
||||
NodeCmd::Unstake { amount } => println!("TODO: unstake {amount} Shell (Faz 1)"),
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,44 +1,68 @@
|
|||
use anyhow::Result;
|
||||
use clap::Subcommand;
|
||||
use serde_json::json;
|
||||
use crate::rpc::Client;
|
||||
|
||||
#[derive(Subcommand)]
|
||||
pub enum StoryCmd {
|
||||
/// Submit a new story node
|
||||
/// Show story DAG with all nodes
|
||||
Show {
|
||||
#[arg(long)] story_id: String,
|
||||
},
|
||||
/// Show a specific node
|
||||
Node {
|
||||
#[arg(long)] node_id: String,
|
||||
},
|
||||
/// List all stories
|
||||
List {
|
||||
#[arg(long, default_value = "1")] page: u64,
|
||||
#[arg(long, default_value = "10")] per_page: u64,
|
||||
},
|
||||
/// List nodes currently open for voting
|
||||
Pending,
|
||||
/// Submit a new story node (stub — requires wallet integration)
|
||||
Submit {
|
||||
#[arg(long)] story_id: String,
|
||||
#[arg(long)] parent_id: String,
|
||||
#[arg(long)] ipfs_hash: String,
|
||||
},
|
||||
/// Register to vote on a node
|
||||
/// Register to vote on a node (stub — Faz 1)
|
||||
Register {
|
||||
#[arg(long)] node_id: String,
|
||||
},
|
||||
/// Cast a vote
|
||||
/// Cast a vote (stub — Faz 1)
|
||||
Vote {
|
||||
#[arg(long)] node_id: String,
|
||||
#[arg(long)] approve: bool,
|
||||
},
|
||||
/// Show story DAG
|
||||
Show {
|
||||
#[arg(long)] story_id: String,
|
||||
},
|
||||
}
|
||||
|
||||
pub async fn run(cmd: StoryCmd, rpc: &Client) -> Result<()> {
|
||||
match cmd {
|
||||
StoryCmd::Show { story_id } => {
|
||||
let result = rpc.call("nu_getStory", vec![json!(story_id)]).await?;
|
||||
println!("{}", serde_json::to_string_pretty(&result)?);
|
||||
}
|
||||
StoryCmd::Node { node_id } => {
|
||||
let result = rpc.call("nu_getNode", vec![json!(node_id)]).await?;
|
||||
println!("{}", serde_json::to_string_pretty(&result)?);
|
||||
}
|
||||
StoryCmd::List { page, per_page } => {
|
||||
let result = rpc.call("nu_listStories", vec![json!(page), json!(per_page)]).await?;
|
||||
println!("{}", serde_json::to_string_pretty(&result)?);
|
||||
}
|
||||
StoryCmd::Pending => {
|
||||
let result = rpc.call("nu_listPendingVotes", vec![]).await?;
|
||||
println!("{}", serde_json::to_string_pretty(&result)?);
|
||||
}
|
||||
StoryCmd::Submit { story_id, parent_id, ipfs_hash } => {
|
||||
println!("TODO: submit node story={story_id} parent={parent_id} hash={ipfs_hash}");
|
||||
println!("TODO: submit node story={story_id} parent={parent_id} hash={ipfs_hash} (requires wallet — Faz 1)");
|
||||
}
|
||||
StoryCmd::Register { node_id } => {
|
||||
println!("TODO: register vote for node={node_id}");
|
||||
println!("TODO: register vote for node={node_id} (Faz 1)");
|
||||
}
|
||||
StoryCmd::Vote { node_id, approve } => {
|
||||
println!("TODO: vote node={node_id} approve={approve}");
|
||||
}
|
||||
StoryCmd::Show { story_id } => {
|
||||
let result = rpc.call("nu_getStory", serde_json::json!({ "story_id": story_id })).await?;
|
||||
println!("{}", serde_json::to_string_pretty(&result)?);
|
||||
println!("TODO: vote node={node_id} approve={approve} (Faz 1)");
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
|
|
|
|||
|
|
@ -9,12 +9,12 @@ pub struct Client {
|
|||
impl Client {
|
||||
pub fn new(endpoint: &str) -> Self {
|
||||
Self {
|
||||
endpoint: endpoint.to_string(),
|
||||
endpoint: format!("{}/rpc", endpoint.trim_end_matches("/rpc")),
|
||||
http: reqwest::Client::new(),
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn call(&self, method: &str, params: Value) -> Result<Value> {
|
||||
pub async fn call(&self, method: &str, params: Vec<Value>) -> Result<Value> {
|
||||
let body = json!({
|
||||
"jsonrpc": "2.0",
|
||||
"method": method,
|
||||
|
|
@ -29,7 +29,7 @@ impl Client {
|
|||
.json()
|
||||
.await?;
|
||||
|
||||
if let Some(err) = resp.get("error") {
|
||||
if let Some(err) = resp.get("error").filter(|e| !e.is_null()) {
|
||||
anyhow::bail!("RPC error: {}", err);
|
||||
}
|
||||
Ok(resp["result"].clone())
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue