Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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
2,259 changes: 1,367 additions & 892 deletions Cargo.lock

Large diffs are not rendered by default.

4 changes: 3 additions & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ members = [
"fedimint-client",
"fedimint-client-module",
"fedimint-client-rpc",
"fedimint-client-uniffi",
"fedimint-client-wasm",
"fedimint-core",
"fedimint-cursed-redb",
Expand Down Expand Up @@ -279,6 +280,7 @@ reqwest = { version = "0.12.22", features = [
rexie = "0.6.2"
ring = "0.17.14"
rocksdb = { version = "0.22.0" }
rustls = { version = "0.23.28", default-features = false, features = ["logging", "ring", "std", "tls12"] }
rustls-pki-types = { version = "1.12.0" }
scopeguard = "1.2.0"
secp256k1 = { version = "0.29.0", default-features = false }
Expand All @@ -304,7 +306,7 @@ threshold_crypto = { version = "0.2.1", package = "fedimint-threshold-crypto" }
tikv-jemallocator = "0.5"
time = "0.3.41"
tokio = "1.46.1"
tokio-rustls = { version = "0.26.0", features = ["aws_lc_rs"] }
tokio-rustls = { version = "0.26.0", features = ["ring"], default-features = false }
tokio-stream = "0.1.17"
tokio-test = "0.4.4"
tokio-util = "0.7.15"
Expand Down
2 changes: 1 addition & 1 deletion fedimint-api-client/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ workspace = true

[target.'cfg(not(target_family = "wasm"))'.dependencies]
arti-client = { workspace = true, optional = true }
aws-lc-sys = { version = "0.30", features = ["bindgen"] }
ring = { workspace = true }
curve25519-dalek = { workspace = true, optional = true }
iroh = { workspace = true, default-features = false, features = [
"discovery-pkarr-dht",
Expand Down
35 changes: 35 additions & 0 deletions fedimint-client-uniffi/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
[package]
authors = { workspace = true }
description = "fedimint client for wasm"
edition = { workspace = true }
license = { workspace = true }
name = "fedimint-client-uniffi"
readme = { workspace = true }
repository = { workspace = true }
version = { workspace = true }

[lib]
crate-type = ["staticlib", "cdylib", "lib"]
name = "fedimint_client_uniffi"
path = "src/lib.rs"

[lints]
workspace = true

[dependencies]
anyhow = { workspace = true }
fedimint-client-rpc = { workspace = true }
fedimint-core = { workspace = true }
fedimint-cursed-redb = { workspace = true }
fedimint-db-locked = { workspace = true }
rustls = { workspace = true }
serde = { workspace = true }
serde_json = { workspace = true }
thiserror = { workspace = true }
tokio = { workspace = true }
uniffi = { version = "0.29", features = ["cli"] }

[build-dependencies]
uniffi = { version = "0.29", features = ["build"] }
fedimint-build = { workspace = true }

3 changes: 3 additions & 0 deletions fedimint-client-uniffi/build.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
fn main() {
fedimint_build::set_code_version();
}
108 changes: 108 additions & 0 deletions fedimint-client-uniffi/src/lib.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
use std::sync::Arc;

use fedimint_client_rpc::{RpcGlobalState, RpcRequest, RpcResponse, RpcResponseHandler};
use fedimint_core::db::Database;

uniffi::setup_scaffolding!();

const DB_FILE_NAME: &str = "fedimint.redb";

#[derive(Debug, thiserror::Error, uniffi::Error)]
pub enum FedimintError {
#[error("Database initialization failed: {msg}")]
DatabaseError { msg: String },

#[error("Failed to create async runtime: {msg}")]
RuntimeError { msg: String },

#[error("Invalid request JSON: {msg}")]
InvalidRequest { msg: String },

#[error("General error: {msg}")]
General { msg: String },
}

#[derive(uniffi::Object)]
pub struct RpcHandler {
state: Arc<RpcGlobalState>,
runtime: tokio::runtime::Runtime,
}

#[uniffi::export]
impl RpcHandler {
#[uniffi::constructor]
pub fn new(db_path: String) -> Result<Arc<Self>, FedimintError> {
let db = create_database(&db_path)
.map_err(|e| FedimintError::DatabaseError { msg: e.to_string() })?;
let state = Arc::new(RpcGlobalState::new(db));

let runtime = tokio::runtime::Runtime::new()
.map_err(|e| FedimintError::RuntimeError { msg: e.to_string() })?;

Ok(Arc::new(Self { state, runtime }))
}

pub async fn rpc(&self, request_json: String) -> Result<String, FedimintError> {
let request: RpcRequest = serde_json::from_str(&request_json)
.map_err(|e| FedimintError::InvalidRequest { msg: e.to_string() })?;

let (tx, rx) = tokio::sync::oneshot::channel();

let handled = self.state.clone().handle_rpc(
request,
PromiseWrapper(std::sync::Mutex::new(Some(tx)))
);

if let Some(task) = handled.task {
self.runtime.spawn(task);
}

rx.await
.map_err(|_| FedimintError::General { msg: "Request cancelled or handler dropped".to_string() })
}
}

struct PromiseWrapper(std::sync::Mutex<Option<tokio::sync::oneshot::Sender<String>>>);

impl RpcResponseHandler for PromiseWrapper {
fn handle_response(&self, response: RpcResponse) {
let json = serde_json::to_string(&response)
.expect("Failed to serialize RPC response");
if let Some(tx) = self.0.lock().unwrap().take() {
let _ = tx.send(json);
}
}
}

/// Creates a redb-based database (pure Rust, no C++ dependencies)
fn create_database(path: &str) -> anyhow::Result<Database> {
use fedimint_cursed_redb::MemAndRedb;

std::fs::create_dir_all(path)?;

let db_path = std::path::Path::new(path).join(DB_FILE_NAME);

let locked_db = tokio::runtime::Runtime::new()?
.block_on(async { MemAndRedb::new(db_path).await })?;

Ok(Database::new(locked_db, Default::default()))
}

#[cfg(test)]
mod tests {
use super::*;

#[test]
fn test_rpc_handler_creation() {
let temp_dir = std::env::temp_dir().join("fedimint-test-db");
let _ = std::fs::remove_dir_all(&temp_dir);

let handler = RpcHandler::new(temp_dir.to_str().unwrap().to_string());
match handler {
Ok(_) => {} // Success
Err(e) => panic!("RpcHandler creation failed: {}", e),
}

let _ = std::fs::remove_dir_all(&temp_dir);
}
}
2 changes: 1 addition & 1 deletion fedimint-core/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,7 @@ tokio-test = { workspace = true }
workspace = true

[target.'cfg(not(target_family = "wasm"))'.dependencies]
aws-lc-sys = { version = "0.30", features = ["bindgen"] }
ring = { workspace = true }
tokio = { workspace = true, features = ["full", "tracing"] }
tokio-rustls = { workspace = true }

Expand Down
2 changes: 1 addition & 1 deletion fedimint-core/src/rustls.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ pub async fn install_crypto_provider() {

INSTALL_CRYPTO
.get_or_init(|| async {
if tokio_rustls::rustls::crypto::aws_lc_rs::default_provider()
if tokio_rustls::rustls::crypto::ring::default_provider()
.install_default()
.is_err()
{
Expand Down
2 changes: 1 addition & 1 deletion fedimint-testing/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ path = "src/lib.rs"
anyhow = { workspace = true }
async-stream = { workspace = true }
async-trait = { workspace = true }
aws-lc-sys = { version = "0.30", features = ["bindgen"] }
ring = { workspace = true }
axum = { workspace = true }
bcrypt = { workspace = true }
bitcoin = { workspace = true }
Expand Down