From d1bdc29d28dc28e99eca794c11b4d190b7128dfe Mon Sep 17 00:00:00 2001 From: Jordan Santell Date: Thu, 9 Mar 2023 10:36:52 -0800 Subject: [PATCH] feat: Expose ipfs-api-url to orb-ns to integrate IPFS cid resolution in NS validation. (#265) --- Cargo.lock | 1 + rust/noosphere-ns/Cargo.toml | 7 +++- .../src/bin/orb-ns/cli/address.rs | 41 +++++++++++-------- rust/noosphere-ns/src/bin/orb-ns/cli/cli.rs | 14 ++++++- rust/noosphere-ns/src/bin/orb-ns/cli/mod.rs | 1 + rust/noosphere-ns/src/bin/orb-ns/main.rs | 7 +--- .../src/bin/orb-ns/runner/config.rs | 20 ++++++--- .../src/bin/orb-ns/runner/runner.rs | 18 +++++--- 8 files changed, 73 insertions(+), 36 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 33de12d32..f5413565f 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3983,6 +3983,7 @@ dependencies = [ "libp2p", "noosphere", "noosphere-core", + "noosphere-ipfs", "noosphere-storage", "rand 0.8.5", "reqwest", diff --git a/rust/noosphere-ns/Cargo.toml b/rust/noosphere-ns/Cargo.toml index 3d42ce3d9..0366c2efe 100644 --- a/rust/noosphere-ns/Cargo.toml +++ b/rust/noosphere-ns/Cargo.toml @@ -37,18 +37,21 @@ ucan-key-support = { version = "0.1.0" } tokio = { version = "1.15", features = ["io-util", "io-std", "sync", "macros", "rt", "rt-multi-thread"] } noosphere-storage = { version = "0.4.2", path = "../noosphere-storage" } noosphere-core = { version = "0.6.3", path = "../noosphere-core" } +libp2p = { version = "0.51.0", default-features = false, features = [ "identify", "dns", "kad", "macros", "mplex", "noise", "serde", "tcp", "tokio", "yamux" ] } + # noosphere_ns::bin noosphere = { version = "0.6.3", path = "../noosphere", optional = true } +noosphere-ipfs = { version = "0.1.2", path = "../noosphere-ipfs", optional = true } clap = { version = "^4.1", features = ["derive"], optional = true } home = { version = "~0.5", optional = true } toml = { version = "~0.5", optional = true } + # noosphere_ns::server axum = { version = "~0.5", features = ["json", "headers", "macros"], optional = true } reqwest = { version = "~0.11", default-features = false, features = ["json", "rustls-tls"], optional = true } tracing-subscriber = { version = "~0.3", features = ["env-filter"], optional = true } tower-http = { version = "~0.3", features = ["trace"], optional = true } url = { version = "^2", features = [ "serde" ], optional = true } -libp2p = { version = "0.51.0", default-features = false, features = [ "identify", "dns", "kad", "macros", "mplex", "noise", "serde", "tcp", "tokio", "yamux" ] } [dev-dependencies] @@ -61,7 +64,7 @@ tempdir = { version = "~0.3" } [features] default = ["orb-ns", "api-server"] api-server = ["axum", "reqwest", "url", "tracing-subscriber", "tower-http"] -orb-ns = ["clap", "noosphere", "home", "toml"] +orb-ns = ["clap", "noosphere", "home", "toml", "noosphere-ipfs"] [[bin]] name = "orb-ns" diff --git a/rust/noosphere-ns/src/bin/orb-ns/cli/address.rs b/rust/noosphere-ns/src/bin/orb-ns/cli/address.rs index 5e5ec6568..ef473877c 100644 --- a/rust/noosphere-ns/src/bin/orb-ns/cli/address.rs +++ b/rust/noosphere-ns/src/bin/orb-ns/cli/address.rs @@ -23,39 +23,46 @@ pub fn parse_cli_address>(input: &str) -> Result(deserializer: D) -> Result, D::Error> where D: Deserializer<'de>, { - match Option::::deserialize(deserializer) { - Ok(address) => match address { - Some(addr) => match addr.try_into() { - Ok(socket) => Ok(Some(socket)), - Err(e) => Err(de::Error::custom(e.to_string())), - }, - None => Ok(None), - }, - Err(e) => Err(de::Error::custom(e.to_string())), - } + deserialize_cliaddress::<'de, D, SocketAddr>(deserializer) } -/// Parses a string in [CLIAddress] form into a [Multiaddr] for -/// serde deserialization. +/// Parses a string in [CLIAddress] form into a [Multiaddr] for serde deserialization. pub fn deserialize_multiaddr<'de, D>(deserializer: D) -> Result, D::Error> where D: Deserializer<'de>, +{ + deserialize_cliaddress::<'de, D, Multiaddr>(deserializer) +} + +/// Parses a string in [CLIAddress] form into a [Url] for serde deserialization. +pub fn deserialize_url<'de, D>(deserializer: D) -> Result, D::Error> +where + D: Deserializer<'de>, +{ + deserialize_cliaddress::<'de, D, Url>(deserializer) +} + +/// Implementation for the serde deserialization methods e.g. `deserialize_url`. +fn deserialize_cliaddress<'de, D, T>(deserializer: D) -> Result, D::Error> +where + D: Deserializer<'de>, + T: TryFrom, + >::Error: ToString, { match Option::::deserialize(deserializer) { Ok(address) => match address { - Some(addr) => match addr.try_into() { - Ok(maddr) => Ok(Some(maddr)), + Some(cli_addr) => match cli_addr.try_into() { + Ok(t_addr) => Ok(Some(t_addr)), Err(e) => Err(de::Error::custom(e.to_string())), }, None => Ok(None), }, - Err(e) => Err(de::Error::custom(e.to_string())), + Err(e) => Err(e), } } diff --git a/rust/noosphere-ns/src/bin/orb-ns/cli/cli.rs b/rust/noosphere-ns/src/bin/orb-ns/cli/cli.rs index 640f2ac51..7db4cbdb7 100644 --- a/rust/noosphere-ns/src/bin/orb-ns/cli/cli.rs +++ b/rust/noosphere-ns/src/bin/orb-ns/cli/cli.rs @@ -1,6 +1,8 @@ #![cfg(not(target_arch = "wasm32"))] -use crate::cli::address::{deserialize_multiaddr, deserialize_socket_addr, parse_cli_address}; +use crate::cli::address::{ + deserialize_multiaddr, deserialize_socket_addr, deserialize_url, parse_cli_address, +}; use clap::{Parser, Subcommand}; use noosphere_core::data::Did; use noosphere_ns::{DHTConfig, Multiaddr, NSRecord}; @@ -55,6 +57,14 @@ pub enum CLICommand { /// the default bootstrap peers. #[arg(long, default_value_t = false)] no_default_peers: bool, + + /// If no configuration path provided, the URL to a Kubo HTTP RPC + /// service to resolve content-addressable content. + #[arg( + long, + value_parser = parse_cli_address:: + )] + ipfs_api_url: Option, }, /// Utility to create keys compatible with Noosphere. @@ -116,4 +126,6 @@ pub struct CLIConfigFile { pub no_default_peers: bool, #[serde(default)] pub dht_config: DHTConfig, + #[serde(default, deserialize_with = "deserialize_url")] + pub ipfs_api_url: Option, } diff --git a/rust/noosphere-ns/src/bin/orb-ns/cli/mod.rs b/rust/noosphere-ns/src/bin/orb-ns/cli/mod.rs index b05b4faf7..b53b6d72f 100644 --- a/rust/noosphere-ns/src/bin/orb-ns/cli/mod.rs +++ b/rust/noosphere-ns/src/bin/orb-ns/cli/mod.rs @@ -45,6 +45,7 @@ mod test { api_address: Some("127.0.0.1:0".parse().unwrap()), peers: None, no_default_peers: true, + ipfs_api_url: None, }, &key_storage, ) diff --git a/rust/noosphere-ns/src/bin/orb-ns/main.rs b/rust/noosphere-ns/src/bin/orb-ns/main.rs index a62b3b5af..0dc66b3de 100644 --- a/rust/noosphere-ns/src/bin/orb-ns/main.rs +++ b/rust/noosphere-ns/src/bin/orb-ns/main.rs @@ -17,9 +17,9 @@ mod inner { pub use crate::cli; pub use anyhow::{anyhow, Result}; pub use noosphere::key::{InsecureKeyStorage, KeyStorage}; + pub use noosphere_core::tracing::initialize_tracing; pub use tokio; pub use tracing::*; - pub use tracing_subscriber::{fmt, prelude::*, EnvFilter}; } #[cfg(not(target_arch = "wasm32"))] @@ -28,10 +28,7 @@ use inner::*; #[cfg(not(target_arch = "wasm32"))] #[tokio::main] async fn main() -> Result<()> { - tracing_subscriber::registry() - .with(fmt::layer()) - .with(EnvFilter::from_default_env()) - .init(); + initialize_tracing(); let key_storage = InsecureKeyStorage::new(&utils::get_keys_dir()?)?; cli::process_args(&key_storage) diff --git a/rust/noosphere-ns/src/bin/orb-ns/runner/config.rs b/rust/noosphere-ns/src/bin/orb-ns/runner/config.rs index 65dda6a9d..807705a64 100644 --- a/rust/noosphere-ns/src/bin/orb-ns/runner/config.rs +++ b/rust/noosphere-ns/src/bin/orb-ns/runner/config.rs @@ -4,8 +4,8 @@ use anyhow::{anyhow, Result}; use noosphere::key::InsecureKeyStorage; use noosphere_ns::{DHTConfig, Multiaddr, BOOTSTRAP_PEERS}; use std::net::SocketAddr; - use ucan_key_support::ed25519::Ed25519KeyMaterial; +use url::Url; /// Configuration for [NameSystemRunner], hydrated/resolved from CLI. pub struct RunnerNodeConfig { @@ -14,6 +14,7 @@ pub struct RunnerNodeConfig { pub listening_address: Option, pub peers: Vec, pub dht_config: DHTConfig, + pub ipfs_api_url: Option, } impl RunnerNodeConfig { @@ -26,6 +27,7 @@ impl RunnerNodeConfig { let dht_config = config.dht_config; let listening_address = config.listening_address; let api_address = config.api_address; + let ipfs_api_url = config.ipfs_api_url; let mut peers = config.peers; if !config.no_default_peers { peers.extend_from_slice(&BOOTSTRAP_PEERS[..]); @@ -37,6 +39,7 @@ impl RunnerNodeConfig { listening_address, peers, dht_config, + ipfs_api_url, }) } @@ -53,6 +56,7 @@ impl RunnerNodeConfig { no_default_peers, listening_address, api_address, + ipfs_api_url, } => match config { Some(config_path) => { let toml_str = tokio::fs::read_to_string(&config_path).await?; @@ -69,11 +73,7 @@ impl RunnerNodeConfig { vec![] }; - let dht_config = if cfg!(test) { - DHTConfig::default() - } else { - DHTConfig::default() - }; + let dht_config = DHTConfig::default(); let config = CLIConfigFile { key: key_name.clone(), @@ -82,6 +82,7 @@ impl RunnerNodeConfig { peers: bootstrap_peers, no_default_peers, dht_config, + ipfs_api_url, }; Ok(RunnerNodeConfig::try_from_config(key_storage, config).await?) } @@ -152,6 +153,7 @@ mod tests { listening_address: Some("/ip4/127.0.0.1/tcp/6666".parse()?), peers: None, no_default_peers: false, + ipfs_api_url: None, }, &env.key_storage, ) @@ -199,6 +201,7 @@ peers = [ listening_address: None, peers: None, no_default_peers: false, + ipfs_api_url: None, }, &env.key_storage, ) @@ -250,6 +253,7 @@ no_default_peers = true listening_address: None, peers: None, no_default_peers: false, + ipfs_api_url: None, }, &env.key_storage, ) @@ -282,6 +286,7 @@ listening_address = 10000 listening_address: Some("/ip4/127.0.0.1/tcp/6666".parse()?), peers: None, no_default_peers: false, + ipfs_api_url: None, }, CLICommand::Run { api_address: None, @@ -290,6 +295,7 @@ listening_address = 10000 listening_address: Some("/ip4/127.0.0.1/tcp/6666".parse()?), peers: None, no_default_peers: false, + ipfs_api_url: None, }, CLICommand::Run { api_address: None, @@ -298,6 +304,7 @@ listening_address = 10000 listening_address: None, peers: None, no_default_peers: false, + ipfs_api_url: None, }, CLICommand::Run { api_address: None, @@ -306,6 +313,7 @@ listening_address = 10000 listening_address: None, peers: None, no_default_peers: false, + ipfs_api_url: None, }, ]; diff --git a/rust/noosphere-ns/src/bin/orb-ns/runner/runner.rs b/rust/noosphere-ns/src/bin/orb-ns/runner/runner.rs index 354a30f01..6d17fe43d 100644 --- a/rust/noosphere-ns/src/bin/orb-ns/runner/runner.rs +++ b/rust/noosphere-ns/src/bin/orb-ns/runner/runner.rs @@ -1,5 +1,8 @@ use crate::runner::config::RunnerNodeConfig; use anyhow::Result; +use noosphere_ipfs::{IpfsStorage, KuboClient}; +#[cfg(feature = "api-server")] +use noosphere_ns::server::APIServer; use noosphere_ns::{Multiaddr, NameSystem, NameSystemClient, PeerId}; use noosphere_storage::{MemoryStorage, SphereDb}; use serde::Serialize; @@ -13,9 +16,6 @@ use std::{ use tokio::sync::Mutex; use url::Url; -#[cfg(feature = "api-server")] -use noosphere_ns::server::APIServer; - #[cfg(not(feature = "api-server"))] struct APIServer; #[cfg(not(feature = "api-server"))] @@ -44,8 +44,16 @@ pub struct NameSystemRunner { impl NameSystemRunner { pub(crate) async fn try_from_config(mut config: RunnerNodeConfig) -> Result { - let store = SphereDb::new(&MemoryStorage::default()).await?; - let node = NameSystem::new(&config.key_material, store, config.dht_config.to_owned())?; + let node = if let Some(ipfs_api_url) = config.ipfs_api_url { + let kubo = KuboClient::new(&ipfs_api_url)?; + let store = + SphereDb::new(&IpfsStorage::new(MemoryStorage::default(), Some(kubo))).await?; + NameSystem::new(&config.key_material, store, config.dht_config.to_owned())? + } else { + let store = SphereDb::new(&MemoryStorage::default()).await?; + NameSystem::new(&config.key_material, store, config.dht_config.to_owned())? + }; + let peer_id = node.peer_id().to_owned(); let listening_address = if let Some(requested_addr) = config.listening_address.take() {