private route work
This commit is contained in:
parent
338dc6b39d
commit
f7f166741b
@ -132,8 +132,11 @@ struct RouteHopData {
|
|||||||
}
|
}
|
||||||
|
|
||||||
struct RouteHop {
|
struct RouteHop {
|
||||||
dialInfo @0 :NodeDialInfo; # dial info for this hop
|
node :union {
|
||||||
nextHop @1 :RouteHopData; # Optional: next hop in encrypted blob
|
nodeId @0 :NodeID; # node id only for established routes
|
||||||
|
peerInfo @1 :PeerInfo; # full peer info for this hop to establish the route
|
||||||
|
}
|
||||||
|
nextHop @2 :RouteHopData; # Optional: next hop in encrypted blob
|
||||||
# Null means no next hop, at destination (only used in private route, safety routes must enclose a stub private route)
|
# Null means no next hop, at destination (only used in private route, safety routes must enclose a stub private route)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -102,48 +102,77 @@ impl TryFrom<String> for AttachmentState {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub struct AttachmentManagerInner {
|
pub struct AttachmentManagerInner {
|
||||||
config: VeilidConfig,
|
|
||||||
attachment_machine: CallbackStateMachine<Attachment>,
|
attachment_machine: CallbackStateMachine<Attachment>,
|
||||||
network_manager: NetworkManager,
|
|
||||||
maintain_peers: bool,
|
maintain_peers: bool,
|
||||||
attach_timestamp: Option<u64>,
|
attach_timestamp: Option<u64>,
|
||||||
update_callback: Option<UpdateCallback>,
|
update_callback: Option<UpdateCallback>,
|
||||||
attachment_maintainer_jh: Option<MustJoinHandle<()>>,
|
attachment_maintainer_jh: Option<MustJoinHandle<()>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub struct AttachmentManagerUnlockedInner {
|
||||||
|
config: VeilidConfig,
|
||||||
|
network_manager: NetworkManager,
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
pub struct AttachmentManager {
|
pub struct AttachmentManager {
|
||||||
inner: Arc<Mutex<AttachmentManagerInner>>,
|
inner: Arc<Mutex<AttachmentManagerInner>>,
|
||||||
|
unlocked_inner: Arc<AttachmentManagerUnlockedInner>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl AttachmentManager {
|
impl AttachmentManager {
|
||||||
fn new_inner(
|
fn new_unlocked_inner(
|
||||||
config: VeilidConfig,
|
config: VeilidConfig,
|
||||||
|
protected_store: ProtectedStore,
|
||||||
table_store: TableStore,
|
table_store: TableStore,
|
||||||
|
block_store: BlockStore,
|
||||||
crypto: Crypto,
|
crypto: Crypto,
|
||||||
) -> AttachmentManagerInner {
|
) -> AttachmentManagerUnlockedInner {
|
||||||
AttachmentManagerInner {
|
AttachmentManagerUnlockedInner {
|
||||||
config: config.clone(),
|
config: config.clone(),
|
||||||
|
network_manager: NetworkManager::new(
|
||||||
|
config,
|
||||||
|
protected_store,
|
||||||
|
table_store,
|
||||||
|
block_store,
|
||||||
|
crypto,
|
||||||
|
),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
fn new_inner() -> AttachmentManagerInner {
|
||||||
|
AttachmentManagerInner {
|
||||||
attachment_machine: CallbackStateMachine::new(),
|
attachment_machine: CallbackStateMachine::new(),
|
||||||
network_manager: NetworkManager::new(config, table_store, crypto),
|
|
||||||
maintain_peers: false,
|
maintain_peers: false,
|
||||||
attach_timestamp: None,
|
attach_timestamp: None,
|
||||||
update_callback: None,
|
update_callback: None,
|
||||||
attachment_maintainer_jh: None,
|
attachment_maintainer_jh: None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
pub fn new(config: VeilidConfig, table_store: TableStore, crypto: Crypto) -> Self {
|
pub fn new(
|
||||||
|
config: VeilidConfig,
|
||||||
|
protected_store: ProtectedStore,
|
||||||
|
table_store: TableStore,
|
||||||
|
block_store: BlockStore,
|
||||||
|
crypto: Crypto,
|
||||||
|
) -> Self {
|
||||||
Self {
|
Self {
|
||||||
inner: Arc::new(Mutex::new(Self::new_inner(config, table_store, crypto))),
|
inner: Arc::new(Mutex::new(Self::new_inner())),
|
||||||
|
unlocked_inner: Arc::new(Self::new_unlocked_inner(
|
||||||
|
config,
|
||||||
|
protected_store,
|
||||||
|
table_store,
|
||||||
|
block_store,
|
||||||
|
crypto,
|
||||||
|
)),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn config(&self) -> VeilidConfig {
|
pub fn config(&self) -> VeilidConfig {
|
||||||
self.inner.lock().config.clone()
|
self.unlocked_inner.config.clone()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn network_manager(&self) -> NetworkManager {
|
pub fn network_manager(&self) -> NetworkManager {
|
||||||
self.inner.lock().network_manager.clone()
|
self.unlocked_inner.network_manager.clone()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn is_attached(&self) -> bool {
|
pub fn is_attached(&self) -> bool {
|
||||||
@ -202,9 +231,10 @@ impl AttachmentManager {
|
|||||||
AttachmentManager::translate_attachment_state(&inner.attachment_machine.state());
|
AttachmentManager::translate_attachment_state(&inner.attachment_machine.state());
|
||||||
|
|
||||||
// get reliable peer count from routing table
|
// get reliable peer count from routing table
|
||||||
let routing_table = inner.network_manager.routing_table();
|
let routing_table = self.network_manager().routing_table();
|
||||||
let health = routing_table.get_routing_table_health();
|
let health = routing_table.get_routing_table_health();
|
||||||
let routing_table_config = &inner.config.get().network.routing_table;
|
let config = self.config();
|
||||||
|
let routing_table_config = &config.get().network.routing_table;
|
||||||
|
|
||||||
let new_peer_state_input =
|
let new_peer_state_input =
|
||||||
AttachmentManager::translate_routing_table_health(health, routing_table_config);
|
AttachmentManager::translate_routing_table_health(health, routing_table_config);
|
||||||
@ -223,11 +253,8 @@ impl AttachmentManager {
|
|||||||
#[instrument(level = "debug", skip(self))]
|
#[instrument(level = "debug", skip(self))]
|
||||||
async fn attachment_maintainer(self) {
|
async fn attachment_maintainer(self) {
|
||||||
debug!("attachment starting");
|
debug!("attachment starting");
|
||||||
let netman = {
|
self.inner.lock().attach_timestamp = Some(intf::get_timestamp());
|
||||||
let mut inner = self.inner.lock();
|
let netman = self.network_manager();
|
||||||
inner.attach_timestamp = Some(intf::get_timestamp());
|
|
||||||
inner.network_manager.clone()
|
|
||||||
};
|
|
||||||
|
|
||||||
let mut restart;
|
let mut restart;
|
||||||
loop {
|
loop {
|
||||||
@ -286,7 +313,7 @@ impl AttachmentManager {
|
|||||||
#[instrument(level = "debug", skip_all, err)]
|
#[instrument(level = "debug", skip_all, err)]
|
||||||
pub async fn init(&self, update_callback: UpdateCallback) -> EyreResult<()> {
|
pub async fn init(&self, update_callback: UpdateCallback) -> EyreResult<()> {
|
||||||
trace!("init");
|
trace!("init");
|
||||||
let network_manager = {
|
{
|
||||||
let mut inner = self.inner.lock();
|
let mut inner = self.inner.lock();
|
||||||
inner.update_callback = Some(update_callback.clone());
|
inner.update_callback = Some(update_callback.clone());
|
||||||
let update_callback2 = update_callback.clone();
|
let update_callback2 = update_callback.clone();
|
||||||
@ -297,10 +324,9 @@ impl AttachmentManager {
|
|||||||
}))
|
}))
|
||||||
},
|
},
|
||||||
));
|
));
|
||||||
inner.network_manager.clone()
|
|
||||||
};
|
};
|
||||||
|
|
||||||
network_manager.init(update_callback).await?;
|
self.network_manager().init(update_callback).await?;
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
@ -309,30 +335,33 @@ impl AttachmentManager {
|
|||||||
pub async fn terminate(&self) {
|
pub async fn terminate(&self) {
|
||||||
// Ensure we detached
|
// Ensure we detached
|
||||||
self.detach().await;
|
self.detach().await;
|
||||||
let network_manager = {
|
self.network_manager().terminate().await;
|
||||||
let inner = self.inner.lock();
|
self.inner.lock().update_callback = None;
|
||||||
inner.network_manager.clone()
|
|
||||||
};
|
|
||||||
network_manager.terminate().await;
|
|
||||||
let mut inner = self.inner.lock();
|
|
||||||
inner.update_callback = None;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[instrument(level = "trace", skip(self))]
|
#[instrument(level = "trace", skip(self))]
|
||||||
fn attach(&self) {
|
fn attach(&self) {
|
||||||
// Create long-running connection maintenance routine
|
// Create long-running connection maintenance routine
|
||||||
let this = self.clone();
|
let inner = self.inner.lock();
|
||||||
self.inner.lock().maintain_peers = true;
|
if inner.attachment_maintainer_jh.is_some() {
|
||||||
self.inner.lock().attachment_maintainer_jh =
|
return;
|
||||||
Some(intf::spawn(this.attachment_maintainer()));
|
}
|
||||||
|
inner.maintain_peers = true;
|
||||||
|
inner.attachment_maintainer_jh = Some(intf::spawn(self.clone().attachment_maintainer()));
|
||||||
}
|
}
|
||||||
|
|
||||||
#[instrument(level = "trace", skip(self))]
|
#[instrument(level = "trace", skip(self))]
|
||||||
async fn detach(&self) {
|
async fn detach(&self) {
|
||||||
let attachment_maintainer_jh = self.inner.lock().attachment_maintainer_jh.take();
|
let attachment_maintainer_jh = {
|
||||||
|
let mut inner = self.inner.lock();
|
||||||
|
let attachment_maintainer_jh = inner.attachment_maintainer_jh.take();
|
||||||
|
if attachment_maintainer_jh.is_some() {
|
||||||
|
// Terminate long-running connection maintenance routine
|
||||||
|
inner.maintain_peers = false;
|
||||||
|
}
|
||||||
|
attachment_maintainer_jh
|
||||||
|
};
|
||||||
if let Some(jh) = attachment_maintainer_jh {
|
if let Some(jh) = attachment_maintainer_jh {
|
||||||
// Terminate long-running connection maintenance routine
|
|
||||||
self.inner.lock().maintain_peers = false;
|
|
||||||
jh.await;
|
jh.await;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -103,7 +103,7 @@ impl ServicesContext {
|
|||||||
// Set up attachment manager
|
// Set up attachment manager
|
||||||
trace!("init attachment manager");
|
trace!("init attachment manager");
|
||||||
let update_callback = self.update_callback.clone();
|
let update_callback = self.update_callback.clone();
|
||||||
let attachment_manager = AttachmentManager::new(self.config.clone(), table_store, crypto);
|
let attachment_manager = AttachmentManager::new(self.config.clone(), protected_store, table_store, block_store, crypto);
|
||||||
if let Err(e) = attachment_manager.init(update_callback).await {
|
if let Err(e) = attachment_manager.init(update_callback).await {
|
||||||
self.shutdown().await;
|
self.shutdown().await;
|
||||||
return Err(e);
|
return Err(e);
|
||||||
|
@ -152,6 +152,12 @@ struct NetworkManagerInner {
|
|||||||
}
|
}
|
||||||
|
|
||||||
struct NetworkManagerUnlockedInner {
|
struct NetworkManagerUnlockedInner {
|
||||||
|
// Handles
|
||||||
|
config: VeilidConfig,
|
||||||
|
protected_store: ProtectedStore,
|
||||||
|
table_store: TableStore,
|
||||||
|
block_store: BlockStore,
|
||||||
|
crypto: Crypto,
|
||||||
// Accessors
|
// Accessors
|
||||||
routing_table: RwLock<Option<RoutingTable>>,
|
routing_table: RwLock<Option<RoutingTable>>,
|
||||||
components: RwLock<Option<NetworkComponents>>,
|
components: RwLock<Option<NetworkComponents>>,
|
||||||
@ -169,9 +175,6 @@ struct NetworkManagerUnlockedInner {
|
|||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
pub struct NetworkManager {
|
pub struct NetworkManager {
|
||||||
config: VeilidConfig,
|
|
||||||
table_store: TableStore,
|
|
||||||
crypto: Crypto,
|
|
||||||
inner: Arc<Mutex<NetworkManagerInner>>,
|
inner: Arc<Mutex<NetworkManagerInner>>,
|
||||||
unlocked_inner: Arc<NetworkManagerUnlockedInner>,
|
unlocked_inner: Arc<NetworkManagerUnlockedInner>,
|
||||||
}
|
}
|
||||||
@ -185,9 +188,20 @@ impl NetworkManager {
|
|||||||
public_address_inconsistencies_table: BTreeMap::new(),
|
public_address_inconsistencies_table: BTreeMap::new(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
fn new_unlocked_inner(config: VeilidConfig) -> NetworkManagerUnlockedInner {
|
fn new_unlocked_inner(
|
||||||
|
config: VeilidConfig,
|
||||||
|
protected_store: ProtectedStore,
|
||||||
|
table_store: TableStore,
|
||||||
|
block_store: BlockStore,
|
||||||
|
crypto: Crypto,
|
||||||
|
) -> NetworkManagerUnlockedInner {
|
||||||
let c = config.get();
|
let c = config.get();
|
||||||
NetworkManagerUnlockedInner {
|
NetworkManagerUnlockedInner {
|
||||||
|
config,
|
||||||
|
protected_store,
|
||||||
|
table_store,
|
||||||
|
block_store,
|
||||||
|
crypto,
|
||||||
routing_table: RwLock::new(None),
|
routing_table: RwLock::new(None),
|
||||||
components: RwLock::new(None),
|
components: RwLock::new(None),
|
||||||
update_callback: RwLock::new(None),
|
update_callback: RwLock::new(None),
|
||||||
@ -202,13 +216,22 @@ impl NetworkManager {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn new(config: VeilidConfig, table_store: TableStore, crypto: Crypto) -> Self {
|
pub fn new(
|
||||||
|
config: VeilidConfig,
|
||||||
|
protected_store: ProtectedStore,
|
||||||
|
table_store: TableStore,
|
||||||
|
block_store: BlockStore,
|
||||||
|
crypto: Crypto,
|
||||||
|
) -> Self {
|
||||||
let this = Self {
|
let this = Self {
|
||||||
config: config.clone(),
|
|
||||||
table_store,
|
|
||||||
crypto,
|
|
||||||
inner: Arc::new(Mutex::new(Self::new_inner())),
|
inner: Arc::new(Mutex::new(Self::new_inner())),
|
||||||
unlocked_inner: Arc::new(Self::new_unlocked_inner(config)),
|
unlocked_inner: Arc::new(Self::new_unlocked_inner(
|
||||||
|
config,
|
||||||
|
protected_store,
|
||||||
|
table_store,
|
||||||
|
block_store,
|
||||||
|
crypto,
|
||||||
|
)),
|
||||||
};
|
};
|
||||||
// Set rolling transfers tick task
|
// Set rolling transfers tick task
|
||||||
{
|
{
|
||||||
@ -323,13 +346,25 @@ impl NetworkManager {
|
|||||||
this
|
this
|
||||||
}
|
}
|
||||||
pub fn config(&self) -> VeilidConfig {
|
pub fn config(&self) -> VeilidConfig {
|
||||||
self.config.clone()
|
self.unlocked_inner.config.clone()
|
||||||
|
}
|
||||||
|
pub fn with_config<F, R>(&self, f: F) -> R
|
||||||
|
where
|
||||||
|
F: FnOnce(&VeilidConfigInner) -> R,
|
||||||
|
{
|
||||||
|
f(&*self.unlocked_inner.config.get())
|
||||||
|
}
|
||||||
|
pub fn protected_store(&self) -> ProtectedStore {
|
||||||
|
self.unlocked_inner.protected_store.clone()
|
||||||
}
|
}
|
||||||
pub fn table_store(&self) -> TableStore {
|
pub fn table_store(&self) -> TableStore {
|
||||||
self.table_store.clone()
|
self.unlocked_inner.table_store.clone()
|
||||||
|
}
|
||||||
|
pub fn block_store(&self) -> BlockStore {
|
||||||
|
self.unlocked_inner.block_store.clone()
|
||||||
}
|
}
|
||||||
pub fn crypto(&self) -> Crypto {
|
pub fn crypto(&self) -> Crypto {
|
||||||
self.crypto.clone()
|
self.unlocked_inner.crypto.clone()
|
||||||
}
|
}
|
||||||
pub fn routing_table(&self) -> RoutingTable {
|
pub fn routing_table(&self) -> RoutingTable {
|
||||||
self.unlocked_inner
|
self.unlocked_inner
|
||||||
@ -540,7 +575,7 @@ impl NetworkManager {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn purge_client_whitelist(&self) {
|
pub fn purge_client_whitelist(&self) {
|
||||||
let timeout_ms = self.config.get().network.client_whitelist_timeout_ms;
|
let timeout_ms = self.with_config(|c| c.network.client_whitelist_timeout_ms);
|
||||||
let mut inner = self.inner.lock();
|
let mut inner = self.inner.lock();
|
||||||
let cutoff_timestamp = intf::get_timestamp() - ((timeout_ms as u64) * 1000u64);
|
let cutoff_timestamp = intf::get_timestamp() - ((timeout_ms as u64) * 1000u64);
|
||||||
// Remove clients from the whitelist that haven't been since since our whitelist timeout
|
// Remove clients from the whitelist that haven't been since since our whitelist timeout
|
||||||
@ -576,10 +611,7 @@ impl NetworkManager {
|
|||||||
RoutingDomain::PublicInternet.into(),
|
RoutingDomain::PublicInternet.into(),
|
||||||
BucketEntryState::Unreliable,
|
BucketEntryState::Unreliable,
|
||||||
);
|
);
|
||||||
let min_peer_count = {
|
let min_peer_count = self.with_config(|c| c.network.dht.min_peer_count as usize);
|
||||||
let c = self.config.get();
|
|
||||||
c.network.dht.min_peer_count as usize
|
|
||||||
};
|
|
||||||
|
|
||||||
// If none, then add the bootstrap nodes to it
|
// If none, then add the bootstrap nodes to it
|
||||||
if live_public_internet_entry_count == 0 {
|
if live_public_internet_entry_count == 0 {
|
||||||
@ -857,7 +889,7 @@ impl NetworkManager {
|
|||||||
// Encode envelope
|
// Encode envelope
|
||||||
let envelope = Envelope::new(version, ts, nonce, node_id, dest_node_id);
|
let envelope = Envelope::new(version, ts, nonce, node_id, dest_node_id);
|
||||||
envelope
|
envelope
|
||||||
.to_encrypted_data(self.crypto.clone(), body.as_ref(), &node_id_secret)
|
.to_encrypted_data(self.crypto(), body.as_ref(), &node_id_secret)
|
||||||
.wrap_err("envelope failed to encode")
|
.wrap_err("envelope failed to encode")
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1095,6 +1127,11 @@ impl NetworkManager {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Get the contact method required for node A to reach node B
|
||||||
|
pub fn get_node_contact_method(node_a: &NodeInfo, node_b: &NodeInfo) -> ContactMethod {
|
||||||
|
unimplemented!();
|
||||||
|
}
|
||||||
|
|
||||||
// Send a reverse connection signal and wait for the return receipt over it
|
// Send a reverse connection signal and wait for the return receipt over it
|
||||||
// Then send the data across the new connection
|
// Then send the data across the new connection
|
||||||
// Only usable for PublicInternet routing domain
|
// Only usable for PublicInternet routing domain
|
||||||
@ -1106,8 +1143,13 @@ impl NetworkManager {
|
|||||||
data: Vec<u8>,
|
data: Vec<u8>,
|
||||||
) -> EyreResult<NetworkResult<ConnectionDescriptor>> {
|
) -> EyreResult<NetworkResult<ConnectionDescriptor>> {
|
||||||
// Build a return receipt for the signal
|
// Build a return receipt for the signal
|
||||||
let receipt_timeout =
|
let receipt_timeout = ms_to_us(
|
||||||
ms_to_us(self.config.get().network.reverse_connection_receipt_time_ms);
|
self.unlocked_inner
|
||||||
|
.config
|
||||||
|
.get()
|
||||||
|
.network
|
||||||
|
.reverse_connection_receipt_time_ms,
|
||||||
|
);
|
||||||
let (receipt, eventual_value) = self.generate_single_shot_receipt(receipt_timeout, [])?;
|
let (receipt, eventual_value) = self.generate_single_shot_receipt(receipt_timeout, [])?;
|
||||||
|
|
||||||
// Get our peer info
|
// Get our peer info
|
||||||
@ -1188,7 +1230,13 @@ impl NetworkManager {
|
|||||||
.unwrap_or_default());
|
.unwrap_or_default());
|
||||||
|
|
||||||
// Build a return receipt for the signal
|
// Build a return receipt for the signal
|
||||||
let receipt_timeout = ms_to_us(self.config.get().network.hole_punch_receipt_time_ms);
|
let receipt_timeout = ms_to_us(
|
||||||
|
self.unlocked_inner
|
||||||
|
.config
|
||||||
|
.get()
|
||||||
|
.network
|
||||||
|
.hole_punch_receipt_time_ms,
|
||||||
|
);
|
||||||
let (receipt, eventual_value) = self.generate_single_shot_receipt(receipt_timeout, [])?;
|
let (receipt, eventual_value) = self.generate_single_shot_receipt(receipt_timeout, [])?;
|
||||||
// Get our peer info
|
// Get our peer info
|
||||||
let peer_info = self
|
let peer_info = self
|
||||||
@ -1404,10 +1452,7 @@ impl NetworkManager {
|
|||||||
// Direct bootstrap request
|
// Direct bootstrap request
|
||||||
#[instrument(level = "trace", err, skip(self))]
|
#[instrument(level = "trace", err, skip(self))]
|
||||||
pub async fn boot_request(&self, dial_info: DialInfo) -> EyreResult<Vec<PeerInfo>> {
|
pub async fn boot_request(&self, dial_info: DialInfo) -> EyreResult<Vec<PeerInfo>> {
|
||||||
let timeout_ms = {
|
let timeout_ms = self.with_config(|c| c.network.rpc.timeout_ms);
|
||||||
let c = self.config.get();
|
|
||||||
c.network.rpc.timeout_ms
|
|
||||||
};
|
|
||||||
// Send boot magic to requested peer address
|
// Send boot magic to requested peer address
|
||||||
let data = BOOT_MAGIC.to_vec();
|
let data = BOOT_MAGIC.to_vec();
|
||||||
let out_data: Vec<u8> = network_result_value_or_log!(debug self
|
let out_data: Vec<u8> = network_result_value_or_log!(debug self
|
||||||
@ -1502,13 +1547,12 @@ impl NetworkManager {
|
|||||||
};
|
};
|
||||||
|
|
||||||
// Get timestamp range
|
// Get timestamp range
|
||||||
let (tsbehind, tsahead) = {
|
let (tsbehind, tsahead) = self.with_config(|c| {
|
||||||
let c = self.config.get();
|
|
||||||
(
|
(
|
||||||
c.network.rpc.max_timestamp_behind_ms.map(ms_to_us),
|
c.network.rpc.max_timestamp_behind_ms.map(ms_to_us),
|
||||||
c.network.rpc.max_timestamp_ahead_ms.map(ms_to_us),
|
c.network.rpc.max_timestamp_ahead_ms.map(ms_to_us),
|
||||||
)
|
)
|
||||||
};
|
});
|
||||||
|
|
||||||
// Validate timestamp isn't too old
|
// Validate timestamp isn't too old
|
||||||
let ts = intf::get_timestamp();
|
let ts = intf::get_timestamp();
|
||||||
@ -1742,11 +1786,14 @@ impl NetworkManager {
|
|||||||
}
|
}
|
||||||
|
|
||||||
let routing_table = self.routing_table();
|
let routing_table = self.routing_table();
|
||||||
let c = self.config.get();
|
let (detect_address_changes, ip6_prefix_size) = self.with_config(|c| {
|
||||||
let detect_address_changes = c.network.detect_address_changes;
|
(
|
||||||
|
c.network.detect_address_changes,
|
||||||
|
c.network.max_connections_per_ip6_prefix_size as usize,
|
||||||
|
)
|
||||||
|
});
|
||||||
|
|
||||||
// Get the ip(block) this report is coming from
|
// Get the ip(block) this report is coming from
|
||||||
let ip6_prefix_size = c.network.max_connections_per_ip6_prefix_size as usize;
|
|
||||||
let ipblock = ip_to_ipblock(
|
let ipblock = ip_to_ipblock(
|
||||||
ip6_prefix_size,
|
ip6_prefix_size,
|
||||||
connection_descriptor.remote_address().to_ip_addr(),
|
connection_descriptor.remote_address().to_ip_addr(),
|
||||||
|
@ -4,9 +4,14 @@ use serde::*;
|
|||||||
|
|
||||||
#[derive(Clone, Debug, Serialize, Deserialize)]
|
#[derive(Clone, Debug, Serialize, Deserialize)]
|
||||||
struct RouteSpecDetail {
|
struct RouteSpecDetail {
|
||||||
/// The actual route spec
|
/// Secret key
|
||||||
#[serde(with = "arc_serialize")]
|
#[serde(skip)]
|
||||||
route_spec: Arc<RouteSpec>,
|
secret_key: DHTKeySecret,
|
||||||
|
/// Route hops
|
||||||
|
hops: Vec<DHTKey>,
|
||||||
|
/// Route noderefs
|
||||||
|
#[serde(skip)]
|
||||||
|
hop_node_refs: Vec<NodeRef>,
|
||||||
/// Transfers up and down
|
/// Transfers up and down
|
||||||
transfer_stats_down_up: TransferStatsDownUp,
|
transfer_stats_down_up: TransferStatsDownUp,
|
||||||
/// Latency stats
|
/// Latency stats
|
||||||
@ -18,10 +23,15 @@ struct RouteSpecDetail {
|
|||||||
#[serde(skip)]
|
#[serde(skip)]
|
||||||
transfer_stats_accounting: TransferStatsAccounting,
|
transfer_stats_accounting: TransferStatsAccounting,
|
||||||
/// Published private route, do not reuse for ephemeral routes
|
/// Published private route, do not reuse for ephemeral routes
|
||||||
|
/// Not serialized because all routes should be re-published when restarting
|
||||||
#[serde(skip)]
|
#[serde(skip)]
|
||||||
published: bool,
|
published: bool,
|
||||||
/// Timestamp of when the route was created
|
/// Timestamp of when the route was created
|
||||||
timestamp: u64,
|
created_ts: u64,
|
||||||
|
/// Timestamp of when the route was last checked for validity
|
||||||
|
last_checked_ts: Option<u64>,
|
||||||
|
/// Directions this route is guaranteed to work in
|
||||||
|
directions: DirectionSet,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// The core representation of the RouteSpecStore that can be serialized
|
/// The core representation of the RouteSpecStore that can be serialized
|
||||||
@ -34,10 +44,6 @@ pub struct RouteSpecStoreContent {
|
|||||||
/// Ephemeral data used to help the RouteSpecStore operate efficiently
|
/// Ephemeral data used to help the RouteSpecStore operate efficiently
|
||||||
#[derive(Debug, Default)]
|
#[derive(Debug, Default)]
|
||||||
pub struct RouteSpecStoreCache {
|
pub struct RouteSpecStoreCache {
|
||||||
/// The fastest routes by latency
|
|
||||||
fastest_routes: Vec<DHTKey>,
|
|
||||||
/// The most reliable routes by node lifetime longevity
|
|
||||||
reliable_routes: Vec<DHTKey>,
|
|
||||||
/// How many times nodes have been used
|
/// How many times nodes have been used
|
||||||
used_nodes: HashMap<DHTKey, usize>,
|
used_nodes: HashMap<DHTKey, usize>,
|
||||||
/// How many times nodes have been used at the terminal point of a route
|
/// How many times nodes have been used at the terminal point of a route
|
||||||
@ -62,14 +68,76 @@ fn route_spec_to_hop_cache(spec: Arc<RouteSpec>) -> Vec<u8> {
|
|||||||
cache
|
cache
|
||||||
}
|
}
|
||||||
|
|
||||||
fn node_sublist_to_hop_cache(
|
/// number of route permutations is the number of unique orderings
|
||||||
nodes: &[(DHTKey, Arc<BucketEntry>)],
|
/// for a set of nodes, given that the first node is fixed
|
||||||
start: usize,
|
fn get_route_permutation_count(hop_count: usize) -> usize {
|
||||||
len: usize,
|
if hop_count == 0 {
|
||||||
) -> Vec<u8> {
|
unreachable!();
|
||||||
let mut cache: Vec<u8> = Vec::with_capacity(len * DHT_KEY_LENGTH);
|
}
|
||||||
for node in &nodes[start..start + len] {
|
// a single node or two nodes is always fixed
|
||||||
cache.extend_from_slice(&node.0.bytes)
|
if hop_count == 1 || hop_count == 2 {
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
// more than two nodes has factorial permutation
|
||||||
|
// hop_count = 3 -> 2! -> 2
|
||||||
|
// hop_count = 4 -> 3! -> 6
|
||||||
|
(3..hop_count).into_iter().fold(2usize, |acc, x| acc * x)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// get the route permutation at particular 'perm' index, starting at the 'start' index
|
||||||
|
/// for a set of 'hop_count' nodes. the first node is always fixed, and the maximum
|
||||||
|
/// number of permutations is given by get_route_permutation_count()
|
||||||
|
fn with_route_permutations<F>(hop_count: usize, start: usize, f: F) -> bool
|
||||||
|
where
|
||||||
|
F: FnMut(&[usize]) -> bool,
|
||||||
|
{
|
||||||
|
if hop_count == 0 {
|
||||||
|
unreachable!();
|
||||||
|
}
|
||||||
|
// initial permutation
|
||||||
|
let mut permutation: Vec<usize> = Vec::with_capacity(hop_count);
|
||||||
|
for n in 0..hop_count {
|
||||||
|
permutation[n] = start + n;
|
||||||
|
}
|
||||||
|
// if we have one hop or two, then there's only one permutation
|
||||||
|
if hop_count == 1 || hop_count == 2 {
|
||||||
|
return f(&permutation);
|
||||||
|
}
|
||||||
|
|
||||||
|
// heaps algorithm
|
||||||
|
fn heaps_permutation<F>(permutation: &mut [usize], size: usize, f: F) -> bool
|
||||||
|
where
|
||||||
|
F: FnMut(&[usize]) -> bool,
|
||||||
|
{
|
||||||
|
if size == 1 {
|
||||||
|
if f(&permutation) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
for i in 0..size {
|
||||||
|
if heaps_permutation(permutation, size - 1, f) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if size % 2 == 1 {
|
||||||
|
permutation.swap(1, size);
|
||||||
|
} else {
|
||||||
|
permutation.swap(1 + i, size);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
false
|
||||||
|
}
|
||||||
|
|
||||||
|
// recurse
|
||||||
|
heaps_permutation(&mut permutation, hop_count - 1, f)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// get the hop cache key for a particular route permutation
|
||||||
|
fn route_permutation_to_hop_cache(nodes: &[(DHTKey, NodeInfo)], perm: &[usize]) -> Vec<u8> {
|
||||||
|
let mut cache: Vec<u8> = Vec::with_capacity(perm.len() * DHT_KEY_LENGTH);
|
||||||
|
for n in perm {
|
||||||
|
cache.extend_from_slice(&nodes[*n].0.bytes)
|
||||||
}
|
}
|
||||||
cache
|
cache
|
||||||
}
|
}
|
||||||
@ -84,30 +152,33 @@ impl RouteSpecStore {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn from_cbor(
|
pub fn load(routing_table: RoutingTable) -> Result<RouteSpecStore, VeilidAPIError> {
|
||||||
routing_table: RoutingTable,
|
// Get cbor blob from table store
|
||||||
cbor: &[u8],
|
|
||||||
) -> Result<RouteSpecStore, VeilidAPIError> {
|
|
||||||
let content: RouteSpecStoreContent = serde_cbor::from_slice(cbor)
|
let content: RouteSpecStoreContent = serde_cbor::from_slice(cbor)
|
||||||
.map_err(|e| VeilidAPIError::parse_error("invalid route spec store content", e))?;
|
.map_err(|e| VeilidAPIError::parse_error("invalid route spec store content", e))?;
|
||||||
let rss = RouteSpecStore {
|
let rss = RouteSpecStore {
|
||||||
content,
|
content,
|
||||||
cache: Default::default(),
|
cache: Default::default(),
|
||||||
};
|
};
|
||||||
rss.rebuild_cache();
|
rss.rebuild_cache(routing_table);
|
||||||
Ok(rss)
|
Ok(rss)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn to_cbor(&self) -> Vec<u8> {
|
pub fn save(&self, routing_table: RoutingTable) -> Result<(), VeilidAPIError> {
|
||||||
serde_cbor::to_vec(&self.content).unwrap()
|
// Save all the fields we care about to the cbor blob in table storage
|
||||||
|
let cbor = serde_cbor::to_vec(&self.content).unwrap();
|
||||||
|
let table_store = routing_table.network_manager().table_store();
|
||||||
|
table_store.open("")
|
||||||
}
|
}
|
||||||
|
|
||||||
fn rebuild_cache(&mut self) {
|
fn rebuild_cache(&mut self, routing_table: RoutingTable) {
|
||||||
//
|
//
|
||||||
|
// xxx also load secrets from pstore
|
||||||
|
let pstore = routing_table.network_manager().protected_store();
|
||||||
}
|
}
|
||||||
|
|
||||||
fn detail_mut(&mut self, spec: Arc<RouteSpec>) -> &mut RouteSpecDetail {
|
fn detail_mut(&mut self, public_key: DHTKey) -> &mut RouteSpecDetail {
|
||||||
self.content.details.get_mut(&spec.public_key).unwrap()
|
self.content.details.get_mut(&public_key).unwrap()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Create a new route
|
/// Create a new route
|
||||||
@ -119,7 +190,8 @@ impl RouteSpecStore {
|
|||||||
routing_table: RoutingTable,
|
routing_table: RoutingTable,
|
||||||
reliable: bool,
|
reliable: bool,
|
||||||
hop_count: usize,
|
hop_count: usize,
|
||||||
) -> Option<Arc<RouteSpec>> {
|
directions: DirectionSet,
|
||||||
|
) -> Option<DHTKey> {
|
||||||
use core::cmp::Ordering;
|
use core::cmp::Ordering;
|
||||||
|
|
||||||
let max_route_hop_count = {
|
let max_route_hop_count = {
|
||||||
@ -257,51 +329,101 @@ impl RouteSpecStore {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Now go through nodes and try to build a route we haven't seen yet
|
// Now go through nodes and try to build a route we haven't seen yet
|
||||||
let mut route_nodes = None;
|
let mut route_nodes: Vec<usize> = Vec::with_capacity(hop_count);
|
||||||
for start in 0..(nodes.len() - hop_count) {
|
for start in 0..(nodes.len() - hop_count) {
|
||||||
// Get the route cache key
|
// Try the permutations available starting with 'start'
|
||||||
let key = node_sublist_to_hop_cache(&nodes, start, hop_count);
|
let done = with_route_permutations(hop_count, start, |permutation: &[usize]| {
|
||||||
|
// Get the route cache key
|
||||||
|
let key = route_permutation_to_hop_cache(&nodes, permutation);
|
||||||
|
|
||||||
// try each route until we find a unique one
|
// Skip routes we have already seen
|
||||||
if !self.cache.hop_cache.contains(&key) {
|
if self.cache.hop_cache.contains(&key) {
|
||||||
route_nodes = Some(&nodes[start..start + hop_count]);
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Ensure this route is viable by checking that each node can contact the next one
|
||||||
|
if directions.contains(Direction::Outbound) {
|
||||||
|
let our_node_info =
|
||||||
|
routing_table.get_own_node_info(RoutingDomain::PublicInternet);
|
||||||
|
let mut previous_node_info = &our_node_info;
|
||||||
|
let mut reachable = true;
|
||||||
|
for n in permutation {
|
||||||
|
let current_node_info = &nodes.get(*n).as_ref().unwrap().1;
|
||||||
|
let cm = NetworkManager::get_node_contact_method(
|
||||||
|
previous_node_info,
|
||||||
|
current_node_info,
|
||||||
|
);
|
||||||
|
if matches!(cm, ContactMethod::Unreachable) {
|
||||||
|
reachable = false;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
previous_node_info = current_node_info;
|
||||||
|
}
|
||||||
|
if !reachable {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if directions.contains(Direction::Inbound) {
|
||||||
|
let our_node_info =
|
||||||
|
routing_table.get_own_node_info(RoutingDomain::PublicInternet);
|
||||||
|
let mut next_node_info = &our_node_info;
|
||||||
|
let mut reachable = true;
|
||||||
|
for n in permutation.iter().rev() {
|
||||||
|
let current_node_info = &nodes.get(*n).as_ref().unwrap().1;
|
||||||
|
let cm = NetworkManager::get_node_contact_method(
|
||||||
|
current_node_info,
|
||||||
|
next_node_info,
|
||||||
|
);
|
||||||
|
if matches!(cm, ContactMethod::Unreachable) {
|
||||||
|
reachable = false;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
next_node_info = current_node_info;
|
||||||
|
}
|
||||||
|
if !reachable {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Keep this route
|
||||||
|
route_nodes = permutation.to_vec();
|
||||||
|
true
|
||||||
|
});
|
||||||
|
if done {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if route_nodes.is_none() {
|
if route_nodes.is_empty() {
|
||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
let route_node = route_nodes.unwrap();
|
|
||||||
|
|
||||||
// Got a unique route, lets build the detail, register it, and return it
|
// Got a unique route, lets build the detail, register it, and return it
|
||||||
let hops: Vec<RouteHopSpec> = route_node
|
let hops = route_nodes.iter().map(|v| nodes[*v].0).collect();
|
||||||
.into_iter()
|
let hop_node_refs = route_nodes
|
||||||
.map(|v| RouteHopSpec {
|
.iter()
|
||||||
dial_info: NodeDialInfo {
|
.map(|v| routing_table.lookup_node_ref(nodes[*v].0).unwrap())
|
||||||
node_id: NodeId::new(v.0),
|
|
||||||
dial_info: xxx,
|
|
||||||
},
|
|
||||||
})
|
|
||||||
.collect();
|
.collect();
|
||||||
|
|
||||||
let (public_key, secret_key) = generate_secret();
|
let (public_key, secret_key) = generate_secret();
|
||||||
let route_spec = Arc::new(RouteSpec {
|
|
||||||
public_key,
|
|
||||||
secret_key,
|
|
||||||
hops,
|
|
||||||
});
|
|
||||||
|
|
||||||
let rsd = RouteSpecDetail {
|
let rsd = RouteSpecDetail {
|
||||||
route_spec,
|
secret_key,
|
||||||
|
hops,
|
||||||
|
hop_node_refs,
|
||||||
transfer_stats_down_up: Default::default(),
|
transfer_stats_down_up: Default::default(),
|
||||||
latency_stats: Default::default(),
|
latency_stats: Default::default(),
|
||||||
latency_stats_accounting: Default::default(),
|
latency_stats_accounting: Default::default(),
|
||||||
transfer_stats_accounting: Default::default(),
|
transfer_stats_accounting: Default::default(),
|
||||||
published: false,
|
published: false,
|
||||||
timestamp: cur_ts,
|
created_ts: cur_ts,
|
||||||
|
last_checked_ts: None,
|
||||||
|
directions,
|
||||||
};
|
};
|
||||||
|
|
||||||
None
|
self.content.details.insert(public_key, rsd);
|
||||||
|
|
||||||
|
// xxx insert into cache too
|
||||||
|
|
||||||
|
Some(public_key)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn release_route(&mut self, spec: Arc<RouteSpec>) {}
|
pub fn release_route(&mut self, spec: Arc<RouteSpec>) {}
|
||||||
@ -311,15 +433,22 @@ impl RouteSpecStore {
|
|||||||
/// Mark route as published
|
/// Mark route as published
|
||||||
/// When first deserialized, routes must be re-published in order to ensure they remain
|
/// When first deserialized, routes must be re-published in order to ensure they remain
|
||||||
/// in the RouteSpecStore.
|
/// in the RouteSpecStore.
|
||||||
pub fn publish_route(&mut self, spec: Arc<RouteSpec>) {
|
pub fn mark_route_published(&mut self, spec: Arc<RouteSpec>) {
|
||||||
//compile private route here?
|
self.detail_mut(spec).published = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn record_latency(
|
/// Mark route as checked
|
||||||
&mut self,
|
pub fn touch_route_checked(&mut self, spec: Arc<RouteSpec>, cur_ts: u64) {
|
||||||
spec: Arc<RouteSpec>,
|
self.detail_mut(spec).last_checked_ts = cur_ts;
|
||||||
latency: u64,
|
}
|
||||||
) -> veilid_api::LatencyStats {
|
|
||||||
|
pub fn record_latency(&mut self, spec: Arc<RouteSpec>, latency: u64) {
|
||||||
|
let lsa = self.detail_mut(spec).latency_stats_accounting;
|
||||||
|
self.detail_mut(spec).latency_stats = lsa.record_latency(latency);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn latency_stats(&self, spec: Arc<RouteSpec>) -> LatencyStats {
|
||||||
|
self.detail_mut(spec).latency_stats.clone()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn add_down(&mut self, spec: Arc<RouteSpec>, bytes: u64) {
|
pub fn add_down(&mut self, spec: Arc<RouteSpec>, bytes: u64) {
|
||||||
|
@ -645,6 +645,14 @@ impl NodeInfo {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[allow(clippy::derive_hash_xor_eq)]
|
||||||
|
#[derive(Debug, PartialOrd, Ord, Hash, Serialize, Deserialize, EnumSetType)]
|
||||||
|
pub enum Direction {
|
||||||
|
Inbound,
|
||||||
|
Outbound,
|
||||||
|
}
|
||||||
|
pub type DirectionSet = EnumSet<Direction>;
|
||||||
|
|
||||||
#[allow(clippy::derive_hash_xor_eq)]
|
#[allow(clippy::derive_hash_xor_eq)]
|
||||||
#[derive(Debug, PartialOrd, Ord, Hash, Serialize, Deserialize, EnumSetType)]
|
#[derive(Debug, PartialOrd, Ord, Hash, Serialize, Deserialize, EnumSetType)]
|
||||||
// Keep member order appropriate for sorting < preference
|
// Keep member order appropriate for sorting < preference
|
||||||
|
@ -1,32 +1,5 @@
|
|||||||
use super::*;
|
use super::*;
|
||||||
|
|
||||||
/////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
||||||
// Privacy Specs
|
|
||||||
|
|
||||||
#[derive(Clone, Debug, Default, Serialize, Deserialize)]
|
|
||||||
pub struct RouteHopSpec {
|
|
||||||
pub dial_info: NodeDialInfo,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Clone, Debug, Default, Serialize, Deserialize)]
|
|
||||||
pub struct RouteSpec {
|
|
||||||
//
|
|
||||||
pub public_key: DHTKey,
|
|
||||||
pub secret_key: DHTKeySecret,
|
|
||||||
pub hops: Vec<RouteHopSpec>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl RouteSpec {
|
|
||||||
pub fn new() -> Self {
|
|
||||||
let (pk, sk) = generate_secret();
|
|
||||||
RouteSpec {
|
|
||||||
public_key: pk,
|
|
||||||
secret_key: sk,
|
|
||||||
hops: Vec::new(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
// Compiled Privacy Objects
|
// Compiled Privacy Objects
|
||||||
|
|
||||||
@ -36,9 +9,27 @@ pub struct RouteHopData {
|
|||||||
pub blob: Vec<u8>,
|
pub blob: Vec<u8>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug)]
|
||||||
|
pub enum RouteNode {
|
||||||
|
NodeId(DHTKey),
|
||||||
|
PeerInfo(PeerInfo),
|
||||||
|
}
|
||||||
|
impl fmt::Display for RouteNode {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||||
|
write!(
|
||||||
|
f,
|
||||||
|
"{}",
|
||||||
|
match self {
|
||||||
|
RouteNode::NodeId(x) => x.encode(),
|
||||||
|
RouteNode::PeerInfo(pi) => pi.node_id.key.encode(),
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Debug)]
|
||||||
pub struct RouteHop {
|
pub struct RouteHop {
|
||||||
pub dial_info: NodeDialInfo,
|
pub node: RouteNode,
|
||||||
pub next_hop: Option<RouteHopData>,
|
pub next_hop: Option<RouteHopData>,
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -46,7 +37,7 @@ pub struct RouteHop {
|
|||||||
pub struct PrivateRoute {
|
pub struct PrivateRoute {
|
||||||
pub public_key: DHTKey,
|
pub public_key: DHTKey,
|
||||||
pub hop_count: u8,
|
pub hop_count: u8,
|
||||||
pub hops: Option<RouteHop>,
|
pub first_hop: Option<RouteHop>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl PrivateRoute {
|
impl PrivateRoute {
|
||||||
@ -54,7 +45,7 @@ impl PrivateRoute {
|
|||||||
Self {
|
Self {
|
||||||
public_key,
|
public_key,
|
||||||
hop_count: 0,
|
hop_count: 0,
|
||||||
hops: None,
|
first_hop: None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -66,8 +57,8 @@ impl fmt::Display for PrivateRoute {
|
|||||||
"PR({:?}+{}{})",
|
"PR({:?}+{}{})",
|
||||||
self.public_key,
|
self.public_key,
|
||||||
self.hop_count,
|
self.hop_count,
|
||||||
if let Some(hops) = &self.hops {
|
if let Some(first_hop) = &self.first_hop {
|
||||||
format!("->{}", hops.dial_info)
|
format!("->{}", first_hop.node)
|
||||||
} else {
|
} else {
|
||||||
"".to_owned()
|
"".to_owned()
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user