Skip to content

Commit

Permalink
Merge pull request #91 from cspr-rad/implement-deposit-cli-3
Browse files Browse the repository at this point in the history
kairos-cli: implement call to deposit endpoint and integration test
  • Loading branch information
marijanp authored May 9, 2024
2 parents 27ef3fc + 1da5208 commit 9ac33ec
Show file tree
Hide file tree
Showing 11 changed files with 164 additions and 53 deletions.
8 changes: 8 additions & 0 deletions Cargo.lock

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

7 changes: 7 additions & 0 deletions kairos-cli/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,14 @@ clap = { version = "4.5", features = ["derive", "deprecated"] }
hex = "0.4"
thiserror = "1"
kairos-crypto = { path = "../kairos-crypto", features = ["fs"] }
kairos-tx = { path = "../kairos-tx" }
kairos-server = { path = "../kairos-server" }
reqwest = { version = "0.12", features = ["blocking", "json"] }
serde_json = "1.0"
serde = "1.0"

[dev-dependencies]
tokio = { version = "1" }
assert_cmd = "2"
predicates = "3"
kairos-test-utils = { path = "../kairos-test-utils" }
22 changes: 3 additions & 19 deletions kairos-cli/bin/main.rs
Original file line number Diff line number Diff line change
@@ -1,25 +1,9 @@
use std::process;

use clap::Parser;
use kairos_cli::commands::{self, Command};

#[derive(Parser)]
#[command(name = "Kairos Client", about = "CLI for interacting with Kairos")]
struct Cli {
#[command(subcommand)]
command: Command,
}
use std::process;

