Files
amethyst/docs/namecoin-nip05-design.md
T
M d82ace2f63 feat: Namecoin NIP-05 identity verification via ElectrumX
Add censorship-resistant NIP-05 verification using the Namecoin blockchain.
Users can set their nip05 field to a .bit domain (e.g. alice@example.bit)
or direct Namecoin name (d/example, id/alice) and Amethyst will resolve
the pubkey mapping via ElectrumX instead of HTTP.

Resolution uses the standard Electrum protocol (scripthash-based lookups):
- Build canonical name index script matching ElectrumX-NMC indexing
- Query blockchain.scripthash.get_history for the name's tx history
- Parse NAME_UPDATE script from the latest transaction output
- Extract Nostr pubkey from the name's JSON value

Supports both d/ (domain) and id/ (identity) Namecoin namespaces,
simple and extended NIP-05-like value formats with relay hints,
LRU caching with 1h TTL, and self-signed TLS certificates.

New files:
- quartz: ElectrumxClient, NamecoinNameResolver, NamecoinLookupCache
- amethyst: NamecoinNameService, Nip05NamecoinAdapter, NamecoinVerificationDisplay
- docs: namecoin-nip05-design.md
- tests: NamecoinNameResolverTest

Modified:
- Nip05Client: optional namecoinResolver routes .bit to blockchain
- AppModules: wire up resolver

See docs/namecoin-nip05-design.md for full architecture and protocol details.
2026-03-02 13:07:46 +11:00

11 KiB
Raw Blame History

Namecoin NIP-05 Resolution — Design Document

Overview

This patch adds Namecoin blockchain-based NIP-05 identity verification to Amethyst. Users can set their nip05 field to a .bit domain (e.g. alice@example.bit) or a direct Namecoin name (d/example, id/alice), and Amethyst will resolve it via the Namecoin blockchain instead of HTTP.

This is censorship-resistant identity verification: no web server to seize, no DNS to hijack, no TLS certificate to revoke. The name-to-pubkey mapping lives in Namecoin UTXOs.

Architecture

┌─────────────────────────────────────────────────────────────┐
│                        Amethyst App                         │
├─────────────────────────────────────────────────────────────┤
│  Nip05Client                                                │
│    ├── HTTP path (existing) ── Nip05Fetcher                 │
│    └── Namecoin path (new)  ── NamecoinNameResolver         │
│                                    │                        │
│  NamecoinNameService (singleton)   │                        │
│    ├── NamecoinLookupCache         │                        │
│    └── Nip05NamecoinAdapter        │                        │
│                                    │                        │
│  UI: NamecoinVerificationDisplay   │                        │
│       NamecoinSearchResult         │                        │
├────────────────────────────────────┼────────────────────────┤
│                        Quartz Library                       │
├────────────────────────────────────┼────────────────────────┤
│  NamecoinNameResolver              │                        │
│    ├── parseIdentifier()           │                        │
│    ├── extractFromDomainValue()    │ (d/ namespace)         │
│    └── extractFromIdentityValue()  │ (id/ namespace)        │
│                                    │                        │
│  ElectrumxClient                   │                        │
│    ├── buildNameIndexScript()      │                        │
│    ├── electrumScriptHash()        │                        │
│    └── parseNameScript()           │                        │
│                                    ▼                        │
│                          ┌──────────────────┐               │
│                          │  ElectrumX Server │               │
│                          │  (Namecoin node)  │               │
│                          └──────────────────┘               │
└─────────────────────────────────────────────────────────────┘

Layer Separation

  • quartz/ (library) — Protocol-level logic. No Android dependencies.

    • ElectrumxClient — TCP/TLS connection to ElectrumX, JSON-RPC, script parsing
    • NamecoinNameResolver — Identifier parsing, value extraction, NIP-05 mapping
    • NamecoinLookupCache — LRU cache with TTL
    • NamecoinNameResolverTest — Unit tests for parsing and value extraction
  • amethyst/ (app) — Android integration and UI.

    • NamecoinNameService — Application singleton, lifecycle management
    • Nip05NamecoinAdapter — Static bridge for NIP-05 verification hooks
    • NamecoinVerificationDisplay — Compose UI for verified badge + search results

ElectrumX Protocol — How Name Resolution Works

Namecoin names are stored as UTXOs with NAME_UPDATE scripts. The ElectrumX server indexes these by a canonical "name index script hash", allowing lookup via standard Electrum protocol methods.

Resolution Steps

1. Build canonical name index script
   OP_NAME_UPDATE(0x53) + push(name_bytes) + push(empty) + OP_2DROP(0x6d) + OP_DROP(0x75) + OP_RETURN(0x6a)

2. Compute Electrum-style scripthash
   SHA-256(script) → reverse bytes → hex encode

3. Query transaction history
   → blockchain.scripthash.get_history(scripthash)
   ← [{tx_hash, height}, ...]

4. Fetch latest transaction (last entry = most recent name update)
   → blockchain.transaction.get(tx_hash, verbose=true)
   ← {vout: [{scriptPubKey: {hex: "53..."}}]}

5. Parse NAME_UPDATE script from transaction output
   Script: OP_NAME_UPDATE <push(name)> <push(value_json)> OP_2DROP OP_DROP <address_script>
   Extract: name string + JSON value

6. Extract Nostr pubkey from the JSON value

Why Not blockchain.name.get_value_proof?

