rust-sdk: start adding rustdoc (#2895)
* rust-sdk: start adding some rustdoc * rust-sdk: whole bunch of rustdoc * rustfmt
This commit is contained in:
@@ -10,7 +10,7 @@ async fn main() {
|
||||
|
||||
let config = mixnet::Config::new(user_chosen_gateway_id, nym_api_endpoints);
|
||||
|
||||
let mut client = mixnet::MixnetClientBuilder::new(Some(config), None)
|
||||
let mut client = mixnet::MixnetClient::builder(Some(config), None)
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
@@ -37,7 +37,8 @@ async fn main() {
|
||||
println!("Our client address is {}", client.nym_address());
|
||||
|
||||
// Send important info up the pipe to a buddy
|
||||
client.send_str("foo.bar@blah", "flappappa").await;
|
||||
let recipient = mixnet::Recipient::try_from_base58_string("foo.bar@blah").unwrap();
|
||||
client.send_str(recipient, "flappappa").await;
|
||||
}
|
||||
|
||||
#[allow(unused)]
|
||||
|
||||
@@ -14,7 +14,7 @@ async fn main() {
|
||||
let keys = mixnet::StoragePaths::new_from_dir(mixnet::KeyMode::Keep, &config_dir).unwrap();
|
||||
|
||||
// Provide key paths for the client to read/write keys to.
|
||||
let client = mixnet::MixnetClientBuilder::new(None, Some(keys))
|
||||
let client = mixnet::MixnetClient::builder(None, Some(keys))
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
@@ -26,9 +26,7 @@ async fn main() {
|
||||
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;
|
||||
client.send_str(*our_address, "hello there").await;
|
||||
|
||||
println!("Waiting for message");
|
||||
if let Some(received) = client.wait_for_messages().await {
|
||||
|
||||
@@ -12,9 +12,7 @@ async fn main() {
|
||||
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;
|
||||
client.send_str(*our_address, "hello there").await;
|
||||
|
||||
println!("Waiting for message (ctrl-c to exit)");
|
||||
client
|
||||
|
||||
@@ -7,7 +7,7 @@ async fn main() {
|
||||
// Create client builder, including ephemeral keys. The builder can be usable in the context
|
||||
// where you don't want to connect just yet.
|
||||
// Since not storage paths are given, the surb storage will be inactive.
|
||||
let client = mixnet::MixnetClientBuilder::new(None, None).await.unwrap();
|
||||
let client = mixnet::MixnetClient::builder(None, None).await.unwrap();
|
||||
|
||||
// Now we connect to the mixnet, using ephemeral keys already created
|
||||
let mut client = client.connect_to_mixnet().await.unwrap();
|
||||
@@ -17,9 +17,7 @@ async fn main() {
|
||||
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;
|
||||
client.send_str(*our_address, "hello there").await;
|
||||
|
||||
println!("Waiting for message");
|
||||
if let Some(received) = client.wait_for_messages().await {
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
use std::path::PathBuf;
|
||||
|
||||
/// Top-level Error enum for the mixnet client and its relevant types.
|
||||
#[derive(Debug, thiserror::Error)]
|
||||
pub enum Error {
|
||||
#[error("i/o error: {0}")]
|
||||
@@ -20,9 +21,16 @@ pub enum Error {
|
||||
DontOverwriteGatewayKey(PathBuf),
|
||||
#[error("no gateway config available for writing")]
|
||||
GatewayNotAvailableForWriting,
|
||||
|
||||
#[error("expected to received a directory, received: {0}")]
|
||||
ExpectedDirectory(PathBuf),
|
||||
|
||||
#[error("failed to transition to registered state before connection to mixnet")]
|
||||
FailedToTransitionToRegisteredState,
|
||||
#[error(
|
||||
"registering with gateway when the client is already in a registered state is not \
|
||||
supported, and likely and user mistake"
|
||||
)]
|
||||
ReregisteringGatewayNotSupported,
|
||||
}
|
||||
|
||||
pub type Result<T, E = Error> = std::result::Result<T, E>;
|
||||
|
||||
@@ -1,2 +1,9 @@
|
||||
pub mod error;
|
||||
//! Rust SDK for the Nym platform
|
||||
//!
|
||||
//! The main component currently is [`mixnet`].
|
||||
|
||||
mod error;
|
||||
|
||||
pub mod mixnet;
|
||||
|
||||
pub use error::{Error, Result};
|
||||
|
||||
@@ -1,17 +1,47 @@
|
||||
//! The mixnet component of the Rust SDK for the Nym platform
|
||||
//!
|
||||
//!
|
||||
//! # Basic example
|
||||
//!
|
||||
//! ```no_run
|
||||
//! use nym_sdk::mixnet;
|
||||
//!
|
||||
//! #[tokio::main]
|
||||
//! async fn main() {
|
||||
//! // Passing no config makes the client fire up an ephemeral session and figure stuff out on
|
||||
//! // its own
|
||||
//! let mut client = mixnet::MixnetClient::connect().await.unwrap();
|
||||
//!
|
||||
//! // Be able to get our client address
|
||||
//! let our_address = client.nym_address();
|
||||
//! println!("Our client nym address is: {our_address}");
|
||||
//!
|
||||
//! // Send a message throught the mixnet to ourselves
|
||||
//! client.send_str(*our_address, "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;
|
||||
//! }
|
||||
//! ```
|
||||
|
||||
mod client;
|
||||
mod config;
|
||||
mod connection_state;
|
||||
mod keys;
|
||||
mod paths;
|
||||
|
||||
pub use client::{MixnetClient, MixnetClientBuilder};
|
||||
pub use client_core::config::GatewayEndpointConfig;
|
||||
pub use config::Config;
|
||||
pub use keys::{Keys, KeysArc};
|
||||
pub use nymsphinx::{
|
||||
addressing::clients::{ClientIdentity, Recipient},
|
||||
receiver::ReconstructedMessage,
|
||||
};
|
||||
|
||||
pub use keys::{Keys, KeysArc};
|
||||
pub use paths::{GatewayKeyMode, KeyMode, StoragePaths};
|
||||
|
||||
pub use client::{MixnetClient, MixnetClientBuilder};
|
||||
pub use config::Config;
|
||||
|
||||
@@ -24,10 +24,18 @@ use task::TaskManager;
|
||||
use futures::StreamExt;
|
||||
use validator_client::nyxd::SigningNyxdClient;
|
||||
|
||||
use super::{connection_state::BuilderState, Config, GatewayKeyMode, Keys, KeysArc, StoragePaths};
|
||||
use crate::error::{Error, Result};
|
||||
use crate::{Error, Result};
|
||||
|
||||
pub struct MixnetClientBuilder<B: ReplyStorageBackend> {
|
||||
use super::{connection_state::BuilderState, Config, GatewayKeyMode, Keys, KeysArc, StoragePaths};
|
||||
|
||||
/// Represents a client that is not yet connected to the mixnet. You typically create one when you
|
||||
/// want to have a separate configuration and connection phase. Once the mixnet client builder is
|
||||
/// configured, call [`MixnetClientBuilder::connect_to_mixnet()`] to transition to a connected
|
||||
/// client.
|
||||
pub struct MixnetClientBuilder<B>
|
||||
where
|
||||
B: ReplyStorageBackend,
|
||||
{
|
||||
/// Keys handled by the client
|
||||
key_manager: KeyManager,
|
||||
|
||||
@@ -45,105 +53,10 @@ pub struct MixnetClientBuilder<B: ReplyStorageBackend> {
|
||||
reply_storage_backend: B,
|
||||
}
|
||||
|
||||
impl MixnetClientBuilder<reply_storage::fs_backend::Backend> {
|
||||
/// 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 async fn new(config: Option<Config>, paths: Option<StoragePaths>) -> Result<Self> {
|
||||
let config = config.unwrap_or_default();
|
||||
|
||||
let reply_surb_database_path = paths.as_ref().map(|p| p.reply_surb_database_path.clone());
|
||||
|
||||
let reply_storage_backend = non_wasm_helpers::setup_fs_reply_surb_backend(
|
||||
reply_surb_database_path,
|
||||
&config.debug_config,
|
||||
)
|
||||
.await?;
|
||||
|
||||
MixnetClientBuilder::new_with_custom_storage(Some(config), paths, reply_storage_backend)
|
||||
}
|
||||
}
|
||||
|
||||
impl MixnetClientBuilder<reply_storage::Empty> {
|
||||
/// 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_without_storage(
|
||||
config: Option<Config>,
|
||||
paths: Option<StoragePaths>,
|
||||
) -> Result<Self> {
|
||||
let config = config.unwrap_or_default();
|
||||
|
||||
let reply_storage_backend =
|
||||
non_wasm_helpers::setup_empty_reply_surb_backend(&config.debug_config);
|
||||
|
||||
MixnetClientBuilder::new_with_custom_storage(Some(config), paths, reply_storage_backend)
|
||||
}
|
||||
}
|
||||
|
||||
impl<B> MixnetClientBuilder<B>
|
||||
where
|
||||
B: ReplyStorageBackend + Sync + Send + 'static,
|
||||
{
|
||||
/// 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.
|
||||
///
|
||||
/// A custom storage backend can be passed in.
|
||||
pub fn new_with_custom_storage(
|
||||
config_option: Option<Config>,
|
||||
paths: Option<StoragePaths>,
|
||||
reply_storage_backend: B,
|
||||
) -> Result<Self> {
|
||||
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 paths) = paths {
|
||||
let path_finder = ClientKeyPathfinder::from(paths.clone());
|
||||
|
||||
// Try load keys
|
||||
match KeyManager::load_keys_but_gateway_is_optional(&path_finder) {
|
||||
Ok(key_manager) => key_manager,
|
||||
Err(err) => {
|
||||
log::debug!("Not loading keys: {err}");
|
||||
if let Some(path) = path_finder.any_file_exists_and_return() {
|
||||
if paths.operating_mode.is_keep() {
|
||||
return Err(Error::DontOverwrite(path));
|
||||
}
|
||||
}
|
||||
|
||||
// Double check using a function that has slightly different internal logic. I
|
||||
// know this is a bit defensive, but I don't want to overwrite
|
||||
assert!(!(path_finder.any_file_exists() && paths.operating_mode.is_keep()));
|
||||
|
||||
// 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(Self {
|
||||
key_manager,
|
||||
config,
|
||||
storage_paths: paths,
|
||||
state: BuilderState::New,
|
||||
reply_storage_backend,
|
||||
})
|
||||
}
|
||||
|
||||
/// 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.
|
||||
@@ -151,6 +64,7 @@ where
|
||||
self.key_manager.is_gateway_key_set()
|
||||
}
|
||||
|
||||
/// Sets the keys of this [`MixnetClientBuilder<B>`].
|
||||
pub fn set_keys(&mut self, keys: Keys) {
|
||||
self.key_manager.set_identity_keypair(keys.identity_keypair);
|
||||
self.key_manager
|
||||
@@ -161,25 +75,35 @@ where
|
||||
.insert_gateway_shared_key(Arc::new(keys.gateway_shared_key));
|
||||
}
|
||||
|
||||
/// Returns the keys of this [`MixnetClientBuilder<B>`]. Client keys are always available since
|
||||
/// if none are specified at creation time, new random ones are generated.
|
||||
pub fn get_keys(&self) -> KeysArc {
|
||||
KeysArc::from(&self.key_manager)
|
||||
}
|
||||
|
||||
/// Sets the gateway endpoint of this [`MixnetClientBuilder<B>`].
|
||||
pub fn set_gateway_endpoint(&mut self, gateway_endpoint_config: GatewayEndpointConfig) {
|
||||
self.state = BuilderState::Registered {
|
||||
gateway_endpoint_config,
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns the get gateway endpoint of this [`MixnetClientBuilder<B>`].
|
||||
pub fn get_gateway_endpoint(&self) -> Option<&GatewayEndpointConfig> {
|
||||
self.state.gateway_endpoint_config()
|
||||
}
|
||||
|
||||
/// Register with a gateway. If a gateway is provided in the config then that will try to be
|
||||
/// used. If none is specified, a gateway at random will be picked.
|
||||
///
|
||||
/// # Errors
|
||||
///
|
||||
/// This function will return an error if you try to re-register when in an already registered
|
||||
/// state.
|
||||
pub async fn register_with_gateway(&mut self) -> Result<()> {
|
||||
assert!(
|
||||
matches!(self.state, BuilderState::New),
|
||||
"can only setup gateway when in `New` connection state"
|
||||
);
|
||||
if self.state != BuilderState::New {
|
||||
return Err(Error::ReregisteringGatewayNotSupported);
|
||||
}
|
||||
|
||||
let user_chosen_gateway = self
|
||||
.config
|
||||
@@ -237,7 +161,24 @@ where
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Connects to the mixnet via the gateway in the client config
|
||||
/// Connect the client to the mixnet.
|
||||
///
|
||||
/// - If the client is already registered with a gateway, use that gateway.
|
||||
/// - If no gateway is registered, but there is an existing configuration and key, use that.
|
||||
/// - If no gateway is registered, and there is no pre-existing configuration or key, try to
|
||||
/// register a new gateway.
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```no_run
|
||||
/// use nym_sdk::mixnet;
|
||||
///
|
||||
/// #[tokio::main]
|
||||
/// async fn main() {
|
||||
/// let client = mixnet::MixnetClient::builder(None, None).await.unwrap();
|
||||
/// let client = client.connect_to_mixnet().await.unwrap();
|
||||
/// }
|
||||
/// ```
|
||||
pub async fn connect_to_mixnet(mut self) -> Result<MixnetClient>
|
||||
where
|
||||
<B as ReplyStorageBackend>::StorageError: Sync + Send,
|
||||
@@ -269,7 +210,7 @@ where
|
||||
// At this point we should be in a registered state, either at function entry or by the
|
||||
// above convenience logic.
|
||||
let BuilderState::Registered { gateway_endpoint_config } = self.state else {
|
||||
todo!();
|
||||
return Err(Error::FailedToTransitionToRegisteredState);
|
||||
};
|
||||
|
||||
let nym_address =
|
||||
@@ -278,7 +219,7 @@ where
|
||||
// TODO: we currently don't support having a bandwidth controller
|
||||
let bandwidth_controller = None;
|
||||
|
||||
let base_builder: BaseClientBuilder<_, SigningNyxdClient> = BaseClientBuilder::new(
|
||||
let base_builder: BaseClientBuilder<'_, _, SigningNyxdClient> = BaseClientBuilder::new(
|
||||
&gateway_endpoint_config,
|
||||
&self.config.debug_config,
|
||||
self.key_manager.clone(),
|
||||
@@ -288,13 +229,13 @@ where
|
||||
self.config.nym_api_endpoints.clone(),
|
||||
);
|
||||
|
||||
let mut started_client = base_builder.start_base().await.unwrap();
|
||||
let mut started_client = base_builder.start_base().await?;
|
||||
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();
|
||||
let reconstructed_receiver = client_output.register_receiver()?;
|
||||
|
||||
Ok(MixnetClient {
|
||||
nym_address,
|
||||
@@ -308,31 +249,170 @@ where
|
||||
}
|
||||
}
|
||||
|
||||
/// Client connected to the Nym mixnet.
|
||||
pub struct MixnetClient {
|
||||
/// The nym address of this connected client.
|
||||
nym_address: Recipient,
|
||||
|
||||
/// Keys handled by the client
|
||||
key_manager: KeyManager,
|
||||
|
||||
/// Input to the client from the users perspective. This can be either data to send or controll
|
||||
/// messages.
|
||||
client_input: ClientInput,
|
||||
|
||||
/// Output from the client from the users perspective. This is typically messages arriving from
|
||||
/// the mixnet.
|
||||
#[allow(dead_code)]
|
||||
client_output: ClientOutput,
|
||||
|
||||
/// The current state of the client that is exposed to the user. This includes things like
|
||||
/// current message send queue length.
|
||||
#[allow(dead_code)]
|
||||
client_state: ClientState,
|
||||
|
||||
/// A channel for messages arriving from the mixnet after they have been reconstructed.
|
||||
reconstructed_receiver: ReconstructedMessagesReceiver,
|
||||
|
||||
/// The task manager that controlls all the spawned tasks that the clients uses to do it's job.
|
||||
task_manager: TaskManager,
|
||||
}
|
||||
|
||||
impl MixnetClient {
|
||||
/// Create a new client and connect to the mixnet using ephemeral in-memory keys that are
|
||||
/// discarded at application close.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```no_run
|
||||
/// use nym_sdk::mixnet;
|
||||
///
|
||||
/// #[tokio::main]
|
||||
/// async fn main() {
|
||||
/// let mut client = mixnet::MixnetClient::connect().await;
|
||||
/// }
|
||||
///
|
||||
/// ```
|
||||
pub async fn connect() -> Result<Self> {
|
||||
let client = MixnetClientBuilder::new_without_storage(None, None)?;
|
||||
let client = MixnetClient::builder_without_storage(None, None)?;
|
||||
client.connect_to_mixnet().await
|
||||
}
|
||||
|
||||
/// Create a new mixnet client builder. 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.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```no_run
|
||||
/// use nym_sdk::mixnet;
|
||||
///
|
||||
/// #[tokio::main]
|
||||
/// async fn main() {
|
||||
/// let client = mixnet::MixnetClient::builder(None, None).await;
|
||||
/// }
|
||||
/// ```
|
||||
pub async fn builder(
|
||||
config: Option<Config>,
|
||||
paths: Option<StoragePaths>,
|
||||
) -> Result<MixnetClientBuilder<reply_storage::fs_backend::Backend>> {
|
||||
let config = config.unwrap_or_default();
|
||||
|
||||
let reply_surb_database_path = paths.as_ref().map(|p| p.reply_surb_database_path.clone());
|
||||
|
||||
let reply_storage_backend = non_wasm_helpers::setup_fs_reply_surb_backend(
|
||||
reply_surb_database_path,
|
||||
&config.debug_config,
|
||||
)
|
||||
.await?;
|
||||
|
||||
MixnetClient::builder_with_custom_storage(Some(config), paths, reply_storage_backend)
|
||||
}
|
||||
|
||||
/// Create a new mixnet client builder. 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.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// use nym_sdk::mixnet;
|
||||
/// let client = mixnet::MixnetClient::builder_without_storage(None, None);
|
||||
/// ```
|
||||
pub fn builder_without_storage(
|
||||
config: Option<Config>,
|
||||
paths: Option<StoragePaths>,
|
||||
) -> Result<MixnetClientBuilder<reply_storage::Empty>> {
|
||||
let config = config.unwrap_or_default();
|
||||
|
||||
let reply_storage_backend =
|
||||
non_wasm_helpers::setup_empty_reply_surb_backend(&config.debug_config);
|
||||
|
||||
MixnetClient::builder_with_custom_storage(Some(config), paths, reply_storage_backend)
|
||||
}
|
||||
|
||||
/// Create a new mixnet client builder. 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.
|
||||
///
|
||||
/// A custom storage backend can be passed in.
|
||||
pub fn builder_with_custom_storage<B>(
|
||||
config_option: Option<Config>,
|
||||
paths: Option<StoragePaths>,
|
||||
reply_storage_backend: B,
|
||||
) -> Result<MixnetClientBuilder<B>>
|
||||
where
|
||||
B: ReplyStorageBackend,
|
||||
{
|
||||
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 paths) = paths {
|
||||
let path_finder = ClientKeyPathfinder::from(paths.clone());
|
||||
|
||||
// Try load keys
|
||||
match KeyManager::load_keys_but_gateway_is_optional(&path_finder) {
|
||||
Ok(key_manager) => key_manager,
|
||||
Err(err) => {
|
||||
log::debug!("Not loading keys: {err}");
|
||||
if let Some(path) = path_finder.any_file_exists_and_return() {
|
||||
if paths.operating_mode.is_keep() {
|
||||
return Err(Error::DontOverwrite(path));
|
||||
}
|
||||
}
|
||||
|
||||
// Double check using a function that has slightly different internal logic. I
|
||||
// know this is a bit defensive, but I don't want to overwrite
|
||||
assert!(!(path_finder.any_file_exists() && paths.operating_mode.is_keep()));
|
||||
|
||||
// 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(MixnetClientBuilder {
|
||||
key_manager,
|
||||
config,
|
||||
storage_paths: paths,
|
||||
state: BuilderState::New,
|
||||
reply_storage_backend,
|
||||
})
|
||||
}
|
||||
|
||||
/// 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()
|
||||
@@ -345,39 +425,59 @@ impl MixnetClient {
|
||||
}
|
||||
|
||||
/// Sends stringy data to the supplied Nym address
|
||||
pub async fn send_str(&self, address: &str, message: &str) {
|
||||
pub async fn send_str(&self, address: Recipient, message: &str) {
|
||||
let message_bytes = message.to_string().into_bytes();
|
||||
self.send_bytes(address, message_bytes).await;
|
||||
}
|
||||
|
||||
/// Sends stringy data to the supplied Nym address, and skip sending reply-SURBs
|
||||
pub async fn send_str_direct(&self, address: &str, message: &str) {
|
||||
pub async fn send_str_direct(&self, address: Recipient, message: &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>) {
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```no_run
|
||||
/// use nym_sdk::mixnet;
|
||||
///
|
||||
/// #[tokio::main]
|
||||
/// async fn main() {
|
||||
/// let address = "foobar";
|
||||
/// let recipient = mixnet::Recipient::try_from_base58_string(address).unwrap();
|
||||
/// let mut client = mixnet::MixnetClient::connect().await.unwrap();
|
||||
/// client.send_bytes(recipient, "hi".to_owned().into_bytes()).await;
|
||||
/// }
|
||||
/// ```
|
||||
pub async fn send_bytes(&self, address: Recipient, message: Vec<u8>) {
|
||||
let lane = TransmissionLane::General;
|
||||
let recipient = Recipient::try_from_base58_string(address).unwrap();
|
||||
let input_msg = InputMessage::new_anonymous(recipient, message, 20, lane);
|
||||
self.client_input
|
||||
let input_msg = InputMessage::new_anonymous(address, message, 20, lane);
|
||||
if self
|
||||
.client_input
|
||||
.input_sender
|
||||
.send(input_msg)
|
||||
.await
|
||||
.unwrap();
|
||||
.is_err()
|
||||
{
|
||||
log::error!("Failed to send message");
|
||||
}
|
||||
}
|
||||
|
||||
/// Sends bytes to the supplied Nym address, and skip sending reply-SURBs
|
||||
pub async fn send_bytes_direct(&self, address: &str, message: Vec<u8>) {
|
||||
pub async fn send_bytes_direct(&self, address: Recipient, message: Vec<u8>) {
|
||||
let lane = TransmissionLane::General;
|
||||
let recipient = Recipient::try_from_base58_string(address).unwrap();
|
||||
let input_msg = InputMessage::new_regular(recipient, message, lane);
|
||||
self.client_input
|
||||
let input_msg = InputMessage::new_regular(address, message, lane);
|
||||
if self
|
||||
.client_input
|
||||
.input_sender
|
||||
.send(input_msg)
|
||||
.await
|
||||
.unwrap();
|
||||
.is_err()
|
||||
{
|
||||
log::error!("Failed to send message");
|
||||
}
|
||||
}
|
||||
|
||||
/// Wait for messages from the mixnet
|
||||
@@ -385,10 +485,7 @@ impl MixnetClient {
|
||||
self.reconstructed_receiver.next().await
|
||||
}
|
||||
|
||||
pub fn wait_for_messages_split(&mut self) -> Option<ReconstructedMessage> {
|
||||
todo!();
|
||||
}
|
||||
|
||||
/// Provide a callback to execute on incoming messages from the mixnet.
|
||||
pub async fn on_messages<F>(&mut self, fun: F)
|
||||
where
|
||||
F: Fn(ReconstructedMessage),
|
||||
|
||||
@@ -2,6 +2,7 @@ use client_core::config::DebugConfig;
|
||||
use network_defaults::mainnet;
|
||||
use url::Url;
|
||||
|
||||
/// Config struct for [`crate::mixnet::MixnetClient`]
|
||||
pub struct Config {
|
||||
/// If the user has explicitly specified a gateway.
|
||||
pub user_chosen_gateway: Option<String>,
|
||||
@@ -26,6 +27,7 @@ impl Default for Config {
|
||||
}
|
||||
|
||||
impl Config {
|
||||
/// Creates a new [`Config`].
|
||||
pub fn new(user_chosen_gateway: Option<String>, nym_api_endpoints: Vec<Url>) -> Self {
|
||||
Self {
|
||||
user_chosen_gateway,
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
use client_core::config::GatewayEndpointConfig;
|
||||
|
||||
#[derive(Debug)]
|
||||
#[derive(Debug, PartialEq, Eq)]
|
||||
pub(super) enum BuilderState {
|
||||
New,
|
||||
Registered {
|
||||
|
||||
@@ -5,17 +5,32 @@ use crypto::asymmetric::{encryption, identity};
|
||||
use gateway_requests::registration::handshake::SharedKeys;
|
||||
use nymsphinx::acknowledgements::AckKey;
|
||||
|
||||
/// The set of keys used by the client. Identity, encryption and ack keys are generated at creating
|
||||
/// unless specified to loaded from storage or somehow explictly specified. The gateway shared key
|
||||
/// is generated when registering with a gateway.
|
||||
pub struct Keys {
|
||||
/// The identity key of the client.
|
||||
pub identity_keypair: identity::KeyPair,
|
||||
/// The encryption key of the client.
|
||||
pub encryption_keypair: encryption::KeyPair,
|
||||
/// The ack key used by the client.
|
||||
pub ack_key: AckKey,
|
||||
|
||||
/// The gateway shared key that is obtained after registering with a gateway.
|
||||
pub gateway_shared_key: SharedKeys,
|
||||
}
|
||||
|
||||
/// The set of keys used by the client, but where each key is stored in an [`std::sync::Arc`] for
|
||||
/// easy cloning.
|
||||
pub struct KeysArc {
|
||||
/// The identity key of the client.
|
||||
pub identity_keypair: Arc<identity::KeyPair>,
|
||||
/// The encryption key of the client.
|
||||
pub encryption_keypair: Arc<encryption::KeyPair>,
|
||||
/// The ack key used by the client.
|
||||
pub ack_key: Arc<AckKey>,
|
||||
|
||||
/// The gateway shared key that is obtained after registering with a gateway.
|
||||
pub gateway_shared_key: Arc<SharedKeys>,
|
||||
}
|
||||
|
||||
|
||||
@@ -30,36 +30,46 @@ impl GatewayKeyMode {
|
||||
}
|
||||
}
|
||||
|
||||
/// Set of storage paths that the client will use if it is setup to persist keys, credentials, and
|
||||
/// reply-SURBs.
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct StoragePaths {
|
||||
// Determines how to handle existing key files found.
|
||||
/// Determines how to handle existing key files found.
|
||||
pub operating_mode: KeyMode,
|
||||
|
||||
// Client identity keys
|
||||
/// Client private identity key
|
||||
pub private_identity: PathBuf,
|
||||
/// Client public identity key
|
||||
pub public_identity: PathBuf,
|
||||
|
||||
// Client encryption keys
|
||||
/// Client private encryption key
|
||||
pub private_encryption: PathBuf,
|
||||
/// Client public encryption key
|
||||
pub public_encryption: PathBuf,
|
||||
|
||||
// Key for handling acks
|
||||
/// Key for handling acks
|
||||
pub ack_key: PathBuf,
|
||||
|
||||
// Key setup after authenticating with a gateway
|
||||
/// 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.
|
||||
/// The key isn't much use without knowing which entity it refers to.
|
||||
pub gateway_endpoint_config: PathBuf,
|
||||
|
||||
// The database containing credentials
|
||||
/// The database containing credentials
|
||||
pub credential_database_path: PathBuf,
|
||||
|
||||
// The database storing reply surbs in-between sessions
|
||||
/// The database storing reply surbs in-between sessions
|
||||
pub reply_surb_database_path: PathBuf,
|
||||
}
|
||||
|
||||
impl StoragePaths {
|
||||
/// Create a set of storage paths from a given directory.
|
||||
///
|
||||
/// # Errors
|
||||
///
|
||||
/// This function will return an error if it is passed a path to an existing file instead of a
|
||||
/// directory.
|
||||
pub fn new_from_dir(operating_mode: KeyMode, dir: &Path) -> Result<Self> {
|
||||
if dir.is_file() {
|
||||
return Err(Error::ExpectedDirectory(dir.to_owned()));
|
||||
|
||||
Reference in New Issue
Block a user