|
| 1 | +use std::error; |
| 2 | + |
| 3 | +use clap::{Parser, Subcommand}; |
| 4 | +use jsonrpc_core::futures; |
| 5 | +use spacesvm::{ |
| 6 | + api::{ |
| 7 | + client::{claim_tx, delete_tx, get_or_create_pk, set_tx, Client, Uri}, |
| 8 | + DecodeTxArgs, IssueTxArgs, ResolveArgs, |
| 9 | + }, |
| 10 | + chain::tx::{decoder, unsigned::TransactionData}, |
| 11 | +}; |
| 12 | + |
| 13 | +#[derive(Subcommand, Debug)] |
| 14 | +enum Command { |
| 15 | + Claim { |
| 16 | + space: String, |
| 17 | + }, |
| 18 | + Set { |
| 19 | + space: String, |
| 20 | + key: String, |
| 21 | + value: String, |
| 22 | + }, |
| 23 | + Delete { |
| 24 | + space: String, |
| 25 | + key: String, |
| 26 | + }, |
| 27 | + Get { |
| 28 | + space: String, |
| 29 | + key: String, |
| 30 | + }, |
| 31 | + Ping {}, |
| 32 | +} |
| 33 | + |
| 34 | +#[derive(Parser)] |
| 35 | +#[command(version, about, long_about = None)] |
| 36 | +struct Cli { |
| 37 | + /// Endpoint for RPC calls. |
| 38 | + #[clap(long)] |
| 39 | + endpoint: String, |
| 40 | + |
| 41 | + /// Private key file. |
| 42 | + #[clap(long, default_value = ".spacesvm-cli-pk")] |
| 43 | + private_key_file: String, |
| 44 | + |
| 45 | + /// Which subcommand to call. |
| 46 | + #[command(subcommand)] |
| 47 | + command: Command, |
| 48 | +} |
| 49 | + |
| 50 | +#[tokio::main] |
| 51 | +async fn main() -> Result<(), Box<dyn error::Error>> { |
| 52 | + let cli = Cli::parse(); |
| 53 | + |
| 54 | + let secret_key = get_or_create_pk(&cli.private_key_file)?; |
| 55 | + let uri = cli.endpoint.parse::<Uri>()?; |
| 56 | + let mut client = Client::new(uri); |
| 57 | + |
| 58 | + if let Command::Get { space, key } = &cli.command { |
| 59 | + let resp = futures::executor::block_on(client.resolve(ResolveArgs { |
| 60 | + space: space.as_bytes().to_vec(), |
| 61 | + key: key.as_bytes().to_vec(), |
| 62 | + })) |
| 63 | + .map_err(|e| e.to_string())?; |
| 64 | + log::debug!("resolve response: {:?}", resp); |
| 65 | + |
| 66 | + println!("{}", serde_json::to_string(&resp)?); |
| 67 | + return Ok(()); |
| 68 | + } |
| 69 | + |
| 70 | + if let Command::Ping {} = &cli.command { |
| 71 | + let resp = futures::executor::block_on(client.ping()).map_err(|e| e.to_string())?; |
| 72 | + |
| 73 | + println!("{}", serde_json::to_string(&resp)?); |
| 74 | + return Ok(()); |
| 75 | + } |
| 76 | + |
| 77 | + // decode tx |
| 78 | + let tx_data = command_to_tx(cli.command)?; |
| 79 | + let resp = futures::executor::block_on(client.decode_tx(DecodeTxArgs { tx_data })) |
| 80 | + .map_err(|e| e.to_string())?; |
| 81 | + |
| 82 | + let typed_data = &resp.typed_data; |
| 83 | + |
| 84 | + // create signature |
| 85 | + let dh = decoder::hash_structured_data(typed_data)?; |
| 86 | + let sig = secret_key.sign_digest(&dh.as_bytes())?; |
| 87 | + |
| 88 | + // issue tx |
| 89 | + let resp = futures::executor::block_on(client.issue_tx(IssueTxArgs { |
| 90 | + typed_data: resp.typed_data, |
| 91 | + signature: sig.to_bytes().to_vec(), |
| 92 | + })) |
| 93 | + .map_err(|e| e.to_string())?; |
| 94 | + println!("{}", serde_json::to_string(&resp)?); |
| 95 | + |
| 96 | + Ok(()) |
| 97 | +} |
| 98 | + |
| 99 | +/// Takes a TX command and returns transaction data. |
| 100 | +fn command_to_tx(command: Command) -> std::io::Result<TransactionData> { |
| 101 | + match command { |
| 102 | + Command::Claim { space } => Ok(claim_tx(space)), |
| 103 | + Command::Set { space, key, value } => Ok(set_tx(space, key, value.as_bytes().to_vec())), |
| 104 | + Command::Delete { space, key } => Ok(delete_tx(space, key)), |
| 105 | + _ => Err(std::io::Error::new( |
| 106 | + std::io::ErrorKind::Other, |
| 107 | + "not a supported tx", |
| 108 | + )), |
| 109 | + } |
| 110 | +} |
0 commit comments