noise between client and gateway, first try

This commit is contained in:
Simon Wicky
2023-07-28 14:33:01 +02:00
parent 331a0328c7
commit 203633cabf
18 changed files with 179 additions and 58 deletions
Generated
+1
View File
@@ -3944,6 +3944,7 @@ dependencies = [
"nym-crypto",
"nym-gateway-requests",
"nym-network-defaults",
"nym-noise",
"nym-pemstore",
"nym-sphinx",
"nym-task",
@@ -306,15 +306,21 @@ where
{
let gateway_address = gateway_config.gateway_listener.clone();
let gateway_id = gateway_config.gateway_id;
let gateway_sphinx = gateway_config.gateway_sphinx;
// TODO: in theory, at this point, this should be infallible
let gateway_identity = identity::PublicKey::from_base58_string(gateway_id)
.map_err(ClientCoreError::UnableToCreatePublicKeyFromGatewayId)?;
let gateway_sphinx_key = encryption::PublicKey::from_base58_string(gateway_sphinx)
.map_err(ClientCoreError::UnableToCreateSphinxKeyFromGatewayId)?;
let mut gateway_client = GatewayClient::new(
gateway_address,
managed_keys.identity_keypair(),
managed_keys.encryption_keypair(),
gateway_identity,
gateway_sphinx_key,
Some(managed_keys.must_get_gateway_shared_key()),
mixnet_message_sender,
ack_sender,
+11 -1
View File
@@ -2,7 +2,7 @@
// SPDX-License-Identifier: Apache-2.0
use nym_config::defaults::NymNetworkDetails;
use nym_crypto::asymmetric::identity;
use nym_crypto::asymmetric::{encryption, identity};
use nym_sphinx::params::{PacketSize, PacketType};
use serde::{Deserialize, Serialize};
use std::time::Duration;
@@ -207,6 +207,8 @@ pub struct GatewayEndpointConfig {
/// If initially omitted, a random gateway will be chosen from the available topology.
pub gateway_id: String,
pub gateway_sphinx: String,
/// Address of the gateway owner to which the client should send messages.
pub gateway_owner: String,
@@ -219,11 +221,13 @@ impl GatewayEndpointConfig {
#[cfg_attr(target_arch = "wasm32", wasm_bindgen(constructor))]
pub fn new(
gateway_id: String,
gateway_sphinx: String,
gateway_owner: String,
gateway_listener: String,
) -> GatewayEndpointConfig {
GatewayEndpointConfig {
gateway_id,
gateway_sphinx,
gateway_owner,
gateway_listener,
}
@@ -236,6 +240,11 @@ impl GatewayEndpointConfig {
identity::PublicKey::from_base58_string(&self.gateway_id)
.map_err(ClientCoreError::UnableToCreatePublicKeyFromGatewayId)
}
pub fn try_get_gateway_sphinx_key(&self) -> Result<encryption::PublicKey, ClientCoreError> {
encryption::PublicKey::from_base58_string(&self.gateway_sphinx)
.map_err(ClientCoreError::UnableToCreateSphinxKeyFromGatewayId)
}
}
impl From<nym_topology::gateway::Node> for GatewayEndpointConfig {
@@ -243,6 +252,7 @@ impl From<nym_topology::gateway::Node> for GatewayEndpointConfig {
let gateway_listener = node.clients_address();
GatewayEndpointConfig {
gateway_id: node.identity_key.to_base58_string(),
gateway_sphinx: node.sphinx_key.to_base58_string(),
gateway_owner: node.owner,
gateway_listener,
}
@@ -68,6 +68,7 @@ pub struct ConfigV1_1_20<T> {
#[derive(Clone, Debug, Default, Deserialize, PartialEq, Eq, Serialize)]
pub struct GatewayEndpointConfigV1_1_20 {
pub gateway_id: String,
pub gateway_sphinx: String,
pub gateway_owner: String,
pub gateway_listener: String,
}
@@ -76,6 +77,7 @@ impl From<GatewayEndpointConfigV1_1_20> for GatewayEndpointConfigV1_1_20_2 {
fn from(value: GatewayEndpointConfigV1_1_20) -> Self {
GatewayEndpointConfigV1_1_20_2 {
gateway_id: value.gateway_id,
gateway_sphinx: value.gateway_sphinx,
gateway_owner: value.gateway_owner,
gateway_listener: value.gateway_listener,
}
@@ -73,6 +73,8 @@ pub struct GatewayEndpointConfigV1_1_20_2 {
/// If initially omitted, a random gateway will be chosen from the available topology.
pub gateway_id: String,
pub gateway_sphinx: String,
/// Address of the gateway owner to which the client should send messages.
pub gateway_owner: String,
@@ -84,6 +86,7 @@ impl From<GatewayEndpointConfigV1_1_20_2> for GatewayEndpointConfig {
fn from(value: GatewayEndpointConfigV1_1_20_2) -> Self {
GatewayEndpointConfig {
gateway_id: value.gateway_id,
gateway_sphinx: value.gateway_sphinx,
gateway_owner: value.gateway_owner,
gateway_listener: value.gateway_listener,
}
+4
View File
@@ -1,6 +1,7 @@
// Copyright 2022 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use nym_crypto::asymmetric::encryption::KeyRecoveryError;
use nym_crypto::asymmetric::identity::Ed25519RecoveryError;
use nym_gateway_client::error::GatewayClientError;
use nym_topology::gateway::GatewayConversionError;
@@ -58,6 +59,9 @@ pub enum ClientCoreError {
#[error("The gateway id is invalid - {0}")]
UnableToCreatePublicKeyFromGatewayId(Ed25519RecoveryError),
#[error("The gateway sphinx is invalid - {0}")]
UnableToCreateSphinxKeyFromGatewayId(KeyRecoveryError),
#[error("The identity of the gateway is unknown - did you run init?")]
GatewayIdUnknown,
+4 -1
View File
@@ -5,7 +5,7 @@ use crate::config::GatewayEndpointConfig;
use crate::error::ClientCoreError;
use futures::{SinkExt, StreamExt};
use log::{debug, info, trace, warn};
use nym_crypto::asymmetric::identity;
use nym_crypto::asymmetric::{encryption, identity};
use nym_gateway_client::GatewayClient;
use nym_gateway_requests::registration::handshake::SharedKeys;
use nym_topology::{filter::VersionFilterable, gateway};
@@ -200,12 +200,15 @@ pub(super) fn uniformly_random_gateway<R: Rng>(
pub(super) async fn register_with_gateway(
gateway: &GatewayEndpointConfig,
our_identity: Arc<identity::KeyPair>,
our_sphinx: Arc<encryption::KeyPair>,
) -> Result<Arc<SharedKeys>, ClientCoreError> {
let timeout = Duration::from_millis(1500);
let mut gateway_client: GatewayClient<DirectSigningNyxdClient, _> = GatewayClient::new_init(
gateway.gateway_listener.clone(),
gateway.try_get_gateway_identity_key()?,
gateway.try_get_gateway_sphinx_key()?,
our_identity.clone(),
our_sphinx.clone(),
timeout,
);
gateway_client
+4 -1
View File
@@ -370,8 +370,11 @@ where
// get our identity key
let our_identity = managed_keys.identity_keypair();
let our_sphinx = managed_keys.encryption_keypair();
// Establish connection, authenticate and generate keys for talking with the gateway
let shared_keys = helpers::register_with_gateway(&gateway_details, our_identity).await?;
let shared_keys =
helpers::register_with_gateway(&gateway_details, our_identity, our_sphinx).await?;
let persisted_details = PersistedGatewayDetails::new(gateway_details, &shared_keys);
@@ -27,6 +27,7 @@ nym-sphinx = { path = "../../nymsphinx" }
nym-pemstore = { path = "../../pemstore" }
nym-validator-client = { path = "../validator-client" }
nym-task = { path = "../../task" }
nym-noise = { path = "../../nymnoise"}
serde = { workspace = true, features = ["derive"] }
@@ -14,24 +14,27 @@ use nym_bandwidth_controller::BandwidthController;
use nym_coconut_interface::Credential;
use nym_credential_storage::ephemeral_storage::EphemeralStorage as EphemeralCredentialStorage;
use nym_credential_storage::storage::Storage as CredentialStorage;
use nym_crypto::asymmetric::identity;
use nym_crypto::asymmetric::{encryption, identity};
use nym_gateway_requests::authentication::encrypted_address::EncryptedAddressBytes;
use nym_gateway_requests::iv::IV;
use nym_gateway_requests::registration::handshake::{client_handshake, SharedKeys};
use nym_gateway_requests::{BinaryRequest, ClientControlRequest, ServerResponse, PROTOCOL_VERSION};
use nym_network_defaults::{REMAINING_BANDWIDTH_THRESHOLD, TOKENS_TO_BURN};
use nym_noise::upgrade_noise_initiator;
use nym_sphinx::forwarding::packet::MixPacket;
use nym_task::TaskClient;
use rand::rngs::OsRng;
use std::convert::TryFrom;
use std::net::SocketAddr;
use std::sync::Arc;
use std::time::Duration;
use tokio::net::TcpStream;
use tungstenite::protocol::Message;
#[cfg(not(target_arch = "wasm32"))]
use nym_validator_client::nyxd::traits::DkgQueryClient;
#[cfg(not(target_arch = "wasm32"))]
use tokio_tungstenite::connect_async;
use tokio_tungstenite::client_async;
#[cfg(target_arch = "wasm32")]
use nym_bandwidth_controller::wasm_mockups::DkgQueryClient;
@@ -49,7 +52,9 @@ pub struct GatewayClient<C, St> {
bandwidth_remaining: i64,
gateway_address: String,
gateway_identity: identity::PublicKey,
gateway_sphinx: encryption::PublicKey,
local_identity: Arc<identity::KeyPair>,
local_sphinx: Arc<encryption::KeyPair>,
shared_key: Option<Arc<SharedKeys>>,
connection: SocketState,
packet_router: PacketRouter,
@@ -75,7 +80,9 @@ impl<C, St> GatewayClient<C, St> {
pub fn new(
gateway_address: String,
local_identity: Arc<identity::KeyPair>,
local_sphinx: Arc<encryption::KeyPair>,
gateway_identity: identity::PublicKey,
gateway_sphinx: encryption::PublicKey,
// TODO: make it mandatory. if you don't want to pass it, use `new_init`
shared_key: Option<Arc<SharedKeys>>,
mixnet_message_sender: MixnetMessageSender,
@@ -89,8 +96,10 @@ impl<C, St> GatewayClient<C, St> {
disabled_credentials_mode: true,
bandwidth_remaining: 0,
gateway_address,
gateway_identity,
local_identity,
local_sphinx,
gateway_identity,
gateway_sphinx,
shared_key,
connection: SocketState::NotConnected,
packet_router: PacketRouter::new(ack_sender, mixnet_message_sender, shutdown.clone()),
@@ -163,7 +172,50 @@ impl<C, St> GatewayClient<C, St> {
#[cfg(not(target_arch = "wasm32"))]
pub async fn establish_connection(&mut self) -> Result<(), GatewayClientError> {
let ws_stream = match connect_async(&self.gateway_address).await {
let socket_addr: SocketAddr = self.gateway_address.parse().unwrap();
let connection_fut = TcpStream::connect(socket_addr);
//arbitrary TO, it's a POC
let noise_conn = match tokio::time::timeout(Duration::from_secs(5), connection_fut).await {
Ok(stream_res) => match stream_res {
Ok(stream) => {
debug!("Managed to establish connection to gateway");
let noise_stream = match upgrade_noise_initiator(
stream,
None, //as a client, the gateway cannot know my pub key
&self.local_sphinx.private_key().to_bytes(),
&self.gateway_sphinx.to_bytes(),
)
.await
{
Ok(noise_stream) => noise_stream,
Err(err) => {
error!(
"Failed to perform Noise handshake with {:?} - {err}",
self.gateway_address
);
return Err(GatewayClientError::ConnectionNotEstablished);
}
};
debug!(
"Noise initiator handshake completed for {:?}",
self.gateway_address
);
noise_stream
}
Err(err) => {
debug!("failed to establish connection to gateway (err: {})", err);
return Err(GatewayClientError::NetworkIoError(err));
}
},
Err(_) => {
debug!("failed to connect to {} within 5s", self.gateway_address);
return Err(GatewayClientError::Timeout);
}
};
let ws_address = format!("ws://{}", self.gateway_address);
let ws_stream = match client_async(ws_address, noise_conn).await {
Ok((ws_stream, _)) => ws_stream,
Err(e) => return Err(GatewayClientError::NetworkError(e)),
};
@@ -773,7 +825,9 @@ impl<C> GatewayClient<C, EphemeralCredentialStorage> {
pub fn new_init(
gateway_address: String,
gateway_identity: identity::PublicKey,
gateway_sphinx: encryption::PublicKey,
local_identity: Arc<identity::KeyPair>,
local_sphinx: Arc<encryption::KeyPair>,
response_timeout_duration: Duration,
) -> Self {
use futures::channel::mpsc;
@@ -791,7 +845,9 @@ impl<C> GatewayClient<C, EphemeralCredentialStorage> {
bandwidth_remaining: 0,
gateway_address,
gateway_identity,
gateway_sphinx,
local_identity,
local_sphinx,
shared_key: None,
connection: SocketState::NotConnected,
packet_router,
@@ -19,6 +19,9 @@ pub enum GatewayClientError {
#[error("There was a network error - {0}")]
NetworkError(#[from] WsError),
#[error("There was a network error - {0}")]
NetworkIoError(#[from] io::Error),
// TODO: see if `JsValue` is a reasonable type for this
#[cfg(target_arch = "wasm32")]
#[error("There was a network error")]
@@ -9,14 +9,13 @@ use futures::stream::{SplitSink, SplitStream};
use futures::{SinkExt, StreamExt};
use log::*;
use nym_gateway_requests::registration::handshake::SharedKeys;
use nym_noise::NoiseStream;
use nym_task::TaskClient;
use std::sync::Arc;
use tungstenite::Message;
#[cfg(not(target_arch = "wasm32"))]
use tokio::net::TcpStream;
#[cfg(not(target_arch = "wasm32"))]
use tokio_tungstenite::{MaybeTlsStream, WebSocketStream};
use tokio_tungstenite::WebSocketStream;
#[cfg(target_arch = "wasm32")]
use wasm_bindgen_futures;
@@ -26,7 +25,7 @@ use wasm_utils::websocket::JSWebsocket;
// type alias for not having to type the whole thing every single time (and now it makes it easier
// to use different types based on compilation target)
#[cfg(not(target_arch = "wasm32"))]
type WsConn = WebSocketStream<MaybeTlsStream<TcpStream>>;
type WsConn = WebSocketStream<NoiseStream>;
#[cfg(target_arch = "wasm32")]
type WsConn = JSWebsocket;
+1 -1
View File
@@ -67,7 +67,7 @@ impl Node {
}
pub fn clients_address(&self) -> String {
format!("ws://{}:{}", self.host, self.clients_port)
format!("{}:{}", self.host, self.clients_port)
}
}
@@ -62,49 +62,48 @@ impl Listener {
while !shutdown.is_shutdown() {
tokio::select! {
biased;
_ = shutdown.recv() => {
log::trace!("client_handling::Listener: received shutdown");
}
connection = tcp_listener.accept() => {
match connection {
Ok((socket, remote_addr)) => {
trace!("received a socket connection from {remote_addr}");
// TODO: I think we *REALLY* need a mechanism for having a maximum number of connected
// clients or spawned tokio tasks -> perhaps a worker system?
// let noise_stream = match upgrade_noise_responder(
// socket,
// &self.local_sphinx.public_key().to_bytes(),
// &self.local_sphinx.private_key().to_bytes(),
// None, //connection from client, no remote pub key
// )
// .await
// {
// Ok(noise_stream) => noise_stream,
// Err(err) => {
// error!("Failed to perform Noise handshake with {remote_addr} - {err}");
// return;
// }
// };
let handle = FreshHandler::new(
OsRng,
socket,
// noise_stream,
self.only_coconut_credentials,
outbound_mix_sender.clone(),
Arc::clone(&self.local_identity),
storage.clone(),
active_clients_store.clone(),
Arc::clone(&self.coconut_verifier),
);
let shutdown = shutdown.clone();
tokio::spawn(async move { handle.start_handling(shutdown).await });
}
Err(err) => warn!("failed to get client: {err}"),
biased;
_ = shutdown.recv() => {
log::trace!("client_handling::Listener: received shutdown");
}
connection = tcp_listener.accept() => {
match connection {
Ok((socket, remote_addr)) => {
trace!("received a socket connection from {remote_addr}");
// TODO: I think we *REALLY* need a mechanism for having a maximum number of connected
// clients or spawned tokio tasks -> perhaps a worker system?
let noise_stream = match upgrade_noise_responder(
socket,
&self.local_sphinx.public_key().to_bytes(),
&self.local_sphinx.private_key().to_bytes(),
None, //connection from client, no remote pub key
)
.await
{
Ok(noise_stream) => noise_stream,
Err(err) => {
error!("Failed to perform Noise handshake with {remote_addr} - {err}");
return;
}
}
};
let handle = FreshHandler::new(
OsRng,
noise_stream,
self.only_coconut_credentials,
outbound_mix_sender.clone(),
Arc::clone(&self.local_identity),
storage.clone(),
active_clients_store.clone(),
Arc::clone(&self.coconut_verifier),
);
let shutdown = shutdown.clone();
tokio::spawn(async move { handle.start_handling(shutdown).await });
}
Err(err) => warn!("failed to get client: {err}"),
}
}
}
}
}
+3
View File
@@ -105,6 +105,7 @@ impl<'a> NetworkMonitorBuilder<'a> {
self.config,
gateway_status_update_sender,
Arc::clone(&identity_keypair),
Arc::clone(&encryption_keypair),
self.config.debug.gateway_sending_rate,
bandwidth_controller,
self.config.debug.disabled_credentials_mode,
@@ -177,6 +178,7 @@ fn new_packet_sender(
config: &config::NetworkMonitor,
gateways_status_updater: GatewayClientUpdateSender,
local_identity: Arc<identity::KeyPair>,
local_sphinx: Arc<encryption::KeyPair>,
max_sending_rate: usize,
bandwidth_controller: BandwidthController<nyxd::Client, PersistentStorage>,
disabled_credentials_mode: bool,
@@ -184,6 +186,7 @@ fn new_packet_sender(
PacketSender::new(
gateways_status_updater,
local_identity,
local_sphinx,
config.debug.gateway_response_timeout,
config.debug.gateway_connection_timeout,
config.debug.max_concurrent_gateway_clients,
@@ -310,6 +310,7 @@ impl PacketPreparer {
GatewayPackets::new(
route.gateway_clients_address(),
route.gateway_identity(),
route.gateway_sphinx(),
mix_packets,
)
}
@@ -387,6 +388,7 @@ impl PacketPreparer {
let route_ext = test_route.test_message_ext(test_nonce);
let gateway_address = test_route.gateway_clients_address();
let gateway_identity = test_route.gateway_identity();
let gateway_sphinx = test_route.gateway_sphinx();
let mut mix_tester = self.ephemeral_mix_tester(test_route);
@@ -408,7 +410,9 @@ impl PacketPreparer {
let gateway_packets = all_gateway_packets
.entry(gateway_identity.to_bytes())
.or_insert_with(|| GatewayPackets::empty(gateway_address, gateway_identity));
.or_insert_with(|| {
GatewayPackets::empty(gateway_address, gateway_identity, gateway_sphinx)
});
gateway_packets.push_packets(mix_packets);
// and generate test packets for gateways (note the variable recipient)
@@ -436,7 +440,9 @@ impl PacketPreparer {
// or create a new one
let gateway_packets = all_gateway_packets
.entry(gateway_identity.to_bytes())
.or_insert_with(|| GatewayPackets::empty(gateway_address, gateway_identity));
.or_insert_with(|| {
GatewayPackets::empty(gateway_address, gateway_identity, gateway_sphinx)
});
gateway_packets.push_packets(gateway_mix_packets);
}
}
+20 -2
View File
@@ -15,6 +15,7 @@ use log::{debug, info, trace, warn};
use nym_bandwidth_controller::BandwidthController;
use nym_config::defaults::REMAINING_BANDWIDTH_THRESHOLD;
use nym_credential_storage::persistent_storage::PersistentStorage;
use nym_crypto::asymmetric::encryption;
use nym_crypto::asymmetric::identity::{self, PUBLIC_KEY_LENGTH};
use nym_gateway_client::error::GatewayClientError;
use nym_gateway_client::{AcknowledgementReceiver, GatewayClient, MixnetMessageReceiver};
@@ -38,6 +39,8 @@ pub(crate) struct GatewayPackets {
/// Public key of the target gateway.
pub(crate) pub_key: identity::PublicKey,
pub(crate) encryption_key: encryption::PublicKey,
/// All the packets that are going to get sent to the gateway.
pub(crate) packets: Vec<MixPacket>,
}
@@ -46,19 +49,26 @@ impl GatewayPackets {
pub(crate) fn new(
clients_address: String,
pub_key: identity::PublicKey,
encryption_key: encryption::PublicKey,
packets: Vec<MixPacket>,
) -> Self {
GatewayPackets {
clients_address,
pub_key,
encryption_key,
packets,
}
}
pub(crate) fn empty(clients_address: String, pub_key: identity::PublicKey) -> Self {
pub(crate) fn empty(
clients_address: String,
pub_key: identity::PublicKey,
encryption_key: encryption::PublicKey,
) -> Self {
GatewayPackets {
clients_address,
pub_key,
encryption_key,
packets: Vec::new(),
}
}
@@ -79,6 +89,7 @@ impl GatewayPackets {
struct FreshGatewayClientData {
gateways_status_updater: GatewayClientUpdateSender,
local_identity: Arc<identity::KeyPair>,
local_sphinx: Arc<encryption::KeyPair>,
gateway_response_timeout: Duration,
bandwidth_controller: BandwidthController<nyxd::Client, PersistentStorage>,
disabled_credentials_mode: bool,
@@ -134,6 +145,7 @@ impl PacketSender {
pub(crate) fn new(
gateways_status_updater: GatewayClientUpdateSender,
local_identity: Arc<identity::KeyPair>,
local_sphinx: Arc<encryption::KeyPair>,
gateway_response_timeout: Duration,
gateway_connection_timeout: Duration,
max_concurrent_clients: usize,
@@ -146,6 +158,7 @@ impl PacketSender {
fresh_gateway_client_data: Arc::new(FreshGatewayClientData {
gateways_status_updater,
local_identity,
local_sphinx,
gateway_response_timeout,
bandwidth_controller,
disabled_credentials_mode,
@@ -171,6 +184,7 @@ impl PacketSender {
fn new_gateway_client_handle(
address: String,
identity: identity::PublicKey,
sphinx: encryption::PublicKey,
fresh_gateway_client_data: &FreshGatewayClientData,
) -> (
GatewayClientHandle,
@@ -187,7 +201,9 @@ impl PacketSender {
let mut gateway_client = GatewayClient::new(
address,
Arc::clone(&fresh_gateway_client_data.local_identity),
Arc::clone(&fresh_gateway_client_data.local_sphinx),
identity,
sphinx,
None,
message_sender,
ack_sender,
@@ -267,6 +283,7 @@ impl PacketSender {
async fn create_new_gateway_client_handle_and_authenticate(
address: String,
identity: identity::PublicKey,
sphinx: encryption::PublicKey,
fresh_gateway_client_data: &FreshGatewayClientData,
gateway_connection_timeout: Duration,
) -> Option<(
@@ -274,7 +291,7 @@ impl PacketSender {
(MixnetMessageReceiver, AcknowledgementReceiver),
)> {
let (new_client, (message_receiver, ack_receiver)) =
Self::new_gateway_client_handle(address, identity, fresh_gateway_client_data);
Self::new_gateway_client_handle(address, identity, sphinx, fresh_gateway_client_data);
// Put this in timeout in case the gateway has incorrectly set their ulimit and our connection
// gets stuck in their TCP queue and just hangs on our end but does not terminate
@@ -351,6 +368,7 @@ impl PacketSender {
Self::create_new_gateway_client_handle_and_authenticate(
packets.clients_address,
packets.pub_key,
packets.encryption_key,
&fresh_gateway_client_data,
gateway_connection_timeout,
)
@@ -3,7 +3,7 @@
use crate::network_monitor::test_packet::NymApiTestMessageExt;
use crate::network_monitor::ROUTE_TESTING_TEST_NONCE;
use nym_crypto::asymmetric::identity;
use nym_crypto::asymmetric::{encryption, identity};
use nym_topology::{gateway, mix, NymTopology};
use std::fmt::{Debug, Formatter};
@@ -63,6 +63,10 @@ impl TestRoute {
self.gateway().identity_key
}
pub(crate) fn gateway_sphinx(&self) -> encryption::PublicKey {
self.gateway().sphinx_key
}
pub(crate) fn topology(&self) -> &NymTopology {
&self.nodes
}