Skip to content

Commit

Permalink
Merge branch 'main' into feature/cli-wasm-discovery
Browse files Browse the repository at this point in the history
  • Loading branch information
koxu1996 authored Jul 25, 2024
2 parents cf2bfae + 7b5f29d commit 2f53b57
Show file tree
Hide file tree
Showing 10 changed files with 135 additions and 53 deletions.
2 changes: 2 additions & 0 deletions Cargo.lock

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

4 changes: 3 additions & 1 deletion kairos-cli/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ default = ["demo", "database"]
all-tests = ["cctl-tests", "database"]
cctl-tests = []
demo = ["dep:kairos-test-utils", "dep:tokio", "dep:dotenvy"]
database = ["kairos-server/database", "kairos-test-utils/database"]
database = ["dep:kairos-data", "dep:chrono", "kairos-server/database", "kairos-test-utils/database"]

[dependencies]
dotenvy = { version = "0.15", optional = true }
Expand All @@ -27,9 +27,11 @@ tracing-subscriber = { version = "0.3", features = ["std", "env-filter"] }
casper-client.workspace = true
casper-client-types = { workspace = true, features = ["std"] } # TODO: Change `std` -> `std-fs-io` in the future version.
clap = { version = "4", features = ["derive", "deprecated"] }
chrono = { version = "0.4", optional = true }
hex = "0.4"
thiserror = "1"
kairos-crypto = { path = "../kairos-crypto", features = [ "std", "fs" ] }
kairos-data = { path = "../kairos-data", optional = true }
kairos-tx = { path = "../kairos-tx" }
kairos-server = { path = "../kairos-server" }
axum-extra = { version = "0.9", features = [ "typed-routing" ] }
Expand Down
26 changes: 26 additions & 0 deletions kairos-cli/src/client.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,11 @@ use reqwest::Url;
use serde::{Deserialize, Serialize};
use std::fmt;

#[cfg(feature = "database")]
use kairos_data::transaction::{TransactionFilter, Transactions};
#[cfg(feature = "database")]
use kairos_server::routes::fetch::QueryTransactionsPath;

// max amount allowed to be used on gas fees
pub const MAX_GAS_FEE_PAYMENT_AMOUNT: u64 = 1_000_000_000_000;

Expand Down Expand Up @@ -121,6 +126,27 @@ pub fn contract_hash(base_url: &Url) -> Result<ContractHash, KairosClientError>
}
}

#[cfg(feature = "database")]
pub fn fetch(
base_url: &Url,
transaction_filter: &TransactionFilter,
) -> Result<Vec<Transactions>, KairosClientError> {
let response = reqwest::blocking::Client::new()
.post(base_url.join(QueryTransactionsPath::PATH).unwrap())
.header("Content-Type", "application/json")
.json(&transaction_filter)
.send()
.map_err(KairosClientError::from)?
.error_for_status();

match response {
Err(err) => Err(KairosClientError::from(err)),
Ok(response) => response
.json::<Vec<Transactions>>()
.map_err(KairosClientError::from),
}
}

