Skip to content

Commit

Permalink
Merge remote-tracking branch 'origin/main' into feature/deposit-contract
Browse files Browse the repository at this point in the history
  • Loading branch information
marijanp committed May 8, 2024
2 parents 3b599c8 + 27ef3fc commit ea90d7f
Show file tree
Hide file tree
Showing 17 changed files with 288 additions and 48 deletions.
4 changes: 4 additions & 0 deletions Cargo.lock

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

2 changes: 1 addition & 1 deletion kairos-cli/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ casper-types = { version = "4.0.1", features = ["std"] } # TODO: Change `std` ->
clap = { version = "4.5", features = ["derive", "deprecated"] }
hex = "0.4"
thiserror = "1"
kairos-crypto = { path = "../kairos-crypto" }
kairos-crypto = { path = "../kairos-crypto", features = ["fs"] }

[dev-dependencies]
assert_cmd = "2"
Expand Down
7 changes: 5 additions & 2 deletions kairos-crypto/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -5,14 +5,17 @@ edition.workspace = true
license.workspace = true

[features]
default = ["crypto-casper"]
default = ["crypto-casper", "tx"]
crypto-casper = ["casper-types"]
tx = ["kairos-tx"]
fs = ["casper-types/std"] # TODO: Change `std` -> `std-fs-io` in the future version.

[lib]

[dependencies]
hex = "0.4"
thiserror = "1"
kairos-tx = { path = "../kairos-tx", optional = true }

# Casper signer implementation.
casper-types = { version = "4", optional = true, features = ["std"] } # TODO: Change `std` -> `std-fs-io` in the future version.
casper-types = { version = "4", optional = true }
9 changes: 9 additions & 0 deletions kairos-crypto/src/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,4 +17,13 @@ pub enum CryptoError {
/// Private key is not provided.
#[error("private key is not provided")]
MissingPrivateKey,

/// Unable to compute transaction hash - invalid data given.
#[cfg(feature = "tx")]
#[error("unable to hash transaction data: {error}")]
TxHashingError { error: String },
/// Signing algorithm is not available in `kairos-tx`.
#[cfg(feature = "tx")]
#[error("algorithm not available in tx format")]
InvalidSigningAlgorithm,
}
47 changes: 47 additions & 0 deletions kairos-crypto/src/implementations/casper.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
use casper_types::bytesrepr::{FromBytes, ToBytes};
use casper_types::{crypto, PublicKey, SecretKey, Signature};

#[cfg(feature = "fs")]
use std::path::Path;

use crate::CryptoError;
Expand All @@ -11,6 +13,7 @@ pub struct Signer {
}

impl CryptoSigner for Signer {
#[cfg(feature = "fs")]
fn from_private_key_file<P: AsRef<Path>>(file: P) -> Result<Self, CryptoError>
where
Self: Sized,
Expand Down Expand Up @@ -85,6 +88,50 @@ impl CryptoSigner for Signer {

Ok(public_key)
}

#[cfg(feature = "tx")]
fn verify_tx(&self, tx: kairos_tx::asn::Transaction) -> Result<(), CryptoError> {
let tx_hash = tx.payload.hash().map_err(|e| CryptoError::TxHashingError {
error: e.to_string(),
})?;
let signature: Vec<u8> = tx.signature.into();
self.verify(tx_hash, signature)?;

Ok(())
}

#[cfg(feature = "tx")]
fn sign_tx_payload(
&self,
payload: kairos_tx::asn::SigningPayload,
) -> Result<kairos_tx::asn::Transaction, CryptoError> {
// Compute payload signature.
let tx_hash = payload.hash().map_err(|e| CryptoError::TxHashingError {
error: e.to_string(),
})?;
let signature = self.sign(tx_hash)?;

// Prepare public key.
let public_key = self.to_public_key()?;

// Prepare algorithm.
let algorithm = match self.public_key {
PublicKey::Ed25519(_) => Ok(kairos_tx::asn::SigningAlgorithm::CasperEd25519),
PublicKey::Secp256k1(_) => Ok(kairos_tx::asn::SigningAlgorithm::CasperSecp256k1),
_ => Err(CryptoError::InvalidSigningAlgorithm),
}?;

// Build full transaction.
let tx = kairos_tx::asn::Transaction::new(
public_key,
payload,
&tx_hash,
algorithm,
signature.into(),
);

Ok(tx)
}
}

