mirror of
https://code.gri.mw/GUI/grim.git
synced 2026-07-15 11:18:55 +00:00
nostr: resolve a contact's @name via authority reverse lookup first
The requester still saw a bare npub for the payer because resolving a name
depended on fetching the peer's kind-0 off a relay (a nostr REQ with tight
timeouts over the private transport) before it could verify the NIP-05. When
that fetch doesn't land, resolution never starts — even though the name server
knows the name perfectly well.
resolve_contact_identity now asks the home authority directly:
GET /api/v1/by-pubkey/{hex} → the active @name for that key, in one HTTP
round-trip, with no profile fetch. That answer is authoritative, so the name is
set verified immediately. The kind-0 + verify path stays as a fallback for
FOREIGN authorities (which the home server can't speak for) and is still what
CLEARS a released/reassigned name. New nip05::name_by_pubkey helper.
Pairs with the goblin-nip05d by-pubkey endpoint. Verified by me only as far as
compile + unit/i18n tests; the live two-party resolution is the owner's call.
This commit is contained in:
+25
-8
@@ -660,8 +660,31 @@ impl NostrService {
|
||||
let Ok(pk) = PublicKey::from_hex(&hex) else {
|
||||
return;
|
||||
};
|
||||
// Check the handle they currently advertise; if the kind-0 can't be
|
||||
// fetched, fall back to the cached handle so a release is still caught.
|
||||
let Ok(rt) = tokio::runtime::Builder::new_current_thread()
|
||||
.enable_all()
|
||||
.build()
|
||||
else {
|
||||
return;
|
||||
};
|
||||
// Primary: ask the home authority directly what @name this key holds.
|
||||
// One HTTP round-trip, authoritative, and independent of whether we can
|
||||
// fetch their kind-0 off a relay (the fragile leg) — this is what
|
||||
// makes a contact's name show on the FIRST interaction.
|
||||
let home = crate::nostr::nip05::home_domain();
|
||||
if let Some(name) = rt.block_on(crate::nostr::nip05::name_by_pubkey(&home, &hex)) {
|
||||
let nip05 = format!("{}@{}", name, home);
|
||||
if let Some(mut c) = svc.store.contact(&hex) {
|
||||
if apply_nip05_check(&mut c, &nip05, crate::nostr::nip05::Nip05Check::Verified)
|
||||
{
|
||||
svc.store.save_contact(&c);
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
// Fallback: the handle they advertise in their kind-0 (covers FOREIGN
|
||||
// authorities the home reverse-lookup can't speak for); if the kind-0 can't
|
||||
// be fetched, fall back to the cached handle so a release is still caught.
|
||||
// This path can also CLEAR a released/reassigned name.
|
||||
let advertised = svc
|
||||
.fetch_profile_blocking(&hex, &hints)
|
||||
.and_then(|p| p.nip05);
|
||||
@@ -671,12 +694,6 @@ impl NostrService {
|
||||
let Some((name, domain)) = nip05.split_once('@') else {
|
||||
return;
|
||||
};
|
||||
let Ok(rt) = tokio::runtime::Builder::new_current_thread()
|
||||
.enable_all()
|
||||
.build()
|
||||
else {
|
||||
return;
|
||||
};
|
||||
let check = rt.block_on(crate::nostr::nip05::check(&pk, name, domain));
|
||||
if let Some(mut c) = svc.store.contact(&hex) {
|
||||
if apply_nip05_check(&mut c, &nip05, check) {
|
||||
|
||||
@@ -106,6 +106,28 @@ pub async fn resolve(name: &str, domain: &str) -> Option<Nip05Resolution> {
|
||||
parse_well_known(&body, name)
|
||||
}
|
||||
|
||||
/// Reverse lookup against an authority: the active `@name` a pubkey holds, if
|
||||
/// any. Authoritative and single-request — unlike fetching the peer's kind-0 off
|
||||
/// a relay and verifying the NIP-05 it advertises, this needs no profile fetch,
|
||||
/// so a contact's name resolves even when their profile can't be retrieved.
|
||||
/// `Some(name)` = server-confirmed; `None` = the key has no name on this
|
||||
/// authority OR the server was unreachable (the two are indistinguishable here,
|
||||
/// so callers must NOT treat `None` as "released" — fall back to the kind-0 +
|
||||
/// verify path, which can tell a definitive miss from a network blip).
|
||||
pub async fn name_by_pubkey(domain: &str, pubkey_hex: &str) -> Option<String> {
|
||||
let url = format!(
|
||||
"https://{}/api/v1/by-pubkey/{}",
|
||||
domain,
|
||||
urlencode(pubkey_hex)
|
||||
);
|
||||
let body = nym::http_request("GET", url, None, vec![]).await?;
|
||||
let doc: Value = serde_json::from_str(&body).ok()?;
|
||||
doc.get("name")
|
||||
.and_then(|v| v.as_str())
|
||||
.filter(|s| !s.is_empty())
|
||||
.map(|s| s.to_string())
|
||||
}
|
||||
|
||||
/// Verify that a pubkey matches its claimed NIP-05 identifier.
|
||||
pub async fn verify(pubkey: &PublicKey, name: &str, domain: &str) -> bool {
|
||||
match resolve(name, domain).await {
|
||||
|
||||
Reference in New Issue
Block a user