fn main() {
let cli = Cli::parse();

let result = match cli.command {
Command::Deposit(args) => commands::deposit::run(args),
Command::Transfer(args) => commands::transfer::run(args),
Command::Withdraw(args) => commands::withdraw::run(args),
};

match result {
let args = kairos_cli::Cli::parse();
match kairos_cli::run(args) {
Ok(output) => {
println!("{}", output)
}
Expand Down
57 changes: 57 additions & 0 deletions kairos-cli/src/client.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
use kairos_server::routes::PayloadBody;

use reqwest::{blocking, Url};
use serde::{Deserialize, Serialize};
use std::fmt;

#[derive(PartialOrd, Ord, PartialEq, Eq, Debug, Serialize, Deserialize)]
pub enum KairosClientError {
ResponseError(String),
ResponseErrorWithCode(u16, String),
DecodeError(String),
KairosServerError(String),
}

impl std::error::Error for KairosClientError {}

impl fmt::Display for KairosClientError {
fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
let json_string = serde_json::to_string(self).map_err(|_| fmt::Error)?;
write!(formatter, "{}", json_string)
}
}
impl From<reqwest::Error> for KairosClientError {
fn from(error: reqwest::Error) -> Self {
let error_without_url = error.without_url();
if error_without_url.is_decode() {
KairosClientError::DecodeError(error_without_url.to_string())
} else {
match error_without_url.status() {
Option::None => Self::ResponseError(error_without_url.to_string()),
Option::Some(status_code) => {
Self::ResponseErrorWithCode(status_code.as_u16(), error_without_url.to_string())
}
}
}
}
}

pub fn submit_transaction_request(
base_url: &Url,
deposit_request: &PayloadBody,
) -> Result<(), KairosClientError> {
let client = blocking::Client::new();
let url = base_url.join("/api/v1/deposit").unwrap();
let response = client
.post(url)
.header("Content-Type", "application/json")
.json(deposit_request)
.send()
.map_err(Into::<KairosClientError>::into)?;
let status = response.status();
if !status.is_success() {
Err(KairosClientError::KairosServerError(status.to_string()))
} else {
Ok(())
}
}
28 changes: 21 additions & 7 deletions kairos-cli/src/commands/deposit.rs
Original file line number Diff line number Diff line change
@@ -1,9 +1,14 @@
use crate::client;
use crate::common::args::{AmountArg, PrivateKeyPathArg};
use crate::error::CliError;

use reqwest::Url;

use kairos_crypto::error::CryptoError;
use kairos_crypto::implementations::Signer;
use kairos_crypto::CryptoSigner;
use kairos_server::routes::PayloadBody;
use kairos_tx::asn::SigningPayload;

use clap::Parser;

Expand All @@ -15,14 +20,23 @@ pub struct Args {
private_key_path: PrivateKeyPathArg,
}

pub fn run(args: Args) -> Result<String, CliError> {
let _amount: u64 = args.amount.field;
let _signer =
pub fn run(args: Args, kairos_server_address: Url) -> Result<String, CliError> {
let amount: u64 = args.amount.field;
let signer =
Signer::from_private_key_file(args.private_key_path.field).map_err(CryptoError::from)?;
let public_key = signer.to_public_key()?;

// TODO: Create transaction and sign it with `signer`.

// TODO: Send transaction to the network, using Rust SDK.
let payload = SigningPayload::new_deposit(amount)
.try_into()
.expect("Failed serialize the deposit payload to bytes");
let signature = signer.sign(&payload)?;
let deposit_request = PayloadBody {
public_key,
payload,
signature,
};

Ok("ok".to_string())
client::submit_transaction_request(&kairos_server_address, &deposit_request)
.map_err(Into::<CliError>::into)
.map(|_| "ok".to_string())
}
12 changes: 0 additions & 12 deletions kairos-cli/src/commands/mod.rs
Original file line number Diff line number Diff line change
@@ -1,15 +1,3 @@
pub mod deposit;
pub mod transfer;
pub mod withdraw;

use clap::Subcommand;

#[derive(Subcommand)]
pub enum Command {
#[command(about = "Deposits funds into your account")]
Deposit(deposit::Args),
#[command(about = "Transfers funds to another account")]
Transfer(transfer::Args),
#[command(about = "Withdraws funds from your account")]
Withdraw(withdraw::Args),
}
11 changes: 9 additions & 2 deletions kairos-cli/src/error.rs
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
use crate::client::KairosClientError;
use kairos_crypto::error::CryptoError;

use hex::FromHexError;
use thiserror::Error;

use kairos_crypto::error::CryptoError;

#[derive(Error, Debug)]
pub enum CliError {
/// Cryptography error.
Expand All @@ -17,4 +18,10 @@ pub enum CliError {
#[from]
error: FromHexError,
},
/// Kairos HTTP client error
#[error("http client error: {error}")]
KairosClientError {
#[from]
error: KairosClientError,
},
}
38 changes: 38 additions & 0 deletions kairos-cli/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,42 @@
pub mod client;
pub mod commands;
pub mod common;
pub mod error;
pub mod utils;

use crate::error::CliError;

use clap::{Parser, Subcommand};
use reqwest::Url;

#[derive(Parser)]
#[command(name = "Kairos Client", about = "CLI for interacting with Kairos")]
pub struct Cli {
#[command(subcommand)]
pub command: Command,
#[arg(long, value_name = "URL", default_value = "http://0.0.0.0:9999")]
pub kairos_server_address: Url,
}

#[derive(Subcommand)]
pub enum Command {
#[command(about = "Deposits funds into your account")]
Deposit(commands::deposit::Args),
#[command(about = "Transfers funds to another account")]
Transfer(commands::transfer::Args),
#[command(about = "Withdraws funds from your account")]
Withdraw(commands::withdraw::Args),
}

pub fn run(
Cli {
command,
kairos_server_address,
}: Cli,
) -> Result<String, CliError> {
match command {
Command::Deposit(args) => commands::deposit::run(args, kairos_server_address),
Command::Transfer(args) => commands::transfer::run(args),
Command::Withdraw(args) => commands::withdraw::run(args),
}
}
28 changes: 18 additions & 10 deletions kairos-cli/tests/cli_tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,17 +8,25 @@ fn fixture_path(relative_path: &str) -> PathBuf {
path
}

#[test]
fn deposit_successful_with_ed25519() {
let secret_key_path = fixture_path("ed25519/secret_key.pem");
#[tokio::test]
async fn deposit_successful_with_ed25519() {
let kairos = kairos_test_utils::kairos::Kairos::run().await.unwrap();

let mut cmd = Command::cargo_bin("kairos-cli").unwrap();
cmd.arg("deposit")
.arg("--amount")
.arg("123")
.arg("--private-key")
.arg(secret_key_path);
cmd.assert().success().stdout("ok\n");
tokio::task::spawn_blocking(move || {
let secret_key_path = fixture_path("ed25519/secret_key.pem");

let mut cmd = Command::cargo_bin("kairos-cli").unwrap();
cmd.arg("--kairos-server-address")
.arg(kairos.url.as_str())
.arg("deposit")
.arg("--amount")
.arg("123")
.arg("--private-key")
.arg(secret_key_path);
cmd.assert().success().stdout("ok\n");
})
.await
.unwrap();
}

#[test]
Expand Down
2 changes: 1 addition & 1 deletion kairos-server/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ pub async fn run(config: ServerConfig) {

let listener = tokio::net::TcpListener::bind(config.socket_addr)
.await
.unwrap();
.unwrap_or_else(|err| panic!("Failed to bind to address {}: {}", config.socket_addr, err));
tracing::info!("listening on `{}`", listener.local_addr().unwrap());

axum::serve(listener, app)
Expand Down
4 changes: 2 additions & 2 deletions nixos/tests/end-to-end.nix
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,7 @@ nixosTest {
client.succeed("curl --fail-with-body -X POST http://kairos/api/v1/withdraw -H 'Content-Type: application/json' -d '{}'".format(json.dumps(withdraw_request)))
# CLI with ed25519
cli_output = client.succeed("kairos-cli deposit --amount 1000 --private-key ${testResources}/ed25519/secret_key.pem")
cli_output = client.succeed("kairos-cli --kairos-server-address http://kairos deposit --amount 1000 --private-key ${testResources}/ed25519/secret_key.pem")
assert "ok\n" in cli_output
cli_output = client.succeed("kairos-cli transfer --recipient '01a26419a7d82b2263deaedea32d35eee8ae1c850bd477f62a82939f06e80df356' --amount 1000 --private-key ${testResources}/ed25519/secret_key.pem")
Expand All @@ -86,7 +86,7 @@ nixosTest {
assert "ok\n" in cli_output
# CLI with secp256k1
cli_output = client.succeed("kairos-cli deposit --amount 1000 --private-key ${testResources}/secp256k1/secret_key.pem")
cli_output = client.succeed("kairos-cli --kairos-server-address http://kairos deposit --amount 1000 --private-key ${testResources}/secp256k1/secret_key.pem")
assert "ok\n" in cli_output
cli_output = client.succeed("kairos-cli transfer --recipient '01a26419a7d82b2263deaedea32d35eee8ae1c850bd477f62a82939f06e80df356' --amount 1000 --private-key ${testResources}/secp256k1/secret_key.pem")
Expand Down

0 comments on commit 9ac33ec

Please sign in to comment.