#[cfg(test)]
Expand Down
10 changes: 10 additions & 0 deletions kairos-crypto/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,11 +1,13 @@
pub mod error;
pub mod implementations;

#[cfg(feature = "fs")]
use std::path::Path;

use error::CryptoError;

pub trait CryptoSigner {
#[cfg(feature = "fs")]
fn from_private_key_file<P: AsRef<Path>>(file: P) -> Result<Self, CryptoError>
where
Self: Sized;
Expand All @@ -20,5 +22,13 @@ pub trait CryptoSigner {
signature_bytes: U,
) -> Result<(), CryptoError>;

#[cfg(feature = "tx")]
fn verify_tx(&self, tx: kairos_tx::asn::Transaction) -> Result<(), CryptoError>;
#[cfg(feature = "tx")]
fn sign_tx_payload(
&self,
payload: kairos_tx::asn::SigningPayload,
) -> Result<kairos_tx::asn::Transaction, CryptoError>;

fn to_public_key(&self) -> Result<Vec<u8>, CryptoError>;
}
2 changes: 1 addition & 1 deletion kairos-server/.env
Original file line number Diff line number Diff line change
@@ -1 +1 @@
KAIROS_SERVER_PORT="8000"
KAIROS_SERVER_SOCKET_ADDR="127.0.0.1:7893"
7 changes: 4 additions & 3 deletions kairos-server/src/config.rs
Original file line number Diff line number Diff line change
@@ -1,14 +1,15 @@
use std::net::SocketAddr;
use std::{fmt, str::FromStr};

#[derive(Debug)]
pub struct ServerConfig {
pub port: u16,
pub socket_addr: SocketAddr,
}

impl ServerConfig {
pub fn from_env() -> Result<Self, String> {
let port = parse_env_as::<u16>("KAIROS_SERVER_PORT")?;
Ok(Self { port })
let socket_addr = parse_env_as::<SocketAddr>("KAIROS_SERVER_SOCKET_ADDR")?;
Ok(Self { socket_addr })
}
}

Expand Down
37 changes: 37 additions & 0 deletions kairos-server/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,9 @@ use axum_extra::routing::RouterExt;

pub use errors::AppErr;

use crate::config::ServerConfig;
use crate::state::BatchStateManager;

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

Expand All @@ -22,3 +25,37 @@ pub fn app_router(state: Arc<state::BatchStateManager>) -> Router {
.typed_post(routes::transfer_handler)
.with_state(state)
}

pub async fn run(config: ServerConfig) {
let app = app_router(BatchStateManager::new_empty());

let listener = tokio::net::TcpListener::bind(config.socket_addr)
.await
.unwrap();
tracing::info!("listening on `{}`", listener.local_addr().unwrap());

axum::serve(listener, app)
.with_graceful_shutdown(shutdown_signal())
.await
.unwrap();
}

async fn shutdown_signal() {
let ctrl_c = async {
tokio::signal::ctrl_c()
.await
.expect("Failed to install Ctrl+C handler");
};

let terminate = async {
tokio::signal::unix::signal(tokio::signal::unix::SignalKind::terminate())
.expect("Failed to install signal handler")
.recv()
.await;
};

tokio::select! {
_ = ctrl_c => {tracing::info!("Received CTRL+C signal, shutting down...")},
_ = terminate => {tracing::info!("Received shutdown signal, shutting down...")},
}
}
24 changes: 11 additions & 13 deletions kairos-server/src/main.rs
Original file line number Diff line number Diff line change
@@ -1,21 +1,19 @@
use std::net::SocketAddr;

use dotenvy::dotenv;
use kairos_server::{config::ServerConfig, state::BatchStateManager};
use kairos_server::config::ServerConfig;

#[tokio::main]
async fn main() {
tracing_subscriber::fmt::init();
dotenv().ok();
let subscriber = tracing_subscriber::fmt()
.with_max_level(tracing::Level::INFO)
.finish();

tracing::subscriber::set_global_default(subscriber).expect("Failed to set subscriber");

// loads the environment from the current directories .env file
// if the .env does not exist in the current directory,
// we still go ahead and try to obtain a server config from the environment
let _ = dotenv();
let config = ServerConfig::from_env()
.unwrap_or_else(|e| panic!("Failed to parse server config from environment: {}", e));

let app = kairos_server::app_router(BatchStateManager::new_empty());

let axum_addr = SocketAddr::from(([127, 0, 0, 1], config.port));

tracing::info!("starting http server on `{}`", axum_addr);
let listener = tokio::net::TcpListener::bind(axum_addr).await.unwrap();
axum::serve(listener, app).await.unwrap();
kairos_server::run(config).await
}
2 changes: 2 additions & 0 deletions kairos-test-utils/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -28,5 +28,7 @@ sd-notify = "0.4"
tokio = { version = "1", features = [ "full", "tracing", "macros" ] }
tempfile = "3"
tracing = "0.1"
tracing-subscriber = { version = "0.3", features = ["std", "env-filter"] }
reqwest = { version = "0.12", features = ["json"] }
kairos-server = { path = "../kairos-server" }

