Skip to content

Commit 2652c55

Browse files
authored
Merge pull request #8 from OpenArchive/feature/dht-schema-group-methods
Store route blob ID on the DHT and manage route IDs
2 parents c3b0549 + 071b406 commit 2652c55

File tree

5 files changed

+166
-27
lines changed

5 files changed

+166
-27
lines changed

src/backend.rs

Lines changed: 38 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -8,19 +8,20 @@ use std::mem;
88
use std::path::{Path, PathBuf};
99
use std::sync::Arc;
1010
use tokio::fs;
11-
use tokio::sync::{mpsc, oneshot};
11+
use tokio::sync::{mpsc::{self, Receiver}, oneshot, broadcast};
1212
use tracing::info;
1313
use veilid_core::{
1414
api_startup_config, vld0_generate_keypair, CryptoKey, CryptoSystem, CryptoSystemVLD0,
1515
CryptoTyped, DHTSchema, KeyPair, ProtectedStore, RoutingContext, SharedSecret, UpdateCallback,
16-
VeilidAPI, VeilidConfigInner, VeilidUpdate, CRYPTO_KIND_VLD0, TypedKey
16+
VeilidAPI, VeilidConfigInner, VeilidUpdate, CRYPTO_KIND_VLD0, TypedKey, VeilidConfigProtectedStore
1717
};
1818
use xdg::BaseDirectories;
1919

2020
pub struct Backend {
2121
path: PathBuf,
2222
port: u16,
2323
veilid_api: Option<VeilidAPI>,
24+
update_rx: Option<broadcast::Receiver<VeilidUpdate>>,
2425
groups: HashMap<CryptoKey, Box<Group>>,
2526
repos: HashMap<CryptoKey, Box<Repo>>,
2627
}
@@ -31,6 +32,7 @@ impl Backend {
3132
path: base_path.to_path_buf(),
3233
port,
3334
veilid_api: None,
35+
update_rx: None,
3436
groups: HashMap::new(),
3537
repos: HashMap::new(),
3638
})
@@ -51,22 +53,15 @@ impl Backend {
5153
)
5254
})?;
5355

54-
let (tx, mut rx) = mpsc::channel(1);
56+
let (update_tx, update_rx) = broadcast::channel::<VeilidUpdate>(32);
5557

