This repository has been archived by the owner on Sep 21, 2024. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 37
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat!: initial work on NameSystem, wrapping the underlying DHT networ…
…k. (#122) * Follow up documentation fixes for noosphere_ns * Refactor internal record representation as a wrapper around a UCAN auth token representing permission to publish. * Add tests for NSRecord::validate for delegated capabilities * Use non-ref NSRecord as input/output of NameSystem so the tokens can be stored/validated later, and enable NameSystem to evict expired records from cache. * Add delegated UCAN tokens to NS tests to more accurately reflect actual usage of the name system.
- Loading branch information
Showing
18 changed files
with
1,411 additions
and
241 deletions.
There are no files selected for viewing
Large diffs are not rendered by default.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,202 @@ | ||
use crate::{dht::DHTConfig, name_system::NameSystem}; | ||
use anyhow::{anyhow, Result}; | ||
use libp2p::{self, Multiaddr}; | ||
use noosphere_storage::{db::SphereDb, interface::Store}; | ||
use std::net::Ipv4Addr; | ||
use ucan_key_support::ed25519::Ed25519KeyMaterial; | ||
|
||
/// [NameSystemBuilder] is the primary external interface for | ||
/// creating a new [NameSystem]. `key_material` and `store` | ||
/// must be provided. | ||
/// | ||
/// # Examples | ||
/// | ||
/// ``` | ||
/// use noosphere_core::authority::generate_ed25519_key; | ||
/// use noosphere_storage::{db::SphereDb, memory::{MemoryStore, MemoryStorageProvider}}; | ||
/// use noosphere_ns::{NameSystem, NameSystemBuilder}; | ||
/// use tokio; | ||
/// | ||
/// #[tokio::main] | ||
/// async fn main() { | ||
/// let key_material = generate_ed25519_key(); | ||
/// let store = SphereDb::new(&MemoryStorageProvider::default()).await.unwrap(); | ||
/// | ||
/// let ns = NameSystemBuilder::default() | ||
/// .key_material(&key_material) | ||
/// .store(&store) | ||
/// .listening_port(30000) | ||
/// .build().expect("valid config"); | ||
/// | ||
/// assert!(NameSystemBuilder::<MemoryStore>::default().build().is_err(), "key_material and store must be provided."); | ||
/// } | ||
/// ``` | ||
pub struct NameSystemBuilder<S> | ||
where | ||
S: Store, | ||
{ | ||
bootstrap_peers: Option<Vec<Multiaddr>>, | ||
dht_config: DHTConfig, | ||
key_material: Option<Ed25519KeyMaterial>, | ||
store: Option<SphereDb<S>>, | ||
propagation_interval: u64, | ||
} | ||
|
||
impl<S> NameSystemBuilder<S> | ||
where | ||
S: Store, | ||
{ | ||
/// If bootstrap peers are provided, how often, | ||
/// in seconds, should the bootstrap process execute | ||
/// to keep routing tables fresh. | ||
pub fn bootstrap_interval(mut self, interval: u64) -> Self { | ||
self.dht_config.bootstrap_interval = interval; | ||
self | ||
} | ||
|
||
/// Peer addresses to query to update routing tables | ||
/// during bootstrap. A standalone bootstrap node would | ||
/// have this field empty. | ||
pub fn bootstrap_peers(mut self, peers: &Vec<Multiaddr>) -> Self { | ||
self.bootstrap_peers = Some(peers.to_owned()); | ||
self | ||
} | ||
|
||
/// Public/private keypair for DHT node. | ||
pub fn key_material(mut self, key_material: &Ed25519KeyMaterial) -> Self { | ||
self.key_material = Some(key_material.to_owned()); | ||
self | ||
} | ||
|
||
/// Port to listen for incoming TCP connections. If not specified, | ||
/// an open port is automatically chosen. | ||
pub fn listening_port(mut self, port: u16) -> Self { | ||
let mut address = Multiaddr::empty(); | ||
address.push(libp2p::multiaddr::Protocol::Ip4(Ipv4Addr::new( | ||
127, 0, 0, 1, | ||
))); | ||
address.push(libp2p::multiaddr::Protocol::Tcp(port)); | ||
self.dht_config.listening_address = Some(address); | ||
self | ||
} | ||
|
||
/// How frequently, in seconds, the DHT attempts to | ||
/// dial peers found in its kbucket. Outside of tests, | ||
/// should not be lower than 5 seconds. | ||
pub fn peer_dialing_interval(mut self, interval: u64) -> Self { | ||
self.dht_config.peer_dialing_interval = interval; | ||
self | ||
} | ||
|
||
/// How long, in seconds, until a network query times out. | ||
pub fn query_timeout(mut self, timeout: u32) -> Self { | ||
self.dht_config.query_timeout = timeout; | ||
self | ||
} | ||
|
||
/// The Noosphere Store to use for reading and writing sphere data. | ||
pub fn store(mut self, store: &SphereDb<S>) -> Self { | ||
self.store = Some(store.to_owned()); | ||
self | ||
} | ||
|
||
/// Default interval for hosted records to be propagated to the network. | ||
pub fn propagation_interval(mut self, propagation_interval: u64) -> Self { | ||
self.propagation_interval = propagation_interval; | ||
self | ||
} | ||
|
||
/// Build a [NameSystem] based off of the provided configuration. | ||
pub fn build(mut self) -> Result<NameSystem<S>> { | ||
let key_material = self | ||
.key_material | ||
.take() | ||
.ok_or_else(|| anyhow!("key_material required."))?; | ||
let store = self | ||
.store | ||
.take() | ||
.ok_or_else(|| anyhow!("store required."))?; | ||
Ok(NameSystem::new( | ||
key_material, | ||
store, | ||
self.bootstrap_peers.take(), | ||
self.dht_config, | ||
self.propagation_interval, | ||
)) | ||
} | ||
} | ||
|
||
impl<S> Default for NameSystemBuilder<S> | ||
where | ||
S: Store, | ||
{ | ||
fn default() -> Self { | ||
Self { | ||
bootstrap_peers: None, | ||
dht_config: DHTConfig::default(), | ||
key_material: None, | ||
store: None, | ||
propagation_interval: 60 * 60 * 24, // 1 day | ||
} | ||
} | ||
} | ||
|
||
#[cfg(test)] | ||
mod tests { | ||
use super::*; | ||
use noosphere_core::authority::generate_ed25519_key; | ||
use noosphere_storage::{ | ||
db::SphereDb, | ||
memory::{MemoryStorageProvider, MemoryStore}, | ||
}; | ||
|
||
#[tokio::test] | ||
async fn test_name_system_builder() -> Result<(), anyhow::Error> { | ||
let key_material = generate_ed25519_key(); | ||
let store = SphereDb::new(&MemoryStorageProvider::default()) | ||
.await | ||
.unwrap(); | ||
let bootstrap_peers: Vec<Multiaddr> = vec![ | ||
"/ip4/127.0.0.50/tcp/33333/p2p/12D3KooWH8WgH9mgbMXrKX4veokUznvEn6Ycwg4qaGNi83nLkoUK" | ||
.parse()?, | ||
"/ip4/127.0.0.50/tcp/33334/p2p/12D3KooWMWo6tNGRx1G4TNqvr4SnHyVXSReC3tdX6zoJothXxV2c" | ||
.parse()?, | ||
]; | ||
|
||
let ns = NameSystemBuilder::default() | ||
.listening_port(30000) | ||
.key_material(&key_material) | ||
.store(&store) | ||
.bootstrap_peers(&bootstrap_peers) | ||
.bootstrap_interval(33) | ||
.peer_dialing_interval(11) | ||
.query_timeout(22) | ||
.propagation_interval(3600) | ||
.build()?; | ||
|
||
assert_eq!(ns.key_material.0.as_ref(), key_material.0.as_ref()); | ||
assert_eq!(ns._propagation_interval, 3600); | ||
assert_eq!(ns.bootstrap_peers.as_ref().unwrap().len(), 2); | ||
assert_eq!(ns.bootstrap_peers.as_ref().unwrap()[0], bootstrap_peers[0],); | ||
assert_eq!(ns.bootstrap_peers.as_ref().unwrap()[1], bootstrap_peers[1]); | ||
assert_eq!( | ||
ns.dht_config.listening_address.as_ref().unwrap(), | ||
&"/ip4/127.0.0.1/tcp/30000".parse()? | ||
); | ||
assert_eq!(ns.dht_config.bootstrap_interval, 33); | ||
assert_eq!(ns.dht_config.peer_dialing_interval, 11); | ||
assert_eq!(ns.dht_config.query_timeout, 22); | ||
|
||
if NameSystemBuilder::default().store(&store).build().is_ok() { | ||
panic!("key_material required."); | ||
} | ||
if NameSystemBuilder::<MemoryStore>::default() | ||
.key_material(&key_material) | ||
.build() | ||
.is_ok() | ||
{ | ||
panic!("store required."); | ||
} | ||
Ok(()) | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.