diff --git a/Cargo.lock b/Cargo.lock index c09d028..9ac610a 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2174,6 +2174,7 @@ dependencies = [ "getrandom 0.3.1", "js-sys", "log", + "mainline", "pkarr", "pubky-common", "pubky-testnet", diff --git a/pubky-testnet/Cargo.toml b/pubky-testnet/Cargo.toml index 5307dd9..d6cfdb7 100644 --- a/pubky-testnet/Cargo.toml +++ b/pubky-testnet/Cargo.toml @@ -15,12 +15,13 @@ anyhow = "1.0.95" http-relay = "0.2.0" mainline = "5.2.0" pkarr-relay = "0.5.7" -pubky = { version = "0.4.1", path = "../pubky" } -pubky-common = { version = "0.3.1", path = "../pubky-common" } -pubky-homeserver = { version = "0.1.1", path = "../pubky-homeserver" } tokio = { version = "1.43.0", features = ["full"] } tracing-subscriber = "0.3.19" url = "2.5.4" +pubky = { path = "../pubky" } +pubky-common = { path = "../pubky-common" } +pubky-homeserver = { path = "../pubky-homeserver" } + [dev-dependencies] tracing-subscriber = "0.3.19" diff --git a/pubky/Cargo.toml b/pubky/Cargo.toml index d7ffbca..cf5ee0c 100644 --- a/pubky/Cargo.toml +++ b/pubky/Cargo.toml @@ -59,6 +59,7 @@ futures-lite = "2.6.0" pubky-testnet = { path = "../pubky-testnet" } tokio = "1.43.0" tracing-subscriber = "0.3.19" +mainline = "5.3.1" [build-dependencies] cfg_aliases = "0.2.1" diff --git a/pubky/README.md b/pubky/README.md index 8ed06c9..38d05af 100644 --- a/pubky/README.md +++ b/pubky/README.md @@ -53,3 +53,27 @@ async fn main () { ## Example code Check more [examples](https://github.com/pubky/pubky-core/tree/main/examples) for using the Pubky client. + +## Wasm Rust Analyzer + +In vscode with the rust-analyzer, wasm behind the `#[cfg(wasm_browser)]` guard is not type checked. To fix this, add +a `.vscode/settings.json` file in the root of this project with the following content: + +```json +{ + "rust-analyzer.cargo.target": "wasm32-unknown-unknown" +} +``` + +If not done already, you need to add the wasm target: `cargo target add wasm32-unknown-unknown`. + +This is just a workaround because it enables the wasm feature in all workspace member which creates problems. +So it is best to enable this settings only temporarily for wasm development and then turn it off again before commiting the +changes. This is a [rust-analyzer issue](https://github.com/rust-lang/rust-analyzer/issues/11900#issuecomment-1166638234). + +## How To Build/Test the NPM Package + +1. Go to `pubky/pkg`. +2. Run `npm run build`. +3. Run a testnet mainline DHT, Pkarr relay and Homeserver `npm run testnet` +4. Run tests with `npm run test`. \ No newline at end of file diff --git a/pubky/pkg/test/auth.js b/pubky/pkg/test/auth.js index 87cea3f..a7e397c 100644 --- a/pubky/pkg/test/auth.js +++ b/pubky/pkg/test/auth.js @@ -103,3 +103,36 @@ test("Auth: 3rd party signin", async (t) => { let session = await client.session(authedPubky); t.deepEqual(session.capabilities(), capabilities.split(',')) }) + + +test('getHomeserver not found', async (t) => { + const client = Client.testnet(); + + const keypair = Keypair.random() + const publicKey = keypair.publicKey() + + try { + let homeserver = await client.getHomeserver(publicKey); + t.fail("getHomeserver should NOT be found."); + } catch (e) { + t.pass("getHomeserver should NOT be found."); + } +}) + +function sleep(ms) { + return new Promise(resolve => setTimeout(resolve, ms)); +} + +test('getHomeserver success', async (t) => { + const client = Client.testnet(); + + const keypair = Keypair.random() + const publicKey = keypair.publicKey() + + const signupToken = await createSignupToken(client) + + await client.signup(keypair, HOMESERVER_PUBLICKEY, signupToken); + + let homeserver = await client.getHomeserver(publicKey); + t.is(homeserver.z32(), HOMESERVER_PUBLICKEY.z32(), "homeserver is correct"); +}) \ No newline at end of file diff --git a/pubky/src/native/api/auth.rs b/pubky/src/native/api/auth.rs index 27bbbaa..a7d644b 100644 --- a/pubky/src/native/api/auth.rs +++ b/pubky/src/native/api/auth.rs @@ -346,6 +346,13 @@ impl Client { ) .await } + + /// Get the homeserver for a given Pubky public key. + /// Looks up the pkarr packet for the given public key and returns the content of the first `_pubky` SVCB record. + pub async fn get_homeserver(&self, pubky: &PublicKey) -> Option { + let packet = self.pkarr.resolve_most_recent(pubky).await?; + Self::extract_host_from_packet(&packet) + } } #[derive(Debug, Clone)] @@ -379,6 +386,8 @@ mod tests { use reqwest::StatusCode; use std::time::Duration; + use crate::{native::internal::pkarr::PublishStrategy, Client}; + #[tokio::test] async fn basic_authn() { let testnet = Testnet::run().await.unwrap(); @@ -822,4 +831,27 @@ mod tests { "Record was not republished after threshold exceeded" ); } + + #[tokio::test] + async fn test_get_homeserver() { + let dht = mainline::Testnet::new(3).unwrap(); + let client = Client::builder() + .pkarr(|builder| builder.bootstrap(&dht.bootstrap)) + .build() + .unwrap(); + let keypair = Keypair::random(); + let pubky = keypair.public_key(); + + let homeserver_key = Keypair::random().public_key().to_z32(); + client + .publish_homeserver( + &keypair, + Some(homeserver_key.as_str()), + PublishStrategy::Force, + ) + .await + .unwrap(); + let homeserver = client.get_homeserver(&pubky).await; + assert_eq!(homeserver, Some(homeserver_key)); + } } diff --git a/pubky/src/native/internal/pkarr.rs b/pubky/src/native/internal/pkarr.rs index 4d25379..b763d2e 100644 --- a/pubky/src/native/internal/pkarr.rs +++ b/pubky/src/native/internal/pkarr.rs @@ -101,7 +101,7 @@ impl Client { /// Helper to extract the current homeserver host from a signed PKarr packet. /// Iterates over the records with name "_pubky" and returns the first SVCB target found. - fn extract_host_from_packet(packet: &SignedPacket) -> Option { + pub(crate) fn extract_host_from_packet(packet: &SignedPacket) -> Option { packet .resource_records("_pubky") .find_map(|rr| match &rr.rdata { diff --git a/pubky/src/wasm/api/auth.rs b/pubky/src/wasm/api/auth.rs index 00023cc..d8b8621 100644 --- a/pubky/src/wasm/api/auth.rs +++ b/pubky/src/wasm/api/auth.rs @@ -106,6 +106,20 @@ impl Client { Ok(()) } + /// Get the homeserver id for a given Pubky public key. + /// Looks up the pkarr packet for the given public key and returns the content of the first `_pubky` SVCB record. + /// Throws an error if no homeserver is found. + #[wasm_bindgen(js_name = "getHomeserver")] + pub async fn get_homeserver(&self, public_key: &PublicKey) -> Result { + let val = self.0.get_homeserver(public_key.as_inner()).await; + if val.is_none() { + return Err(JsValue::from_str("No homeserver found")); + } + let val = val.unwrap(); + let js_val = JsValue::from_str(val.as_str()); + PublicKey::try_from(js_val) + } + /// Republish the user's PKarr record pointing to their homeserver. /// /// This method will republish the record if no record exists or if the existing record diff --git a/pubky/src/wasm.rs b/pubky/src/wasm/mod.rs similarity index 100% rename from pubky/src/wasm.rs rename to pubky/src/wasm/mod.rs