feat: integrate iroh-gossip for mesh networking, using ALPN for protocol routing and node events to manage gossip topics.
This commit is contained in:
@@ -35,7 +35,7 @@ pub mod store_actor;
|
||||
pub const MAX_ENTRY_SIZE: usize = 16 * 1024 * 1024;
|
||||
|
||||
pub use node_identity::{NodeIdentity, PeerStatus};
|
||||
pub use node::{Node, NodeBuilder, NodeInfo, StoreInfo, StoreHandle, NodeError, PeerInfo, JoinAcceptance};
|
||||
pub use node::{Node, NodeBuilder, NodeInfo, StoreInfo, StoreHandle, NodeError, NodeEvent, PeerInfo, JoinAcceptance};
|
||||
pub use sigchain::{SigChain, SigChainManager};
|
||||
pub use entry::Entry;
|
||||
pub use sync_state::{SyncState, AuthorInfo, MissingRange};
|
||||
|
||||
@@ -8,9 +8,11 @@ use crate::{
|
||||
store::StoreError,
|
||||
spawn_store_actor, StoreCmd,
|
||||
node_identity::NodeError as IdentityError,
|
||||
proto::SignedEntry,
|
||||
};
|
||||
use std::path::Path;
|
||||
use thiserror::Error;
|
||||
use tokio::sync::broadcast;
|
||||
|
||||
#[derive(Error, Debug)]
|
||||
pub enum NodeError {
|
||||
@@ -67,6 +69,13 @@ pub struct PeerInfo {
|
||||
pub status: PeerStatus,
|
||||
}
|
||||
|
||||
/// Events emitted by Node for interested listeners (e.g., LatticeServer)
|
||||
#[derive(Clone, Debug)]
|
||||
pub enum NodeEvent {
|
||||
/// Root store was activated (opened or set)
|
||||
RootStoreActivated(StoreHandle),
|
||||
}
|
||||
|
||||
pub struct NodeBuilder {
|
||||
pub data_dir: DataDir,
|
||||
}
|
||||
@@ -90,7 +99,6 @@ impl NodeBuilder {
|
||||
};
|
||||
|
||||
let meta = MetaStore::open(self.data_dir.meta_db())?;
|
||||
|
||||
// Set hostname on first creation
|
||||
if is_new {
|
||||
let hostname = hostname::get()
|
||||
@@ -98,12 +106,16 @@ impl NodeBuilder {
|
||||
.unwrap_or_else(|_| "unknown".to_string());
|
||||
let _ = meta.set_name(&hostname);
|
||||
}
|
||||
|
||||
// Create event channel
|
||||
let (event_tx, _) = broadcast::channel(16);
|
||||
|
||||
Ok(Node {
|
||||
data_dir: self.data_dir,
|
||||
node: std::sync::Arc::new(node),
|
||||
meta,
|
||||
root_store: tokio::sync::RwLock::new(None),
|
||||
event_tx,
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -118,6 +130,7 @@ pub struct Node {
|
||||
node: std::sync::Arc<NodeIdentity>,
|
||||
meta: MetaStore,
|
||||
root_store: tokio::sync::RwLock<Option<StoreHandle>>,
|
||||
event_tx: broadcast::Sender<NodeEvent>,
|
||||
}
|
||||
|
||||
impl Node {
|
||||
@@ -132,6 +145,11 @@ impl Node {
|
||||
pub fn node_id(&self) -> [u8; 32] {
|
||||
self.node.public_key_bytes()
|
||||
}
|
||||
|
||||
/// Subscribe to node events (e.g., root store activation)
|
||||
pub fn subscribe_events(&self) -> broadcast::Receiver<NodeEvent> {
|
||||
self.event_tx.subscribe()
|
||||
}
|
||||
|
||||
/// Get the secret key bytes for Iroh integration (same Ed25519 key)
|
||||
pub fn secret_key_bytes(&self) -> [u8; 32] {
|
||||
@@ -184,7 +202,13 @@ impl Node {
|
||||
match self.meta.root_store()? {
|
||||
Some(id) => {
|
||||
let (handle, info) = self.open_store(id).await?;
|
||||
|
||||
// Emit event for listeners (send clone, keep original)
|
||||
let _ = self.event_tx.send(NodeEvent::RootStoreActivated(handle.clone()));
|
||||
|
||||
// Store original handle (owns actor thread)
|
||||
*self.root_store.write().await = Some(handle);
|
||||
|
||||
Ok(Some(info))
|
||||
}
|
||||
None => Ok(None),
|
||||
@@ -234,14 +258,18 @@ impl Node {
|
||||
self.create_store_with_uuid(store_id)?;
|
||||
self.meta.set_root_store(store_id)?;
|
||||
|
||||
// Open and cache the handle
|
||||
// Open and cache the handle (original stays in cache)
|
||||
let (handle, _) = self.open_store(store_id).await?;
|
||||
*self.root_store.write().await = Some(handle.clone());
|
||||
let handle_clone = handle.clone();
|
||||
*self.root_store.write().await = Some(handle);
|
||||
|
||||
// Emit event for listeners
|
||||
let _ = self.event_tx.send(NodeEvent::RootStoreActivated(handle_clone.clone()));
|
||||
|
||||
// Publish our name to the store
|
||||
let _ = self.publish_name().await;
|
||||
|
||||
Ok(handle)
|
||||
Ok(handle_clone)
|
||||
}
|
||||
|
||||
// --- Peer Management ---
|
||||
@@ -471,17 +499,19 @@ impl Node {
|
||||
let info = StoreInfo { store_id, entries_replayed };
|
||||
|
||||
// Spawn actor thread - actor owns store, sigchain, and node copy
|
||||
let (tx, actor_handle) = spawn_store_actor(
|
||||
let (tx, entry_tx, actor_handle) = spawn_store_actor(
|
||||
store_id,
|
||||
store,
|
||||
sigchain,
|
||||
(*self.node).clone(),
|
||||
);
|
||||
|
||||
// Store the entry sender for gossip
|
||||
let handle = StoreHandle {
|
||||
store_id,
|
||||
tx,
|
||||
actor_handle: Some(actor_handle),
|
||||
entry_tx,
|
||||
};
|
||||
|
||||
Ok((handle, info))
|
||||
@@ -489,10 +519,12 @@ impl Node {
|
||||
}
|
||||
|
||||
/// A handle to a specific store - wraps channel to actor thread
|
||||
#[derive(Debug)]
|
||||
pub struct StoreHandle {
|
||||
store_id: Uuid,
|
||||
tx: tokio::sync::mpsc::Sender<StoreCmd>,
|
||||
actor_handle: Option<std::thread::JoinHandle<()>>,
|
||||
entry_tx: broadcast::Sender<SignedEntry>,
|
||||
}
|
||||
|
||||
impl Clone for StoreHandle {
|
||||
@@ -501,12 +533,18 @@ impl Clone for StoreHandle {
|
||||
store_id: self.store_id,
|
||||
tx: self.tx.clone(),
|
||||
actor_handle: None, // Clones don't own the actor thread
|
||||
entry_tx: self.entry_tx.clone(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl StoreHandle {
|
||||
pub fn id(&self) -> Uuid { self.store_id }
|
||||
|
||||
/// Subscribe to receive entries as they're committed locally
|
||||
pub fn subscribe_entries(&self) -> broadcast::Receiver<SignedEntry> {
|
||||
self.entry_tx.subscribe()
|
||||
}
|
||||
|
||||
pub async fn get(&self, key: &[u8]) -> Result<Option<Vec<u8>>, NodeError> {
|
||||
use StoreCmd;
|
||||
|
||||
@@ -10,7 +10,7 @@ use crate::{
|
||||
proto::SignedEntry,
|
||||
log,
|
||||
};
|
||||
use tokio::sync::{mpsc, oneshot};
|
||||
use tokio::sync::{mpsc, oneshot, broadcast};
|
||||
use std::thread::{self, JoinHandle};
|
||||
|
||||
/// Commands sent to the store actor
|
||||
@@ -103,9 +103,11 @@ impl std::error::Error for StoreActorError {}
|
||||
pub struct StoreActor {
|
||||
store_id: Uuid,
|
||||
store: Store,
|
||||
chain_manager: SigChainManager, // Manages all authors' sigchains
|
||||
chain_manager: SigChainManager,
|
||||
node: NodeIdentity,
|
||||
rx: mpsc::Receiver<StoreCmd>,
|
||||
/// Broadcast sender for emitting entries after they're committed locally
|
||||
entry_tx: broadcast::Sender<SignedEntry>,
|
||||
}
|
||||
|
||||
impl StoreActor {
|
||||
@@ -116,6 +118,7 @@ impl StoreActor {
|
||||
sigchain: SigChain,
|
||||
node: NodeIdentity,
|
||||
rx: mpsc::Receiver<StoreCmd>,
|
||||
entry_tx: broadcast::Sender<SignedEntry>,
|
||||
) -> Self {
|
||||
// Derive logs_dir from sigchain's log file path
|
||||
let logs_dir = sigchain.log_path()
|
||||
@@ -134,6 +137,7 @@ impl StoreActor {
|
||||
chain_manager,
|
||||
node,
|
||||
rx,
|
||||
entry_tx,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -253,6 +257,9 @@ impl StoreActor {
|
||||
let sigchain = self.chain_manager.get_or_create(local_author);
|
||||
sigchain.append(&entry)?;
|
||||
self.store.apply_entry(&entry)?;
|
||||
|
||||
// Broadcast the entry to listeners (for gossip)
|
||||
let _ = self.entry_tx.send(entry.clone());
|
||||
|
||||
Ok(seq)
|
||||
}
|
||||
@@ -276,16 +283,17 @@ impl StoreActor {
|
||||
}
|
||||
}
|
||||
|
||||
/// Spawn a store actor in a new thread, returns (sender, join_handle)
|
||||
/// Spawn a store actor in a new thread, returns (cmd_tx, entry_tx, join_handle)
|
||||
/// Uses std::thread since redb is blocking
|
||||
pub fn spawn_store_actor(
|
||||
store_id: Uuid,
|
||||
store: Store,
|
||||
sigchain: SigChain,
|
||||
node: NodeIdentity,
|
||||
) -> (mpsc::Sender<StoreCmd>, JoinHandle<()>) {
|
||||
) -> (mpsc::Sender<StoreCmd>, broadcast::Sender<SignedEntry>, JoinHandle<()>) {
|
||||
let (tx, rx) = mpsc::channel(32);
|
||||
let actor = StoreActor::new(store_id, store, sigchain, node, rx);
|
||||
let (entry_tx, _entry_rx) = broadcast::channel(64);
|
||||
let actor = StoreActor::new(store_id, store, sigchain, node, rx, entry_tx.clone());
|
||||
let handle = thread::spawn(move || actor.run());
|
||||
(tx, handle)
|
||||
(tx, entry_tx, handle)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user