Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

kairos-cli: implement call to deposit endpoint and integration test #91

Merged
merged 9 commits into from
May 9, 2024
7 changes: 7 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" }
tokio = { version = "1" }
reqwest = { version = "0.12", features = ["json"] }
serde_json = "1.0"
serde = "1.0"

[dev-dependencies]
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
58 changes: 58 additions & 0 deletions kairos-cli/src/client.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
use kairos_server::routes::PayloadBody;

use reqwest::{Client, 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),
koxu1996 marked this conversation as resolved.
Show resolved Hide resolved
}

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 async fn submit_transaction_request(
client: &Client,
base_url: &Url,
deposit_request: &PayloadBody,
) -> Result<(), KairosClientError> {
let url = base_url.join("/api/v1/deposit").unwrap();
let response = client
.post(url)
.header("Content-Type", "application/json")
.json(deposit_request)
.send()
.await
.map_err(Into::<KairosClientError>::into)?;
let status = response.status();
if !status.is_success() {
Err(KairosClientError::ResponseError(status.to_string()))
} else {
Ok(())
}
koxu1996 marked this conversation as resolved.
Show resolved Hide resolved
}
34 changes: 28 additions & 6 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,31 @@ 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)?;

// TODO: Create transaction and sign it with `signer`.
let client = reqwest::Client::new();
marijanp marked this conversation as resolved.
Show resolved Hide resolved
let public_key = signer.to_public_key()?;

// 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,
};
koxu1996 marked this conversation as resolved.
Show resolved Hide resolved

Ok("ok".to_string())
tokio::runtime::Runtime::new()
.unwrap()
.block_on(client::submit_transaction_request(
&client,
&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,
},
}
40 changes: 40 additions & 0 deletions kairos-cli/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,44 @@
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")]
pub kairos_server_address: Option<Url>,
koxu1996 marked this conversation as resolved.
Show resolved Hide resolved
}

#[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> {
let kairos_server_address =
kairos_server_address.unwrap_or(Url::parse("http://0.0.0.0:9999").unwrap());
koxu1996 marked this conversation as resolved.
Show resolved Hide resolved
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
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