rust-sdk: initial version
This commit is contained in:
Generated
+47
-24
@@ -1778,6 +1778,14 @@ version = "2.5.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0206175f82b8d6bf6652ff7d71a1e27fd2e4efde587fd368662814d6ec1d9ce0"
|
||||
|
||||
[[package]]
|
||||
name = "examples-common"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"log",
|
||||
"pretty_env_logger",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "execute"
|
||||
version = "0.1.0"
|
||||
@@ -1970,9 +1978,9 @@ checksum = "e6d5a32815ae3f33302d95fdcb2ce17862f8c65363dcfd29360480ba1001fc9c"
|
||||
|
||||
[[package]]
|
||||
name = "futures"
|
||||
version = "0.3.24"
|
||||
version = "0.3.25"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7f21eda599937fba36daeb58a22e8f5cee2d14c4a17b5b7739c7c8e5e3b8230c"
|
||||
checksum = "38390104763dc37a5145a53c29c63c1290b5d316d6086ec32c293f6736051bb0"
|
||||
dependencies = [
|
||||
"futures-channel",
|
||||
"futures-core",
|
||||
@@ -1985,9 +1993,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "futures-channel"
|
||||
version = "0.3.24"
|
||||
version = "0.3.25"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "30bdd20c28fadd505d0fd6712cdfcb0d4b5648baf45faef7f852afb2399bb050"
|
||||
checksum = "52ba265a92256105f45b719605a571ffe2d1f0fea3807304b522c1d778f79eed"
|
||||
dependencies = [
|
||||
"futures-core",
|
||||
"futures-sink",
|
||||
@@ -1995,15 +2003,15 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "futures-core"
|
||||
version = "0.3.24"
|
||||
version = "0.3.25"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4e5aa3de05362c3fb88de6531e6296e85cde7739cccad4b9dfeeb7f6ebce56bf"
|
||||
checksum = "04909a7a7e4633ae6c4a9ab280aeb86da1236243a77b694a49eacd659a4bd3ac"
|
||||
|
||||
[[package]]
|
||||
name = "futures-executor"
|
||||
version = "0.3.24"
|
||||
version = "0.3.25"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9ff63c23854bee61b6e9cd331d523909f238fc7636290b96826e9cfa5faa00ab"
|
||||
checksum = "7acc85df6714c176ab5edf386123fafe217be88c0840ec11f199441134a074e2"
|
||||
dependencies = [
|
||||
"futures-core",
|
||||
"futures-task",
|
||||
@@ -2023,15 +2031,15 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "futures-io"
|
||||
version = "0.3.24"
|
||||
version = "0.3.25"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bbf4d2a7a308fd4578637c0b17c7e1c7ba127b8f6ba00b29f717e9655d85eb68"
|
||||
checksum = "00f5fb52a06bdcadeb54e8d3671f8888a39697dcb0b81b23b55174030427f4eb"
|
||||
|
||||
[[package]]
|
||||
name = "futures-macro"
|
||||
version = "0.3.24"
|
||||
version = "0.3.25"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "42cd15d1c7456c04dbdf7e88bcd69760d74f3a798d6444e16974b505b0e62f17"
|
||||
checksum = "bdfb8ce053d86b91919aad980c220b1fb8401a9394410e1c289ed7e66b61835d"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
@@ -2040,21 +2048,21 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "futures-sink"
|
||||
version = "0.3.24"
|
||||
version = "0.3.25"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "21b20ba5a92e727ba30e72834706623d94ac93a725410b6a6b6fbc1b07f7ba56"
|
||||
checksum = "39c15cf1a4aa79df40f1bb462fb39676d0ad9e366c2a33b590d7c66f4f81fcf9"
|
||||
|
||||
[[package]]
|
||||
name = "futures-task"
|
||||
version = "0.3.24"
|
||||
version = "0.3.25"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a6508c467c73851293f390476d4491cf4d227dbabcd4170f3bb6044959b294f1"
|
||||
checksum = "2ffb393ac5d9a6eaa9d3fdf37ae2776656b706e200c8e16b1bdb227f5198e6ea"
|
||||
|
||||
[[package]]
|
||||
name = "futures-util"
|
||||
version = "0.3.24"
|
||||
version = "0.3.25"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "44fb6cb1be61cc1d2e43b262516aafcf63b241cffdb1d3fa115f91d9c7b09c90"
|
||||
checksum = "197676987abd2f9cadff84926f410af1c183608d36641465df73ae8211dc65d6"
|
||||
dependencies = [
|
||||
"futures-channel",
|
||||
"futures-core",
|
||||
@@ -3579,9 +3587,24 @@ dependencies = [
|
||||
name = "nym-sdk"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"client-connections",
|
||||
"client-core",
|
||||
"crypto",
|
||||
"examples-common",
|
||||
"futures",
|
||||
"gateway-client",
|
||||
"gateway-requests",
|
||||
"log",
|
||||
"network-defaults",
|
||||
"nymsphinx",
|
||||
"pretty_env_logger",
|
||||
"rand 0.7.3",
|
||||
"tap",
|
||||
"task",
|
||||
"thiserror",
|
||||
"tokio",
|
||||
"toml",
|
||||
"url",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -5986,18 +6009,18 @@ checksum = "b1141d4d61095b28419e22cb0bbf02755f5e54e0526f97f1e3d1d160e60885fb"
|
||||
|
||||
[[package]]
|
||||
name = "thiserror"
|
||||
version = "1.0.37"
|
||||
version = "1.0.38"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "10deb33631e3c9018b9baf9dcbbc4f737320d2b576bac10f6aefa048fa407e3e"
|
||||
checksum = "6a9cd18aa97d5c45c6603caea1da6628790b37f7a34b6ca89522331c5180fed0"
|
||||
dependencies = [
|
||||
"thiserror-impl",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "thiserror-impl"
|
||||
version = "1.0.37"
|
||||
version = "1.0.38"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "982d17546b47146b28f7c22e3d08465f6b8903d0ea13c1660d9d84a6e7adcdbb"
|
||||
checksum = "1fb327af4685e4d03fa8cbcf1716380da910eeb2bb8be417e7f9fd3fb164f36f"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
@@ -6217,9 +6240,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "toml"
|
||||
version = "0.5.8"
|
||||
version = "0.5.10"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a31142970826733df8241ef35dc040ef98c679ab14d7c3e54d827099b3acecaa"
|
||||
checksum = "1333c76748e868a4d9d1017b5ab53171dfd095f70c712fdb4653a406547f598f"
|
||||
dependencies = [
|
||||
"serde",
|
||||
]
|
||||
|
||||
@@ -33,12 +33,15 @@ use log::{debug, info};
|
||||
use nymsphinx::acknowledgements::AckKey;
|
||||
use nymsphinx::addressing::clients::Recipient;
|
||||
use nymsphinx::addressing::nodes::NodeIdentity;
|
||||
use nymsphinx::receiver::ReconstructedMessage;
|
||||
use std::sync::Arc;
|
||||
use std::time::Duration;
|
||||
use tap::TapFallible;
|
||||
use task::{TaskClient, TaskManager};
|
||||
use url::Url;
|
||||
|
||||
use super::received_buffer::ReceivedBufferMessage;
|
||||
|
||||
#[cfg(all(not(target_arch = "wasm32"), feature = "fs-surb-storage"))]
|
||||
pub mod non_wasm_helpers;
|
||||
|
||||
@@ -48,10 +51,30 @@ pub struct ClientInput {
|
||||
}
|
||||
|
||||
pub struct ClientOutput {
|
||||
pub shared_lane_queue_lengths: LaneQueueLengths,
|
||||
pub received_buffer_request_sender: ReceivedBufferRequestSender,
|
||||
}
|
||||
|
||||
impl ClientOutput {
|
||||
pub fn register_receiver(
|
||||
&mut self,
|
||||
) -> Result<mpsc::UnboundedReceiver<Vec<ReconstructedMessage>>, ClientCoreError> {
|
||||
let (reconstructed_sender, reconstructed_receiver) = mpsc::unbounded();
|
||||
|
||||
self.received_buffer_request_sender
|
||||
.unbounded_send(ReceivedBufferMessage::ReceiverAnnounce(
|
||||
reconstructed_sender,
|
||||
))
|
||||
.map_err(|_| ClientCoreError::FailedToRegisterReceiver)?;
|
||||
|
||||
Ok(reconstructed_receiver)
|
||||
}
|
||||
}
|
||||
|
||||
pub struct ClientState {
|
||||
pub shared_lane_queue_lengths: LaneQueueLengths,
|
||||
pub reply_controller_sender: ReplyControllerSender,
|
||||
}
|
||||
|
||||
pub enum ClientInputStatus {
|
||||
AwaitingProducer { client_input: ClientInput },
|
||||
Connected,
|
||||
@@ -475,11 +498,13 @@ where
|
||||
},
|
||||
client_output: ClientOutputStatus::AwaitingConsumer {
|
||||
client_output: ClientOutput {
|
||||
shared_lane_queue_lengths,
|
||||
received_buffer_request_sender,
|
||||
},
|
||||
},
|
||||
reply_controller_sender,
|
||||
client_state: ClientState {
|
||||
shared_lane_queue_lengths,
|
||||
reply_controller_sender,
|
||||
},
|
||||
task_manager,
|
||||
})
|
||||
}
|
||||
@@ -488,9 +513,7 @@ where
|
||||
pub struct BaseClient {
|
||||
pub client_input: ClientInputStatus,
|
||||
pub client_output: ClientOutputStatus,
|
||||
|
||||
// it feels very wrong to put this channel here, but I can't think of any other way of passing it to the native client
|
||||
pub reply_controller_sender: ReplyControllerSender,
|
||||
pub client_state: ClientState,
|
||||
|
||||
pub task_manager: TaskManager,
|
||||
}
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use crate::client::replies::reply_storage::{
|
||||
fs_backend, CombinedReplyStorage, ReplyStorageBackend,
|
||||
browser_backend, fs_backend, CombinedReplyStorage, ReplyStorageBackend,
|
||||
};
|
||||
use crate::config::DebugConfig;
|
||||
use crate::error::ClientCoreError;
|
||||
@@ -85,3 +85,10 @@ pub async fn setup_fs_reply_surb_backend<P: AsRef<Path>>(
|
||||
setup_fresh_backend(db_path, debug_config).await
|
||||
}
|
||||
}
|
||||
|
||||
pub fn setup_empty_reply_surb_backend(debug_config: &DebugConfig) -> browser_backend::Backend {
|
||||
browser_backend::Backend::new(
|
||||
debug_config.minimum_reply_surb_storage_threshold,
|
||||
debug_config.maximum_reply_surb_storage_threshold,
|
||||
)
|
||||
}
|
||||
|
||||
@@ -17,6 +17,8 @@ use std::sync::Arc;
|
||||
// use the old key after new one was issued.
|
||||
|
||||
// Remember that Arc<T> has Deref implementation for T
|
||||
// WIP(JON): let's try not to have the clone
|
||||
#[derive(Clone)]
|
||||
pub struct KeyManager {
|
||||
/// identity key associated with the client instance.
|
||||
identity_keypair: Arc<identity::KeyPair>,
|
||||
@@ -57,6 +59,20 @@ impl KeyManager {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn new_from_keys(
|
||||
id_keypair: identity::KeyPair,
|
||||
enc_keypair: encryption::KeyPair,
|
||||
gateway_shared_key: SharedKeys,
|
||||
ack_key: AckKey,
|
||||
) -> Self {
|
||||
Self {
|
||||
identity_keypair: Arc::new(id_keypair),
|
||||
encryption_keypair: Arc::new(enc_keypair),
|
||||
gateway_shared_key: Some(Arc::new(gateway_shared_key)),
|
||||
ack_key: Arc::new(ack_key),
|
||||
}
|
||||
}
|
||||
|
||||
// this is actually **NOT** dead code
|
||||
// I have absolutely no idea why the compiler insists it's unused. The call happens during client::init::execute
|
||||
#[allow(dead_code)]
|
||||
@@ -65,8 +81,8 @@ impl KeyManager {
|
||||
self.gateway_shared_key = Some(gateway_shared_key)
|
||||
}
|
||||
|
||||
/// Loads previously stored keys from the disk.
|
||||
pub fn load_keys(client_pathfinder: &ClientKeyPathfinder) -> io::Result<Self> {
|
||||
/// Loads previously stored client keys from the disk.
|
||||
fn load_keys_client_only(client_pathfinder: &ClientKeyPathfinder) -> io::Result<Self> {
|
||||
let identity_keypair: identity::KeyPair =
|
||||
pemstore::load_keypair(&pemstore::KeyPairPath::new(
|
||||
client_pathfinder.private_identity_key().to_owned(),
|
||||
@@ -78,21 +94,47 @@ impl KeyManager {
|
||||
client_pathfinder.public_encryption_key().to_owned(),
|
||||
))?;
|
||||
|
||||
let gateway_shared_key: SharedKeys =
|
||||
pemstore::load_key(client_pathfinder.gateway_shared_key())?;
|
||||
|
||||
let ack_key: AckKey = pemstore::load_key(client_pathfinder.ack_key())?;
|
||||
|
||||
// TODO: ack key is never stored so it is generated now. But perhaps it should be stored
|
||||
// after all for consistency sake?
|
||||
Ok(KeyManager {
|
||||
identity_keypair: Arc::new(identity_keypair),
|
||||
encryption_keypair: Arc::new(encryption_keypair),
|
||||
gateway_shared_key: Some(Arc::new(gateway_shared_key)),
|
||||
gateway_shared_key: None,
|
||||
ack_key: Arc::new(ack_key),
|
||||
})
|
||||
}
|
||||
|
||||
/// Loads previously stored keys from the disk. Fails if not all, including the shared gateway
|
||||
/// key, is available.
|
||||
pub fn load_keys(client_pathfinder: &ClientKeyPathfinder) -> io::Result<Self> {
|
||||
let mut key_manager = Self::load_keys_client_only(client_pathfinder)?;
|
||||
|
||||
let gateway_shared_key: SharedKeys =
|
||||
pemstore::load_key(client_pathfinder.gateway_shared_key())?;
|
||||
|
||||
key_manager.gateway_shared_key = Some(Arc::new(gateway_shared_key));
|
||||
|
||||
Ok(key_manager)
|
||||
}
|
||||
|
||||
pub fn load_keys_maybe_gateway(client_pathfinder: &ClientKeyPathfinder) -> io::Result<Self> {
|
||||
let mut key_manager = Self::load_keys_client_only(client_pathfinder)?;
|
||||
|
||||
let gateway_shared_key: Result<SharedKeys, io::Error> =
|
||||
pemstore::load_key(client_pathfinder.gateway_shared_key());
|
||||
|
||||
// It's ok if the gateway key was not found
|
||||
let gateway_shared_key = match gateway_shared_key {
|
||||
Err(err) if err.kind() == io::ErrorKind::NotFound => Ok(None),
|
||||
Err(err) => Err(err),
|
||||
Ok(key) => Ok(Some(key)),
|
||||
}?;
|
||||
|
||||
key_manager.gateway_shared_key = gateway_shared_key.map(Arc::new);
|
||||
|
||||
Ok(key_manager)
|
||||
}
|
||||
|
||||
// this is actually **NOT** dead code
|
||||
// I have absolutely no idea why the compiler insists it's unused. The call happens during client::init::execute
|
||||
#[allow(dead_code)]
|
||||
@@ -119,7 +161,21 @@ impl KeyManager {
|
||||
pemstore::store_key(self.ack_key.as_ref(), client_pathfinder.ack_key())?;
|
||||
|
||||
match self.gateway_shared_key.as_ref() {
|
||||
None => warn!("No gateway shared key available to store!"),
|
||||
None => debug!("No gateway shared key available to store!"),
|
||||
Some(gate_key) => {
|
||||
pemstore::store_key(gate_key.as_ref(), client_pathfinder.gateway_shared_key())?
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn store_key_gateway_only(
|
||||
&self,
|
||||
client_pathfinder: &ClientKeyPathfinder,
|
||||
) -> io::Result<()> {
|
||||
match self.gateway_shared_key.as_ref() {
|
||||
None => panic!("WIP(JON)"),
|
||||
Some(gate_key) => {
|
||||
pemstore::store_key(gate_key.as_ref(), client_pathfinder.gateway_shared_key())?
|
||||
}
|
||||
|
||||
+15
-4
@@ -213,7 +213,11 @@ impl ActionController {
|
||||
}
|
||||
|
||||
// note: when the entry expires it's automatically removed from pending_acks_timers
|
||||
fn handle_expired_ack_timer(&mut self, expired_ack: Expired<FragmentIdentifier>) {
|
||||
fn handle_expired_ack_timer(
|
||||
&mut self,
|
||||
expired_ack: Expired<FragmentIdentifier>,
|
||||
task_client: &mut task::TaskClient,
|
||||
) {
|
||||
// I'm honestly not sure how to handle it, because getting it means other things in our
|
||||
// system are already misbehaving. If we ever see this panic, then I guess we should worry
|
||||
// about it. Perhaps just reschedule it at later point?
|
||||
@@ -231,9 +235,16 @@ impl ActionController {
|
||||
// downgrading an arc and then upgrading vs cloning is difference of 30ns vs 15ns
|
||||
// so it's literally a NO difference while it might prevent us from unnecessarily
|
||||
// resending data (in maybe 1 in 1 million cases, but it's something)
|
||||
self.retransmission_sender
|
||||
if self
|
||||
.retransmission_sender
|
||||
.unbounded_send(Arc::downgrade(pending_ack_data))
|
||||
.unwrap()
|
||||
.is_err()
|
||||
{
|
||||
assert!(
|
||||
task_client.is_shutdown_poll(),
|
||||
"Failed to send pending ack for retransmission"
|
||||
);
|
||||
}
|
||||
} else {
|
||||
// this shouldn't cause any issues but shouldn't have happened to begin with!
|
||||
error!("An already removed pending ack has expired")
|
||||
@@ -264,7 +275,7 @@ impl ActionController {
|
||||
}
|
||||
},
|
||||
expired_ack = self.pending_acks_timers.next() => match expired_ack {
|
||||
Some(expired_ack) => self.handle_expired_ack_timer(expired_ack),
|
||||
Some(expired_ack) => self.handle_expired_ack_timer(expired_ack, &mut shutdown),
|
||||
None => {
|
||||
log::trace!("ActionController: Stopping since ack channel closed");
|
||||
break;
|
||||
|
||||
@@ -99,6 +99,24 @@ impl ReplyControllerSender {
|
||||
}
|
||||
}
|
||||
|
||||
pub struct ReplyQueueLengths {
|
||||
reply_controller_sender: ReplyControllerSender,
|
||||
}
|
||||
|
||||
impl ReplyQueueLengths {
|
||||
pub fn new(reply_controller_sender: ReplyControllerSender) -> Self {
|
||||
Self {
|
||||
reply_controller_sender,
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn get_lane_queue_length(&self, connection_id: ConnectionId) -> usize {
|
||||
self.reply_controller_sender
|
||||
.get_lane_queue_length(connection_id)
|
||||
.await
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) type ReplyControllerReceiver = mpsc::UnboundedReceiver<ReplyControllerMessage>;
|
||||
|
||||
#[derive(Debug)]
|
||||
|
||||
@@ -7,6 +7,7 @@ use async_trait::async_trait;
|
||||
|
||||
// well, right now we don't have the browser storage : (
|
||||
// so we keep everything in memory
|
||||
#[derive(Debug)]
|
||||
pub struct Backend {
|
||||
empty: Empty,
|
||||
}
|
||||
|
||||
@@ -6,10 +6,12 @@ use async_trait::async_trait;
|
||||
use std::error::Error;
|
||||
use thiserror::Error;
|
||||
|
||||
#[cfg(target_arch = "wasm32")]
|
||||
//#[cfg(target_arch = "wasm32")]
|
||||
//#[cfg(all(not(target_arch = "wasm32"), feature = "fs-surb-storage"))]
|
||||
pub mod browser_backend;
|
||||
|
||||
#[cfg(all(not(target_arch = "wasm32"), feature = "fs-surb-storage"))]
|
||||
//#[cfg(target_arch = "wasm32")]
|
||||
pub mod fs_backend;
|
||||
|
||||
// #[cfg(all(test, feature = "std"))]
|
||||
@@ -19,10 +21,11 @@ pub mod fs_backend;
|
||||
#[error("no information provided")]
|
||||
pub struct UndefinedError;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct Empty {
|
||||
// we need to keep 'basic' metadata here to "load" the CombinedReplyStorage
|
||||
min_surb_threshold: usize,
|
||||
max_surb_threshold: usize,
|
||||
pub min_surb_threshold: usize,
|
||||
pub max_surb_threshold: usize,
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
|
||||
@@ -7,12 +7,12 @@ use std::path::{Path, PathBuf};
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct ClientKeyPathfinder {
|
||||
identity_private_key: PathBuf,
|
||||
identity_public_key: PathBuf,
|
||||
encryption_private_key: PathBuf,
|
||||
encryption_public_key: PathBuf,
|
||||
gateway_shared_key: PathBuf,
|
||||
ack_key: PathBuf,
|
||||
pub identity_private_key: PathBuf,
|
||||
pub identity_public_key: PathBuf,
|
||||
pub encryption_private_key: PathBuf,
|
||||
pub encryption_public_key: PathBuf,
|
||||
pub gateway_shared_key: PathBuf,
|
||||
pub ack_key: PathBuf,
|
||||
}
|
||||
|
||||
impl ClientKeyPathfinder {
|
||||
@@ -22,13 +22,25 @@ impl ClientKeyPathfinder {
|
||||
ClientKeyPathfinder {
|
||||
identity_private_key: config_dir.join("private_identity.pem"),
|
||||
identity_public_key: config_dir.join("public_identity.pem"),
|
||||
encryption_private_key: config_dir.join("public_encryption.pem"),
|
||||
encryption_public_key: config_dir.join("private_encryption.pem"),
|
||||
encryption_private_key: config_dir.join("private_encryption.pem"),
|
||||
encryption_public_key: config_dir.join("public_encryption.pem"),
|
||||
gateway_shared_key: config_dir.join("gateway_shared.pem"),
|
||||
ack_key: config_dir.join("ack_key.pem"),
|
||||
}
|
||||
}
|
||||
|
||||
//pub fn new_from_dir(config_dir: &PathBuf) -> Self {
|
||||
// dbg!(&config_dir);
|
||||
// ClientKeyPathfinder {
|
||||
// identity_private_key: config_dir.join("private_identity.pem"),
|
||||
// identity_public_key: config_dir.join("public_identity.pem"),
|
||||
// encryption_private_key: config_dir.join("public_encryption.pem"),
|
||||
// encryption_public_key: config_dir.join("private_encryption.pem"),
|
||||
// gateway_shared_key: config_dir.join("gateway_shared.pem"),
|
||||
// ack_key: config_dir.join("ack_key.pem"),
|
||||
// }
|
||||
//}
|
||||
|
||||
pub fn new_from_config<T: NymConfig>(config: &Config<T>) -> Self {
|
||||
ClientKeyPathfinder {
|
||||
identity_private_key: config.get_private_identity_key_file(),
|
||||
@@ -40,6 +52,19 @@ impl ClientKeyPathfinder {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn any_file_exists(&self) -> bool {
|
||||
matches!(self.identity_public_key.try_exists(), Ok(true))
|
||||
|| matches!(self.identity_private_key.try_exists(), Ok(true))
|
||||
|| matches!(self.encryption_public_key.try_exists(), Ok(true))
|
||||
|| matches!(self.encryption_private_key.try_exists(), Ok(true))
|
||||
|| matches!(self.gateway_shared_key.try_exists(), Ok(true))
|
||||
|| matches!(self.ack_key.try_exists(), Ok(true))
|
||||
}
|
||||
|
||||
pub fn gateway_key_file_exists(&self) -> bool {
|
||||
matches!(self.gateway_shared_key.try_exists(), Ok(true))
|
||||
}
|
||||
|
||||
pub fn private_identity_key(&self) -> &Path {
|
||||
&self.identity_private_key
|
||||
}
|
||||
|
||||
@@ -55,6 +55,9 @@ pub enum ClientCoreError {
|
||||
#[error("The address of the gateway is unknown - did you run init?")]
|
||||
GatwayAddressUnknown,
|
||||
|
||||
#[error("failed to register receiver for reconstructed mixnet messages")]
|
||||
FailedToRegisterReceiver,
|
||||
|
||||
#[error("Unexpected exit")]
|
||||
UnexpectedExit,
|
||||
}
|
||||
|
||||
@@ -10,7 +10,7 @@ use config::NymConfig;
|
||||
use crypto::asymmetric::identity;
|
||||
use gateway_client::GatewayClient;
|
||||
use gateway_requests::registration::handshake::SharedKeys;
|
||||
use rand::{rngs::OsRng, seq::SliceRandom, thread_rng};
|
||||
use rand::{seq::SliceRandom, thread_rng};
|
||||
use std::{sync::Arc, time::Duration};
|
||||
use tap::TapFallible;
|
||||
use topology::{filter::VersionFilterable, gateway};
|
||||
@@ -51,7 +51,7 @@ pub(super) async fn query_gateway_details(
|
||||
}
|
||||
}
|
||||
|
||||
async fn register_with_gateway(
|
||||
pub(super) async fn register_with_gateway(
|
||||
gateway: &gateway::Node,
|
||||
our_identity: Arc<identity::KeyPair>,
|
||||
) -> Result<Arc<SharedKeys>, ClientCoreError> {
|
||||
@@ -74,20 +74,13 @@ async fn register_with_gateway(
|
||||
Ok(shared_keys)
|
||||
}
|
||||
|
||||
pub(super) async fn register_with_gateway_and_store_keys<T>(
|
||||
gateway_details: gateway::Node,
|
||||
pub(super) fn store_keys<T>(
|
||||
key_manager: &KeyManager,
|
||||
config: &Config<T>,
|
||||
) -> Result<(), ClientCoreError>
|
||||
where
|
||||
T: NymConfig,
|
||||
{
|
||||
let mut rng = OsRng;
|
||||
let mut key_manager = KeyManager::new(&mut rng);
|
||||
|
||||
let shared_keys =
|
||||
register_with_gateway(&gateway_details, key_manager.identity_keypair()).await?;
|
||||
key_manager.insert_gateway_shared_key(shared_keys);
|
||||
|
||||
let pathfinder = ClientKeyPathfinder::new_from_config(config);
|
||||
Ok(key_manager
|
||||
.store_keys(&pathfinder)
|
||||
|
||||
@@ -6,23 +6,26 @@
|
||||
use std::fmt::Display;
|
||||
|
||||
use nymsphinx::addressing::{clients::Recipient, nodes::NodeIdentity};
|
||||
use rand::rngs::OsRng;
|
||||
use serde::Serialize;
|
||||
use tap::TapFallible;
|
||||
|
||||
use config::NymConfig;
|
||||
use crypto::asymmetric::{encryption, identity};
|
||||
use url::Url;
|
||||
|
||||
use crate::client::key_manager::KeyManager;
|
||||
use crate::{
|
||||
config::{
|
||||
persistence::key_pathfinder::ClientKeyPathfinder, ClientCoreConfigTrait, Config,
|
||||
GatewayEndpointConfig,
|
||||
},
|
||||
error::ClientCoreError,
|
||||
init::helpers::{query_gateway_details, register_with_gateway_and_store_keys},
|
||||
};
|
||||
|
||||
mod helpers;
|
||||
|
||||
/// Struct describing the results of the client initialization procedure.
|
||||
#[derive(Debug, Serialize)]
|
||||
pub struct InitResults {
|
||||
version: String,
|
||||
@@ -60,6 +63,12 @@ impl Display for InitResults {
|
||||
}
|
||||
}
|
||||
|
||||
/// Create a new set of client keys.
|
||||
pub fn new_client_keys() -> KeyManager {
|
||||
let mut rng = OsRng;
|
||||
KeyManager::new(&mut rng)
|
||||
}
|
||||
|
||||
/// Convenience function for setting up the gateway for a client. Depending on the arguments given
|
||||
/// it will do the sensible thing.
|
||||
pub async fn setup_gateway<C, T>(
|
||||
@@ -74,7 +83,7 @@ where
|
||||
{
|
||||
let id = config.get_id();
|
||||
if register_gateway {
|
||||
register_with_gateway(user_chosen_gateway_id, config).await
|
||||
register_with_gateway_and_store(user_chosen_gateway_id, config).await
|
||||
} else if let Some(user_chosen_gateway_id) = user_chosen_gateway_id {
|
||||
config_gateway_with_existing_keys(user_chosen_gateway_id, config).await
|
||||
} else {
|
||||
@@ -82,27 +91,51 @@ where
|
||||
}
|
||||
}
|
||||
|
||||
/// Get the gateway details by querying the validator-api. Either pick one at random or use
|
||||
/// the chosen one if it's among the available ones.
|
||||
pub async fn register_with_gateway(
|
||||
key_manager: &mut KeyManager,
|
||||
nym_api_endpoints: Vec<Url>,
|
||||
chosen_gateway_id: Option<String>,
|
||||
) -> Result<GatewayEndpointConfig, ClientCoreError> {
|
||||
// Our identity is derived from our key
|
||||
let our_identity = key_manager.identity_keypair();
|
||||
|
||||
// Get the gateway details of the gateway we will use
|
||||
let gateway = helpers::query_gateway_details(nym_api_endpoints, chosen_gateway_id).await?;
|
||||
log::debug!("Querying gateway gives: {}", gateway);
|
||||
|
||||
// Establish connection, authenticate and generate keys for talking with the gateway
|
||||
let shared_keys = helpers::register_with_gateway(&gateway, our_identity).await?;
|
||||
key_manager.insert_gateway_shared_key(shared_keys);
|
||||
|
||||
Ok(gateway.into())
|
||||
}
|
||||
|
||||
/// Get the gateway details by querying the validator-api. Either pick one at random or use
|
||||
/// the chosen one if it's among the available ones.
|
||||
/// Saves keys to disk, specified by the paths in `config`.
|
||||
pub async fn register_with_gateway<T>(
|
||||
user_chosen_gateway_id: Option<String>,
|
||||
pub async fn register_with_gateway_and_store<T>(
|
||||
chosen_gateway_id: Option<String>,
|
||||
config: &Config<T>,
|
||||
) -> Result<GatewayEndpointConfig, ClientCoreError>
|
||||
where
|
||||
T: NymConfig,
|
||||
{
|
||||
println!("Configuring gateway");
|
||||
let gateway =
|
||||
query_gateway_details(config.get_nym_api_endpoints(), user_chosen_gateway_id).await?;
|
||||
log::debug!("Querying gateway gives: {}", gateway);
|
||||
let mut key_manager = new_client_keys();
|
||||
|
||||
// Registering with gateway by setting up and writing shared keys to disk
|
||||
log::trace!("Registering gateway");
|
||||
register_with_gateway_and_store_keys(gateway.clone(), config).await?;
|
||||
let gateway = register_with_gateway(
|
||||
&mut key_manager,
|
||||
config.get_nym_api_endpoints(),
|
||||
chosen_gateway_id,
|
||||
)
|
||||
.await?;
|
||||
|
||||
helpers::store_keys(&key_manager, config)?;
|
||||
println!("Saved all generated keys");
|
||||
|
||||
Ok(gateway.into())
|
||||
Ok(gateway)
|
||||
}
|
||||
|
||||
/// Set the gateway using the usual procedue of querying the validator-api, but don't register or
|
||||
@@ -117,8 +150,11 @@ where
|
||||
T: NymConfig,
|
||||
{
|
||||
println!("Using gateway provided by user, keeping existing keys");
|
||||
let gateway =
|
||||
query_gateway_details(config.get_nym_api_endpoints(), Some(user_chosen_gateway_id)).await?;
|
||||
let gateway = helpers::query_gateway_details(
|
||||
config.get_nym_api_endpoints(),
|
||||
Some(user_chosen_gateway_id),
|
||||
)
|
||||
.await?;
|
||||
log::debug!("Querying gateway gives: {}", gateway);
|
||||
Ok(gateway.into())
|
||||
}
|
||||
@@ -143,6 +179,20 @@ where
|
||||
})
|
||||
}
|
||||
|
||||
/// Get the full client address from the client keys and the gateway identity
|
||||
pub fn get_client_address(
|
||||
key_manager: &KeyManager,
|
||||
gateway_config: &GatewayEndpointConfig,
|
||||
) -> Recipient {
|
||||
Recipient::new(
|
||||
*key_manager.identity_keypair().public_key(),
|
||||
*key_manager.encryption_keypair().public_key(),
|
||||
// TODO: below only works under assumption that gateway address == gateway id
|
||||
// (which currently is true)
|
||||
NodeIdentity::from_base58_string(&gateway_config.gateway_id).unwrap(),
|
||||
)
|
||||
}
|
||||
|
||||
/// Get the client address by loading the keys from stored files.
|
||||
pub fn get_client_address_from_stored_keys<T>(
|
||||
config: &Config<T>,
|
||||
|
||||
@@ -8,12 +8,11 @@ use crate::error::ClientError;
|
||||
use crate::websocket;
|
||||
use client_connections::TransmissionLane;
|
||||
use client_core::client::base_client::{
|
||||
non_wasm_helpers, BaseClientBuilder, ClientInput, ClientOutput,
|
||||
non_wasm_helpers, BaseClientBuilder, ClientInput, ClientOutput, ClientState,
|
||||
};
|
||||
use client_core::client::inbound_messages::InputMessage;
|
||||
use client_core::client::key_manager::KeyManager;
|
||||
use client_core::client::received_buffer::{ReceivedBufferMessage, ReconstructedMessagesReceiver};
|
||||
use client_core::client::replies::reply_controller::requests::ReplyControllerSender;
|
||||
use client_core::config::persistence::key_pathfinder::ClientKeyPathfinder;
|
||||
use futures::channel::mpsc;
|
||||
use gateway_client::bandwidth::BandwidthController;
|
||||
@@ -87,8 +86,8 @@ impl SocketClient {
|
||||
config: &Config,
|
||||
client_input: ClientInput,
|
||||
client_output: ClientOutput,
|
||||
client_state: ClientState,
|
||||
self_address: &Recipient,
|
||||
reply_controller_sender: ReplyControllerSender,
|
||||
shutdown: task::TaskClient,
|
||||
) {
|
||||
info!("Starting websocket listener...");
|
||||
@@ -99,10 +98,14 @@ impl SocketClient {
|
||||
} = client_input;
|
||||
|
||||
let ClientOutput {
|
||||
shared_lane_queue_lengths,
|
||||
received_buffer_request_sender,
|
||||
} = client_output;
|
||||
|
||||
let ClientState {
|
||||
shared_lane_queue_lengths,
|
||||
reply_controller_sender,
|
||||
} = client_state;
|
||||
|
||||
let websocket_handler = websocket::HandlerBuilder::new(
|
||||
input_sender,
|
||||
connection_command_sender,
|
||||
@@ -151,13 +154,14 @@ impl SocketClient {
|
||||
let mut started_client = base_builder.start_base().await?;
|
||||
let client_input = started_client.client_input.register_producer();
|
||||
let client_output = started_client.client_output.register_consumer();
|
||||
let client_state = started_client.client_state;
|
||||
|
||||
Self::start_websocket_listener(
|
||||
&self.config,
|
||||
client_input,
|
||||
client_output,
|
||||
client_state,
|
||||
&self_address,
|
||||
started_client.reply_controller_sender,
|
||||
started_client.task_manager.subscribe(),
|
||||
);
|
||||
|
||||
|
||||
@@ -9,7 +9,7 @@ use crate::socks::{
|
||||
server::SphinxSocksServer,
|
||||
};
|
||||
use client_core::client::base_client::{
|
||||
non_wasm_helpers, BaseClientBuilder, ClientInput, ClientOutput,
|
||||
non_wasm_helpers, BaseClientBuilder, ClientInput, ClientOutput, ClientState,
|
||||
};
|
||||
use client_core::client::key_manager::KeyManager;
|
||||
use client_core::config::persistence::key_pathfinder::ClientKeyPathfinder;
|
||||
@@ -95,6 +95,7 @@ impl NymClient {
|
||||
config: &Config,
|
||||
client_input: ClientInput,
|
||||
client_output: ClientOutput,
|
||||
client_status: ClientState,
|
||||
self_address: Recipient,
|
||||
shutdown: TaskClient,
|
||||
) {
|
||||
@@ -108,10 +109,14 @@ impl NymClient {
|
||||
} = client_input;
|
||||
|
||||
let ClientOutput {
|
||||
shared_lane_queue_lengths,
|
||||
received_buffer_request_sender,
|
||||
} = client_output;
|
||||
|
||||
let ClientState {
|
||||
shared_lane_queue_lengths,
|
||||
reply_controller_sender: _,
|
||||
} = client_status;
|
||||
|
||||
let authenticator = Authenticator::new(auth_methods, allowed_users);
|
||||
let mut sphinx_socks = SphinxSocksServer::new(
|
||||
config.get_listening_port(),
|
||||
@@ -218,11 +223,13 @@ impl NymClient {
|
||||
let mut started_client = base_builder.start_base().await?;
|
||||
let client_input = started_client.client_input.register_producer();
|
||||
let client_output = started_client.client_output.register_consumer();
|
||||
let client_state = started_client.client_state;
|
||||
|
||||
Self::start_socks5_listener(
|
||||
&self.config,
|
||||
client_input,
|
||||
client_output,
|
||||
client_state,
|
||||
self_address,
|
||||
started_client.task_manager.subscribe(),
|
||||
);
|
||||
|
||||
@@ -208,7 +208,7 @@ pub struct TaskClient {
|
||||
|
||||
impl TaskClient {
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
const SHUTDOWN_TIMEOUT: Duration = Duration::from_secs(5);
|
||||
const SHUTDOWN_TIMEOUT: Duration = Duration::from_secs(1);
|
||||
|
||||
fn new(
|
||||
notify: watch::Receiver<()>,
|
||||
@@ -300,8 +300,8 @@ impl TaskClient {
|
||||
has_changed
|
||||
}
|
||||
Err(err) => {
|
||||
log::debug!("Polling shutdown failed: {err}");
|
||||
log::debug!("Assuming this means we should shutdown...");
|
||||
log::error!("Polling shutdown failed: {err}");
|
||||
log::error!("Assuming this means we should shutdown...");
|
||||
true
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,8 +6,24 @@ edition = "2021"
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[dependencies]
|
||||
client-core = { path = "../../../clients/client-core" }
|
||||
client-connections = { path = "../../../common/client-connections" }
|
||||
client-core = { path = "../../../clients/client-core", features = ["fs-surb-storage"]}
|
||||
crypto = { path = "../../../common/crypto" }
|
||||
gateway-client = { path = "../../../common/client-libs/gateway-client" }
|
||||
gateway-requests = { path = "../../../gateway/gateway-requests" }
|
||||
network-defaults = { path = "../../../common/network-defaults" }
|
||||
nymsphinx = { path = "../../../common/nymsphinx" }
|
||||
task = { path = "../../../common/task" }
|
||||
|
||||
futures = "0.3"
|
||||
log = "0.4"
|
||||
rand = { version = "0.7.3" }
|
||||
tap = "1.0.1"
|
||||
thiserror = "1.0.38"
|
||||
url = "2.2"
|
||||
toml = "0.5.10"
|
||||
|
||||
[dev-dependencies]
|
||||
tokio = { version = "1", features = ["full"] }
|
||||
pretty_env_logger = "0.4.0"
|
||||
tokio = { version = "1", features = ["full"] }
|
||||
examples-common = { path = "examples-common" }
|
||||
|
||||
@@ -0,0 +1,10 @@
|
||||
[package]
|
||||
name = "examples-common"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[dependencies]
|
||||
log = "0.4.17"
|
||||
pretty_env_logger = "0.4.0"
|
||||
@@ -0,0 +1,21 @@
|
||||
pub fn setup_logging() {
|
||||
let mut log_builder = pretty_env_logger::formatted_timed_builder();
|
||||
if let Ok(s) = ::std::env::var("RUST_LOG") {
|
||||
log_builder.parse_filters(&s);
|
||||
} else {
|
||||
// default to 'Info'
|
||||
log_builder.filter(None, log::LevelFilter::Info);
|
||||
}
|
||||
|
||||
log_builder
|
||||
.filter_module("hyper", log::LevelFilter::Warn)
|
||||
.filter_module("tokio_reactor", log::LevelFilter::Warn)
|
||||
.filter_module("reqwest", log::LevelFilter::Warn)
|
||||
.filter_module("mio", log::LevelFilter::Warn)
|
||||
.filter_module("want", log::LevelFilter::Warn)
|
||||
.filter_module("tungstenite", log::LevelFilter::Warn)
|
||||
.filter_module("tokio_tungstenite", log::LevelFilter::Warn)
|
||||
.filter_module("handlebars", log::LevelFilter::Warn)
|
||||
.filter_module("sled", log::LevelFilter::Warn)
|
||||
.init();
|
||||
}
|
||||
@@ -1,24 +1,40 @@
|
||||
use std::path::PathBuf;
|
||||
|
||||
use examples_common::setup_logging;
|
||||
use nym_sdk::mixnet;
|
||||
|
||||
#[tokio::main]
|
||||
async fn main() {
|
||||
// specify some config options
|
||||
let keys = Some(PathBuf::from("~/.nym/clients/superfoomp"));
|
||||
let config = mixnet::Config { keys };
|
||||
setup_logging();
|
||||
|
||||
let client = mixnet::Client::new(Some(config)); // passing a config allows the user to set values
|
||||
// Specify some config options
|
||||
let config_dir = PathBuf::from("/tmp/mixnet-client");
|
||||
|
||||
// let show_receive = move || println!("got a message from the mixnet: {}", message); // might need to bury this in a struct as a `FnOnce`, see https://stackoverflow.com/questions/41081240/idiomatic-callbacks-in-rust
|
||||
// client.on_receive(show_receive); // have some way to pipe any received info to a function for processing
|
||||
// Setting `KeyMode::Keep` will use existing keys, and existing config, if there is one.
|
||||
// Regardles of `user_chosen_gateway`.
|
||||
let keys = mixnet::KeyPaths::new_from_dir(mixnet::KeyMode::Keep, &config_dir);
|
||||
|
||||
// connect to the mixnet, now we're listening for incoming
|
||||
client.connect_to_mixnet();
|
||||
// Provide key paths for the client to read/write keys to.
|
||||
let mut client = mixnet::Client::new(None, Some(keys)).unwrap();
|
||||
|
||||
// be able to get our client address
|
||||
println!("Our client address is {}", client.nym_address);
|
||||
// Connect to the mixnet, now we're listening for incoming
|
||||
client.connect_to_mixnet().await;
|
||||
|
||||
// send important info up the pipe to a buddy
|
||||
client.send_str("foo.bar@blah", "flappappa");
|
||||
// Be able to get our client address
|
||||
let our_address = client.nym_address().unwrap();
|
||||
println!("Our client nym address is: {our_address}");
|
||||
|
||||
// Send a message throught the mixnet to ourselves
|
||||
client
|
||||
.send_str(&our_address.to_string(), "hello there")
|
||||
.await;
|
||||
|
||||
println!("Waiting for message");
|
||||
if let Some(received) = client.wait_for_messages().await {
|
||||
for r in received {
|
||||
println!("Received: {}", String::from_utf8_lossy(&r.message));
|
||||
}
|
||||
}
|
||||
|
||||
client.disconnect().await;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,45 @@
|
||||
use examples_common::setup_logging;
|
||||
use nym_sdk::mixnet;
|
||||
|
||||
#[tokio::main]
|
||||
async fn main() {
|
||||
setup_logging();
|
||||
|
||||
// We can set a few options
|
||||
let user_chosen_gateway_id = None;
|
||||
let nym_api_endpointgs = vec![];
|
||||
let config = mixnet::Config::new(user_chosen_gateway_id, nym_api_endpointgs);
|
||||
|
||||
let mut client = mixnet::Client::new(Some(config), None).unwrap();
|
||||
|
||||
// In this we want to provide our own gateway config struct, and handle persisting this info to disk
|
||||
// ourselves (e.g., as part of our own configuration file).
|
||||
// NOTE: gateway shared key is written to disk according to the path given earlier
|
||||
// Checks if we have a shared gateway key loaded
|
||||
let first_run = true;
|
||||
if first_run {
|
||||
client.register_with_gateway().await;
|
||||
write_to_storage(client.get_keys(), client.get_gateway_endpoint().unwrap());
|
||||
} else {
|
||||
let (keys, gateway_config) = read_from_storage();
|
||||
client.set_keys(&keys);
|
||||
client.set_gateway_endpoint(&gateway_config);
|
||||
}
|
||||
|
||||
// Connect to the mixnet, now we're listening for incoming
|
||||
client.connect_to_mixnet().await;
|
||||
|
||||
// Be able to get our client address
|
||||
println!("Our client address is {}", client.nym_address().unwrap());
|
||||
|
||||
// Send important info up the pipe to a buddy
|
||||
client.send_str("foo.bar@blah", "flappappa").await;
|
||||
}
|
||||
|
||||
fn write_to_storage(_keys: &mixnet::Keys, _gateway_config: &mixnet::GatewayEndpointConfig) {
|
||||
todo!();
|
||||
}
|
||||
|
||||
fn read_from_storage() -> (mixnet::Keys, mixnet::GatewayEndpointConfig) {
|
||||
todo!();
|
||||
}
|
||||
@@ -1,22 +1,27 @@
|
||||
use examples_common::setup_logging;
|
||||
use nym_sdk::mixnet;
|
||||
|
||||
#[tokio::main]
|
||||
async fn main() {
|
||||
// here's what I'd actually like to write
|
||||
let client = mixnet::Client::new(None); // passing no config makes the client fire up an ephemeral session and figure shit out on its own
|
||||
setup_logging();
|
||||
|
||||
// let show_receive = move || println!("got a message from the mixnet: {}", message); // might need to bury this in a struct as a `FnMut`, see https://stackoverflow.com/questions/41081240/idiomatic-callbacks-in-rust
|
||||
// connect to the mixnet, now we're listening for incoming
|
||||
client.connect_to_mixnet();
|
||||
// Passing no config makes the client fire up an ephemeral session and figure shit out on its own
|
||||
let mut client = mixnet::Client::new(None, None).unwrap();
|
||||
|
||||
// be able to get our client address
|
||||
println!("Our client address is {}", client.nym_address);
|
||||
// Connect to the mixnet, now we're listening for incoming
|
||||
client.connect_to_mixnet().await;
|
||||
|
||||
// send important string info up the pipe to a buddy
|
||||
client.send_str("foo.bar@blah", "flappappa");
|
||||
// Be able to get our client address
|
||||
let our_address = client.nym_address().unwrap();
|
||||
println!("Our client nym address is: {our_address}");
|
||||
|
||||
// send some bytes to a buddy
|
||||
client.send_bytes("foo.bar@blah", "flappappa".as_bytes().to_vec());
|
||||
// Send a message throught the mixnet to ourselves
|
||||
client
|
||||
.send_str(&our_address.to_string(), "hello there")
|
||||
.await;
|
||||
|
||||
|
||||
println!("Waiting for message");
|
||||
client
|
||||
.on_messages(|msg| println!("Received: {}", String::from_utf8_lossy(&msg.message)))
|
||||
.await;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,10 @@
|
||||
#[derive(Debug, thiserror::Error)]
|
||||
pub enum Error {
|
||||
#[error("i/o error: {0}")]
|
||||
IoError(#[from] std::io::Error),
|
||||
|
||||
#[error("key file encountered that we don't want to overwrite")]
|
||||
DontOverwrite,
|
||||
}
|
||||
|
||||
pub type Result<T, E = Error> = std::result::Result<T, E>;
|
||||
@@ -1 +1,2 @@
|
||||
pub mod error;
|
||||
pub mod mixnet;
|
||||
|
||||
@@ -0,0 +1,15 @@
|
||||
mod client;
|
||||
mod config;
|
||||
mod connection_state;
|
||||
mod key_paths;
|
||||
|
||||
pub use client_core::config::GatewayEndpointConfig;
|
||||
pub use nymsphinx::{
|
||||
addressing::clients::{ClientIdentity, Recipient},
|
||||
receiver::ReconstructedMessage,
|
||||
};
|
||||
|
||||
pub use key_paths::{GatewayKeyMode, KeyMode, KeyPaths, Keys};
|
||||
|
||||
pub use client::Client;
|
||||
pub use config::Config;
|
||||
@@ -0,0 +1,306 @@
|
||||
use futures::StreamExt;
|
||||
use nymsphinx::{
|
||||
addressing::clients::{ClientIdentity, Recipient},
|
||||
receiver::ReconstructedMessage,
|
||||
};
|
||||
use tap::TapOptional;
|
||||
|
||||
use client_connections::TransmissionLane;
|
||||
use client_core::{
|
||||
client::{
|
||||
base_client::{non_wasm_helpers, BaseClientBuilder},
|
||||
inbound_messages::InputMessage,
|
||||
key_manager::KeyManager,
|
||||
},
|
||||
config::{persistence::key_pathfinder::ClientKeyPathfinder, GatewayEndpointConfig},
|
||||
};
|
||||
|
||||
use crate::error::{Error, Result};
|
||||
|
||||
use super::{connection_state::ConnectionState, Config, GatewayKeyMode, KeyPaths, Keys};
|
||||
|
||||
pub struct Client {
|
||||
/// Keys handled by the client
|
||||
key_manager: KeyManager,
|
||||
|
||||
/// Client configuration
|
||||
config: Config,
|
||||
|
||||
/// Paths for client keys, including identity, encryption, ack and shared gateway keys.
|
||||
key_paths: Option<KeyPaths>,
|
||||
|
||||
/// The client can be in one of multiple states, depending on how it is created and if it's
|
||||
/// connected to the mixnet.
|
||||
connection_state: ConnectionState,
|
||||
}
|
||||
|
||||
impl Client {
|
||||
/// Create a new mixnet client. If no config options are supplied, creates a new client with
|
||||
/// ephemeral keys stored in RAM, which will be discarded at application close.
|
||||
///
|
||||
/// Callers have the option of supplying futher parameters to store persistent identities at a
|
||||
/// location on-disk, if desired.
|
||||
pub fn new(config_option: Option<Config>, key_paths: Option<KeyPaths>) -> Result<Client> {
|
||||
let config = config_option.unwrap_or_default();
|
||||
|
||||
// If we are provided paths to keys, use them if they are available. And if they are
|
||||
// not, write the generated keys back to storage.
|
||||
let key_manager = if let Some(ref key_paths) = key_paths {
|
||||
let path_finder = ClientKeyPathfinder::from(key_paths.clone());
|
||||
|
||||
// Try load keys
|
||||
match KeyManager::load_keys_maybe_gateway(&path_finder) {
|
||||
Ok(key_manager) => key_manager,
|
||||
Err(err) => {
|
||||
log::debug!("Not loading keys: {err}");
|
||||
if path_finder.any_file_exists() && key_paths.operating_mode.is_keep() {
|
||||
return Err(Error::DontOverwrite);
|
||||
}
|
||||
|
||||
// Create new keys and write to storage
|
||||
let key_manager = client_core::init::new_client_keys();
|
||||
// WARN: this will overwrite!
|
||||
key_manager.store_keys(&path_finder)?;
|
||||
key_manager
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// Ephemeral keys that we only store in memory
|
||||
client_core::init::new_client_keys()
|
||||
};
|
||||
|
||||
Ok(Client {
|
||||
key_manager,
|
||||
config,
|
||||
key_paths,
|
||||
connection_state: ConnectionState::New,
|
||||
})
|
||||
}
|
||||
|
||||
/// Get the client identity, which is the public key of the identity key pair.
|
||||
pub fn identity(&self) -> ClientIdentity {
|
||||
*self.key_manager.identity_keypair().public_key()
|
||||
}
|
||||
|
||||
/// Get the nym address for this client, if it is available. The nym address is composed of the
|
||||
/// client identity, the client encryption key, and the gateway identity.
|
||||
pub fn nym_address(&self) -> Option<&Recipient> {
|
||||
self.connection_state.nym_address()
|
||||
}
|
||||
|
||||
/// Client keys are generated at client creation if none were found. The gateway shared
|
||||
/// key, however, is created during the gateway registration handshake so it might not
|
||||
/// necessarily be available.
|
||||
pub fn has_gateway_key(&self) -> bool {
|
||||
self.key_manager.gateway_key_set()
|
||||
}
|
||||
|
||||
pub fn set_keys(&mut self, _keys: &Keys) {
|
||||
todo!();
|
||||
}
|
||||
|
||||
pub fn get_keys(&self) -> &Keys {
|
||||
todo!();
|
||||
}
|
||||
|
||||
pub fn set_gateway_endpoint(&mut self, _gateway_endpoint_config: &GatewayEndpointConfig) {
|
||||
todo!();
|
||||
}
|
||||
|
||||
pub fn get_gateway_endpoint(&self) -> Option<&GatewayEndpointConfig> {
|
||||
self.connection_state.gateway_endpoint_config()
|
||||
}
|
||||
|
||||
pub async fn register_with_gateway(&mut self) {
|
||||
assert!(
|
||||
matches!(self.connection_state, ConnectionState::New),
|
||||
"can only setup gateway when in `New` connection state"
|
||||
);
|
||||
|
||||
let gateway_config = client_core::init::register_with_gateway(
|
||||
&mut self.key_manager,
|
||||
self.config.nym_api_endpoints.clone(),
|
||||
self.config.user_chosen_gateway.clone(),
|
||||
)
|
||||
.await
|
||||
.expect("WIP");
|
||||
|
||||
let nym_address = client_core::init::get_client_address(&self.key_manager, &gateway_config);
|
||||
|
||||
self.connection_state = ConnectionState::Registered {
|
||||
gateway_endpoint_config: gateway_config,
|
||||
nym_address,
|
||||
};
|
||||
}
|
||||
|
||||
fn write_gateway_key(&self, key_mode: &GatewayKeyMode) {
|
||||
let key_paths = self.key_paths.as_ref().unwrap().clone();
|
||||
|
||||
let path_finder = ClientKeyPathfinder::from(key_paths);
|
||||
if path_finder.gateway_key_file_exists() && key_mode.is_keep() {
|
||||
todo!("WIP");
|
||||
};
|
||||
self.key_manager
|
||||
.store_key_gateway_only(&path_finder)
|
||||
.expect("WIP");
|
||||
}
|
||||
|
||||
fn write_gateway_endpoint_config(&self) {
|
||||
let key_paths = self.key_paths.as_ref().unwrap().clone();
|
||||
let gateway_endpoint_config_path = key_paths.gateway_endpoint_config;
|
||||
let gateway_endpoint_config =
|
||||
toml::to_string(self.get_gateway_endpoint().unwrap()).unwrap();
|
||||
|
||||
// Ensure the whole directory structure exists
|
||||
if let Some(parent_dir) = gateway_endpoint_config_path.parent() {
|
||||
std::fs::create_dir_all(parent_dir).expect("WIP");
|
||||
}
|
||||
std::fs::write(gateway_endpoint_config_path, gateway_endpoint_config).expect("WIP");
|
||||
}
|
||||
|
||||
fn read_gateway_endpoint_config(&mut self) {
|
||||
let key_paths = self.key_paths.as_ref().unwrap().clone();
|
||||
let gateway_endpoint_config: GatewayEndpointConfig = toml::from_str(
|
||||
&std::fs::read_to_string(key_paths.gateway_endpoint_config).expect("WIP"),
|
||||
)
|
||||
.expect("WIP");
|
||||
|
||||
let nym_address =
|
||||
client_core::init::get_client_address(&self.key_manager, &gateway_endpoint_config);
|
||||
|
||||
self.connection_state = ConnectionState::Registered {
|
||||
gateway_endpoint_config,
|
||||
nym_address,
|
||||
};
|
||||
}
|
||||
|
||||
/// Connects to the mixnet via the gateway in the client config
|
||||
pub async fn connect_to_mixnet(&mut self) {
|
||||
// For some simple cases we can figure how to setup gateway without it having to have been
|
||||
// called in advance.
|
||||
if matches!(self.connection_state, ConnectionState::New) {
|
||||
if self.key_paths.is_some() {
|
||||
if self.has_gateway_key() {
|
||||
// If we have a gateway key from client, then we can just read the corresponding
|
||||
// config
|
||||
println!("Has gateway key: loading");
|
||||
self.read_gateway_endpoint_config();
|
||||
} else {
|
||||
// If we didn't find any shared gateway key during creation, that means we first
|
||||
// need to register a gateway
|
||||
println!("NO gateway key: registering new");
|
||||
self.register_with_gateway().await;
|
||||
self.write_gateway_key(&GatewayKeyMode::Overwrite);
|
||||
self.write_gateway_endpoint_config();
|
||||
}
|
||||
} else {
|
||||
// If we don't have any key paths, just use ephemeral keys
|
||||
self.register_with_gateway().await;
|
||||
}
|
||||
}
|
||||
|
||||
// At this point we should be in a registered state, either at function entry or by the
|
||||
// above convenience logic.
|
||||
assert!(matches!(
|
||||
self.connection_state,
|
||||
ConnectionState::Registered { .. }
|
||||
));
|
||||
|
||||
let bandwidth_controller = None;
|
||||
|
||||
let reply_storage_backend =
|
||||
non_wasm_helpers::setup_empty_reply_surb_backend(&self.config.debug_config);
|
||||
|
||||
let disabled_credentials = true;
|
||||
|
||||
let gateway_config = self.connection_state.gateway_endpoint_config().unwrap();
|
||||
|
||||
let base_builder = BaseClientBuilder::new(
|
||||
gateway_config,
|
||||
&self.config.debug_config,
|
||||
self.key_manager.clone(),
|
||||
bandwidth_controller,
|
||||
reply_storage_backend,
|
||||
disabled_credentials,
|
||||
self.config.nym_api_endpoints.clone(),
|
||||
);
|
||||
|
||||
let nym_address = base_builder.as_mix_recipient();
|
||||
|
||||
let mut started_client = base_builder.start_base().await.unwrap();
|
||||
let client_input = started_client.client_input.register_producer();
|
||||
let mut client_output = started_client.client_output.register_consumer();
|
||||
let client_state = started_client.client_state;
|
||||
|
||||
// Register our receiver
|
||||
let reconstructed_receiver = client_output.register_receiver().unwrap();
|
||||
|
||||
self.connection_state = ConnectionState::Connected {
|
||||
nym_address,
|
||||
client_input,
|
||||
client_output,
|
||||
client_state,
|
||||
reconstructed_receiver,
|
||||
task_manager: started_client.task_manager,
|
||||
};
|
||||
}
|
||||
|
||||
/// Sends stringy data to the supplied Nym address
|
||||
pub async fn send_str(&self, address: &str, message: &str) {
|
||||
log::debug!("send_str");
|
||||
let message_bytes = message.to_string().into_bytes();
|
||||
self.send_bytes(address, message_bytes).await;
|
||||
}
|
||||
|
||||
/// Sends bytes to the supplied Nym address
|
||||
pub async fn send_bytes(&self, address: &str, message: Vec<u8>) {
|
||||
log::debug!("send_bytes");
|
||||
let Some(client_input) = self.connection_state.client_input() else {
|
||||
log::error!("Error: trying to send without being connected");
|
||||
return;
|
||||
};
|
||||
|
||||
let lane = TransmissionLane::General;
|
||||
let recipient = Recipient::try_from_base58_string(address).unwrap();
|
||||
let input_msg = InputMessage::new_regular(recipient, message, lane);
|
||||
client_input.input_sender.send(input_msg).await.unwrap();
|
||||
}
|
||||
|
||||
/// Wait for messages from the mixnet
|
||||
pub async fn wait_for_messages(&mut self) -> Option<Vec<ReconstructedMessage>> {
|
||||
let receiver = self
|
||||
.connection_state
|
||||
.reconstructed_receiver()
|
||||
.tap_none(|| log::error!("Error: trying to wait without being connected"))?;
|
||||
|
||||
receiver.next().await
|
||||
}
|
||||
|
||||
pub fn wait_for_messages_split(&mut self) -> Option<ReconstructedMessage> {
|
||||
todo!();
|
||||
}
|
||||
|
||||
pub async fn on_messages<F>(&mut self, fun: F)
|
||||
where
|
||||
F: Fn(ReconstructedMessage),
|
||||
{
|
||||
while let Some(msgs) = self.wait_for_messages().await {
|
||||
for msg in msgs {
|
||||
fun(msg)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Disconnect from the mixnet. Currently it is not supported to reconnect a disconnected
|
||||
/// client.
|
||||
pub async fn disconnect(&mut self) {
|
||||
let Some(task_manager) = self.connection_state.task_manager() else {
|
||||
log::error!("Trying to disconnect when not connected!");
|
||||
return;
|
||||
};
|
||||
|
||||
task_manager.signal_shutdown().ok();
|
||||
task_manager.wait_for_shutdown().await;
|
||||
self.connection_state = ConnectionState::Disconnected;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
use client_core::config::DebugConfig;
|
||||
use network_defaults::mainnet;
|
||||
use url::Url;
|
||||
|
||||
pub struct Config {
|
||||
/// If the user has explicitly specified a gateway.
|
||||
pub user_chosen_gateway: Option<String>,
|
||||
|
||||
/// List of nym-api endpoints
|
||||
pub nym_api_endpoints: Vec<Url>,
|
||||
|
||||
/// Flags controlling all sorts of internal client behaviour.
|
||||
/// Changing these risk compromising network anonymity!
|
||||
pub debug_config: DebugConfig,
|
||||
}
|
||||
|
||||
impl Default for Config {
|
||||
fn default() -> Self {
|
||||
let nym_api_endpoints = vec![mainnet::API_VALIDATOR.to_string().parse().unwrap()];
|
||||
Self {
|
||||
user_chosen_gateway: Default::default(),
|
||||
nym_api_endpoints,
|
||||
debug_config: Default::default(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Config {
|
||||
pub fn new(user_chosen_gateway: Option<String>, nym_api_endpoints: Vec<Url>) -> Self {
|
||||
Self {
|
||||
user_chosen_gateway,
|
||||
nym_api_endpoints,
|
||||
debug_config: DebugConfig::default(),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,115 @@
|
||||
use client_core::{
|
||||
client::{
|
||||
base_client::{ClientInput, ClientOutput, ClientState},
|
||||
received_buffer::ReconstructedMessagesReceiver,
|
||||
},
|
||||
config::GatewayEndpointConfig,
|
||||
};
|
||||
use nymsphinx::addressing::clients::Recipient;
|
||||
use task::TaskManager;
|
||||
|
||||
/// States that the client connection can be in. Currently the states can only progress linearly
|
||||
/// from New -> Registered -> Connected -> Disconnected. In the future it could be useful to be able
|
||||
/// to re-connect a disconnected client.
|
||||
// WIP(JON): consider adding inner types
|
||||
#[allow(clippy::large_enum_variant)]
|
||||
pub(super) enum ConnectionState {
|
||||
New,
|
||||
Registered {
|
||||
nym_address: Recipient,
|
||||
gateway_endpoint_config: GatewayEndpointConfig,
|
||||
},
|
||||
Connected {
|
||||
nym_address: Recipient,
|
||||
client_input: ClientInput,
|
||||
#[allow(dead_code)]
|
||||
client_output: ClientOutput,
|
||||
#[allow(dead_code)]
|
||||
client_state: ClientState,
|
||||
reconstructed_receiver: ReconstructedMessagesReceiver,
|
||||
task_manager: TaskManager,
|
||||
},
|
||||
Disconnected,
|
||||
}
|
||||
|
||||
impl std::fmt::Debug for ConnectionState {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
match self {
|
||||
Self::New => write!(f, "New"),
|
||||
Self::Registered {
|
||||
nym_address,
|
||||
gateway_endpoint_config: gateway_config,
|
||||
} => f
|
||||
.debug_struct("Registered")
|
||||
.field("nym_address", nym_address)
|
||||
.field("gateway_config", gateway_config)
|
||||
.finish(),
|
||||
Self::Connected {
|
||||
nym_address,
|
||||
client_input: _,
|
||||
client_output: _,
|
||||
client_state: _,
|
||||
reconstructed_receiver,
|
||||
task_manager,
|
||||
} => f
|
||||
.debug_struct("Connected")
|
||||
.field("nym_address", nym_address)
|
||||
.field("reconstructed_receiver", reconstructed_receiver)
|
||||
.field("task_manager", task_manager)
|
||||
.finish(),
|
||||
Self::Disconnected => write!(f, "Disconnected"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl ConnectionState {
|
||||
pub(super) fn client_input(&self) -> Option<&ClientInput> {
|
||||
match self {
|
||||
ConnectionState::New
|
||||
| ConnectionState::Registered { .. }
|
||||
| ConnectionState::Disconnected => None,
|
||||
ConnectionState::Connected { client_input, .. } => Some(client_input),
|
||||
}
|
||||
}
|
||||
|
||||
pub(super) fn reconstructed_receiver(&mut self) -> Option<&mut ReconstructedMessagesReceiver> {
|
||||
match self {
|
||||
ConnectionState::New
|
||||
| ConnectionState::Registered { .. }
|
||||
| ConnectionState::Disconnected => None,
|
||||
ConnectionState::Connected {
|
||||
reconstructed_receiver,
|
||||
..
|
||||
} => Some(reconstructed_receiver),
|
||||
}
|
||||
}
|
||||
|
||||
pub(super) fn gateway_endpoint_config(&self) -> Option<&GatewayEndpointConfig> {
|
||||
match self {
|
||||
ConnectionState::New
|
||||
| ConnectionState::Connected { .. }
|
||||
| ConnectionState::Disconnected => None,
|
||||
ConnectionState::Registered {
|
||||
gateway_endpoint_config: gateway_config,
|
||||
..
|
||||
} => Some(gateway_config),
|
||||
}
|
||||
}
|
||||
|
||||
pub(super) fn nym_address(&self) -> Option<&Recipient> {
|
||||
match self {
|
||||
ConnectionState::New | ConnectionState::Disconnected => None,
|
||||
ConnectionState::Registered { nym_address, .. }
|
||||
| ConnectionState::Connected { nym_address, .. } => Some(nym_address),
|
||||
}
|
||||
}
|
||||
|
||||
pub(super) fn task_manager(&mut self) -> Option<&mut TaskManager> {
|
||||
match self {
|
||||
ConnectionState::New
|
||||
| ConnectionState::Registered { .. }
|
||||
| ConnectionState::Disconnected => None,
|
||||
ConnectionState::Connected { task_manager, .. } => Some(task_manager),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,142 @@
|
||||
use client_core::{
|
||||
client::key_manager::KeyManager, config::persistence::key_pathfinder::ClientKeyPathfinder,
|
||||
};
|
||||
use crypto::asymmetric::{encryption, identity};
|
||||
use gateway_requests::registration::handshake::SharedKeys;
|
||||
use nymsphinx::acknowledgements::AckKey;
|
||||
|
||||
use std::path::{Path, PathBuf};
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub enum KeyMode {
|
||||
/// Use existing key files if they exists, otherwise create new ones.
|
||||
Keep,
|
||||
/// Create new keys, overwriting any potential previously existing keys.
|
||||
Overwrite,
|
||||
}
|
||||
|
||||
impl KeyMode {
|
||||
pub(crate) fn is_keep(&self) -> bool {
|
||||
matches!(self, KeyMode::Keep)
|
||||
}
|
||||
}
|
||||
|
||||
pub enum GatewayKeyMode {
|
||||
/// Keep shared gateway key if found, otherwise create a new one.
|
||||
Keep,
|
||||
/// Create a new shared key and overwrite any potential existing one.
|
||||
Overwrite,
|
||||
}
|
||||
|
||||
impl GatewayKeyMode {
|
||||
pub(crate) fn is_keep(&self) -> bool {
|
||||
matches!(self, GatewayKeyMode::Keep)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct KeyPaths {
|
||||
// Determines how to handle existing key files found.
|
||||
pub operating_mode: KeyMode,
|
||||
|
||||
// Client identity keys
|
||||
pub private_identity: PathBuf,
|
||||
pub public_identity: PathBuf,
|
||||
|
||||
// Client encryption keys
|
||||
pub private_encryption: PathBuf,
|
||||
pub public_encryption: PathBuf,
|
||||
|
||||
// Key for handling acks
|
||||
pub ack_key: PathBuf,
|
||||
|
||||
// Key setup after authenticating with a gateway
|
||||
pub gateway_shared_key: PathBuf,
|
||||
|
||||
// The key isn't much use without knowing which entity it refers to.
|
||||
// This is an `Option` in case the end user might want to read write keys to storage, but
|
||||
// handle the gateway configuration manually through `set_gateway_endpoint`.
|
||||
// WIP(JON): make it an option?
|
||||
pub gateway_endpoint_config: PathBuf,
|
||||
|
||||
pub credential_database_path: PathBuf,
|
||||
|
||||
pub reply_surb_database_path: PathBuf,
|
||||
}
|
||||
|
||||
pub struct Keys {
|
||||
pub identity_keypair: identity::KeyPair,
|
||||
pub encryption_keypair: encryption::KeyPair,
|
||||
pub ack_key: AckKey,
|
||||
pub gateway_shared_key: SharedKeys,
|
||||
}
|
||||
|
||||
//#[derive(Clone, Debug)]
|
||||
//pub struct GatewaySetup {
|
||||
// pub gateway_shared_key: PathBuf,
|
||||
// pub gateway_endpoint_config: GatewayEndpointConfig,
|
||||
// //pub gateway_endpoint_config: GatewayConfig,
|
||||
//}
|
||||
|
||||
//#[derive(Clone, Debug)]
|
||||
//pub enum GatewayConfig {
|
||||
// File(PathBuf),
|
||||
// Struct(GatewayEndpointConfig),
|
||||
//}
|
||||
|
||||
impl KeyPaths {
|
||||
pub fn new_from_dir(operating_mode: KeyMode, dir: &Path) -> Self {
|
||||
assert!(!dir.is_file(), "WIP");
|
||||
Self {
|
||||
// These filenames were chosen to match the ones we use in `nym-client`. Consider
|
||||
// changing the defaults
|
||||
operating_mode,
|
||||
private_identity: dir.join("private_identity.pem"),
|
||||
public_identity: dir.join("public_identity.pem"),
|
||||
private_encryption: dir.join("private_encryption.pem"),
|
||||
public_encryption: dir.join("public_encryption.pem"),
|
||||
ack_key: dir.join("ack_key.pem"),
|
||||
gateway_shared_key: dir.join("gateway_shared.pem"),
|
||||
gateway_endpoint_config: dir.join("gateway_endpoint_config.toml"),
|
||||
credential_database_path: dir.join("db.sqlite"),
|
||||
reply_surb_database_path: dir.join("persistent_reply_store.sqlite"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<KeyPaths> for ClientKeyPathfinder {
|
||||
fn from(paths: KeyPaths) -> Self {
|
||||
Self {
|
||||
identity_private_key: paths.private_identity,
|
||||
identity_public_key: paths.public_identity,
|
||||
encryption_private_key: paths.private_encryption,
|
||||
encryption_public_key: paths.public_encryption,
|
||||
gateway_shared_key: paths.gateway_shared_key,
|
||||
ack_key: paths.ack_key,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Keys> for KeyManager {
|
||||
fn from(keys: Keys) -> Self {
|
||||
KeyManager::new_from_keys(
|
||||
keys.identity_keypair,
|
||||
keys.encryption_keypair,
|
||||
keys.gateway_shared_key,
|
||||
keys.ack_key,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
//pub struct GatewayConfigPath {
|
||||
// pub path: PathBuf,
|
||||
//}
|
||||
//
|
||||
//impl GatewayConfigPath {
|
||||
// pub fn new_from_dir(dir: PathBuf) -> Self {
|
||||
// assert!(!dir.is_file(), "WIP");
|
||||
// Self {
|
||||
// path: dir.join("gateway_endpoint_config.toml"),
|
||||
// }
|
||||
// }
|
||||
//}
|
||||
@@ -1,62 +0,0 @@
|
||||
use std::path::PathBuf;
|
||||
|
||||
pub struct Client {
|
||||
pub nym_address: String,
|
||||
config: Config,
|
||||
}
|
||||
|
||||
impl Client {
|
||||
/// Create a new mixnet client. If no config options are supplied, creates a new client with ephemeral keys
|
||||
/// stored in RAM, which will be discarded at application close.
|
||||
///
|
||||
/// Callers have the option of supplying futher parameters to store persistent identities at a location on-disk,
|
||||
/// if desired.
|
||||
pub fn new(config_option: Option<Config>) -> Client {
|
||||
if config_option.is_none() {
|
||||
let config = Self::some_minimal_config();
|
||||
let nym_address = Self::new_nym_address();
|
||||
Client {
|
||||
config,
|
||||
nym_address,
|
||||
}
|
||||
} else {
|
||||
let self_address = Self::new_nym_address();
|
||||
let nym_address = Self::new_nym_address();
|
||||
|
||||
Client {
|
||||
config: config_option.unwrap(),
|
||||
nym_address,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Connects to the mixnet via the gateway in the client config
|
||||
pub fn connect_to_mixnet(&self) {}
|
||||
|
||||
/// Sets the callback function which is run when the client receives a message
|
||||
/// from the mixnet
|
||||
pub fn on_receive(&mut self, message: &str) {
|
||||
println!("Message received: {}", message);
|
||||
}
|
||||
|
||||
/// Sends stringy data to the supplied Nym address
|
||||
pub fn send_str(&self, address: &str, message: &str) {}
|
||||
|
||||
/// Sends bytes to the supplied Nym address
|
||||
pub fn send_bytes(&self, address: &str, message: Vec<u8>) {}
|
||||
|
||||
fn new_nym_address() -> String {
|
||||
"the.nym@address".to_string()
|
||||
}
|
||||
|
||||
fn some_minimal_config() -> Config {
|
||||
let mut keys_path = PathBuf::new();
|
||||
Config {
|
||||
keys: Some(keys_path),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct Config {
|
||||
pub keys: Option<PathBuf>,
|
||||
}
|
||||
Reference in New Issue
Block a user