pub fn get_chain_name(base_url: &Url) -> Result<String, KairosClientError> {
let response = reqwest::blocking::Client::new()
.get(base_url.join(GetChainNamePath::PATH).unwrap())
Expand Down
69 changes: 69 additions & 0 deletions kairos-cli/src/commands/fetch.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
use clap::{Parser, ValueEnum};
use reqwest::Url;

use crate::client;
use crate::error::CliError;
use kairos_data::transaction::{Transaction, TransactionFilter};

use chrono::NaiveDateTime;

#[derive(Parser, Debug)]
pub struct Args {
#[arg(long, short, value_name = "PUBLIC_KEY_HEX")]
sender: Option<String>,
#[arg(long, short, value_name = "ISO8601_TIMESTAMP")]
min_timestamp: Option<NaiveDateTime>,
#[arg(long, short, value_name = "ISO8601_TIMESTAMP")]
max_timestamp: Option<NaiveDateTime>,
#[arg(long, short, value_name = "NUM_MOTES")]
min_amount: Option<u64>,
#[arg(long, short, value_name = "NUM_MOTES")]
max_amount: Option<u64>,
#[arg(long, short, value_name = "PUBLIC_KEY_HEX")]
recipient: Option<String>,
#[arg(long, short, value_name = "TRANSACTION_TYPE", value_enum)]
transaction_type: Option<TransactionType>,
}

#[derive(ValueEnum, Debug, Clone)] // ArgEnum here
pub enum TransactionType {
Deposit,
Transfer,
Withdrawal,
}

impl From<TransactionType> for Transaction {
fn from(t: TransactionType) -> Transaction {
match t {
TransactionType::Deposit => Transaction::Deposit,
TransactionType::Transfer => Transaction::Transfer,
TransactionType::Withdrawal => Transaction::Withdrawal,
}
}
}

pub fn run(
Args {
sender,
min_timestamp,
max_timestamp,
min_amount,
max_amount,
recipient,
transaction_type,
}: Args,
kairos_server_address: Url,
) -> Result<String, CliError> {
let transaction_filter = TransactionFilter {
sender,
min_timestamp,
max_timestamp,
min_amount,
max_amount,
recipient,
transaction_type: transaction_type.map(Into::into),
};
let transactions = client::fetch(&kairos_server_address, &transaction_filter)
.map_err(Into::<CliError>::into)?;
serde_json::to_string_pretty(&transactions).map_err(Into::into)
}
2 changes: 2 additions & 0 deletions kairos-cli/src/commands/mod.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
pub mod deposit;
#[cfg(feature = "database")]
pub mod fetch;
#[cfg(feature = "demo")]
pub mod run_cctl;
pub mod transfer;
Expand Down
6 changes: 6 additions & 0 deletions kairos-cli/src/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,12 @@ use thiserror::Error;

#[derive(Error, Debug)]
pub enum CliError {
/// Failed to serialize to JSON string.
#[error("failed to parse hex string: {error}")]
SerializationError {
#[from]
error: serde_json::Error,
},
/// Cryptography error.
#[error("cryptography error: {error}")]
CryptoError {
Expand Down
4 changes: 4 additions & 0 deletions kairos-cli/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,8 @@ pub enum Command {
Transfer(commands::transfer::Args),
#[command(about = "Withdraws funds from your account")]
Withdraw(commands::withdraw::Args),
#[cfg(feature = "database")]
Fetch(commands::fetch::Args),

#[cfg(feature = "demo")]
RunDemoCctl,
Expand All @@ -42,6 +44,8 @@ pub fn run(
Command::Deposit(args) => commands::deposit::run(args, kairos_server_address),
Command::Transfer(args) => commands::transfer::run(args, kairos_server_address),
Command::Withdraw(args) => commands::withdraw::run(args, kairos_server_address),
#[cfg(feature = "database")]
Command::Fetch(args) => commands::fetch::run(args, kairos_server_address),

#[cfg(feature = "demo")]
Command::RunDemoCctl => commands::run_cctl::run(),
Expand Down
12 changes: 8 additions & 4 deletions kairos-data/src/transaction.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ pub enum Transaction {
Withdrawal,
}

#[derive(Queryable, Debug, Identifiable, Insertable, Serialize, Selectable)]
#[derive(Queryable, Debug, Identifiable, Insertable, Serialize, Selectable, Deserialize)]
#[diesel(primary_key(timestamp, amount, public_key))]
#[diesel(table_name = transactions)]
#[diesel(check_for_backend(diesel::pg::Pg))]
Expand All @@ -30,14 +30,15 @@ pub struct Transactions {
pub recipient: Option<String>,
}

#[derive(Deserialize)]
#[derive(Deserialize, Debug, Serialize)]
pub struct TransactionFilter {
pub sender: Option<String>,
pub min_timestamp: Option<NaiveDateTime>,
pub max_timestamp: Option<NaiveDateTime>,
pub min_amount: Option<i64>,
pub max_amount: Option<i64>,
pub min_amount: Option<u64>,
pub max_amount: Option<u64>,
pub recipient: Option<String>,
pub transaction_type: Option<Transaction>,
}

pub async fn get(
Expand Down Expand Up @@ -67,6 +68,9 @@ pub async fn get(
if let Some(recipient) = filter.recipient {
query = query.filter(transactions::recipient.eq(recipient));
}
if let Some(transaction_type) = filter.transaction_type {
query = query.filter(transactions::trx.eq(transaction_type));
}

query
.select(Transactions::as_select())
Expand Down
31 changes: 2 additions & 29 deletions kairos-server/src/routes/fetch.rs
Original file line number Diff line number Diff line change
@@ -1,22 +1,10 @@
use axum::{extract::State, Json};
use axum_extra::routing::TypedPath;
use chrono::NaiveDateTime;
use kairos_data::transaction::*;
use serde::Deserialize;
use tracing::instrument;

use crate::{state::ServerState, AppErr};

#[derive(Deserialize, Debug)]
pub struct QueryTransactionsPayload {
sender: Option<String>,
min_timestamp: Option<String>,
max_timestamp: Option<String>,
min_amount: Option<i64>,
max_amount: Option<i64>,
recipient: Option<String>,
}

#[derive(TypedPath)]
#[typed_path("/api/v1/transactions")]
pub struct QueryTransactionsPath;
Expand All @@ -25,22 +13,7 @@ pub struct QueryTransactionsPath;
pub async fn query_transactions_handler(
_: QueryTransactionsPath,
State(state): State<ServerState>,
Json(payload): Json<QueryTransactionsPayload>,
Json(filter): Json<TransactionFilter>,
) -> Result<Json<Vec<Transactions>>, AppErr> {
let filter = TransactionFilter {
sender: payload.sender,
min_timestamp: payload
.min_timestamp
.and_then(|ts| NaiveDateTime::parse_from_str(&ts, "%Y-%m-%d %H:%M:%S").ok()),
max_timestamp: payload
.max_timestamp
.and_then(|ts| NaiveDateTime::parse_from_str(&ts, "%Y-%m-%d %H:%M:%S").ok()),
min_amount: payload.min_amount,
max_amount: payload.max_amount,
recipient: payload.recipient,
};

let transactions = get(&state.pool, filter).await?;

Ok(Json(transactions))
get(&state.pool, filter).await.map(Json).map_err(Into::into)
}
32 changes: 13 additions & 19 deletions nixos/tests/end-to-end.nix
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
{ nixosTest
, mkKairosHostConfig
, kairos
, testResources ? ../../kairos-cli/tests/fixtures
, kairos-contracts
, cctlModule
, fetchurl
Expand Down Expand Up @@ -104,12 +103,11 @@ nixosTest {
raise Exception("Success key not found in JSON")
@backoff.on_exception(backoff.expo, Exception, max_tries=5, jitter=backoff.full_jitter)
def wait_for_deposit(depositor, amount):
transactions_query = { "sender": depositor }
transactions_result = client.succeed("curl --fail-with-body -X POST http://kairos/api/v1/transactions -H 'Content-Type: application/json' -d '{}'".format(json.dumps(transactions_query)))
def wait_for_transaction(sender, transaction_type, amount):
transactions_result = client.succeed("kairos-cli --kairos-server-address http://kairos fetch --sender {} --transaction-type {}".format(sender, transaction_type))
transactions = json.loads(transactions_result)
if not any(transaction.get("public_key") == depositor and transaction.get("amount") == str(amount) for transaction in transactions):
raise Exception("Couldn't find deposit for depositor {} with amount {} in transactions\n:{}".format(depositor, amount, transactions))
if not any(transaction.get("public_key") == sender and transaction.get("amount") == str(amount) for transaction in transactions):
raise Exception("Couldn't find {} for sender {} with amount {} in transactions\n:{}".format(transaction_type, sender, amount, transactions))
# Test
start_all()
Expand Down Expand Up @@ -139,31 +137,27 @@ nixosTest {
wait_for_successful_deploy(deposit_deploy_hash)
wait_for_deposit(depositor, 3000000000)
wait_for_transaction(depositor, "deposit", 3000000000)
# transfer
beneficiary = client.succeed("cat ${clientUsersDirectory}/user-3/public_key_hex")
transfer_output = client.succeed("kairos-cli --kairos-server-address http://kairos transfer --amount 1000 --recipient {} --private-key {}".format(beneficiary, depositor_private_key))
assert "Transfer successfully sent to L2\n" in transfer_output, "The transfer command was not successful: {}".format(transfer_output)
# data availability
transactions_query = { "recipient": beneficiary }
transactions_result = client.succeed("curl --fail-with-body -X POST http://kairos/api/v1/transactions -H 'Content-Type: application/json' -d '{}'".format(json.dumps(transactions_query)))
transactions_result = client.succeed("kairos-cli --kairos-server-address http://kairos fetch --recipient {}".format(beneficiary))
transactions = json.loads(transactions_result)
assert any(transaction.get("recipient") == beneficiary and transaction.get("amount") == str(1000) for transaction in transactions), "Couldn't find the transfer in the L2's DA: {}".format(transactions)
# TODO test withdraw
# withdraw
withdrawer = client.succeed("cat ${clientUsersDirectory}/user-3/public_key_hex")
withdrawer_private_key = "${clientUsersDirectory}/user-3/secret_key.pem"
withdraw_output = client.succeed("kairos-cli --kairos-server-address http://kairos withdraw --amount 800 --private-key {}".format(withdrawer_private_key))
assert "Withdrawal successfully sent to L2\n" in withdraw_output, "The withdraw command was not successful: {}".format(withdraw_output)
# TODO cctl does not provide any secp256k1 keys
# CLI with secp256k1
# 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
wait_for_transaction(withdrawer, "withdrawal", 800)
# cli_output = client.succeed("kairos-cli transfer --recipient '01a26419a7d82b2263deaedea32d35eee8ae1c850bd477f62a82939f06e80df356' --amount 1000 --private-key ${testResources}/secp256k1/secret_key.pem")
# assert "ok\n" in cli_output
# cli_output = client.succeed("kairos-cli withdraw --amount 1000 --private-key ${testResources}/secp256k1/secret_key.pem")
# assert "ok\n" in cli_output
# TODO cctl does not provide any secp256k1 keys, once support is added it should be tested here
'';
}

0 comments on commit 2f53b57

Please sign in to comment.