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

deposit: refactor to forward deploys to L1 #94

Merged
merged 22 commits into from
Jun 6, 2024
Merged
Show file tree
Hide file tree
Changes from 20 commits
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
f62b24d
kairos-server/config: add casper-node url
marijanp May 7, 2024
35186cf
kairos-server/state: introduce ServerState consisting of BatchStateMa…
marijanp May 7, 2024
663e3b7
kairos-test-utils/kairos: use updated kairos-server config
marijanp May 7, 2024
d1b47cb
kairos-server/deposit: refactor to forward deploys, impl system integ…
marijanp May 7, 2024
62291cd
kairos-server: add cctl-tests feature
marijanp May 7, 2024
916d307
kairos-server/deposit: return deploy hash
marijanp May 8, 2024
5eff792
kairos-cli: implement deposit deploy creation, and system integration…
marijanp May 8, 2024
f3e7422
fixme: use jonas's branch
marijanp May 8, 2024
b53294f
Merge branch 'main' into implement-deposit-cli-4
marijanp May 9, 2024
357fd53
kairos-cli: remove needless borrow
marijanp May 9, 2024
d335a35
Merge remote-tracking branch 'origin/main' into implement-deposit-cli-4
marijanp May 22, 2024
8a0736a
kairos-server: add deposit mock handler
marijanp May 23, 2024
3a134e5
Merge remote-tracking branch 'origin/main' into implement-deposit-cli-4
marijanp May 28, 2024
1a8c4a4
cctl: assets_dir -> working_dir
marijanp May 28, 2024
eea9a24
nixos/modules/kairos: add casperRpcUrl option
marijanp May 30, 2024
c0179c9
nixos/modules/cctl: remove unnecessary braces
marijanp May 30, 2024
2374f04
nixos/modules/cctl: add workingDirectory option
marijanp May 30, 2024
5fb2c0a
nixos/modules/cctl: serve generated users directory with nginx
marijanp May 30, 2024
a7c33b7
tests/e2e: verify successful deposit deploy submission
marijanp May 30, 2024
12a2d10
tests/verify-config: add placeholder URL to make test pass
marijanp May 30, 2024
fdaea36
Merge remote-tracking branch 'origin/main' into implement-deposit-cli-4
marijanp Jun 5, 2024
b12939d
Merge branch 'main' into implement-deposit-cli-4
marijanp Jun 5, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
416 changes: 215 additions & 201 deletions Cargo.lock

Large diffs are not rendered by default.

2 changes: 2 additions & 0 deletions flake.nix
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,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 @@ -112,6 +113,7 @@
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 ];
};
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 @@ -34,3 +41,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
Loading