Files
nym/gateway/src/node/mod.rs
T
Jędrzej Stuczyński 12637d93ff Feature/persistent gateway storage (#784)
* Sqlx struct stub

* Initial schema

* Initial error enum

* Managed for persisted shared keys

* Initial inbox manager

* Comments

* Using new database in clients handler

* Extending gateway storage API

* tokio::main + placeholder values

* Removed old client store

* Simplified logic of async packet processing

* Renamed table + not null restriction

* BandwidthManager

* Removed sled dependency

* Using centralised storage for bandwidth

* Dead code removal

* WIP connection_handler split and simplification

Maybe it doesn't look like it right now, but once completed it will remove bunch of redundant checks for Nones etc

* Further more explicit clients handler split

* Minor cleanup

* Temporary store for active client handles

* Fixed error types

* Error trait on iv and encrypted address

* Authentication and registration moved to the handler

* Removal of clients handler

* Further logic simplification + returned explicit bandwidth values

* Further cleanup and comments

* Updated config with relevant changes

* Basic bandwidth tracking in client

* FreshHandle doc comments + fixed stagger issue

* Removed side-effects from .map

* More doc comments

* Database migration on build

* Increased default claimed bandwidth

* Renaming

* Fixed client determining available bandwidth

* Removed dead sql table that might be used in the future

* Windows workaround

* Comment

* Return error rather than cap credential
2021-09-30 09:45:21 +01:00

196 lines
6.7 KiB
Rust

// Copyright 2020 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use crate::config::Config;
use crate::node::client_handling::active_clients::ActiveClientsStore;
use crate::node::client_handling::websocket;
use crate::node::mixnet_handling::receiver::connection_handler::ConnectionHandler;
use crate::node::storage::PersistentStorage;
use coconut_interface::VerificationKey;
use credentials::obtain_aggregate_verification_key;
use crypto::asymmetric::{encryption, identity};
use log::*;
use mixnet_client::forwarder::{MixForwardingSender, PacketForwarder};
use rand::seq::SliceRandom;
use rand::thread_rng;
use std::net::SocketAddr;
use std::process;
use std::sync::Arc;
pub(crate) mod client_handling;
pub(crate) mod mixnet_handling;
pub(crate) mod storage;
pub struct Gateway {
config: Config,
/// ed25519 keypair used to assert one's identity.
identity: Arc<identity::KeyPair>,
/// x25519 keypair used for Diffie-Hellman. Currently only used for sphinx key derivation.
encryption_keys: Arc<encryption::KeyPair>,
storage: PersistentStorage,
}
impl Gateway {
async fn initialise_storage(config: &Config) -> PersistentStorage {
let path = config.get_persistent_store_path();
let retrieval_limit = config.get_message_retrieval_limit();
match PersistentStorage::init(path, retrieval_limit).await {
Err(err) => panic!("failed to initialise gateway storage - {}", err),
Ok(storage) => storage,
}
}
pub async fn new(
config: Config,
encryption_keys: encryption::KeyPair,
identity: identity::KeyPair,
) -> Self {
let storage = Self::initialise_storage(&config).await;
Gateway {
config,
identity: Arc::new(identity),
encryption_keys: Arc::new(encryption_keys),
storage,
}
}
fn start_mix_socket_listener(
&self,
ack_sender: MixForwardingSender,
active_clients_store: ActiveClientsStore,
) {
info!("Starting mix socket listener...");
let packet_processor =
mixnet_handling::PacketProcessor::new(self.encryption_keys.private_key());
let connection_handler = ConnectionHandler::new(
packet_processor,
self.storage.clone(),
ack_sender,
active_clients_store,
);
let listening_address = SocketAddr::new(
self.config.get_listening_address(),
self.config.get_mix_port(),
);
mixnet_handling::Listener::new(listening_address).start(connection_handler);
}
fn start_client_websocket_listener(
&self,
forwarding_channel: MixForwardingSender,
verification_key: VerificationKey,
active_clients_store: ActiveClientsStore,
) {
info!("Starting client [web]socket listener...");
let listening_address = SocketAddr::new(
self.config.get_listening_address(),
self.config.get_clients_port(),
);
websocket::Listener::new(
listening_address,
Arc::clone(&self.identity),
verification_key,
)
.start(
forwarding_channel,
self.storage.clone(),
active_clients_store,
);
}
fn start_packet_forwarder(&self) -> MixForwardingSender {
info!("Starting mix packet forwarder...");
let (mut packet_forwarder, packet_sender) = PacketForwarder::new(
self.config.get_packet_forwarding_initial_backoff(),
self.config.get_packet_forwarding_maximum_backoff(),
self.config.get_initial_connection_timeout(),
self.config.get_maximum_connection_buffer_size(),
);
tokio::spawn(async move { packet_forwarder.run().await });
packet_sender
}
async fn wait_for_interrupt(&self) {
if let Err(e) = tokio::signal::ctrl_c().await {
error!(
"There was an error while capturing SIGINT - {:?}. We will terminate regardless",
e
);
}
println!(
"Received SIGINT - the gateway will terminate now (threads are not yet nicely stopped, if you see stack traces that's alright)."
);
}
// TODO: ask DH whether this function still makes sense in ^0.10
async fn check_if_same_ip_gateway_exists(&self) -> Option<String> {
let endpoints = self.config.get_validator_api_endpoints();
let validator_api = endpoints
.choose(&mut thread_rng())
.expect("The list of validator apis is empty");
let validator_client = validator_client::ApiClient::new(validator_api.clone());
let existing_gateways = match validator_client.get_cached_gateways().await {
Ok(gateways) => gateways,
Err(err) => {
error!("failed to grab initial network gateways - {}\n Please try to startup again in few minutes", err);
process::exit(1);
}
};
let our_host = self.config.get_announce_address();
existing_gateways
.iter()
.find(|node| node.gateway.host == our_host)
.map(|node| node.gateway().identity_key.clone())
}
pub async fn run(&mut self) {
info!("Starting nym gateway!");
if let Some(duplicate_node_key) = self.check_if_same_ip_gateway_exists().await {
if duplicate_node_key == self.identity.public_key().to_base58_string() {
warn!("We seem to have not unregistered after going offline - there's a node with identical identity and announce-host as us registered.")
} else {
error!(
"Our announce-host is identical to an existing node's announce-host! (its key is {:?})",
duplicate_node_key
);
return;
}
}
let validators_verification_key =
obtain_aggregate_verification_key(&self.config.get_validator_api_endpoints())
.await
.expect("failed to contact validators to obtain their verification keys");
let mix_forwarding_channel = self.start_packet_forwarder();
let active_clients_store = ActiveClientsStore::new();
self.start_mix_socket_listener(
mix_forwarding_channel.clone(),
active_clients_store.clone(),
);
self.start_client_websocket_listener(
mix_forwarding_channel,
validators_verification_key,
active_clients_store,
);
info!("Finished nym gateway startup procedure - it should now be able to receive mix and client traffic!");
self.wait_for_interrupt().await
}
}