Skip to content

Commit

Permalink
Merge pull request #94 from cspr-rad/implement-deposit-cli-4
Browse files Browse the repository at this point in the history
deposit: refactor to forward deploys to L1
  • Loading branch information
Avi-D-coder committed Jun 6, 2024
2 parents eeda444 + b12939d commit 535790a
Show file tree
Hide file tree
Showing 22 changed files with 602 additions and 341 deletions.
359 changes: 178 additions & 181 deletions Cargo.lock

Large diffs are not rendered by default.

3 changes: 3 additions & 0 deletions flake.nix
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,7 @@
inputs'.csprpkgs.packages.cctl
];

CASPER_CHAIN_NAME = "cspr-dev-cctl";
PATH_TO_WASM_BINARIES = "${self'.packages.kairos-contracts}/bin";

meta.mainProgram = "kairos-server";
Expand All @@ -106,8 +107,10 @@
devShells.default = pkgs.mkShell {
# Rust Analyzer needs to be able to find the path to default crate
RUST_SRC_PATH = "${rustToolchain}/lib/rustlib/src/rust/library";
CASPER_CHAIN_NAME = "cspr-dev-cctl";
PATH_TO_WASM_BINARIES = "${self'.packages.kairos-contracts}/bin";
inputsFrom = [ self'.packages.kairos ];
packages = [ inputs'.csprpkgs.packages.cctl ];
};

packages = {
Expand Down
8 changes: 7 additions & 1 deletion kairos-cli/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,13 @@ license.workspace = true

# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html

[features]
all-tests = ["cctl-tests"]
cctl-tests = []

[dependencies]
casper-types = { version = "4.0.1", features = ["std"] } # TODO: Change `std` -> `std-fs-io` in the future version.
casper-client = "2"
casper-types = { version = "3", features = ["std"] } # TODO: Change `std` -> `std-fs-io` in the future version.
clap = { version = "4.5", features = ["derive", "deprecated"] }
hex = "0.4"
thiserror = "1"
Expand All @@ -30,3 +35,4 @@ tokio = { version = "1" }
assert_cmd = "2"
predicates = "3"
kairos-test-utils = { path = "../kairos-test-utils" }
casper-hashing = "2"
42 changes: 35 additions & 7 deletions kairos-cli/src/client.rs
Original file line number Diff line number Diff line change
@@ -1,14 +1,18 @@
use kairos_server::routes::PayloadBody;

use casper_client::types::DeployHash;
use casper_client::types::{DeployBuilder, ExecutableDeployItem, TimeDiff, Timestamp};
use casper_types::{crypto::SecretKey, runtime_args, RuntimeArgs};
use reqwest::{blocking, Url};
use serde::{Deserialize, Serialize};
use std::fmt;
use std::fs;
use std::path::Path;

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

Expand Down Expand Up @@ -36,22 +40,46 @@ impl From<reqwest::Error> for KairosClientError {
}
}

pub fn submit_transaction_request(
pub fn deposit(
base_url: &Url,
deposit_request: &PayloadBody,
) -> Result<(), KairosClientError> {
depositor_secret_key: &SecretKey,
amount: u64,
) -> Result<DeployHash, KairosClientError> {
let deposit_session_wasm_path =
Path::new(env!("PATH_TO_WASM_BINARIES")).join("deposit-session-optimized.wasm");
let deposit_session_wasm_bytes = fs::read(&deposit_session_wasm_path).unwrap_or_else(|err| {
panic!(
"Failed to read the deposit session wasm as bytes from file: {:?}.\n{}",
deposit_session_wasm_path, err
)
});
let deposit_session =
ExecutableDeployItem::new_module_bytes(deposit_session_wasm_bytes.into(), runtime_args! {});
let deploy = DeployBuilder::new(
env!("CASPER_CHAIN_NAME"),
deposit_session,
depositor_secret_key,
)
.with_standard_payment(amount)
.with_timestamp(Timestamp::now())
.with_ttl(TimeDiff::from_millis(60_000)) // 1 min
.build()
.map_err(|err| KairosClientError::CasperClientError(err.to_string()))?;

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)
.json(&deploy)
.send()
.map_err(Into::<KairosClientError>::into)?;
let status = response.status();
if !status.is_success() {
Err(KairosClientError::KairosServerError(status.to_string()))
} else {
Ok(())
response
.json::<DeployHash>()
.map_err(Into::<KairosClientError>::into)
}
}
42 changes: 19 additions & 23 deletions kairos-cli/src/commands/deposit.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,9 @@ 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 casper_types::crypto::SecretKey;
use clap::Parser;
use reqwest::Url;

#[derive(Parser, Debug)]
pub struct Args {
Expand All @@ -22,21 +16,23 @@ pub struct Args {

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()?;

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,
};
let path = args.private_key_path.field;
let depositor_secret_key = SecretKey::from_file(&path)
.map_err(|err| panic!("Failed to read secret key from file {:?}: {}", path, err))
.unwrap();

client::submit_transaction_request(&kairos_server_address, &deposit_request)
client::deposit(&kairos_server_address, &depositor_secret_key, amount)
.map_err(Into::<CliError>::into)
.map(|_| "ok".to_string())
.map(|deploy_hash| {
// to_string crops the hash to <hash-prefix>..<hash-postfix>
// thus we use serde to get the full string, and remove the
// double quotes that get added during serialization
let mut output: String = serde_json::to_string(&deploy_hash)
.unwrap()
.chars()
.filter(|&c| c != '"') // Filter out the double quotes
.collect();
output.push('\n');
output
})
}
42 changes: 31 additions & 11 deletions kairos-cli/tests/cli_tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,10 @@ use assert_cmd::Command;
use reqwest::Url;
use std::path::PathBuf;

use casper_client::types::DeployHash;
use casper_hashing::Digest;
use kairos_test_utils::{cctl, kairos};

// Helper function to get the path to a fixture file
fn fixture_path(relative_path: &str) -> PathBuf {
let mut path = PathBuf::from(env!("CARGO_MANIFEST_DIR"));
Expand All @@ -10,14 +14,21 @@ fn fixture_path(relative_path: &str) -> PathBuf {
}

#[tokio::test]
#[cfg_attr(not(feature = "cctl-tests"), ignore)]
async fn deposit_successful_with_ed25519() {
let dummy_rpc = Url::parse("http://127.0.0.1:11101").unwrap();
let kairos = kairos_test_utils::kairos::Kairos::run(dummy_rpc)
.await
.unwrap();
let network = cctl::CCTLNetwork::run(Option::None).await.unwrap();
let node = network
.nodes
.first()
.expect("Expected at least one node after successful network run");
let node_url = Url::parse(&format!("http://localhost:{}/rpc", node.port.rpc_port)).unwrap();

let kairos = kairos::Kairos::run(node_url).await.unwrap();

tokio::task::spawn_blocking(move || {
let secret_key_path = fixture_path("ed25519/secret_key.pem");
let depositor_secret_key_path = network
.working_dir
.join("assets/users/user-1/secret_key.pem");

let mut cmd = Command::cargo_bin("kairos-cli").unwrap();
cmd.arg("--kairos-server-address")
Expand All @@ -26,8 +37,17 @@ async fn deposit_successful_with_ed25519() {
.arg("--amount")
.arg("123")
.arg("--private-key")
.arg(secret_key_path);
cmd.assert().success().stdout("ok\n");
.arg(depositor_secret_key_path);
cmd.assert()
.success()
.stdout(predicates::function::function(|stdout: &str| {
let raw_hash = stdout.trim_end();
DeployHash::new(
Digest::from_hex(raw_hash)
.expect("Failed to parse deploy hash after depositing"),
);
true
}));
})
.await
.unwrap();
Expand Down Expand Up @@ -89,7 +109,7 @@ fn deposit_invalid_private_key_path() {
.arg(secret_key_path);
cmd.assert()
.failure()
.stderr(predicates::str::contains("failed to parse private key"));
.stderr(predicates::str::contains("No such file or directory"));
}

#[test]
Expand All @@ -102,9 +122,9 @@ fn deposit_invalid_private_key_content() {
.arg("123")
.arg("--private-key")
.arg(secret_key_path);
cmd.assert()
.failure()
.stderr(predicates::str::contains("failed to parse private key"));
cmd.assert().failure().stderr(predicates::str::contains(
"Failed to read secret key from file",
));
}

#[test]
Expand Down
9 changes: 9 additions & 0 deletions kairos-server/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,11 @@ name = "kairos-server"

# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html

[features]
all-tests = ["cctl-tests", "deposit-mock"]
cctl-tests = []
deposit-mock = []

[dependencies]
dotenvy = "0.15"
axum = { version = "0.7", features = ["tracing"] }
Expand All @@ -20,6 +25,8 @@ axum-extra = { version = "0.9", features = [
"json-deserializer",
] }
anyhow = "1"
casper-client = "2"
rand = "0.8"
serde = { version = "1", features = ["derive"] }
serde_json = "1"
tokio = { version = "1", features = ["full", "tracing", "macros"] }
Expand All @@ -35,3 +42,5 @@ reqwest = "0.12"
[dev-dependencies]
proptest = "1"
axum-test = "14"
kairos-test-utils = { path = "../kairos-test-utils" }
casper-types = "3"
5 changes: 2 additions & 3 deletions kairos-server/src/config.rs
Original file line number Diff line number Diff line change
@@ -1,9 +1,8 @@
use reqwest::Url;
use std::net::SocketAddr;
use std::{fmt, str::FromStr};

use reqwest::Url;

#[derive(Debug)]
#[derive(Clone, Debug)]
pub struct ServerConfig {
pub socket_addr: SocketAddr,
pub casper_rpc: Url,
Expand Down
9 changes: 9 additions & 0 deletions kairos-server/src/errors.rs
Original file line number Diff line number Diff line change
Expand Up @@ -75,3 +75,12 @@ impl From<kairos_trie::TrieError> for AppErr {
}
}
}

impl From<casper_client::Error> for AppErr {
fn from(error: casper_client::Error) -> Self {
Self {
error: anyhow::Error::msg(error.to_string()),
status: Some(StatusCode::INTERNAL_SERVER_ERROR),
}
}
}
23 changes: 19 additions & 4 deletions kairos-server/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,27 +13,42 @@ use axum_extra::routing::RouterExt;
pub use errors::AppErr;

use crate::config::ServerConfig;
use crate::state::BatchStateManager;
use crate::state::{BatchStateManager, ServerState, ServerStateInner};

type PublicKey = Vec<u8>;
type Signature = Vec<u8>;

pub fn app_router(state: Arc<state::BatchStateManager>) -> Router {
#[cfg(not(feature = "deposit-mock"))]
pub fn app_router(state: ServerState) -> Router {
Router::new()
.typed_post(routes::deposit_handler)
.typed_post(routes::withdraw_handler)
.typed_post(routes::transfer_handler)
.with_state(state)
}

pub async fn run(config: ServerConfig) {
let app = app_router(BatchStateManager::new_empty());
#[cfg(feature = "deposit-mock")]
pub fn app_router(state: ServerState) -> Router {
Router::new()
.typed_post(routes::deposit_handler)
.typed_post(routes::withdraw_handler)
.typed_post(routes::transfer_handler)
.typed_post(routes::deposit_mock_handler)
.with_state(state)
}

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

let state = Arc::new(ServerStateInner {
batch_state_manager: BatchStateManager::new_empty(),
server_config: config.clone(),
});
let app = app_router(state);

axum::serve(listener, app)
.with_graceful_shutdown(shutdown_signal())
.await
Expand Down
Loading

0 comments on commit 535790a

Please sign in to comment.