The Electrum-NMC fork of ElectrumX advertises a blockchain.name.get_value_proof method (protocol v1.4.3), but in practice this method expects a scripthash parameter, not a name string. The scripthash-based approach described above works with both the Namecoin ElectrumX fork and stock ElectrumX pointed at a Namecoin node, as long as the server has a name index.

Namecoin Value Formats

Domain namespace (d/)

Namecoin d/ names store domain configuration as JSON. Two Nostr formats are supported:

Simple form — single pubkey for the root domain:

{
  "nostr": "b0635d6a9851d3aed0cd6c495b282167acf761729078d975fc341b22650b07b9"
}

Extended form — multiple users with relay hints (mirrors NIP-05 JSON structure):

{
  "nostr": {
    "names": {
      "_": "aaaa...0001",
      "alice": "bbbb...0002"
    },
    "relays": {
      "bbbb...0002": ["wss://relay.example.com"]
    }
  }
}

Identity namespace (id/)

Namecoin id/ names store personal identity data:

{
  "nostr": "cccc...0003"
}

Or with relay hints:

{
  "nostr": {
    "pubkey": "dddd...0004",
    "relays": ["wss://relay.example.com"]
  }
}

Identifier Formats

User input Namecoin name Local part Namespace
alice@example.bit d/example alice DOMAIN
_@example.bit d/example _ DOMAIN
example.bit d/example _ DOMAIN
d/example d/example _ DOMAIN
id/alice id/alice _ IDENTITY

NIP-05 Integration

The integration is minimal and non-invasive:

  1. Nip05Client gains an optional namecoinResolver parameter
  2. On verify() and get(), if the identifier matches .bit / d/ / id/, it routes to NamecoinNameResolver instead of the HTTP fetcher
  3. Non-Namecoin identifiers are completely unaffected
  4. AppModules wires up the resolver at construction time

Default ElectrumX Server

electrumx.testls.space:50002  (TLS, self-signed certificate)
  • ElectrumX 1.16.0, Namecoin chain, protocol 1.41.4.3
  • Also available via Tor: i665jpwsq46zlsdbnj4axgzd3s56uzey5uhotsnxzsknzbn36jaddsid.onion:50002
  • Fallback servers: ulrichard.ch:50006, nmc2.lelux.fi:50006 (currently offline)

The trustAllCerts flag is set for servers with self-signed certificates. Users can configure custom servers via NamecoinNameService.setCustomServers().

Caching

  • LRU cache with configurable max entries (default 500) and TTL (default 1 hour)
  • Cache key is the normalized (lowercased, trimmed) identifier
  • Both positive and negative results are cached
  • Cache is invalidated on TTL expiry; manual invalidate() and clear() are available

UI Components

NamecoinVerificationDisplay

Shows a ⛓ chain-link badge next to profiles verified via Namecoin. Distinct from the standard NIP-05 checkmark — uses Namecoin blue (#4A90D9) and sea green (#2E8B57).

NamecoinSearchResult

Search bar integration. When a user types a .bit identifier, shows a loading state during resolution, then the resolved pubkey with a clickable profile link.

Security Considerations

  • Self-signed certificates: The primary ElectrumX server uses a self-signed TLS cert. The trustAllCerts option accepts any certificate for that server. This is acceptable because the Namecoin blockchain itself provides the trust anchor — we verify names against on-chain data, not the transport layer. A MITM could return stale data but cannot forge name registrations.
  • Name expiry: Namecoin names expire after ~36,000 blocks (~250 days) if not renewed. The current implementation does not check expiry. Future work should compare the name's height + expiresIn against the current block height.
  • Server trust: The client trusts that the ElectrumX server returns accurate transaction data. For higher assurance, SPV proof verification could be added in the future.

Files Changed

New files (quartz/)

  • quartz/.../nip05/namecoin/ElectrumxClient.kt — ElectrumX TCP/TLS client, scripthash-based name resolution
  • quartz/.../nip05/namecoin/NamecoinNameResolver.kt — Identifier parsing, value extraction
  • quartz/.../nip05/namecoin/NamecoinLookupCache.kt — LRU cache with TTL
  • quartz/src/jvmTest/.../NamecoinNameResolverTest.kt — Unit tests

New files (amethyst/)

  • amethyst/.../service/namecoin/NamecoinNameService.kt — App singleton, coroutine scope
  • amethyst/.../service/namecoin/Nip05NamecoinAdapter.kt — Static bridge for NIP-05 hooks
  • amethyst/.../ui/note/namecoin/NamecoinVerificationDisplay.kt — Compose UI components

Modified files

  • amethyst/.../AppModules.kt — Wire up NamecoinNameResolver into Nip05Client
  • quartz/.../nip05DnsIdentifiers/Nip05Client.kt — Route .bit identifiers to Namecoin resolver
  • amethyst/.../relays/RelayInformationScreen.kt — Import reordering (spotless)

Testing

Unit tests

./gradlew :quartz:jvmTest --tests "*NamecoinNameResolverTest*"

Manual testing (emulator)

  1. Build and install: ./gradlew :amethyst:installFdroidDebug
  2. Search for m@testls.bit — should resolve to pubkey 6cdebcca...18667d
  3. Search for testls.bit — root domain lookup
  4. Search for d/testls — direct namespace format

Live verification

The name d/testls is registered on the Namecoin blockchain (block 551519+, last updated block 814278) with value:

{
  "nostr": {
    "names": {
      "m": "6cdebccabda1dfa058ab85352a79509b592b2bdfa0370325e28ec1cb4f18667d"
    }
  }
}