38 changes: 12 additions & 26 deletions kairos-test-utils/src/kairos.rs
Original file line number Diff line number Diff line change
@@ -1,21 +1,10 @@
use backoff::future::retry;
use backoff::ExponentialBackoff;
use reqwest::Url;
use std::env;
use std::io;
use std::net::{SocketAddr, TcpListener};
use std::path::PathBuf;
use std::process::{Child, Command};
use tokio::net::TcpStream;

// A hacky way to get the cargo binary directory path
pub fn bin_dir() -> PathBuf {
let mut path = env::current_exe().unwrap();
path.pop(); // pop kairos_test_utils-hash
path.pop(); // pop deps
path
}

async fn wait_for_port(address: &SocketAddr) -> Result<(), io::Error> {
retry(ExponentialBackoff::default(), || async {
Ok(TcpStream::connect(address).await.map(|_| ())?)
Expand All @@ -25,25 +14,22 @@ async fn wait_for_port(address: &SocketAddr) -> Result<(), io::Error> {

pub struct Kairos {
pub url: Url,
process_handle: Child,
process_handle: tokio::task::JoinHandle<()>,
}

impl Kairos {
pub async fn run() -> Result<Kairos, io::Error> {
let port = TcpListener::bind("127.0.0.1:0")?
.local_addr()?
.port()
.to_string();
let url = Url::parse(format!("http://127.0.0.1:{}", port).as_str()).unwrap();
let kairos = bin_dir().join("kairos-server");
let process_handle = Command::new(kairos)
.env("KAIROS_SERVER_PORT", &port)
.spawn()
.expect("Failed to start the kairos-server");
let socket_addr = TcpListener::bind("127.0.0.1:0")?.local_addr()?;
let port = socket_addr.port().to_string();
let url = Url::parse(&format!("http://127.0.0.1:{}", port)).unwrap();
let config = kairos_server::config::ServerConfig { socket_addr };

let process_handle = tokio::spawn(async move {
tracing_subscriber::fmt::init();
kairos_server::run(config).await;
});

wait_for_port(url.socket_addrs(|| Option::None).unwrap().first().unwrap())
.await
.unwrap();
wait_for_port(&socket_addr).await.unwrap();

Ok(Kairos {
url,
Expand All @@ -54,7 +40,7 @@ impl Kairos {

impl Drop for Kairos {
fn drop(&mut self) {
let _ = self.process_handle.kill();
self.process_handle.abort()
}
}

Expand Down
1 change: 1 addition & 0 deletions kairos-tx/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ license.workspace = true
[dependencies]
num-traits = "0.2"
rasn = { version = "0.12", default-features = false, features = ["macros"] }
sha2 = "0.10"

[dev-dependencies]
hex = "0.4"
Expand Down
19 changes: 19 additions & 0 deletions kairos-tx/schema.asn
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,28 @@ TxSchema DEFINITIONS AUTOMATIC TAGS ::= BEGIN

-- Basic types.
PublicKey ::= OCTET STRING
Signature ::= OCTET STRING
PayloadHash ::= OCTET STRING
Amount ::= INTEGER (0..18446744073709551615)
Nonce ::= INTEGER (0..18446744073709551615)

-- Full, top-level transaction type.
Transaction ::= SEQUENCE {
publicKey PublicKey,
payload SigningPayload,
hash PayloadHash,
algorithm SigningAlgorithm,
signature Signature,
...
}

-- Support for multiple signing algorithms.
SigningAlgorithm ::= ENUMERATED {
casperSecp256k1 (0),
casperEd25519 (1),
...
}

-- Transaction payload for signing.
SigningPayload ::= SEQUENCE {
nonce Nonce,
Expand Down
Loading

0 comments on commit ea90d7f

Please sign in to comment.