#[doc(hidden)]
mod handler;
#[doc(hidden)]
mod logger;
use clap::{value_parser, Arg, Command};
use commonware_cryptography::{ed25519, Signer as _};
use commonware_p2p::{authenticated::discovery, Manager as _};
use commonware_runtime::{tokio, Metrics, Quota, Runner as _};
use commonware_utils::{ordered::Set, sync::Mutex, TryCollect, NZU32};
use std::{
net::{IpAddr, Ipv4Addr, SocketAddr},
str::FromStr,
sync::Arc,
};
use tracing::info;
const APPLICATION_NAMESPACE: &[u8] = b"_COMMONWARE_EXAMPLES_CHAT";
#[doc(hidden)]
fn main() {
let executor = tokio::Runner::default();
let matches = Command::new("commonware-chat")
.about("send encrypted messages to a group of friends")
.arg(Arg::new("me").long("me").required(true))
.arg(
Arg::new("friends")
.long("friends")
.required(true)
.value_delimiter(',')
.value_parser(value_parser!(u64)),
)
.arg(
Arg::new("bootstrappers")
.long("bootstrappers")
.required(false)
.value_delimiter(',')
.value_parser(value_parser!(String)),
)
.get_matches();
let logs = Arc::new(Mutex::new(Vec::new()));
let writer = logger::Writer::new(logs.clone());
tracing_subscriber::fmt()
.json()
.with_max_level(tracing::Level::DEBUG)
.with_writer(writer)
.init();
let me = matches
.get_one::<String>("me")
.expect("Please provide identity");
let parts = me.split('@').collect::<Vec<&str>>();
let key = parts[0].parse::<u64>().expect("Key not well-formed");
let signer = ed25519::PrivateKey::from_seed(key);
info!(key = ?signer.public_key(), "loaded signer");
let port = parts[1].parse::<u16>().expect("Port not well-formed");
info!(port, "loaded port");
let allowed_keys = matches
.get_many::<u64>("friends")
.expect("Please provide friends to chat with")
.copied();
if allowed_keys.len() == 0 {
panic!("Please provide at least one friend");
}
let recipients: Set<_> = allowed_keys
.into_iter()
.map(|peer| {
let verifier = ed25519::PrivateKey::from_seed(peer).public_key();
info!(key = ?verifier, "registered authorized key");
verifier
})
.try_collect()
.expect("public keys are unique");
let bootstrappers = matches.get_many::<String>("bootstrappers");
let mut bootstrapper_identities = Vec::new();
if let Some(bootstrappers) = bootstrappers {
for bootstrapper in bootstrappers {
let parts = bootstrapper.split('@').collect::<Vec<&str>>();
let bootstrapper_key = parts[0]
.parse::<u64>()
.expect("Bootstrapper key not well-formed");
let verifier = ed25519::PrivateKey::from_seed(bootstrapper_key).public_key();
let bootstrapper_address =
SocketAddr::from_str(parts[1]).expect("Bootstrapper address not well-formed");
bootstrapper_identities.push((verifier, bootstrapper_address.into()));
}
}
const MAX_MESSAGE_SIZE: u32 = 1024; let p2p_cfg = discovery::Config::local(
signer.clone(),
APPLICATION_NAMESPACE,
SocketAddr::new(IpAddr::V4(Ipv4Addr::LOCALHOST), port),
SocketAddr::new(IpAddr::V4(Ipv4Addr::LOCALHOST), port),
bootstrapper_identities.clone(),
MAX_MESSAGE_SIZE,
);
executor.start(|context| async move {
let (mut network, mut oracle) =
discovery::Network::new(context.with_label("network"), p2p_cfg);
oracle.track(0, recipients).await;
const MAX_MESSAGE_BACKLOG: usize = 128;
let (chat_sender, chat_receiver) = network.register(
handler::CHANNEL,
Quota::per_second(NZU32!(128)),
MAX_MESSAGE_BACKLOG,
);
let network_handler = network.start();
handler::run(
context.with_label("handler"),
signer.public_key().to_string(),
logs,
chat_sender,
chat_receiver,
)
.await;
network_handler.abort();
});
}