Feature/addressing move (#169)

* Moved 'addressing' to nymsphinx + slightly better API + tests

* Replaced all usages of above in the project

* Removed 'split_and_encapsulate_message' from chunking API to get rid of circular dependency + to prefer usage of TopologyAccessor

* changes in Cargo.lock

* formatting
This commit is contained in:
Jędrzej Stuczyński
2020-04-03 15:07:02 +01:00
committed by GitHub
parent d859a98367
commit b564cd935e
19 changed files with 246 additions and 204 deletions
Generated
+4 -15
View File
@@ -17,14 +17,6 @@ dependencies = [
"tokio 0.1.22",
]
[[package]]
name = "addressing"
version = "0.1.0"
dependencies = [
"log 0.4.8",
"pretty_env_logger",
]
[[package]]
name = "adler32"
version = "1.0.4"
@@ -931,13 +923,13 @@ dependencies = [
name = "healthcheck"
version = "0.1.0"
dependencies = [
"addressing",
"crypto",
"directory-client",
"futures 0.3.4",
"itertools",
"log 0.4.8",
"multi-tcp-client",
"nymsphinx",
"pretty_env_logger",
"provider-client",
"rand 0.7.3",
@@ -1432,8 +1424,8 @@ dependencies = [
name = "mix-client"
version = "0.1.0"
dependencies = [
"addressing",
"log 0.4.8",
"nymsphinx",
"pretty_env_logger",
"rand 0.7.3",
"rand_distr",
@@ -1537,7 +1529,6 @@ dependencies = [
name = "nym-client"
version = "0.5.0"
dependencies = [
"addressing",
"bs58",
"built",
"clap",
@@ -1572,7 +1563,6 @@ dependencies = [
name = "nym-mixnode"
version = "0.5.0"
dependencies = [
"addressing",
"bs58",
"built",
"clap",
@@ -1585,6 +1575,7 @@ dependencies = [
"futures 0.3.4",
"log 0.4.8",
"multi-tcp-client",
"nymsphinx",
"pemstore",
"pretty_env_logger",
"serde",
@@ -1654,11 +1645,9 @@ dependencies = [
name = "nymsphinx"
version = "0.1.0"
dependencies = [
"addressing",
"log 0.4.8",
"rand 0.7.3",
"sphinx",
"topology",
]
[[package]]
@@ -2858,10 +2847,10 @@ dependencies = [
name = "topology"
version = "0.1.0"
dependencies = [
"addressing",
"bs58",
"itertools",
"log 0.4.8",
"nymsphinx",
"pretty_env_logger",
"rand 0.7.3",
"serde",
-1
View File
@@ -9,7 +9,6 @@ members = [
"common/clients/multi-tcp-client",
"common/clients/provider-client",
"common/clients/validator-client",
"common/addressing",
"common/config",
"common/crypto",
"common/healthcheck",
-11
View File
@@ -1,11 +0,0 @@
[package]
name = "addressing"
version = "0.1.0"
authors = ["Jedrzej Stuczynski <andrew@nymtech.net>"]
edition = "2018"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
log = "0.4"
pretty_env_logger = "0.3"
-80
View File
@@ -1,80 +0,0 @@
use std::convert::{TryFrom, TryInto};
use std::net::{IpAddr, Ipv4Addr, Ipv6Addr, SocketAddr};
pub enum AddressType {
V4,
V6,
}
impl Into<u8> for AddressType {
fn into(self) -> u8 {
use AddressType::*;
match self {
V4 => 4,
V6 => 6,
}
}
}
#[derive(Debug)]
pub enum AddressTypeError {
InvalidPrefixError,
}
impl TryFrom<u8> for AddressType {
type Error = AddressTypeError;
fn try_from(value: u8) -> Result<Self, Self::Error> {
use AddressType::*;
use AddressTypeError::*;
match value {
4 => Ok(V4),
6 => Ok(V6),
_ => Err(InvalidPrefixError),
}
}
}
/// FLAG || port || octets || zeropad
pub fn encoded_bytes_from_socket_address(address: SocketAddr) -> [u8; 32] {
let port_bytes = address.port().to_be_bytes();
let encoded_host: Vec<u8> = match address.ip() {
IpAddr::V4(ip) => std::iter::once(AddressType::V4.into())
.chain(port_bytes.iter().cloned())
.chain(ip.octets().iter().cloned())
.chain(std::iter::repeat(0))
.take(32)
.collect(),
IpAddr::V6(ip) => std::iter::once(AddressType::V6.into())
.chain(port_bytes.iter().cloned())
.chain(ip.octets().iter().cloned())
.chain(std::iter::repeat(0))
.take(32)
.collect(),
};
let mut address_bytes = [0u8; 32];
address_bytes.copy_from_slice(&encoded_host[..32]);
address_bytes
}
pub fn socket_address_from_encoded_bytes(b: [u8; 32]) -> Result<SocketAddr, AddressTypeError> {
let address_type: AddressType = b[0].try_into()?;
let port: u16 = u16::from_be_bytes([b[1], b[2]]);
let ip = match address_type {
AddressType::V4 => IpAddr::V4(Ipv4Addr::new(b[3], b[4], b[5], b[6])),
AddressType::V6 => {
let mut address_octets = [0u8; 16];
address_octets.copy_from_slice(&b[3..19]);
IpAddr::V6(Ipv6Addr::from(address_octets))
}
};
Ok(SocketAddr::new(ip, port))
}
+1 -1
View File
@@ -14,7 +14,7 @@ rand_distr = "0.2.2"
tokio = { version = "0.2", features = ["full"] }
## internal
addressing = {path = "../../addressing"}
nymsphinx = {path = "../../nymsphinx"}
topology = {path = "../../topology"}
## will be moved to proper dependencies once released
+8 -8
View File
@@ -1,7 +1,7 @@
use addressing;
use addressing::AddressTypeError;
use nymsphinx::addressing::nodes::{NymNodeRoutingAddress, NymNodeRoutingAddressError};
use sphinx::route::{Destination, DestinationAddressBytes, SURBIdentifier};
use sphinx::SphinxPacket;
use std::convert::TryFrom;
use std::net::SocketAddr;
use std::time;
use topology::{NymTopology, NymTopologyError};
@@ -32,8 +32,8 @@ impl From<sphinx::header::SphinxUnwrapError> for SphinxPacketEncapsulationError
}
}
impl From<AddressTypeError> for SphinxPacketEncapsulationError {
fn from(_: AddressTypeError) -> Self {
impl From<NymNodeRoutingAddressError> for SphinxPacketEncapsulationError {
fn from(_: NymNodeRoutingAddressError) -> Self {
use SphinxPacketEncapsulationError::*;
InvalidFirstMixAddress
}
@@ -96,9 +96,9 @@ pub fn encapsulate_message<T: NymTopology>(
// we know the mix route must be valid otherwise we would have already returned an error
let first_node_address =
addressing::socket_address_from_encoded_bytes(route.first().unwrap().address.to_bytes())?;
NymNodeRoutingAddress::try_from(route.first().unwrap().address.clone())?;
Ok((first_node_address, packet))
Ok((first_node_address.into(), packet))
}
pub fn encapsulate_message_route(
@@ -113,7 +113,7 @@ pub fn encapsulate_message_route(
let packet = sphinx::SphinxPacket::new(message, &route[..], &recipient, &delays)?;
let first_node_address =
addressing::socket_address_from_encoded_bytes(route.first().unwrap().address.to_bytes())?;
NymNodeRoutingAddress::try_from(route.first().unwrap().address.clone())?;
Ok((first_node_address, packet))
Ok((first_node_address.into(), packet))
}
+1 -1
View File
@@ -18,10 +18,10 @@ serde_derive = "1.0.104"
tokio = { version = "0.2", features = ["full"] }
## internal
addressing = {path = "../addressing" }
crypto = { path = "../crypto" }
directory-client = { path = "../clients/directory-client" }
multi-tcp-client = { path = "../clients/multi-tcp-client" }
nymsphinx = {path = "../nymsphinx" }
provider-client = { path = "../clients/provider-client" }
sfw-provider-requests = { path = "../../sfw-provider/sfw-provider-requests" }
topology = {path = "../topology" }
+7 -3
View File
@@ -1,10 +1,13 @@
use crypto::identity::MixIdentityKeyPair;
use itertools::Itertools;
use log::{debug, error, info, trace, warn};
use nymsphinx::addressing::nodes::NymNodeRoutingAddress;
use provider_client::{ProviderClient, ProviderClientError};
use sphinx::header::delays::Delay;
use sphinx::route::{Destination, Node as SphinxNode};
use std::collections::HashMap;
use std::convert::TryFrom;
use std::net::SocketAddr;
use std::time::Duration;
use topology::provider;
@@ -217,9 +220,10 @@ impl PathChecker {
.expect("We checked the path to contain at least one entry");
// we generated the bytes data so unwrap is fine
let first_node_address =
addressing::socket_address_from_encoded_bytes(layer_one_mix.address.to_bytes())
.unwrap();
let first_node_address: SocketAddr =
NymNodeRoutingAddress::try_from(layer_one_mix.address.clone())
.unwrap()
.into();
let delays: Vec<_> = path.iter().map(|_| Delay::new_from_nanos(0)).collect();
-6
View File
@@ -10,12 +10,6 @@ edition = "2018"
log = "0.4.8"
rand = "0.7.3"
# TODO: eventually move this functionality to this crate and remove `addressing` completely
addressing = {path = "../addressing" }
topology = {path = "../topology" }
## will be moved to proper dependencies once released
sphinx = { git = "https://github.com/nymtech/sphinx", rev="44d8f2aece5049eaa4fe84b7948758ce82b4b80d" }
+1
View File
@@ -0,0 +1 @@
pub mod nodes;
+206
View File
@@ -0,0 +1,206 @@
use sphinx::constants::NODE_ADDRESS_LENGTH;
use sphinx::route::NodeAddressBytes;
use std::convert::{TryFrom, TryInto};
use std::net::{IpAddr, Ipv4Addr, Ipv6Addr, SocketAddr};
/// This module is responsible for encoding and decoding node routing information, so that
/// they could be later put into an appropriate field in a sphinx header.
/// Currently, that routing information is an IP address, but in principle it can be anything
/// for as long as it's going to fit in the field.
#[derive(Debug)]
pub enum NymNodeRoutingAddressError {
InsufficientNumberOfBytesAvailableError,
InvalidIPVersion,
}
/// Current representation of Node routing information used in Nym system.
/// At this point of time it is a simple `SocketAddr`.
#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)]
pub struct NymNodeRoutingAddress(SocketAddr);
impl NymNodeRoutingAddress {
/// Minimum number of bytes that need to be available to represent self.
/// The value has no upper bound as when converted into bytes, it's always
/// padded with zeroes to be exactly NODE_ADDRESS_LENGTH long.
pub fn bytes_min_len(&self) -> usize {
match self.0 {
SocketAddr::V4(_) => 7,
SocketAddr::V6(_) => 19,
}
}
/// Converts self into a vector of bytes.
/// Note, this represents a generic bytes vector, not necessarily a NodeAddressBytes
/// and hence is not zero-padded.
pub fn as_bytes(&self) -> Vec<u8> {
let port_bytes = self.0.port().to_be_bytes();
let ip_octets_vec = match self.0.ip() {
IpAddr::V4(ip) => ip.octets().to_vec(),
IpAddr::V6(ip) => ip.octets().to_vec(),
};
std::iter::once(self.addr_type_as_u8())
.chain(port_bytes.iter().cloned())
.chain(ip_octets_vec.iter().cloned())
.collect()
}
/// Tries to recover `Self` from a bytes slice.
/// Does not care if it's zero-padded or not.
pub fn try_from_bytes(b: &[u8]) -> Result<Self, NymNodeRoutingAddressError> {
// the bare minimum to represent `Self` is 7 bytes (for the shorter V4 version)
if b.len() < 7 {
return Err(NymNodeRoutingAddressError::InsufficientNumberOfBytesAvailableError);
}
let ip_version = b[0];
let port: u16 = u16::from_be_bytes([b[1], b[2]]);
let ip = match ip_version {
4 => IpAddr::V4(Ipv4Addr::new(b[3], b[4], b[5], b[6])),
6 => {
if b.len() < 19 {
return Err(
NymNodeRoutingAddressError::InsufficientNumberOfBytesAvailableError,
);
}
let mut address_octets = [0u8; 16];
address_octets.copy_from_slice(&b[3..19]);
IpAddr::V6(Ipv6Addr::from(address_octets))
}
_ => return Err(NymNodeRoutingAddressError::InvalidIPVersion),
};
Ok(Self(SocketAddr::new(ip, port)))
}
/// Single byte representation of self ip version.
pub fn addr_type_as_u8(&self) -> u8 {
match self.0 {
SocketAddr::V4(_) => 4,
SocketAddr::V6(_) => 6,
}
}
}
/// Considering `NymNodeRoutingAddress` is equivalent to a `SocketAddr` at this point,
/// it makes perfect sense to allow the bilateral transformation.
impl From<SocketAddr> for NymNodeRoutingAddress {
fn from(addr: SocketAddr) -> Self {
Self(addr)
}
}
/// Considering `NymNodeRoutingAddress` is equivalent to a `SocketAddr` at this point,
/// it makes perfect sense to allow the bilateral transformation.
impl Into<SocketAddr> for NymNodeRoutingAddress {
fn into(self) -> SocketAddr {
self.0
}
}
impl TryInto<NodeAddressBytes> for NymNodeRoutingAddress {
type Error = NymNodeRoutingAddressError;
/// `NymNodeRoutingAddress` (as a `SocketAddr`) is represented the following way:
/// VersionFlag || port || octets || zeropad
/// VersionFlag is one byte representing whether self is ipv4 or ipv6 address,
/// port is 16bit big endian representation of port value
/// octets is bytes representation of octets making up the ip address of the socket address
/// (either 4 bytes for ipv4 or 16 bytes for ipv6)
/// zeropad is padding of 0 for the `NymNodeRoutingAddress` to be
/// exactly `NODE_ADDRESS_LENGTH` long.
fn try_into(self) -> Result<NodeAddressBytes, Self::Error> {
// first check if we have enough bytes to represent `self`:
if self.bytes_min_len() > NODE_ADDRESS_LENGTH {
return Err(NymNodeRoutingAddressError::InsufficientNumberOfBytesAvailableError);
}
let unpadded_address = self.as_bytes();
let padded_address: Vec<_> = unpadded_address
.into_iter()
.chain(std::iter::repeat(0))
.take(NODE_ADDRESS_LENGTH)
.collect();
let mut node_address_bytes = [0u8; 32];
node_address_bytes.copy_from_slice(&padded_address);
Ok(NodeAddressBytes::from_bytes(node_address_bytes))
}
}
impl TryFrom<NodeAddressBytes> for NymNodeRoutingAddress {
type Error = NymNodeRoutingAddressError;
fn try_from(value: NodeAddressBytes) -> Result<Self, Self::Error> {
Self::try_from_bytes(value.as_bytes())
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn nym_node_routing_address_can_be_converted_to_and_from_bytes_for_v4_address() {
let address = NymNodeRoutingAddress(SocketAddr::new(IpAddr::from([1, 2, 3, 4]), 42));
let address_bytes = address.as_bytes();
assert_eq!(
address,
NymNodeRoutingAddress::try_from_bytes(&address_bytes).unwrap()
)
}
#[test]
fn nym_node_routing_address_can_be_converted_to_and_from_bytes_for_v6_address() {
let address = NymNodeRoutingAddress(SocketAddr::new(
IpAddr::from([1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16]),
42,
));
let address_bytes = address.as_bytes();
assert_eq!(
address,
NymNodeRoutingAddress::try_from_bytes(&address_bytes).unwrap()
)
}
#[test]
fn nym_node_routing_address_can_be_converted_to_and_from_bytes_for_empty_v4_address() {
let address = NymNodeRoutingAddress(SocketAddr::new(IpAddr::from([0, 0, 0, 0]), 42));
let address_bytes = address.as_bytes();
assert_eq!(
address,
NymNodeRoutingAddress::try_from_bytes(&address_bytes).unwrap()
)
}
#[test]
fn nym_node_routing_address_can_be_converted_to_and_from_bytes_for_empty_v6_address() {
let address = NymNodeRoutingAddress(SocketAddr::new(
IpAddr::from([0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]),
42,
));
let address_bytes = address.as_bytes();
assert_eq!(
address,
NymNodeRoutingAddress::try_from_bytes(&address_bytes).unwrap()
)
}
#[test]
fn nym_node_routing_address_can_be_converted_to_and_from_node_address_bytes_with_no_data_loss()
{
let address_v4 = NymNodeRoutingAddress(SocketAddr::new(IpAddr::from([1, 2, 3, 4]), 42));
let address_v6 = NymNodeRoutingAddress(SocketAddr::new(
IpAddr::from([1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16]),
42,
));
let node_address1: NodeAddressBytes = address_v4.try_into().unwrap();
let node_address2: NodeAddressBytes = address_v6.try_into().unwrap();
assert_eq!(address_v4, node_address1.try_into().unwrap());
assert_eq!(address_v6, node_address2.try_into().unwrap());
}
}
-64
View File
@@ -1,9 +1,4 @@
use crate::chunking::set::split_into_sets;
use addressing::AddressTypeError;
use sphinx::route::{Destination, Node};
use std::net::SocketAddr;
use std::time;
use topology::{NymTopology, NymTopologyError};
pub mod fragment;
pub mod reconstruction;
@@ -48,25 +43,6 @@ pub enum ChunkingError {
UnexpectedFragmentCount,
}
impl From<topology::NymTopologyError> for ChunkingError {
fn from(_: NymTopologyError) -> Self {
use ChunkingError::*;
NoValidRoutesAvailableError
}
}
// this will later be completely removed when `addressing` crate is moved into this crate
impl From<AddressTypeError> for ChunkingError {
fn from(_: AddressTypeError) -> Self {
use ChunkingError::*;
InvalidTopologyError
}
}
// the user of this library can either prepare payloads for sphinx packets that he needs to
// encapsulate themselves by creating [sphinx] headers.
// or alternatively provide network topology and get bytes ready to be sent over the network
/// Takes the entire message and splits it into bytes chunks that will fit into sphinx packets
/// directly. After receiving they can be combined using `reconstruction::MessageReconstructor`
/// to obtain the original message back.
@@ -78,43 +54,3 @@ pub fn split_and_prepare_payloads(message: &[u8]) -> Vec<Vec<u8>> {
.map(|fragment| fragment.into_bytes())
.collect()
}
// note that for very long messages, this function will take a very long time to complete
// due to expensive sphinx wrapping operations
/// Takes the entire message and splits it into bytes chunks that are then encapsulated into
/// sphinx packets usingn provided network topology. The resultant bytes chunks can be sent
/// directly on the wire to the `SocketAddr` part of the tuple.
/// After being received back by a client they can be combined using `reconstruction::MessageReconstructor`
/// to obtain the original message back.
pub fn split_and_encapsulate_message<T: NymTopology>(
message: &[u8],
// TODO: in the future this will require also a specific provider of this particular recipient
recipient: Destination,
average_delay: time::Duration,
topology: &T,
) -> Result<Vec<(SocketAddr, Vec<u8>)>, ChunkingError> {
let ready_payloads = split_and_prepare_payloads(message);
let mut providers = topology.providers();
if providers.is_empty() {
return Err(ChunkingError::NoValidProvidersError);
}
let provider: Node = providers.pop().unwrap().into();
let mut encapsulated_sphinx_packets = Vec::new();
for message_fragment in ready_payloads {
let route = topology.route_to(provider.clone())?;
let delays =
sphinx::header::delays::generate_from_average_duration(route.len(), average_delay);
let packet =
sphinx::SphinxPacket::new(message_fragment, &route[..], &recipient, &delays).unwrap(); // this cannot fail unless there's an underlying bug which we must find and fix anyway
let first_node_address = addressing::socket_address_from_encoded_bytes(
route.first().unwrap().address.to_bytes(),
)?;
encapsulated_sphinx_packets.push((first_node_address, packet.to_bytes()));
}
Ok(encapsulated_sphinx_packets)
}
+1
View File
@@ -1,3 +1,4 @@
pub mod addressing;
pub mod chunking;
// Future consideration: currently in a lot of places, the payloads have randomised content
+1 -1
View File
@@ -15,7 +15,7 @@ rand = "0.7.2"
serde = { version = "1.0.104", features = ["derive"] }
## internal
addressing = {path = "../addressing"}
nymsphinx = {path = "../nymsphinx"}
version-checker = {path = "../version-checker" }
## will be moved to proper dependencies once released
+4 -3
View File
@@ -1,6 +1,7 @@
use crate::filter;
use nymsphinx::addressing::nodes::NymNodeRoutingAddress;
use sphinx::route::Node as SphinxNode;
use sphinx::route::NodeAddressBytes;
use std::convert::TryInto;
use std::net::SocketAddr;
#[derive(Debug, Clone)]
@@ -29,10 +30,10 @@ impl filter::Versioned for Node {
impl Into<SphinxNode> for Node {
fn into(self) -> SphinxNode {
let address_bytes = addressing::encoded_bytes_from_socket_address(self.host);
let node_address_bytes = NymNodeRoutingAddress::from(self.host).try_into().unwrap();
let key_bytes = self.get_pub_key_bytes();
let key = sphinx::key::new(key_bytes);
SphinxNode::new(NodeAddressBytes::from_bytes(address_bytes), key)
SphinxNode::new(node_address_bytes, key)
}
}
+6 -3
View File
@@ -1,6 +1,7 @@
use crate::filter;
use nymsphinx::addressing::nodes::NymNodeRoutingAddress;
use sphinx::route::Node as SphinxNode;
use sphinx::route::NodeAddressBytes;
use std::convert::TryInto;
use std::net::SocketAddr;
#[derive(Debug, Clone)]
@@ -35,10 +36,12 @@ impl filter::Versioned for Node {
impl Into<SphinxNode> for Node {
fn into(self) -> SphinxNode {
let address_bytes = addressing::encoded_bytes_from_socket_address(self.mixnet_listener);
let node_address_bytes = NymNodeRoutingAddress::from(self.mixnet_listener)
.try_into()
.unwrap();
let key_bytes = self.get_pub_key_bytes();
let key = sphinx::key::new(key_bytes);
SphinxNode::new(NodeAddressBytes::from_bytes(address_bytes), key)
SphinxNode::new(node_address_bytes, key)
}
}
+1 -1
View File
@@ -20,11 +20,11 @@ serde = { version = "1.0.104", features = ["derive"] }
tokio = { version = "0.2", features = ["full"] }
## internal
addressing = {path = "../common/addressing" }
config = {path = "../common/config"}
crypto = {path = "../common/crypto"}
directory-client = { path = "../common/clients/directory-client" }
multi-tcp-client = { path = "../common/clients/multi-tcp-client" }
nymsphinx = {path = "../common/nymsphinx" }
pemstore = {path = "../common/pemstore"}
topology = {path = "../common/topology"}
+5 -5
View File
@@ -1,10 +1,11 @@
use crate::node::metrics;
use addressing::AddressTypeError;
use crypto::encryption;
use log::*;
use nymsphinx::addressing::nodes::{NymNodeRoutingAddress, NymNodeRoutingAddressError};
use sphinx::header::delays::Delay as SphinxDelay;
use sphinx::route::NodeAddressBytes;
use sphinx::{ProcessedPacket, SphinxPacket};
use std::convert::TryFrom;
use std::net::SocketAddr;
use std::ops::Deref;
use std::sync::Arc;
@@ -32,8 +33,8 @@ impl From<sphinx::ProcessingError> for MixProcessingError {
}
}
impl From<addressing::AddressTypeError> for MixProcessingError {
fn from(_: AddressTypeError) -> Self {
impl From<NymNodeRoutingAddressError> for MixProcessingError {
fn from(_: NymNodeRoutingAddressError) -> Self {
use MixProcessingError::*;
InvalidHopAddress
@@ -68,8 +69,7 @@ impl PacketProcessor {
forward_address: NodeAddressBytes,
delay: SphinxDelay,
) -> Result<MixProcessingResult, MixProcessingError> {
let next_hop_address =
addressing::socket_address_from_encoded_bytes(forward_address.to_bytes())?;
let next_hop_address: SocketAddr = NymNodeRoutingAddress::try_from(forward_address)?.into();
// Delay packet for as long as required
tokio::time::delay_for(delay.to_duration()).await;
-1
View File
@@ -28,7 +28,6 @@ tokio = { version = "0.2", features = ["full"] }
tokio-tungstenite = "0.10.1"
## internal
addressing = {path = "../common/addressing" }
config = {path = "../common/config"}
crypto = {path = "../common/crypto"}
directory-client = { path = "../common/clients/directory-client" }