diff --git a/radicle-cli/src/commands/clone.rs b/radicle-cli/src/commands/clone.rs index de689a8a..92d69622 100644 --- a/radicle-cli/src/commands/clone.rs +++ b/radicle-cli/src/commands/clone.rs @@ -80,7 +80,7 @@ pub fn run(options: Options, ctx: impl term::Context) -> anyhow::Result<()> { pub fn clone(id: Id, _interactive: Interactive, ctx: impl term::Context) -> anyhow::Result<()> { let profile = ctx.profile()?; - let mut node = radicle::node::connect(profile.node())?; + let mut node = radicle::node::connect(profile.socket())?; let signer = term::signer(&profile)?; // Track & fetch project. diff --git a/radicle-cli/src/commands/track.rs b/radicle-cli/src/commands/track.rs index 1f9a70e4..d0a9fcb5 100644 --- a/radicle-cli/src/commands/track.rs +++ b/radicle-cli/src/commands/track.rs @@ -94,7 +94,7 @@ pub fn run(options: Options, ctx: impl term::Context) -> anyhow::Result<()> { let storage = &profile.storage; let (_, rid) = radicle::rad::cwd().context("this command must be run within a project")?; let project = storage.repository(rid)?.project_of(profile.id())?; - let mut node = radicle::node::connect(profile.node())?; + let mut node = radicle::node::connect(profile.socket())?; term::info!( "Establishing 🌱 tracking relationship for {}", diff --git a/radicle-cli/src/commands/untrack.rs b/radicle-cli/src/commands/untrack.rs index 1762df8d..83dd80a6 100644 --- a/radicle-cli/src/commands/untrack.rs +++ b/radicle-cli/src/commands/untrack.rs @@ -88,6 +88,6 @@ pub fn run(options: Options, ctx: impl term::Context) -> anyhow::Result<()> { } pub fn untrack(id: Id, profile: &Profile) -> anyhow::Result { - let mut node = radicle::node::connect(profile.node())?; + let mut node = radicle::node::connect(profile.socket())?; node.untrack_repo(id).map_err(|e| anyhow!(e)) } diff --git a/radicle-cli/tests/commands.rs b/radicle-cli/tests/commands.rs index 63efd1b6..4826637a 100644 --- a/radicle-cli/tests/commands.rs +++ b/radicle-cli/tests/commands.rs @@ -16,7 +16,7 @@ fn test( let base = Path::new(env!("CARGO_MANIFEST_DIR")); let tmp = tempfile::tempdir().unwrap(); let home = if let Some(profile) = profile { - profile.home.as_path().to_path_buf() + profile.home().to_path_buf() } else { tmp.path().to_path_buf() }; diff --git a/radicle-crypto/src/test/signer.rs b/radicle-crypto/src/test/signer.rs index c315e757..947e9224 100644 --- a/radicle-crypto/src/test/signer.rs +++ b/radicle-crypto/src/test/signer.rs @@ -71,3 +71,28 @@ impl Signer for MockSigner { Ok(self.sign(msg)) } } + +#[cfg(feature = "cyphernet")] +impl cyphernet::EcSk for MockSigner { + type Pk = PublicKey; + + fn generate_keypair() -> (Self, Self::Pk) + where + Self: Sized, + { + unimplemented! {} + } + + fn to_pk(&self) -> Result { + Ok(*self.public_key()) + } +} + +#[cfg(feature = "cyphernet")] +impl cyphernet::EcSign for MockSigner { + type Sig = Signature; + + fn sign(&self, msg: impl AsRef<[u8]>) -> Self::Sig { + Signer::sign(self, msg.as_ref()) + } +} diff --git a/radicle-httpd/src/lib.rs b/radicle-httpd/src/lib.rs index b89c4d91..baa77cd8 100644 --- a/radicle-httpd/src/lib.rs +++ b/radicle-httpd/src/lib.rs @@ -51,7 +51,7 @@ pub async fn run(options: Options) -> anyhow::Result<()> { tracing::info!("{}", str::from_utf8(&git_version)?.trim()); let profile = Arc::new(radicle::Profile::load()?); - tracing::info!("using radicle home at {}", profile.home.display()); + tracing::info!("using radicle home at {}", profile.home().display()); let git_router = Router::new() .route("/:project/*request", any(git_handler)) diff --git a/radicle-node/src/client.rs b/radicle-node/src/client.rs index b1012c24..719c5421 100644 --- a/radicle-node/src/client.rs +++ b/radicle-node/src/client.rs @@ -1,8 +1,9 @@ -use std::io; -use std::{net, thread, time}; +use std::{io, net, thread, time}; use cyphernet::{Cert, EcSign}; use netservices::resource::NetAccept; +use radicle::profile::Paths; +use radicle::Storage; use reactor::poller::popol; use reactor::Reactor; use thiserror::Error; @@ -14,7 +15,7 @@ use crate::node::NodeId; use crate::service::{routing, tracking}; use crate::wire::Wire; use crate::worker::{WorkerPool, WorkerReq}; -use crate::{crypto, profile, service, LocalTime}; +use crate::{crypto, service, LocalTime}; pub mod handle; use handle::Handle; @@ -66,19 +67,19 @@ impl + Clone + 'static> /// /// This function spawns threads. pub fn with( - profile: profile::Profile, + paths: Paths, config: service::Config, listen: Vec, proxy: net::SocketAddr, signer: G, ) -> Result, Error> { - let id = *profile.id(); - let node = profile.node(); + let id = *signer.public_key(); + let node_sock = paths.socket(); + let node_dir = paths.node(); let network = config.network; let rng = fastrand::Rng::new(); let clock = LocalTime::now(); - let storage = profile.storage; - let node_dir = profile.home.join(NODE_DIR); + let storage = Storage::open(paths.storage())?; let address_db = node_dir.join(ADDRESS_DB_FILE); let routing_db = node_dir.join(ROUTING_DB_FILE); let tracking_db = node_dir.join(TRACKING_DB_FILE); @@ -122,7 +123,7 @@ impl + Clone + 'static> let handle = Handle::from(reactor.controller()); let control = thread::spawn({ let handle = handle.clone(); - move || control::listen(node, handle) + move || control::listen(node_sock, handle) }); let controller = reactor.controller(); let mut local_addrs = Vec::new(); diff --git a/radicle-node/src/main.rs b/radicle-node/src/main.rs index c7248db6..56211e7b 100644 --- a/radicle-node/src/main.rs +++ b/radicle-node/src/main.rs @@ -6,8 +6,9 @@ use nakamoto_net::LocalDuration; use radicle::profile; use radicle_node::client::Runtime; -use radicle_node::crypto::ssh::keystore::MemorySigner; +use radicle_node::crypto::ssh::keystore::{Keystore, MemorySigner}; use radicle_node::prelude::{Address, NodeId}; +use radicle_node::profile::Paths; use radicle_node::{logger, service}; #[derive(Debug)] @@ -77,11 +78,13 @@ fn main() -> anyhow::Result<()> { logger::init(log::Level::Debug)?; let options = Options::from_env()?; - let profile = radicle::Profile::load().context("Failed to load node profile")?; + let home = profile::home()?; + let paths = Paths::new(home); let passphrase = env::var(profile::env::RAD_PASSPHRASE) .context("`RAD_PASSPHRASE` is required to be set for the node to establish connections")? .into(); - let signer = MemorySigner::load(&profile.keystore, passphrase)?; + let keystore = Keystore::new(&paths.keys()); + let signer = MemorySigner::load(&keystore, passphrase)?; let config = service::Config { connect: options.connect.into_iter().collect(), external_addresses: options.external_addresses, @@ -89,7 +92,7 @@ fn main() -> anyhow::Result<()> { ..service::Config::default() }; let proxy = net::SocketAddr::new(net::Ipv4Addr::LOCALHOST.into(), 9050); - let runtime = Runtime::with(profile, config, options.listen, proxy, signer)?; + let runtime = Runtime::with(paths, config, options.listen, proxy, signer)?; runtime.run()?; diff --git a/radicle-node/src/tests/e2e.rs b/radicle-node/src/tests/e2e.rs index 391d27dc..d477b67c 100644 --- a/radicle-node/src/tests/e2e.rs +++ b/radicle-node/src/tests/e2e.rs @@ -5,13 +5,13 @@ use std::{ time::Duration, }; -use radicle::crypto::ssh::keystore::MemorySigner; +use radicle::crypto::test::signer::MockSigner; use radicle::git::refname; use radicle::identity::Id; use radicle::node::Handle; +use radicle::profile::Paths; use radicle::storage::WriteStorage; use radicle::test::fixtures; -use radicle::Profile; use radicle::Storage; use radicle::{assert_matches, rad}; @@ -27,8 +27,8 @@ use crate::{client, client::Runtime, service}; struct Node { id: NodeId, addr: net::SocketAddr, - handle: client::handle::Handle>, - signer: MemorySigner, + handle: client::handle::Handle>, + signer: MockSigner, storage: Storage, #[allow(dead_code)] thread: thread::JoinHandle>, @@ -42,15 +42,12 @@ impl Node { .take(8) .collect::(), ); - - let profile = Profile::init(home.as_path(), "pasphrase".to_owned()).unwrap(); - let signer = MemorySigner::load(&profile.keystore, "pasphrase".to_owned().into()).unwrap(); + let paths = Paths::init(home).unwrap(); + let signer = MockSigner::default(); let listen = vec![([0, 0, 0, 0], 0).into()]; let proxy = net::SocketAddr::new(net::Ipv4Addr::LOCALHOST.into(), 9050); - let storage = profile.storage.clone(); - - let rt = Runtime::with(profile, config, listen, proxy, signer.clone()).unwrap(); - + let storage = Storage::open(paths.storage()).unwrap(); + let rt = Runtime::with(paths, config, listen, proxy, signer.clone()).unwrap(); let addr = *rt.local_addrs.first().unwrap(); let id = rt.id; let handle = rt.handle.clone(); diff --git a/radicle-remote-helper/src/lib.rs b/radicle-remote-helper/src/lib.rs index c81258af..1ccbc0cc 100644 --- a/radicle-remote-helper/src/lib.rs +++ b/radicle-remote-helper/src/lib.rs @@ -108,7 +108,7 @@ pub fn run(profile: radicle::Profile) -> Result<(), Box anyhow::Result<()> { }; println!("id: {}", profile.id()); - println!("home: {}", profile.home.display()); + println!("home: {}", profile.home().display()); Ok(()) } diff --git a/radicle-tools/src/rad-clone.rs b/radicle-tools/src/rad-clone.rs index ac8fc67e..f2e31b63 100644 --- a/radicle-tools/src/rad-clone.rs +++ b/radicle-tools/src/rad-clone.rs @@ -11,7 +11,7 @@ fn main() -> anyhow::Result<()> { if let Some(id) = env::args().nth(1) { let id = Id::from_str(&id)?; - let mut node = radicle::node::connect(profile.node())?; + let mut node = radicle::node::connect(profile.socket())?; let repo = radicle::rad::clone(id, &cwd, &signer, &profile.storage, &mut node)?; println!( diff --git a/radicle-tools/src/rad-push.rs b/radicle-tools/src/rad-push.rs index e2da84b8..daedbc8a 100644 --- a/radicle-tools/src/rad-push.rs +++ b/radicle-tools/src/rad-push.rs @@ -16,7 +16,7 @@ fn main() -> anyhow::Result<()> { let sigrefs = project.sign_refs(&signer)?; let head = project.set_head()?; - radicle::node::connect(profile.node())?.announce_refs(id)?; + radicle::node::connect(profile.socket())?.announce_refs(id)?; println!("head: {}", head); println!("ok: {}", sigrefs.signature); diff --git a/radicle-tools/src/rad-self.rs b/radicle-tools/src/rad-self.rs index fe65e015..2058f604 100644 --- a/radicle-tools/src/rad-self.rs +++ b/radicle-tools/src/rad-self.rs @@ -7,7 +7,7 @@ fn main() -> anyhow::Result<()> { "fingerprint: {}", radicle::crypto::ssh::fmt::fingerprint(profile.id()) ); - println!("home: {}", profile.home.display()); + println!("home: {}", profile.home().display()); Ok(()) } diff --git a/radicle/src/profile.rs b/radicle/src/profile.rs index 375fecc0..e6a80532 100644 --- a/radicle/src/profile.rs +++ b/radicle/src/profile.rs @@ -59,7 +59,7 @@ pub enum Error { #[derive(Debug, Clone)] pub struct Profile { - pub home: PathBuf, + pub paths: Paths, pub storage: Storage, pub keystore: Keystore, pub public_key: PublicKey, @@ -68,19 +68,15 @@ pub struct Profile { impl Profile { pub fn init(home: impl AsRef, passphrase: impl Into) -> Result { let home = home.as_ref().to_path_buf(); - let paths = Paths { - home: home.as_path(), - }; + let paths = Paths::init(home.as_path())?; let storage = Storage::open(paths.storage())?; let keystore = Keystore::new(&paths.keys()); let public_key = keystore.init("radicle", passphrase)?; - fs::create_dir_all(paths.node()).ok(); - transport::local::register(storage.clone()); Ok(Profile { - home, + paths, storage, keystore, public_key, @@ -89,16 +85,17 @@ impl Profile { pub fn load() -> Result { let home = self::home()?; - let storage = Storage::open(home.join("storage"))?; - let keystore = Keystore::new(&home.join("keys")); + let paths = Paths::new(home); + let storage = Storage::open(paths.storage())?; + let keystore = Keystore::new(&paths.keys()); let public_key = keystore .public_key()? - .ok_or_else(|| Error::NotFound(home.clone()))?; + .ok_or_else(|| Error::NotFound(paths.home.clone()))?; transport::local::register(storage.clone()); Ok(Profile { - home, + paths, storage, keystore, public_key, @@ -130,19 +127,22 @@ impl Profile { /// Return the path to the keys folder. pub fn keys(&self) -> PathBuf { - self.home.join("keys") + self.paths.keys() + } + + /// Get the profile home directory. + pub fn home(&self) -> &Path { + self.paths.home() } /// Get the path to the radicle node socket. - pub fn node(&self) -> PathBuf { - env::var_os(env::RAD_SOCKET) - .map(PathBuf::from) - .unwrap_or_else(|| self.home.join("node").join(node::DEFAULT_SOCKET_NAME)) + pub fn socket(&self) -> PathBuf { + self.paths.socket() } /// Get `Paths` of profile - pub fn paths(&self) -> Paths { - Paths { home: &self.home } + pub fn paths(&self) -> &Paths { + &self.paths } } @@ -161,13 +161,24 @@ pub fn home() -> Result { } #[derive(Debug, Clone)] -pub struct Paths<'a> { - home: &'a Path, +pub struct Paths { + home: PathBuf, } -impl<'a> Paths<'a> { - pub fn new(home: &'a Path) -> Self { - Self { home } +impl Paths { + pub fn init(home: impl Into) -> Result { + let paths = Self::new(home); + fs::create_dir_all(paths.node()).ok(); + + Ok(paths) + } + + pub fn new(home: impl Into) -> Self { + Self { home: home.into() } + } + + pub fn home(&self) -> &Path { + self.home.as_path() } pub fn storage(&self) -> PathBuf { @@ -181,4 +192,10 @@ impl<'a> Paths<'a> { pub fn node(&self) -> PathBuf { self.home.join("node") } + + pub fn socket(&self) -> PathBuf { + env::var_os(env::RAD_SOCKET) + .map(PathBuf::from) + .unwrap_or_else(|| self.node().join(node::DEFAULT_SOCKET_NAME)) + } }