5658
let update_callback: UpdateCallback = Arc::new(move |update| {
57-
// Else handle update for something
58-
// info!("Received update: {:?}", update);
59-
if let VeilidUpdate::Attachment(attachment_state) = &update {
60-
if attachment_state.public_internet_ready {
61-
println!("Public internet ready!");
62-
let tx = tx.clone();
63-
tokio::spawn(async move {
64-
if tx.send(()).await.is_err() {
65-
println!("receiver dropped");
66-
}
67-
});
59+
let update_tx = update_tx.clone();
60+
tokio::spawn(async move {
61+
if let Err(e) = update_tx.send(update) {
62+
println!("Failed to send update: {}", e);
6863
}
69-
}
64+
});
7065
});
7166

7267
let xdg_dirs = BaseDirectories::with_prefix("save-dweb-backend")?;
@@ -111,7 +106,12 @@ impl Backend {
111106

112107
println!("Waiting for network ready state");
113108

114-
rx.recv().await.expect("Unable to wait for veilid init");
109+
self.update_rx = Some(update_rx);
110+
111+
// Wait for network ready state
112+
if let Some(rx) = &self.update_rx {
113+
self.wait_for_network(rx.resubscribe()).await?;
114+
}
115115

116116
Ok(())
117117
}
@@ -129,13 +129,25 @@ impl Backend {
129129
Ok(())
130130
}
131131

132+
async fn wait_for_network(&self, mut update_rx: broadcast::Receiver<VeilidUpdate>) -> Result<()> {
133+
while let Ok(update) = update_rx.recv().await {
134+
if let VeilidUpdate::Attachment(attachment_state) = update {
135+
if attachment_state.public_internet_ready {
136+
println!("Public internet ready!");
137+
break;
138+
}
139+
}
140+
}
141+
Ok(())
142+
}
143+
132144
pub async fn create_group(&mut self) -> Result<Group> {
133145
let veilid = self
134146
.veilid_api
135147
.as_ref()
136148
.ok_or_else(|| anyhow!("Veilid API is not initialized"))?;
137149
let routing_context = veilid.routing_context()?;
138-
let schema = DHTSchema::dflt(1)?;
150+
let schema = DHTSchema::dflt(3)?;
139151
let kind = Some(CRYPTO_KIND_VLD0);
140152

141153
let dht_record = routing_context.create_dht_record(schema, kind).await?;
@@ -234,7 +246,7 @@ impl Backend {
234246
.as_ref()
235247
.ok_or_else(|| anyhow!("Veilid API is not initialized"))?;
236248
let routing_context = veilid.routing_context()?;
237-
let schema = DHTSchema::dflt(1)?;
249+
let schema = DHTSchema::dflt(3)?;
238250
let kind = Some(CRYPTO_KIND_VLD0);
239251

240252
let dht_record = routing_context.create_dht_record(schema, kind).await?;
@@ -305,4 +317,12 @@ impl Backend {
305317

306318
Ok(Box::new(repo))
307319
}
320+
321+
pub fn subscribe_updates(&self) -> Option<broadcast::Receiver<VeilidUpdate>> {
322+
self.update_rx.as_ref().map(|rx| rx.resubscribe())
323+
}
324+
325+
pub fn get_veilid_api(&self) -> Option<&VeilidAPI> {
326+
self.veilid_api.as_ref()
327+
}
308328
}

src/common.rs

Lines changed: 68 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,13 @@
11
#![allow(async_fn_in_trait)]
22
#![allow(clippy::async_yields_async)]
33

4+
use crate::constants::ROUTE_ID_DHT_KEY;
45
use serde::{Serialize, Deserialize};
56
use eyre::{Result, anyhow};
67
use std::sync::Arc;
78
use veilid_core::{
89
CryptoKey, SharedSecret, CryptoTyped, DHTRecordDescriptor, RoutingContext, CryptoSystemVLD0,
9-
ProtectedStore, Nonce, CRYPTO_KIND_VLD0, CryptoSystem
10+
ProtectedStore, Nonce, CRYPTO_KIND_VLD0, CryptoSystem, KeyPair, VeilidAPI
1011
};
1112

1213
#[derive(Serialize, Deserialize)]
@@ -40,6 +41,16 @@ pub trait DHTEntity {
4041
fn get_dht_record(&self) -> DHTRecordDescriptor;
4142
fn get_secret_key(&self) -> Option<CryptoKey>;
4243

44+
// Default method to get the owner key
45+
fn owner_key(&self) -> CryptoKey {
46+
self.get_dht_record().owner().clone()
47+
}
48+
49+
// Default method to get the owner secret
50+
fn owner_secret(&self) -> Option<CryptoKey> {
51+
self.get_dht_record().owner_secret().cloned()
52+
}
53+
4354
fn encrypt_aead(&self, data: &[u8], associated_data: Option<&[u8]>) -> Result<Vec<u8>> {
4455
let nonce = self.get_crypto_system().random_nonce();
4556
let mut buffer = Vec::with_capacity(nonce.as_slice().len() + data.len());
@@ -90,6 +101,62 @@ pub trait DHTEntity {
90101
Ok(())
91102
}
92103

104+
async fn store_route_id_in_dht(
105+
&self,
106+
route_id_blob: Vec<u8>,
107+
) -> Result<()> {
108+
let routing_context = &self.get_routing_context();
109+
let dht_record = self.get_dht_record();
110+
routing_context.set_dht_value(
111+
dht_record.key().clone(),
112+
ROUTE_ID_DHT_KEY,
113+
route_id_blob,
114+
None,
115+
)
116+
.await
117+
.map_err(|e| anyhow!("Failed to store route ID blob in DHT: {}", e))?;
118+
119+
Ok(())
120+
}
121+
122+
async fn get_route_id_from_dht(&self, subkey: u32) -> Result<Vec<u8>> {
123+
let routing_context = &self.get_routing_context();
124+
125+
// Use the existing DHT record
126+
let dht_record = self.get_dht_record();
127+
128+
// Get the stored route ID blob at subkey
129+
let stored_blob = routing_context
130+
.get_dht_value(dht_record.key().clone(), ROUTE_ID_DHT_KEY, false)
131+
.await?
132+
.ok_or_else(|| anyhow!("Route ID blob not found in DHT"))?;
133+
134+
Ok(stored_blob.data().to_vec())
135+
}
136+
137+
138+
// Send an AppMessage to the repo owner using the stored route ID blob
139+
async fn send_message_to_owner(
140+
&self,
141+
veilid: &VeilidAPI,
142+
message: Vec<u8>,
143+
subkey: u32,
144+
) -> Result<()> {
145+
let routing_context = &self.get_routing_context();
146+
147+
// Retrieve the route ID blob from DHT
148+
let route_id_blob = self.get_route_id_from_dht(subkey).await?;
149+
150+
// Import the route using the blob via VeilidAPI
151+
let route_id = veilid.import_remote_private_route(route_id_blob)?;
152+
153+
// Send an AppMessage to the repo owner using the imported route ID
154+
routing_context
155+
.app_message(veilid_core::Target::PrivateRoute(route_id), message)
156+
.await?;
157+
158+
Ok(())
159+
}
93160

94161
fn get_write_key(&self) -> Option<CryptoKey> {
95162
unimplemented!("WIP")

src/constants.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,4 +5,5 @@ pub const TEST_GROUP_NAME: &str = "Test Group";
55
pub const UNABLE_TO_STORE_KEYPAIR: &str = "Unable to store keypair";
66
pub const FAILED_TO_LOAD_KEYPAIR: &str = "Failed to load keypair";
77
pub const KEYPAIR_NOT_FOUND: &str = "Keypair not found";
8-
pub const FAILED_TO_DESERIALIZE_KEYPAIR: &str = "Failed to deserialize keypair";
8+
pub const FAILED_TO_DESERIALIZE_KEYPAIR: &str = "Failed to deserialize keypair";
9+
pub const ROUTE_ID_DHT_KEY: u32 = 2;

src/group.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,7 @@ impl Group {
6161
Err(anyhow!("Repo not found"))
6262
}
6363
}
64+
6465
}
6566

6667
impl DHTEntity for Group {

src/lib.rs

Lines changed: 57 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -4,18 +4,19 @@ pub mod backend;
44
pub mod common;
55
pub mod constants;
66

7-
use crate::constants::{GROUP_NOT_FOUND, UNABLE_TO_SET_GROUP_NAME, UNABLE_TO_GET_GROUP_NAME, TEST_GROUP_NAME, UNABLE_TO_STORE_KEYPAIR, FAILED_TO_LOAD_KEYPAIR, KEYPAIR_NOT_FOUND, FAILED_TO_DESERIALIZE_KEYPAIR};
7+
use crate::constants::{GROUP_NOT_FOUND, UNABLE_TO_SET_GROUP_NAME, UNABLE_TO_GET_GROUP_NAME, TEST_GROUP_NAME, UNABLE_TO_STORE_KEYPAIR, FAILED_TO_LOAD_KEYPAIR, KEYPAIR_NOT_FOUND, FAILED_TO_DESERIALIZE_KEYPAIR, ROUTE_ID_DHT_KEY};
88

99
use crate::backend::Backend;
1010
use crate::common::{CommonKeypair, DHTEntity};
1111
use veilid_core::{
12-
vld0_generate_keypair, TypedKey, CRYPTO_KIND_VLD0
12+
vld0_generate_keypair, TypedKey, CRYPTO_KIND_VLD0, VeilidUpdate, VALID_CRYPTO_KINDS
1313
};
1414

1515
#[cfg(test)]
1616
mod tests {
1717
use super::*;
1818
use tokio::fs;
19+
use tokio::sync::mpsc;
1920
use tmpdir::TmpDir;
2021

2122
#[tokio::test]
@@ -37,7 +38,7 @@ mod tests {
3738
backend.stop().await.expect("Unable to stop");
3839

3940
backend.start().await.expect("Unable to restart");
40-
let loaded_group = backend.get_group(TypedKey::new(CRYPTO_KIND_VLD0, group.id())).await.expect(GROUP_NOT_FOUND);
41+
let mut loaded_group = backend.get_group(TypedKey::new(CRYPTO_KIND_VLD0, group.id())).await.expect(GROUP_NOT_FOUND);
4142

4243
let protected_store = backend.get_protected_store().unwrap();
4344
let keypair_data = protected_store.load_user_secret(group.id().to_string())
@@ -48,16 +49,14 @@ mod tests {
4849

4950
// Check that the id matches group.id()
5051
assert_eq!(retrieved_keypair.id, group.id());
51-
52+
5253
// Check that the public_key matches the owner public key from the DHT record
5354
assert_eq!(retrieved_keypair.public_key, loaded_group.get_dht_record().owner().clone());
5455

5556
// Check that the secret and encryption keys match
5657
assert_eq!(retrieved_keypair.secret_key, group.get_secret_key());
5758
assert_eq!(retrieved_keypair.encryption_key, group.get_encryption_key());
5859

59-
let mut loaded_group = backend.get_group(TypedKey::new(CRYPTO_KIND_VLD0, group.id())).await.expect(GROUP_NOT_FOUND);
60-
6160
// Check if we can get group name
6261
let group_name = loaded_group.get_name().await.expect(UNABLE_TO_GET_GROUP_NAME);
6362
assert_eq!(group_name, TEST_GROUP_NAME);
@@ -76,7 +75,7 @@ mod tests {
7675
assert_eq!(name, repo_name);
7776

7877
// Add repo to group
79-
loaded_group.add_repo(repo).await.expect("Unable to add repo to group");
78+
loaded_group.add_repo(repo.clone()).await.expect("Unable to add repo to group");
8079

8180
// List known repos
8281
let repos = loaded_group.list_repos().await;
@@ -89,6 +88,57 @@ mod tests {
8988
let retrieved_name = loaded_repo.get_name().await.expect("Unable to get repo name after restart");
9089
assert_eq!(retrieved_name, repo_name);
9190

91+
// Get the update receiver from the backend
92+
let update_rx = backend.subscribe_updates().expect("Failed to subscribe to updates");
93+
94+
// Set up a channel to receive AppMessage updates
95+
let (message_tx, mut message_rx) = mpsc::channel(1);
96+
97+
// Spawn a task to listen for updates
98+
tokio::spawn(async move {
99+
let mut rx = update_rx.resubscribe();
100+
while let Ok(update) = rx.recv().await {
101+
if let VeilidUpdate::AppMessage(app_message) = update {
102+
// Optionally, filter by route_id or other criteria
103+
message_tx.send(app_message).await.unwrap();
104+
}
105+
}
106+
});
107+
108+
// Get VeilidAPI instance from backend
109+
let veilid_api = backend.get_veilid_api().expect("Failed to get VeilidAPI instance");
110+
111+
// Create a new private route
112+
let (route_id, route_id_blob) = veilid_api
113+
.new_custom_private_route(
114+
&VALID_CRYPTO_KINDS,
115+
veilid_core::Stability::Reliable,
116+
veilid_core::Sequencing::PreferOrdered,
117+
)
118+
.await
119+
.expect("Failed to create route");
120+
121+
// Store the route_id_blob in DHT
122+
loaded_repo
123+
.store_route_id_in_dht(route_id_blob.clone())
124+
.await
125+
.expect("Failed to store route ID blob in DHT");
126+
127+
// Define the message to send
128+
let message = b"Test Message to Repo Owner".to_vec();
129+
130+
// Send the message
131+
loaded_repo
132+
.send_message_to_owner(veilid_api, message.clone(), ROUTE_ID_DHT_KEY)
133+
.await
134+
.expect("Failed to send message to repo owner");
135+
136+
// Receive the message
137+
let received_app_message = message_rx.recv().await.expect("Failed to receive message");
138+
139+
// Verify the message
140+
assert_eq!(received_app_message.message(), message.as_slice());
141+
92142
backend.stop().await.expect("Unable to stop");
93143
}
94144

0 commit comments

Comments
 (0)