Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 12 additions & 8 deletions fabric/go/fabric.go
Original file line number Diff line number Diff line change
Expand Up @@ -428,25 +428,26 @@ func (f *Fabric) updateAnchors(trustID string, kind trustKind) error {
return nil
}

// Resolve a single handle. Supports dotted names like "hello.alice@bitcoin".
func (f *Fabric) Resolve(handle string) (Resolved, error) {
// Resolve a single handle. Returns nil if not found. Supports dotted names like "hello.alice@bitcoin".
func (f *Fabric) Resolve(handle string) (*Resolved, error) {
batch, err := f.ResolveAll([]string{handle})
if err != nil {
return Resolved{}, err
return nil, err
}
for _, z := range batch.Zones {
if z.Handle == handle {
return Resolved{Zone: z, Roots: batch.Roots}, nil
return &Resolved{Zone: z, Roots: batch.Roots}, nil
}
}
return Resolved{}, &FabricError{Code: "decode", Message: handle + " not found"}
return nil, nil
}

// ResolveById resolves a numeric ID to a handle by querying relays
// for the reverse mapping, then verifying via forward resolution.
func (f *Fabric) ResolveById(numId string) (Resolved, error) {
// Returns nil if not found.
func (f *Fabric) ResolveById(numId string) (*Resolved, error) {
if err := f.Bootstrap(); err != nil {
return Resolved{}, err
return nil, err
}
urls := f.pool.ShuffledURLs(4)
var lastErr error = &FabricError{Code: "no_peers", Message: "reverse resolution failed"}
Expand Down Expand Up @@ -486,6 +487,9 @@ func (f *Fabric) ResolveById(numId string) (Resolved, error) {
lastErr = err
continue
}
if resolved == nil {
continue
}

if resolved.Zone.NumId == nil || *resolved.Zone.NumId != numId {
lastErr = &FabricError{Code: "verify", Message: fmt.Sprintf("reverse mismatch: expected %s", numId)}
Expand All @@ -494,7 +498,7 @@ func (f *Fabric) ResolveById(numId string) (Resolved, error) {

return resolved, nil
}
return Resolved{}, lastErr
return nil, lastErr
}

// SearchAddr searches for handles by address record, verifies via forward resolution.
Expand Down
9 changes: 5 additions & 4 deletions fabric/js/fabric-core/src/fabric.ts
Original file line number Diff line number Diff line change
Expand Up @@ -427,12 +427,12 @@ export class Fabric {

// ── Resolution ──

/** Resolve a single handle. Supports nested names like `hello.alice@bitcoin`. */
async resolve(handle: string): Promise<Resolved> {
/** Resolve a single handle. Returns null if not found. Supports nested names like `hello.alice@bitcoin`. */
async resolve(handle: string): Promise<Resolved | null> {
const batch = await this.resolveAll([handle]);
const zone = batch.zones.find((z) => z.handle === handle);
if (!zone) {
throw new FabricError(`${handle} not found`, "decode");
return null;
}
return { zone, roots: batch.roots };
}
Expand All @@ -451,13 +451,14 @@ export class Fabric {
const entry = records.find(r => r.id === numId);
if (!entry) continue;

let resolved: Resolved;
let resolved: Resolved | null;
try {
resolved = await this.resolve(entry.name);
} catch (e) {
lastErr = e instanceof Error ? e : new FabricError(String(e), "decode");
continue;
}
if (!resolved) continue;

const json = resolved.zone.toJson();
if (json?.num_id !== numId) {
Expand Down
16 changes: 5 additions & 11 deletions fabric/kotlin/src/main/kotlin/org/spacesprotocol/fabric/Fabric.kt
Original file line number Diff line number Diff line change
Expand Up @@ -199,14 +199,13 @@ class Fabric(

// -- Resolution --

fun resolve(handle: String): Resolved {
fun resolve(handle: String): Resolved? {
val batch = resolveAll(listOf(handle))
val zone = batch.zones.find { it.handle == handle }
?: throw FabricError("decode", "$handle not found")
val zone = batch.zones.find { it.handle == handle } ?: return null
return Resolved(zone, batch.roots)
}

fun resolveById(numId: String): Resolved {
fun resolveById(numId: String): Resolved? {
bootstrap()
val urls = pool.shuffledUrls(4)
var lastErr: Exception = FabricError("no_peers", "reverse resolution failed")
Expand All @@ -231,12 +230,7 @@ class Fabric(

val entry = entries.find { it.id == numId } ?: continue

val resolved = try {
resolve(entry.name)
} catch (e: Exception) {
lastErr = e
continue
}
val resolved = resolve(entry.name) ?: continue

if (resolved.zone.numId != numId) {
lastErr = FabricError("verify", "reverse mismatch: expected $numId, got ${resolved.zone.numId}")
Expand All @@ -246,7 +240,7 @@ class Fabric(
return resolved
}

throw lastErr
return null
}

fun searchAddr(name: String, addr: String): ResolvedBatch {
Expand Down
16 changes: 7 additions & 9 deletions fabric/python/fabric/client.py
Original file line number Diff line number Diff line change
Expand Up @@ -219,15 +219,15 @@ def badge_for(self, sovereignty: str, roots: list[str]) -> str:
return BADGE_UNVERIFIED
return BADGE_NONE

def resolve(self, handle: str) -> Resolved:
def resolve(self, handle: str) -> Resolved | None:
batch = self.resolve_all([handle])
zone = next((z for z in batch.zones if z.handle == handle), None)
if zone is None:
raise FabricError("decode", f"{handle} not found")
return None
return Resolved(zone=zone, roots=batch.roots)

def resolve_by_id(self, num_id: str) -> Resolved:
"""Resolve a numeric ID to a verified handle."""
def resolve_by_id(self, num_id: str) -> Resolved | None:
"""Resolve a numeric ID to a verified handle. Returns None if not found."""
self.bootstrap()
urls = self._pool.shuffled_urls(4)
last_err: Exception = FabricError("no_peers", "reverse resolution failed")
Expand All @@ -249,10 +249,8 @@ def resolve_by_id(self, num_id: str) -> Resolved:
if entry is None:
continue

try:
resolved = self.resolve(entry["name"])
except Exception as e:
last_err = e
resolved = self.resolve(entry["name"])
if resolved is None:
continue

if getattr(resolved.zone, "num_id", None) != num_id:
Expand All @@ -262,7 +260,7 @@ def resolve_by_id(self, num_id: str) -> Resolved:
self._pool.mark_alive(u)
return resolved

raise last_err
return None

def search_addr(self, name: str, addr: str) -> ResolvedBatch:
"""Search for handles by address record, verify via forward resolution."""
Expand Down
17 changes: 7 additions & 10 deletions fabric/swift/Sources/Fabric/Fabric.swift
Original file line number Diff line number Diff line change
Expand Up @@ -322,17 +322,17 @@ public final class Fabric: @unchecked Sendable {

// MARK: - Resolution

/// Resolve a single handle. Supports dotted names like `hello.alice@bitcoin`.
public func resolve(_ handle: String) async throws -> Resolved {
/// Resolve a single handle. Returns nil if not found. Supports dotted names like `hello.alice@bitcoin`.
public func resolve(_ handle: String) async throws -> Resolved? {
let batch = try await resolveAll([handle])
guard let zone = batch.zones.first(where: { $0.handle == handle }) else {
throw FabricError.decode("\(handle) not found")
return nil
}
return Resolved(zone: zone, roots: batch.roots)
}

/// Resolve a numeric ID to a verified handle.
public func resolveById(_ numId: String) async throws -> Resolved {
/// Resolve a numeric ID to a verified handle. Returns nil if not found.
public func resolveById(_ numId: String) async throws -> Resolved? {
try await bootstrap()
let relays = pool.shuffledUrls(4)

Expand All @@ -347,16 +347,13 @@ public final class Fabric: @unchecked Sendable {

guard let entry = entries.first(where: { $0.id == numId }) else { continue }

let resolved: Resolved
do {
resolved = try await resolve(entry.name)
} catch { continue }
guard let resolved = try await resolve(entry.name) else { continue }

guard resolved.zone.numId == numId else { continue }
return resolved
}

throw FabricError.noPeers
return nil
}

/// Search for handles by address record, verify via forward resolution.
Expand Down
Loading