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"
|
path = "src/main.rs"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
clap = { version = "4", features = ["derive"] }
|
clap = { version = "4", features = ["derive", "env"] }
|
||||||
tokio = { version = "1", features = ["full"] }
|
tokio = { version = "1", features = ["full"] }
|
||||||
serde = { version = "1", features = ["derive"] }
|
serde = { version = "1", features = ["derive"] }
|
||||||
serde_json = "1"
|
serde_json = "1"
|
||||||
|
|
|
||||||
|
|
@ -1,25 +1,56 @@
|
||||||
use anyhow::Result;
|
use anyhow::Result;
|
||||||
use clap::Subcommand;
|
use clap::Subcommand;
|
||||||
|
use serde_json::json;
|
||||||
use crate::rpc::Client;
|
use crate::rpc::Client;
|
||||||
|
|
||||||
#[derive(Subcommand)]
|
#[derive(Subcommand)]
|
||||||
pub enum NodeCmd {
|
pub enum NodeCmd {
|
||||||
/// Show chain info
|
/// Show chain info and latest block
|
||||||
Info,
|
Info,
|
||||||
/// Register as validator
|
/// Show a specific block by height
|
||||||
Stake { #[arg(long)] amount: u64 },
|
Block {
|
||||||
/// Unstake from validator set
|
height: u64,
|
||||||
Unstake { #[arg(long)] amount: 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<()> {
|
pub async fn run(cmd: NodeCmd, rpc: &Client) -> Result<()> {
|
||||||
match cmd {
|
match cmd {
|
||||||
NodeCmd::Info => {
|
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)?);
|
println!("{}", serde_json::to_string_pretty(&info)?);
|
||||||
}
|
}
|
||||||
NodeCmd::Stake { amount } => println!("TODO: stake {amount} NUT"),
|
NodeCmd::Block { height } => {
|
||||||
NodeCmd::Unstake { amount } => println!("TODO: unstake {amount} NUT"),
|
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(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,44 +1,68 @@
|
||||||
use anyhow::Result;
|
use anyhow::Result;
|
||||||
use clap::Subcommand;
|
use clap::Subcommand;
|
||||||
|
use serde_json::json;
|
||||||
use crate::rpc::Client;
|
use crate::rpc::Client;
|
||||||
|
|
||||||
#[derive(Subcommand)]
|
#[derive(Subcommand)]
|
||||||
pub enum StoryCmd {
|
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 {
|
Submit {
|
||||||
#[arg(long)] story_id: String,
|
#[arg(long)] story_id: String,
|
||||||
#[arg(long)] parent_id: String,
|
#[arg(long)] parent_id: String,
|
||||||
#[arg(long)] ipfs_hash: String,
|
#[arg(long)] ipfs_hash: String,
|
||||||
},
|
},
|
||||||
/// Register to vote on a node
|
/// Register to vote on a node (stub — Faz 1)
|
||||||
Register {
|
Register {
|
||||||
#[arg(long)] node_id: String,
|
#[arg(long)] node_id: String,
|
||||||
},
|
},
|
||||||
/// Cast a vote
|
/// Cast a vote (stub — Faz 1)
|
||||||
Vote {
|
Vote {
|
||||||
#[arg(long)] node_id: String,
|
#[arg(long)] node_id: String,
|
||||||
#[arg(long)] approve: bool,
|
#[arg(long)] approve: bool,
|
||||||
},
|
},
|
||||||
/// Show story DAG
|
|
||||||
Show {
|
|
||||||
#[arg(long)] story_id: String,
|
|
||||||
},
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn run(cmd: StoryCmd, rpc: &Client) -> Result<()> {
|
pub async fn run(cmd: StoryCmd, rpc: &Client) -> Result<()> {
|
||||||
match cmd {
|
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 } => {
|
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 } => {
|
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 } => {
|
StoryCmd::Vote { node_id, approve } => {
|
||||||
println!("TODO: vote node={node_id} approve={approve}");
|
println!("TODO: vote node={node_id} approve={approve} (Faz 1)");
|
||||||
}
|
|
||||||
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)?);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
|
|
|
||||||
|
|
@ -9,12 +9,12 @@ pub struct Client {
|
||||||
impl Client {
|
impl Client {
|
||||||
pub fn new(endpoint: &str) -> Self {
|
pub fn new(endpoint: &str) -> Self {
|
||||||
Self {
|
Self {
|
||||||
endpoint: endpoint.to_string(),
|
endpoint: format!("{}/rpc", endpoint.trim_end_matches("/rpc")),
|
||||||
http: reqwest::Client::new(),
|
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!({
|
let body = json!({
|
||||||
"jsonrpc": "2.0",
|
"jsonrpc": "2.0",
|
||||||
"method": method,
|
"method": method,
|
||||||
|
|
@ -29,7 +29,7 @@ impl Client {
|
||||||
.json()
|
.json()
|
||||||
.await?;
|
.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);
|
anyhow::bail!("RPC error: {}", err);
|
||||||
}
|
}
|
||||||
Ok(resp["result"].clone())
|
Ok(resp["result"].clone())
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue