[ENG-732] Pairing by library (#943)

* library keypair

* allow opening stream to non-connected node + more

* library identity

* fix types

* fix maybe undefined

* don't forgor migrations file

* library manager inside p2p manager

* rename

* `NodeInformation` struct

* node info exchange

* fill in info

* streamify tunnel

* use tunnel for p2p events

* libp2p is annoying + stop leaking private key's

* Clippy cleanup
This commit is contained in:
Oscar Beaumont authored and GitHub committed 2023-06-19 05:13:30 +00:00
1 parent 6975ebb664
commit 0f7a669e5a
44 files changed
+1355 -468

No files matched your search

+20 -2
View File
@@ -15,7 +15,7 @@ use crate::util::migrator::{Migrate, MigratorError};
pub const NODE_STATE_CONFIG_NAME: &str = "node_state.sdconfig";
/// NodeConfig is the configuration for a node. This is shared between all libraries and is stored in a JSON file on disk.
#[derive(Debug, Serialize, Deserialize, Clone, Type)]
#[derive(Debug, Serialize, Deserialize, Clone)] // If you are adding `specta::Type` on this your probably about to leak the P2P private key
pub struct NodeConfig {
/// id is a unique identifier for the current node. Each node has a public identifier (this one) and is given a local id for each library (done within the library code).
pub id: Uuid,
@@ -24,7 +24,7 @@ pub struct NodeConfig {
// the port this node uses for peer to peer communication. By default a random free port will be chosen each time the application is started.
pub p2p_port: Option<u32>,
/// The p2p identity keypair for this node. This is used to identify the node on the network.
#[specta(skip)]
/// This keypair does effectively nothing except for provide libp2p with a stable peer_id.
pub keypair: Keypair,
// TODO: These will probs be replaced by your Spacedrive account in the near future.
pub p2p_email: Option<String>,
@@ -63,6 +63,24 @@ impl Migrate for NodeConfig {
type Ctx = ();
fn default(_path: PathBuf) -> Result<Self, MigratorError> {
Ok(Self {
id: Uuid::new_v4(),
name: match hostname::get() {
// SAFETY: This is just for display purposes so it doesn't matter if it's lossy
Ok(hostname) => hostname.to_string_lossy().into_owned(),
Err(err) => {
eprintln!("Falling back to default node name as an error occurred getting your systems hostname: '{err}'");
"my-spacedrive".into()
}
},
p2p_port: None,
keypair: Keypair::generate(),
p2p_email: None,
p2p_img_url: None,
})
}
async fn migrate(
from_version: u32,
_config: &mut Map<String, Value>,
+26 -26
View File
@@ -1,35 +1,13 @@
use crate::{prisma::node, NodeError};
use crate::NodeError;
use serde::{Deserialize, Serialize};
use specta::Type;
use uuid::Uuid;
mod config;
pub mod peer_request;
pub use config::*;
#[derive(Debug, Clone, Serialize, Deserialize, Type)]
pub struct LibraryNode {
pub uuid: Uuid,
pub name: String,
pub platform: Platform,
}
impl TryFrom<node::Data> for LibraryNode {
type Error = String;
fn try_from(data: node::Data) -> Result<Self, Self::Error> {
Ok(Self {
uuid: Uuid::from_slice(&data.pub_id).map_err(|_| "Invalid node pub_id")?,
name: data.name,
platform: Platform::try_from(data.platform).map_err(|_| "Invalid platform_id")?,
})
}
}
#[allow(clippy::upper_case_acronyms)]
#[repr(i32)]
#[repr(u8)]
#[derive(Debug, Clone, Copy, Serialize, Deserialize, Type, Eq, PartialEq)]
pub enum Platform {
Unknown = 0,
@@ -40,10 +18,32 @@ pub enum Platform {
Android = 5,
}
impl TryFrom<i32> for Platform {
impl Platform {
#[allow(unreachable_code)]
pub fn current() -> Self {
#[cfg(target_os = "windows")]
return Self::Windows;
#[cfg(target_os = "macos")]
return Self::MacOS;
#[cfg(target_os = "linux")]
return Self::Linux;
#[cfg(target_os = "ios")]
return Self::IOS;
#[cfg(target_os = "android")]
return Self::Android;
Self::Unknown
}
}
impl TryFrom<u8> for Platform {
type Error = NodeError;
fn try_from(value: i32) -> Result<Self, Self::Error> {
fn try_from(value: u8) -> Result<Self, Self::Error> {
let s = match value {
0 => Self::Unknown,
1 => Self::Windows,
-154
View File
@@ -1,154 +0,0 @@
#![allow(dead_code, unused_variables, clippy::panic, clippy::unwrap_used)] // TODO: Reenable once this is working
use serde::{Deserialize, Serialize};
use specta::Type;
use tokio::sync::{mpsc, oneshot};
pub enum PeerRequest {
Guest(guest::PeerRequest),
Host(host::PeerRequest),
}
enum PlaceholderP2PAction {
SubmitPeeringPassword {
peer_id: String,
password: String,
tx: oneshot::Sender<bool>,
},
}
pub mod guest {
use super::*;
#[derive(Type, Deserialize)]
pub enum Action {
PromptPassword,
ProcessPassword { password: String },
}
#[derive(Type, Serialize, Clone)]
pub enum State {
Start,
AwaitingPassword { prev_invalid: bool },
AwaitingConfirmation,
ChallengeSuccess,
}
pub struct PeerRequest {
pub tx: mpsc::Sender<Action>,
pub peer_id: String,
}
struct ActorArgs {
peer_id: String,
p2p: mpsc::Sender<PlaceholderP2PAction>,
}
async fn loop_until<T, R>(rx: &mut mpsc::Receiver<T>, func: impl Fn(T) -> Option<R>) -> R {
loop {
let Some(msg) = rx.recv().await else {
panic!()
};
if let Some(d) = func(msg) {
break d;
}
}
}
impl PeerRequest {
pub fn new_actor(peer_id: String) -> (Self, mpsc::Receiver<State>) {
let (itx, irx) = mpsc::channel(8);
let (otx, orx) = mpsc::channel(8);
let (p2ptx, _) = mpsc::channel(8);
tokio::spawn(Self::actor(
otx,
irx,
ActorArgs {
peer_id: peer_id.clone(),
p2p: p2ptx,
},
));
(Self { tx: itx, peer_id }, orx)
}
async fn actor(
state_tx: mpsc::Sender<State>,
mut action_rx: mpsc::Receiver<Action>,
ActorArgs { peer_id, p2p }: ActorArgs,
) {
let send_state = |state| async { state_tx.send(state).await.ok() };
send_state(State::Start).await;
loop_until(&mut action_rx, |msg| {
matches!(Action::PromptPassword, msg).then_some(())
})
.await;
send_state(State::AwaitingPassword {
prev_invalid: false,
})
.await;
loop {
let password = loop_until(&mut action_rx, |msg| match msg {
Action::ProcessPassword { password } => Some(password),
_ => None,
})
.await;
let (tx, rx) = oneshot::channel();
p2p.send(PlaceholderP2PAction::SubmitPeeringPassword {
peer_id: peer_id.clone(),
password,
tx,
})
.await
.ok();
if rx.await.unwrap() {
break;
}
send_state(State::AwaitingPassword { prev_invalid: true }).await;
}
send_state(State::ChallengeSuccess).await;
}
pub async fn submit_password(&self, password: String) {
self.tx
.send(Action::ProcessPassword { password })
.await
.ok();
}
}
}
pub mod host {
use super::*;
#[derive(Type, Deserialize)]
pub enum Action {
PromptPassword,
ProcessPassword { password: String },
}
#[derive(Type, Serialize, Clone)]
pub enum State {
AwaitingResponse,
ChallengeReceived,
}
pub struct PeerRequest {
pub tx: mpsc::Sender<Action>,
pub peer_id: String,
}
// impl PeerRequest {
// pub fn new_actor() -> (Self, mpsc::Receiver<State>) {}
// }
}